MCP 实战
所有 Skill、MCP、工作流仅供个人学习测试,功能、计费、权限以 workbuddy.cn 官方为准。文中凡涉及 WorkBuddy 专有配置项、文件路径、版本号与官方接入入口,均标注「待核验(以 workbuddy.cn 官方为准)」;MCP 协议本身(JSON-RPC 2.0、initialize、tools/list、tools/call、stdio/SSE 传输)为公开规范。
本栏目聚焦市面教程薄弱区:MCP 连不上、不稳定、权限泄露、多服务冲突等真实痛点。不做「贴一段配置就结束」的搬运,重点讲排错思路、隔离方案与最小权限落地。全部密钥一律占位符,禁止明文存储。
MCP 协议基础:MCP 和原生连接器怎么选
正文:原理与选型
MCP(Model Context Protocol)是一套让 AI 应用以统一方式接入外部工具与数据源的开放协议,消息层基于 JSON-RPC 2.0。一次典型调用生命周期为:客户端与服务端先做 initialize 握手并交换能力(capabilities),随后客户端调用 tools/list 拿到可用工具清单,再用 tools/call 发起具体调用。传输上有两类主流形态:stdio(本地子进程,通过标准输入输出通信)与 HTTP·SSE(远端常驻服务,通过 HTTP 端点加事件流通信)。
「原生连接器」指 WorkBuddy 内置、由平台托管鉴权与运维的官方接入渠道,开箱即用;MCP 则是自己声明的通用协议服务,灵活但要自己管进程、密钥与稳定性。选型原则:
官方原生连接器已覆盖的场景,优先用原生——鉴权、升级、故障兜底都由平台承担。
官方没有、但需要接本地数据(本机 SQLite、本地文件、个人脚本)的场景,用本地 stdio MCP,数据不出本机。
需要接自建服务或多端共用的远端能力时,用 HTTP·SSE MCP,并配合网络隔离与出站审计。
判断不准时,先用只读、最小工具集的 MCP 跑通,再逐步扩权限。
示例:一次 tools/list 的 JSON-RPC 报文
// 请求(客户端 → 服务端)
{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}
// 返回(服务端 → 客户端,节选)
{"jsonrpc":"2.0","id":1,"result":{"tools":[
{"name":"read_only_note","description":"按 id 返回一条只读笔记",
"inputSchema":{"type":"object","properties":{"note_id":{"type":"string"}},"required":["note_id"]}}
]}}截图脚本(待补:需在真实客户端按此清单截图)
| 序号 | 页面/位置 | 要展示的动作或元素 | 标注说明 |
|---|---|---|---|
| 图1-1 | WorkBuddy 连接器/MCP 入口页(路径待核验) | 同时出现「原生连接器」与「MCP」两类入口 | 圈出两类入口的位置差异 |
| 图1-2 | 某个已接入 MCP 的工具列表 | 展示 tools/list 后出现的工具名称 | 标注「这一步对应 tools/list」 |
配套模板:选型速查表
| 需求 | 推荐方式 | 理由 |
|---|---|---|
| 官方已支持的 SaaS 服务 | 原生连接器 | 托管鉴权与升级,零运维 |
| 本机数据库/本地文件 | stdio 本地 MCP | 数据不出本机,只读优先 |
| 自建远端服务、多端共用 | HTTP·SSE MCP | 远程部署,需网络隔离与出站审计 |
把本地数据库安全地交给 AI 查询,关键不是「接得上」,而是「只读、最小权限、随时能断」。
mcp.json 完整配置规范与密钥安全管理
正文:配置结构与密钥红线
适配版本:待核验(以 workbuddy.cn 官方为准)。WorkBuddy 读取 MCP 配置的具体文件路径、文件名与专有字段命名均以官方为准;下面给出社区通用的字段形态,便于你对照官方文档映射。
一份通用 MCP 配置以服务为单位组织:每个服务声明「怎么拉起它」(command/args 或 url/transport)、「给它什么环境变量」(env)。核心原则是密钥绝不写进配置文件本体:配置文件可能被同步、截图、误提交到仓库;正确做法是把密钥放在环境变量或系统密钥环,配置里只保留引用占位。
禁止把 <YOUR_API_KEY> 这类真实令牌直接粘进 mcp.json。
用独立的
.env文件加载,并把它加入.gitignore,文件权限设为仅本人可读。每个 MCP 服务用独立的密钥,不用一把总钥匙开所有服务。
配置文件本身只放命令路径、参数与 env 引用,不放任何可反推凭证的信息。
示例:带 env 引用的最小配置
{
"mcpServers": {
"local-demo": {
"command": "python3",
"args": ["/ABS/PATH/TO/minimal_stdio_mcp.py"],
"env": {
"DEMO_API_KEY": "${DEMO_API_KEY}"
}
}
}
}# .env —— 加入 .gitignore,权限 chmod 600
DEMO_API_KEY=<YOUR_API_KEY>
# 加载后再启动客户端,使 ${DEMO_API_KEY} 可被引用截图脚本(待补:需在真实客户端按此清单截图)
| 序号 | 页面/位置 | 要展示的动作或元素 | 标注说明 |
|---|---|---|---|
| 图2-1 | mcp.json 配置文件(路径待核验) | 展示 mcpServers 块,env 中为 ${...} 引用而非明文 | 红框圈出 env 引用写法 |
| 图2-2 | 终端执行 chmod 600 .env | 展示文件权限已收紧 | 标注「密钥文件仅本人可读」 |
配套模板:可直接复制的占位配置
{
"mcpServers": {
"<SERVER_NAME>": {
"command": "<COMMAND>",
"args": ["<ARG_1>", "<ARG_2>"],
"env": {
"<ENV_KEY>": "${<ENV_KEY>}"
}
}
}
}安全检查清单:配置文件中全局搜索真实令牌串、确认 .env 已被忽略、确认每个服务用独立密钥、确认仓库历史里没有误提交过旧密钥(若有,立即轮换)。
密钥写进配置文件的那一刻,它就已经在仓库、截图和备份里裸奔了——环境变量是最低成本的隔离。
飞书/企微机器人 MCP 接入教程与占位模板
正文:接入思路与最小权限
适配版本:待核验(以 workbuddy.cn 官方为准)。思路是写一个(或选用一个)MCP Server,把「发消息、查群聊、读公告」等机器人能力封装成 tools,再由 WorkBuddy 通过 MCP 调用。凭证层面,自建应用需要 app_id 与 app_secret,或使用机器人 Webhook;二者都是占位符,绝不落盘到配置。
最小权限落地:
只申请任务真正需要的权限——例如只需要「发群消息」,就不要申请读取通讯录、审批、日历等范围。
Webhook 模式比长连接模式权限面更小,但能力也更受限,按需求取舍。
机器人只拉进必要群聊,不拉全员群;发消息工具在服务端做群 ID 白名单。
app_secret 按环境变量注入,泄露后立即在开放平台轮换。
示例:凭证与工具占位
# .env
FEISHU_APP_ID=<YOUR_APP_ID>
FEISHU_APP_SECRET=<YOUR_APP_SECRET>{"name":"send_group_text","description":"向白名单群发送一条文本消息",
"inputSchema":{"type":"object","properties":{
"chat_id":{"type":"string"},"text":{"type":"string"}},
"required":["chat_id","text"]}}截图脚本(待补:需在真实客户端按此清单截图)
| 序号 | 页面/位置 | 要展示的动作或元素 | 标注说明 |
|---|---|---|---|
| 图3-1 | 开放平台权限管理页 | 展示只勾选了必要权限项 | 标注「最小权限勾选」 |
| 图3-2 | WorkBuddy 中调用 send_group_text | 展示一条测试消息成功发出 | 标注「白名单群内测试」 |
配套模板:接入占位与权限清单
{
"mcpServers": {
"bot-demo": {
"command": "python3",
"args": ["/ABS/PATH/TO/bot_mcp.py"],
"env": {
"FEISHU_APP_ID": "${FEISHU_APP_ID}",
"FEISHU_APP_SECRET": "${FEISHU_APP_SECRET}"
}
}
}
}权限申请核对:是否只申请了发消息、是否限定群聊范围、是否关闭了不需要的读取类权限、secret 是否只在环境变量中。
让 AI 帮你往群里发消息很简单,难的是它只能发到你指定的那几个群。
SQLite/本地数据库只读 MCP 搭建(最小权限)
正文:只读优先的四道闸门
适配版本:待核验(以 workbuddy.cn 官方为准)。把本地数据库交给 AI 查询,最大的风险不是查不到,而是「顺手」被写入、删改或拖走整库。用四道闸门把风险压住:
连接只读:用 URI 形式以只读模式打开数据库,例如
file:/ABS/PATH/data.db?mode=ro,数据库引擎层面拒绝任何写操作。语句白名单:Server 内只放行 SELECT,拦截 INSERT/UPDATE/DELETE/ATTACH/DDL,并对单条查询加 LIMIT 上限。
数据脱敏:敏感列(手机号、身份证等)在查询层做列级屏蔽,不把原始字段暴露给模型。
文件权限:db 文件放在仅本人可读的目录,不直接挂生产库,优先用导出的分析副本。
示例:只读连接与语句拦截
import sqlite3
# mode=ro:引擎级只读;URI 方式打开
conn = sqlite3.connect("file:/ABS/PATH/data.db?mode=ro", uri=True)
FORBIDDEN = ["insert", "update", "delete", "attach", "drop", "alter", "create"]
def safe_query(sql):
low = sql.strip().lower()
if not low.startswith("select"):
raise ValueError("仅允许 SELECT")
if any(k in low for k in FORBIDDEN):
raise ValueError("检测到写操作关键字,已拒绝")
cur = conn.cursor()
cur.execute("select * from (" + low + ") limit 200")
return cur.fetchall()截图脚本(待补:需在真实客户端按此清单截图)
| 序号 | 页面/位置 | 要展示的动作或元素 | 标注说明 |
|---|---|---|---|
| 图4-1 | 某次 SELECT 查询返回结果 | 展示 AI 查询到的只读数据 | 标注「只读连接成功」 |
| 图4-2 | 尝试 UPDATE 被拦截 | 展示服务端拒绝并返回错误 | 标注「写操作被白名单拒绝」 |
配套模板:只读 MCP 配置与启动脚本
{
"mcpServers": {
"sqlite-ro": {
"command": "python3",
"args": ["/ABS/PATH/TO/sqlite_ro_mcp.py", "--db", "/ABS/PATH/data.db"],
"env": {}
}
}
}chmod 600 /ABS/PATH/data.db
ls -l /ABS/PATH/data.db
# 确认属主为本人、组与其他用户无写权限把本地数据库安全地交给 AI 查询:只读连接是底线,语句白名单是保险。
MCP 超时、重试、离线排查完整决策树
正文:通用排查框架
适配版本:待核验(以 workbuddy.cn 官方为准)。MCP 出问题时,最忌讳一上来就改配置重启。先用一个二分把问题切成「连接期」还是「调用期」:tools/list 列不出工具,问题在握手与连接;列得出但 tools/call 失败,问题在调用与外部依赖。再区分是「必现」还是「偶发」——必现查配置与权限,偶发查超时、退避与进程稳定性。
超时与重试的通用原则:
每个调用设独立超时阈值,外部依赖慢不能无限挂住对话;阈值具体数值以官方建议为准。
重试用指数退避加少量随机抖动,避免对下游形成重试风暴;重试次数克制(个位数)。
离线时要有降级:明确告诉用户「工具暂不可用」,而不是静默卡死。
stdio 服务查退出码与 stderr;HTTP 服务查端口、防火墙与状态码。
示例:连通性自查命令
# stdio:直接手动拉起,看是否报错、是否能握手
python3 /ABS/PATH/TO/server.py < startup.json
# HTTP/SSE:确认端口可达
curl -sS -o /dev/null -w "%{http_code}\n" http://<HOST>:<PORT>/<ENDPOINT>截图脚本(待补:需在真实客户端按此清单截图)
| 序号 | 页面/位置 | 要展示的动作或元素 | 标注说明 |
|---|---|---|---|
| 图5-1 | 客户端工具列表 | 展示 tools/list 是否成功列出 | 这是二分排查的第一刀 |
| 图5-2 | 服务端日志(位置待核验) | 展示某次失败的错误行 | 标注「按错误码回到决策树分支」 |
配套模板:排查清单
[ ] tools/list 能否列出工具?
[ ] 能列出:是否 tools/call 才报错?错误码是什么?
[ ] 列不出:command 路径是否存在?args 是否正确?
[ ] env 是否注入?密钥占位是否已被真实值替换?
[ ] HTTP 服务:端口可达?防火墙放行?状态码是多少?
[ ] 是否偶发?偶发则加超时阈值与指数退避重试
[ ] 进程是否反复退出?看 stderr 与资源占用
[ ] 离线时是否有明确降级提示,而非静默卡死MCP 连不上时,先问一句「工具列不列得出来」——这一刀能砍掉一半的误判。
多 MCP 服务共存:进程隔离与日志分离
正文:为什么必须隔离
适配版本:待核验(以 workbuddy.cn 官方为准)。当你挂了好几个 MCP 服务,最大的隐患是「一损俱损」:一个服务崩溃、死循环或吃满资源,把其他服务拖慢甚至带挂。隔离的核心是让每个服务独立进程、独立工作目录、独立日志、独立环境变量。
进程隔离:每个 server 由独立命令拉起,互不共享子进程;崩溃一个不影响其他。
日志分离:每个服务写自己的日志文件,命名带服务名,排错时一眼定位,不混在一起。
端口/资源隔离:HTTP 服务端口互不冲突;对常驻服务设资源占用上限。
凭证隔离:每个服务只读自己的 env 文件,不共用一把总密钥。
示例:多服务配置片段
{
"mcpServers": {
"sqlite-ro": {"command":"python3","args":["/ABS/PATH/to/sqlite_ro_mcp.py"],"env":{"DB_PATH":"${DB_PATH}"}},
"bot-demo": {"command":"python3","args":["/ABS/PATH/to/bot_mcp.py"],"env":{"FEISHU_APP_ID":"${FEISHU_APP_ID}"}}
}
}截图脚本(待补:需在真实客户端按此清单截图)
| 序号 | 页面/位置 | 要展示的动作或元素 | 标注说明 |
|---|---|---|---|
| 图6-1 | 配置文件中多个 mcpServers 块 | 展示每个服务独立 command 与独立 env | 标注「一服务一进程一 env」 |
| 图6-2 | 日志目录 | 展示按服务名分文件的日志 | 标注「日志按服务分离」 |
配套模板:目录结构与启动约定
~/mcp/
sqlite-ro/ { server.py, .env, logs/sqlite-ro.log }
bot-demo/ { server.py, .env, logs/bot-demo.log }
# 每个服务独立目录、独立 .env、独立日志文件;.env 均 chmod 600挂五个 MCP 不可怕,可怕的是它们共用一个进程、一把钥匙、一本混在一起的账。
自建简易 MCP Server 开发入门(Python)
正文:最小可运行的 stdio Server
适配版本:待核验(以 workbuddy.cn 官方为准)。下面给一个零第三方依赖的最小 stdio MCP Server,按公开 JSON-RPC 2.0 协议实现 initialize、tools/list、tools/call 三个方法。它能让你在不依赖任何 SDK 版本的前提下,先跑通「客户端能列出并调用你的工具」这一条主链路;后续再按官方 SDK 与 WorkBuddy 接入文档映射。
工具设计原则:先做一个只读、无副作用的工具跑通链路,再逐步加能力;每个工具的入参都要用 inputSchema 讲清楚,模型才能正确调用。
示例:最小 stdio MCP Server
#!/usr/bin/env python3
# 适配版本:待核验(以 workbuddy.cn 官方为准);协议本体为公开规范
import sys, json
def send(msg):
sys.stdout.write(json.dumps(msg) + "\n")
sys.stdout.flush()
def handle(req):
method = req.get("method")
rid = req.get("id")
if method == "initialize":
return {"jsonrpc":"2.0","id":rid,
"result":{"protocolVersion":"2024-11-05",
"capabilities":{"tools":{}},
"serverInfo":{"name":"minimal-demo","version":"0.1.0"}}}
if method == "tools/list":
return {"jsonrpc":"2.0","id":rid,"result":{"tools":[
{"name":"read_only_note",
"description":"按 id 返回一条只读笔记示例",
"inputSchema":{"type":"object",
"properties":{"note_id":{"type":"string"}},
"required":["note_id"]}}]}}
if method == "tools/call":
params = req.get("params", {})
if params.get("name") == "read_only_note":
nid = params.get("arguments", {}).get("note_id", "")
return {"jsonrpc":"2.0","id":rid,"result":{"content":[
{"type":"text","text":"note " + str(nid) + " (只读示例)"}]}}
return {"jsonrpc":"2.0","id":rid,"error":{"code":-32601,"message":"unknown tool"}}
if method == "notifications/initialized":
return None
return {"jsonrpc":"2.0","id":rid,"error":{"code":-32601,"message":"method not found"}}
def main():
for line in sys.stdin:
line = line.strip()
if not line:
continue
try:
req = json.loads(line)
except Exception:
continue
resp = handle(req)
if resp is not None:
send(resp)
if __name__ == "__main__":
main()截图脚本(待补:需在真实客户端按此清单截图)
| 序号 | 页面/位置 | 要展示的动作或元素 | 标注说明 |
|---|---|---|---|
| 图7-1 | WorkBuddy 工具列表 | 展示 read_only_note 已被列出 | 标注「你的 Server 已被识别」 |
| 图7-2 | 对话中调用 read_only_note | 展示返回的只读文本 | 标注「tools/call 链路打通」 |
配套模板:运行与接线
# 手动跑通(stdio 走标准输入输出)
python3 minimal_stdio_mcp.py
# 在 mcp.json 中用 command/args 指向该脚本(字段形态待核验)自己写一个 MCP Server 没有想象中难:一个只读工具、三个协议方法,链路就通了。
MCP 安全红线:哪些第三方 MCP 不能接入与审计要点
正文:第三方 MCP 的风险面
适配版本:待核验(以 workbuddy.cn 官方为准)。第三方 MCP Server 不是一段「配置」,而是一段在你机器上以你的身份运行的程序:它能读到你注入的环境变量、访问它进程权限范围内的文件、并把数据发到它的服务器。接入前必须按审计清单过一遍。
红线(满足任一条就不要接):
来源不明、无维护者、无开源可查的 MCP Server。
要求你一把总钥匙、或远超任务所需的宽泛权限(如既要读数据库又要发邮件)。
拒绝说明数据去了哪里、有出站传输却无隐私说明。
要求把生产密钥、真实隐私数据直接喂给它做「演示」。
供应链风险:依赖大量不明第三方包、安装脚本里夹带联网下载。
示例:接入前审计清单
| 审计项 | 通过标准 |
|---|---|
| 来源与维护 | 可溯源:有明确维护者、仓库可查、issue 有响应 |
| 权限范围 | 最小化:只开放任务所需工具,不要写权限就不开写 |
| 凭证暴露 | env 中只放该服务必需的密钥,不放总钥匙 |
| 数据出站 | 清楚知道它连向哪些域名,无隐蔽外传 |
| 运行隔离 | 独立进程、独立日志、可随时一键停用 |
截图脚本(待补:需在真实客户端按此清单截图)
| 序号 | 页面/位置 | 要展示的动作或元素 | 标注说明 |
|---|---|---|---|
| 图8-1 | 某 MCP 的 tools 列表 | 逐个核对工具数量与名称是否超出预期 | 标注「多余工具就是多余风险面」 |
| 图8-2 | env 配置块 | 展示该服务只拿到自己的专用密钥 | 标注「凭证隔离」 |
配套模板:停用与应急动作
[ ] 立即在 mcp.json 中停用(注释或删除该 mcpServers 块)
[ ] 轮换该服务专用的密钥(已注入即视为可能泄露)
[ ] 检查它进程访问过的文件与出站连接
[ ] 复盘当初为何给了超额权限,补齐最小权限规则
[ ] 不收录交易策略、自媒体批量脚本类工具,风险与收益不成比例第三方 MCP 不是插件,是你亲手在自己电脑上开的一扇门——门后是谁,接之前就得看清。
(注:部分内容由豆包工作 AI 生成)