MCP 协议开发实战:从 Server 到企业权限治理

开发者教程MCPModel Context ProtocolOAuth 2.1权限治理Server 开发Python SDK2026-09-24

安全团队问你:Claude 通过 MCP 读订单库,怎么保证只读、可追溯、离职即失效?2026 年做 MCP 接入,这个问题绕不过去。下面用一个只读订单查询 Server 走完全程:本地跑通、接客户端、加 OAuth 和沙箱、补审计、换模型测试。规范以 modelcontextprotocol.io/specification 上的当前版本为准,示例按 2025-06-18 及之后的 Streamable HTTP、OAuth 2.1 路线写。

MCP 核心概念

MCP 是 JSON-RPC 2.0 协议。Host 是用户用的客户端,Claude Desktop、Cursor、VS Code Copilot Chat 都算。Host 里的 MCP Client 负责和 Server 建连接、协商能力、转发调用。Server 暴露三类原语:

原语用途例子
Tools可执行动作查订单、发退款、创建工单
Resources只读数据订单 JSON、日志文件、数据库 schema
Prompts预置提示模板退款审核话术、故障排查 checklist
传输层两种常见选择:
传输适用注意
stdio本地进程客户端拉起 Server 子进程,权限跟本机用户走
Streamable HTTP远程共享要处理认证、限流、会话、审计,旧 HTTP+SSE 已不推荐新项目使用
工具调用链路:
sequenceDiagram participant U as 用户 participant H as Host/MCP Client participant S as MCP Server participant A as 企业 API U->>H: 问“订单 A123 到哪了” H->>S: tools/call get_order S->>A: 带服务凭证查询 A-->>S: 订单数据 S-->>H: JSON-RPC result H-->>U: 自然语言回答

快速搭建 Server

用官方 Python SDK。包名 mcp,FastMCP 封装了工具注册和传输。2026 年建新项目,建议锁 SDK 版本,别直接依赖浮动 latest;MCP 规范还在演进。官方仓库:modelcontextprotocol/python-sdk。

python -m venv .venv
source .venv/bin/activate  # Windows: .venv\\Scripts\\activate
pip install 'mcp[cli]'

server.py:

from mcp.server.fastmcp import FastMCP

mcp = FastMCP('order-service')

@mcp.tool() def get_order(order_id: str) -> dict: '''按订单号查询订单摘要。只读,不返回手机号和地址。''' # 替换成你的内部 API 或数据库查询 return { 'order_id': order_id, 'status': 'paid', 'amount': 199.00, 'updated_at': '2026-01-15T10:23:45Z', }

if __name__ == '__main__': mcp.run()

mcp.run() 默认 stdio。跑之前先用 MCP Inspector 验证:

npx @modelcontextprotocol/inspector python server.py

Inspector 里重点看两件事:tools/list 能不能列出 get_order,tools/call 传 {"order_id":"A123"} 能不能返回结构化结果。如果 Inspector 报 schema 错误,通常是 Python 类型注解和返回类型不匹配;把返回值限制为 dict、list、str、int 这类 JSON 可序列化类型。

接入主流客户端

客户端配置文件传输实操注意
Claude DesktopmacOS: ~/Library/Application Support/Claude/claude_desktop_config.json;Windows: %APPDATA%\\Claude\\claude_desktop_config.jsonstdio改完完全退出再启动;路径写绝对路径
Cursor~/.cursor/mcp.json 或项目 .cursor/mcp.jsonstdio / Streamable HTTP项目级配置放进仓库前先删密钥
VS Code + GitHub Copilot.vscode/mcp.json 或用户设置stdio / HTTP不同版本字段可能变化,以 VS Code 文档为准
自研 Agent用官方 SDK 写 Client任意要处理初始化、能力协商、超时、重连
Claude Desktop 的 stdio 配置片段:
{
  "mcpServers": {
    "order-service": {
      "command": "python",
      "args": ["/绝对路径/server.py"],
      "env": {
        "ORDER_API_BASE": "https://internal-api.corp.local"
      }
    }
  }
}

Cursor 和 VS Code 的字段名接近,但 VS Code 有些版本用 servers 而不是 mcpServers。别复制粘贴就完事,打开客户端设置页确认当前字段。远程 Streamable HTTP 则把 command/args 换成 URL 和认证配置;具体字段看客户端文档。

OAuth、沙箱与审计

stdio 模式下,Server 权限等于启动它的用户权限。安全团队通常不接受“本机用户能读数据库就能读全部订单”这种模型。远程 MCP Server 要按 OAuth 2.1 资源服务器做。

流程:

sequenceDiagram participant C as MCP Client participant S as MCP Server participant A as 授权服务器 participant R as 企业 API C->>S: tools/call,无 token S-->>C: 401 + WWW-Authenticate C->>A: OAuth 2.1 + PKCE A-->>C: access_token C->>S: tools/call + Bearer token S->>S: 校验 audience、scope、过期时间 S->>R: 用下游凭证查询 R-->>S: 数据 S-->>C: JSON-RPC result

实现时抓住四个检查点:

  • token 的 aud 必须等于 MCP Server 的资源标识,不能拿别的 API token 直接复用。
  • 工具级 scope 单独定义,例如 orders.read、orders.refund,别给一个 mcp.all。
  • 每次 tools/call 都重新校验 scope,不要只在连接建立时查一次。
  • 下游 API 用独立凭证,MCP Server 不保存用户密码,也不把用户 token 直接透传给内部 API。
OAuth 相关规范可查:OAuth 2.1 草案、RFC 8707 Resource Indicators、RFC 9728 Protected Resource Metadata。MCP 授权细节以官方 spec 的 Authorization 章节为准。

沙箱不是把 Server 丢进 Docker 就完事。只读查询 Server 的容器参数可以这样:

docker run --rm -i --read-only --tmpfs /tmp:rw,noexec,nosuid,size=64m --cap-drop=ALL --security-opt no-new-privileges --network=none mcp-order:0.1

--network=none 适合完全本地计算的工具。要访问内网 API,就换成受限网络或出口代理,并在代理层做域名白名单和审计。别用 --privileged,也别挂载 Docker socket。

审计日志至少留这些字段:

字段说明
tsUTC 时间
request_idJSON-RPC 请求 ID,方便串联
client_id哪个客户端
subject用户或服务身份
tool工具名
scopes本次调用命中的 scope
args_digest参数摘要,不存完整 PII
statusok / denied / error
latency_ms耗时
日志写到只追加存储,例如 S3 Object Lock、ClickHouse 或企业 SIEM。审计不是记完就算,告警规则要覆盖:非工作时间大量 `tools/call`、同一 token 跨工具越权、`orders.refund` 高频调用、参数里出现敏感字段。

跨模型兼容实践

MCP 本身不绑定模型。兼容性取决于客户端怎么把工具描述塞进上下文,以及模型怎么生成参数。减少踩坑的做法:

  • 工具名用动词加名词:get_order、search_invoice,别用 query、handle。
  • 描述写清楚“什么时候用、什么时候别用、参数含义、返回结构”。模型看不到你的内部代码。
  • 参数 schema 尽量扁平,少用深层嵌套和 oneOf。必填字段控制在 3 个以内。
  • 返回结果别超长。列表默认 20 条,给 next_cursor。大段文本让模型自己截断,容易丢关键信息。
  • 错误信息给可行动提示,比如“order_id 格式应为 A 开头加 6 位数字”,别返回 Python 堆栈。
  • 在真实客户端里跑测试矩阵:Claude Desktop、Cursor、VS Code Copilot、你们自研 Agent。同一工具在不同客户端里表现可能不同。
测试时用一个固定提示词:“查订单 A123,只返回状态和金额。” 观察三个点:模型有没有选对工具、参数有没有按 schema 填、返回后有没有编造字段。编造字段通常说明工具描述太模糊,或者返回 JSON 里字段名和描述不一致。

下一步

挑一个只读查询接口,用 FastMCP 写 30 行 Server,在 MCP Inspector 里跑通 tools/list 和 tools/call。然后把 OAuth 的 audience 校验和工具级 scope 加在入口,再决定要不要接生产客户端。检查项只有一个:把 token 换成无 orders.read 的凭证,调用必须返回 denied,并且审计日志里能看到这次拒绝。

PREMIUM

需要完整版教程?

包含详细步骤、视频演示、提示词模板和可下载资料包。微信支付即时获取。

购买完整版 ¥29.90