详解Model Context Protocol架构,通过TodoHub示例展示如何让AI Agent通过MCP工具调用内部REST API,绕过直接API封装。
Model Context Protocol(MCP)允许 Kiro、Codex、Claude 或其他 MCP 兼容的 AI 助手以结构化的方式与外部系统交互。
一个实用的心智模型是:
AI Agent --> MCP Client --> MCP Server --> External API / Database / Application
在这个示例中,假设我们有一个名为 TodoHub 的内部 Todo 管理 API。
TodoHub 提供如下 REST API:
GET /todos/123
POST /todos
PUT /todos/123
POST /todos/123/comments
我们希望 AI 代理能够理解如下请求:
Create a high-priority todo for fixing the login issue.
我们的 MCP 服务器充当 AI 代理与 TodoHub API 之间的桥梁。
架构如下:
User ----> "Show todo 123"
AI Agent
(Kiro / Codex / Claude) ----> MCP tool call
TodoHub MCP Server ----> HTTP REST call ---> TodoHub API
MCP 服务器暴露的工具包括:
get_todo
create_todo
update_todo
add_comment
AI 不需要了解底层 REST API 的具体实现。
它只需要理解工具及其输入:
Tool: get_todo
Input:
todo_id
MCP 服务器负责实际的 API 通信。
AI ----> get_todo(todo_id=123)
|
|-----> MCP Server ---> GET /api/todos/123
----> TodoHub
这个区别很重要。
为什么我们不直接给 AI 提供 REST API?
原因是 MCP 服务器为底层 API 提供了更清晰、对 AI 友好的抽象。
你的 REST API 可能需要这样的请求:
POST /api/v2/workitems
{
"subject": "...",
"type_id": 7,
"priority_id": 3,
"workspace_id": 19,
"creator": 758
}
然而,将所有这些内部实现细节暴露给 AI 是不必要的。
相反,MCP 工具可以暴露一个简单得多的接口:
create_todo(
title,
description,
priority
)
MCP 服务器将 AI 友好的参数转换为内部应用所需的参数。
priority = "high"
│
▼
MCP Server
│
▼
priority_id = 3
因此架构变为:
AI-friendly parameters
│
▼
MCP Server
│
▼
Internal application parameters
│
▼
REST API
这将实现细节与 AI 隔离,同时为 AI 提供更简单的接口。
MCP 服务器可以为不同操作暴露不同的工具。
以 TodoHub 为例:
Input:
todo_id: integer
Input:
title: string
description: string
priority: string
AI 然后可以根据用户请求选择合适的工具。
AI 代理需要知道如何启动和与 MCP 服务器通信。
例如,我们可以创建一个 mcp.json 配置文件:
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
"mcpServers": {
"todohub": {
"type": "stdio",
"command": "uvx",
"args": [
"--from",
"mcp-todohub",
"mcp-todohub"
],
"env": {
"TODOHUB_URL": "https://todos.example.com",
"TODOHUB_API_KEY": "xxxxx"
}
}
}
}
关键部分包括:
mcpServers
│
└── todohub
│
|___ SKILL.md
├── type
├── command
├── args
└── env
配置告诉 AI 客户端:
有一个名为 todohub 的 MCP 服务器可用。
服务器使用 stdio 进行通信。
使用 uvx 启动服务器。
所需的环境变量会提供给服务器。
让我们追踪一个完整的请求。
第一步 — AI 理解请求
AI 确定用户想要获取某个 todo 的信息。
Intent:
Retrieve todo information
Todo ID:
123
第二步 — AI 查看可用的 MCP 工具
MCP 服务器已声明的工具包括:
get_todo(todo_id: int)
create_todo(...)
update_todo(...)
add_comment(...)
第三步 — AI 选择合适的工具
get_todo
todo_id = 123
第四步 — MCP 工具调用
从概念上讲,请求如下:
{
"name": "get_todo",
"arguments": {
"todo_id": 123
}
}
第五步 — MCP 服务器执行工具
MCP 服务器接收请求并执行等效于:
get_todo(123)
第六步 — Python 调用 REST API
MCP 服务器随后与 TodoHub 通信:
GET https://todos.example.com/api/todos/123
第七步 — TodoHub 响应
{
"id": 123,
"title": "Payment timeout",
"status": "In Progress"
}
第八步 — MCP 服务器返回结果
MCP 服务器将结果发送回 AI:
TodoHub
↓
MCP Server
↓
AI
第九步 — AI 自然回答
AI 现在可以回应用户:
Task #123 is "Payment timeout" and is currently In Progress.
将所有内容整合在一起:
User
│
│ "Show me todo 123"
▼
AI Agent
│
│ Understands intent
▼
Selects MCP Tool
│
│ get_todo(todo_id=123)
▼
MCP Server
│
│ Translates tool input
▼
REST API
│
│ GET /api/todos/123
▼
TodoHub
│
│ Returns JSON
▼
MCP Server
│
│ Returns structured result
▼
AI Agent
│
│ Generates natural-language response
▼
User
MCP 在 AI 代理和外部系统之间提供了标准化的桥梁。
AI 使用有意义的工具如 get_todo 和 create_todo,而 MCP 服务器负责处理认证、API 调用、参数转换和其他实现细节。
这就是完整的 MCP 循环。
此外,在上述 MCP 项目目录结构下,我们可以有一个 SKILL.md 文件。
当 MCP 工具需要业务上下文或参数构建指导,而这些东西无法通过工具 schema 干净地表达时,这一点尤其有用。
一个重要的区别是:
SKILL.md 通常包含:
→ 业务上下文
→ 如何构建参数
→ 业务规则
→ 示例
例如,MCP 工具可能只定义:
create_task(
title,
description,
priority
)
而 SKILL.md 可以解释 AI 应如何从用户请求中推导这些参数,包括业务规则和示例。
如果 SKILL.md 变得太大,我们可以将内容拆分到多个 Markdown 文件中,并在 references 目录下组织它们。
taskhub-mcp/
├── SKILL.md
├── server.py
├── tools/
│ ├── get_task.py
│ ├── create_task.py
│ ├── update_task.py
│ └── add_comment.py
└── references/
├── task-creation.md
├── priority-rules.md
└── business-rules.md
这保持主 SKILL.md 简洁,同时允许将更详细的业务上下文单独维护。