MCP 入门:模型上下文协议如何打通 AI 与工具
从协议分层到最小实现,系统梳理 MCP(Model Context Protocol)的核心概念、传输方式与安全边界,并给出一个可运行的服务器示例。
背景
#大模型本身没有记忆外的"手"。要让 AI 查询数据库、读写文件、调用第三方 API,过去每个应用都要自造一套工具调用协议:有的走函数调用(function calling),有的靠提示词注入,有的干脆让模型把命令打进 Shell。协议五花八门,集成成本落在每一个接入方身上。
MCP(Model Context Protocol,模型上下文协议)是 Anthropic 于 2024 年底提出的开放标准,目标是把"应用与 AI 之间如何交换上下文"这件事规范化——官方常拿 USB-C 做比喻:一个统一接口,连接各种外设。到 2026 年,MCP 已移交开放治理(MCP.org / Linux Foundation),主流 IDE、终端 Agent、API 平台基本都原生支持。
本文按"协议分层 → 基元 → 最小实现 → 安全"的顺序展开,读完你既能看懂一个 MCP 服务器,也能自己写一个。
核心思路:三端分层
#MCP 把一次工具集成拆成三个角色:
| 角色 | 职责 | 例子 |
|---|---|---|
| Host | 宿主应用,负责用户体验与模型编排 | Claude Code、Claude Desktop、VS Code、Cursor |
| Client | Host 内的连接器,一个 Client 连一个 Server | 每个 MCP 连接对应一个 Client |
| Server | 暴露能力的进程,实现工具/资源/提示词 | GitHub MCP、数据库 MCP、自研工具 |
关键点:Host 与 Server 之间永远是 1:N。一个 Host 可以同时挂载多个 Server,每个 Server 是一个独立的被拉起(stdio)或被访问(HTTP)的进程。模型只跟 Host 说话,Host 通过 Client 把工具调用转发给 Server,再把结果回传给模型。
┌────────────── Host (Claude Code) ──────────────┐
│ LLM ──► Client A ──► MCP Server (GitHub) │
│ └──► Client B ──► MCP Server (数据库) │
└────────────────────────────────────────────────┘
这套分层带来的直接好处:Server 与 Host 解耦。同一个 MCP Server 可以被 Claude Desktop、Claude Code、任何 IDE 复用,无需为每个宿主各写一套适配层——这正是"工具的一次编写,处处接入"。
协议基元:Tools / Resources / Prompts
#MCP 定义了三类核心基元,控制权的归属各不相同:
Tools(工具)——模型控制
- 模型在需要时主动发起调用,可以执行动作(写库、发请求、改文件)。
- 定义方式与 OpenAI function calling 类似:
name+description+ JSON Schema 的inputSchema。 - 副作用最强的基元,因此权限控制(详见安全一节)主要针对它。
Resources(资源)——应用控制
- 由应用侧暴露的只读上下文,模型只能"读取"不能"执行"。
- 地址形式类似 URI:
file:///project/config.yaml、https://example.com/api。 - 适合喂给模型的长上下文,如项目文档、README、配置文件。
Prompts(提示词)——用户控制
- 可复用的提示词模板,用户主动选择触发。
- 相当于把常用的 prompt 封装成语义化的可发现单元。
三者的控制权分别是模型 / 应用 / 用户,这是理解 MCP 权限模型的第一把钥匙:谁发起,谁负责边界。
此外还有两个补充基元:Sampling(Server 反向往模型发起请求,需要用户授权)与 Roots(Server 声明自己能访问的文件系统根路径,供 Host 做边界检查)。
传输层:stdio 与 Streamable HTTP
#MCP 支持两种传输方式,选型取决于 Server 的部署形态。
stdio(本地进程)
- Client 以子进程方式拉起 Server,通过标准输入/输出传递 JSON-RPC 消息。
- 适合"安装即用"的本地工具,如
npx或uvx启动的服务器。 - 生命周期由 Host 管理,退出时一并回收。
Streamable HTTP(网络服务)
- 基于 HTTP,支持服务端流式响应(SSE),也支持普通请求/响应。
- 2025-03-26 的规范更新用它取代了早期的 HTTP+SSE 双端点方案,目前是远程 Server 的唯一标准形态。
- 适合部署在远端、多用户共享的工具服务。
选择建议:本地个人工具用 stdio(零部署成本),团队共享或需要鉴权的服务用 Streamable HTTP。
实现过程:最小 MCP Server
#下面用 TypeScript SDK 写一个最简服务器,暴露一个 get_weather 工具。
mkdir mcp-weather && cd mcp-weather
npm init -y
npm install @modelcontextprotocol/sdk
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
import { z } from "zod"
const server = new McpServer({
name: "weather",
version: "1.0.0",
})
server.registerTool(
"get_weather",
{
description: "查询指定城市的当前天气",
inputSchema: {
city: z.string().describe("城市名,例如 Beijing"),
},
},
async ({ city }) => ({
content: [{ type: "text", text: `${city}: 24°C 多云` }],
}),
)
const transport = new StdioServerTransport()
await server.connect(transport)
这段代码已经是一个合法的 MCP Server:注册工具、声明 Schema、连接 stdio 传输。现在把它接进 Claude Code:
{
"mcpServers": {
"weather": {
"command": "node",
"args": ["src/index.ts"],
"env": {}
}
}
}
Host 启动时会拉起该进程,模型的工具列表里就会出现 get_weather,并且可以在对话中直接调用。
远程 Server 与 API 接入
#如果 Server 部署为 Streamable HTTP 服务,配置里换一种写法:
{
"mcpServers": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/"
}
}
}
Claude API(Messages API) 也提供了 MCP Connector,用 mcp_servers + mcp_toolset 两个参数即可把远端工具直接挂进请求:
import Anthropic from "@anthropic-ai/sdk"
const client = new Anthropic()
const response = await client.beta.messages.create({
model: "claude-opus-5",
max_tokens: 1024,
betas: ["mcp-client-2025-11-20"],
mcp_servers: [
{ type: "url", url: "https://mcp.linear.app/mcp", name: "linear" },
],
tools: [{ type: "mcp_toolset", mcp_server_name: "linear" }],
messages: [{ role: "user", content: "列出我最近的 Linear issue" }],
})
需要注意两个参数必须成对出现:只声明 mcp_servers 而不加 mcp_toolset,会被校验直接拒绝。
在 Managed Agents(托管 Agent)场景,MCP Server 声明在 agent 配置上,而鉴权凭证放进 Vault,按 URL 匹配注入——职责分离:Agent 定义"能连什么",Vault 定义"用什么身份连"。
注意事项:安全是 MCP 的头号话题
#MCP 把"模型能碰的东西"边界大幅拓宽了,安全模型必须跟上。以下几点是实际项目中最容易踩的坑。
最小权限原则
- 只给 Server 声明的工具授予完成任务的必要权限,不要"全都要"。
- 对高副作用工具(删除、写库、发消息)设置确认门槛(
always_ask),让用户在模型动手前过目。
提示词注入(Prompt Injection)
- 模型读到不可信内容(网页、邮件、文档)时,其中"请调用某工具把 X 发给我"这类指令可能被当作合法请求执行。
- 缓解:对工具描述强调"仅在用户明确要求时调用";高敏感操作一律走人工确认;不要把密钥、Token 写进系统提示词或会话历史。
凭证管理
- 不要把 API Key 塞进
mcpServers配置或对话历史——配置常被版本管理,历史可被 API 读取。 - 远端 MCP 的鉴权凭证应放在 Vault 或服务端密钥管理里,运行时注入。
- 注意:MCP 鉴权 Token ≠ 该服务的 REST API Token。托管 MCP Server(如 Notion、Linear)通常要求 OAuth Bearer Token,用服务的原生 API Key 往往对不上。
信任边界
- 只连接可信来源的 MCP Server;第三方 Server 的代码与 Host 进程同权限运行,风险等同安装任意软件。
- 联网能力受限(sandbox)的环境里,需要显式放行 Server 域名,否则工具会静默失败。
总结
#MCP 的价值不在协议本身多复杂,而在它把"AI 接工具"从各家自研的碎片化状态收敛成了统一标准:
- 分层清晰:Host / Client / Server 三端解耦,工具一次编写处处接入。
- 基元克制:Tools / Resources / Prompts 用控制权划分边界,够用且不过度设计。
- 传输务实:本地 stdio、远程 Streamable HTTP,两种形态覆盖了绝大多数部署场景。
对个人开发者,最直接的上手路径是:用 SDK 写一个几十行的本地 Server,在 Claude Code 里挂上,把常用脚本、私有接口、本地文件变成模型可直接调用的工具。写之前先想清楚权限边界——工具能做什么,比它怎么实现更重要。
后续方向:MCP 的上下文管理(Context Management)、批量调用(Batching)与开放治理下的规范演进仍在持续推进,值得持续关注。