Skip to main content
Glama
README.md
# 📘 ApiBridge MCP

基于 Python 3.10+ 和 FastAPI 开发的HTTP API桥接MCP服务,支持多工具集(Toolkits)、动态工具加载、多种认证方式和自动API文档生成。

## 🚀 功能特性

- **多工具集支持**: 从 `toolkits/` 目录动态加载多个工具集(每个 JSON 文件一个工具集),每个工具集有独立的配置和工具。
- **动态工具加载**: 根据JSON配置文件自动创建和注册工具,无需修改代码。
- **灵活的认证机制**: 支持 `http basic` 和 `oauth2_jwt` 两种认证方式,可为每个工具集独立配置。
- **中间件驱动的路由**: 通过 `ToolkitRoutingMiddleware` 中间件,根据 `Toolkit-Code` HTTP头动态路由到正确的工具集。
- **内置工具**: 提供 `current_time`, `get_api_docs`, `api_version` 等实用内置工具。
- **自动文档生成**: 支持通过命令行生成所有或单个工具集的Markdown格式API文档。
- **异步HTTP客户端**: 使用 `aiohttp` 进行高效的异步外部API调用。
- **容器化部署**: 提供完整的Docker和Docker Compose配置,方便快速部署。

## 🛠️ 环境要求

- Python 3.10+
- pip
- pnpm (或 npm/yarn)
- Docker(可选)

## 项目结构

```
apibridge-mcp-service/
├── .env.example      # 环境变量示例文件
├── README.md           # 项目文档
├── app/                # 主应用目录
│   ├── auth/           # 认证模块 (http_basic, oauth2_jwt)
│   ├── config/         # 配置模块 (settings.py)
│   ├── mcp_server.py   # FastAPI应用和MCP服务器核心
│   ├── tools/          # 内置工具实现
│   └── utils/          # 实用工具 (doc_generator, tool_loader, middlewares)
├── toolkits/           # 工具集配置目录(每个文件代表一个工具集)
│   └── *.json
├── docs/               # 生成的文档存放目录
├── tests/              # 测试代码目录
├── main.py             # 应用入口
└── requirements.txt    # Python依赖
```

## ⚙️ 安装与配置

1.  **克隆项目**

2.  **安装依赖**

    ```bash
    pip install -r requirements.txt
    ```

3.  **配置工具集**

在 `toolkits/` 目录下创建JSON文件来定义你的工具集。例如, `my_toolkit.json`:

    ```json
    {
      "code": "my_toolkit",
      "name": "My Custom Toolkit",
      "description": "A toolkit for custom operations.",
      "auth": {
        "type": "http_basic",
        "user_name": "myuser",
        "password": "mypass"
      },
      "tools": [
        {
          "name": "my_tool",
          "description": "Does something awesome.",
          "path": "/custom/endpoint",
          "method": "POST",
          "parameters": { ... },
          "response": { ... }
        }
      ]
    }
    ```

4.  **配置环境变量** (可选)

    创建 `.env` 文件并根据 `.env.example` 的内容进行配置,可以覆盖 `settings.py` 中的默认值。

## 运行服务

```bash
python main.py
```

服务默认启动在 `http://0.0.0.0:8200`。

## 🚀 使用方法

### 工具调用

所有工具调用都通过 `/tools/` 端点进行。你需要提供 `Toolkit-Code` HTTP头来指定要使用的工具集。

**请求示例**:

```http
POST /tools/ HTTP/1.1
Host: localhost:8200
Content-Type: application/json
Toolkit-Code: my_toolkit

{
  "tool_name": "my_tool",
  "parameters": { ... }
}
```

### 内置工具

-   `current_time`: 获取当前时间。
-   `get_api_docs`: 获取API文档。可使用 `toolkit_code` 参数获取特定工具集的文档。
-   `api_version`: 获取服务版本。

## 📚 API文档生成

通过命令行生成Markdown格式的API文档。

-   **生成所有工具集的文档**:

    ```bash
    python main.py --doc
    ```

-   **生成特定工具集的文档**:

    ```bash
    python main.py --doc --toolkit your_toolkit_code
    ```

文档将生成在 `docs/` 目录下。

## ✅ 测试

项目包含一套完整的单元测试和集成测试。运行测试:

```bash
pytest
```

## 🐳 Docker部署

使用提供的脚本构建和运行Docker容器。

1.  **构建镜像**:

    ```bash
    cd docker
    ./build_image.sh
    ```

2.  **使用Docker Compose运行**:

    ```bash
    cd docker
    docker-compose up -d
    ```

## 📄 日志

服务日志提供了详细的请求、响应和错误信息,方便调试和监控。

## 🧰 工具列表(简版) / Tools List (Simple)

- 路由:GET `/debug/tools-simple`
- 作用:返回简化的工具映射,包含 `toolkitCode`、`toolCode`、`internalName` 以及 `description`
- 查询参数:
  - `toolkit`(可选):仅列出指定工具包的工具
  - `tool`(可选):仅列出指定工具名的工具
- 冲突提示:当同名 `toolCode` 出现在多个工具包中,会在返回中附带 `conflicts` 列表(字段:`toolCode`、`toolkits`),便于客户端避免歧义。

查询参数 / Query params:
- toolkit(string,可选):按工具包过滤
- tool(string,可选):按工具名过滤
- page(number,可选,默认 1):分页页码
- pageSize(number,可选,默认 50,最大 500):分页大小
- sortBy(string,可选,默认 toolCode):可选 toolCode | toolkitCode | internalName
- sortOrder(string,可选,默认 asc):可选 asc | desc
- unique(boolean,可选):仅返回不冲突(唯一)的工具名
- conflictsOnly(boolean,可选):仅返回存在跨工具包同名冲突的工具条目

示例(PowerShell):

```powershell
Invoke-RestMethod "http://127.0.0.1:8201/debug/tools-simple" | ConvertTo-Json -Depth 3
Invoke-RestMethod "http://127.0.0.1:8201/debug/tools-simple?toolkit=sale-solution-tools" | ConvertTo-Json -Depth 3
Invoke-RestMethod "http://127.0.0.1:8201/debug/tools-simple?tool=current_time" | ConvertTo-Json -Depth 3
Invoke-RestMethod "http://127.0.0.1:8201/debug/tools-simple?page=1&pageSize=20&sortBy=toolkitCode&sortOrder=desc" | ConvertTo-Json -Depth 3
Invoke-RestMethod "http://127.0.0.1:8201/debug/tools-simple?unique=true" | ConvertTo-Json -Depth 3
Invoke-RestMethod "http://127.0.0.1:8201/debug/tools-simple?conflictsOnly=true" | ConvertTo-Json -Depth 3
```

English:
- Endpoint: GET `/debug/tools-simple`
- Purpose: Return a simplified mapping with `toolkitCode`, `toolCode`, `internalName`, and `description`
- Query params:
  - `toolkit` (optional), `tool` (optional)
  - `page` (optional, default 1), `pageSize` (optional, default 50, max 500)
  - `sortBy` (optional, default toolCode): toolCode | toolkitCode | internalName
  - `sortOrder` (optional, default asc): asc | desc
  - `unique` (optional): only unique toolCodes
  - `conflictsOnly` (optional): only conflicting toolCodes
- Conflicts: If the same `toolCode` appears across multiple toolkits, a `conflicts` array is included.

## 📦 工具包列表(简版) / Toolkits List (Simple)

- 路由:GET `/debug/toolkits-simple`
- 返回:每个工具包的 `toolkitCode`、工具数量 `toolsCount` 和内置工具数量 `builtinCount`

示例(PowerShell):
```powershell
Invoke-RestMethod "http://127.0.0.1:8201/debug/toolkits-simple" | ConvertTo-Json -Depth 3
```

English:
- Endpoint: GET `/debug/toolkits-simple`
- Returns: `toolkitCode`, `toolsCount`, `builtinCount` for each toolkit

## 🔗 MCP Stream 调用约定 / MCP Stream Protocol

- 服务端内部注册名统一为:`toolkitCode__toolCode`(避免跨工具包重名冲突,便于统计与治理)
- `mcp:list-tools` 响应返回组合名 `toolkitCode__toolCode` 列表(与内部注册保持一致,避免客户端拿到纯名后底层出现 Unknown tool)
- `mcp:call-tool` 支持两种调用方式:
  - 组合名:传 `tool_name=toolkitCode__toolCode`(推荐;同时提供 `Toolkit-Code` 以校验工具集存在与权限)
  - 纯名:传 `tool_name=toolCode`(兼容;中间件会根据 `Toolkit-Code` 自动改写为组合名)
- Header 要求:`Accept` 必须包含 `application/json, text/event-stream`,`Content-Type` 使用 `application/json`
- 会话要求:若返回 “Missing session ID”,请先进行一次会话初始化(例如通过浏览器打开 `/dashboard` 或按你的会话流程),再调用 `mcp:call-tool`

示例(PowerShell):

```powershell
# 初始化(内置方法)
Invoke-WebRequest -Uri "http://127.0.0.1:8200/mcp/stream" -Method POST -Headers @{ "x-client-key"="abc123"; "toolkit-code"="sale-solution-tools"; "Accept"="application/json, text/event-stream" } -ContentType "application/json" -Body '{"jsonrpc":"2.0","id":"init-1","method":"initialize","params":{"clientName":"CherryStudio","clientVersion":"1.0"}}'

# 列表工具(内置方法)
Invoke-WebRequest -Uri "http://127.0.0.1:8200/mcp/stream?toolkit-code=sale-solution-tools" -Method POST -Headers @{ "x-client-key"="abc123"; "Accept"="application/json, text/event-stream" } -ContentType "application/json" -Body '{"jsonrpc":"2.0","id":"list-1","method":"mcp:list-tools","params":{}}'

# 调用具体工具(纯 toolCode + Toolkit-Code)
Invoke-WebRequest -Uri "http://127.0.0.1:8200/mcp/stream" -Method POST -Headers @{ "x-client-key"="abc123"; "toolkit-code"="sale-solution-tools"; "Accept"="application/json, text/event-stream" } -ContentType "application/json" -Body '{"jsonrpc":"2.0","id":"call-1","method":"mcp:call-tool","params":{"tool_name":"current_time","arguments":{}}}'
```

English:
- Server internal registration uses `toolkitCode__toolCode` to avoid cross-toolkit naming conflicts and keep stats consistent.
- `mcp:list-tools` returns combined names `toolkitCode__toolCode` (consistent with server registration; avoids Unknown tool when calling).
- `mcp:call-tool` supports both:
  - Combined name: send `tool_name=toolkitCode__toolCode` (recommended; still provide `Toolkit-Code` for toolkit validation)
  - Pure name: send `tool_name=toolCode` (compatible; middleware rewrites using `Toolkit-Code`)
- Headers: `Accept` must include `application/json, text/event-stream`, `Content-Type` should be `application/json`.
- Session: If you receive “Missing session ID”, initialize a session first (e.g., open `/dashboard` in browser or follow your session init flow), then call `mcp:call-tool`.

## 🔐 日志脱敏策略 / Log Sanitization Policy

- 为满足安全规范,服务会对以下 Header/字段进行脱敏输出:`authorization`、`x-client-key`、`x-api-key`、`x-token`、`token`、`password`
- 日志中的敏感值统一显示为 `***`

English:
- For security, logs mask sensitive headers/fields: `authorization`, `x-client-key`, `x-api-key`, `x-token`, `token`, `password`.
- Masked values appear as `***` in logs.

## ❗ 错误码与常见排错 / Error Codes & Troubleshooting

说明 / Notes:以下错误示例为典型形态,实际返回格式可能因 FastAPI 的异常处理而有所差异(可能返回 `{ "detail": "..." }`);建议客户端同时兼容 `detail` 和自定义 `error.message` 两种字段。

常见场景 / Common Cases:

1) 缺失 Toolkit-Code(工具调用)/ Missing Toolkit-Code (tool call)
- 条件:调用 `mcp:call-tool` 时未在 Header 或 Query 提供 `Toolkit-Code` / `toolkit-code`
- 请求示例(PowerShell):
```powershell
Invoke-WebRequest -Uri "http://127.0.0.1:8201/mcp/stream" -Method POST -Headers @{ "x-client-key"="abc123" } -ContentType "application/json" -Body '{"jsonrpc":"2.0","id":"call-1","method":"mcp:call-tool","params":{"tool_name":"current_time","arguments":{}}}'
```
- 预期响应(400):
```json
{
  "error": {
    "code": 400,
    "message": "Toolkit-Code required for tool call (send header 'Toolkit-Code' or query 'toolkit-code')",
    "hint": "Use pure toolCode plus Toolkit-Code. Example: header Toolkit-Code=sale-solution-tools"
  }
}
```
或(FastAPI 默认)
```json
{ "detail": "Toolkit-Code is required when calling tools" }
```

2) 工具名建议 / Tool naming recommendation
- 推荐:使用组合名 `toolkitCode__toolCode`;纯名也兼容(中间件会根据 `Toolkit-Code` 自动改写)。
- 请始终提供 `Toolkit-Code`(header 或 query),以便服务在鉴权与治理上进行工具集校验。

3) 工具包不存在 / Toolkit JSON not found
- 条件:`Toolkit-Code` 指向不存在的工具包 JSON
- 请求示例:
```powershell
Invoke-WebRequest -Uri "http://127.0.0.1:8201/mcp/stream" -Method POST -Headers @{ "x-client-key"="abc123"; "toolkit-code"="unknown-toolkit" } -ContentType "application/json" -Body '{"jsonrpc":"2.0","id":"call-3","method":"mcp:call-tool","params":{"tool_name":"current_time","arguments":{}}}'
```
- 预期响应(404):
```json
{
  "error": {
    "code": 404,
    "message": "Toolkit not found: unknown-toolkit",
    "hint": "Check available toolkits via /debug/toolkits-simple or /debug/tools-simple"
  }
}
```
或(FastAPI 默认)
```json
{ "detail": "Toolkit not found: unknown-toolkit" }
```

4) 工具不存在(在指定工具包下)/ Tool not found in toolkit
- 条件:`tool_name` 在该 `Toolkit-Code` 下未注册
- 请求示例:
```powershell
Invoke-WebRequest -Uri "http://127.0.0.1:8201/mcp/stream" -Method POST -Headers @{ "x-client-key"="abc123"; "toolkit-code"="sale-solution-tools" } -ContentType "application/json" -Body '{"jsonrpc":"2.0","id":"call-4","method":"mcp:call-tool","params":{"tool_name":"non_exist_tool","arguments":{}}}'
```
- 预期响应(404):
```json
{
  "error": {
    "code": 404,
    "message": "Tool not found in toolkit",
    "hint": "Use /debug/tools-simple?toolkit=sale-solution-tools to confirm available toolCode"
  }
}
```
或(FastAPI 默认)
```json
{ "detail": "Tool not found in toolkit" }
```

5) 内置 JSON-RPC 方法(不需要 tool_name)/ Built-in methods (no tool_name required)
- 条件:`initialize`、`mcp:list-tools` 等方法无需 `tool_name`,会直接透传到 FastMCP
- 请求示例:
```powershell
Invoke-WebRequest -Uri "http://127.0.0.1:8201/mcp/stream" -Method POST -Headers @{ "x-client-key"="abc123"; "toolkit-code"="sale-solution-tools" } -ContentType "application/json" -Body '{"jsonrpc":"2.0","id":"init-1","method":"initialize","params":{"clientName":"CherryStudio","clientVersion":"1.0"}}'

Invoke-WebRequest -Uri "http://127.0.0.1:8201/mcp/stream?toolkit-code=sale-solution-tools" -Method POST -Headers @{ "x-client-key"="abc123" } -ContentType "application/json" -Body '{"jsonrpc":"2.0","id":"list-1","method":"mcp:list-tools","params":{}}'
```

网络错误 / Network Errors:
- “无法连接到远程服务器”:检查服务是否运行、端口是否开放(8201)、本机防火墙策略、代理软件拦截;可使用 `netstat -ano | findstr 8201` 验证监听状态。

English:

Notes: Response formats may vary (`{"detail":"..."}` vs. custom `error.message`). Clients should handle both.

Common cases:
1) Missing Toolkit-Code (tool call)
- Condition: Calling `mcp:call-tool` without `Toolkit-Code` (header or query)
- Expected 400:
```json
{ "error": { "code": 400, "message": "Toolkit-Code required", "hint": "Send header Toolkit-Code or query toolkit-code" } }
```

2) Tool naming recommendation
- Preferred: use combined names `toolkitCode__toolCode`; pure names are compatible (middleware rewrites using `Toolkit-Code`).
- Always provide `Toolkit-Code` (header or query) so the service can validate toolkit in auth and governance.

3) Toolkit JSON not found
- Condition: `Toolkit-Code` points to a non-existent toolkit
- Expected 404:
```json
{ "error": { "code": 404, "message": "Toolkit not found: unknown-toolkit" } }
```

4) Tool not found in toolkit
- Condition: `tool_name` not registered under the given toolkit
- Expected 404:
```json
{ "error": { "code": 404, "message": "Tool not found in toolkit" } }
```

5) Built-in JSON-RPC methods
- `initialize` and `mcp:list-tools` require no `tool_name`; they are passed through to FastMCP.

Network errors:
- "Cannot connect to remote server": ensure the service is running, port 8201 is open, firewall allows local connections, and no proxy blocks requests; use `netstat -ano | findstr 8201` to verify the listening state.