Model Context Protocol (MCP) Server auf Cloudflare Workers erstellen: Edge AI Agent Tools Praxisguide

Das Model Context Protocol (MCP) von Anthropic hat sich rasch als offener Standard für die Verbindung von KI-Agenten-Hosts (wie Claude Desktop, Cursor oder Zed) mit externen Tools und Datenquellen etabliert.
In diesem Artikel zeigen wir, wie Sie SSE-basierte MCP-Server auf Cloudflare Workers bereitstellen, um weltweite Latenzen unter 10ms ohne Cold Starts zu erreichen.
Wichtige Erkenntnisse
- stdio vs. SSE/HTTP: Für lokale Tests ist
stdiopraktisch, für Mehrbenutzerumgebungen und Produktion ist SSE (Server-Sent Events) erforderlich.- Edge-Vorteile: V8 Isolates verhindern Cold Starts und antworten weltweit in 10-30ms.
- D1 & KV Integration: Ermöglicht KI-Agenten die direkte Abfrage von Cloudflare D1 (SQLite) Datenbanken am Edge.
MCP Server auf Cloudflare Workers implementieren
import { Hono } from "hono";
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
type Bindings = { DB: D1Database; AUTH_TOKEN: string };
const app = new Hono<{ Bindings: Bindings }>();
const activeTransports = new Map<string, SSEServerTransport>();
function createMcpServer(db: D1Database) {
const server = new Server(
{ name: "effidev-edge-mcp", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "query_user_stats",
description: "Fragt Nutzungsstatistiken aus der D1 Edge-Datenbank ab.",
inputSchema: {
type: "object",
properties: { startDate: { type: "string" }, limit: { type: "number" } },
required: ["startDate"]
}
}
]
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === "query_user_stats") {
const startDate = String(request.params.arguments?.startDate);
const limit = Number(request.params.arguments?.limit ?? 10);
const { results } = await db
.prepare("SELECT user_id, status, amount FROM transactions WHERE created_at >= ? LIMIT ?")
.bind(startDate, limit)
.all();
return {
content: [{ type: "text", text: JSON.stringify({ success: true, data: results }) }]
};
}
throw new Error(`Unbekanntes Tool: ${request.params.name}`);
});
return server;
}
app.get("/sse", async (c) => {
const sessionId = crypto.randomUUID();
const { readable, writable } = new TransformStream();
const writer = writable.getWriter();
const transport = new SSEServerTransport(`/message?sessionId=${sessionId}`, {
write: async (message) => {
await writer.write(new TextEncoder().encode(`data: ${JSON.stringify(message)}\n\n`));
},
close: async () => {
await writer.close();
activeTransports.delete(sessionId);
}
});
activeTransports.set(sessionId, transport);
const server = createMcpServer(c.env.DB);
server.connect(transport);
return new Response(readable, {
headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache" }
});
});
app.post("/message", async (c) => {
const sessionId = c.req.query("sessionId");
if (!sessionId || !activeTransports.has(sessionId)) return c.text("Sitzung nicht gefunden", 404);
const transport = activeTransports.get(sessionId)!;
await transport.handlePostMessage(c.req.raw, c.res.raw, await c.req.json());
return c.text("Accepted", 202);
});
export default app;
Fazit
Die Bereitstellung von MCP auf Cloudflare Workers verwandelt lokale Skripte in skalierbare, hochverfügbare KI-Infrastruktur für Unternehmen.