examples/MCP-tools Public
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";import { createServer } from "./server.ts";import { MAX_DAYS, countCall, readUsage } from "./usage.ts";export type Env = { USAGE: KVNamespace };// Browser clients (the MCP Inspector in direct mode, web agents) call /mcp cross-origin. The// server has no cookies or credentials to protect, so any origin may call it.const CORS = { "access-control-allow-origin": "*", "access-control-allow-methods": "GET, POST, OPTIONS", "access-control-allow-headers": "content-type, accept, authorization, mcp-protocol-version, mcp-session-id, last-event-id", "access-control-expose-headers": "mcp-session-id, mcp-protocol-version", "access-control-max-age": "86400",};export async function handle(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> { const { pathname } = new URL(request.url); if (pathname === "/mcp") return withCors(await serveMcp(request, env, ctx)); if (pathname === "/stats" && request.method === "GET") return withCors(await stats(request, env)); if (pathname === "/" && request.method === "GET") { return new Response("mcp-tools: an MCP server. Point a client at /mcp (Streamable HTTP).\n", { headers: { "content-type": "text/plain; charset=utf-8" } }); } return new Response("Not found\n", { status: 404 });}async function serveMcp(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> { if (request.method === "OPTIONS") return new Response(null, { status: 204 }); // Stateless: no sessions, so there is no standalone SSE stream to GET and nothing to DELETE. if (request.method !== "POST") { return Response.json( { jsonrpc: "2.0", error: { code: -32000, message: "This server is stateless: send JSON-RPC requests with POST." }, id: null }, { status: 405, headers: { allow: "POST, OPTIONS" } }, ); } // A fresh server and transport for every request. Nothing carries over, so any isolate can // answer any request and there is no session to lose. // Counting happens after the response is sent; a KV hiccup never fails a tool call. const server = createServer({ onCall: (tool, ok) => ctx.waitUntil(countCall(env.USAGE, tool, ok).catch((err) => console.error("usage count failed", err))), }); const transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: undefined, enableJsonResponse: true }); await server.connect(transport); return transport.handleRequest(request);}// GET /stats?days=7: calls and errors per tool for the last few UTC days, newest first.async function stats(request: Request, env: Env): Promise<Response> { const param = new URL(request.url).searchParams.get("days") ?? "7"; const days = Number(param); if (!Number.isInteger(days) || days < 1 || days > MAX_DAYS) { return Response.json({ error: `days must be a whole number from 1 to ${MAX_DAYS}` }, { status: 400 }); } return Response.json({ days: await readUsage(env.USAGE, days) }, { headers: { "cache-control": "no-store" } });}function withCors(response: Response): Response { const out = new Response(response.body, response); for (const [k, v] of Object.entries(CORS)) out.headers.set(k, v); return out;}