前面两节我们用了乐享连接器——内置的,现成的,点启用就能用。但有些服务不在内置列表里:公司内部的 OA 系统、自建的 API 服务、第三方平台没有公开 MCP Server。这时就需要自己写一个 MCP Server。这一节手把手教你用 Python 写一个自定义连接器,把任意 API 接入 WorkBuddy。
1. 什么场景需要自定义 Server
内置连接器有 30+,覆盖了主流办公场景。但总有例外:
| 场景 | 内置连接器能解决吗 |
|---|---|
| 查公司内部 OA 审批流 | ❌ 没有 |
| 调自建的后台 API | ❌ 没有 |
| 接第三方平台(没有官方 MCP) | ❌ 没有 |
| 把脚本结果推送到特定渠道 | ⚠️ 部分可以 |
| 定时抓取某个网站数据 | ❌ 没有 |
这些场景的共同点:服务不在 WorkBuddy 内置列表里,但有 HTTP API。这时就需要自定义 MCP Server。
2. MCP Server 架构一览
MCP Server 本质是一个 HTTP 服务,遵循 JSON-RPC 2.0 协议:
WorkBuddy(MCP Client)
↓ JSON-RPC 请求
MCP Server(你写的)
↓ HTTP 调用
目标 API(OA/自建系统/第三方)
↓ JSON 响应
MCP Server
↓ JSON-RPC 响应
WorkBuddy → 展示给用户
一个 MCP Server 需要实现以下核心能力:
| 能力 | 说明 | 必须 |
|---|---|---|
| 工具列表(tools/list) | 返回 Server 提供哪些工具 | ✅ |
| 工具调用(tools/call) | 执行具体的工具调用 | ✅ |
| 资源列表(resources/list) | 返回可访问的资源(可选) | ❌ |
| 提示模板(prompts/list) | 预定义的提示词(可选) | ❌ |
WorkBuddy 通过这两个 JSON-RPC 接口发现和调用工具。 你只需要实现这两个接口,Server 就能工作。
3. 最简 Server:Python 实现
用一个完整的最小示例来演示。先装依赖:
pip install fastapi uvicorn
完整代码
# mcp_server.py
from fastapi import FastAPI, Request
from pydantic import BaseModel
from typing import Any, List, Dict, Optional
import uvicorn
import json
app = FastAPI()
# ========== 1. 工具定义 ==========
TOOLS = [
{
"name": "get_weather",
"description": "查询指定城市的天气",
"inputSchema": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,如北京、上海"
}
},
"required": ["city"]
}
},
{
"name": "send_notification",
"description": "发送通知消息",
"inputSchema": {
"type": "object",
"properties": {
"message": {"type": "string", "description": "通知内容"},
"channel": {"type": "string", "description": "渠道:email/sms/dingtalk"}
},
"required": ["message", "channel"]
}
}
]
# ========== 2. 工具实现 ==========
async def get_weather(city: str) -> Dict:
"""模拟天气查询"""
weather_map = {
"北京": {"temp": 28, "condition": "晴"},
"上海": {"temp": 32, "condition": "多云"},
"广州": {"type": 35, "condition": "雷阵雨"}
}
result = weather_map.get(city, {"temp": "未知", "condition": "未知"})
return {"city": city, "weather": result}
async def send_notification(message: str, channel: str) -> Dict:
"""模拟发送通知"""
return {
"status": "success",
"channel": channel,
"message": message,
"timestamp": "2026-07-13T10:00:00Z"
}
# ========== 3. JSON-RPC 入口 ==========
@app.post("/mcp")
async def mcp_endpoint(request: Request):
"""MCP 协议入口"""
body = await request.json()
method = body.get("method")
params = body.get("params", {})
request_id = body.get("id")
# 工具列表
if method == "tools/list":
return {
"jsonrpc": "2.0",
"id": request_id,
"result": {"tools": TOOLS}
}
# 工具调用
elif method == "tools/call":
tool_name = params.get("name")
tool_args = params.get("arguments", {})
if tool_name == "get_weather":
result = await get_weather(**tool_args)
elif tool_name == "send_notification":
result = await send_notification(**tool_args)
else:
result = {"error": f"Unknown tool: {tool_name}"}
return {
"jsonrpc": "2.0",
"id": request_id,
"result": {
"content": [
{
"type": "text",
"text": json.dumps(result, ensure_ascii=False)
}
]
}
}
return {"jsonrpc": "2.0", "id": request_id, "error": "Method not found"}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8080)
运行 Server
python mcp_server.py
# Server 启动在 http://localhost:8080/mcp
关键点解释
| 部分 | 作用 |
|---|---|
TOOLS 列表 | 声明 Server 提供哪些工具,供 WorkBuddy 发现 |
inputSchema | 定义每个工具的参数,AI 根据这个知道怎么调用 |
tools/list | WorkBuddy 启动时调用,获取工具列表 |
tools/call | 用户请求时调用,执行具体逻辑 |
| 返回格式 | content[].text 必须是 JSON 字符串,AI 会解析 |
4. 配置到 WorkBuddy
Server 写好了,怎么让 WorkBuddy 发现它?修改 ~/.workbuddy/mcp.json:
{
"mcpServers": {
"my-weather-server": {
"command": "python",
"args": ["D:/path/to/mcp_server.py"],
"url": "http://localhost:8080/mcp"
}
}
}
两种启动方式:
| 方式 | 说明 | 适用场景 |
|---|---|---|
| command + args | WorkBuddy 帮你启动进程 | 本地 Server |
| url | Server 自己运行,WorkBuddy 通过 HTTP 调用 | 远程 Server / 已运行的进程 |
配置完成后:
- 重启 WorkBuddy(或刷新连接器)
- 到"连接器管理"页,找到
my-weather-server - 点击"Trust"信任这个自定义 Server
- 开始使用:
问:"北京今天天气怎么样?"
WorkBuddy 自动调用 get_weather 工具,返回:
{"city": "北京", "weather": {"temp": 28, "condition": "晴"}}
5. 进阶:带认证的 Server
生产环境不能裸奔,加一层认证:
# 带 API Key 认证的版本
from fastapi import Header, HTTPException
async def mcp_endpoint(
request: Request,
authorization: Optional[str] = Header(None)
):
# 验证 API Key
if authorization != "Bearer YOUR_API_KEY":
raise HTTPException(status_code=401, detail="Unauthorized")
# ... 原有逻辑 ...
配置时也加上 Key:
{
"mcpServers": {
"my-secure-server": {
"command": "python",
"args": ["mcp_server.py"],
"env": {
"API_KEY": "your-secret-key"
}
}
}
}
安全建议:
- 不要把 Key 写死在代码里,用环境变量
- 生产用 HTTPS
- 限制调用频率防滥用
6. 进阶:调用真实 API
上面的示例是模拟的。实战中通常是调真实 API:
import httpx
async def query_oa_approval(approval_id: str) -> Dict:
"""查询 OA 审批单"""
async with httpx.AsyncClient() as client:
response = await client.get(
f"https://oa.company.com/api/approvals/{approval_id}",
headers={"Authorization": "Bearer YOUR_TOKEN"},
timeout=10
)
return response.json()
# 在 TOOLS 中注册
{
"name": "query_oa_approval",
"description": "查询公司 OA 审批单状态",
"inputSchema": {
"type": "object",
"properties": {
"approval_id": {
"type": "string",
"description": "审批单号"
}
},
"required": ["approval_id"]
}
}
调真实 API 的要点:
- 用
httpx或aiohttp保持异步 - 超时设置(
timeout=10)防止卡死 - 错误处理(API 可能返回 400/500)
- 日志记录(方便排查问题)
7. Node.js 实现(可选)
如果你更熟 Node.js,也可以用 TypeScript 写:
// mcp-server.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
const server = new Server({
name: "my-custom-server",
version: "1.0.0"
}, {
capabilities: { tools: {} }
});
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [{
name: "my_tool",
description: "My custom tool",
inputSchema: {
type: "object",
properties: {
param: { type: "string", description: "A parameter" }
},
required: ["param"]
}
}]
};
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
// 实现你的逻辑
return { content: [{ type: "text", text: "result" }] };
});
const transport = new StdioServerTransport();
await server.connect(transport);
Node.js 方式:通过 stdio 通信,适合本地进程。Python 方式:通过 HTTP,更灵活。选你熟悉的。
8. 调试技巧
写 MCP Server 免不了调试,几个有用技巧:
本地测试
# 直接发 JSON-RPC 请求测试
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}'
WorkBuddy 调试
- 打开 WorkBuddy 的开发者工具(设置 → 高级 → 开发者模式)
- 调用工具时,底部面板会显示:
- 调了哪个工具
- 传了什么参数
- 返回了什么
常见错误
| 错误 | 原因 | 解决 |
|---|---|---|
| 404 | Server 没启动或端口错 | 检查 Server 是否运行 |
| 401 | 认证失败 | 检查 API Key |
| 工具不显示 | 没有 Trust | 到连接器管理页 Trust |
| 调用超时 | Server 逻辑太慢 | 加超时设置,加日志 |
小结
| 学会的 | 要点 |
|---|---|
| 自定义 Server 场景 | 内置没有的 API、服务 |
| MCP Server 架构 | JSON-RPC 协议,tools/list + tools/call |
| Python 最小实现 | FastAPI + uvicorn,50 行代码跑起来 |
| 配置到 WorkBuddy | mcp.json + Trust 机制 |
| 认证与安全 | API Key + 环境变量 + HTTPS |
| 调真实 API | httpx 异步调用 + 错误处理 |
| 调试技巧 | curl 测试 + WorkBuddy 调试面板 |
技术路线
第1节:理解 MCP,启用第一个连接器 ✅
↓
第2节:乐享连接器深度实战 ✅
↓
第3节(本文):自定义 MCP Server ✅
↓
第4节:MCP + Skill 组合技(浏览器 + API 双通道)
↓
第5节:消息通知全集成(飞书/钉钉/企业微信)
↓
第6节:综合实战(日报自动生成系统)
📺 系列文章:
基础篇(Skill 开发)
- 第1节:WorkBuddy Skill 入门
- 第2节:CDP 浏览器操控原理
- 第3节:云端共享与多终端部署
- 第4节:WorkBuddy 自动化实战
- 第5节:登录态持久化
- 第6节:Angular 页面自动化进阶
- 第7节:定时任务与自动化调度
- 第8节:错误处理与运维监控
连接器篇(MCP 实战)