引入

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 调试或者自动化页面任务等。

使用方法可以参照这个。