引入
Model Context Protocol,模型上下文协议。
从 agent 诞生之初,很多人都在想如何让 agent 连接外部系统。MCP 就是为了让 agent 操纵万物而诞生的。
MCP 是应用层通信协议,统一了大语言模型应用(Host/Client)与外部工具、环境(Server)之间的发现、上下文挂载与动作调用机制。 有了 MCP 服务,agent 就能便捷地使用各种第三方软件。
架构角色
mcp 是典型的 c/s 架构,与 restapi 不同的是,使用 mcp 的应用或服务通常不需要具有网络能力,他们可以通过 IPC 方式来进行通信。
mcp 的主要组件如下:
- Host/Client:请求的调用方,一般指的就是有 MCP 能力的 Agent。
- Sever:请求的执行方,通常认为与目标工具成一个整体,通过标准协议把底层能力抽象并暴露给 Client。
例子
自己动手编写一个 MCP,对于我们的工具,可以选择内置 MCP 或者外挂 MCP。
内置 MCP
假设有这么一个业务代码。
app.py:
ORDERS = {
"A1024": {"item": "机械键盘", "status": "运输中", "location": "杭州转运中心"},
"A1025": {"item": "显示器", "status": "已签收", "location": "上海,本人签收"},
"B3301": {"item": "咖啡豆", "status": "待揽收", "location": "仓库"},
}
def track_order(order_id: str) -> str:
order = ORDERS.get(order_id.strip().upper())
if order is None:
return f"没有单号 {order_id}"
return f"{order_id}:{order['item']},{order['status']},现在在{order['location']}"
def list_open_orders() -> str:
open_orders = [oid for oid, order in ORDERS.items() if order["status"] != "已签收"]
if not open_orders:
return "没有未完成的订单。"
lines = [track_order(oid) for oid in open_orders]
return "未完成的订单:\n" + "\n".join(lines)
写一个 MCP Server,借助已有的框架,我们可以在非侵入性地向 MCP 暴露自己的功能。
sever.py:
"""快递查询 MCP 服务。
要点:
- 传输用 stdio。宿主往 stdin 发 JSON-RPC,本进程从 stdout 回。
stdout 只能走协议,日志写 stderr,否则会把响应冲乱。
- 启动后宿主先 initialize,再 tools/list。@mcp.tool 登记的函数就是工具:
函数名是工具名,文档字符串和参数注解会变成模型看到的说明和参数格式。
- instructions 是整台服务器的使用说明,initialize 时交给宿主。
- 真正执行发生在 tools/call。这里只返回结果,不自己调用模型。
"""
import logging
import sys
from mcp.server.mcpserver import MCPServer
import app
# 协议占着 stdout,日志必须走 stderr。
logging.basicConfig(stream=sys.stderr, level=logging.INFO, format="%(message)s")
# name 是宿主里看到的服务器名。instructions 约束模型怎么用这些工具。
mcp = MCPServer(
"ParcelTracker",
instructions="订单信息只能用工具查。没查到就说没有,不要编单号。",
)
# 装饰器把函数登记进 tools/list。文档字符串是给模型看的说明。
@mcp.tool()
def track_order(order_id: str) -> str:
"""按单号查询一件快递现在到哪了。
Args:
order_id: 运单号,例如 A1024
"""
logging.info("track %s", order_id)
return app.track_order(order_id)
@mcp.tool()
def list_open_orders() -> str:
"""列出还没签收的订单。"""
return app.list_open_orders()
if __name__ == "__main__":
# 阻塞在 stdio 上,直到宿主关掉管道。不要往 stdout 打印别的内容。
mcp.run(transport="stdio")
我们可以在 opencode 中配置本地 MCP 服务:
"mcp": {
"test": {
"type": "local",
"command": [
"python",
"/home/Brant/test/chat/server.py"
],
"enabled": true
}
},启动后可以在 status 中看到本地的 MCP,并且也能正常使用目标应用的相关方法。
外挂 MCP
大多数情况,我们使用的 MCP Sever 本质上都是外挂型(非侵入式适配器)。
无论是操作 Github、Docker,还是读取本地的数据库、系统日志或浏览器,开发者通常不需要知晓和改动原软件的源码。MCP Sever 可以通过各种接口、命令行、网络协议等方式间接掌控应用。
以 browser-mcp 为例,通过该 MCP 可以让 agent 直接操作浏览器界面,方便 web 调试或者自动化页面任务等。
使用方法可以参照这个。