惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 司徒正美
M
MIT News - Artificial intelligence
博客园_首页
IT之家
IT之家
L
LangChain Blog
D
DataBreaches.Net
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Google DeepMind News
Google DeepMind News
Blog — PlanetScale
Blog — PlanetScale
人人都是产品经理
人人都是产品经理
博客园 - Franky
云风的 BLOG
云风的 BLOG
罗磊的独立博客
量子位
G
Google Developers Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园 - 【当耐特】
博客园 - 叶小钗
S
SegmentFault 最新的问题
Stack Overflow Blog
Stack Overflow Blog
B
Blog
T
Tailwind CSS Blog
A
About on SuperTechFans
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com

博客园 - 人艰不拆_zmc

AG-UI 是什么?一篇文章讲清楚 AI Agent 与前端如何交互 AI 里的“本体”到底是什么?一篇写给技术小白的通俗解释 15000mAh 到底是什么概念?一篇看懂电池容量 go2_ros2_sdk 到底是干什么的?从 Go2、官方 SDK、ROS2 一路讲到 SLAM 和 Nav2 买了宇树 Go2 以后怎么二次开发?写给第一次做机器狗项目的人 买了一块 NVIDIA Jetson,怎么在上面安装 ROS2?从 JetPack 到 ROS2 的完整入门指南 NVIDIA Jetson 到底是什么?写给第一次接触机器人和边缘 AI 的人 ROS / ROS2 到底是什么?写给第一次接触机器人开发的人 大模型到底能同时多少人用?一篇看懂并发、排队与容量估算 大模型为什么有快有慢?一篇看懂响应速度背后的关键因素 技术小白也能看懂:大模型里的量化、蒸馏到底是什么意思? Codex 使用技巧:从“会聊天”到“真正能干活” 我终于搞懂了:Codex 对话中插件和 Skill 到底怎么用 我终于搞懂了 Agent Spec:它其实就是 Agent 的“标准设计图” 我终于搞懂了 Tool:原来不只是 Function Calling 里的函数 Skill 里的 Python 脚本,到底是不是 Tool? 我终于搞懂了 Codex 的 Plugin 和 Skill:顺便把 App、MCP 一次理清 我终于搞懂了 Codex 的“应用”:App 到底是什么,怎么添加和维护? 我终于搞懂了 Tool、Function Calling 和 MCP:大模型到底怎么知道该调哪个接口? 我终于搞懂了 Codex 的“记忆”是怎么回事 Codex 用久了越来越慢?我的上下文管理小技巧 我终于搞懂了 Codex 里的 Thread、Turn 和 Session 从 Qwen3.8-27B 到 FP8、NVFP4、MoE:一次搞懂几个常见大模型概念 FPS 是什么意思?简单理解 60 FPS、25 FPS 和视频帧率 DeepSeek Harness 明明像 AI Coding 工具,为什么又能用来构建各种 Agent? 使用 Codex 开发项目,怎么才能节约 Token? 模型里的 32K、128K、256K 是什么意思?简单聊聊上下文限制 Codex 一次对话到底会给模型发送什么?以 Spring Boot 项目为例讲清 Context、代码读取与 Token 消耗 AI Agent 中的 Rule 是什么?以 Codex 为例,小白也能看懂 我终于搞懂了 Harness:它不是论文,也不是标准,更不是 Codex 独有
我终于搞懂了 MCP:从 HTTP API 到 ERP MCP Server 的完整入门
人艰不拆_zmc · 2026-09-04 · via 博客园 - 人艰不拆_zmc

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 之间遵守的通信协议

2. 图里的“MCP”到底是什么意思

经常会看到这种图:

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 才是你真正需要开发和运行的程序。


3. MCP 和普通 HTTP API 有什么区别

可以先这样理解:

HTTP API
=
给普通程序调用的业务接口

MCP
=
给 Agent 使用的一套标准化能力协议

例如 ERP 原来有:

GET /api/inventory?materialCode=ABC001

MCP 可以把它包装成一个 Tool:

Tool 名称:
query_inventory

作用:
查询指定物料库存

参数:
material_code:物料编码

返回:
库存数量、可用数量、仓库信息

Agent 通过 MCP 可以知道:

这个 Tool 是干什么的
什么时候适合调用
需要哪些参数
参数是什么类型

所以:

普通 HTTP API 不会因为“接口文档写得很清楚”就自动变成 MCP。


4. 开发一个 ERP MCP Server,要做哪些事情

假设 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

5. 第一步:准备 Python 环境

这里以 Python 为例。

安装 MCP Python SDK:

pip install "mcp[cli]"

如果后面需要调用 ERP HTTP API,再安装:

pip install httpx

项目结构可以先很简单:

erp-mcp/
└── server.py

6. 第二步:创建 MCP Server

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

以及它的说明和参数。


8. 第四步:先用假数据测试

先不要急着连真实 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。


10. MCP 和 HTTP API 能不能放在同一个服务、同一个端口

可以。

这是最容易让人困惑的地方。

假设 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。


11. 同一个服务时,不需要 MCP 再绕一圈调用自己的 HTTP API

如果 MCP 和 HTTP API 本来就在同一个程序里,更合理的做法是:

             ERP 业务代码
             query_inventory()
              ↑           ↑
              │           │
         HTTP API      MCP Tool
              │           │
 /api/inventory      query_inventory

也就是:

普通 HTTP API 和 MCP Tool 共用同一套业务代码。

没必要:

MCP Tool
↓
再调用自己的 HTTP API
↓
再进入业务代码

这样反而多绕了一层。


12. 那什么时候应该单独部署 MCP Server

如果 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 基本不用修改。


13. 两个独立程序能不能使用同一个端口

一般不行。

例如:

ERP 进程
想监听 8000

MCP Server 进程
也想监听 8000

在同一个 IP 上,两个独立进程通常不能同时占用:

同一个 IP + 同一个端口

所以如果拆成两个独立服务,一般会是:

ERP:8080

MCP Server:3001

14. 为什么线上看起来又可以都是 443 端口

因为生产环境通常还有:

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 端口,不代表里面一定是同一个程序。


15. 第六步:启动 MCP Server

MCP Server 可以运行在本地,也可以通过网络提供服务。

可以先简单理解成:

本地使用:
Codex
↓
本地 MCP Server


服务端使用:
Codex
↓
网络
↓
MCP Server

MCP 可以使用不同的传输方式,例如本地进程通信或基于 HTTP 的网络传输。

需要注意:

即使底层使用 HTTP 进行网络传输,Agent 和 MCP Server 之间遵守的仍然是 MCP 协议。

所以:

HTTP
= 可以作为传输通道

MCP
= Agent 与 MCP Server 之间的能力调用协议

16. 第七步:在 Codex 中添加 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 能力。


18. 一个完整例子

用户说:

查一下 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。


20. 企业开发一定要考虑权限

不要这样:

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

需要单独维护 Tool 文档吗

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。


22. Codex 里的 MCP 是全局的,还是可以按项目配置

Codex 里的 MCP 不一定只能全局配置。

可以简单分成两层:

全局 MCP
=
所有项目都可能使用

项目级 MCP
=
只针对某个项目使用

全局 MCP

用户级配置通常放在:

~/.codex/config.toml

例如:

[mcp_servers.github]
url = "https://example.com/github-mcp"
enabled = true

这种 MCP 更像:

Codex 这个用户的通用工具。

适合放:

GitHub
通用搜索
通用文档工具

项目级 MCP

如果某个项目只需要自己专属的 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 都塞给所有项目。

为什么项目级 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 是否同端口,取决于它们是不是运行在同一个程序里。