MCP 怎么接大模型?Model Context Protocol 接入教程
Agent 想读文件、查数据库、调内部接口,早期每个工具都得自己写一套适配代码。MCP(Model Context Protocol)把「模型 ↔ 工具」的协议统一了:只要工具实现了 MCP server,任何支持 MCP 的客户端都能直接用。这篇文章讲怎么在应用里接上 MCP server,让大模型调起真实工具。
MCP 在架构里的位置
大模型
↑ 函数调用(function calling)
应用客户端(把 MCP 工具转成函数 schema)
↑ MCP 协议
MCP server(文件系统 / 数据库 / 内部 API ...)
MCP server 暴露三类能力:tools (可执行的动作)、resources (可读取的数据)、prompts(预设提示)。我们最常用的是 tools。
环境准备
bash
pip install mcp openai
# 以官方 filesystem server 为例
pip install mcp-server-filesystem
启动一个文件系统 MCP server(stdio 方式):
bash
# 让 server 能访问 /data 目录
python -m mcp_server_filesystem /data
客户端列出工具并转成函数 schema
python
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def list_tools():
server_params = StdioServerParameters(
command="python",
args=["-m", "mcp_server_filesystem", "/data"],
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
for t in tools.tools:
print(t.name, "→", t.description)
return tools.tools
tools = asyncio.run(list_tools())
把工具交给大模型调用
拿到工具后,转成 OpenAI 的函数定义,让模型决定什么时候调:
python
from openai import OpenAI
def to_function_schema(tool):
return {
"type": "function",
"function": {
"name": tool.name,
"description": tool.description or "",
"parameters": tool.inputSchema,
},
}
client = OpenAI(api_key="YOUR_API_KEY", base_url="YOUR_BASE_URL")
messages = [{"role": "user", "content": "把 /data/report.txt 的前 5 行读出来"}]
resp = client.chat.completions.create(
model="YOUR_MODEL",
messages=messages,
tools=[to_function_schema(t) for t in tools],
)
执行模型选中的工具,再把结果喂回去
python
import json
tool_call = resp.choices[0].message.tool_calls[0]
name = tool_call.function.name
args = json.loads(tool_call.function.arguments)
# 真正执行 MCP 工具
async def call_tool(name, args):
server_params = StdioServerParameters(
command="python", args=["-m", "mcp_server_filesystem", "/data"])
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
return await session.call_tool(name, args)
result = asyncio.run(call_tool(name, args))
# 把工具结果回传给模型,让它生成最终回答
messages.append(resp.choices[0].message)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": str(result.content),
})
final = client.chat.completions.create(
model="YOUR_MODEL", messages=messages)
print(final.choices[0].message.content)
Agent 循环:多轮工具调用
真实 Agent 会反复「模型选工具 → 执行 → 回传」直到模型不再调用工具:
python
while True:
resp = client.chat.completions.create(
model="YOUR_MODEL", messages=messages, tools=function_schemas)
msg = resp.choices[0].message
if not msg.tool_calls:
print(msg.content)
break
messages.append(msg)
for call in msg.tool_calls:
out = asyncio.run(call_tool(call.function.name,
json.loads(call.function.arguments)))
messages.append({"role": "tool", "tool_call_id": call.id,
"content": str(out.content)})
安全:工具别裸奔
MCP 让模型能执行真实动作,权限必须收口:
| 风险 | 做法 |
|---|---|
| 模型删库 | 只暴露只读工具,写操作加人工确认 |
| 越权读文件 | server 根目录锁死,禁止 .. 逃逸 |
| 工具参数注入 | 校验参数类型与范围 |
| 无限循环 | 限制最大工具调用轮数 |
快速排错表
| 问题 | 可能原因 | 解决方法 |
|---|---|---|
| 连不上 server | 命令/路径错 | 先本地手动起 server |
| 工具列表为空 | 没 initialize | 调用前 session.initialize() |
| 模型不调工具 | schema 描述差 | 写清名称与用途 |
| 参数解析失败 | 类型不符 | 按 inputSchema 校验 |
| 死循环 | 无轮数上限 | 限制最大调用次数 |
| 执行报错 | 权限不足 | 检查 server 目录/账号 |
| 结果太大 | 返回超长 | 工具侧截断再回传 |
| 超时 | 工具太慢 | 设客户端超时 |
配置检查清单
| 检查项 | 怎么确认 |
|---|---|
| server 能独立启动 | 手动运行无报错 |
| 工具 schema 清晰 | 名称/描述/参数齐全 |
| 权限最小化 | 只读优先、写操作确认 |
| 轮数有上限 | 防止无限循环 |
| 参数校验 | 类型与范围都检查 |
| 结果截断 | 大返回先裁剪 |
| 超时设置 | 慢工具不卡死 |
| 日志留痕 | 每次调用可回溯 |
MCP 的价值是把「接工具」从体力活变成标准协议。客户端负责把 MCP 工具转成函数调用,模型负责决定何时调用,你负责把权限收口。三层各管各的,Agent 就能稳稳跑起来。