大模型很聪明,但它默认是「瞎子」——看不到你的数据库、调不了你的内部系统、读不到你公司的文档。过去每个团队都在重复造轮子:写一堆胶水代码把 API 包成提示词。MCP(Model Context Protocol,模型上下文协议)的出现改变了这一点:它把「模型连外部世界」这件事标准化了。本文用 WorkBuddy 作为编排端,手把手带你用 Python 从零写一个 MCP Server,把任意一个内部 HTTP API 暴露成大模型可调用的标准工具,并讲清传输层、鉴权、动态注册、排错这些生产环境才会遇到的问题。读完你就能把公司里任何一个系统「接」进大模型。
先说一个真实场景。假设你公司有一套「订单查询系统」,提供 REST 接口:传订单号,返回订单状态、金额、物流节点。你想让客服同学用自然语言问大模型:「查一下订单 A12345 到哪了?」大模型本身做不到——它不知道这个接口长什么样,也调不了。
传统的做法是「提示词工程 + 代码封装」:
这两种都脆弱。更糟的是,每接一个系统就要重写一遍,十几个系统就是十几个半成品。
MCP 想解决的就是这个「N 个模型 × M 个工具」的组合爆炸。它定义了一套标准协议:只要你的工具按 MCP 规范暴露出来,任何支持 MCP 的客户端(Claude Desktop、WorkBuddy、Cursor 等)都能即插即用。你写一次 Server,所有客户端通用。
对开发者来说,MCP 的价值可以一句话概括:把「调接口」这件事,从写代码变成了「声明一个工具」。
MCP 采用经典的客户端-服务器架构,但层次比你想的清晰。理解这三层,后面写代码就不迷路。
注意一个常见误解:Server 里不放大模型,它只是「能力提供方」。模型在 Host 里,Server 是被调用的。
MCP Server 能向外暴露三类东西,理解它们的区别很重要:
对大多数工程场景,你 80% 的精力会花在 Tool 上。
新手一律先用 stdio,踩坑最少。本文示例也用 stdio。
动手前,先把心智模型钉死:你不是在「教模型调接口」,而是在「给模型一个标准工具描述」。模型看到的是工具的名字、一句话说明、参数 schema,然后它自己决定什么时候用、传什么参数。你写 Server 的本质,就是把这个「名字 + 说明 + 参数 + 执行逻辑」描述清楚。
环境准备(以 Python 为例):
pip install mcpFastMCP 高层封装(来自 mcp.server.fastmcp),它用装饰器就能定义工具,省去大量样板代码不需要 Docker、不需要框架,一个 .py 文件就能跑起来。
下面这个类比是关键:你写的每个 Tool,本质是一个被 @mcp.tool() 装饰的普通 Python 函数。函数的 docstring 就是给模型看的「说明书」,函数的参数就是模型的「输入表单」,函数返回值就是「工具结果」。
下面是一个最小但完整的 MCP Server,它暴露一个 query_order 工具:
from mcp.server.fastmcp import FastMCP import httpx import os
mcp = FastMCP("order-tool-server")
ORDER_API_BASE = os.getenv("ORDER_API_BASE", "https://api.example.com") ORDER_API_TOKEN = os.getenv("ORDER_API_TOKEN", "")
@mcp.tool() async def query_order(order_id: str) -> str: """根据订单号查询订单的状态、金额和最新物流节点。
逐行拆解,确保你真的懂:
FastMCP("order-tool-server"):创建一个 Server 实例,名字随便起,会显示在客户端里。@mcp.tool():把下面这个函数注册成一个 MCP 工具。模型看到的工具名就是函数名 query_order。order_id: str:SDK 会自动把它转成 JSON Schema 的一部分,模型知道要传字符串。str:工具结果以文本形式回传给模型。复杂结构建议序列化成 JSON 字符串,模型照样能读。生产环境绝不能像上面那样裸奔。模型偶尔会传错参数(比如传了空字符串、传了 SQL 片段)。加上校验:
from mcp.server.fastmcp import FastMCP import httpx, os, re
mcp = FastMCP("order-tool-server-v2") ORDER_API_BASE = os.getenv("ORDER_API_BASE", "https://api.example.com") ORDER_API_TOKEN = os.getenv("ORDER_API_TOKEN", "")
@mcp.tool() async def query_order(order_id: str) -> str: """根据订单号查询订单的状态、金额和最新物流节点。 Args: order_id: 订单编号,纯字母数字,例如 A12345 """ if not re.fullmatch(r"[A-Za-z0-9]{3,20}", order_id): return "参数错误:订单号需为3-20位字母或数字,请确认后重试。"
注意三个工程化细节:
Server 写好了,怎么让 WorkBuddy 用上它?核心是把 Server 注册进客户端的 MCP 配置。不同客户端配置位置不同,但结构一致:告诉客户端「Server 的名字、启动命令、环境变量」。
以 stdio 方式为例,WorkBuddy 的 MCP 配置文件(通常是 mcp.json)里加一段:
填好后重启 WorkBuddy(或点「信任」新 Server),它会在后台拉起你的 order_server.py 子进程,自动发现 query_order 工具。之后你在对话里说「查订单 A12345」,WorkBuddy 就会自动调用这个工具,把结果拼回回答里——你完全不用写调用代码。
这一步的意义:你的订单系统现在成了 WorkBuddy 的「原生能力」。任何同事用 WorkBuddy 都能查订单,不用知道接口细节。这就是 MCP 的威力——一次接入,处处可用。
写了工具只是入门。三个进阶点能让你在团队里显得专业。
有些东西不适合做成「动作」,而适合做成「可读取的数据」。比如一份「配送区域表」:
模型在需要时会通过 config://delivery-zones 这个 URI 读取它,不会误当成「要执行某个动作」。
如果你的工具有几十个,一个个写函数太笨。可以用循环动态注册:
TOOL_REGISTRY = { "query_order": {...}, "refund_order": {...}, }
这样你可以用配置文件驱动工具列表,新增工具改配置即可,不用动代码。
如果 Server 跑在远端(SSE 模式),必须加鉴权:
order_id 里夹带恶意指令,要在执行前剥离)。安全提醒:MCP Server 等于把系统能力开放给大模型,参数校验和鉴权不是可选项,是必选项。
这部分是血泪经验,照着排查能省几小时。
pip install mcp 没装对版本,或 Python 路径不对。用绝对路径指定 python 解释器最稳。mcp.run(transport="stdio") 写对了,且配置里 args 的路径是绝对路径。相对路径在子进程里会找不到文件。httpx 拿到的响应 .json() 正确解码。MCP 把「大模型连外部系统」从手工胶水代码,升级成了标准协议。你今天学会的三件事:
@mcp.tool() 装饰的函数就能暴露一个工具,docstring 是效果的关键;下一步你可以把公司里任何系统——CRM、工单、库存、知识库——都按这个模板接进来。当你的 WorkBuddy 能查订单、能看库存、能发工单,它就从「聊天机器人」变成了「真正能干活的同事」。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。