# service-plugins **Repository Path**: freen/service-plugins ## Basic Information - **Project Name**: service-plugins - **Description**: apex-mcp-bridge 官方插件生态。开箱即用、自包含的服务插件 —— 文件管理、数据报表、网络打印等。用自然语言向 AI 智能体描述需求,即可自动生成你需要的插件。 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-07-27 - **Last Updated**: 2026-07-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README

Host API Version Python MCP Protocol License

# apex-mcp-bridge 服务插件集 [apex-mcp-bridge](https://gitee.com/freen/apex-mcp-bridge) 的官方插件生态仓库。每个插件为一个独立服务能力 —— 文件管理、数据报表、网络打印等。**现已全面扩展,完整支持 [MCP(Model Context Protocol,模型上下文协议)](https://modelcontextprotocol.io/)(2026-07-28)** —— 所有插件均原生暴露为 MCP 工具,实现与 AI 智能体的无缝集成。 > **设计哲学**:把插件文件夹拷贝到 `service_plugins/` 下,bridge 自动检测并动态加载,无需重启、无需接线、无需注册。 > > **真正的亮点**:你甚至不需要亲手写一行代码。本仓库提供了完整的标准化插件框架 —— 所有方法的入参规范、配置模板、stdin/stdout 通信协议、统一响应格式均已预置就绪。只需将这些模板作为约束交给 AI 智能体,用自然语言描述你想要的服务(打印机插件?数据报表?),智能体即可在框架内自动生成完整的、可直接运行的插件代码。脚本怎么读参数、怎么返回结果、怎么处理错误 —— 这些都已在模板中写好,无需你操心。优秀社区作品将收录到官方插件库。 > ⚠️ **安全提醒**:插件会在你的主机上执行 Python 代码。请仅安装来自官方仓库或你完全信任来源的插件。如果你从非官方渠道获取了插件,且不了解其代码内容,**请不要使用** —— 它可能包含恶意逻辑,危及你的系统和数据安全。 > **相关项目:** > - [apex-esp32-s3-v6](https://gitee.com/freen/apex-esp32-s3-v6) — 底层硬件框架 (ESP32-S3) > - [apex-esp32-c3-v6](https://gitee.com/freen/apex-esp32-c3-v6) — 底层硬件框架 (ESP32-C3) > - [service-plugins](https://gitee.com/freen/service-plugins) — 插件框架(本仓库) > - [apex-mcp-bridge](https://gitee.com/freen/apex-mcp-bridge) — 核心项目框架 ## 目录 - [已有插件](#已有插件) - [架构概览](#架构概览) - [快速开始:使用一个插件](#快速开始使用一个插件) - [插件开发指南](#插件开发指南) - [目录结构](#目录结构) - [plugin.json 规范](#pluginjson-规范) - [通信协议](#通信协议) - [方法处理器模板](#方法处理器模板) - [响应格式](#响应格式) - [错误处理](#错误处理) - [公共工具模块](#公共工具模块) - [常见问题](#常见问题) ## 已有插件 | 插件 | 服务类型 | 说明 | |------|----------|------| | [fnos-smb-file-manager](./fnos-smb-file-manager/) | `file-manager` | FNOS SMB 文件管理 —— 通过 MCP 工具实现列出文件、创建目录、删除文件/空目录 | > 插件生态已全面扩展。所有插件均原生暴露为 MCP 工具 —— 安装后即可通过任意 MCP 兼容客户端调用。更多插件正在积极开发中。 ## 架构概览 ``` ┌──────────────────────────────┐ │ apex-mcp-bridge │ ← Rust 宿主,以 Docker 运行于 FNOS │ (插件自动发现) │ ├──────────────────────────────┤ │ service_plugins/ │ ← 本仓库 │ ┌──────────────────────────┐ │ │ │ fnos-smb-file-manager/ │ │ ← 自包含插件 │ │ plugin.json │ │ • manifest(身份标识) │ │ requirements.txt │ │ • methods(方法定义) │ │ *.py(处理器脚本) │ │ • runtime(运行环境) │ └──────────────────────────┘ │ • config(私有配置) │ ┌──────────────────────────┐ │ • handler 脚本 │ │ 未来更多插件... │ │ │ └──────────────────────────┘ │ └──────────────────────────────┘ ``` **核心设计原则:** 1. **自包含** —— 每个插件是一个独立文件夹。拷贝即用。 2. **声明式清单** —— `plugin.json` 是唯一真相来源:描述插件是什么、暴露哪些方法、参数长什么样、如何执行。 3. **stdin/stdout 协议** —— bridge 启动处理器脚本后通过标准输入输出通信。没有共享内存、没有 RPC 框架、没有 import 耦合。 4. **进程隔离** —— 每次方法调用启动全新 Python 进程。一个处理器崩溃不会影响 bridge 或其他插件。 5. **配置自管** —— 插件私有配置(服务器地址、凭证等)放在插件自身的 `plugin.json` 中,由处理器直接读取。bridge 完全不需要了解这些。 ### 一次 MCP 工具调用的完整流程 ``` MCP 客户端 → bridge (MCP 服务器) → 在 plugin.json 中发现方法 → 启动: python3 <方法名> → 通过 stdin 写入参数 JSON → 通过 stdout 读取响应 JSON → 返回 MCP 工具结果给客户端 ``` ## 快速开始:使用一个插件 1. 下载插件文件夹,拷贝到 bridge 的插件目录: ```bash cp -r fnos-smb-file-manager/ /path/to/apex-mcp-bridge/service_plugins/ ``` bridge 自动检测并动态加载,无需重启。 > 依赖安装是自动的 —— bridge 启动时会扫描所有 `requirements.txt` 并安装。 2. **在管理界面中完成配置** —— 插件下载后为标准化出厂默认值。打开 `apex-mcp-bridge` 的插件管理页面进行调整: | 你必须配置 | 说明 | |---|---| | **服务地址**(`serverUrl`) | 告诉插件目标服务跑在哪台服务器上。每个插件对接一个具体的服务端 —— 在这里填入对应的 IP 或主机名。 | | **风险等级**(`risk_level`) | 插件各方法携带出厂默认的风险等级,但你的实际环境可能需要更严格的风控。将任意方法调整为 `normal`、`risk`、`auth` 或 `disable`。 | 其余配置项(端口、共享名、凭证等)因插件而异 —— 详见各插件自身的 README。 3. 完成。通过 MCP 调用一次方法,验证连通性。 ## 插件开发指南 ### 目录结构 每个插件遵循统一布局: ``` <插件名称>/ ├── plugin.json # 清单文件 —— 唯一真相来源 ├── requirements.txt # Python 依赖(pip install 格式) ├── <公共模块>.py # 公共工具(可选) ├── <处理器_a>.py # 方法处理器脚本 ├── <处理器_b>.py ├── README.md # 英文文档 └── README_ZH.md # 中文文档 ``` - 文件夹名称即插件标识(如 `fnos-smb-file-manager`)。 - 每个 `.py` 处理器对应 `plugin.json` 中的一个方法。 ### plugin.json 规范 清单文件分为五个顶层区块: | 区块 | 用途 | |------|------| | `manifest` | 插件身份:名称、版本、目标服务器地址 | | `info` | 人类可读的元信息:标题、描述、标签 | | `runtime` | 运行环境:解释器、工作目录 | | `methods` | 暴露的 MCP 方法:名称、参数、处理器、风险等级 | | `config` | 插件私有配置(服务器凭证等) | #### 完整 Schema ```jsonc { // ── 插件身份 ── "manifest": { "name": "string", // 唯一插件 ID,kebab-case 格式 "apiVersion": "plugin.gis/v1", // 协议版本 "kind": "Plugin", // 固定值 "version": "1.0.0", // 语义化版本 "serviceType": "string", // 如 "file-manager"、"printer"、"report" "disabled": false, // 设为 true 可临时禁用 "serverUrl": "string" // 目标服务 IP(管理员可修改) }, // ── 展示元信息 ── "info": { "title": "string", // 人类可读的名称 "description": "string", // 一段话简介 "tags": ["string", "..."] // 用于发现和筛选 }, // ── 运行环境 ── "runtime": { "interpreter": "python3", // 固定值 —— 所有处理器均为 Python "workDir": "./service_plugins/<插件名称>", "defaultTimeout": 30 // 秒 }, // ── 方法定义 ── "methods": [ { "name": "string", // MCP 方法名,用点号分隔 "description": "string", // 一句话描述功能 "inputSchema": { // 参数的 JSON Schema "type": "object", "properties": { /* ... */ }, "required": ["..."] }, "handler": "script.py", // 相对于插件根目录 "mode": "sync", // 固定值 —— 所有方法均为同步 "timeout": 30, // 秒,单次调用超时 "risk_level": "normal" // "normal" | "risk" | "auth" | "disable" } ], // ── 插件配置 ── "config": { // 插件自定义键值对 // 由处理器直接读取 —— bridge 不关心内容 } } ``` #### 方法 `risk_level` 说明 控制 AI Agent 执行该方法时的风控规则: | 级别 | 行为 | |------|------| | `normal` | 直接放行。记录到调用审计日志,无特殊标记。 | | `risk` | 直接放行,但在审计日志中**醒目标记**,便于事后审查。 | | `auth` | **HITL(Human In The Loop)** —— Agent 暂停执行,必须由指定授权人批准后才能继续。 | | `disable` | **禁用**该功能。标准插件中用户不需要的方法可直接关闭,Agent 调用时将收到"功能已禁用"响应。 | #### 方法 `name` 命名规范 使用点号分隔的层级命名: ``` <领域>.<类别>.<动作> 示例: smb.file.list —— SMB 领域,文件类别,列表操作 smb.file.mkdir smb.file.delete printer.job.submit —— (未来)打印领域,任务类别 report.sales.weekly —— (未来)报表领域,销售类别 ``` ### 通信协议 bridge 与每个处理器之间通过 **stdin / stdout** 通信。除了方法名外,不通过命令行参数传参。 #### Bridge → 处理器 ``` 命令: python3 <方法名> stdin: {"param1": "value1", "param2": "value2"} ``` - `sys.argv[1]` —— 方法名(如 `"smb.file.list"`)。用于日志或分发。 - `sys.stdin` —— 完整的参数对象,单行 JSON 字符串,格式匹配方法的 `inputSchema`。 #### 处理器 → Bridge ``` stdout: {"code": 0, "msg": "ok", "data": { ... }} ``` - `code`=`0` → 成功,bridge 将 `data` 返回给调用方。 - `code`=`-1` → 失败,bridge 将 `msg` 作为错误描述返回。 - stdout **必须且只能有一行** —— 就是这条 JSON 响应。 - 调试/错误日志输出到 `stderr`,绝对不要输出到 stdout。 #### 为什么用 stdin/stdout 而非 CLI 参数? 1. **支持任意复杂参数** —— stdin 上的 JSON 可以承载嵌套对象、数组、大数据量,不受 shell 转义限制。 2. **配置隔离** —— bridge 只传入方法参数。插件私有配置(服务器地址、凭证)由处理器直接从 `plugin.json` 读取,bridge 不接触。 3. **简单统一** —— 所有插件一套协议,不用记参数位置顺序。 ### 方法处理器模板 ```python #!/usr/bin/env python3 """ .py —— <简要说明> """ import sys import json import traceback from <公共模块> import output_json def main(): # 1. 获取方法名 method_name = sys.argv[1] if len(sys.argv) > 1 else "unknown" # 2. 从 stdin 读取参数 raw = sys.stdin.read().strip() try: params = json.loads(raw) if raw else {} except json.JSONDecodeError: print(f"[{method_name}] 参数 JSON 格式无效: {raw[:200]}", file=sys.stderr) sys.exit(1) # 3. 校验必填参数 required_param = params.get("required_param") if not required_param: output_json(-1, "缺少必填参数: required_param") # 4. 业务逻辑 try: # ... 执行业务操作 ... result = {"key": "value"} output_json(0, "ok", result) except Exception as e: traceback.print_exc(file=sys.stderr) output_json(-1, str(e)) if __name__ == "__main__": main() ``` ### 响应格式 每个处理器必须向 stdout 输出唯一一个 JSON 对象: ```json // 成功 { "code": 0, "msg": "ok", "data": { // 方法特定的返回数据。 // 可以是任意合法 JSON:对象、数组、字符串、数字、null。 } } // 失败 { "code": -1, "msg": "人类可读的错误描述。", "data": null } ``` 规则: - 失败时的 `msg` 必须具体、可操作(如 `"路径 'foo/bar' 不存在"`,而非 `"错误"`)。 - 失败时的 `data` 必须为 `null`。 - 调用 `json.dumps` 时使用 `ensure_ascii=False`,保留响应中的非 ASCII 字符。 ### 错误处理 - **顶层包裹** —— 在 `main()` 最外层用 `try / except` 包裹全部逻辑。 - **绝不让脚本崩溃** —— 未捕获异常会导致 bridge 收不到有效 JSON,只能返回一个通用失败。 - **日志走 stderr** —— 用 `traceback.print_exc(file=sys.stderr)` 输出完整堆栈;bridge 完全忽略 stderr。 - **提前校验** —— 在任何副作用操作(网络调用、文件写入)之前检查必填参数。 ### 公共工具模块 把通用逻辑提取到插件文件夹内的公共模块(如 `smb_utils.py`): ```python # smb_utils.py —— 示例结构 import json, sys, os def output_json(code: int, msg: str, data=None): """统一 JSON 响应输出。失败时自动退出。""" print(json.dumps({"code": code, "msg": msg, "data": data}, ensure_ascii=False, default=str)) if code != 0: sys.exit(1) def load_plugin_config() -> dict: """从脚本所在目录读取 plugin.json。""" config_path = os.path.join( os.path.dirname(os.path.abspath(__file__)), "plugin.json" ) with open(config_path, "r", encoding="utf-8") as f: return json.load(f) def get_config_section(section: str) -> dict: """获取 plugin.json 中指定区块的配置。""" return load_plugin_config().get(section, {}) ``` 要点: - 配置是从 `plugin.json` 中,**相对于脚本自身位置**(`os.path.dirname(__file__)`)读取的,保证插件完全可移植。 - 如果频繁读取配置可以做缓存(每个处理器是短生命周期进程,缓存仅在一次调用内有效)。 ## 常见问题
Q: bridge 如何发现插件? 启动时,bridge 扫描 `service_plugins/*/plugin.json`。每个包含合法 `plugin.json` 的文件夹会被注册为活跃插件。不合法的清单会被记录日志并跳过。
Q: 两个插件可以暴露同名方法吗? 不可以。`plugin.json` 中的方法名在所有插件之间必须全局唯一。bridge 以方法名作为路由的唯一键。
Q: 如何给已有插件增加新方法? 1. 在插件文件夹内编写新的 `.py` 处理器脚本。 2. 在 `plugin.json` 的 `methods` 数组中新增一条定义,`handler` 指向新脚本。 3. bridge 在下次检测周期自动加载新方法。
Q: 插件之间可以有依赖关系吗? 插件设计为独立、自包含。不支持插件间依赖 —— 如果两个服务需要交互,可以在一个插件上暴露更多方法,或引入第三个协调插件。
Q: 处理器超时会怎样? bridge 会终止 Python 进程并向调用方返回超时错误。每个方法定义中的 `timeout` 字段控制单次调用的超时限制。