branch:
server.ts
3226 bytesRaw
import { createMcpHandler } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { DynamicWorkerExecutor } from "@cloudflare/codemode";
import { codeMcpServer } from "@cloudflare/codemode/mcp";
import { z } from "zod";

/**
 * Create an upstream MCP server with some demo tools.
 * In a real app, this could be any MCP server — GitHub, Stripe, etc.
 */
function createUpstreamServer() {
  const server = new McpServer({
    name: "demo-tools",
    version: "1.0.0"
  });

  server.registerTool(
    "add",
    {
      description: "Add two numbers together",
      inputSchema: {
        a: z.number().describe("First number"),
        b: z.number().describe("Second number")
      }
    },
    async ({ a, b }) => ({
      content: [{ type: "text", text: String(a + b) }]
    })
  );

  server.registerTool(
    "greet",
    {
      description: "Generate a greeting message",
      inputSchema: {
        name: z.string().describe("Name to greet"),
        language: z
          .enum(["en", "es", "fr"])
          .optional()
          .describe("Language for the greeting")
      }
    },
    async ({ name, language }) => {
      const greetings = {
        en: `Hello, ${name}!`,
        es: `¡Hola, ${name}!`,
        fr: `Bonjour, ${name}!`
      };
      const text = greetings[language ?? "en"];
      return { content: [{ type: "text", text }] };
    }
  );

  server.registerTool(
    "list_items",
    {
      description: "List items with optional filtering",
      inputSchema: {
        category: z.string().optional().describe("Filter by category"),
        limit: z.number().optional().describe("Max items to return")
      }
    },
    async ({ category, limit }) => {
      const items = [
        { id: 1, name: "Widget", category: "hardware" },
        { id: 2, name: "Gadget", category: "hardware" },
        { id: 3, name: "Service A", category: "software" },
        { id: 4, name: "Service B", category: "software" },
        { id: 5, name: "Manual", category: "docs" }
      ];
      let filtered = category
        ? items.filter((i) => i.category === category)
        : items;
      if (limit) filtered = filtered.slice(0, limit);
      return {
        content: [{ type: "text", text: JSON.stringify(filtered) }]
      };
    }
  );

  return server;
}

export default {
  async fetch(
    request: Request,
    env: Env,
    ctx: ExecutionContext
  ): Promise<Response> {
    const url = new URL(request.url);

    // /mcp — the original upstream MCP server (raw tools)
    if (url.pathname === "/mcp") {
      const upstream = createUpstreamServer();
      return createMcpHandler(upstream, { route: "/mcp" })(request, env, ctx);
    }

    // /codemode — the codemode-wrapped server (single code tool)
    if (url.pathname === "/codemode") {
      const upstream = createUpstreamServer();
      const executor = new DynamicWorkerExecutor({ loader: env.LOADER });
      const server = await codeMcpServer({ server: upstream, executor });
      return createMcpHandler(server, { route: "/codemode" })(
        request,
        env,
        ctx
      );
    }

    return new Response("Not found. Use /mcp or /codemode", { status: 404 });
  }
};