从零封装MCP Server:让WorkBuddy 听懂你的工具
从零封装 MCP Server:让 WorkBuddy 听懂你的工具
什么是 MCP
MCP(Model Context Protocol)是 Anthropic 推出的开放协议,让 AI 助手(如 WorkBuddy)可以安全地调用外部工具和API。它就像一个标准化的"插件接口"——你写好服务端,AI 就能自动发现并调用你的工具。
核心概念只有三个:
- Server:你写的服务端程序,注册了多个工具
- Tool:一个具体的功能(比如「发帖到知乎」)
- Transport:通信方式(通常是 stdio——标准输入输出)
第一步:项目结构
一个最小化的 MCP Server 只需要三个文件:
my-mcp-server/ ├── package.json # 依赖声明 ├── tsconfig.json # TS 编译配置 └── src/ └── index.ts # MCP Server 入口第二步:package.json
{"name":"my-mcp-server","version":"1.0.0","type":"module","scripts":{"build":"tsc","start":"node dist/index.js"},"dependencies":{"@modelcontextprotocol/sdk":"^1.0.4"},"devDependencies":{"typescript":"^5.7.2","@types/node":"^22.10.0"}}第三步:编写 Server
import{Server}from'@modelcontextprotocol/sdk/server/index.js';import{StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import{CallToolRequestSchema,ListToolsRequestSchema}from'@modelcontextprotocol/sdk/types.js';// 1. 创建 Serverconstserver=newServer({name:'my-mcp',version:'1.0.0'},{capabilities:{tools:{}}});// 2. 注册工具列表server.setRequestHandler(ListToolsRequestSchema,async()=>({tools:[{name:'hello_world',description:'打个招呼',inputSchema:{type:'object',properties:{name:{type:'string',description:'你的名字'}},required:['name']}}]}));// 3. 处理工具调用server.setRequestHandler(CallToolRequestSchema,async(req)=>{const{name,arguments:args}=req.params;if(name==='hello_world'){return{content:[{type:'text',text:`你好,${args.name}!`}]};}return{content:[{type:'text',text:'未知工具'}],isError:true};});// 4. 启动consttransport=newStdioServerTransport();awaitserver.connect(transport);console.error('MCP Server started');第四步:注册到 WorkBuddy
编译后在~/.workbuddy/mcp.json中添加:
{"mcpServers":{"my-mcp":{"command":"node","args":["/path/to/my-mcp-server/dist/index.js"],"cwd":"/path/to/my-mcp-server","disabled":false}}}重启 WorkBuddy,去连接器面板点「信任」,你的工具就生效了。
进阶:接入 Playwright 浏览器
实际项目中,你通常需要在 MCP Server 里集成浏览器自动化。以下是一个带 Playwright 的完整示例:
import{chromium}from'playwright';// 在工具处理中启动浏览器server.setRequestHandler(CallToolRequestSchema,async(req)=>{const{name,arguments:args}=req.params;if(name==='screenshot'){constbrowser=awaitchromium.launch({headless:true});constpage=awaitbrowser.newPage();awaitpage.goto(args.url);constpath=`screenshot-${Date.now()}.png`;awaitpage.screenshot({path});awaitbrowser.close();return{content:[{type:'text',text:`截图已保存:${path}`}]};}return{content:[{type:'text',text:'未知工具'}],isError:true};});关键踩坑
- ESM 模式:MCP SDK 只支持 ESM,package.json 必须声明
"type": "module" - stdio 通信:不要用 console.log 输出普通内容,用 console.error 输出日志(stdout 被 MCP 协议占用)
- 路径问题:mcp.json 里的 command 和 args 要用绝对路径,
~/不会被展开 - 端口冲突:stdio 模式下不存在端口冲突问题
最佳实践
| 维度 | 建议 |
|---|---|
| 错误处理 | 每个工具调用都要 try/catch,返回友好的错误信息 |
| 超时控制 | 网络请求设置 30s 超时,避免 WorkBuddy 卡死 |
| 日志 | 用 console.error 输出操作日志,方便排查 |
| 版本管理 | SemVer 严格管理,工具定义变更时更新 schema |
| 测试 | 用 `echo ‘{“method”:“tools/list”}’ |
总结
从零到一封装一个 MCP Server 只需要 4 步:
- 创建项目 → 安装 SDK
- 写 index.ts → 注册工具 + 处理调用
- 配置 mcp.json → 让 WorkBuddy 识别
- 去连接器面板点「信任」
完整代码约 50 行,半小时能跑通。真正花时间的是工具本身的业务逻辑
