Skip to main content

local-project-mcp-bridge

把本地 Python/Node 项目包装成 MCP stdio 连接器并注册到 ~/.workbuddy/mcp.json。当用户要求"把某个本地项目安装成连接器/MCP server""给这个项目加 MCP 接口"时使用。

Jump to install

Source facts

Repository
simcrq/Total-pipe
Last source activity
September 7, 2026 at 03:51
Detected SKILL.md language
Chinese
Stars
1
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
local-project-mcp-bridge
description
把本地 Python/Node 项目包装成 MCP stdio 连接器并注册到 ~/.workbuddy/mcp.json。当用户要求"把某个本地项目安装成连接器/MCP server""给这个项目加 MCP 接口"时使用。
agent_created
true
# 本地项目 → MCP 连接器 ## 何时用 用户说"把这个项目装成连接器""给 X 加 MCP 接口""让 Agent 能调用我这个脚本"。 目标是让本地 CLI/库项目通过 MCP 暴露工具给 WorkBuddy。 ## 第一步:先找现成的桥 **不要一上来就写工具逻辑。** 先找项目里是否已有 JSON-in/JSON-out 的入口: - 插件目录(`integrations/`、`plugins/`)里的 `bridge.py`、`index.js` - 自带的 `agent_tools.py` / `tools.py` 等"给 Agent 用的包装层" - CLI 的 `--json` 输出模式 多数成熟项目已有一个被别的宿主(如 DeepSeek Harness)驱动的桥。找到它, 你只需要写协议外壳,工具契约和参数校验直接沿用,不用重新设计。 ``` grep -rn "def run\|operation\|json.load(sys.stdin)" --include=*.py --include=*.mjs . ls integrations plugins ``` ## 第二步:准备运行环境 先判断项目有没有第三方依赖: - **纯标准库项目**(pyproject 的 dependencies 为空或全是 stdlib):跳过 venv, `command` 直接用 managed python 的绝对路径,例如 `C:/Users/<user>/.workbuddy/binaries/python/versions/3.13.12/python.exe`。 - **有依赖**:系统 Python 几乎总是缺包,优先用项目自带的锁文件建隔离 venv: ```bash uv sync --frozen # 有 uv.lock 时;通常十几秒 ``` `command` 指向 `<project>/.venv/Scripts/python.exe`(Windows) 或 `<project>/.venv/bin/python`(WSL/Linux)。**不要用系统 python 裸跑**。 Node 项目同理,用 managed node 的绝对路径。 ## 第三步:写 stdio 协议外壳 纯标准库即可,不要引入 mcp SDK。必须实现的方法: | 方法 | 说明 | |---|---| | `initialize` | 回 `protocolVersion` / `capabilities` / `serverInfo` / `instructions` | | `notifications/initialized` | 不回复 | | `tools/list` | 工具数组(name + description + inputSchema) | | `tools/call` | 调桥,返回 `{content:[{type:"text",text}], isError}` | | `ping`、`resources/list`、`prompts/list` | 空实现,避免客户端报错 | 工具执行异常要转成 `isError: true` 的结果,**不要让 server 崩溃**。 ### 工具怎么切 - **只读校验和写文件拆成两个工具**(如 `pwf2rpa_check` / `pwf2rpa_convert`)。 Agent 先 dry-run 看警告、修好再落盘,比一把梭写坏文件强得多。 - 给每个工具配独立 `timeout`:列表/查询类 30s,常规转换 120s, 要 spawn 外部工具链的(如调 node 重建数据表)给 600s。 - 若项目的异常本身会收集多个问题(如 pwf2rpa 的 `AdapterError.problems`), 单独捕获并把全部问题一次性回传。**只抛第一条**会把修 bug 变成慢速猜错循环。 参考实现(两个已落地的外壳,结构一致,可直接抄): `F:/Workbuddy/Total-pipe/paperworkflow/integrations/mcp/server.py` `F:/Workbuddy/pwf2rpa/integrations/mcp/server.py` ## 三个必踩的坑 1. **stdout 是协议通道。** 被调用的库和它的进度条(tqdm)、`print` 会写 stdout, 一个字节就污染响应。执行期间把 `sys.stdout` 换成转发到 stderr 的代理对象, 主线程保留原始 stdout 写响应 —— 这样即使超时线程残留也不会污染。 代理要实现 `write` / `flush` / `isatty` / `fileno`。 2. **长任务必须能超时。** 在线程里跑,`join(timeout)`,超时抛错。 超时上限用环境变量暴露(如 `PAPERWORKFLOW_TOOL_TIMEOUT`),默认给足 (OCR/批处理类给 1800s),因为 MCP 客户端超时往往比实际任务短。 3. **路径沙箱。** 如果桥没有边界检查,自己加:解析后必须 `path.relative_to(PROJECT_ROOT)`,否则拒绝。这是唯一挡住"读任意文件"的地方。 读 stdin 用 `sys.stdin.buffer.readline()`,同时兼容换行分隔 JSON 和 `Content-Length` 分帧(看首行是否以 `content-length` 开头)。 ## 第四步:注册 `~/.workbuddy/mcp.json`(**不是** `.workbuddy/.mcp.json`)。读取现有内容后 **追加**条目,不要覆盖其他 server: ```json "myproject": { "command": "F:/path/.venv/Scripts/python.exe", "args": ["F:/path/integrations/mcp/server.py"], "cwd": "F:/path", "disabled": false } ``` `cwd` 要设成项目根 —— 项目里用相对路径定位资源的逻辑全靠它。 改完用 `json.load` 校验一遍合法性,并确认其他条目没被动过。 **写完配置不会自动生效**,必须告诉用户去连接器管理页对新 server 点 Trust。 ## 验证清单(都要实跑,别只看代码) ```bash printf '%s\n%s\n%s\n' \ '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"0"}}}' \ '{"jsonrpc":"2.0","method":"notifications/initialized"}' \ '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \ | timeout 60 <python> server.py ``` 1. `initialize` 返回正确的 `serverInfo` 2. `tools/list` 返回全部工具 3. `tools/call` 实跑**一个不依赖网络**的工具(列表/索引类最合适) 4. 故意传一个项目外路径,确认被拒绝且返回 `isError: true` 而不是崩掉 ## 版本对齐(跨项目时) 如果本地项目还依赖另一个 MCP 服务的内部数据表,注册前先核对版本: 双方各查一次版本号和条目数(如版面数/分类数),一致才说明没漂移。 不一致就要跑该项目自带的 refresh 脚本。
View on GitHub