MCP服务器云端转换工具

将本地MCP服务器转换为基于Cloudflare Workers的远程HTTP/SSE端点的解决方案,提供边缘计算部署、安全认证和协议转换功能。

  • by forayconsulting
  • TypeScript
  • 本地部署
  • 5 星标
  • 更新于
在 GitHub 查看 forayconsulting/cloudflare_mcp

使用说明

将本地MCP服务器转换为远程Cloudflare Workers端点

此README提供了一个全面的转换指南 任何 从stdio传输到在Cloudflare Workers上运行的完全远程HTTP/SSE端点的本地模型上下文协议(MCP)服务器。

目录

  1. 概述
  2. 先决条件
  3. 分步转换指南
  4. 需要进行关键转换
  5. 身份验证和秘密管理
  6. 测试和验证
  7. 生产部署
  8. 常见陷阱和解决方案
  9. 故障排除

概述

我们正在取得的成就

将本地MCP服务器(使用stdio传输进行Claude Desktop集成)转换为远程HTTP端点,该端点:

  • 在Cloudflare Workers边缘基础架构上运行
  • 使用HTTP/SSE(服务器发送事件)传输而不是stdio
  • 保持所有原始MCP工具功能
  • 通过Cloudflare机密安全地处理身份验证
  • Claude Desktop或任何MCP客户端都可以通过HTTP访问

为什么进行此转换

  • 可扩展性:具有全球分布的边缘部署
  • 可靠性:Cloudflare的基础设施和正常运行时间
  • 成本:适用于大多数用例的慷慨免费层
  • 演出:通过边缘计算减少延迟
  • 共享:允许团队访问您的MCP工具
  • 无状态:不需要本地依赖关系或环境设置

先决条件

所需帐户和工具

  1. Cloudflare帐户 (免费等级足够)
  2. 牧马人CLI 全局安装: npm install -g wrangler
  3. Node.js 18+促进地方发展
  4. Git 用于版本控制
  5. 正在工作的本地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.复制你的业务逻辑

  1. 将现有的MCP工具和业务逻辑复制到新项目中
  2. 创建适当的目录结构(例如。, src/api/, src/tools/, src/types/)
  3. 更新导入路径以使用以结尾的相对导入 .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

安全最佳实践

  1. 永远不要泄露秘密 到版本控制
  2. 使用不同的值 开发与生产
  3. 定期轮换机密
  4. 使用最小特权原则 用于API访问
  5. 监控秘密使用情况 通过 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

部署后验证

  1. 端点可访问性:测试所有HTTP端点
  2. 工具功能:验证每个MCP工具是否正常工作
  3. 认证:确认可以访问机密
  4. 错误处理:测试错误场景
  5. 演出:检查响应时间和资源使用情况

常见陷阱和解决方案

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.jsonc durable_objects配置

调试技术

  1. 地方发展:使用 console.log 和 wrangler dev
  2. 生产日志:使用 wrangler tail 用于实时日志
  3. MCP检查员:隔离测试工具
  4. 克劳德桌面:测试端到端集成

性能优化

  1. 最小化捆绑包大小:只导入您需要的内容
  2. 使用边缘缓存:尽可能缓存静态响应
  3. 优化API调用:在可行的情况下批量处理请求
  4. 监视器使用情况:使用Cloudflare分析

结论

本指南提供了一个完整的框架,用于将任何本地MCP服务器转换为远程Cloudflare Workers端点。关键是要理解Node.js和Workers运行时环境之间的根本区别,正确处理身份验证,并在更改传输层时维护MCP协议语义。

如需额外支持:

请记住,这种转换为在全球范围内扩展、共享和部署MCP工具开辟了新的可能性,同时保持了用户期望从本地实现中获得的相同功能。