# MCP Server 开发

按 Model Context Protocol 开发工具服务，由 Circow 桌面客户端拉起并托管；绑定到工作流后即可在对话中被 AI 调用。可以直接复用现成的 MCP 生态（npx / python 等任意可执行）。

> 推荐路径：在桌面客户端聊天中对 AI 说「创建一个 MCP」，内置的 `mcp-manager` 技能会完成模板复制、安装、绑定与发布。以下规范供手写与排障参考。

## 1. 创建

复制 `mcp-manager` 技能自带的 `template/` 目录后只改两个文件：`mcp.json`（元信息与启动方式）和 `server.py`（工具定义与实现）。如需额外 Python 依赖，在 `pyproject.toml` 的 dependencies 中添加。

### mcp.json

```json
{
  "name": "my-mcp",
  "display_name": "My MCP",
  "description": "做什么",
  "version": "0.1.0",
  "launch": {
    "command": "uv",
    "args": ["run", "--directory", "${MCP_DIR}", "server.py"],
    "env": {}
  },
  "env_schema": {
    "API_KEY": "第三方服务的 API Key"
  }
}
```

| 字段 | 必填 | 说明 |
| --- | --- | --- |
| name | 是 | 唯一标识，仅小写字母 / 数字 / 连字符，与安装目录名一致 |
| display_name / description / version | 是 | 展示名 / 描述 / 版本 |
| launch.command + launch.args | 是 | 启动命令，支持任意可执行（python / uv / npx / node / exe）。`${MCP_DIR}` 占位符会替换为该 MCP 实际目录，进程 cwd 恒为该目录 |
| launch.env | 否 | 内置默认环境变量 |
| env_schema | 否 | 声明需用户填写的环境变量（key → 说明），安装时提示用户输入 |

npx 类示例：`"command": "npx", "args": ["-y", "@scope/pkg"]`（国内网络需先预热，见下）。

### server.py 三个要点

- `@server.list_tools()` 中定义工具的 `name` / `description` / `inputSchema`
- `@server.call_tool()` 中按工具名分发实现
- 每个工具返回 `[TextContent(type="text", text="结果")]`

## 2. 安装（mcp_tool.py）

CLI 入口统一为 `python {skill_dir}/scripts/mcp_tool.py <子命令>`（`{skill_dir}` 为 mcp-manager 技能目录）。路径 / 鉴权全部依赖客户端注入的环境变量（`CIRCLE_CONFIG_DIR` / `CIRCLE_FLOW_ID` / `CIRCLE_USER_ID` / `CIRCLE_AUTH_TOKEN` / `FLOW_API_URL`），勿硬编码。

```bash
# 本地安装（项目目录已写入 {CIRCLE_CONFIG_DIR}/mcps/<name>/，name 与目录名一致）
python {skill_dir}/scripts/mcp_tool.py install <name> --bind
# 市场安装（zip 由客户端自动下载解压，无需先落盘）
python {skill_dir}/scripts/mcp_tool.py install <name> --bind --url <zip_url> --hash <sha256>
# 列出已装 MCP（JSON：name / version / phase / ok / tools）
python {skill_dir}/scripts/mcp_tool.py list
# 校验 + 打包 + 上传市场
python {skill_dir}/scripts/mcp_tool.py publish <project_dir>
```

`install` 一条命令内部自动完成：触发 `install-mcp` deep link（scheme 自动从 `CIRCLE_CONFIG_DIR` 派生）→ 轮询 `status.json` 验收 → `--bind` 时绑定到当前工作流。任一步失败会打印可执行的排障建议并以非零退出码结束。`--bind` 强烈建议带上，否则聊天中无法使用该 MCP 的工具。可选 `--timeout <秒>`（默认 120）。

### npx 预热（规则，非技巧）

`launch.command=npx` 的 MCP 在国内网络首次启动需在线下载包，几乎必然 >60s 握手超时。安装前必须先全局预装：

```bash
npm i -g <包名> --registry=https://registry.npmmirror.com
```

然后把 `mcp.json` 的 `launch.command` 改为该全局命令（或其绝对路径），`launch.args` 去掉 `-y <包名>`，再执行 install。直接用 `npx -y` 在线拉包是已知的失败路径。

## 3. 启动 / 停止 / 卸载

通过 `manage-mcp` deep link 触发（见 [deep-link.md](https://circow.cn/docs/deep-link.md)），再用 `verify` 轮询验收：

```bash
start "" "{scheme}://manage-mcp?op=stop&name=<name>"
python {skill_dir}/scripts/mcp_tool.py verify <name> --since <触发前毫秒时间戳> --expect stopped

start "" "{scheme}://manage-mcp?op=start&name=<name>"
python {skill_dir}/scripts/mcp_tool.py verify <name> --since <ts>
```

- start / stop 是持久声明：stop 会将该 MCP 的 `auto_start` 置 false（应用重启不复活），start 置回 true
- 卸载：先 `unbind-flow <name>` 解绑 → `manage-mcp?op=uninstall&name=<name>` → 轮询 `{CIRCLE_CONFIG_DIR}/mcps/<name>/` 目录消失 → 再跑一次 `unbind-flow <name>` 兜底（幂等），防止工作流残留孤儿工具条目
- deep link 命令立即返回，结果一律靠 verify 轮询 status.json / 目录存在性验收，`--since` 用触发前的毫秒时间戳区分本次结果

## 4. 分发

两条分发通道，二选一或并用：

1. 市场分发：`mcp_tool.py publish` 上架官方市场（自动校验含 launch、打包整目录、上传），用户在客户端内一键安装
2. 链接分发：把 `install-mcp` deep link（务必带 SHA-256 hash，可附 `env_vars`）放到你的官网或安装器里，用户点击即装
