Metadata-Version: 2.4
Name: loongclaw-devkit
Version: 0.10.14
Summary: LoongClaw MCP 开发者工具包 — 创建、加密、打包、发布、下架/重新上架 MCP 插件
Author: LoongClaw Team
License-Expression: MIT
Keywords: ai,devkit,loongclaw,mcp,plugin
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.9
Requires-Dist: cython>=3.0
Requires-Dist: mcp[cli]>=1.0.0
Requires-Dist: pathspec>=0.11
Requires-Dist: pip
Requires-Dist: requests-toolbelt>=1.0.0
Requires-Dist: requests>=2.25
Requires-Dist: setuptools
Requires-Dist: wheel
Description-Content-Type: text/markdown

# LoongClaw DevKit — MCP 插件开发完整指南

> **目标读者**：想开发 LoongClaw MCP 插件的开发者

MCP 插件开发者工具包。一键创建、加密打包、发布 MCP 插件到 LoongClaw 商店。

---

## 目录

| # | 章节 | 解决什么 |
|---|---|---|
| 1 | [5 分钟上手](#1-5-分钟上手) | 装好 → 让 AI 帮你产出第一个插件 |
| 2 | [一个合格的 MCP 插件长什么样](#2-一个合格的-mcp-插件长什么样) | 不管用不用 DevKit 都要懂的 MCP 基础 |
| 3 | [DevKit 提供的工具（AI-first）](#3-devkit-提供的工具ai-first) | 5 个工具能干啥（不用记命令） |
| 4 | [manifest.json 字段速查表](#4-manifestjson-完整字段速查表) | 每个 .publish.json 字段长什么样 |
| 5 | [configFields — 让用户填 API Key](#5-configfields--让用户填-api-key) | 插件需要用户自己输入密钥时怎么写 |
| 5A | [使用 LoongClaw 平台 LLM](#5a-使用-loongclaw-平台-llm不让用户填-openai-key-的方案) | 让用户不用自带 OpenAI Key（合作方专用） |
| 6 | [postInstallCommands](#6-postinstallcommands--需要浏览器字体模型时怎么办) | 装完插件后还需要下浏览器/模型时怎么写 |
| 7 | [Python 版本与依赖策略](#7-python-版本与依赖策略) | 跨平台不踩坑的 requirements.txt 写法 |
| 8 | [加密发布（Cython）](#8-加密发布cython-自动编译) | 源码保护怎么自动开启 |
| 9 | [商店访问控制](#9-商店访问控制public--private) | public / private 两种访问类型怎么选 |
| 10 | [付费/授权模式](#10-付费授权模式如何落地) | 把插件做成商业化付费的流程 |
| 11 | [更新与增量发布](#11-更新与增量发布) | 改一行也要重发整个包吗 |
| 12 | [在其他 AI 客户端中使用](#12-在其他-ai-客户端中使用) | Claude Desktop / Cursor / VS Code 怎么接 |
| 13 | [常见报错 FAQ](#13-常见报错-faq) | 出问题先来这查 |
| 14 | [参考代码 duanju-mcp](#14-参考代码duanju-mcp-实战样例) | 真实可跑的样例项目 |
| 15 | [附录](#15-附录) | 命令行逃生 / Store Key / 调试技巧 / 能力边界 |

---

## 1. 5 分钟上手

### 1.1 什么是 LoongClaw MCP 插件

**MCP**（Model Context Protocol，模型上下文协议）是 Anthropic 提出的标准，让 AI 能调用外部工具。**LoongClaw MCP 插件** = 一个独立的 Python 程序，按 MCP 协议暴露工具给 LoongClaw 桌面客户端里的 AI 调用。

类比：AI 就像一个刚入职的新员工，MCP 插件就是你给他的一本「操作手册」——上面写明这个员工可以用哪些工具、每个工具怎么用、参数有哪些。AI 看完手册就知道「原来我能查短剧库、发布视频、下载素材」。

### 1.2 DevKit 是什么

`loongclaw-devkit` 是一个**本身也是 MCP 插件**的工具包。安装后，你的 AI 助手（LoongClaw、Claude Desktop、Cursor）会多出 5 个工具：

- `create_mcp_project` — 一句话生成项目骨架
- `publish_mcp` — 一键加密 + 打包 + 上传
- `unpublish_mcp` — 下架自己发布的插件（软下架，不影响已安装用户）
- `republish_mcp` — 重新上架之前下架的插件
- `check_mcp_status` — 查看已发布状态

**一句话：你让 AI 干活，AI 让 DevKit 干活。** 你不用记任何命令。

### 1.3 安装

推荐使用 [uvx](https://docs.astral.sh/uv/)（零安装直接运行）：

```bash
uvx loongclaw-devkit
```

或用 pip 安装：

```bash
pip install loongclaw-devkit
```

在 LoongClaw 客户端里**直接搜索 `loongclaw-devkit` 一键安装**即可，不需要命令行。在 Claude Desktop / Cursor / VS Code 中使用，见[第 12 章](#12-在其他-ai-客户端中使用)。

> **� v0.10.6（2026-04-27）— 性能 + 质量两大升级**
>
> **加密耗时大幅缩短**：以前每个 .py 文件都单独冷启动 Python 子进程跑 setup.py（每次 1-2s）。文件多的项目（如 70 文件级别）光启动开销就 ~2-3 分钟。v0.10.6 改成**批量并行编译**——一次性 `cythonize(nthreads=CPU核数)` + `build_ext -jN`，多核机器加速明显（实测 8 核 macOS：56 文件 ~15min → ~3min）。**无需修改 .publish.json，升级即享**。
>
> **新增 `--dry-run` 预览模式**：第一次发版或改了 `.mcpignore` 后，先 `python publish.py --auto --dry-run` 把"什么进了包"看一眼再决定要不要真上传。完整跑加密 + 打包 + 烟雾测试，最后打印 zip 内全部文件列表（每条标 🔒 加密 / 📄 明文 / 📦 资源 + 大小）；不生成 manifest、不上传、不修改 `.publish.json`。
>
> **新增 staging 烟雾测试**（自动行为）：打包前自动跑 `compileall` 验证所有明文 .py 语法 + 子进程 `import server` 验证入口能加载，能在发版前抓"明文 .py 损坏"和"`.mcpignore` 误排除关键模块"两类问题。**注意**：开发机里依赖未装属正常（包还没 pip install 呢），这种情况降级为 ⚠ warn 不阻断；语法损坏 / `__init__.py` 缺失等真错误才阻断。
>
> **升级**：`pip install -U loongclaw-devkit==0.10.6`。

> **�🔥 v0.10.5（2026-04-27）— 关键修复：所有 .publish.json 字段现在真正生效**
>
> 如果你写的 `.publish.json` 里有 `configFields` / `env` / `postInstallCommands`，**v0.10.4 及之前的版本会在生成 manifest 时悄悄丢掉这些字段**——你写的中文 label、自定义环境变量、装后命令全都进不到服务器，客户端也读不到。v0.10.4 我们修了入口侧白名单，但下游 manifest 组装阶段还有一道独立白名单当时没扫到。**现在彻底修了**。
>
> v0.10.5 一并修复 5 个 bug：
> - **`configFields` / `env` / `postInstallCommands` / `replaces` / `companionSkill` 全部透传**：用户在 `.publish.json` 写的字段进 manifest（同时保留 server.py 自动扫描作 configFields 兜底）。replaces 和 companionSkill 是发版前自审查新发现的——v0.10.4 修了 main() 但 generate_manifest 同样从未透传这俩字段，客户端的「配套技能」UI 标签和「压制内置工具组」功能此前一直默默不生效
> - **`--json-output` 失败时 `sys.exit(1)`**：CI 脚本（`$LASTEXITCODE`）现在能正确判断成败，不再"假发版"
> - **Windows 系统代理隔离**：requests 默认会读 winhttp（v2rayN/Clash 装代理后必踩），现在显式 `trust_env=False`
> - **PowerShell 5.1 中文不再乱码**：启动时 `sys.stdout.reconfigure(encoding="utf-8")`
>
> **升级**：`pip install -U loongclaw-devkit==0.10.5`，再重新发一次插件即可。

<details>
<summary>📜 历史版本变更（v0.9.x — v0.10.4，已稳定，仅作存档）</summary>

**v0.9.1（2026-04-24）— 三项开发者体验补丁**

1. **Cython 产物警告文案修正**：旧文案只提"声明 requiredPython"，没提还会自动写 `buildPlatform`。v0.9.1 修正为同时说明两个字段及实际值。
2. **`.mcpignore` UTF-8 BOM 自动处理**：Windows 记事本等编辑器默认保存 BOM，会污染第一行规则（`*.log` 被当成 `\ufeff*.log` 而匹配失败）。v0.9.1 用 `utf-8-sig` 自动剥离 BOM 并给出警告。
3. **上传成功后输出丰富化**：打印插件 ID / 版本 / access 类型 / buildPlatform / requiredPython / 包大小 / 上传地址，方便开发者和 AI 核对。

**⚠️ v0.9.0 Breaking Changes（2026-04-24）**

行为变化（可能影响现有项目）：

1. **`skipEncrypt` / `--skip-encrypt` / `publish_mcp(skip_encrypt=...)` 全部删除**。非 MCP 运行时文件请改用 `.mcpignore`（gitignore 语法）**完全排除出包**——保留明文既无法加密保护又占用 zip 体积，不是正确做法。
2. **`_vendor/` 默认不再打入 zip**。客户端 `mcp-store-python.ts` 已有 PyPI 在线安装三级 fallback（`--only-binary` → 正常 pip → venv pip 兜底），打 vendor 会让 zip 从几 MB 涨到几十 MB 常被网关拦截。需要离线分发时，在 `.publish.json` 显式设 `"bundleVendor": true`。
3. **Cython 编译失败 = 发版失败**（Fail Fast）。旧版本会回退为明文导致源码泄漏，现在直接 abort 并输出 stderr，要求开发者修好再发。
4. **发版前自动做 Cython 可行性前置扫描**：文件名必须是 ASCII 合法标识符（中文/空格/连字符拒绝）；函数参数名不能撞 C/Cython 保留字（`int/char/long/...`）。一次性报告所有问题，不再走到子进程编译阶段才挂。
5. **上传不再设固定 read timeout**（对齐「超时铁律」）。v0.8.x 的 300s read-timeout 在慢网络下会误杀合法上传；v0.9.0 仅保留 10s 连接超时，body 上传由 TCP/服务端自行判活。
6. **上传体积上限改为服务端单源查询**：`Cloud /v1/store/limits` → `LOONGCLAW_MAX_UPLOAD_MB` 环境变量 → 内置兜底（三层 fallback）。具体阈值以服务端为准。超过 warn 打印警告但允许上传，超过 max 直接拒绝。
7. **CLI flag 在所有模式下都覆盖 saved 配置**：v0.8.x 仅 `--auto` 模式下 CLI 生效，导致 `python publish.py --version 1.0.1` 在已有 `.publish.json` 时参数被静默忽略。

新增能力：

- **新增 `.mcpignore` 文件支持**：项目根创建即可，支持 `**` / `!negate` / `#comment` 标准 gitignore 语法。
- **硬红线不可覆盖**（即使 `.mcpignore` 写 `!.git/` 也无效）：`__pycache__/` `*.pyc` `*.pyo` `.git/` `.venv/` `venv/` `_staging/` `.publish.json` `project.zip` `.mcpignore`。
- **staging 清理用 try/finally 保证**：任何一步失败（含 SystemExit）都会清理 `_staging/`，防止下次 publish 读到旧残留。
- **自动包含构建工具链**：`setuptools / wheel / Cython / pip / pathspec` 现为 devkit 运行时依赖。

老 `.publish.json` 里的 `skipEncrypt` 字段会被自动丢弃并提示迁移。

**v0.10.4（2026-04-27）— main() 字段透传**

修了 publish.py main() 里的字段白名单——以前 `runtime` / `mcp` / `replaces` / `companionSkill` / `usesPlatformLlm` 等字段在 saved → config 阶段会被丢，导致重复发版时 .publish.json 有的字段进不到 manifest。**但 generate_manifest 阶段还有第二道白名单当时没扫到，所以 configFields/env/postInstallCommands 还是会丢——v0.10.5 才彻底修完。**

</details>

### 1.4 三句话产出一个插件

在 LoongClaw / Claude Desktop 里直接对 AI 说：

> **你**："帮我在 `~/projects/weather-mcp` 创建一个 MCP 插件，ID 叫 `weather-mcp`，功能是查天气"

AI 调用 `create_mcp_project` 生成骨架。

> **你**："在 server.py 里加一个工具，接收城市名，返回今天天气"

AI 编辑代码。

> **你**："发布到 LoongClaw 商店"

AI 调用 `publish_mcp` 完成加密、打包、上传。

就这样。

---

## 2. 一个合格的 MCP 插件长什么样

这一章是**所有** MCP 开发者都要懂的基础——不管你用不用 DevKit。

### 2.1 最小可运行结构

```
weather-mcp/
├── server.py            ← MCP 入口（FastMCP 实例 + 工具注册）
├── requirements.txt     ← Python 依赖
└── .publish.json        ← DevKit 发布配置（DevKit 自动生成骨架，access 字段必须你/AI 手动填 public 或 private）
```

**`server.py` 模板**：

```python
#!/usr/bin/env python3
"""weather-mcp — 天气查询插件"""

from mcp.server.fastmcp import FastMCP

mcp = FastMCP(
    "weather-mcp",
    instructions="提供全球城市天气查询能力。"
)

@mcp.tool()
def get_weather(city: str) -> str:
    """查询指定城市今天的天气。

    Args:
        city: 城市名（中文或英文均可，如 "北京" / "Tokyo"）
    """
    # ... 你的业务逻辑
    return f"{city}: 晴，25°C"

if __name__ == "__main__":
    mcp.run(transport="stdio")
```

**`requirements.txt`**：

```
mcp>=0.4.0
requests>=2.31.0
```

就这样。**一个合格的 MCP 插件 = FastMCP 实例 + 若干 @mcp.tool() 装饰的函数 + stdio transport 启动。**

### 2.2 三个必知原则

#### 原则一：工具名**禁止**加前缀 ⚠️ 这是第一大坑

**不要**这样写：

```python
# ❌ 错误示范
@mcp.tool()
def mcp__weather__get_weather(city: str) -> str:  # 别加前缀！
    ...
```

LoongClaw 的 MCP Manager 会**自动**把你的工具重命名为 `mcp__weather-mcp__get_weather`（对标 Claude Code 标准）。你手动加前缀 → 双重前缀 → 工具名里会变成 `mcp__weather-mcp__mcp__weather__get_weather` → AI 根本调不到。

**正确做法**：工具名只写功能名，如 `get_weather`、`send_message`、`list_accounts`。

> 代码依据：[loongclaw/gateway/mcp/mcp-manager.ts:309](../loongclaw/gateway/mcp/mcp-manager.ts) —— `const prefixedName = \`mcp${PREFIX_SEP}${serverName}${PREFIX_SEP}${mcpTool.name}\``

#### 原则二：docstring 决定 AI 用不用你的工具

AI **只看 docstring**来决定要不要调这个工具。Docstring 写得烂 = 工具形同虚设。

**差的 docstring**：

```python
@mcp.tool()
def process(data: str) -> str:
    """处理数据"""  # AI 看了一脸懵：啥数据？怎么处理？
```

**好的 docstring**：

```python
@mcp.tool()
def get_weather(city: str, unit: str = "celsius") -> str:
    """查询城市今日天气。支持全球 200+ 城市。

    Args:
        city: 城市名，中文或英文均可（如 "北京" / "Tokyo" / "New York"）
        unit: 温度单位，"celsius" 或 "fahrenheit"，默认 celsius

    Returns:
        JSON 格式字符串，包含 temperature, condition, humidity, wind 四个字段

    Example:
        get_weather("北京") → {"temperature": 25, "condition": "晴", ...}
    """
```

**规则**：
- 首句用动词开头，一句话说清楚**做什么**
- 每个参数都写**类型 + 示例**
- 有返回结构时用 `Returns:` 块说明
- 有副作用（写文件、发网络请求、花钱）必须标明

> LoongClaw 限制：工具 description 超过 **2048 字符**会被截断（防止 OpenAPI 自动生成的 60KB 文档撑爆上下文）。

#### 原则三：核心业务逻辑放独立文件

```python
# server.py（入口，明文）
from core import query_weather_api

@mcp.tool()
def get_weather(city: str) -> str:
    return query_weather_api(city)
```

```python
# core.py（核心逻辑，发布时会被 Cython 加密成 .so/.pyd）
def query_weather_api(city: str) -> str:
    # 你的真正业务逻辑，发布后用户看不到源码
    ...
```

**为什么这样分**：`server.py` 是启动入口，必须明文；其他 `.py` 文件 DevKit 会自动 Cython 编译加密。详见[第 8 章](#8-加密发布cython-自动编译)。

---

## 3. DevKit 提供的工具（AI-first）

DevKit 本身是一个 MCP 插件，注册在你的 AI 客户端里。v0.9.0 起对 AI 暴露 9 个工具，
覆盖创建→诊断→配置→发布→下架的完整生命周期。**推荐 AI 工作流**：

```
create_mcp_project → (写业务逻辑) → update_mcp_config (author/access)
  → add_ignore_pattern (排除测试/数据) → diagnose_mcp_project
  → publish_mcp(upload=True)
```

所有错误返回 `{status, code, error, fix}`，AI 看 `code` 字段（稳定枚举）自动路由修复。

### 3.1 `create_mcp_project` — 创建项目

**对 AI 说**："创建一个 MCP 插件到 `~/projects/weather-mcp`，ID 叫 `weather-mcp`，功能是天气查询"

**幕后做了什么**：

1. 在目标目录创建骨架：
   - `server.py`（填入 ID 和描述的 FastMCP 模板）
   - `requirements.txt`（含 `mcp>=0.4.0`）
   - `AGENTS.md`（给 AI 看的开发规范）
   - `.publish.json`（发布配置：ID / 版本 / 访问类型；author 和 access 留空，强制显式填充）

   **v0.9.0 不再复制 publish.py 到用户项目**——发布逻辑在 devkit 自身，用户项目保持纯净。
2. 返回 `next_steps` 提示 AI 下一步干什么

> 代码依据：[server.py](./src/loongclaw_devkit/server.py) `create_mcp_project`，模板文件在 [src/loongclaw_devkit/templates/](./src/loongclaw_devkit/templates/)

### 3.1b 配置/诊断工具（v0.9.0 新增）

| 工具 | 作用 |
|---|---|
| `update_mcp_config(project_dir, field, value)` | 更新 .publish.json 单个字段（白名单校验；access 限 public/private） |
| `add_ignore_pattern(project_dir, patterns)` | 追加 .mcpignore 规则（去重、保序） |
| `list_mcp_tools(project_dir)` | AST 解析 server.py 列出所有 @mcp.tool() 函数及签名 |
| `diagnose_mcp_project(project_dir)` | 不构建不上传的预检：配置完整性 + Cython 可行性 + @mcp.tool() 数量 + 体积预估，返回 `{ok[], warnings[], issues[], ready_to_publish}` |

AI **强烈推荐**在 `publish_mcp` 之前先调 `diagnose_mcp_project`——本地秒级反馈，
避免发到云端才发现 author 没填、Cython 函数参数撞保留字等问题。

### 3.2 `publish_mcp` — 一键发布

**对 AI 说**："把 `~/projects/weather-mcp` 发布到 LoongClaw 商店，版本 1.0.0"

**幕后 6 个步骤**（全自动）：

| 步骤 | 做什么 | 失败后果 |
|---|---|---|
| 1. 校验项目 | 检查 `server.py` 是否有 FastMCP 实例 + `mcp.run()`、`requirements.txt` 是否存在；**private 付费插件额外要求 FastMCP 实例命名为 `mcp`**（壳模式约定，见[§8.3](#83-私有插件全量加密壳模式)） | 返回错误并给修复建议 |
| 2. 检测跨平台风险 | 扫 requirements.txt，警告 `greenlet==X.Y.Z` 等严格锁版（Windows 可能装不上） | 警告但不中断 |
| 3. Cython 加密 | `public`：除 `server.py/__init__.py` 外所有 `.py` 加密。`private`：**连 `server.py` 一起加密**成 `server_impl.so`，staging 自动生成入口壳 `server.py`（含 `sys.path` 自注入，兼容嵌入式 Python） | 任何业务 `.py` 编译失败 → `private` 模式 **Fail-Close abort**；`public` 模式回退明文会警告 |
| 4. 离线 wheel 打包 | 根据 `requirements.txt` 下载所有依赖到 `_vendor/`（用户无网也能装） | 默认 PyPI 失败 → 自动重试清华镜像 |
| 5. 打 zip | 明文 + `.so` + `_vendor/` → `project.zip`（排除 `__pycache__` / `.venv` / 上次残留的 `project.zip`） | — |
| 6. 生成 manifest + 上传 | 算每个文件的 SHA-256 + zip 整体 SHA-256，组装 manifest.json，上传到 Cloud API | 缺 Store Key → 报错并提示申请地址 |

> 代码依据：[publish.py](./src/loongclaw_devkit/publish.py)（devkit 内部引擎 ~1200 行，涵盖全部 6 步；v0.9.0 起不再复制到用户项目）

**紧急逃生（日常不用）**：`python -c "import loongclaw_devkit, pathlib; print(pathlib.Path(loongclaw_devkit.__file__).parent)"` 找到 devkit 安装位置后直接跑 `python <路径>/publish.py --auto ...`。仅在 devkit MCP 服务挂了或 CI 流水线场景使用。

### 3.3 `check_mcp_status` — 查状态

**对 AI 说**："查下 weather-mcp 现在商店里是什么版本"

**幕后做了什么**：从 Cloud 拉 `registry.json`，对比本地 `.publish.json` 的版本号，告诉你是否需要 bump version。

### 3.4 `unpublish_mcp` — 下架自己发布的插件

**对 AI 说**："我的 weather-mcp 有严重 bug，先下架"

**幕后做了什么**：调用 Cloud 的 `POST /v1/store/unpublish`，服务端校验「当前 Store Key 的 userId == manifest.uploadedBy」才允许操作。下架后：

- 新用户在商店里**看不到**这个插件
- **已安装的用户不受影响**——他们本地已有的代码继续工作（LoongClaw 不会远程禁用用户本地代码，这是用户权益铁律）
- 你之后上传同 id 的新版本**不会自动复活**——防止带 bug 的旧版本又被重新展示。确认新版本修复了问题后，调用 `republish_mcp` 才重新上架

> 参数：`plugin_id`（必填）、`store_type`（mcp|skill，默认 mcp）、`reason`（可选，便于 admin 后续排查）

### 3.5 `republish_mcp` — 重新上架

**对 AI 说**："weather-mcp 新版本 1.2.0 我已经测过了，重新上架"

**幕后做了什么**：调用 `POST /v1/store/republish`，把 `store_revocations` 表里的记录标为 `restored`，registry 自动刷新后用户就能再次看到。

> 只能操作你自己发布的插件（uploadedBy 校验），别人的下架不了。

---

## 4. manifest.json 完整字段速查表

DevKit 发布时会**自动生成** manifest.json，你一般不用手写。但你得懂里面每个字段在 LoongClaw 客户端对应什么行为——**调试 bug 时就靠它**。

| 字段 | 类型 | 必填 | 作用 |
|---|---|---|---|
| `id` | string | ✅ | 插件唯一 ID。仅允许 `[a-zA-Z0-9_-]`，长度 ≤100。DevKit 从 `.publish.json` 读取 |
| `name` | string | ✅ | 显示名（用户在商店看到的） |
| `description` | string | ✅ | 插件描述，AI 选择插件时会读这个 |
| `version` | string | ✅ | 语义版本号（如 `1.0.0`）。LoongClaw 通过字符串比较判断「是否有更新」 |
| `author` | string | ✅ | 作者名 |
| `icon` | string | ✅ | emoji 或 URL |
| `runtime` | string | ❌ | 支持 `"python"`（缺省）或 `"self-contained"`（自带运行时，见[§4.7](#47-self-contained--自带运行时高级)）。老 .publish.json 不写默认 python |
| `entrypoint` | string | ✅ | 入口文件，始终 `"server.py"`（self-contained 下不被客户端读，仅占位） |
| `files` | object | ✅ | **新格式**：`{ "文件相对路径": { "hash": "SHA-256", "size": 字节数 } }`。用于增量更新判断 |
| `sourceArchive` | string | ✅ | zip 文件名，DevKit 统一为 `"project.zip"` |
| `sourceArchiveHash` | string | ✅ | zip 整体 SHA-256。**增量更新的关键判断依据**（见[第 11 章](#11-更新与增量发布)） |
| `env` | object | ❌ | 默认环境变量（用户可在客户端 UI 覆盖） |
| `configFields` | array | ❌ | 用户可填的配置项，见[第 5 章](#5-configfields--让用户填-api-key) |
| `postInstallCommands` | string[][] | ❌ | pip install 后执行的命令，见[第 6 章](#6-postinstallcommands--需要浏览器字体模型时怎么办) |
| `requiredPython` | string | ❌ | Python 版本约束，见[第 7 章](#7-python-版本与依赖策略) |
| `replaces` | string[] | ❌ | 替代的内置工具组名（安装后自动隐藏，避免 UI 重复） |
| `companionSkill` | string | ❌ | 配套 Skill 名（前端用于关联提示） |
| `accessType` | `"public"` \| `"private"` | ✅ | 访问控制，见[第 9 章](#9-商店访问控制public--private)。**必填，无默认值**；Cloud 白名单拒绝任何非法值，DevKit `publish.py` 上传前本地预校验拦截 |
| `usesPlatformLlm` | bool | ❌ | 声明该 MCP 需要使用 LoongClaw 平台 LLM（默认 `false`）。**类型必须是 JSON 布尔 `true`/`false`，不能写成 `"true"` / `1` / `True`——v0.10.4+ devkit 会在 publish 预检 fail-fast 报错**。⚠️ **v0.10.3 及以前版本字段会被丢**（main() 白名单），v0.10.4 修了 main() 但 generate_manifest 仍漏 `replaces` / `companionSkill` / `env` / `configFields` / `postInstallCommands` —— **必须升级到 v0.10.5** 才彻底正常。详见 [5A 章](#5a-使用-loongclaw-平台-llm不让用户填-openai-key-的方案)——**该能力暂不开放外部开发者自助申请**，仅 LoongClaw 自研插件 + 已签约合作方使用，未加白名单时调用稳定返 403。 |
| `mcp` | object | ❌ | MCP 标准字段（transport/command/args）。`runtime=self-contained` 时必填 `command` |
| `skipCython` | bool | ❌ | 跳过 Cython 加密（默认 false）。适用于中间产物已是 .so/.pyd 的场景；self-contained 自动跳过不需显式设置 |
| `buildPlatform` | string | ❌ | 构建平台（darwin/linux/win32）。默认仅在有 Cython 产物时自动写入；**runtime=self-contained 时必填**（自带二进制必然平台特定，客户端 precheck 据此过滤） |

> 代码依据：[store-types.ts](../loongclaw/gateway/mcp/store-types.ts) —— `StoreManifest` 接口（约第 57 行起）

**Cloud 端额外自动注入**的字段（你不用写）：

- `uploadedBy` — 上传者的 userId，**用于防止他人覆盖你的插件**（所有权校验）
- `uploadedAt` — 上传时间

### 4.7 self-contained — 自带运行时（高级）

绝大多数插件应该用默认 `runtime=python`。仅当你**自带 Python 解释器或单文件二进制**（pyinstaller / nuitka / 嵌入式 Python / Go 编译产物）时才用 `self-contained`。

**与 python 模式的区别**：

| 行为 | python | self-contained |
|---|---|---|
| 客户端创建 venv | ✅ | ❌ |
| 客户端跑 `pip install -r requirements.txt` | ✅ | ❌ |
| 启动方式 | `python server.py` | `manifest.mcp.command` + `args` |
| `requirements.txt` 必需 | ✅ | ❌（可缺失） |
| `mcp.command` 必需 | ❌ | ✅ |
| `buildPlatform` 必需 | ❌ | ✅ |
| Cython 加密 | `access=public` 时跳过 / `access=private` 时强制 | `access=public` 时跳过 / `access=private` 时**仍强制加密**项目内 .py |

> ⚠️ **`accessType=private` 安全承诺**：无论 `runtime=self-contained` 还是 `skipCython=true`，只要 access=private，项目内的业务 .py 文件**都会被强制 Cython 加密**——这是平台付费包源码保护的硬性承诺，无法绕过。如果 self-contained 项目纯靠预编译二进制启动（项目里不含业务 .py，仅 `__init__.py` 占位），则正常打包不加密；只要存在 .py 业务源码就必须加密成功才能继续发版。

**.publish.json 最小示例**：

```json
{
  "id": "my-binary-mcp",
  "name": "My Binary MCP",
  "description": "Self-contained MCP with bundled binary",
  "version": "1.0.0",
  "author": "you",
  "access": "public",
  "runtime": "self-contained",
  "buildPlatform": "darwin",
  "mcp": {
    "command": "./bin/my-server",
    "args": ["--stdio"]
  }
}
```

**`mcp.command` 取值规则**：

- 路径形式（含 `/` `\` 或 `.` 开头）：相对 installDir 解析，必须落在 installDir 内（沙箱防穿越）
- 裸命令名：必须在客户端白名单内 — `python` `python3` `node` `npx` `uv` `uvx` `deno` `bun` `cmd` `sh` `bash` `pwsh`

**多平台分发**：自带二进制天然单平台。如要支持 macOS/Windows/Linux 三端，需各构建一份 zip 并各自上传（Store 多 slot 模型已支持，见 BACKLOG #14）。

---

## 5. configFields — 让用户填 API Key

### 5.1 场景

你的天气插件需要调和风天气 API，用户得先去注册账号拿自己的 Key。你不能把 Key 硬编码进代码（那是你的 Key，用户用你的额度）。

### 5.2 声明方式

在 `server.py` 里**直接写** `os.environ.get("HEFENG_API_KEY", "")`，DevKit 的 `publish.py` 会**自动扫描**并生成 `configFields`！

```python
import os

HEFENG_API_KEY = os.environ.get("HEFENG_API_KEY", "")  # DevKit 自动识别
HEFENG_UNIT    = os.environ.get("HEFENG_UNIT", "celsius")
```

生成的 manifest 里：

```json
{
  "configFields": [
    { "key": "HEFENG_API_KEY", "label": "Hefeng Api Key", "default": "", "required": true },
    { "key": "HEFENG_UNIT",    "label": "Hefeng Unit",    "default": "celsius", "required": false }
  ]
}
```

规则：**无默认值** → `required: true`；**有默认值** → `required: false`。

> 代码依据：[publish.py:372](./src/loongclaw_devkit/publish.py) —— `re.finditer(r'os\.environ\.get\(...)`

### 5.3 手动覆盖（密码类型 / 中文 label / 帮助文案）

自动扫描出来的字段是纯文本输入框，label 是英文 `Hefeng Api Key`。如果你想要**密码遮罩 / 中文 label / 友好的帮助文案**，在 `.publish.json` 里**显式写 `configFields`**——v0.10.5+ 会优先用你写的，扫源码作兜底。

```json
{
  "id": "weather-mcp",
  "version": "1.0.0",
  "configFields": [
    {
      "key": "HEFENG_API_KEY",
      "label": "和风天气 API Key",
      "type": "password",
      "required": true,
      "description": "在 https://dev.qweather.com 注册账号后到「应用管理」获取"
    }
  ]
}
```

> ⚠️ **v0.10.4 及之前的版本有 bug**：即使你在 `.publish.json` 写了 `configFields`，发布时也会被丢，最终 manifest 还是用扫源码生成的英文 label。**v0.10.5 修复**。

### 5.4 用户视角

用户在 LoongClaw 商店点「安装」→ 弹出填写表单 → 用户填完 → 客户端把值以环境变量形式注入你的 Python 进程。你在 `server.py` 里 `os.environ.get(...)` 直接拿到。

### 5.5 装好后用户怎么改 secret（API Key 轮换 / Token 过期）

**LoongClaw 客户端 v?.??+ 起**：用户在「商店 → 已安装条目卡片」可以看到 「⚙ 配置」按钮，点开会弹出和首次安装一样的 configFields 表单。

行为：

- **留空 = 保持原值**（password 字段不预填，避免 DOM/截屏泄露）
- 提交后客户端会停掉 MCP 进程 → 改写本机 `mcp-servers.json` 的对应 env → 自动 重启 MCP 加载新值
- 用户在 mcp-servers.json 里手加的额外 env 字段（manifest 没声明的，比如 `DEBUG=1` 之类诊断变量）会被保留

**作为开发者你不需要做任何额外工作** —— configFields 已经在 manifest 里了，「⚙ 配置」会自动复用。但请注意：

- 不要在 `server.py` 里把 secret 写到 stderr/log，否则 Cloud 端遥测可能采到（v0.9.0+ 客户端有 `sanitizeErrorMessage` 但你的业务日志不在保护范围）
- 不要让 AI 帮用户写 secret：客户端 system prompt 已经引导 AI 让用户走「⚙ 配置」UI 路径，避免 secret 进对话历史

---

## 5A. 使用 LoongClaw 平台 LLM（不让用户填 OpenAI Key 的方案）

> **此功能暂不开放外部开发者自助申请。** 仅 LoongClaw 自研插件 + 已签约的长期合作方可用。
> 如有合作意向请邮件联系：**XiaojieMa103@163.com**（注明插件用途、目标用户群、预估 LLM 调用量）。
>
> 未加入白名单的 MCP 调用 `/v1/mcp-llm/issue-token` 会**稳定返回 HTTP 403 `MCP_NOT_WHITELISTED`**——不是偶发错误，重试无意义。请用[第 5 章 configFields](#5-configfields--让用户填-api-key) 让用户填自己的 OpenAI Key 作为替代方案。

### 5A.1 适用场景

你的 MCP 内部需要调 LLM（让 AI 总结 / 抽取 / 翻译 / JSON 化），但你不想让每个用户都自己去注册 OpenAI 账号、复制 API Key。

LoongClaw 提供平台 LLM 代理：声明一个开关后，客户端会在 spawn 你的子进程时自动注入 `OPENAI_BASE_URL` / `OPENAI_API_KEY` → 你的代码用标准 `openai` SDK 调用 → 客户端本地代理拦截后，**用最终用户的算力余额扣费**（不是开发者掏钱）。

> **计费归属**：调用产生的费用从**最终用户**的算力账户扣（流水标 `source=mcp` + `mcp_id`），不是从开发者账户扣。请在你的 MCP 文档里向用户说明这一点，避免认知偏差。

### 5A.2 开启方式（合作方专用）

AI 调一次工具即可：

```
update_mcp_config(field="usesPlatformLlm", value=True)
```

之后请把 `mcpId` 邮件给我们，由 LoongClaw 后台手动加入 `mcp_llm_whitelist` 表后生效。

### 5A.3 调用代码（合作方落地后才有效）

零代码改动，标准 `openai` SDK 即可：

```python
from openai import OpenAI

client = OpenAI()  # 自动读 env 里的 OPENAI_BASE_URL / OPENAI_API_KEY
resp = client.chat.completions.create(
    model="qwen3.5-plus",  # 见 5A.4 模型选择
    messages=[{"role": "user", "content": "..."}],
)
```

协议：纯 OpenAI Chat Completions 兼容。

> 💡 **本地开发期想脱离客户端单跑 server.py 调试？** 见 [§5A.9 本地开发期 LLM 兜底](#5a9-本地开发期-llm-兜底ide--cli-调试)。

### 5A.4 可用模型清单（运行时拉取）

**不要把模型 ID 硬编码在文档里**——LoongClaw 上下线模型很频繁，写死的清单会过期。

查询当前可用模型的标准方式：

```bash
curl https://api.loongclaw.net.cn/v1/models | jq -r '.data[].id'
```

输出形如 `claude-sonnet-4.5` / `gpt-5` / `qwen3.5-plus` / `MiniMax/MiniMax-M2.5` / ...——**这些就是可以直接填到 `model=` 参数里的真实 ID**（注意：客户端 UI 上显示的是美化后的名字如「Claude Sonnet 4.5」，那个名字不能用于 API 调用，必须用 `/v1/models` 返回的原始 ID）。

该端点**无需认证**，开发者和 AI 在写代码前都可以直接拉一次确认当前清单。

> **⚠️ 服务端有 IP 限流**：`/v1/models`、`/v1/models/config`、`/v1/model-context-limits` 三个端点**各自独立计数**，每个端点同 IP 每分钟最多 30 次。开发者拉一次确认即可，**不要在 CI / 启动脚本里轮询**——超限会返 `429` + `Retry-After` 响应头。

#### 拿不准选哪个模型时的兜底建议

如果你是 AI / 开发者写代码时不确定该选哪个模型，**推荐**（不是强制）选 `qwen3.5-plus` 作为起点——综合性价比、稳定性、响应速度比较平衡，适合大多数 MCP 内部调用场景（总结、抽取、JSON 化）。

但请注意：
- 这只是「拿不准时的合理默认」，**不是强制要求**
- 你完全可以从 `/v1/models` 拉到的清单里**任选其它模型**（比如对生成质量要求高 → `claude-sonnet-4.5`；对成本敏感 → 选更便宜的 flash 系列）
- **不要在代码里写死单一模型**——把 model 名留作 MCP 工具参数或 configField，让用户/上层 AI 决定更好

### 5A.5 ⚠️ 安全规则：禁止把 OPENAI_API_KEY 落盘 / 打日志

每次客户端启动 / MCP 重连，注入的 token 都会变（基于 nonce 重新分配）。

- ❌ 不要 `print(os.environ["OPENAI_API_KEY"])` —— 真 token 泄漏到日志
- ❌ 不要把 key 写到本地缓存文件 —— 下次启动就废了
- ❌ 不要在工具返回值里回显 key —— 暴露给上层 AI 和用户
- ✅ 只在调用 LLM 时通过 `OpenAI()` 默认读取，用完就忘

### 5A.6 ⚠️ 401 自动续约由代理层处理

如果用户在其它设备登录把当前设备踢下线 / 重新登录 → 旧 token 失效 → 你这边 chat 调用返 401。**本地代理会自动重新申请 token 并重试一次**，对你的代码完全透明。

**不要自己实现 401 重试逻辑**——会和代理层冲突，可能导致重复扣费或卡死。

### 5A.7 ⚠️ 首次调用可能 ~200-500ms 延迟（不是失败）

客户端 spawn 你的子进程时 env 里塞的是临时占位符（`lc-pending-<nonce>`），不是真 token。第一次 chat 请求到达本地代理时才向 Cloud 申请真 token，所以**首次请求会比后续慢 200-500ms**，之后所有请求走缓存。

网络抖动导致首次签发失败时会返 502，OpenAI Python SDK 默认会自动重试 2 次（指数退避 0.5s → 1s）——通常无需你额外处理。如果你在工具里捕获了 LLM 异常，**保留至少一次重试**，不要把 502 直接透传给上层 AI。

### 5A.8 不要做的事

| 行为 | 后果 |
|---|---|
| 用 `requests` 直连 `https://api.openai.com` | 绕过代理 = 用户没付钱给你跑了 LLM |
| 自己读 `OPENAI_API_KEY` 拼别的 base_url | 真 token 暴露给第三方上游 |
| 假设 base_url 一定是 `http://127.0.0.1:18789/v1` | 端口可能变（用户自定义 gateway 端口）—— 永远用 SDK 默认 |
| 在工具内调 `openai` SDK 但不 catch 异常 | 用户首次使用偶发慢/失败，体验差 |
| 在代码里硬编码模型名（如 `model="gpt-4o"`） | 模型下线后整个 MCP 失效；改成 configField 或工具参数 |

### 5A.9 本地开发期 LLM 兜底（IDE / CLI 调试）

生产环境的平台 LLM 走客户端注入，但开发期常常**脱离 LoongClaw 客户端单跑** server.py（IDE F5 调试 / CLI 跑测试 / 单跑某个工具看效果）。这种场景下没有客户端注入 env，标准 `OpenAI()` 默认调 `https://api.openai.com` 拿不到 key 直接 401。

**官方推荐姿势：开发者各自配自己的 OpenAI / DeepSeek / 百炼 key + 显式开发标记触发兜底。**

> ⚠️ **平台 token 不签发给开发者**。dev token / CLI 一键拉 token 等方案我们均不提供——平台 token 是绑定"客户端运行时 + 最终用户"的 nonce 凭据，签发给开发者长期持有 = 计费归属混乱 + 绕过白名单审计 + token 泄漏风险。如果你需要**用平台同款模型测口径**，直接装 LoongClaw 客户端跑一次端到端是最准确的做法（那条路径就是生产路径）。
>
> 开发期 MCP 工具逻辑（参数解析、数据流、业务规则）和 LLM 模型解耦，用自己的 OpenAI / DeepSeek key 测出能跑通，生产期换成平台模型也能跑通。release 前装客户端冒烟一遍即可校准 prompt 工程口径。

#### 推荐写法（fail-fast + 显式开发标记）

```python
import os
from openai import OpenAI

def _make_openai_client() -> OpenAI:
    """
    生产路径：客户端注入 OPENAI_BASE_URL → 走平台 LLM（用户余额扣费）
    开发路径：必须显式设 LOONGCLAW_DEV=1 + 自带 OpenAI/DeepSeek key
    其它情况：fail-fast，避免静默走错路径
    """
    # 1) 生产：客户端注入了 base_url（这是路径标志，比 OPENAI_API_KEY 更可靠
    #    —— 开发者本机可能因为别的项目就有 OPENAI_API_KEY）
    if os.environ.get("OPENAI_BASE_URL"):
        return OpenAI()  # 自动读 env

    # 2) 开发：显式 LOONGCLAW_DEV=1 才走兜底
    if os.environ.get("LOONGCLAW_DEV") == "1":
        api_key = os.environ.get("LOONGCLAW_DEV_OPENAI_API_KEY")
        # base_url 选填：用 OpenAI 官方 key 不填；用 DeepSeek/百炼/其它兼容 key 才填
        base_url = os.environ.get("LOONGCLAW_DEV_OPENAI_BASE_URL")
        if not api_key:
            raise RuntimeError(
                "LOONGCLAW_DEV=1 但未设 LOONGCLAW_DEV_OPENAI_API_KEY；"
                "请配置开发期 LLM key（自己的 OpenAI/DeepSeek/百炼 key 均可）"
            )
        return OpenAI(api_key=api_key, base_url=base_url)

    # 3) 既无生产 env 也无开发标记 = 配置错误，立即报错
    raise RuntimeError(
        "未检测到 LoongClaw 平台 LLM 环境变量（OPENAI_BASE_URL）。\n"
        "→ LoongClaw 客户端内运行：检查 manifest.usesPlatformLlm + 白名单是否生效。\n"
        "→ IDE/CLI 本地开发：设 LOONGCLAW_DEV=1 + LOONGCLAW_DEV_OPENAI_API_KEY=<你的 key>"
    )

# 调用处
client = _make_openai_client()
resp = client.chat.completions.create(model="qwen3.5-plus", messages=[...])
```

#### 三个关键设计点

1. **生产判断必须用 `OPENAI_BASE_URL`，不能用 `OPENAI_API_KEY`**——后者开发者本机可能因为别的项目就有，会误判为生产环境。
2. **变量名加 `LOONGCLAW_DEV_` 前缀**——避免和 `OPENAI_API_KEY` 这种系统级 OpenAI 配置混淆。
3. **`LOONGCLAW_DEV=1` 必须显式人工设置**——正常用户场景永远不会有这个 env，所以不存在"客户机巧合走兜底"的风险。极端情况客户端注入失败 → 直接 raise 报错（用户能看到），而不是静默走开发兜底 → 默默扣开发者钱 + 数据漏到第三方上游。

#### 开发者本机一次性配置示例

```bash
# ~/.zshrc 或项目本地 .env（别提交到仓库！）
export LOONGCLAW_DEV=1
export LOONGCLAW_DEV_OPENAI_API_KEY="sk-xxx"   # 你自己的 OpenAI key
# 用 DeepSeek/百炼 时再加：
# export LOONGCLAW_DEV_OPENAI_BASE_URL="https://api.deepseek.com/v1"
```

#### 上架前自查清单

- [ ] 在 LoongClaw 客户端内装一次跑通（确认生产路径生效，不是只跑过开发兜底）
- [ ] grep 代码确认没有把 `LOONGCLAW_DEV_*` 写到任何 manifest / 默认值 / 上传产物里（这些只能存在于开发者本机环境变量）
- [ ] 关掉所有 `LOONGCLAW_DEV*` env 直接跑 server.py，应该看到清晰的 fail-fast 报错而不是 401 / 502 / 静默调到 OpenAI 官方

---

## 6. postInstallCommands — 需要浏览器/字体/模型时怎么办

### 6.1 场景

`playwright` 需要下载 Chromium；`transformers` 需要拉模型权重；`matplotlib` 需要装中文字体。这些不能靠 `pip install` 解决，必须在安装完依赖**之后**跑额外命令。

### 6.2 写法

在 `.publish.json` 里加一项（或让 AI 帮你改 manifest）：

```json
{
  "postInstallCommands": [
    ["python", "-m", "playwright", "install", "chromium"],
    ["python", "-m", "nltk.downloader", "punkt"]
  ]
}
```

每个元素是一个命令数组（**不是**字符串，防 shell 注入）。LoongClaw 客户端会在 `pip install` 完成后逐个执行。

### 6.3 特殊处理

命令数组里如果第一个元素是 `"python"`，客户端**自动替换**为 venv 内的 Python 路径（Windows 是 `venv\Scripts\python.exe`，Unix 是 `venv/bin/python`）。你不用关心跨平台。

### 6.4 不要在 postInstallCommands 里重复写 `pip install -r requirements.txt`

LoongClaw 客户端在 `postInstallCommands` 之前**已经自动**跑过一次 `pip install -r requirements.txt`，所以你**不需要**再写一遍——会重复装包浪费时间。

```json
{
  "postInstallCommands": [
    // ❌ 不要这样写——客户端已经自动装过依赖了
    ["python", "-m", "pip", "install", "-r", "requirements.txt", "-i", "https://pypi.tuna.tsinghua.edu.cn/simple"],
    // ✅ 只写真正的 post-install 步骤
    ["python", "-m", "playwright", "install", "chromium"]
  ]
}
```

> **关于国内镜像源**：LoongClaw 客户端 v1.31+ 内嵌 Python 是隔离环境，**不再读用户系统 `pip config`**——一律默认走清华镜像 + pypi.org 兜底，并把镜像写入插件 venv 的 `pip.conf`/`pip.ini`。你的 `postInstallCommands` 里跑 `python -m pip install ...` 会自动用这套镜像，**不需要**自己加 `-i` 参数。如果你的插件依赖私有索引，用 `--extra-index-url` 即可。

---

## 7. Python 版本与依赖策略

### 重要：客户端 ≥1.31 已内嵌 Python 3.12.13

从 **LoongClaw 客户端 1.31** 开始，安装包自带 [python-build-standalone](https://github.com/astral-sh/python-build-standalone) 的 Python 3.12.13（mac-arm64 / win-x64 双平台）。**终端用户不需再自己安装 Python**。

对你（插件作者）意味着：

- 仍然需要在 `manifest.json` 声明 `requiredPython` 范围，客户端会校验内嵌版本是否在范围内
- 内嵌版是 **3.12**，你的插件锁死 `==3.10` / `<3.12` 会被拒绝——推荐写 `>=3.10,<3.13`
- 系统 python 不再被使用。插件在用户机器上跑的是内嵌解释器 + 插件 venv 里的依赖

### 7.1 `requiredPython` 版本范围

```json
{ "requiredPython": ">=3.10,<3.13" }
```

解析规则：
- 语法：`op major.minor[,op major.minor]`，op 支持 `>=` `>` `<=` `<` `==`
- 不填默认 `>=3.10`（兼容旧插件）
- 客户端 1.31+ 只查内嵌 Python（3.12.x）是否符合范围
- 不符合 → 提示「请联系插件作者放宽 `requiredPython` 范围」——不再诱导用户去装别的版本

> 代码依据：[mcp-store-python.ts](../loongclaw/gateway/mcp/mcp-store-python.ts) — `findPython()`。1.31 起仅读 `LOONGCLAW_BUNDLED_PYTHON_DIR` env，不再扫系统 PATH。

### 7.2 requirements.txt 避坑

**第一坑：C 扩展严格锁版**

```
# ❌ 风险
greenlet==2.0.1
# 问题：Windows 用户没 MSVC Build Tools → pip 要源码编译 → 失败

# ✅ 推荐
greenlet>=2.0
# pip 找预编译 wheel，三平台都能装
```

DevKit `publish.py` 会扫 `requirements.txt`，对以下包的严格锁版发出警告：
`greenlet / numpy / scipy / pandas / lxml / pillow / cryptography / grpcio / psycopg2`

> 代码依据：[publish.py:111-135](./src/loongclaw_devkit/publish.py)

**第二坑：跨平台特定依赖**

`uvloop` 只支持 Linux/macOS，Windows 装不上。用环境标记：

```
uvloop>=0.17; sys_platform != "win32"
```

### 7.3 离线 wheel 机制

DevKit 发布时会**提前下载所有 wheel 包**到 `_vendor/` 目录，打进 `project.zip`。用户安装时即使无网也能装。

客户端 `pip install` 时会优先用 `_vendor/` 里的 wheel。

### 7.X LoongClaw 客户端从哪里下载依赖（P-002 后,2026-04-28+）

**MCP 作者须知**:从 LoongClaw 客户端 v1.32+(对应 P-002 阶段 3 落地)起,客户端装 MCP 时 pip 链路改成:

```
客户端 pip install
  → 优先 https://pypi.loongclaw.net.cn/simple/  (LoongClaw 官方私有 PyPI,我们主动准备的 wheels)
  → 找不到 fallback 到 https://pypi.tuna.tsinghua.edu.cn/simple  (清华兜底)
  → ~~pypi.org 直连~~  (弃用,境外不稳定)
```

**对你 MCP 作者的影响**: **零代码改动**。客户机器 pip 自动走 LoongClaw mirror,你不用改 requirements.txt 任何东西。

**LoongClaw 自研 MCP 的额外收益**:发版时跑 `loongclaw-server:/usr/local/bin/prep-loongclaw-wheels.sh <requirements.txt>` 把依赖灌进 mirror,客户机器装 MCP 时**字节级一致地**从我们服务器拉(避免不同客户机器装出不同版本 venv)。

**Playwright Chromium driver**:客户端也注入 `PLAYWRIGHT_DOWNLOAD_HOST=https://pwbin.loongclaw.net.cn`,你的 MCP 用 playwright 时 chromium 二进制走我们 nginx 反代缓存(国内带宽 13MB/s,境外直连可能 1MB/s 以下)。

**故障演练**:客户报"装不上",让客户在 `~/.loongclaw/pip-mirror.json` 写 `{"indexUrl":"https://pypi.tuna.tsinghua.edu.cn/simple","extraIndexUrl":"https://pypi.org/simple"}` → 重启客户端 → 走清华全量(等同 P-002 之前状态)。

详见 [plan/P-002](https://github.com/loongclaw/loongclaw/blob/main/plan/P-002-private-pypi-mirror.md)(workspace 内)。

---

## 8. 加密发布（Cython 自动编译）

### 8.1 为什么要加密

你的核心算法、接入的 API Key 逻辑、独特的数据处理流程……不想被用户直接 `cat core.py` 看光光。

### 8.2 DevKit 自动做了什么

发布时自动把除以下白名单外的所有 `.py` 编译成 `.so`（macOS/Linux）或 `.pyd`（Windows）：

**白名单（始终保留明文）**：
- `publish.py` —— 你的发布脚本（硬红线排除出 zip）
- `__init__.py` / `setup.py` —— Python 打包约定

> **排除非 MCP 运行时文件（v0.9.0）**：代码/测试/数据仪表板等非运行时文件，在项目根创建 `.mcpignore`（gitignore 语法）完全排除出 zip。示例：
> ```gitignore
> # 当前项目根下的 .mcpignore
> tests/
> dashboard/
> docs/
> *.log
> ```

**公开（public）插件白名单额外加**：
- `server.py` —— 入口文件保持明文，客户端直接 `python server.py` 启动

**付费（private）插件**不在此列——`server.py` 会被**强制加密**（见 §8.3）。其他所有 `.py` 都会被加密。流程：

```
core.py  →  复制为 core.pyx  →  Cython 编译为 core.cpython-312-darwin.so  →  删除 .pyx 和 .c 中间文件
```

<a id="83-私有插件全量加密壳模式"></a>

### 8.3 私有插件全量加密（壳模式）

**2026-04-22 安全升级**：private 付费插件的 `server.py` 默认也会被加密，防止核心业务逻辑源码泄露。

#### 8.3.1 为什么

历史版本的 DevKit 永久豁免 `server.py` 不加密——因为 Cython 不支持直接编译含 `if __name__ == "__main__":` 启动块的入口文件。但很多开发者把核心调用链直接写在 `server.py` 里（例如 `server.py` 调 `core.process()` 再调 `pipeline/step_1.py`），结果：**所有被 import 的模块都加密了，但 import 它们的 server.py 自己保留明文**——攻击者 `cat server.py` 就能反推整条业务逻辑。

#### 8.3.2 怎么做（自动，无需手工干预）

```
```text
你写的 server.py
    ↓
DevKit 加密成 server_impl.cpython-XXX.so
    ↓
DevKit 自动生成壳 server.py 塑回 zip（v0.10.2 起包含 sys.path 自注入）：
    #!/usr/bin/env python3
    import os, sys
    # WHY 见 publish.py _SHELL_SERVER_PY 内详细注释（防 REV3/PERM5 类回归）：
    # 嵌入式 Python（python._pth 隔离模式）和 `python -I` 启动时不会自动把
    # 脚本目录加进 sys.path，bare 导入会 ModuleNotFoundError，子进程在 MCP
    # stdio 握手前秒死，客户端只看到 -32000 Connection closed。
    sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
    from server_impl import mcp
    if __name__ == "__main__":
        mcp.run(transport="stdio")
    ↓
客户端 `python server.py` 启动 → 加载 server_impl.so → 拿到 mcp 实例 → 启动 stdio 服务
```

`manifest.entrypoint` 仍是 `server.py`，客户端启动逻辑零改动，**老版本客户端完全兼容**。

#### 8.3.3 开发者只需要遵守一个约定：FastMCP 实例命名为 `mcp`

```python
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("my-private-mcp", instructions="...")  # ← 实例名必须是 mcp

@mcp.tool()
def tool_x(...):
    ...

if __name__ == "__main__":
    mcp.run(transport="stdio")     # ← FastMCP 标准写法，本地直跑也能走
```

**为什么要叫 `mcp`**：壳脚本需要从加密模块里拿到 FastMCP 实例启动服务。选 `mcp` 这个名字是因为：
- FastMCP 官方文档示例统一用 `mcp = FastMCP(...)`
- DevKit 项目骨架 (`create_mcp_project`) 生成的模板就是 `mcp = FastMCP(...)`
- 不需要开发者额外写 `def main()` 或任何额外代码

> **为什么不用 `runpy.run_module` 触发加密模块的 `if __name__ == "__main__"` 块？**
> Python 标准库 `runpy` 不支持扩展模块（`.so`/`.pyd`）——Cython 编译产物没有 Python code object 可供重放，会报 `ImportError: No code object available`。因此采用 `from server_impl import mcp` + 显式调 `mcp.run()` 的方式。

#### 8.3.4 三层 Fail-Close 防护

| 时机 | 检查 | 失败后果 |
|---|---|---|
| 发布前预校验 | `access=private` 且 `server.py` 未定义 `mcp = FastMCP(...)`（实例名不是 `mcp`） | 立即 abort，提示改名 |
| 加密过程中 | `access=private` 且 `server.py` Cython 编译失败回退明文 | 立即 abort（不允许壳模式下 server.py 回退明文） |
| 加密完成后 | `access=private` 且有任何非 `__init__.py` 的明文业务 `.py` 残留 | 立即 abort |

这三层防护闭合了「付费插件源码泄露」的根因——**private + 明文业务 .py** 的组合永远不可能进入 project.zip。

### 8.4 跨平台

DevKit 只编译**当前平台**的产物。用户装你的插件时，客户端会**在用户机器上重新跑** `pip install`，但 `.so/.pyd` 是二进制，**不会重新编译**。

**所以**：macOS 开发者发布 → `.so` 打进 zip → Windows 用户装了 → 缺 `.pyd` → 报错 `ImportError: no such module`。

**解决方案二选一**：

1. **在每个目标平台各跑一次 DevKit 发布**，最终 zip 里同时包含 `core.cpython-312-darwin.so` + `core.cp312-win_amd64.pyd` + `core.cpython-312-x86_64-linux-gnu.so`（server.py 加密后是 `server_impl.cpython-XXX.so`，同理需要各平台产物）。
2. **不用 Cython（仅 public 插件）**：把不需要加密的文件整个放进 `.mcpignore` 排除出包；或者把真正保留明文的逻辑拆到独立包中以越过 Cython 编译失败。private 插件禁止明文业务 `.py`——Fail-Close 第 3 层会阻断。

> **DevKit 路线图**：未来会加 GitHub Actions 模板一次性跨平台编译。当前阶段建议方案 1。

### 8.5 ⚠️ 禁忌

- **禁止** 把 `.pyx` 源码上传到商店。DevKit 会自动删除中间文件，但如果你自己手动打包，要确保 zip 里没有 `.pyx`。
- **禁止** 付费（private）插件把 FastMCP 实例叫成 `app`/`server` 等其他名字——壳模式约定必须命名为 `mcp`，预校验会拦截。
- **禁止** 把敏感信息（API Key、内部 URL）写死在代码里——就算 `.so` 也能被逆向工程师用 Ghidra/IDA 看出大概逻辑。敏感信息应用 `configFields` 让用户填。

---

## 9. 商店访问控制（public / private）

### 9.1 两种访问类型

| 值 | 谁能下载 | 使用场景 |
|---|---|---|
| `"public"` | 所有**已登录** LoongClaw 用户 | 开源插件、免费工具、推广插件 |
| `"private"` | **授权列表内**的用户 | 企业定制、付费插件、内测阶段插件 |

**⚠️ 关键澄清**：

1. `private` **不等于**「付费」——它只是「需要授权」。付费模式本身由商务流程处理（付款后把用户加入授权表），商店代码不碰价格。
2. `accessType` 必填且只能是 `"public"` 或 `"private"`。**Fail-Close 三层防护**（Cloud 安全升级后）：
   - DevKit `publish.py` 上传前本地预校验：缺失/非法 → 立即退出，不浪费构建时间
   - Cloud `/v1/store/upload` 入口：白名单拒绝，返回 400 错误
   - Cloud registry 重建时：遗留的未知值降级为 `"private"`（更安全的默认，不是 `public`）
   任何拼写错误（如 `"paid"` / `"premium"`）都会被拒绝上传。

> 代码依据：[store-upload.ts](../loongclaw-cloud/src/routes/store-upload.ts) —— `ALLOWED_ACCESS_TYPES` + `validateManifest` 严格校验

### 9.2 DevKit 怎么设

在 `.publish.json` 里**必填**：

```json
{ "access": "private" }
```

`create_mcp_project` 生成的模板里 `access` 字段是**空字符串**（`""`），并带 `⚠️ 必填` 注释——你或 AI 必须显式改为 `public` 或 `private` 后才能上传，否则 `publish.py` 会在本地预校验阶段报错退出。这是有意设计：防止 AI 走神漏填导致插件被默认公开。

也可以发布时让 AI 说："发布 weather-mcp，版本 1.0.0，access 设为 private"——AI 会依次调 `update_mcp_config(field="access", value="private")` + `update_mcp_config(field="version", value="1.0.0")` + `publish_mcp(...)`。

### 9.3 客户端行为

| 情况 | 用户体验 |
|---|---|
| public 插件 | 商店直接「安装」按钮 |
| private 插件，用户已授权 | 正常安装 |
| private 插件，用户未授权 | 按钮变灰，提示「此插件需联系客服开通」 |
| private 插件已安装，随后授权被撤销 | 下次启动时加载失败，UI 提示需重新开通 |

### 9.4 授权数据存哪

Cloud 端有个表 `mcp_permissions (user_id, mcp_id)`。管理员在后台添加记录即开通权限。

下载时校验逻辑：
```
用户请求下载 private 插件
 → Cloud 查 accessTypeCache（5 分钟 TTL）
 → private → 查 mcp_permissions 表
 → 有记录 → 放行；无记录 → 403
```

> 代码依据：[store-download.ts:26-45](../loongclaw-cloud/src/routes/store-download.ts) —— `accessTypeCache` + 5 分钟 TTL

---

## 10. 付费/授权模式如何落地

LoongClaw MCP 商店目前的「付费插件」模式：

### 10.1 流程

```
1. 开发者发布插件时 accessType: "private"
2. 用户在客户端看到插件 → 点「安装」
3. 客户端调用 Cloud API → 未授权 → 返回 403 + 友好提示
4. 用户看到提示联系客服 / 开发者
5. 商务走完付款流程，管理员在后台 mcp_permissions 表加记录
6. 用户在客户端重试安装 → 通过
```

### 10.2 你（开发者）需要做什么

1. **发布时设 `access: "private"`**
2. **提供联系方式**（微信/邮箱，在 `description` 里写清楚）
3. **收款 + 授权**（和 LoongClaw 管理员协调，或自己走付款然后通知平台加记录）

### 10.3 运营方做什么

- 维护 `mcp_permissions` 表（加/删记录）
- 管理 Store Key 体系（谁能上传插件）
- 提供授权管理后台 UI（规划中）

### 10.4 不支持的模式（现阶段）

- ❌ 按调用次数计费（客户端不上报调用数据到 Cloud）
- ❌ 订阅制自动扣费（无订阅系统）
- ❌ 零散充值余额兑换（积分和 LLM 余额是分开的）

如需这些模式，请联系 LoongClaw 团队走定制化方案。

---

## 11. 更新与增量发布

### 11.1 用户侧的增量更新体验

用户点「更新」时，LoongClaw 并**不**无脑重新下载。流程：

```
1. 拉新版 manifest.json
2. 对比 sourceArchiveHash：
   ├─ 相同 → 整个目录从旧版「硬链接」复用（O(1)，零下载，秒更新）
   └─ 不同 → 重新下载 project.zip + 重建 venv
3. 失败自动回滚到旧版
```

**硬链接**是什么：类比「快捷方式的升级版」，两个路径指向磁盘同一份数据，不占额外空间，切换比复制快 1000 倍。

**失败回滚**：旧目录在更新期间先改名 `.old`，成功才删，失败则改回。用户永远不会遇到「更新一半插件坏掉」。

> 代码依据：[mcp-store.ts:230-370](../loongclaw/gateway/mcp/mcp-store.ts)

### 11.2 开发者要做什么

**什么都不用做。** DevKit `publish_mcp` 每次都会：
1. 重新算 zip 的 SHA-256 → 写入 `sourceArchiveHash`
2. 代码没变 → zip 相同 → hash 相同 → 用户秒更新
3. 代码变了 → zip 变了 → hash 变了 → 用户重新下载

你只需要 bump `version` 字段。

### 11.3 版本号规则

- 建议使用语义版本：`主.次.修订`（如 `1.2.3`）
- **客户端判断「是否有更新」用字符串不等（`local.version !== entry.version`），不做大小比较也不做 semver 解析**——任何不相等的版本号都会触发更新，哪怕新版本号「更小」
- 因此**每次发布都必须 bump version**，否则客户端认为「无更新」；反之只要改了版本号就一定能触发

> 代码依据：[mcp-store.ts:105 / 440](../loongclaw/gateway/mcp/mcp-store.ts) —— `local.version !== entry.version`

### 11.4 所有权保护

插件一旦被你上传过，manifest 里会注入 `uploadedBy: "你的 userId"`。之后**别人用自己的 Store Key 上传同 ID 插件会被 403 拒绝**——防止恶意覆盖。

> 代码依据：[store-upload.ts:109-130](../loongclaw-cloud/src/routes/store-upload.ts) —— `existing.uploadedBy !== userId` 拒绝 + 新记录注入 `uploadedBy`

---

## 12. 在其他 AI 客户端中使用

### 12.1 LoongClaw 桌面客户端

在 MCP 商店中搜索 `loongclaw-devkit` 一键安装。

### 12.2 Claude Desktop / Cursor / VS Code

在 MCP 配置文件中添加：

```json
{
  "mcpServers": {
    "loongclaw-devkit": {
      "command": "uvx",
      "args": ["loongclaw-devkit"]
    }
  }
}
```

### 12.3 命令行直接使用

也可以不通过 AI，直接命令行运行：

```bash
# 启动 MCP server（开发调试用）
loongclaw-devkit

# 或用 Python 模块方式
python -m loongclaw_devkit
```

---

## 13. 常见报错 FAQ

### Q1. 安装后工具不可用，AI 说「不知道有这个工具」

**症结**：工具名双重前缀（第 2.2 节原则一）。

**检查**：
```bash
grep "def mcp__" server.py  # 应该没有任何匹配
```

**修正**：去掉所有以 `mcp__` 开头的函数名，只写功能名。

### Q2. `publish_mcp` 报 `LOONGCLAW_STORE_KEY` 缺失

```bash
# 方式 1：环境变量
export LOONGCLAW_STORE_KEY="lc-store-xxxxx"

# 方式 2：.publish.json
{ "token": "lc-store-xxxxx" }
```

Store Key 获取：[loongclaw.net.cn/dev/](https://loongclaw.net.cn/dev/) 登录 → 申请开发者 → 审批通过 → 生成 Store Key。

### Q3. 上传被拒 `无权更新此插件（不是原上传者）`

**原因**：你的 Store Key 对应的 userId ≠ 插件第一次上传时的 userId（manifest 里的 `uploadedBy`）。

**解决**：
- 用原 userId 的 Store Key 上传
- 或换个 `id`（改 `.publish.json` 里的 id 字段）
- 或联系管理员清掉旧所有权

### Q4. Cython 编译失败，某个 `.py` 没被加密

DevKit 的行为：**编译失败 → 自动回退为明文 + 打日志**，不中断发布。

**常见原因**：
- macOS 缺 Xcode CLI：`xcode-select --install`
- Windows 缺 MSVC Build Tools：装 Visual Studio Build Tools 或从 python.org 装 Python（自带）

**临时方案**（仅 public 插件）：在项目根 `.mcpignore` 中添加该文件路径把它完全排除出包（而不是保留明文，生产发布前修好编译环境再重发）。

### Q5. Windows 用户装不上，报 C 扩展编译错误

**99% 原因**：`requirements.txt` 里有严格锁版的 C 扩展（`numpy==1.24.0` 等），Windows 没预编译 wheel。

**解决**：改成 `>=` 宽松版本：

```
# 改之前
numpy==1.24.0

# 改之后
numpy>=1.24,<2.0
```

### Q6. `configFields` 没有被自动识别

DevKit 靠正则扫 `os.environ.get("KEY", "default")`。以下写法**不会**被识别：

```python
# ❌ 不识别
k = "MY_KEY"
v = os.environ.get(k, "")

# ❌ 不识别（非字符串字面量）
v = os.environ.get(MY_CONST)

# ✅ 识别
v = os.environ.get("MY_KEY", "")
```

实在不行，**手动改 manifest.json**（让 AI 帮你改，然后重新上传）。

### Q7. 更新了代码但用户客户端说「无更新」

**可能原因**：
1. 没 bump version（最常见）
2. Cloud registry 更新失败（上传返回 ok 但 registry 没同步，5 分钟内缓存过期）
3. 用户客户端缓存（重启客户端）

**排查**：
```bash
# 查 Cloud registry 最新版本
curl https://api.loongclaw.net.cn/v1/store/mcp/registry.json | jq '.servers[] | select(.id == "your-id")'
```

### Q8. 本地测试 `python server.py` 正常，上传后装不起来

**常见原因**：
- `requirements.txt` 漏了运行时依赖（你本地 venv 有但 requirements 里没写）
- `_vendor/` wheel 下载失败（检查 `publish.py` 输出的警告）
- 用了相对路径导入 `__file__` 的资源，但 zip 解压层级错了

**排查**：解压 `project.zip` 手动 `pip install -r requirements.txt && python server.py` 跑一遍。

### Q9. 发布报 `.publish.json 缺少必填字段` / `字段 access 必填且只能是 ['private', 'public']`

**原因**：v0.6.0 起 `publish.py` 在上传前本地预校验 `id` / `name` / `description` / `version` / `author` / `access` 六个字段，缺任何一个或 `access` 不在白名单内 → 立即退出。

**修法**：编辑 `.publish.json` 补全字段：

```json
{
  "id": "weather-mcp",
  "name": "和风天气插件",
  "description": "查询全球城市天气，支持中英文城市名（≥10 字符）",
  "version": "1.0.0",
  "author": "你的名字",
  "access": "public"
}
```

或让 AI 调 `update_mcp_config(field="author", value="你的名字")` / `update_mcp_config(field="access", value="public")` 补全（v0.9.0 起已删除 `--reconfigure` 交互式配置，统一走 AI 工具路径）。

**为什么这么严**：2026-04-22 发生过「5 个 MCP 上传后全被标成 `public` 可免费下载」的事故，根因是旧版本对 `access` 字段有默认降级 fallback。修复后本地 + Cloud 双层拦截，宁可报错也不静默默认。

### Q10. 上传报 `上传被中断（Broken pipe / Connection reset）`

**✅ v0.8.0 已修复**：DevKit 现在优先使用 `requests` + `requests-toolbelt.MultipartEncoder` 做流式上传（带 `Expect: 100-continue` + 分段 timeout），不再把 zip 整体拼进内存。**升级命令**：

```bash
pip install -U loongclaw-devkit  # 应为 >= 0.8.0
```

**如果升级后仍报依赖缺失错误**：v0.9.0 起 `setuptools / wheel / Cython / pip / pathspec / requests / requests-toolbelt` 已声明为 devkit 运行时依赖，`pip install -U loongclaw-devkit` 或 `uv tool install loongclaw-devkit` 自动带齐。如果你用了 `pipx` 独立 venv 或手工 pip install 老版本，**修复**：

```bash
pip install -U loongclaw-devkit  # 应 >= 0.9.0
# 重新让 AI 调 publish_mcp 即可
```

**最终兜底方案（极端情况下）**：如果以上都不行，可以用 curl 手动上传——本地打包产物在 `.loongclaw-build/staging/`：

```bash
curl -X POST https://api.loongclaw.net.cn/v1/store/upload \
  -H "Authorization: Bearer lc-store-YOUR_STORE_KEY" \
  -F "manifest=@.loongclaw-build/staging/manifest.json;type=application/json" \
  -F "archive=@.loongclaw-build/staging/project.zip;type=application/zip" \
  -F "accessType=public"
```

三个字段名不能写错：`manifest`、`archive`、`accessType`（`public` 或 `private`，必须和 `.publish.json` 的 `access` 字段一致）。成功响应：`{"ok": true, "id": "...", "version": "..."}`。

**注意**：curl 绕过的只是 DevKit 本地上传实现，**不绕过任何服务端安全检查**——Store Key 认证、manifest 字段白名单、私有包明文扫描、所有权（`uploadedBy`）校验在服务端全都照跑。

---

## 14. 参考代码：duanju-mcp 实战样例

`duanju-mcp` 是短剧自动发布插件，LoongClaw 内部使用，是一个**复杂场景**样例。

**关键看点**：

1. **多模块拆分**：[mcp_server.py](../duanju-mcp/mcp_server.py) 作为入口，逻辑分散在 `mcp_preflight_tools.py` / `mcp_query_tools.py` / `mcp_pipeline_tools.py`
2. **configFields 复杂用法**：比特浏览器端口、发布路径等 15+ 配置项
3. **postInstallCommands**：装 Playwright Chromium
4. **companionSkill**：关联 `duanju-full-pipeline-executor` Skill

建议当作写复杂插件时的参考——结构清晰、分层合理。

---

## 15. 附录

### 附录 A：命令行紧急逃生（高级用法）

DevKit 的日常路径是让 AI 调工具（[§3](#3-devkit-提供的工具ai-first)）。下面的命令行仅用于 **devkit MCP 服务挂了** 或 **CI 流水线** 场景，日常别用。

```bash
# 启动 DevKit 自己作为 MCP server（调试用）
loongclaw-devkit

# 紧急逃生：直接跑 devkit 内部的 publish.py
# 1) 找到 devkit 安装位置
DEVKIT_DIR=$(python -c "import loongclaw_devkit, pathlib; print(pathlib.Path(loongclaw_devkit.__file__).parent)")

# 2) 在你的 MCP 项目目录执行
cd ~/projects/weather-mcp
python "$DEVKIT_DIR/publish.py" --auto --json-output --version 1.0.1
python "$DEVKIT_DIR/publish.py" --auto --json-output --access private --version 1.0.1
python "$DEVKIT_DIR/publish.py" --auto --json-output --no-upload        # 只打包不上传
```

> v0.9.0 已删除 `--reconfigure` 和交互模式；`--access` 只接受 `public` / `private`。配置变更统一走 `update_mcp_config` 工具。

### 附录 B：Store Key 申请流程

1. 访问 [loongclaw.net.cn/dev/](https://loongclaw.net.cn/dev/) 登录
2. 提交开发者申请（填个人/公司信息、开发目的）
3. 等待管理员审批（通常 1 个工作日）
4. 审批通过后生成 Store Key（形如 `lc-store-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`）
5. 配置到环境变量或 `.publish.json`

**⚠️ Store Key 保管**：
- 不要上传到 Git（`.publish.json` 默认在 `.gitignore` 里的话 OK；否则用环境变量）
- 泄露后立即到开发者中心吊销 + 重新生成

### 附录 C：调试技巧

**本地测试 MCP server**（不发布）：

```bash
cd ~/projects/weather-mcp

# 方式 1：直接跑（会等待 stdin）
python server.py
# 输入 JSON-RPC 请求（高级用户）

# 方式 2：MCP Inspector（可视化调试）
npx @modelcontextprotocol/inspector python server.py
# 浏览器打开 http://localhost:5173 图形化调工具
```

**装到 LoongClaw 本地调试**（不过 Cloud）：

编辑 `~/.loongclaw/mcp-servers.json`，手动加一项（CC 标准格式，Claude Desktop / Cursor 同结构）：

```json
{
  "mcpServers": {
    "weather-mcp-dev": {
      "command": "python",
      "args": ["/Users/you/projects/weather-mcp/server.py"],
      "env": { "HEFENG_API_KEY": "..." }
    }
  }
}
```

重启 LoongClaw 即生效。这种方式**不走商店**，不需要上传。

### 附录 D：能力边界速查

| 能力 | DevKit 支持？ | 备注 |
|---|---|---|
| FastMCP + stdio | ✅ | 核心 |
| HTTP/SSE transport | ❌ | 未来支持 |
| Cython 加密 `.py` | ✅ | 自动 |
| 离线 wheel 打包 | ✅ | 自动 |
| 增量更新 | ✅ | 对开发者透明 |
| configFields 自动生成 | ✅ | 扫 `os.environ.get` |
| 多 Python 版本约束 | ✅ | `requiredPython` |
| postInstallCommands | ✅ | manifest 字段 |
| Private 授权 | ✅ | `accessType: private` |
| 跨平台一次性编译 | ⚠️ | 需各平台各发一次 |
| Node.js runtime | ❌ | 类型定义预留，未落地 |
| 按调用计费 | ❌ | 不支持 |
| MCP Resources / Prompts | ⚠️ | FastMCP 原生支持，但 LoongClaw 当前主要消费 tools |

---

## License

MIT

---

**文档结束。** 有问题？到 [LoongClaw 开发者中心](https://loongclaw.net.cn/dev/) 反馈。
