







MCP(Model Context Protocol)是一套让 Agent 统一发现、理解和调用外部能力的协议。
本文主要用一个 ERP 的“查询库存”场景,解释 MCP 是什么、MCP Server 是什么、怎么开发,以及 MCP 和普通 HTTP API 到底是什么关系。
假设 ERP 原来有一个普通 HTTP 接口:
GET /api/inventory?materialCode=ABC001
现在希望以后可以直接对 Codex 说:
帮我查一下物料 ABC001 还有多少库存
那就可以在 MCP Server 中提供一个 Tool:
query_inventory(material_code)
整个关系是:
用户
↓
Codex / Agent
↓
MCP Tool:query_inventory
↓
MCP Server
↓
ERP HTTP API / ERP 内部业务代码
↓
ERP
简单理解:
Tool
= Agent 能调用的一个具体能力
MCP Server
= 把这些能力按照 MCP 标准提供出去的程序
MCP
= Agent 与 MCP Server 之间遵守的通信协议
经常会看到这种图:
Codex / Agent
↓
MCP
↓
ERP MCP Server
↓
ERP
这里中间的 MCP 不是一个单独的软件,也不是还要再部署一个叫“MCP”的服务。
它表示的是:
Codex 和 ERP MCP Server 之间,按照 MCP 协议通信。
更准确一点可以画成:
Codex / Agent
↓
MCP Client
↓
MCP 协议
↓
ERP MCP Server
↓
ERP API / 业务代码
可以类比成:
浏览器
↓
HTTP
↓
Web Server
这里 HTTP 是协议,Web Server 才是真正运行的程序。
所以:
MCP 是通信规则,MCP Server 才是你真正需要开发和运行的程序。
可以先这样理解:
HTTP API
=
给普通程序调用的业务接口
MCP
=
给 Agent 使用的一套标准化能力协议
例如 ERP 原来有:
GET /api/inventory?materialCode=ABC001
MCP 可以把它包装成一个 Tool:
Tool 名称:
query_inventory
作用:
查询指定物料库存
参数:
material_code:物料编码
返回:
库存数量、可用数量、仓库信息
Agent 通过 MCP 可以知道:
这个 Tool 是干什么的
什么时候适合调用
需要哪些参数
参数是什么类型
所以:
普通 HTTP API 不会因为“接口文档写得很清楚”就自动变成 MCP。
假设 ERP 有很多功能:
库存管理
订单管理
供应商管理
采购管理
财务管理
……
不要一上来把整个 ERP 全部暴露给 Agent。
先挑一个最简单的功能:
查询库存
我们最终希望得到一个 MCP Tool:
query_inventory(material_code)
整个开发过程可以简单分成 7 步:
1. 安装 MCP SDK
2. 创建 MCP Server
3. 定义 Tool
4. 先用假数据测试
5. Tool 内接 ERP 业务能力
6. 启动 MCP Server
7. 在 Codex 中添加这个 MCP Server
这里以 Python 为例。
安装 MCP Python SDK:
pip install "mcp[cli]"
如果后面需要调用 ERP HTTP API,再安装:
pip install httpx
项目结构可以先很简单:
erp-mcp/
└── server.py
在 server.py 中创建一个 MCP Server:
from mcp.server import MCPServer
mcp = MCPServer("ERP MCP Server")
可以简单理解成:
创建了一个名字叫
ERP MCP Server的 MCP 服务。
目前它还没有任何业务能力。
我们增加一个查询库存的 Tool:
from mcp.server import MCPServer
mcp = MCPServer("ERP MCP Server")
@mcp.tool()
def query_inventory(material_code: str) -> dict:
"""查询指定物料的库存。"""
return {
"material_code": material_code,
"quantity": 120,
"available_quantity": 100
}
if __name__ == "__main__":
mcp.run()
这里最关键的是:
@mcp.tool()
可以简单理解成:
把下面这个 Python 函数注册成一个 MCP Tool。
Codex 等 MCP Client 连接以后,就能发现:
query_inventory
以及它的说明和参数。
先不要急着连真实 ERP。
例如调用:
query_inventory("ABC001")
返回:
{
"material_code": "ABC001",
"quantity": 120,
"available_quantity": 100
}
如果这一步能跑通,说明:
MCP Server 和 Tool 这一层基本正常。
下一步再接真实 ERP。
假设 ERP 已经有接口:
GET http://erp.company.local/api/inventory
参数:
materialCode=ABC001
那么 MCP Tool 内部可以调用这个 API:
import httpx
from mcp.server import MCPServer
mcp = MCPServer("ERP MCP Server")
@mcp.tool()
def query_inventory(material_code: str) -> dict:
"""查询 ERP 中指定物料的库存。"""
response = httpx.get(
"http://erp.company.local/api/inventory",
params={
"materialCode": material_code
},
timeout=10
)
response.raise_for_status()
data = response.json()
return {
"material_code": material_code,
"quantity": data["quantity"],
"available_quantity": data["availableQuantity"]
}
if __name__ == "__main__":
mcp.run()
这时候链路就变成:
Codex
↓
query_inventory
↓
ERP MCP Server
↓
GET /api/inventory
↓
ERP
↓
返回真实库存
需要注意:
ERP 原来的 HTTP API 并没有变成 MCP。
只是我们新增了一个 MCP Server,把原来的业务能力包装成 MCP Tool。
可以。
这是最容易让人困惑的地方。
假设 ERP 服务监听:
8000 端口
完全可以同时提供:
http://erp.company.com:8000/api/inventory
↑
普通 HTTP API
http://erp.company.com:8000/mcp
↑
MCP 入口
结构可以理解成:
ERP Service
监听 8000 端口
│
┌────────────┴────────────┐
│ │
↓ ↓
/api/inventory /mcp
普通 HTTP API MCP 入口
│ │
└────────────┬────────────┘
↓
ERP 业务代码
↓
数据库
所以:
同一个程序,可以用同一个端口,通过不同 URL 路径同时提供普通 HTTP API 和 MCP。
如果 MCP 和 HTTP API 本来就在同一个程序里,更合理的做法是:
ERP 业务代码
query_inventory()
↑ ↑
│ │
HTTP API MCP Tool
│ │
/api/inventory query_inventory
也就是:
普通 HTTP API 和 MCP Tool 共用同一套业务代码。
没必要:
MCP Tool
↓
再调用自己的 HTTP API
↓
再进入业务代码
这样反而多绕了一层。
如果 ERP 已经运行很多年,例如:
ERP
Java 开发
监听 8080
已经有完整 HTTP API
不想修改原系统
这时候最省事的是:
Codex
↓
ERP MCP Server :3001
↓
ERP HTTP API :8080
↓
ERP
例如:
ERP:
http://erp-server:8080/api/inventory
MCP Server:
http://mcp-server:3001/mcp
这种方式的好处是:
原 ERP 基本不用修改。
一般不行。
例如:
ERP 进程
想监听 8000
MCP Server 进程
也想监听 8000
在同一个 IP 上,两个独立进程通常不能同时占用:
同一个 IP + 同一个端口
所以如果拆成两个独立服务,一般会是:
ERP:8080
MCP Server:3001
因为生产环境通常还有:
Nginx / 网关
例如对外统一:
https://erp.company.com
都是 HTTPS 443。
但是网关可以按照路径分流:
https://erp.company.com/api/*
↓
ERP :8080
https://erp.company.com/mcp
↓
MCP Server :3001
结构就是:
用户 / Codex
↓
https://xxx:443
↓
Nginx
┌──────┴──────┐
↓ ↓
/api /mcp
↓ ↓
ERP:8080 MCP:3001
所以:
外面看起来是同一个 443 端口,不代表里面一定是同一个程序。
MCP Server 可以运行在本地,也可以通过网络提供服务。
可以先简单理解成:
本地使用:
Codex
↓
本地 MCP Server
服务端使用:
Codex
↓
网络
↓
MCP Server
MCP 可以使用不同的传输方式,例如本地进程通信或基于 HTTP 的网络传输。
需要注意:
即使底层使用 HTTP 进行网络传输,Agent 和 MCP Server 之间遵守的仍然是 MCP 协议。
所以:
HTTP
= 可以作为传输通道
MCP
= Agent 与 MCP Server 之间的能力调用协议
MCP Server 启动后,就可以在 Codex 中配置。
例如添加:
ERP MCP Server
连接成功以后,Codex 就可以发现:
query_inventory
以后用户只需要说:
帮我查一下物料 ABC001 的库存
执行过程可能是:
用户提问
↓
Codex 判断需要查询 ERP
↓
调用 query_inventory
↓
ERP MCP Server 收到请求
↓
调用 ERP API / 业务代码
↓
ERP 返回库存
↓
MCP Server 返回 Tool Result
↓
Codex 整理成自然语言
↓
告诉用户结果
查询库存跑通以后,可以继续增加:
query_order
query_supplier
create_purchase_request
最终形成:
ERP MCP Server
│
├── query_inventory
├── query_order
├── query_supplier
└── create_purchase_request
这样 Agent 就拥有了部分 ERP 能力。
用户说:
查一下 ABC001 的库存,
如果少于 100,就帮我创建一个采购 500 个的申请。
Agent 可以这样执行:
第 1 步
调用 query_inventory
↓
返回:
库存 60
↓
第 2 步
模型判断:
60 < 100
↓
第 3 步
调用 create_purchase_request
↓
ERP 创建采购申请
↓
第 4 步
Codex 返回最终结果
这就是 MCP 的价值:
让 Agent 不只是回答问题,而是真正能够调用企业系统完成事情。
MCP Server 还可以提供:
Tools
Resources
Prompts
小白阶段先重点理解 Tool 就够了。
可以简单理解:
Tool
= 让 Agent 执行动作
Resource
= 给 Agent 提供可以读取的数据
Prompt
= 可复用的提示词模板
对于 ERP 接入,最常见的还是:
把查询和操作能力做成 Tool。
不要这样:
Codex
↓
MCP Server
↓
超级管理员账号
↓
ERP 全部数据
更合理的是:
用户
↓
Codex
↓
MCP Server
↓
识别当前用户
↓
权限校验
↓
ERP
真正上线时,至少要考虑:
身份认证
权限控制
数据范围
操作审计
参数校验
敏感操作确认
日志
超时和异常处理
权限不能只靠 Prompt。
这是理解 MCP 时非常关键的一点。
很多人会以为:
还需要单独写一个 Markdown 文档,把每个接口的用途告诉模型。
其实通常不需要额外写一个 md 文档给模型看。
MCP Tool 本身就会把这些信息提供出去:
Tool 名称
Tool 说明
输入参数
参数类型
返回结构
例如:
@mcp.tool()
def query_inventory(
material_code: str,
warehouse: str | None = None
) -> dict:
"""
查询指定物料在 ERP 中的库存信息。
material_code:物料编码
warehouse:仓库,可选
"""
...
这里:
函数名
query_inventory
↓
告诉模型:
这个 Tool 叫什么
docstring
查询指定物料在 ERP 中的库存信息
↓
告诉模型:
这个 Tool 是干什么的
参数类型
material_code: str
warehouse: str | None
↓
告诉模型:
需要传哪些参数、参数是什么类型
MCP Server 会把这些内容整理成类似下面的 Tool 描述:
{
"name": "query_inventory",
"description": "查询指定物料在 ERP 中的库存信息。",
"inputSchema": {
"type": "object",
"properties": {
"material_code": {
"type": "string"
},
"warehouse": {
"type": ["string", "null"]
}
},
"required": ["material_code"]
}
}
Codex 连接 MCP Server 后,会先获取这个 Server 提供了哪些 Tool。
然后模型就能看到:
query_inventory
= 查询 ERP 库存
query_order
= 查询 ERP 订单
create_purchase_request
= 创建采购申请
所以当用户说:
帮我查一下 ABC001 的库存
模型就能判断:
应该调用 query_inventory
MCP 协议本身不要求必须额外写 Markdown 文档。
但是企业项目里,仍然建议额外维护一份开发文档,例如:
erp-mcp/
├── server.py
├── README.md
└── docs/
└── tools.md
tools.md 可以给开发人员看:
Tool:query_inventory
用途:
查询 ERP 库存
底层接口:
GET /api/inventory
权限:
只能查询当前用户有权限的仓库
注意:
禁止返回采购成本字段
要注意:
这份 Markdown 主要是给开发人员维护和查看的,不是 MCP 必须提供给模型的。
模型真正依赖的核心信息,还是 MCP Tool 自己提供的:
name
description
inputSchema
outputSchema(可选)
annotations(可选)
一句话记住:
模型不是靠额外的 md 文档认识 Tool,而是靠 MCP Server 提供的 Tool 名称、说明和参数 Schema。
Codex 里的 MCP 不一定只能全局配置。
可以简单分成两层:
全局 MCP
=
所有项目都可能使用
项目级 MCP
=
只针对某个项目使用
用户级配置通常放在:
~/.codex/config.toml
例如:
[mcp_servers.github]
url = "https://example.com/github-mcp"
enabled = true
这种 MCP 更像:
Codex 这个用户的通用工具。
适合放:
GitHub
通用搜索
通用文档工具
如果某个项目只需要自己专属的 MCP,可以在项目目录里配置:
my-project/
├── .codex/
│ └── config.toml
├── src/
└── ...
例如这个项目只需要 ERP MCP:
[mcp_servers.erp]
url = "https://erp.company.com/mcp"
enabled = true
可以理解成:
项目 A
└── ERP MCP
项目 B
└── OA MCP
项目 C
└── 没有专属 MCP
这样就不会把所有 MCP 都塞给所有项目。
假设你有很多项目:
项目 A:ERP
项目 B:OA
项目 C:网站
项目 D:运维
如果所有 MCP 都全局配置:
ERP MCP
OA MCP
数据库 MCP
运维 MCP
……
那么每个项目都可能看到一堆自己根本用不到的 Tool。
这样会带来两个问题:
1. 工具越来越多,模型选择 Tool 时更容易受到干扰
2. MCP 初始化和 Tool 描述也会占用一定 Context / 运行资源
所以更推荐:
全局 MCP
=
真正通用的能力
项目级 MCP
=
当前项目专属能力
可以类比成:
全局 MCP
= 员工随身携带的通用工具
项目级 MCP
= 进入这个项目后才发给他的专用工具
可以用下面这张关系记住:
用户
↓
Agent
↓
MCP Tool
↓
MCP 协议
↓
MCP Server
↓
HTTP API / 内部业务代码
↓
ERP
再翻译成人话:
Agent
= 使用能力干活的人
Tool
= Agent 可以调用的具体能力
MCP
= Agent 与外部能力之间的统一协议
MCP Server
= 真正实现并提供这些 Tool 的程序
HTTP API
= ERP 原来已有的普通业务接口
对于一个已经存在的 ERP,最容易落地的路线通常是:
先选一个 ERP 功能
↓
定义一个 MCP Tool
↓
开发 MCP Server
↓
Tool 内调用 ERP HTTP API
↓
测试 Tool
↓
部署 MCP Server
↓
添加到 Codex
↓
用自然语言调用 ERP
关于部署,再记住:
同一个程序
→ 可以同一个端口
→ /api 提供普通 HTTP API
→ /mcp 提供 MCP
两个独立程序
→ 一般使用不同端口
→ ERP 8080
→ MCP Server 3001
生产环境
→ 可以再通过 Nginx / 网关
→ 统一对外暴露 443
关于 Tool 和项目配置,再记住:
模型怎么知道 Tool 是干什么的
→ 看 MCP Tool 的 name / description / inputSchema
→ 不需要额外给模型准备一个 md 文档
Codex 的 MCP 怎么配置
→ 通用 MCP 放全局配置
→ 项目专属 MCP 放项目 .codex/config.toml
一句话总结:
HTTP API 是业务系统原来的接口,MCP 是给 Agent 用的统一能力协议,MCP Server 则负责把业务能力按照 MCP 标准提供给 Agent;至于 HTTP API 和 MCP 是否同端口,取决于它们是不是运行在同一个程序里。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。