# MCP Router
[](https://github.com/ChuranNeko/mcp_router/actions/workflows/python-publish.yml)
[](https://github.com/astral-sh/ruff)
[](https://www.python.org/downloads/)
[](https://opensource.org/licenses/MIT)
MCP Router 是一个模型上下文协议(MCP)路由/代理系统,作为MCP服务端和客户端,支持动态管理MCP工具配置,解决LLM无法区分同名工具的问题。
## 特性
- **动态路由**: 文件系统路由,使用 `mcp_settings.json` 配置
- **快速启动**: 后台加载客户端,启动时间<0.1秒
- **热加载**: 自动检测配置变化并重新加载
- **多传输支持**: Stdio、SSE、HTTP 传输协议
- **实时日志**: WebSocket实时日志流(可选)
- **权限控制**: 可配置的LLM实例管理权限
- **智能端口**: 端口占用时自动查找可用端口
- **安全认证**: Bearer Token认证,输入验证
- **REST API**: 完整的HTTP API用于配置管理
<details>
<summary><b>📁 项目结构</b></summary>
```
mcp_router/
├── main.py # 项目入口
├── config.json # 全局配置文件
├── requirements.txt # 依赖文件
├── pyproject.toml # uv项目配置
│
├── src/
│ ├── core/ # 核心模块
│ │ ├── logger.py # 日志系统
│ │ ├── config.py # 配置管理器
│ │ └── exceptions.py # 自定义异常
│ │
│ ├── mcp/ # MCP模块
│ │ ├── client.py # MCP客户端管理
│ │ ├── server.py # MCP服务端
│ │ ├── router.py # 路由核心逻辑
│ │ └── transport.py # 传输层
│ │
│ ├── api/ # API模块
│ │ ├── app.py # FastAPI应用
│ │ └── routes.py # API路由处理
│ │
│ └── utils/ # 工具模块
│ ├── validator.py # 输入验证
│ ├── watcher.py # 文件监视器
│ └── security.py # 安全工具
│
├── data/ # MCP配置目录
│ ├── example/
│ │ └── mcp_settings.json
│ └── ...
│
└── test/ # 测试文件
├── test_router.py
├── test_api.py
└── test_security.py
```
</details>
## 快速开始
### 安装
**从 PyPI 安装(推荐):**
```bash
pip install mcp-router
```
<details>
<summary><b>从源码安装</b></summary>
```bash
# 使用 uv (推荐)
uv venv .venv
uv pip install -e ".[dev]"
# 或使用 pip
python -m venv .venv
.venv\Scripts\activate # Windows
source .venv/bin/activate # Linux/Mac
pip install -e ".[dev]"
# 或使用 conda
conda env create -f environment.yml
conda activate mcp_router
```
</details>
### 配置
编辑 `config.json` 文件:
```json
{
"api": {
"enabled": false,
"port": 8001,
"host": "0.0.0.0"
},
"server": {
"host": "0.0.0.0",
"http": { "enabled": true, "port": 3000 },
"sse": { "enabled": true, "port": 3001 }
},
"security": {
"bearer_token": "",
"enable_validation": true
},
"logging": {
"level": "INFO",
"directory": "logs"
}
}
```
<details>
<summary><b>⚙️ 配置项说明</b></summary>
- `server.host`: HTTP/SSE模式的监听地址(默认:0.0.0.0)
- `server.http.port`: HTTP模式的监听端口(默认:3000)
- `server.sse.port`: SSE模式的监听端口(默认:3001)
- `server.allow_instance_management`: 允许LLM管理实例(默认:false)
- `api.enabled`: 是否启动REST API服务器(默认:false)
- `api.port`: REST API端口(默认:8001)
- `api.auto_find_port`: 端口占用时自动递增查找可用端口
- `api.enable_realtime_logs`: 启用WebSocket实时日志 (ws://host:port/ws)
- `logging.directory`: 日志目录,使用Minecraft风格 (latest-{mode}.txt + 时间戳备份)
- `logging.level`: 日志级别(DEBUG/INFO/WARNING/ERROR/OFF)
**注意**:传输模式(stdio/http/sse)通过命令行参数指定,不在配置文件中设置。
</details>
### 添加MCP配置
在 `data/{provider}/mcp_settings.json` 中添加MCP服务器配置:
```json
{
"provider": "example",
"isActive": true,
"name": "example_instance",
"type": "stdio",
"command": "python",
"args": ["-m", "example_mcp"],
"env": {}
}
```
### 运行
```bash
# 直接指定传输模式
python main.py # Stdio模式(默认)
python main.py stdio # Stdio模式
python main.py http # HTTP模式
python main.py sse # SSE模式
python main.py api # API服务器模式
# 查看帮助
python main.py help
python main.py -h
```
## MCP 工具
MCP Router 提供以下工具给 LLM 使用:
**基础工具** (总是可用):
- `mcp.router.list()` - 列出所有已注册的MCP客户端实例
- `mcp.router.help()` - 返回所有实例的工具列表和使用说明
- `mcp.router.use(instance_name)` - 使用指定的MCP实例
- `mcp.router.call(instance_name, tool_name, **kwargs)` - 调用指定实例的指定工具
**管理工具** (需启用 `allow_instance_management`):
- `mcp.router.add(provider_name, config)` - 动态添加新的MCP配置
- `mcp.router.remove(instance_name)` - 移除MCP配置
- `mcp.router.enable(instance_name)` - 启用MCP实例
- `mcp.router.disable(instance_name)` - 禁用MCP实例
<details>
<summary><b>📡 REST API 端点</b></summary>
当 API 模式启用时,可通过以下端点管理 MCP Router:
**实例管理**:
- `GET /api/instances` - 列出所有实例
- `GET /api/instances/{name}` - 获取实例详情
- `POST /api/instances` - 添加新实例
- `PATCH /api/instances/{name}` - 更新实例配置
- `DELETE /api/instances/{name}` - 删除实例
- `POST /api/instances/{name}/enable` - 启用实例
- `POST /api/instances/{name}/disable` - 禁用实例
**工具管理**:
- `GET /api/tools` - 列出所有工具
- `GET /api/tools/{instance_name}` - 获取实例的工具列表
- `POST /api/call` - 调用工具
**其他**:
- `GET /` - 服务状态
- `GET /health` - 健康检查
- `GET /api/config` - 获取配置
- `WS /ws` - 实时日志流 (需启用 `enable_realtime_logs`)
</details>
## 与 LLM 集成
### Stdio 模式(推荐)
适用于单个LLM客户端(如Claude Desktop、Cursor)。
**客户端配置示例** (mcp.json):
```json
{
"mcpServers": {
"mcp_router": {
"command": "uv",
"args": [
"--directory",
"C:/path/to/mcp_router",
"run",
"python",
"main.py"
],
"transport": "stdio"
}
}
}
```
### HTTP 模式
适用于多客户端并发连接(端口 3000)。
**客户端配置示例** (mcp.json):
```json
{
"mcpServers": {
"mcp_router_http": {
"url": "http://localhost:3000/mcp",
"transport": "streamableHttp"
}
}
}
```
<details>
<summary><b>HTTP 使用示例(curl / Python)</b></summary>
**curl 示例**:
```bash
# 初始化会话
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}'
# 列出工具
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
```
**Python 客户端示例**:
```python
import httpx
class MCPHTTPClient:
def __init__(self, base_url="http://localhost:3000"):
self.client = httpx.Client()
self.base_url = base_url
self.request_id = 0
self.initialized = False
def call_method(self, method, params=None):
self.request_id += 1
response = self.client.post(f"{self.base_url}/mcp", json={
"jsonrpc": "2.0",
"id": self.request_id,
"method": method,
"params": params or {}
})
return response.json()
def initialize(self):
result = self.call_method("initialize", {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {"name": "python-client", "version": "1.0.0"}
})
self.initialized = True
return result
def list_tools(self):
if not self.initialized:
self.initialize()
return self.call_method("tools/list")
# 使用示例
client = MCPHTTPClient()
tools = client.list_tools()
print(tools)
```
</details>
### SSE 模式
适用于实时推送场景(端口 3001)。
**客户端配置示例** (mcp.json):
```json
{
"mcpServers": {
"mcp_router_sse": {
"url": "http://localhost:3001/sse",
"transport": "sse"
}
}
}
```
<details>
<summary><b>SSE JavaScript 客户端示例</b></summary>
```javascript
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
const transport = new SSEClientTransport(
new URL("http://localhost:3001/sse")
);
const client = new Client(
{ name: "my-client", version: "1.0.0" },
{ capabilities: {} }
);
await client.connect(transport);
const tools = await client.listTools();
console.log(tools);
```
</details>
### REST API 模式
独立的配置管理接口(端口 8001)。
```bash
# 单独启动API模式
python main.py api
# 或在任意MCP模式下同时启用API(需config.json中配置api.enabled: true)
python main.py http
```
<details>
<summary><b>REST API 使用示例</b></summary>
**curl 示例**:
```bash
# 列出所有实例
curl http://localhost:8001/api/instances \
-H "Authorization: Bearer your-token"
# 调用工具
curl -X POST http://localhost:8001/api/call \
-H "Authorization: Bearer your-token" \
-H "Content-Type: application/json" \
-d '{"instance": "openai_doc", "tool": "read_project_oas", "params": {}}'
```
**Python 客户端示例**:
```python
import httpx
client = httpx.Client(
base_url="http://localhost:8001",
headers={"Authorization": "Bearer your-token"}
)
# 列出实例
instances = client.get("/api/instances").json()
# 调用工具
result = client.post("/api/call", json={
"instance": "openai_doc",
"tool": "read_project_oas",
"params": {}
}).json()
```
</details>
### 混合模式(MCP + API)
同时运行MCP服务器和REST API,使用独立端口互不干扰。
**配置**:
```json
{
"api": {"enabled": true, "port": 8001},
"server": {
"host": "0.0.0.0",
"http": {"enabled": true, "port": 3000}
}
}
```
**启动**:
```bash
python main.py http # MCP@3000 + API@8001
```
## 开发
### 代码风格
本项目使用 [Ruff](https://github.com/astral-sh/ruff) 进行代码格式化和 linting:
```bash
# 格式化代码
ruff format .
# 检查代码
ruff check .
# 自动修复
ruff check --fix .
```
<details>
<summary><b>🧪 运行测试</b></summary>
```bash
# 运行所有测试
pytest
# 运行特定测试
pytest test/test_router.py
# 带覆盖率
pytest --cov=src --cov-report=html
```
</details>
## 安全性
- **输入验证**: 防止SQL注入、XSS攻击、路径遍历
- **Bearer Token**: 可选的API认证
- **CORS配置**: 灵活的跨域请求控制
- **文件大小限制**: 防止DOS攻击
- **HTTP安全头**: X-Frame-Options, CSP, HSTS等
## 许可证
[MIT License](LICENSE)
## 贡献
欢迎提交 Issue 和 Pull Request!
请确保:
- 代码通过 `ruff` 检查
- 添加或更新相关测试
- 更新文档(如果需要)
Raw data
{
"_id": null,
"home_page": null,
"name": "mcp-router",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": "ChuranNeko <churanneko@qq.com>",
"keywords": "ai, llm, mcp, proxy, router",
"author": null,
"author_email": "ChuranNeko <churanneko@qq.com>",
"download_url": "https://files.pythonhosted.org/packages/e2/8a/7e364a9e0444397f20ae358a2b53db535445b2aadbce24d8cb60c5b1e889/mcp_router-1.0.8.tar.gz",
"platform": null,
"description": "# MCP Router\n\n[](https://github.com/ChuranNeko/mcp_router/actions/workflows/python-publish.yml)\n[](https://github.com/astral-sh/ruff)\n[](https://www.python.org/downloads/)\n[](https://opensource.org/licenses/MIT)\n\nMCP Router \u662f\u4e00\u4e2a\u6a21\u578b\u4e0a\u4e0b\u6587\u534f\u8bae\uff08MCP\uff09\u8def\u7531/\u4ee3\u7406\u7cfb\u7edf\uff0c\u4f5c\u4e3aMCP\u670d\u52a1\u7aef\u548c\u5ba2\u6237\u7aef\uff0c\u652f\u6301\u52a8\u6001\u7ba1\u7406MCP\u5de5\u5177\u914d\u7f6e\uff0c\u89e3\u51b3LLM\u65e0\u6cd5\u533a\u5206\u540c\u540d\u5de5\u5177\u7684\u95ee\u9898\u3002\n\n## \u7279\u6027\n\n- **\u52a8\u6001\u8def\u7531**: \u6587\u4ef6\u7cfb\u7edf\u8def\u7531\uff0c\u4f7f\u7528 `mcp_settings.json` \u914d\u7f6e\n- **\u5feb\u901f\u542f\u52a8**: \u540e\u53f0\u52a0\u8f7d\u5ba2\u6237\u7aef\uff0c\u542f\u52a8\u65f6\u95f4<0.1\u79d2\n- **\u70ed\u52a0\u8f7d**: \u81ea\u52a8\u68c0\u6d4b\u914d\u7f6e\u53d8\u5316\u5e76\u91cd\u65b0\u52a0\u8f7d\n- **\u591a\u4f20\u8f93\u652f\u6301**: Stdio\u3001SSE\u3001HTTP \u4f20\u8f93\u534f\u8bae\n- **\u5b9e\u65f6\u65e5\u5fd7**: WebSocket\u5b9e\u65f6\u65e5\u5fd7\u6d41\uff08\u53ef\u9009\uff09\n- **\u6743\u9650\u63a7\u5236**: \u53ef\u914d\u7f6e\u7684LLM\u5b9e\u4f8b\u7ba1\u7406\u6743\u9650\n- **\u667a\u80fd\u7aef\u53e3**: \u7aef\u53e3\u5360\u7528\u65f6\u81ea\u52a8\u67e5\u627e\u53ef\u7528\u7aef\u53e3\n- **\u5b89\u5168\u8ba4\u8bc1**: Bearer Token\u8ba4\u8bc1\uff0c\u8f93\u5165\u9a8c\u8bc1\n- **REST API**: \u5b8c\u6574\u7684HTTP API\u7528\u4e8e\u914d\u7f6e\u7ba1\u7406\n\n<details>\n<summary><b>\ud83d\udcc1 \u9879\u76ee\u7ed3\u6784</b></summary>\n\n```\nmcp_router/\n\u251c\u2500\u2500 main.py # \u9879\u76ee\u5165\u53e3\n\u251c\u2500\u2500 config.json # \u5168\u5c40\u914d\u7f6e\u6587\u4ef6\n\u251c\u2500\u2500 requirements.txt # \u4f9d\u8d56\u6587\u4ef6\n\u251c\u2500\u2500 pyproject.toml # uv\u9879\u76ee\u914d\u7f6e\n\u2502\n\u251c\u2500\u2500 src/\n\u2502 \u251c\u2500\u2500 core/ # \u6838\u5fc3\u6a21\u5757\n\u2502 \u2502 \u251c\u2500\u2500 logger.py # \u65e5\u5fd7\u7cfb\u7edf\n\u2502 \u2502 \u251c\u2500\u2500 config.py # \u914d\u7f6e\u7ba1\u7406\u5668\n\u2502 \u2502 \u2514\u2500\u2500 exceptions.py # \u81ea\u5b9a\u4e49\u5f02\u5e38\n\u2502 \u2502\n\u2502 \u251c\u2500\u2500 mcp/ # MCP\u6a21\u5757\n\u2502 \u2502 \u251c\u2500\u2500 client.py # MCP\u5ba2\u6237\u7aef\u7ba1\u7406\n\u2502 \u2502 \u251c\u2500\u2500 server.py # MCP\u670d\u52a1\u7aef\n\u2502 \u2502 \u251c\u2500\u2500 router.py # \u8def\u7531\u6838\u5fc3\u903b\u8f91\n\u2502 \u2502 \u2514\u2500\u2500 transport.py # \u4f20\u8f93\u5c42\n\u2502 \u2502\n\u2502 \u251c\u2500\u2500 api/ # API\u6a21\u5757\n\u2502 \u2502 \u251c\u2500\u2500 app.py # FastAPI\u5e94\u7528\n\u2502 \u2502 \u2514\u2500\u2500 routes.py # API\u8def\u7531\u5904\u7406\n\u2502 \u2502\n\u2502 \u2514\u2500\u2500 utils/ # \u5de5\u5177\u6a21\u5757\n\u2502 \u251c\u2500\u2500 validator.py # \u8f93\u5165\u9a8c\u8bc1\n\u2502 \u251c\u2500\u2500 watcher.py # \u6587\u4ef6\u76d1\u89c6\u5668\n\u2502 \u2514\u2500\u2500 security.py # \u5b89\u5168\u5de5\u5177\n\u2502\n\u251c\u2500\u2500 data/ # MCP\u914d\u7f6e\u76ee\u5f55\n\u2502 \u251c\u2500\u2500 example/\n\u2502 \u2502 \u2514\u2500\u2500 mcp_settings.json\n\u2502 \u2514\u2500\u2500 ...\n\u2502\n\u2514\u2500\u2500 test/ # \u6d4b\u8bd5\u6587\u4ef6\n \u251c\u2500\u2500 test_router.py\n \u251c\u2500\u2500 test_api.py\n \u2514\u2500\u2500 test_security.py\n```\n\n</details>\n\n## \u5feb\u901f\u5f00\u59cb\n\n### \u5b89\u88c5\n\n**\u4ece PyPI \u5b89\u88c5\uff08\u63a8\u8350\uff09\uff1a**\n\n```bash\npip install mcp-router\n```\n\n<details>\n<summary><b>\u4ece\u6e90\u7801\u5b89\u88c5</b></summary>\n\n```bash\n# \u4f7f\u7528 uv (\u63a8\u8350)\nuv venv .venv\nuv pip install -e \".[dev]\"\n\n# \u6216\u4f7f\u7528 pip\npython -m venv .venv\n.venv\\Scripts\\activate # Windows\nsource .venv/bin/activate # Linux/Mac\npip install -e \".[dev]\"\n\n# \u6216\u4f7f\u7528 conda\nconda env create -f environment.yml\nconda activate mcp_router\n```\n\n</details>\n\n### \u914d\u7f6e\n\n\u7f16\u8f91 `config.json` \u6587\u4ef6\uff1a\n\n```json\n{\n \"api\": {\n \"enabled\": false,\n \"port\": 8001,\n \"host\": \"0.0.0.0\"\n },\n \"server\": {\n \"host\": \"0.0.0.0\",\n \"http\": { \"enabled\": true, \"port\": 3000 },\n \"sse\": { \"enabled\": true, \"port\": 3001 }\n },\n \"security\": {\n \"bearer_token\": \"\",\n \"enable_validation\": true\n },\n \"logging\": {\n \"level\": \"INFO\",\n \"directory\": \"logs\"\n }\n}\n```\n\n<details>\n<summary><b>\u2699\ufe0f \u914d\u7f6e\u9879\u8bf4\u660e</b></summary>\n\n- `server.host`: HTTP/SSE\u6a21\u5f0f\u7684\u76d1\u542c\u5730\u5740\uff08\u9ed8\u8ba4\uff1a0.0.0.0\uff09\n- `server.http.port`: HTTP\u6a21\u5f0f\u7684\u76d1\u542c\u7aef\u53e3\uff08\u9ed8\u8ba4\uff1a3000\uff09\n- `server.sse.port`: SSE\u6a21\u5f0f\u7684\u76d1\u542c\u7aef\u53e3\uff08\u9ed8\u8ba4\uff1a3001\uff09\n- `server.allow_instance_management`: \u5141\u8bb8LLM\u7ba1\u7406\u5b9e\u4f8b\uff08\u9ed8\u8ba4\uff1afalse\uff09\n- `api.enabled`: \u662f\u5426\u542f\u52a8REST API\u670d\u52a1\u5668\uff08\u9ed8\u8ba4\uff1afalse\uff09\n- `api.port`: REST API\u7aef\u53e3\uff08\u9ed8\u8ba4\uff1a8001\uff09\n- `api.auto_find_port`: \u7aef\u53e3\u5360\u7528\u65f6\u81ea\u52a8\u9012\u589e\u67e5\u627e\u53ef\u7528\u7aef\u53e3\n- `api.enable_realtime_logs`: \u542f\u7528WebSocket\u5b9e\u65f6\u65e5\u5fd7 (ws://host:port/ws)\n- `logging.directory`: \u65e5\u5fd7\u76ee\u5f55\uff0c\u4f7f\u7528Minecraft\u98ce\u683c (latest-{mode}.txt + \u65f6\u95f4\u6233\u5907\u4efd)\n- `logging.level`: \u65e5\u5fd7\u7ea7\u522b\uff08DEBUG/INFO/WARNING/ERROR/OFF\uff09\n\n**\u6ce8\u610f**\uff1a\u4f20\u8f93\u6a21\u5f0f\uff08stdio/http/sse\uff09\u901a\u8fc7\u547d\u4ee4\u884c\u53c2\u6570\u6307\u5b9a\uff0c\u4e0d\u5728\u914d\u7f6e\u6587\u4ef6\u4e2d\u8bbe\u7f6e\u3002\n\n</details>\n\n### \u6dfb\u52a0MCP\u914d\u7f6e\n\n\u5728 `data/{provider}/mcp_settings.json` \u4e2d\u6dfb\u52a0MCP\u670d\u52a1\u5668\u914d\u7f6e\uff1a\n\n```json\n{\n \"provider\": \"example\",\n \"isActive\": true,\n \"name\": \"example_instance\",\n \"type\": \"stdio\",\n \"command\": \"python\",\n \"args\": [\"-m\", \"example_mcp\"],\n \"env\": {}\n}\n```\n\n### \u8fd0\u884c\n\n```bash\n# \u76f4\u63a5\u6307\u5b9a\u4f20\u8f93\u6a21\u5f0f\npython main.py # Stdio\u6a21\u5f0f\uff08\u9ed8\u8ba4\uff09\npython main.py stdio # Stdio\u6a21\u5f0f\npython main.py http # HTTP\u6a21\u5f0f\npython main.py sse # SSE\u6a21\u5f0f\npython main.py api # API\u670d\u52a1\u5668\u6a21\u5f0f\n\n# \u67e5\u770b\u5e2e\u52a9\npython main.py help\npython main.py -h\n```\n\n## MCP \u5de5\u5177\n\nMCP Router \u63d0\u4f9b\u4ee5\u4e0b\u5de5\u5177\u7ed9 LLM \u4f7f\u7528\uff1a\n\n**\u57fa\u7840\u5de5\u5177** (\u603b\u662f\u53ef\u7528):\n- `mcp.router.list()` - \u5217\u51fa\u6240\u6709\u5df2\u6ce8\u518c\u7684MCP\u5ba2\u6237\u7aef\u5b9e\u4f8b\n- `mcp.router.help()` - \u8fd4\u56de\u6240\u6709\u5b9e\u4f8b\u7684\u5de5\u5177\u5217\u8868\u548c\u4f7f\u7528\u8bf4\u660e\n- `mcp.router.use(instance_name)` - \u4f7f\u7528\u6307\u5b9a\u7684MCP\u5b9e\u4f8b\n- `mcp.router.call(instance_name, tool_name, **kwargs)` - \u8c03\u7528\u6307\u5b9a\u5b9e\u4f8b\u7684\u6307\u5b9a\u5de5\u5177\n\n**\u7ba1\u7406\u5de5\u5177** (\u9700\u542f\u7528 `allow_instance_management`):\n- `mcp.router.add(provider_name, config)` - \u52a8\u6001\u6dfb\u52a0\u65b0\u7684MCP\u914d\u7f6e\n- `mcp.router.remove(instance_name)` - \u79fb\u9664MCP\u914d\u7f6e\n- `mcp.router.enable(instance_name)` - \u542f\u7528MCP\u5b9e\u4f8b\n- `mcp.router.disable(instance_name)` - \u7981\u7528MCP\u5b9e\u4f8b\n\n<details>\n<summary><b>\ud83d\udce1 REST API \u7aef\u70b9</b></summary>\n\n\u5f53 API \u6a21\u5f0f\u542f\u7528\u65f6\uff0c\u53ef\u901a\u8fc7\u4ee5\u4e0b\u7aef\u70b9\u7ba1\u7406 MCP Router\uff1a\n\n**\u5b9e\u4f8b\u7ba1\u7406**:\n- `GET /api/instances` - \u5217\u51fa\u6240\u6709\u5b9e\u4f8b\n- `GET /api/instances/{name}` - \u83b7\u53d6\u5b9e\u4f8b\u8be6\u60c5\n- `POST /api/instances` - \u6dfb\u52a0\u65b0\u5b9e\u4f8b\n- `PATCH /api/instances/{name}` - \u66f4\u65b0\u5b9e\u4f8b\u914d\u7f6e\n- `DELETE /api/instances/{name}` - \u5220\u9664\u5b9e\u4f8b\n- `POST /api/instances/{name}/enable` - \u542f\u7528\u5b9e\u4f8b\n- `POST /api/instances/{name}/disable` - \u7981\u7528\u5b9e\u4f8b\n\n**\u5de5\u5177\u7ba1\u7406**:\n- `GET /api/tools` - \u5217\u51fa\u6240\u6709\u5de5\u5177\n- `GET /api/tools/{instance_name}` - \u83b7\u53d6\u5b9e\u4f8b\u7684\u5de5\u5177\u5217\u8868\n- `POST /api/call` - \u8c03\u7528\u5de5\u5177\n\n**\u5176\u4ed6**:\n- `GET /` - \u670d\u52a1\u72b6\u6001\n- `GET /health` - \u5065\u5eb7\u68c0\u67e5\n- `GET /api/config` - \u83b7\u53d6\u914d\u7f6e\n- `WS /ws` - \u5b9e\u65f6\u65e5\u5fd7\u6d41 (\u9700\u542f\u7528 `enable_realtime_logs`)\n\n</details>\n\n## \u4e0e LLM \u96c6\u6210\n\n### Stdio \u6a21\u5f0f\uff08\u63a8\u8350\uff09\n\n\u9002\u7528\u4e8e\u5355\u4e2aLLM\u5ba2\u6237\u7aef\uff08\u5982Claude Desktop\u3001Cursor\uff09\u3002\n\n**\u5ba2\u6237\u7aef\u914d\u7f6e\u793a\u4f8b** (mcp.json):\n```json\n{\n \"mcpServers\": {\n \"mcp_router\": {\n \"command\": \"uv\",\n \"args\": [\n \"--directory\",\n \"C:/path/to/mcp_router\",\n \"run\",\n \"python\",\n \"main.py\"\n ],\n \"transport\": \"stdio\"\n }\n }\n}\n```\n\n### HTTP \u6a21\u5f0f\n\n\u9002\u7528\u4e8e\u591a\u5ba2\u6237\u7aef\u5e76\u53d1\u8fde\u63a5\uff08\u7aef\u53e3 3000\uff09\u3002\n\n**\u5ba2\u6237\u7aef\u914d\u7f6e\u793a\u4f8b** (mcp.json):\n```json\n{\n \"mcpServers\": {\n \"mcp_router_http\": {\n \"url\": \"http://localhost:3000/mcp\",\n \"transport\": \"streamableHttp\"\n }\n }\n}\n```\n\n<details>\n<summary><b>HTTP \u4f7f\u7528\u793a\u4f8b\uff08curl / Python\uff09</b></summary>\n\n**curl \u793a\u4f8b**:\n```bash\n# \u521d\u59cb\u5316\u4f1a\u8bdd\ncurl -X POST http://localhost:3000/mcp \\\n -H \"Content-Type: application/json\" \\\n -d '{\"jsonrpc\":\"2.0\",\"id\":0,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2024-11-05\",\"capabilities\":{},\"clientInfo\":{\"name\":\"test\",\"version\":\"1.0.0\"}}}'\n\n# \u5217\u51fa\u5de5\u5177\ncurl -X POST http://localhost:3000/mcp \\\n -H \"Content-Type: application/json\" \\\n -d '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\",\"params\":{}}'\n```\n\n**Python \u5ba2\u6237\u7aef\u793a\u4f8b**:\n```python\nimport httpx\n\nclass MCPHTTPClient:\n def __init__(self, base_url=\"http://localhost:3000\"):\n self.client = httpx.Client()\n self.base_url = base_url\n self.request_id = 0\n self.initialized = False\n \n def call_method(self, method, params=None):\n self.request_id += 1\n response = self.client.post(f\"{self.base_url}/mcp\", json={\n \"jsonrpc\": \"2.0\",\n \"id\": self.request_id,\n \"method\": method,\n \"params\": params or {}\n })\n return response.json()\n \n def initialize(self):\n result = self.call_method(\"initialize\", {\n \"protocolVersion\": \"2024-11-05\",\n \"capabilities\": {},\n \"clientInfo\": {\"name\": \"python-client\", \"version\": \"1.0.0\"}\n })\n self.initialized = True\n return result\n \n def list_tools(self):\n if not self.initialized:\n self.initialize()\n return self.call_method(\"tools/list\")\n\n# \u4f7f\u7528\u793a\u4f8b\nclient = MCPHTTPClient()\ntools = client.list_tools()\nprint(tools)\n```\n\n</details>\n\n### SSE \u6a21\u5f0f\n\n\u9002\u7528\u4e8e\u5b9e\u65f6\u63a8\u9001\u573a\u666f\uff08\u7aef\u53e3 3001\uff09\u3002\n\n**\u5ba2\u6237\u7aef\u914d\u7f6e\u793a\u4f8b** (mcp.json):\n```json\n{\n \"mcpServers\": {\n \"mcp_router_sse\": {\n \"url\": \"http://localhost:3001/sse\",\n \"transport\": \"sse\"\n }\n }\n}\n```\n\n<details>\n<summary><b>SSE JavaScript \u5ba2\u6237\u7aef\u793a\u4f8b</b></summary>\n\n```javascript\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { SSEClientTransport } from \"@modelcontextprotocol/sdk/client/sse.js\";\n\nconst transport = new SSEClientTransport(\n new URL(\"http://localhost:3001/sse\")\n);\nconst client = new Client(\n { name: \"my-client\", version: \"1.0.0\" }, \n { capabilities: {} }\n);\n\nawait client.connect(transport);\nconst tools = await client.listTools();\nconsole.log(tools);\n```\n\n</details>\n\n### REST API \u6a21\u5f0f\n\n\u72ec\u7acb\u7684\u914d\u7f6e\u7ba1\u7406\u63a5\u53e3\uff08\u7aef\u53e3 8001\uff09\u3002\n\n```bash\n# \u5355\u72ec\u542f\u52a8API\u6a21\u5f0f\npython main.py api\n\n# \u6216\u5728\u4efb\u610fMCP\u6a21\u5f0f\u4e0b\u540c\u65f6\u542f\u7528API\uff08\u9700config.json\u4e2d\u914d\u7f6eapi.enabled: true\uff09\npython main.py http\n```\n\n<details>\n<summary><b>REST API \u4f7f\u7528\u793a\u4f8b</b></summary>\n\n**curl \u793a\u4f8b**:\n```bash\n# \u5217\u51fa\u6240\u6709\u5b9e\u4f8b\ncurl http://localhost:8001/api/instances \\\n -H \"Authorization: Bearer your-token\"\n\n# \u8c03\u7528\u5de5\u5177\ncurl -X POST http://localhost:8001/api/call \\\n -H \"Authorization: Bearer your-token\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"instance\": \"openai_doc\", \"tool\": \"read_project_oas\", \"params\": {}}'\n```\n\n**Python \u5ba2\u6237\u7aef\u793a\u4f8b**:\n```python\nimport httpx\n\nclient = httpx.Client(\n base_url=\"http://localhost:8001\",\n headers={\"Authorization\": \"Bearer your-token\"}\n)\n\n# \u5217\u51fa\u5b9e\u4f8b\ninstances = client.get(\"/api/instances\").json()\n\n# \u8c03\u7528\u5de5\u5177\nresult = client.post(\"/api/call\", json={\n \"instance\": \"openai_doc\",\n \"tool\": \"read_project_oas\",\n \"params\": {}\n}).json()\n```\n\n</details>\n\n### \u6df7\u5408\u6a21\u5f0f\uff08MCP + API\uff09\n\n\u540c\u65f6\u8fd0\u884cMCP\u670d\u52a1\u5668\u548cREST API\uff0c\u4f7f\u7528\u72ec\u7acb\u7aef\u53e3\u4e92\u4e0d\u5e72\u6270\u3002\n\n**\u914d\u7f6e**:\n```json\n{\n \"api\": {\"enabled\": true, \"port\": 8001},\n \"server\": {\n \"host\": \"0.0.0.0\",\n \"http\": {\"enabled\": true, \"port\": 3000}\n }\n}\n```\n\n**\u542f\u52a8**:\n```bash\npython main.py http # MCP@3000 + API@8001\n```\n\n## \u5f00\u53d1\n\n### \u4ee3\u7801\u98ce\u683c\n\n\u672c\u9879\u76ee\u4f7f\u7528 [Ruff](https://github.com/astral-sh/ruff) \u8fdb\u884c\u4ee3\u7801\u683c\u5f0f\u5316\u548c linting\uff1a\n\n```bash\n# \u683c\u5f0f\u5316\u4ee3\u7801\nruff format .\n\n# \u68c0\u67e5\u4ee3\u7801\nruff check .\n\n# \u81ea\u52a8\u4fee\u590d\nruff check --fix .\n```\n\n<details>\n<summary><b>\ud83e\uddea \u8fd0\u884c\u6d4b\u8bd5</b></summary>\n\n```bash\n# \u8fd0\u884c\u6240\u6709\u6d4b\u8bd5\npytest\n\n# \u8fd0\u884c\u7279\u5b9a\u6d4b\u8bd5\npytest test/test_router.py\n\n# \u5e26\u8986\u76d6\u7387\npytest --cov=src --cov-report=html\n```\n\n</details>\n\n## \u5b89\u5168\u6027\n\n- **\u8f93\u5165\u9a8c\u8bc1**: \u9632\u6b62SQL\u6ce8\u5165\u3001XSS\u653b\u51fb\u3001\u8def\u5f84\u904d\u5386\n- **Bearer Token**: \u53ef\u9009\u7684API\u8ba4\u8bc1\n- **CORS\u914d\u7f6e**: \u7075\u6d3b\u7684\u8de8\u57df\u8bf7\u6c42\u63a7\u5236\n- **\u6587\u4ef6\u5927\u5c0f\u9650\u5236**: \u9632\u6b62DOS\u653b\u51fb\n- **HTTP\u5b89\u5168\u5934**: X-Frame-Options, CSP, HSTS\u7b49\n\n## \u8bb8\u53ef\u8bc1\n\n[MIT License](LICENSE)\n\n## \u8d21\u732e\n\n\u6b22\u8fce\u63d0\u4ea4 Issue \u548c Pull Request\uff01\n\n\u8bf7\u786e\u4fdd\uff1a\n- \u4ee3\u7801\u901a\u8fc7 `ruff` \u68c0\u67e5\n- \u6dfb\u52a0\u6216\u66f4\u65b0\u76f8\u5173\u6d4b\u8bd5\n- \u66f4\u65b0\u6587\u6863\uff08\u5982\u679c\u9700\u8981\uff09\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A routing/proxy system for MCP servers",
"version": "1.0.8",
"project_urls": {
"Documentation": "https://github.com/ChuranNeko/mcp_router#readme",
"Homepage": "https://github.com/ChuranNeko/mcp_router",
"Issues": "https://github.com/ChuranNeko/mcp_router/issues",
"Repository": "https://github.com/ChuranNeko/mcp_router"
},
"split_keywords": [
"ai",
" llm",
" mcp",
" proxy",
" router"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "cacaacadd20ec6e6464aae5000ecb9459be0268a64aa1ef1f3cb4b035e35368f",
"md5": "5f84232aee5486fc5e6e225462054150",
"sha256": "0967b00ddf76171065d51faec327952844e2b2f3bf2ddbb747abfefac919e427"
},
"downloads": -1,
"filename": "mcp_router-1.0.8-py3-none-any.whl",
"has_sig": false,
"md5_digest": "5f84232aee5486fc5e6e225462054150",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 33439,
"upload_time": "2025-10-29T04:26:19",
"upload_time_iso_8601": "2025-10-29T04:26:19.033796Z",
"url": "https://files.pythonhosted.org/packages/ca/ca/acadd20ec6e6464aae5000ecb9459be0268a64aa1ef1f3cb4b035e35368f/mcp_router-1.0.8-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "e28a7e364a9e0444397f20ae358a2b53db535445b2aadbce24d8cb60c5b1e889",
"md5": "5e22dd4d23823a5ac44e5aef86ab631c",
"sha256": "0898f166046919835f0f7913e93827ac1a885bcc69bf109e3fe425f74abd8c14"
},
"downloads": -1,
"filename": "mcp_router-1.0.8.tar.gz",
"has_sig": false,
"md5_digest": "5e22dd4d23823a5ac44e5aef86ab631c",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 26839,
"upload_time": "2025-10-29T04:26:22",
"upload_time_iso_8601": "2025-10-29T04:26:22.198105Z",
"url": "https://files.pythonhosted.org/packages/e2/8a/7e364a9e0444397f20ae358a2b53db535445b2aadbce24d8cb60c5b1e889/mcp_router-1.0.8.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-29 04:26:22",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "ChuranNeko",
"github_project": "mcp_router#readme",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [
{
"name": "fastapi",
"specs": [
[
">=",
"0.104.0"
]
]
},
{
"name": "uvicorn",
"specs": [
[
">=",
"0.24.0"
]
]
},
{
"name": "httpx",
"specs": [
[
">=",
"0.25.0"
]
]
},
{
"name": "pydantic",
"specs": [
[
">=",
"2.0.0"
]
]
},
{
"name": "watchdog",
"specs": [
[
">=",
"3.0.0"
]
]
},
{
"name": "python-dotenv",
"specs": [
[
">=",
"1.0.0"
]
]
},
{
"name": "mcp",
"specs": [
[
">=",
"1.0.0"
]
]
},
{
"name": "starlette",
"specs": [
[
">=",
"0.27.0"
]
]
},
{
"name": "pytest",
"specs": [
[
">=",
"7.0.0"
]
]
},
{
"name": "pytest-asyncio",
"specs": [
[
">=",
"0.21.0"
]
]
}
],
"lcname": "mcp-router"
}