【学习总结】MCP协议之MCP简述:从零搭建一个可复用的MCP Server配置骨架
1. 从一次“工具接不上”的调试说起如果你最近在折腾 AI Agent大概率会遇到这样一个尴尬场面模型明明能聊天、能写代码但一旦让它去查数据库、读本地文件、调内部接口就立刻“断片”。你不得不为每个模型、每个 IDE、每个客户端单独写一套适配层接完 Claude 接 Cursor接完 Cursor 接自研 Agent代码越堆越厚维护成本直线上升。MCP 协议Model Context Protocol模型上下文协议就是冲着这个痛点来的。你可以把它理解成 AI 工具链里的“USB-C 接口”以前每个设备一个插口现在统一成一种标准谁都能插。它由 MCP Host、MCP Client、MCP Server、本地数据源、远程服务五部分构成核心价值是让 AI 模型与外部工具之间的集成从“点对点硬编码”变成“标准化插拔”。这篇内容面向初次接触 MCP 的开发者聚焦一件事从零搭一个可复用的 MCP Server 配置骨架包含config.toml/settings.json示例并完成一次本地验证。读完你能拿到一套能直接改参数就用的结构而不是停留在概念层面。过程中我会用 TaoToken 作为模型接入侧的统一入口把 Key 管理和模型调用这两件杂事收拢到一处让 MCP Server 的调试更聚焦。2. 先把 MCP 的通信骨架讲清楚在动手写配置之前有必要把 MCP 的运行链路捋一遍否则后面配command和args时容易一头雾水。MCP 采用客户端-服务器模型。Host 是最终用户接触的程序比如 IDE、桌面 AI 工具或你自研的 AgentClient 由 Host 内部创建与 Server 保持 1:1 连接Server 是真正暴露能力的轻量程序它把工具、资源、提示模板通过协议标准化地“摆出来”。本地数据源和远程服务则是 Server 背后实际访问的东西比如本地文件、数据库或者一个 HTTP API。通信方式上最常见的是stdioHost 启动 Server 子进程双方通过标准输入输出交换 JSON-RPC 消息。这种方式部署简单、无需端口适合本地工具。另一种是 SSE / HTTP 流式适合远程共享的 Server。入门阶段我建议先用stdio跑通因为排查问题最直观——进程起没起、日志有没有输出一眼就能看到。这里有个容易混淆的点MCP Server 本身不“思考”它只负责把能力描述清楚并执行调用。真正决定“要不要调这个工具”的是模型。所以一个完整的验证链路是模型收到用户问题 → 判断需要调用某工具 → Host 通过 Client 转发到 Server → Server 执行并返回结果 → 模型基于结果组织回答。理解这条链路后面看配置文件里的每个字段就都有归属了。3. TaoToken 前置把模型接入这步先收拢MCP Server 调试时你往往需要一个能稳定调用工具的模型来配合验证。如果每个客户端都单独填 Key、单独配 Base URL改一次要改好几处很容易漏。我的做法是先把模型接入侧统一到 TaoToken再让各个 Host 去引用同一套配置。TaoToken 在这里扮演的是模型调用入口的角色它提供兼容常见接口规范的调用方式你拿到 API Key 后在 Host 或 Agent 侧填一次即可。具体操作路径如下先在浏览器打开官网了解整体能力https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content然后进入控制台创建密钥地址是https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content密钥管理页在这里方便你后续轮换或吊销https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content如果你打算长期做编码类 Agent可以顺带看下 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content接口基地址统一用https://taotoken.net/api拿到 Key 之后先别急着写 MCP 代码建议在模型对话页做一次最小连通性确认确保 Key 和网络都正常https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content这一步的意义在于“隔离变量”。如果后面 MCP 工具调不通你能确定问题出在 Server 配置而不是模型接入。接入文档放在这里遇到字段疑问可以对照https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content4. 可复制的 MCP Server 配置骨架下面给出一套我实际用过的骨架分三块项目结构、Server 代码、Host 侧配置。你可以直接复制后改路径和 Key。4.1 项目结构与依赖# 创建项目目录 uv init mcp-skeleton cd mcp-skeleton # 创建并激活虚拟环境 uv venv source .venv/bin/activate # Windows 用 .venv\Scripts\activate # 安装 MCP SDK 与 HTTP 客户端 uv add mcp[cli] httpx目录建议保持这样方便后续扩展多个工具mcp-skeleton/ ├── pyproject.toml ├── .env └── server.py.env里放敏感信息不要写进代码TAOTOKEN_API_KEY你的Key WEATHER_API_KEY你的天气服务Key4.2 Server 代码骨架这份代码的重点不是天气逻辑本身而是“可复用结构”常量区、请求封装区、工具注册区、入口区四段分明换业务时只改中间两段。from typing import Any, Dict import os import httpx from mcp.server.fastmcp import FastMCP # 初始化 MCP Server名字会显示在 Host 的工具列表里 mcp FastMCP(skeleton) # 常量区统一管理外部服务地址与 Key WEATHER_API_BASE https://api.map.baidu.com/weather/v1/ WEATHER_API_KEY os.getenv(WEATHER_API_KEY, ) # 城市与行政区 ID 映射按需扩展 DISTRICT_ID { 北京: 110100, 上海: 310000, 广州: 440100, 深圳: 440300, } async def request_weather(district_id: str) - Dict[str, Any] | None: 封装外部请求统一处理超时与异常 params { district_id: district_id, data_type: now, ak: WEATHER_API_KEY, } async with httpx.AsyncClient() as client: try: resp await client.get(WEATHER_API_BASE, paramsparams, timeout30.0) resp.raise_for_status() return resp.json() except Exception: return None def format_weather(data: Dict) - str: 把原始响应整理成模型易读的文本 loc data[result][location] now data[result][now] return ( f城市: {loc[city]}\n f天气: {now[text]}\n f温度: {now[temp]}°C\n f体感: {now[feels_like]}°C\n f湿度: {now[rh]}%\n f风力: {now[wind_class]}\n f更新时间: {now[uptime]} ) mcp.tool() async def get_weather(city: str) - str: 获取指定城市的当前天气 Args: city: 城市名称例如 北京 district_id DISTRICT_ID.get(city) if not district_id: return f未找到 {city} 对应的行政区 ID。 data await request_weather(district_id) if not data or data.get(status) ! 0: return 无法获取天气信息。 return format_weather(data) if __name__ __main__: # stdio 方式启动适合本地 Host 拉起子进程 mcp.run(transportstdio)关键点在于mcp.tool()装饰器它把普通函数注册成模型可调用的工具函数签名和 docstring 会成为模型判断“何时调用”的依据。所以 docstring 要写清楚参数含义别偷懒。4.3 Host 侧配置settings.json 与 config.toml不同 Host 的配置格式略有差异但核心字段一致command指向可执行文件args指向项目目录和启动脚本。通用 JSON 形式多数 IDE 和桌面工具适用{ mcpServers: { skeleton: { command: /Users/you/.local/bin/uv, args: [ --directory, /Users/you/projects/mcp-skeleton, run, server.py ], env: { WEATHER_API_KEY: 你的天气服务Key } } } }command必须是 uv 的绝对路径用which uvmacOS/Linux或where uvWindows查出来再填相对路径经常导致 Host 找不到进程。如果你的工具使用 TOML 配置可以写成这样[mcp_servers.skeleton] command /Users/you/.local/bin/uv args [--directory, /Users/you/projects/mcp-skeleton, run, server.py] [mcp_servers.skeleton.env] WEATHER_API_KEY 你的天气服务Key两种格式表达的是同一件事按你所用 Host 的文档选一种即可。改完配置记得重启 Host很多工具不会热加载 MCP 配置。5. 验证请求与成功结果配置写完先别急着接模型用官方 Inspector 单独验证 Server 是否正常这一步能把“Server 问题”和“模型问题”彻底分开。npx modelcontextprotocol/inspector启动后浏览器打开http://localhost:5173/在页面里填入Command: uv Arguments: --directory /Users/you/projects/mcp-skeleton run server.py点击 Connect如果左侧状态变为已连接说明 Server 进程被成功拉起。接着在 Tools 面板点 List Tools应该能看到get_weather选中它参数填“北京”点 Run Tool。正常返回类似城市: 北京 天气: 晴 温度: 26°C 体感: 27°C 湿度: 45% 风力: 3级 更新时间: 2025-xx-xx xx:xx看到这段文本就代表 MCP Server 本身已经跑通。接下来把它接进 Host用模型触发一次真实调用。在 Host 里启用该 Server选择你通过 TaoToken 接入的模型提问“北京今天天气怎么样”。如果模型回复中出现了天气数据并且 Host 日志里能看到工具调用记录整条链路就闭环了。这里补一句验证模型是否具备工具调用能力可以在模型对话页先做一次简单测试确认返回结构里带 tool_calls 字段再回到 Host 里联调能省不少来回折腾的时间。6. 本篇常见错排查报错一Failed to connect to github.com port 443安装 uv 或依赖时偶尔会遇到网络抖动。先确认网络环境正常稍等片刻重试如果持续失败检查本机 DNS 或换用镜像源安装 Python 依赖。这类问题多数是临时性的不必大改配置。报错二Host 里 Server 显示未连接Inspector 却正常九成是command路径写错。Host 启动子进程时不继承你终端的 PATH必须写 uv 的绝对路径。用which uv查出来原样填进去。报错三工具列表为空看不到get_weather检查mcp.tool()装饰器是否加在函数上以及函数是否是async def。另外Server 启动时如果 import 阶段就抛异常工具不会注册去看 Host 的 MCP 日志通常能看到 Python traceback。报错四调用工具返回“无法获取天气信息”先确认WEATHER_API_KEY是否通过env正确传入。stdio 模式下 Server 读的是进程环境变量如果你只在.env里写了但没在 Host 配置的env字段里传Server 是读不到的。其次检查行政区 ID 是否在映射表里没匹配到会直接返回提示。报错五模型不调用工具只是自己编答案这通常不是 MCP 的问题而是模型侧工具调用能力或提示词的问题。确认你选的模型支持 function calling并在系统提示里明确“需要实时数据时优先调用工具”。如果模型本身不支持换一个支持工具调用的模型再试。7. 把骨架用起来下一步怎么扩展这套骨架的价值在于“换业务不换结构”。你要加一个新工具只需要在常量区加服务地址写一个请求封装函数再用mcp.tool()注册一个新函数Host 侧配置完全不用动。多个工具可以共用一个 Server也可以拆成多个 Server 分别配置按职责划分即可。如果你打算长期做编码类 Agent把 MCP Server 和 Coding Plan 配合使用会更顺https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content需要管理多个项目的 Key 时回到密钥页统一维护https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content字段拿不准就翻接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content接口基地址固定用https://taotoken.net/api最后留一个我踩过的坑改完 Server 代码后一定要重启 Host 里的 MCP 连接stdio 模式下子进程不会自动重载你以为改了没生效其实跑的还是旧进程。把这条记住能省下不少“明明改了却没用”的困惑。