使用说明
将本地MCP服务器转换为远程Cloudflare Workers端点
此README提供了一个全面的转换指南 任何 从stdio传输到在Cloudflare Workers上运行的完全远程HTTP/SSE端点的本地模型上下文协议(MCP)服务器。
目录
概述
我们正在取得的成就
将本地MCP服务器(使用stdio传输进行Claude Desktop集成)转换为远程HTTP端点,该端点:
- 在Cloudflare Workers边缘基础架构上运行
- 使用HTTP/SSE(服务器发送事件)传输而不是stdio
- 保持所有原始MCP工具功能
- 通过Cloudflare机密安全地处理身份验证
- Claude Desktop或任何MCP客户端都可以通过HTTP访问
为什么进行此转换
- 可扩展性:具有全球分布的边缘部署
- 可靠性:Cloudflare的基础设施和正常运行时间
- 成本:适用于大多数用例的慷慨免费层
- 演出:通过边缘计算减少延迟
- 共享:允许团队访问您的MCP工具
- 无状态:不需要本地依赖关系或环境设置
先决条件
所需帐户和工具
- Cloudflare帐户 (免费等级足够)
- 牧马人CLI 全局安装:
npm install -g wrangler - Node.js 18+促进地方发展
- Git 用于版本控制
- 正在工作的本地MCP服务器 (你的起点)
所需知识
- 对MCP协议的基本理解
- 熟悉Types/JavaScript
- HTTP和WebSockets/SSE的基本知识
- 了解环境变量和秘密
分步转换指南
步骤1:初始化Cloudflare Workers项目
从Cloudflare的官方MCP模板开始:
# Create new project from template
npm create cloudflare@latest your-mcp-server-name -- --template https://github.com/cloudflare/mcp-server-template
# Navigate to project
cd your-mcp-server-name
# Install dependencies
npm install
# Authenticate with Cloudflare
wrangler auth login
第二步:项目结构设置
你的新项目应该有这样的结构:
your-mcp-server-name/
├── src/
│ └── index.ts # Main entry point
├── package.json
├── wrangler.jsonc # Cloudflare configuration
├── tsconfig.json
├── worker-configuration.d.ts # Type definitions
└── .dev.vars # Local environment variables
步骤3:转换本地MCP服务器
A.复制你的业务逻辑
- 将现有的MCP工具和业务逻辑复制到新项目中
- 创建适当的目录结构(例如。,
src/api/,src/tools/,src/types/) - 更新导入路径以使用以结尾的相对导入
.js(工人要求)
B.替换模板的主文件
替换默认值 src/index.ts 使用您的MCP服务器逻辑。以下是基本结构:
import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
// Import your business logic
import { YourAPIClient } from "./api/client.js";
import type { YourConfig } from "./types/config.js";
export class YourMCP extends McpAgent {
server = new McpServer({
name: "your-mcp-server",
version: "1.0.0",
description: "Your MCP server description"
});
private client!: YourAPIClient;
private workerEnv!: Env;
constructor(state: DurableObjectState, env: Env) {
super(state, env);
this.workerEnv = env;
}
async init() {
// Initialize your client with environment variables
if (this.workerEnv) {
const config = this.loadConfig(this.workerEnv);
this.client = new YourAPIClient(config);
}
// Register your tools
this.server.tool("your_tool_name", {
param1: z.string().describe("Description"),
param2: z.number().optional().describe("Optional parameter"),
}, async ({ param1, param2 }) => {
try {
if (!this.client) {
return {
content: [{
type: "text",
text: "Client not initialized. Check environment variables."
}],
isError: true
};
}
const result = await this.client.yourMethod(param1, param2);
return {
content: [{
type: "text",
text: JSON.stringify(result, null, 2)
}]
};
} catch (error: any) {
return {
content: [{
type: "text",
text: `Error: ${error.message}`
}],
isError: true
};
}
});
}
private loadConfig(env: Env): YourConfig {
const config = {
apiKey: env.YOUR_API_KEY || '',
apiSecret: env.YOUR_API_SECRET || '',
// ... other config values
};
// Validate required configuration
const missing = Object.entries(config)
.filter(([_, value]) => !value)
.map(([key, _]) => key);
if (missing.length > 0) {
throw new Error(`Missing required environment variables: ${missing.join(', ')}`);
}
return config;
}
}
// Export default handler for Cloudflare Workers
export default {
fetch(request: Request, env: Env, ctx: ExecutionContext) {
const url = new URL(request.url);
if (url.pathname === "/sse" || url.pathname === "/sse/message") {
return YourMCP.serveSSE("/sse").fetch(request, env, ctx);
}
if (url.pathname === "/mcp") {
return YourMCP.serve("/mcp").fetch(request, env, ctx);
}
return new Response("Your MCP Server - Available endpoints: /sse, /mcp", {
status: 200,
headers: { "Content-Type": "text/plain" }
});
},
};
步骤4:配置牧马人
更新 wrangler.jsonc 针对您的特定需求:
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "your-mcp-server",
"main": "src/index.ts",
"compatibility_date": "2025-03-10",
"compatibility_flags": ["nodejs_compat"],
"migrations": [
{
"new_sqlite_classes": ["YourMCP"],
"tag": "v1"
}
],
"durable_objects": {
"bindings": [
{
"class_name": "YourMCP",
"name": "MCP_OBJECT"
}
]
},
"observability": {
"enabled": true
}
}
步骤5:更新类型定义
更新 worker-configuration.d.ts 环境接口:
declare namespace Cloudflare {
interface Env {
// Your environment variables
YOUR_API_KEY: string;
YOUR_API_SECRET: string;
// ... add all your required env vars
// Durable Object binding
MCP_OBJECT: DurableObjectNamespace<import("./src/index").YourMCP>;
}
}
interface Env extends Cloudflare.Env {}
需要进行关键转换
1.传输层:stdio→ HTTP/SSE
之前(stdio):
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
const server = new Server(/* ... */);
const transport = new StdioServerTransport();
await server.connect(transport);
之后(HTTP/SSE):
import { McpAgent } from "agents/mcp";
export class YourMCP extends McpAgent {
// Agent framework handles HTTP/SSE transport automatically
}
2.密码学:Node.js加密→ 网络加密API
之前(Node.js):
import crypto from 'crypto';
function createSignature(data: string, secret: string): string {
return crypto
.createHmac('sha256', secret)
.update(data)
.digest('base64');
}
之后(网络加密):
async function createSignature(data: string, secret: string): Promise<string> {
const encoder = new TextEncoder();
const key = await crypto.subtle.importKey(
'raw',
encoder.encode(secret),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
);
const signature = await crypto.subtle.sign(
'HMAC',
key,
encoder.encode(data)
);
return btoa(String.fromCharCode(...new Uint8Array(signature)));
}
3.HTTP客户端:axios/node获取→ fetch
之前(轴):
import axios from 'axios';
const response = await axios.post(url, data, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
在(fetch)之后:
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const result = await response.json();
4.环境变量:process.env→ env参数
之前(Node.js):
const apiKey = process.env.API_KEY;
const config = {
apiKey: apiKey || '',
// ...
};
之后(工人):
// In your MCP class
private loadConfig(env: Env): YourConfig {
const config = {
apiKey: env.API_KEY || '',
// ...
};
return config;
}
身份验证和秘密管理
本地开发(.dev.vars)
创建 .dev.vars 本地开发文件:
# .dev.vars (never commit this file)
YOUR_API_KEY=your_local_api_key
YOUR_API_SECRET=your_local_api_secret
OTHER_CONFIG=your_local_value
生产秘密(Cloudflare)
使用Wrangler设置生产秘密:
# Set each secret individually
wrangler secret put YOUR_API_KEY
# Enter your production API key when prompted
wrangler secret put YOUR_API_SECRET
# Enter your production API secret when prompted
# Verify secrets are set
wrangler secret list
安全最佳实践
- 永远不要泄露秘密 到版本控制
- 使用不同的值 开发与生产
- 定期轮换机密
- 使用最小特权原则 用于API访问
- 监控秘密使用情况 通过 Cloudflare 仪表板
测试和验证
局部测试
# Start local development server
npm run dev
# or
wrangler dev
# Test endpoints
curl http://localhost:8787/sse
curl http://localhost:8787/mcp
Claude桌面集成
添加到您的Claude Desktop配置(claude_desktop_config.json):
{
"mcpServers": {
"your-server-name": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/inspector"],
"env": {
"MCP_SERVER_URL": "http://localhost:8787/sse"
}
}
}
}
生产测试
# Deploy to production
npm run deploy
# or
wrangler deploy
# Test production endpoints
curl https://your-mcp-server.your-subdomain.workers.dev/sse
curl https://your-mcp-server.your-subdomain.workers.dev/mcp
更新生产环境下的Claude Desktop配置:
{
"mcpServers": {
"your-server-name": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/inspector"],
"env": {
"MCP_SERVER_URL": "https://your-mcp-server.your-subdomain.workers.dev/sse"
}
}
}
}
生产部署
部署前检查表
- []Cloudflare中配置的所有机密
- [ ]
.dev.vars已从部署中排除(检查.gitignore) - []环境特定配置已验证
- []类型检查通行证(
npm run type-check) - []本地测试已成功完成
- []牧马人配置已审核
部署命令
# Type check
npm run type-check
# Deploy to production
wrangler deploy
# Monitor deployment
wrangler tail
# View logs (if issues occur)
wrangler tail --format=pretty
部署后验证
- 端点可访问性:测试所有HTTP端点
- 工具功能:验证每个MCP工具是否正常工作
- 认证:确认可以访问机密
- 错误处理:测试错误场景
- 演出:检查响应时间和资源使用情况
常见陷阱和解决方案
1.导入路径问题
问题: Module not found Workers中的错误
解决方案:使用以结尾的相对进口 .js:
// Wrong
import { Client } from './api/client';
// Correct
import { Client } from './api/client.js';
2.环境变量访问
问题: undefined 环境变量的值
解决方案:确保正确的构造函数模式:
export class YourMCP extends McpAgent {
private workerEnv!: Env;
constructor(state: DurableObjectState, env: Env) {
super(state, env);
this.workerEnv = env; // Store env for later use
}
}
3.加密API差异
问题: crypto.createHmac is not a function
解决方案:使用具有异步/等待模式的Web Crypto API(请参阅转换部分)
4.获取响应处理
问题:假设成功响应而不进行检查
解决方案:始终检查 response.ok:
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
5.耐用对象配置
问题: DurableObject binding not found
解决方案:确保牧马人配置与类导出匹配:
// wrangler.jsonc
"durable_objects": {
"bindings": [
{
"class_name": "YourMCP", // Must match exported class name
"name": "MCP_OBJECT"
}
]
}
6.MCP检查员vs克劳德桌面
问题:测试工具之间的混淆 解决方案:
- MCP检查员:用于开发/调试(显示原始MCP协议)
- 克劳德桌面:用于最终用户体验(自然集成工具)
故障排除
常见错误消息和解决方案
“NetSuite客户端未初始化”
- 原因:无法访问环境变量
- 解决方案:检查构造函数模式和秘密配置
“找不到模块”
- 原因:导入路径不正确
- 解决方案:使用
.js进口延期
“获取失败”
- 原因:网络/API问题
- 解决方案:添加适当的错误处理和重试逻辑
“未找到DurableObject绑定”
- 原因:牧马人配置不匹配
- 解决方案:验证
wrangler.jsoncdurable_objects配置
调试技术
- 地方发展:使用
console.log和wrangler dev - 生产日志:使用
wrangler tail用于实时日志 - MCP检查员:隔离测试工具
- 克劳德桌面:测试端到端集成
性能优化
- 最小化捆绑包大小:只导入您需要的内容
- 使用边缘缓存:尽可能缓存静态响应
- 优化API调用:在可行的情况下批量处理请求
- 监视器使用情况:使用Cloudflare分析
结论
本指南提供了一个完整的框架,用于将任何本地MCP服务器转换为远程Cloudflare Workers端点。关键是要理解Node.js和Workers运行时环境之间的根本区别,正确处理身份验证,并在更改传输层时维护MCP协议语义。
如需额外支持:
请记住,这种转换为在全球范围内扩展、共享和部署MCP工具开辟了新的可能性,同时保持了用户期望从本地实现中获得的相同功能。
