如果把大模型比作一位厨艺高超但手边没食材的“头脑派主厨”,那 MCP Server 就是它专属的“后厨供应链”。你——作为开发者,就是这个后厨的掌勺人。大模型很聪明,但它不会自己查数据库、调 API、操作文件。它需要一套标准化的“点餐流程”,把需求写成“菜单”,交给你的后厨,后厨做好菜再端回给大模型。这套流程就是 MCP(Model Context Protocol)协议,而你搭建的那个“后厨”,就是 MCP Server。 本篇文章,我们会像筹备一家餐厅一样,从选址装修(环境搭建)、设计菜单(定义 Tools)、制定出餐流程(JSON-RPC 处理)、试营业(MCP Inspector 调试),一直到正式开业(部署发布),完整走一遍 MCP Server 的开发全流程。读完这篇,你不仅能看懂 MCP Server 是怎么跑起来的,还能亲手用 Python SDK 写出第一个属于自己的“工具服务”。

岗位关联

一句话价值:MCP Server 是连接大模型与真实世界的“万能插座”,掌握它的开发,你就能让 AI 真正“动手干活”,这是 MCP 时代 AI 工程化最硬核的生存技能之一。

目录
  1. 可视化演示
  2. 开餐厅前的准备:MCP 协议速览
  3. 选址装修:开发环境搭建
  4. 设计菜单:定义你的工具箱
  5. 后厨传菜:JSON-RPC 请求处理
  6. 试营业:用 MCP Inspector 调试
  7. 正式开业:部署与发布
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示工具定义与注册机制、JSON-RPC请求处理通信流程、以及容器化部署与MCP Inspector调试三大核心开发环节。

本节配有交互动效(进入视口后自动加载,约 3KB)。

MCP Server 工具定义:@tool装饰器注册函数,自动生成schema并加入工具注册表 JSON-RPC:客户端请求→服务端解析路由执行→响应返回,支持心跳保活 Pipeline: Code→Build→Container→Deploy→Run 部署调试:容器化部署+MCP Inspector工具测试+日志监控

开餐厅前的准备:MCP 协议速览

想象你要开一家专门给大模型供餐的“后厨外卖店”。在开店之前,你得先搞清楚三个角色的分工:点餐的顾客(LLM 大模型)、跑堂的传菜员(MCP Client 客户端)、以及后厨你自己(MCP Server 服务端)。顾客不会直接冲进后厨,传菜员负责把顾客的“口头需求”翻译成一张标准化的点餐单(JSON-RPC 消息),后厨按单做菜,再经由传菜员把菜端回去。

MCP 协议就是这套“点餐单”的标准格式。它规定了消息怎么传(stdio 或 HTTP)、有哪些类型的“菜品”(tools、resources、prompts)、以及从开门到打烊的完整流程(initialize 初始化 → 能力协商 → 正常服务 → 关闭)。理解了这个三角色模型,你就拿到了往后厨所有菜谱的“总纲”。

技术上说,一次完整的 MCP 交互长这样:客户端先发一个 initialize 请求表明来意,服务端回传自己支持的能力列表(如支持哪些工具),双方“握手”成功后,客户端就可以通过 tools/call 方法点具体的“菜”了。整个过程中,JSON-RPC 2.0 是唯一的“传菜语言”。

1. initialize 初始化:这是“递名片”环节。客户端告诉服务端“我是谁、我需要什么版本协议”,服务端回复“我支持这些能力”。

Client→initialize 请求Server\text{Client} \xrightarrow{\text{initialize 请求}} \text{Server}

2. 能力协商:服务端返回支持的协议版本和工具清单,相当于餐厅展示“今日菜单”。

Server→capabilities 响应Client\text{Server} \xrightarrow{\text{capabilities 响应}} \text{Client}

3. 请求响应循环:完成握手后,进入点餐循环,双方用请求-响应对完成业务。

Client→tools/callServer→resultClient\text{Client} \xrightarrow{\text{tools/call}} \text{Server} \xrightarrow{\text{result}} \text{Client}

把三个角色装进脑子里,后面所有的代码都是给这个“后厨”添砖加瓦。记住:传菜员(Client)不一定是你写的,但后厨(Server)是你的核心资产。

选址装修:开发环境搭建

开店第一步不是炒菜,而是把灶台装好。对 MCP Server 开发来说,“灶台”就是 Python 环境加 MCP SDK。官方推荐用 uv 这个工具来管理项目——它就像一位高效的“装修队长”,能一键创建项目骨架、安装依赖、管理虚拟环境,省去你手工配置的琐碎。

用 uv init mcp-server-demo 初始化项目后,你会得到一个干净的项目骨架。接着执行 uv add "mcp[cli]" 把 MCP Python SDK 装进项目里。这个 SDK 就是你的“后厨设备包”——里面预制了 Server 类、工具注册器、传输层封装等所有基础设备,你不用从零造轮子。

SDK 里最核心的两个导入是 Server 和 stdio_server。前者是后厨的“总操作台”,后者是连接传菜员的“传菜口”——它负责从标准输入读取 JSON-RPC 消息,并把响应写回标准输出。理解了这一进一出,你就理解了 MCP Server 的运行骨架。

环境搭建就像装修:今天装好灶台(Python/uv),明天买齐厨具(SDK)。一旦基础设备到位,你就可以开始设计菜单了。

1. 项目初始化:创建项目文件夹和配置文件,相当于划定后厨区域。

uv init→pyproject.toml\text{uv init} \rightarrow \text{pyproject.toml}

2. 安装依赖:把 MCP SDK 装进虚拟环境。

uv add mcp[cli]→site-packages/mcp\text{uv add mcp[cli]} \rightarrow \text{site-packages/mcp}

3. 服务实例化:创建 Server 对象,绑定服务名。

Server("name")→App\text{Server("name")} \rightarrow \text{App}

python3 行
from mcp.server import Server, stdio_server app = Server("demo-server")  # 总操作台

设计菜单:定义你的工具箱

餐厅的灵魂是菜单。对 MCP Server 来说,“菜单”就是 Tool 工具集——大模型能调用的每一个功能都是一道“菜”。而 MCP Python SDK 提供了最优雅的“写菜方式”:装饰器。

用 @app.tool() 装饰一个普通的 Python 函数,这个函数瞬间就变成了一道“标准菜”。函数名是菜名,函数参数是配菜(输入),返回值是成品(输出)。SDK 会自动把你这个函数翻译成 MCP 协议里的 Tool 定义,包括参数的模式(inputSchema)和函数的描述。

这里最关键的技巧是“把描述写得像菜单文案”。比如“查询用户信息”这个函数,如果你只写 查询用户,大模型可能不知道什么时候该调它;但如果你写 根据用户ID查询用户的姓名、邮箱和注册时间,用户ID是整数,大模型就能精准地“点这道菜”。菜单写得好,大模型才知道什么场合点什么菜。

菜单设计决定了顾客体验。一个清晰的工具列表,让大模型“看菜单就知道点什么”;一个混乱的工具列表,则会让大模型像走进一家没有图片的异国餐厅一样手足无措。

1. 装饰器注册:@app.tool() 把函数注册进服务的工具清单。

f→@toolToolf \xrightarrow{\text{@tool}} \text{Tool}

2. 类型标注转 Schema:Python 类型提示被自动转为 JSON Schema 格式。

int→{type: "integer"}\text{int} \rightarrow \{\text{type: "integer"}\}

3. docstring 转描述:函数文档字符串变成工具描述。

docstring→description\text{docstring} \rightarrow \text{description}

python4 行
@app.tool()def get_user(user_id: int) -> dict:    """根据用户ID查询用户信息"""    return {"id": user_id, "name": "小明", "email": "ming@example.com"}

后厨传菜:JSON-RPC 请求处理

现在后厨设备有了,菜单也写好了,接下来最关键的一环是“传菜流程”——也就是 JSON-RPC 消息处理。如果把 MCP 比作餐厅的传菜系统,那 JSON-RPC 就是传菜员手里的“单子”:每张单子上写着菜名(method)、配菜(params)和单号(id)。

标准 JSON-RPC 2.0 的消息长这样:{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_user","arguments":{"user_id":123}}}。服务端要做的事情是:解析这张单子,认准菜名(路由到对应 Tool),核对配菜(参数校验),然后下厨做菜(执行函数),最后把成品装盘(返回结果)按原路送回(带同一个 id)。

SDK 已经帮我们封装好了这套“传菜管线”。你在定义 Tool 时,SDK 底层自动注册了 tools/call 的处理逻辑:收到请求后,它根据 params.name 找到对应的 Python 函数,把 params.arguments 解包成函数参数去调用,最后把返回值序列化成 JSON-RPC 响应。你只需要关心“炒菜”,传菜的事交给 SDK。

如果出了问题呢?比如大模型点了一道不存在的菜,SDK 会返回一个 -32602 错误码(无效参数);如果函数内部抛出异常,则返回 -32603(内部错误)。这些错误码就是后厨的“客诉单”,帮助调用方快速定位问题。

1. 解析请求:把接收到的 JSON 字符串解析为对象。

JSON string→json.loadsdict\text{JSON string} \xrightarrow{\text{json.loads}} \text{dict}

2. 路由分发:根据 method 字段找到对应的工具处理函数。

method→handler\text{method} \rightarrow \text{handler}

3. 参数绑定:把 arguments 字典解包为函数关键字参数。

{args}→f(kwargs)\{\text{args}\} \rightarrow f(\text{kwargs})

4. 错误响应:异常时返回带错误码的响应对象。

Error→{"code":−32602,"message":"..."}\text{Error} \rightarrow \{"\text{code}": -32602, "\text{message}": "..."\}

后厨传菜流程越标准,翻台率越高。SDK 帮你把传菜管线做好了,你把精力聚焦在“菜品的味道”(业务逻辑)上即可。

试营业:用 MCP Inspector 调试

餐厅开业前,一定要先试营业。MCP 生态里的“试营业工具”就是 MCP Inspector——一个可视化调试面板。它就像一位“神秘顾客”,能模拟大模型的点餐行为,把你后厨的每一个环节都“看”得清清楚楚。

启动 Inspector 的命令是 mcp inspector(在使用 mcp[cli] 安装的前提下)。它会自动拉起一个本地 Web 面板和一个指向你 Server 的连接。在这个面板里,你可以看到工具列表、手动调用某个工具并观察完整请求响应、查看服务端日志——相当于餐厅里装了“后厨直播摄像头”。

调试的核心技巧是“分步观察”:先看 initialize 是否握手成功,再看 tools/list 能否拉到工具清单,最后才测 tools/call 的实际调用。如果某一步出问题,查看 Inspector 捕获的原始 JSON 消息,几乎能立刻定位是协议层问题还是业务逻辑问题。

有了 Inspector 这个“后厨直播”,你不再需要盲猜问题出在哪里。它把你的 Server 变成一个透明的玻璃房:请求怎么进来的、怎么路由的、怎么响应的,一目了然。

正式开业:部署与发布

试营业没问题了,就要正式开业。正式营业意味着你的后厨要“全天候运转”——这个过程就是部署与发布。MCP Server 的部署,核心是“让后厨时刻保持接待状态”。

由于 MCP Server 走的是 stdio 传输,它本质上是“被拉起”的进程:由客户端(如 Claude Desktop)启动并管理。但如果要在服务器上长期运行,你需要考虑三件事:进程守护、环境配置、容器化。

进程管理上,用 systemd 或 supervisor 这类工具守护你的 Server 进程,这样哪怕程序崩溃了,也能自动重启——就像餐厅的“备用发电机”,停电了自动切换。环境配置上,把数据库密码、API Key 之类的敏感信息放进环境变量而不是硬编码在代码里——就像后厨的“配方锁在保险柜”,只给授权的人看。容器化上,用 Docker 打包你的 Server,让它在任何机器上都能以完全一致的方式运行——就像“中央厨房统一配送”,搬到哪家门店味道都一样。

部署是把代码变成服务的关键一跃。守护进程保命、环境变量保安全、容器化保一致——这三板斧砍完,你的 MCP Server 就真的“开业大吉”了。

dockerfile5 行
FROM python:3.12-slimCOPY . /appWORKDIR /appRUN pip install uv && uv sync --frozenCMD ["uv", "run", "python", "server.py"]

AI 应用场景

数据库查询助手

阶段:工具调用(Tool Use)

解决的问题:大模型本身不能直接连接数据库,只能“纸上谈兵”。传统做法是让人手工查询数据库再喂给大模型,效率低下且无法自动化。通过 MCP Server 暴露数据库查询工具,大模型可以直接“点餐”式地获取数据,实现自然语言查库。

具体案例:某电商运营团队构建了一个 MySQL 查询 MCP Server,大模型收到“上周销量 Top10 的商品有哪些”这类问题时,自动调用 query_db 工具执行 SQL,把结果整理成分析报告回复用户。运营同学不再需要写 SQL,用大白话就能完成数据查询。

企业内部系统集成

阶段:Agent 编排(Agent Orchestration)

解决的问题:企业有 OA、CRM、ERP 等系统,数据孤岛严重。传统集成需要针对每个系统开发定制 API,工作量大且难维护。用 MCP Server 统一暴露工具接口,任何支持 MCP 的客户端都能即插即用。

具体案例:某公司把“请假审批”“会议预定”“工单创建”三个功能封装成三个 MCP Tool,员工通过聊天客户端就能直接完成操作:对 AI 说“帮我预定明天下午两点的会议室”,AI 自动调用工具、执行预定、返回结果。

Python 演示

python53 行
import jsonimport sqlite3from typing import Anyfrom mcp.server import Server, stdio_server app = Server("db-query-server")DB_PATH = "app.db"  def _get_conn() -> sqlite3.Connection:    """建立数据库连接"""    conn = sqlite3.connect(DB_PATH)    conn.row_factory = sqlite3.Row    return conn  @app.tool()def query_user(user_id: int) -> dict[str, Any]:    """根据用户ID查询用户信息,返回姓名和邮箱"""    conn = _get_conn()    row = conn.execute(        "SELECT name, email FROM users WHERE id = ?", (user_id,)    ).fetchone()    conn.close()    if row is None:        return {"found": False}    return {"found": True, "name": row["name"], "email": row["email"]}  @app.tool()def log_event(event_type: str, message: str) -> dict[str, Any]:    """写一条事件日志到数据库,返回日志ID"""    conn = _get_conn()    cur = conn.execute(        "INSERT INTO logs (event_type, message) VALUES (?, ?)",        (event_type, message),    )    conn.commit()    log_id = cur.lastrowid    conn.close()    return {"log_id": log_id}  async def main() -> None:    """程序入口:通过stdio通道运行服务"""    async with stdio_server() as (read_stream, write_stream):        await app.run(read_stream, write_stream, app.create_initialization_options())  if __name__ == "__main__":    import asyncio     asyncio.run(main())
目的:这是一个可以通过 MCP 协议连接的数据库查询服务端程序。它把 SQLite 数据表的查询和写入能力封装成 MCP 工具,任何 MCP 客户端(如 Claude、Cursor)都可以直接调用。
  1. app = Server("db-query-server")创建 MCP 服务实例,相当于“后厨总操作台”
  2. def _get_conn() -> sqlite3.Connection封装数据库连接,每个请求独立开合
  3. @app.tool()装饰器把函数注册为 MCP 工具
  4. def query_user(user_id: int) -> dict[str, Any]定义查询工具,注意类型标注决定参数模式
  5. row_factory = sqlite3.Row让查询结果支持按字段名访问
  6. async with stdio_server() as (read_stream, write_stream)建立标准输入输出双向通道
  7. await app.run(read_stream, write_stream, ...)启动服务,监听请求并自动路由

常见误区

误区刚接触 MCP 时,总是担心要不要自己写一套 JSON-RPC 解析器。然后就开始研究 `method` 字段有哪些值、`params` 怎么拆,觉得这一定是开发 MCP Server 最核心的工作。

正解SDK 已经帮你封装了完整的 JSON-RPC 处理管线。用 @app.tool() 装饰一个普通函数后,SDK 自动完成消息解析、方法路由、参数绑定、响应序列化。你要做的只是“写函数”。就像开餐厅,你不需要自己种小麦、磨面粉,直接用买来的面粉做面包就好。

为何常见:因为 MCP 是协议,很多人下意识认为“搞协议就必须从底层做起”,忽视了 SDK 的存在价值。

误区写工具函数时,docstring 随手写一句“获取用户数量”。然后觉得大模型应该能理解这个函数是干嘛的、什么时候调用。反正自己看得懂就行。

正解大模型不看你的代码实现,它只读你的工具描述和参数定义来决定调用时机。描述写得含糊,就像餐厅菜单上只有“家常菜三个字”,顾客根本不知道能点什么。好描述应包含功能、适用场景、参数含义。例如“根据用户ID查询用户姓名、邮箱和注册时间,用户ID是整数,用于获取用户详细资料”。

为何常见:人类开发者在阅读源代码时,靠上下文理解意图,但大模型看到的只有协议层的静态描述。写描述时没转换视角。

误区觉得 MCP Server 就是个简单的脚本,跑起来后一直等着就行,不用管它崩不崩、有没有人连。反正我已经把代码写完跑起来了。

正解真实部署中,Server 可能同时被多个客户端拉起,进程崩溃、端口冲突、环境变量缺失都是高频问题。需要 systemd 等守护进程托管,配合健康检查和日志收集。后厨不是炒完一道菜就关门,而是要支撑一整天的持续运转。

为何常见:本地开发时,跑起来一次成功就觉得万事大吉,没经历过长期运行的真实场景。

误区觉得 stdio 和 HTTP 就是换个连接方式而已,既然本地开发用的是 stdio,那部署到服务器上应该也完全一样。到时候接口不通再排查。

正解stdio 适合本地进程内通信,客户端直接拉起 Server 子进程,适合个人工具的场景。HTTP(Streamable HTTP)适合远程服务,Server 作为独立服务监听端口,适合多客户端共享的场景。选错传输模式,就像把外卖窗口装在了厨房内部,顾客根本够不着。

为何常见:教程大多基于本地 stdio 演示,开发者容易把本地经验直接套用到生产环境,缺少对分布式场景的感知。

面试题

Q1. MCP 协议中,谁负责发起 initialize 请求?

单选题 · easy

  • A.LLM 大模型直接发起
  • B.MCP Client 向 Server 发起← 正确答案
  • C.MCP Server 向 Client 发起
  • D.第三方监控系统发起

答案:B。MCP 的交互模型是客户端-服务端模式,客户端负责发起握手请求。大模型通过客户端间接接入,Server 是被动等待方。就像顾客通过服务员点单,而不是直接冲进后厨。

Q2. 以下哪个是 MCP Server 中最常用的工具调用方法名?

单选题 · mid

  • A.tools/execute
  • B.call/tool
  • C.tools/call← 正确答案
  • D.tool/invoke

答案:C。MCP 协议规定调用工具的方法是 tools/call,属于 JSON-RPC 2.0 规范中的 method 字段。其他选项是干扰项。点餐单上的“菜名”是标准化的,不能随便写。

Q3. MCP Python SDK 中 @app.tool() 装饰器自动完成了哪些工作?

多选题 · mid

  • A.函数注册为 Tool← 正确答案
  • B.函数签名转成参数 schema← 正确答案
  • C.docstring 转成工具描述← 正确答案
  • D.SQL 语句自动优化

答案:ABC。装饰器会注册工具、根据类型标注生成 inputSchema、用 docstring 作为描述。它不做 SQL 优化等业务层的事。这就像厨房里的“标准菜谱生成器”,把家常做法自动变成正式菜单。

Q4. MCP Server 通过什么通道与本地客户端通信?

单选题 · easy

  • A.标准输入输出(stdio)← 正确答案
  • B.共享内存
  • C.文件系统
  • D.邮件服务

答案:A。本地 MCP Server 使用 stdio 作为传输层,从 stdin 读请求、往 stdout 写响应,这是最简单直接的进程间通信。就像传菜员和后厨之间通过传菜窗口递单子。

Q5. JSON-RPC 响应中,错误码 -32602 表示什么?

单选题 · mid

  • A.服务器内部错误
  • B.无效参数← 正确答案
  • C.方法不存在
  • D.请求已超时

答案:B。-32602 是 JSON-RPC 2.0 标准中“Invalid params“的错误码,表示参数类型不对或缺少必填项。就像顾客点了一份”宫保鸡丁”却要求不放鸡丁,厨房自然没法做。

Q6. MCP Server 部署时,以下哪些做法是正确的?

多选题 · hard

  • A.用 systemd 守护进程← 正确答案
  • B.数据库密码硬编码在代码里
  • C.环境变量注入敏感配置← 正确答案
  • D.用 Docker 打包保证环境一致← 正确答案

答案:ACD。进程守护保证崩溃自动重启;环境变量隔离敏感信息;容器化实现环境一致性。硬编码密码是安全大忌。后厨的保险柜钥匙必须单独保管,不能贴在门上。

Q7. MCP Inspector 的主要作用是?

单选题 · mid

  • A.替代大模型进行推理
  • B.可视化调试 MCP Server← 正确答案
  • C.生成数据库建表语句
  • D.自动部署代码到服务器

答案:B。Inspector 是调试工具,提供 Web 面板查看工具列表、手动调用工具、观察请求响应。它像后厨的“直播摄像头”,让开发者看清每个环节。

Q8. uv 在 MCP 项目中的作用是?

单选题 · easy

  • A.数据库迁移工具
  • B.日志收集框架
  • C.Python 项目管理工具← 正确答案
  • D.前端 UI 组件库

答案:C。uv 是现代化的 Python 项目管理器,负责创建项目、管理依赖和虚拟环境。它像餐厅装修团队的“总监理”,负责把后厨的基础设施一次性搞定。

Q9. MCP 协议中,一个 Tool 的定义通常包含哪些部分?

多选题 · mid

  • A.工具名称← 正确答案
  • B.参数 schema← 正确答案
  • C.描述文本← 正确答案
  • D.前端组件代码

答案:ABC。MCP 的 Tool 定义包含 name、inputSchema、description 三要素。前端组件是应用程序层面的东西,与协议无关。菜单上的每道菜都要有菜名、食材清单和介绍文案。

Q10. 以下哪种场景最适合使用 HTTP 传输而非 stdio?

单选题 · hard

  • A.单机本地工具调用
  • B.多个远程客户端共享同一服务← 正确答案
  • C.进程内函数直接调用
  • D.无网络环境的离线脚本

答案:B。HTTP(Streamable HTTP)适合服务端独立部署、多客户端远程访问的场景;stdio 适合本地单机使用。“中央厨房”的服务要供多家门店使用,就得上“配送网络”而不是指望传菜员跑腿。