Metadata-Version: 2.4
Name: loongclaw-devkit
Version: 0.9.1
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-分钟上手)
2. [一个合格的 MCP 插件长什么样](#2-一个合格的-mcp-插件长什么样)
3. [DevKit 提供的工具（AI-first）](#3-devkit-提供的工具ai-first)
4. [manifest.json 完整字段速查表](#4-manifestjson-完整字段速查表)
5. [configFields — 让用户填 API Key](#5-configfields--让用户填-api-key)
6. [postInstallCommands — 需要浏览器/字体/模型时怎么办](#6-postinstallcommands--需要浏览器字体模型时怎么办)
7. [Python 版本与依赖策略](#7-python-版本与依赖策略)
8. [加密发布（Cython 自动编译）](#8-加密发布cython-自动编译)
9. [商店访问控制（public / private）](#9-商店访问控制public--private)
10. [付费/授权模式如何落地](#10-付费授权模式如何落地)
11. [更新与增量发布](#11-更新与增量发布)
12. [在其他 AI 客户端中使用](#12-在其他-ai-客户端中使用)
13. [常见报错 FAQ](#13-常见报错-faq)
14. [参考代码：duanju-mcp 实战样例](#14-参考代码duanju-mcp-实战样例)
15. [附录](#15-附录)

---

## 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.9.1 Fix（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` 环境变量 → 内置 200 MB 兜底（三层 fallback）。>80 MB 打印警告，超限直接拒绝上传。
> 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 运行时依赖，`uv tool install loongclaw-devkit` 一条命令可用。
>
> 老 `.publish.json` 里的 `skipEncrypt` 字段会被自动丢弃并提示迁移。

### 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 自动生成 3 行壳 server.py | 任何业务 `.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"`（`"node"` 类型定义里预留但未落地） |
| `entrypoint` | string | ✅ | 入口文件，始终 `"server.py"` |
| `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`）。声明后客户端会在 spawn 子进程时自动注入 `OPENAI_BASE_URL` / `OPENAI_API_KEY` 指向本地代理（最终走 LoongClaw Cloud 计费）。需 Cloud admin 后台把 mcpId 加入白名单后才生效。**只对 OpenAI Chat Completions 协议透明兼容**——MCP 内部用 `openai` SDK 即可零代码切换。**首次调用偶发 502 + token 自动续约 + 禁止落盘 key 等开发者注意事项见 `AGENTS.md` 「使用 LoongClaw 平台 LLM」章节** |
| `mcp` | object | ❌ | MCP 标准字段（transport/command/args）。填了会优先于自动推导 |

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

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

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

---

## 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 手动覆盖（密码类型）

自动生成的字段是纯文本输入框。如果字段是**密码/API Key**，你想让 UI 打码显示，可以在发布后手动编辑 `.publish.json` 或用参数重新发布。或者直接在 `publish_mcp` 后，让 AI 帮你改 manifest。

手动编辑时加 `"type": "password"`：

```json
{ "key": "HEFENG_API_KEY", "label": "和风天气 API Key", "type": "password", "required": true }
```

### 5.4 用户视角

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

---

## 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`）。你不用关心跨平台。

---

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

### 7.1 `requiredPython` 版本范围

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

解析规则：
- 语法：`op major.minor[,op major.minor]`，op 支持 `>=` `>` `<=` `<` `==`
- 不填默认 `>=3.10`（兼容旧插件）
- 客户端安装时查系统 PATH 里所有 Python，挑第一个符合的
- 都不符合 → 给用户**中文错误提示**（如"你的 Python 3.9 版本过低，建议安装 3.12"）

> 代码依据：[mcp-store-python.ts:147-210](../loongclaw/gateway/mcp/mcp-store-python.ts) —— `findPython()`

### 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。

---

## 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 怎么做（自动，无需手工干预）

```
你写的 server.py
    ↓
DevKit 加密成 server_impl.cpython-XXX.so
    ↓
DevKit 自动生成壳 server.py 塑回 zip：
    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/) 反馈。
