MCP 教程:用 Python 搭建订单查询服务,接入 Claude Code 和 Cursor
MCP 教程:用 Python 搭建只读订单查询服务,先用 Inspector 验证工具,再接入 Claude Code 与 Cursor。随后配置 OAuth 认证与订单权限,选择本地 stdio 或共享 HTTP 部署;附完整代码、Render 与 Cloudflare Workers 托管价格及上线前的安全检查。
发布于

这篇 MCP 教程要实现的是:让 Claude Code 或 Cursor 直接从公司数据中回答订单状态,无需把记录复制进聊天窗口。先写一个只读 MCP 工具,用 Inspector 验证,再选择本地进程,或让团队共享经过身份认证的 HTTP 服务。
第一个服务只需做好一件事:接收订单 ID,返回订单状态。就从这里起步。通用数据库工具会把太多决策交给模型,也会授予这项工作流根本用不到的访问权限。
本文沿用当前的官方服务端教程。截至 2026 年 10 月 7 日,官方文档使用 MCP 规范 2026-07-28,以及负责处理 MCP 消息的官方 Python SDK 和其中的 MCPServer API。示例固定使用 SDK 2.3.0,避免照着旧教程的导入语句操作时,不知不觉装上不同版本。
MCP 教程入门:服务器能提供什么?
可以把 MCP 服务器理解为 AI 应用与业务系统之间一个权限可控的服务窗口。应用可以询问有哪些能力、提交请求、接收结果;这个窗口允许做什么,由你的代码决定。
MCP,也就是 Model Context Protocol,为这种交互规定了统一格式。**宿主(host)是你使用的应用,例如 Claude Code 或 Cursor;它的客户端(client)**负责与服务器进行协议交互。这些词描述的是角色,并不意味着还要另外安装三个应用。
工具也可以是只读的。把某项能力称为资源,并不能省掉访问检查;提示词给出的是指令,不是权限。不同客户端对这些能力的支持和呈现方式各不相同,因此要验证实际开放的功能。这就是服务端的三类基本能力;下面的服务器只需要工具。

动手之前,先看看是否已有持续维护的连接器能完成这项工作。可以从我们的 2026 年最佳 MCP 服务器开始找起。当内部数据、权限规则或工作流与现成连接器不匹配时,自建服务器才更有价值。
用 Python 写一个只读订单查询工具
让官方 SDK 处理协议,自己的代码只负责查询。需要准备 Python 3.10 或更新版本、官方教程使用的 Python 项目管理工具 uv,以及运行 Inspector 所需的 Node.js。当前 Inspector 要求 Node 22.19.0 或更新版本。
在终端中依次执行:
uv init orders-mcpcd orders-mcpuv venvuv add "mcp[cli]==2.3.0"
在该目录中创建 orders.py,粘贴下面这份完整的服务端代码。记录都是虚构的练习数据,不含客户姓名、付款信息或 API 凭据。
import json
from mcp.server import MCPServer
mcp = MCPServer("orders")
# Fictional training data. No customer records or credentials.
ORDERS = {
"A100": {"status": "shipped", "carrier": "Demo Courier"},
"A101": {"status": "packing", "carrier": "not assigned"},
}
@mcp.tool()
def lookup_order(order_id: str) -> str:
"""Look up a fictional order by ID, such as A100. Read-only.
Args:
order_id: Exact order ID, for example A100 or A101.
"""
key = order_id.strip().upper()
order = ORDERS.get(key)
if order is None:
return json.dumps({"found": False, "order_id": key})
return json.dumps({"found": True, "order_id": key, **order})
if __name__ == "__main__":
mcp.run(transport="stdio")这里沿用教程中记录的 MCPServer、@mcp.tool() 和 mcp.run(transport="stdio") 写法,只是把天气工具换成了订单查询。字符串类型注解告诉 SDK:order_id 是必填文本;文档字符串告诉客户端什么时候适合使用这个工具。SDK 会生成工具定义,并处理协议消息。
执行 uv run orders.py。进程没有输出、一直等待输入,是正常现象。stdio 指标准输入和标准输出,也是客户端与这个进程通信的管道。在让客户端启动自己的服务进程之前,先停止这次手动运行。
应用日志不要写到标准输出。使用 Python 的 logging 模块,它默认输出到标准错误。一个意外的 print() 就可能破坏协议消息流。这是文档明确规定的 stdio 约束,并非单纯的日志风格偏好。
把字典换成数据库时,仍然保留这个小接口。使用参数化查询,只允许数据库账号读取必要字段,并在返回记录前检查当前调用者的权限。不要为了完成这项任务,让模型传入任意 SQL 语句。
先用 MCP Inspector 把工具测通
让模型使用工具之前,先证明工具本身能正常工作。在项目目录中执行 uv run mcp dev orders.py。SDK 的开发命令会启动 MCP Inspector。打开命令输出的浏览器地址;如果尚未连接,就连接服务器。
在 Tools 中选择 lookup_order。表单应显示必填的 order_id 字段。传入 A100,结果应包含 found: true、status: shipped 和 carrier: Demo Courier;传入 A101,状态应为 packing;传入 DOES-NOT-EXIST,结果应包含 found: false。
再试一次不带 order_id 的请求。它应在输入校验阶段失败,而不是执行查询。Inspector 的 Protocol 和 Console 视图可以帮助区分请求格式错误与服务进程故障。
要在终端中重复执行检查,可以用 npx @modelcontextprotocol/inspector --cli uv run orders.py --method tools/list。调用工具则使用 npx @modelcontextprotocol/inspector --cli uv run orders.py --method tools/call --tool-name lookup_order --tool-arg order_id=A100。这些命令都遵循官方 Inspector CLI 用法。
验收标准要明确:能发现这个工具;已知订单返回预期状态;不存在的订单返回明确的未找到结果。模型生成一段看似合理的回答,并不能证明查询真的执行过。
把同一个服务接入 Claude Code 和 Cursor
每个本地客户端都会启动自己的服务进程,无需一直开着 Inspector。使用绝对路径,避免启动结果受客户端当前打开的目录影响。
**Claude Code:**执行 claude mcp add --transport stdio --scope local orders -- /ABSOLUTE/PATH/orders-mcp/.venv/bin/python /ABSOLUTE/PATH/orders-mcp/orders.py。Windows 下的解释器路径是 .venv\Scripts\python.exe。这遵循 Claude Code 的本地服务配置语法,包括启动命令前的 -- 分隔符。
执行 claude mcp get orders 检查连接。在 Claude Code 会话中打开 /mcp,然后提问:“使用 lookup_order 查询 A100,只报告返回的状态和承运商。”检查实际的工具调用及其参数。
**Cursor:**在项目中创建 .cursor/mcp.json。加入下面的 JSON,并替换其中两处绝对路径:{"mcpServers":{"orders":{"type":"stdio","command":"/ABSOLUTE/PATH/orders-mcp/.venv/bin/python","args":["/ABSOLUTE/PATH/orders-mcp/orders.py"]}}}。
打开 Customize,启用服务器,再在 Agent 中提出同样的问题。按自己的审批设置审查调用。配置文件位置、启动字段和操作控件均遵循 Cursor 的 MCP 配置说明。如果连接失败,先检查可执行文件路径和服务器的标准错误输出,再考虑修改工具。
本地 stdio 还是远程 HTTP?
个人工作流可以先留在本地。需要多人或托管客户端共用一个集中管理的服务时,再选择远程 HTTP。
远程服务还必须能通过网络访问公司数据。发布一个端点,既不会自动打通私有数据库的网络,也不会自动配好权限。
2026-07-28 版 HTTP 协议使用自包含请求。当前 Python SDK 也能服务旧版客户端;增加副本时,这些客户端的会话可能需要粘性路由。扩容前应明确配置文档中的兼容旧版选项,不要默认所有已连接客户端都在使用最新协议版本。

改用 HTTP,并加上身份认证
用专门签发给这项服务的令牌保护 HTTP 访问。OAuth 2.1 是 MCP 规范采用的授权框架:身份提供方负责用户登录并签发令牌,MCP 服务器负责验证。scope 是命名权限,例如 orders:read;audience 则规定哪些服务可以接受这枚令牌。
SDK 提供的是资源服务器集成能力,不是公司的登录系统。这个示例需要身份提供方支持 OAuth 发现、所选客户端的注册、用于用户登录的 PKCE,以及令牌内省端点。PKCE 用于证明完成登录的应用就是发起登录的应用;内省则向签发方查询令牌是否有效,以及它允许哪些操作。
这个适配器要求通过 HTTPS 进行内省,以 HTTP Basic 方式完成客户端认证,并在响应中返回 active、aud、exp、client_id 和 scope。配置签发方,让 aud 包含此端点精确的公开 URL,并签发 orders:read 权限。如果身份提供方采用其他内省认证方式,就按其文档调整请求。如果提供的是 JWT,也就是签名令牌,则在同一个 TokenVerifier 接口中实现签名、签发方、有效期和 audience 校验。
安装 uvicorn:执行 uv add uvicorn,然后创建 remote.py,放在 orders.py 旁。下面沿用 SDK 的官方内省示例及文档中的 HTTP 与认证接口,并复用刚刚测通的查询函数。
import os
import time
from urllib.parse import urlsplit
import httpx2
from pydantic import AnyHttpUrl
from mcp.server import MCPServer
from mcp.server.auth.provider import AccessToken, TokenVerifier
from mcp.server.auth.settings import AuthSettings
from mcp.server.transport_security import TransportSecuritySettings
from orders import lookup_order as local_lookup
RESOURCE = os.environ["MCP_RESOURCE_URL"]
ISSUER = os.environ["MCP_ISSUER_URL"]
INTROSPECT = os.environ["MCP_INTROSPECTION_URL"]
if any(urlsplit(url).scheme != "https" for url in (RESOURCE, ISSUER, INTROSPECT)):
raise ValueError("Public auth and resource URLs must use HTTPS")
class OrderTokenVerifier(TokenVerifier):
async def verify_token(self, token: str) -> AccessToken | None:
try:
async with httpx2.AsyncClient(timeout=5.0) as client:
response = await client.post(
INTROSPECT,
data={"token": token},
auth=(os.environ["MCP_INTROSPECTION_CLIENT_ID"],
os.environ["MCP_INTROSPECTION_CLIENT_SECRET"]),
)
response.raise_for_status()
data = response.json()
audiences = data.get("aud", [])
if isinstance(audiences, str):
audiences = [audiences]
expiry = data.get("exp")
if (data.get("active") is not True or RESOURCE not in audiences
or not isinstance(expiry, int) or expiry <= time.time()):
return None
if data.get("iss", ISSUER) != ISSUER:
return None
return AccessToken(
token=token, client_id=data["client_id"],
scopes=data.get("scope", "").split(), expires_at=expiry,
resource=RESOURCE, subject=data.get("sub"),
)
except Exception:
return None
mcp = MCPServer(
"orders",
token_verifier=OrderTokenVerifier(),
auth=AuthSettings(
issuer_url=AnyHttpUrl(ISSUER),
resource_server_url=AnyHttpUrl(RESOURCE),
required_scopes=["orders:read"], validate_token_resource=True,
),
)
@mcp.tool()
def lookup_order(order_id: str) -> str:
"""Look up a fictional order by ID, such as A100. Read-only."""
return local_lookup(order_id)
hostname = urlsplit(RESOURCE).hostname
security = TransportSecuritySettings(
allowed_hosts=[hostname, f"{hostname}:*"],
allowed_origins=[os.environ["MCP_ALLOWED_ORIGIN"]],
)
app = mcp.streamable_http_app(transport_security=security)在部署环境或密钥管理系统中配置以下值:
这里明确设置主机白名单,是因为 SDK 默认只接受 localhost;使用公开主机名时会被拒绝,并返回 421 Misdirected Request。浏览器源是另一项独立检查,只列出实际使用的源即可。返回的应用已经包含启动和关闭生命周期。这些细节来自 SDK 部署文档及 ASGI 应用文档。ASGI 是 Python Web 服务器运行此应用所用的接口。
把应用放在托管平台的 HTTPS 代理之后,以 uv run uvicorn remote:app --host 0.0.0.0 --port 8000 启动。即使代理通过 HTTP 与进程通信,公开的资源 URL 仍使用 HTTPS。转发头的信任范围,应按该平台实际的代理边界配置。
接入真实记录前,通过 HTTP 验证以下情况:
- 无令牌、令牌过期,或令牌的 audience 指向其他服务:拒绝访问。
- 令牌有效,但没有
orders:read:拒绝访问。 - 令牌有效,且 audience 与 scope 正确:
lookup_order返回演示订单的状态。 /.well-known/oauth-protected-resource/mcp:元数据指向正确的资源和签发方。
使用 npx @modelcontextprotocol/inspector --server-url https://orders.example.com/mcp --transport http 检查已部署的端点,并完成认证流程。内存中的工具测试会绕过 HTTP 授权,因此无法证明这层边界有效。
在 Claude Code 中,执行 claude mcp add --transport http orders-remote https://orders.example.com/mcp 添加单独的连接,再通过 /mcp 认证。在 Cursor 中,在 mcpServers 下添加包含 "url":"https://orders.example.com/mcp" 的远程配置,并完成 OAuth。Cursor 文档也提供了面向预注册客户端的 auth 对象,其中包含 CLIENT_ID 和 scopes。记得在签发方注册相应的客户端回调。详见 Claude Code 认证说明及 Cursor 远程 OAuth 配置。
这只是一个带认证的小型适配器,还不是完整的生产系统。替换演示字典前,要基于经过验证的身份落实租户与记录权限,为调用增加审计事件,复用 HTTP 连接,并限制请求量。scope 允许执行某类操作,但并不意味着调用者拥有每一笔订单的访问权。
部署到 Render 或 Cloudflare Workers
对于上面的 Python 服务,我会先选 Render。Python Web 服务能保留已经写好的应用。如果希望用文档提供的 Worker 处理器实现同样的受限工具,Cloudflare Workers 也很合适。
以下是服务商公布的价格,核对日期为 2026 年 10 月 7 日:
来源:Cloudflare Workers 定价与 Render 定价。如果选择团队功能,Render 的 Pro 工作区还需支付 $25/月,另计计算资源费用。存储、身份服务、模型使用等额外支出需单独预算;这里列的是托管价格,不是整个 AI 工作流的成本。
**部署到 Render:**把 orders.py、remote.py 和 requirements.txt 放入代码仓库。依赖文件中需要 mcp[cli]==2.3.0 和 uvicorn,各占一行。创建 Python Web Service,构建命令填 pip install -r requirements.txt,启动命令填 uvicorn remote:app --host 0.0.0.0 --port $PORT。添加上面的环境变量,再把平台分配的主机名或自定义域名用于 MCP_RESOURCE_URL。这套配置将 Render 官方的 Python Web 服务部署方法用于 SDK 的 ASGI 应用。
Render 的免费服务适合演示,但它会在空闲 15 分钟后休眠,唤醒约需一分钟。对于需要交互的团队共享工具,我会使用付费计算资源。
**部署到 Cloudflare:**参考当前的 MCP 处理器文档和远程服务指南。当前的 TypeScript 方案使用 createMcpHandler(来自 agents/mcp/server),配合 @modelcontextprotocol/server。在这里实现同样的订单查询,并在分享 URL 前配置认证。Python 的 uvicorn 启动命令适用于 Python 托管平台,不能直接当作 Worker 部署方案。
把安全边界限制在必要范围内
服务器只应获得工具必需的权限。查询订单状态,只需要只读后端凭据、选定字段和逐条记录的权限检查。退款、取消订单和修改地址,应交给独立工具与权限控制。模型选择了某个参数,并不代表它获得了授权。
验证 HTTP 令牌时,检查签发方、有效期、audience 和 scope。使用 HTTPS;服务器调用下游 API 时,使用独立凭据。MCP 的安全指南禁止透传令牌:提交给 MCP 端点的令牌,并不自动成为订单系统的访问凭据。本地 stdio 则要限制启动进程、进程环境及其文件系统访问。
日志应记录经过验证的调用者、工具名称、适当脱敏的记录引用、执行结果、延迟和请求 ID。不要记录令牌或完整客户记录。stdio 日志写到标准错误,HTTP 日志写入托管平台的日志系统。把记录中取出的文本当作数据;订单里的备注不能授予执行其他操作的权限。
当多个服务器或团队需要统一身份策略、限流、审计汇总或权限撤销时,可以在前面加一层网关。网关能集中管理这些控制措施,但每个后端仍需正确处理记录权限。我们的 MCP 网关指南解释了何时值得这样做。

6 种实用工作流,按直接收益排序
下面这些场景都可以沿用同样的模式。优先找那些需要反复查询、范围明确,而且使用者能判断答案是否正确的任务。
收益测算应从自己的工作流出发。**以下仅为举例:**每天查询 80 次,每次 2 分钟,共消耗 160 分钟。如果后续测量表明,接入工具后每次查询节省 1 分钟,那么每天可以省下 80 分钟。这只是算术示例,不是性能基准。先测量回答是否正确、实际节省了多少时间,再谈托管支出带来的回报。
值得做的 2 个产品方向
**最有潜力的方向,是为客服团队提供订单上下文适配器。**团队可以为一项聚焦订单的集成付费,在已经使用的助手里获取正确的发货信息。DataForSEO 估算,美国 Google 对 “customer support automation” 的月搜索量为 260,核对日期为 2026 年 10 月 7 日。它说明的是这类工作的广泛需求,并非 MCP 买家的数量。Intercom 公布的 Fin 定价按处理结果计费,每个 $0.99,说明客服自动化已有相应预算;这个小型适配器提供上下文,而不是替代该产品。
最小可售版本可以只支持一个订单后端、lookup_order、带 scope 控制的登录、审计记录,以及引用返回字段的回复草稿。优势在于适配某家公司的数据和访问规则。难点是现有厂商可能已经提供连接器,而团队的模型席位、数据清理和支持服务仍然需要花钱。扩展更多工具前,先找客服负责人确认这个缺口是否存在。
**第二个方向,是具备权限控制的政策查询。**运营人员可以通过资源或范围明确的搜索工具,让用户访问公司批准的政策集合。DataForSEO 估算,美国对 “enterprise search” 的月搜索量为 390,核对日期相同。MVP 可以包含一个文档集合、来源引用、时效检查,以及按登录者权限过滤结果。难点在于,检索质量和访问控制才是产品的核心;给文件夹套一层 MCP 很容易被复制。搜索量是更广泛需求的信号,并不能证明用户愿意为这个实现付费。
MCP 解决不了哪些问题?
MCP 统一的是访问方式。数据质量、授权、后端可靠性,以及工具到底允许做什么,仍然由你负责。模型可能误读有效结果,不同客户端的能力支持和审批策略也可能不同。
当共享工具接口能改善一个可测量的工作流时,才值得搭建。对于固定的批处理任务,如果从来不需要助手来选择工具,普通 API 调用或脚本可能更合适。
周一开工时,就做这件事:与一位客服人员选出一项反复进行的查询,先用虚构记录接通两个客户端,再通过只读账号替换背后的数据集,并测试记录权限。首轮上线先保持只读。等工作流和身份边界准备好,再升级为共享 HTTP 服务。
搭建 MCP 服务器难吗?
用官方 SDK 写一个小型只读服务器并不复杂:定义函数、描述输入,再选择传输方式即可。但要安全地开放公司数据,工作量会大得多,因为还要落实权限、管理凭据并运维服务。
测试时有免费的 MCP 服务器可用吗?
本教程的虚构订单服务器可以在本地运行,无需支付托管费用。MCP Inspector 也不需要模型订阅就能调用它。云托管和之后选用的 AI 客户端,各有自己的收费方式。
MCP 服务器要花多少钱?
本地进程无需单独购买托管套餐。Cloudflare Workers 提供免费档,付费档最低 $5/月;Render 小型付费 Web 服务的计算资源价格为 $7/月。这些费用不包含模型使用、身份服务、存储和开发工作。
使用 MCP 时必须安装服务器吗?
使用 stdio 时,服务器运行在客户端所在的机器上,因此该机器必须具备服务代码和运行环境。使用远程 HTTP 时,只需配置端点并完成认证,服务器运行在托管平台上。选择客户端支持的部署方式即可。
如果希望为团队搭建并持续运维公司数据 MCP 服务,我们的 AI 生产系统服务涵盖集成及其访问控制。
- 发布日期
- 分类
- Build
- 语言







