Cloudflare Workers에서 Model Context Protocol(MCP) 서버 구축하기: 에지 AI 에이전트 도구 개발 실전

Anthropic이 공개한 Model Context Protocol(MCP) 은 Claude Desktop, Cursor, Zed와 같은 AI 에이전트 환경과 외부 데이터소스·도구를 연결하는 사실상의 오픈 표준으로 자리잡았습니다. 하지만 기존 MCP 서버 가이드 대부분은 stdio 통신을 기반으로 한 로컬 Node.js/Python 프로세스 실행에 집중되어 있어, 팀 단위의 원격 공유 도구나 프로덕션 에이전트 서비스로 확장할 때 한계에 부딪힙니다.
이 글에서는 Cloudflare Workers를 활용해 Server-Sent Events(SSE) 트랜스포트 기반의 MCP 서버를 에지(Edge)에 배포하는 실전 패턴을 다룹니다. Cloudflare의 300여 개 글로벌 데이터센터 인프라를 활용하여 Cold Start 없는 10ms 미만 지연시간의 에이전트 도구를 구축하는 방법부터, D1 데이터베이스 연동, OAuth/Bearer 토큰 보안 처리까지 코드로 살펴봅니다.
핵심 요약
- stdio vs SSE/HTTP: 로컬 개발에서는
stdio트랜스포트가 편리하지만, 멀티 사용자 지원 및 프로덕션 배포를 위해서는 HTTP 기반의 SSE(Server-Sent Events) 트랜스포트가 필수적입니다.- Cloudflare Workers의 이점: V8 isolate 기반 런타임으로 Cold Start가 없으며, 전 세계 300+ 에지 로케이션에서 10~30ms 내외의 응답 속도로 MCP 요청을 처리할 수 있습니다.
- D1 & KV 연동: Worker 내부에서 Cloudflare D1(SQLite) 및 KV(Key-Value)를 연동하여 에이전트가 직접 에지 데이터베이스를 조회가 가능하도록 MCP Tools를 정의할 수 있습니다.
- 보안 패턴: 인터넷에 노출되는 에지 MCP 서버는 Bearer Token 인증 미들웨어 및 Cloudflare Access를 결합하여 외부의 악의적 도구 호출을 차단해야 합니다.
MCP 트랜스포트 아키텍처: stdio에서 SSE로
MCP specification은 클라이언트(AI 에이전트 Host)와 서버간 메시지 교환을 위해 JSON-RPC 2.0 프로토콜을 사용합니다. 이때 물리적인 이동 통로는 크게 두 가지입니다.
| 항목 | stdio Transport | SSE / HTTP Transport |
|---|---|---|
| 운영 환경 | 로컬 개발 환경 (Client가 자식 프로세스 생성) | 프로덕션 / 에지 서버리스 |
| 연결 방식 | Standard Input / Output 스트림 | HTTP GET (SSE) + POST (JSON-RPC) |
| 네트워크 접근성 | 로컬 머신 내부로 제한 | 인터넷(HTTPS) 어디서나 접근 가능 |
| 확장성 | 싱글 유저 (로컬 CLI) | 멀티 유저 (인증/권한 부여 가능) |
| 적합한 백엔드 | Node.js, Python CLI | Cloudflare Workers, AWS Lambda, Fastify |
Cloudflare Workers 환경에서는 로컬 프로세스를 띄울 수 없으므로, HTTP 기반 SSE(Server-Sent Events) 패턴을 구현해야 합니다. 클라이언트는 /sse 엔드포인트로 GET 요청을 보내 실시간 이벤트 스트림을 유지하고, 전달받은 endpoint URL(예: /message?sessionId=...)로 POST 요청을 보내 툴 실행 및 리소스 조회를 요청합니다.
Cloudflare Workers용 MCP 서버 실전 구현
Official @modelcontextprotocol/sdk 패키지는 Web Standard API(Request/Response, ReadableStream)를 지원하므로 Cloudflare Workers 런타임에서 완벽히 동작합니다.
1. 프로젝트 설정 및 패키지 설치
npm init cloudflare@latest mcp-edge-worker -- --type=workers-types-typescript
cd mcp-edge-worker
npm install @modelcontextprotocol/sdk hono
wrangler.toml 파일에 D1 데이터베이스 바인딩을 추가합니다:
name = "mcp-edge-worker"
main = "src/index.ts"
compatibility_date = "2026-07-01"
[[d1_databases]]
binding = "DB"
database_name = "production-db"
database_id = "your-d1-database-id"
2. SSE 트랜스포트 및 MCP 핸들러 작성
아래는 Hono 프레임워크를 기반으로 MCP Server 및 SSEServerTransport를 Workers 환경에 연동한 코드입니다.
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 }>();
// 활성화된 SSE 트랜스포트 세션 관리 (인메모리 Map)
const activeTransports = new Map<string, SSEServerTransport>();
// MCP Server 인스턴스 팩토리 함수
function createMcpServer(db: D1Database) {
const server = new Server(
{ name: "effidev-edge-mcp", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
// 1. 도구 목록(Tools List) 정의
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "query_user_stats",
description: "D1 에지 DB에서 특정 기간 사용자의 결제 및 활동 통계를 조회합니다.",
inputSchema: {
type: "object",
properties: {
startDate: { type: "string", description: "YYYY-MM-DD" },
limit: { type: "number", description: "최대 조회 건수 (기본 10)" },
},
required: ["startDate"],
},
},
],
};
});
// 2. 도구 실행(Call Tool) 핸들러
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, count: results.length, data: results }),
},
],
};
}
throw new Error(`알 수 없는 도구: ${request.params.name}`);
});
return server;
}
// Bearer Token 인증 미들웨어
app.use("*", async (c, next) => {
const authHeader = c.req.header("Authorization");
if (!authHeader || authHeader !== `Bearer ${c.env.AUTH_TOKEN}`) {
return c.text("Unauthorized MCP Request", 401);
}
await next();
});
// SSE 연결 엔드포인트 (GET /sse)
app.get("/sse", async (c) => {
const sessionId = crypto.randomUUID();
// TransformStream을 사용하여 Web ReadableStream 기반 SSE 생성
const { readable, writable } = new TransformStream();
const writer = writable.getWriter();
const transport = new SSEServerTransport(`/message?sessionId=${sessionId}`, {
write: async (message) => {
const data = `data: ${JSON.stringify(message)}\n\n`;
await writer.write(new TextEncoder().encode(data));
},
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",
"Connection": "keep-alive",
},
});
});
// JSON-RPC 메시지 수신 엔드포인트 (POST /message)
app.post("/message", async (c) => {
const sessionId = c.req.query("sessionId");
if (!sessionId || !activeTransports.has(sessionId)) {
return c.text("Session not found or expired", 404);
}
const transport = activeTransports.get(sessionId)!;
const body = await c.req.json();
await transport.handlePostMessage(c.req.raw, c.res.raw, body);
return c.text("Accepted", 202);
});
export default app;
Claude Desktop & Cursor 클라이언트 연결 설정
Cloudflare Workers에 배포된 MCP 서버 URL이 https://mcp.effidev.workers.dev 라면, 클라이언트에 HTTP/SSE 트랜스포트를 등록해야 합니다.
Claude Desktop (claude_desktop_config.json)
최신 Claude Desktop은 SSE 트랜스포트를 직접 지원하거나, mcp-remote 브릿지 유틸리티를 활용해 연결할 수 있습니다.
{
"mcpServers": {
"effidev-edge-tools": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/inspector",
"--sse",
"https://mcp.effidev.workers.dev/sse",
"--header",
"Authorization: Bearer YOUR_SECURE_TOKEN"
]
}
}
}
벤치마크 및 비용 비교
기존 AWS Fargate나 Self-hosted Node.js 서버에 MCP 서버를 띄우는 것 대비 Cloudflare Workers 인프라의 효율성을 수치로 비교했습니다.
| 성능/비용 지표 | Node.js (AWS EC2 t4g.small) | Cloudflare Workers Paid |
|---|---|---|
| 월 고정 비용 | $14.60 (24/7 인스턴스) | $5.00 (월 1,000만 요청 포함) |
| TTFB (Time to First Byte) | 120ms ~ 280ms (리전 이격 발생) | 12ms ~ 35ms (글로벌 에지) |
| Cold Start 지연 | 없음 (항상 켜짐) | 0ms (V8 Isolate) |
| 최대 동시 SSE 커넥션 | 메모리 상한선 (약 2,000개) | Workers 수평 확장 (무제한급) |
결론: 에지 에이전트 인프라의 미래
Model Context Protocol(MCP)을 로컬 환경에 갇혀두지 않고 Cloudflare Workers 같은 글로벌 에지 인프라로 올리면, 팀원 누구나 공통의 데이터베이스와 내부 API 툴을 공유하는 ** enterprise-ready AI Agent 인프라**를 구축할 수 있습니다.
Cloudflare D1, KV, Vectorize와 같은 에지 렌더링 서버리스 스토리지와 MCP 표준을 조합하여 고성능 AI 에이전트 툴백엔드를 경험해 보세요.