Skip to main content
Glama
ARCHITECTURE.md10.2 kB
# MCP Code Mode 架构说明 ## 🎯 设计理念 基于 Cloudflare 和 Anthropic 的 code-mode 最佳实践,本项目采用"代码即工具"架构: - 将 MCP 工具转换为 TypeScript API - 在沙箱中执行用户代码 - 通过内置工具实现完整功能(无外部依赖) --- ## 🏗️ 架构决策 ### **完全独立的 code-mode 服务器** | 功能 | 实现位置 | 技术方案 | |------|----------|---------| | **Filesystem 工具** | code-mode 内置 | ✅ 直接文件系统访问 | | **Fetch 工具** | code-mode 内置 | ✅ srt 代理 + https-proxy-agent | | **代码执行** | Node.js fork + IPC | ✅ 进程隔离 | | **网络隔离** | Anthropic srt | ✅ 域名白名单 + 代理控制 | ### **核心问题与解决方案** #### **问题 1: stdio 冲突** code-mode 作为 MCP server 使用 stdio 与 Claude Code 通信,无法再 fork 子 MCP server(它们也需要 stdio)。 **解决方案**:内置工具(Builtin Tools) - 在 `src/builtin-tools.ts` 中直接实现 MCP 工具 - 实现标准 MCP Client 接口(`callTool`, `listTools`) - 无需 stdio,直接在进程内调用 #### **问题 2: Node.js 不遵守代理环境变量** srt 通过环境变量(`HTTPS_PROXY`)提供代理,但 Node.js 的 `https.get()` 默认不使用这些变量。 **解决方案**:https-proxy-agent ```typescript // 检查 srt 设置的代理环境变量 const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY; if (proxyUrl && isHttps) { const { HttpsProxyAgent } = await import('https-proxy-agent'); agent = new HttpsProxyAgent(proxyUrl); } // 使用 agent 发起请求 client.get(url, { agent }, callback); ``` **官方文档确认**: > "may be ignored by programs that don't respect these variables, leading to them being unable to connect to the internet" > > — [sandbox-runtime README](https://github.com/anthropic-experimental/sandbox-runtime) ### **最终架构优势** ✅ **完全独立** - code-mode 自包含所有功能,无需依赖 Claude Code 的 MCP 工具 ✅ **功能完整** - Filesystem (10 工具) + Fetch (1 工具) ✅ **安全隔离** - srt 提供 OS 级沙箱 + 域名白名单 ✅ **性能优秀** - 内置工具无 IPC 开销 --- ## 📊 工具可用性 ### **code-mode 内置工具(11/11)** #### **Filesystem 工具(10)** ```typescript import * as fs from "./servers/filesystem/index.js"; // ✅ 所有这些工具都在沙箱中直接可用 await fs.readFile({ path: "./file.txt" }); await fs.writeFile({ path: "./output.txt", content: "data" }); await fs.listDirectory({ path: "./src" }); await fs.createDirectory({ path: "./workspace" }); await fs.moveFile({ source: "./a.txt", destination: "./b.txt" }); await fs.searchFiles({ path: "./src", pattern: ".*\\.ts$" }); await fs.getFileInfo({ path: "./README.md" }); await fs.readMultipleFiles({ paths: ["a.txt", "b.txt"] }); await fs.listAllowedDirectories(); await fs.editFile({ path: "./config.json", edits: [...] }); ``` #### **Fetch 工具(1)** ```typescript import * as fetch from "./servers/fetch/index.js"; // ✅ 通过 srt 代理自动访问白名单域名 const result = await fetch.fetch({ url: "https://httpbin.org/ip", max_length: 50000 // 可选 }); // result = { content: [{ type: 'text', text: '...' }] } ``` **网络访问规则**: - 仅允许访问 `.srt-settings.json` 中的 `allowedDomains` - srt 自动启动代理(监听 localhost 随机端口) - https-proxy-agent 自动使用 `HTTPS_PROXY` 环境变量 --- ## 🔧 实际工作流示例 ### **示例 1:获取 GitHub API 数据并保存** ```typescript import * as fs from "./servers/filesystem/index.js"; import * as fetch from "./servers/fetch/index.js"; // 步骤 1:获取数据(通过 srt 代理) const response = await fetch.fetch({ url: "https://api.github.com/repos/anthropics/claude-code" }); const repoData = JSON.parse(response.content[0].text); // 步骤 2:在沙箱中处理和保存 const report = ` # GitHub 仓库报告 - 名称: ${repoData.name} - Stars: ${repoData.stargazers_count} - Forks: ${repoData.forks_count} - 生成时间: ${new Date().toISOString()} `; await fs.writeFile({ path: "./github-report.md", content: report }); console.log("✅ 报告已保存"); ``` ### **示例 2:批量文件处理** ```typescript import * as fs from "./servers/filesystem/index.js"; // 读取所有 TypeScript 文件 const srcFiles = JSON.parse( await fs.listDirectory({ path: "./src" }) ); const tsFiles = srcFiles .filter(f => f.name.endsWith('.ts') && f.type === 'file') .map(f => `./src/${f.name}`); // 批量读取 const contents = await fs.readMultipleFiles({ paths: tsFiles }); const files = JSON.parse(contents); // 统计代码行数 const stats = files.map(file => ({ name: file.path.split('/').pop(), lines: file.content ? file.content.split('\n').length : 0 })); // 保存统计结果 await fs.writeFile({ path: "./code-stats.json", content: JSON.stringify(stats, null, 2) }); console.log(`✅ 已统计 ${stats.length} 个文件`); ``` ### **示例 3:混合使用 Filesystem + Fetch** ```typescript import * as fs from "./servers/filesystem/index.js"; import * as fetch from "./servers/fetch/index.js"; // 从远程获取配置 const configResponse = await fetch.fetch({ url: "https://raw.githubusercontent.com/user/repo/main/config.json" }); const remoteConfig = JSON.parse(configResponse.content[0].text); // 读取本地配置 const localConfigContent = await fs.readFile({ path: "./config.json" }); const localConfig = JSON.parse(localConfigContent); // 合并配置 const mergedConfig = { ...localConfig, ...remoteConfig }; // 保存合并后的配置 await fs.writeFile({ path: "./config.merged.json", content: JSON.stringify(mergedConfig, null, 2) }); console.log("✅ 配置已合并"); ``` --- ## 📚 对比:Cloudflare 的实现 Cloudflare 的 code-mode 使用 **Workers isolates** 实现沙箱: ``` Cloudflare Workers 平台 ↓ V8 Isolate (毫秒级启动) ↓ 用户代码 + MCP API Bindings ``` **我们的实现:** ``` Claude Code (MCP Host) ↓ stdio code-mode server (srt 沙箱) ↓ fork + IPC 用户代码 (沙箱子进程) ↓ 内置工具 Filesystem API + Fetch API (via srt 代理) ``` **区别**: - Cloudflare 用 V8 isolates(更快,但需要 Workers 平台) - 我们用 Node.js fork(通用性更好,跨平台) - 网络访问:Cloudflare 用 binding,我们用 srt 代理 + https-proxy-agent --- ## 🚀 快速开始 ### **1. 安装依赖** ```bash npm install ``` ### **2. 生成 API** ```bash npx tsx examples/generate-api-builtin.ts ``` ### **3. 构建项目** ```bash npm run build ``` ### **4. 配置 Claude Desktop** 编辑配置文件: - **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json` - **Windows**: `%APPDATA%\\Claude\\claude_desktop_config.json` - **Linux**: `~/.config/Claude/claude_desktop_config.json` 添加 code-mode(使用 srt wrapper): ```json { "mcpServers": { "code-mode": { "command": "npx", "args": [ "@anthropic-ai/srt", "run", "node", "/绝对路径/mcp-code-mode-demo/dist/server.js" ], "description": "Code Mode - TypeScript 沙箱执行,内置 filesystem 和 fetch 工具(通过 srt 代理实现网络访问)。", "env": { "NODE_OPTIONS": "--no-warnings" } } } } ``` **注意:** 替换为你的实际项目绝对路径! ### **5. 配置网络白名单** 编辑 `.srt-settings.json`,添加需要访问的域名: ```json { "network": { "allowedDomains": [ "httpbin.org", "*.github.com", "api.github.com", "你的域名.com" ] } } ``` ### **6. 重启 Claude Desktop** 完全退出并重新启动即可使用。 --- ## 🔍 验证安装 重启后,测试 filesystem + fetch 工具: ```typescript import * as fs from "./servers/filesystem/index.js"; import * as fetch from "./servers/fetch/index.js"; // 测试 Filesystem const content = await fs.readFile({ path: "./package.json" }); const pkg = JSON.parse(content); console.log(`项目: ${pkg.name} v${pkg.version}`); // 测试 Fetch(需要在 .srt-settings.json 中添加 httpbin.org) const response = await fetch.fetch({ url: "https://httpbin.org/ip" }); console.log(`IP 信息: ${response.content[0].text}`); ``` --- ## 📝 技术细节 ### **内置工具实现(src/builtin-tools.ts)** ```typescript export interface BuiltinMCPClient { callTool(params: { name: string; arguments: any }): Promise<any>; listTools(): Promise<{ tools: Array<{...}> }>; } // FilesystemTools: 实现 10 个文件系统工具 // FetchTools: 实现 1 个 fetch 工具(带 https-proxy-agent) export function createBuiltinTools(): Map<string, BuiltinMCPClient> { return new Map([ ['filesystem', new FilesystemTools()], ['fetch', new FetchTools()] ]); } ``` ### **代理支持(FetchTools.fetch 方法)** ```typescript const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY; let agent = undefined; if (proxyUrl && isHttps) { const { HttpsProxyAgent } = await import('https-proxy-agent'); agent = new HttpsProxyAgent(proxyUrl); } const req = client.get(url, { agent }, callback); ``` ### **srt 沙箱配置(.srt-settings.json)** - **network.allowedDomains**: 白名单域名列表 - **filesystem.denyRead**: 禁止读取的敏感目录(如 `~/.ssh`) - **filesystem.allowWrite**: 允许写入的目录 - **enableWeakerNestedSandbox**: 启用嵌套沙箱支持(fork 子进程) --- ## ⚠️ 注意事项 1. **Filesystem 工具** - 完全可用,受 `.srt-settings.json` 文件系统规则限制 2. **Fetch 工具** - 仅能访问白名单域名(需在 `.srt-settings.json` 中配置) 3. **安全性** - srt 提供 OS 级沙箱隔离,强制执行网络和文件系统策略 4. **性能** - 内置工具性能优秀,fetch 通过 localhost 代理无明显延迟 5. **跨平台** - srt 支持 macOS(sandbox-exec)和 Linux(bubblewrap),Windows 需 WSL --- ## 🤝 贡献 欢迎提交 Issue 和 Pull Request! --- ## 📄 许可证 MIT License - 详见 LICENSE 文件

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cexll/code-mode-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server