初识MCP(Model Context Protocol):从零搭建一个可用的MCP Server

发布时间:2026/9/30 19:33:35
初识MCP(Model Context Protocol):从零搭建一个可用的MCP Server 1. 从零理解 MCP为什么你的 AI 应用需要一个标准协议如果你最近在折腾 Claude、Cursor 或者自己写的 AI Agent大概率会撞上同一个问题模型本身很聪明但它拿不到你本地的文件、查不了你的数据库、也调不动你内部的 API。每次接一个新工具就要写一套胶水代码换一个模型胶水代码又得重写。这种“点对点硬连”的方式就是 MCP 想要解决的核心痛点。MCP 全称 Model Context Protocol是一个开放标准协议用来把 AI 模型和外部数据源、工具做标准化集成。你可以把它理解成 AI 世界里的 USB-C 接口以前每个设备都有自己的充电口现在统一成一个标准谁都能插。对开发者来说MCP 的价值在于把“n 个模型 × m 个工具”的爆炸式集成压缩成“n m”的线性工作量——模型侧只需要实现一个 MCP Client工具侧只需要实现一个 MCP Server中间由协议负责协调。这篇文章面向第一次接触 MCP 的开发者目标很明确不讲空泛概念直接带你跑通一个最小可用的 MCP Server并完成一次真实的工具调用。你会看到完整的配置片段、本地验证命令以及我实际踩过的报错。整个流程围绕三个角色展开Host承载 LLM 的应用比如 Claude Desktop、ClientHost 内部负责连接的部分、Server独立进程对外暴露工具和资源。理解这三者比背五个原语更重要。在动手之前先明确一个判断如果你只是想让模型读几个本地文件写个脚本就够了但如果你希望同一套工具能被不同模型、不同客户端复用MCP 才真正划算。接下来的步骤就是帮你验证这套复用机制到底怎么落地。2. TaoToken 前置准备给 MCP Server 配一个稳定的模型出口MCP Server 本身不产生智能它只是把工具能力暴露出去真正做决策的是背后的模型。所以在写 Server 之前得先有一个能稳定调用的模型入口。我试过直接用官方接口也试过各种中转最后在本地开发阶段固定用 TaoToken 来做模型出口原因是它的 Base URL 和 Key 管理比较清晰切换模型时不用改代码结构。你需要准备三样东西这三件套在任何 MCP 相关配置里都会反复出现Base URL、API Key、Model ID。Base URL 用https://taotoken.net/api注意这个地址不带任何查询参数直接作为 OpenAI 兼容接口的根路径。API Key 在控制台的 API Keys 页面生成建议单独建一个用于本地开发的 Key方便随时吊销。Model ID 根据你要验证的场景选做工具调用测试时选一个支持 function calling 的模型即可。拿到 Key 之后先别急着写 Server用一条 curl 确认出口是通的。这一步能帮你排除掉后面 80% 的“到底是 Server 写错了还是 Key 没配对”的扯皮。curl https://taotoken.net/api/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -d { model: claude-3-5-sonnet, messages: [{role: user, content: reply with ok}], max_tokens: 16 }如果返回里能看到choices字段和正常的文本内容说明出口没问题。如果返回 401先检查 Key 有没有复制完整、有没有多余空格如果返回local proxy failed这类错误通常是本地网络层的问题跟 Key 无关换个网络环境再试。这里要强调一点MCP Server 和模型出口是两个独立的东西。Server 负责“有什么工具可以调”模型出口负责“谁来决策调哪个工具”。把这两层分开理解后面排查问题时思路会清楚很多。TaoToken 在这里扮演的是第二层的角色它不参与 MCP 协议本身的交互只在你需要模型做推理时被调用。配置建议写进环境变量不要硬编码在代码里。本地开发可以用.env文件配合dotenv加载。这样后面把 Server 部署到别的地方时只需要换环境变量代码一行不用动。# .env TAOTOKEN_BASE_URLhttps://taotoken.net/api TAOTOKEN_API_KEYsk-你的key TAOTOKEN_MODELclaude-3-5-sonnet把这三行准备好前置工作就算完成了。接下来进入正题写一个真正能被 MCP Client 连上的 Server。3. 可复制配置手写一个最小 MCP Server 并接入客户端MCP Server 的实现方式有好几种官方提供了 Python 和 TypeScript 的 SDK。为了让你能最快看到效果这里用 Python SDK 写一个只暴露一个工具的 Server工具功能很简单接收一个城市名返回一句模拟的天气描述。重点不在功能而在于让你看清 Server 的注册、参数定义、以及客户端配置的完整链路。先装依赖。建议用虚拟环境避免污染全局包。python -m venv mcp-demo source mcp-demo/bin/activate pip install mcp python-dotenv然后创建weather_server.py。这个文件的核心是用Server类注册一个工具工具的参数用 JSON Schema 描述这样客户端才能知道该传什么。import asyncio import os from dotenv import load_dotenv from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent load_dotenv() app Server(weather-demo) app.list_tools() async def list_tools(): return [ Tool( nameget_weather, description根据城市名返回天气描述, inputSchema{ type: object, properties: { city: {type: string, description: 城市名称} }, required: [city] } ) ] app.call_tool() async def call_tool(name: str, arguments: dict): if name get_weather: city arguments.get(city, 未知城市) return [TextContent(typetext, textf{city} 今天晴气温 22 度)] raise ValueError(f未知工具: {name}) async def main(): async with stdio_server() as (read, write): await app.run(read, write, app.create_initialization_options()) if __name__ __main__: asyncio.run(main())这段代码里有两个关键点。第一list_tools返回的工具描述会被客户端读取模型就是靠这个描述决定要不要调用、传什么参数。第二call_tool是实际执行的地方参数从arguments里取返回必须是TextContent列表。很多人第一次写会直接返回字符串结果客户端报reading choices之类的解析错误就是因为返回格式不对。Server 写好后需要让客户端知道怎么启动它。以 Claude Desktop 为例配置文件在~/Library/Application Support/Claude/claude_desktop_config.jsonmacOS或%APPDATA%\Claude\claude_desktop_config.jsonWindows。配置片段如下注意command要指向虚拟环境里的 pythonargs指向你的脚本绝对路径。{ mcpServers: { weather-demo: { command: /Users/you/mcp-demo/bin/python, args: [/Users/you/mcp-demo/weather_server.py], env: { TAOTOKEN_BASE_URL: https://taotoken.net/api, TAOTOKEN_API_KEY: sk-你的key, TAOTOKEN_MODEL: claude-3-5-sonnet } } } }如果你用的是 Cline 或者别的支持 MCP 的客户端配置结构大同小异核心都是三件套启动命令、脚本路径、环境变量。这里把 Base URL、Key、Model ID 都放进env是为了让 Server 在需要调用模型时能直接读到不用额外写配置文件。配置保存后重启客户端。如果客户端能正常启动你会在工具列表里看到get_weather。这一步如果没看到工具先别怀疑代码去看客户端的日志通常是路径写错或者 python 环境不对。4. 验证请求跑通第一次工具调用并看懂返回配置生效后验证分两步走。第一步是确认 Server 进程能被客户端拉起第二步是让模型真正调用一次工具。很多人卡在第一步以为配置写完就完事了其实进程启动失败时客户端往往只给一个很模糊的提示。先手动跑一次 Server确认它本身没有语法或依赖问题。cd /Users/you/mcp-demo ./bin/python weather_server.py如果进程挂起不退出说明 stdio 模式正常在等输入这是对的。按 CtrlC 退出即可。如果直接报ModuleNotFoundError说明依赖没装进这个虚拟环境如果报Address already in use检查是不是有别的进程占用了同样的启动方式。确认 Server 能跑之后回到客户端在对话框里输入类似“帮我查一下北京今天的天气”这样的自然语言。模型会读取get_weather的描述判断需要调用它然后客户端会向 Server 发起call_tool请求。你会在界面上看到工具调用的过程最终返回“北京 今天晴气温 22 度”。这个过程中实际发生了三次交互客户端先向 Server 请求工具列表模型根据列表决定调用哪个工具并生成参数客户端把参数传给 Server 执行并拿到结果。理解这个链路比记住任何配置都重要。因为一旦出错你可以按这个顺序逐段排查工具列表有没有拿到、模型有没有生成正确的参数、Server 有没有正确执行。如果你想在命令行里直接验证可以用 MCP 官方的 inspector 工具它能模拟客户端行为把每一步的请求和响应都打印出来。npx modelcontextprotocol/inspector ./bin/python weather_server.py运行后会打开一个本地页面你可以在里面手动触发list_tools和call_tool看到原始的 JSON 消息。这个工具在排查“到底是客户端问题还是 Server 问题”时特别有用。如果 inspector 里能正常调用但客户端里不行那问题就在客户端配置如果 inspector 里也失败问题就在 Server 代码。验证通过后你会对 MCP 的交互流程有一个具体的感知它不是魔法就是一套基于 JSON-RPC 的消息规范把工具发现、参数传递、结果返回标准化了。剩下的工作就是往这个框架里不断加工具。5. 常见报错排查401、local proxy failed 与 reading choices第一次搭 MCP Server报错基本集中在几个固定位置。我把实际遇到过的几个典型错误和排查路径列出来你对照着看能省不少时间。401 Unauthorized这个最直接Key 不对或者没传。检查三件事环境变量有没有被正确加载、Key 有没有多余空格、请求头里Authorization格式是不是Bearer sk-xxx。如果是在客户端配置里写的env注意 JSON 里不能有注释也不能用单引号。有时候 Key 是对的但 Server 启动时没读到.env也会报 401这时候在代码里加一行打印确认环境变量存在。local proxy failed这个错误通常跟模型出口的网络层有关不是 MCP 协议本身的问题。表现是 Server 能启动、工具能列出但模型调用时失败。排查顺序是先确认 Base URL 写对了没有https://taotoken.net/api后面不要多加/v1之外的路径再确认本地网络能正常访问这个地址。如果换了网络环境就好了说明是本地网络策略的问题跟代码无关。reading choices 相关错误这个报错一般出现在模型返回结果解析阶段典型信息是cannot read property choices of undefined或者类似的。原因通常是模型出口返回的不是标准 OpenAI 格式或者返回体里根本没有choices字段。检查你的请求体里model字段是不是写了一个不存在的模型名或者messages格式不对。另一个常见原因是把 MCP Server 的返回格式和模型接口的返回格式搞混了——Server 的call_tool返回的是TextContent模型接口返回的是choices两者不能混用。OAuth 相关报错如果你在配置里用了需要 OAuth 的客户端可能会遇到 token 过期或 scope 不对的提示。这类问题跟 MCP Server 本身无关去客户端的授权设置里重新走一遍授权流程即可。注意不要把 OAuth token 和 API Key 搞混它们是两套东西。工具列表为空客户端连上了但看不到任何工具。先确认list_tools有没有被正确注册装饰器app.list_tools()不能漏。再确认客户端配置里的command和args指向的是同一个 Python 环境如果command用的是系统 python而依赖装在虚拟环境里就会因为找不到mcp包而静默失败。这种情况去看客户端日志通常能看到ModuleNotFoundError。排查的核心思路是分层先确认 Server 能独立跑再确认 inspector 能调通最后才怀疑客户端配置。按这个顺序大部分问题都能在五分钟内定位。6. 继续深入把 MCP 用进真实工作流跑通第一个工具调用之后你对 MCP 的理解已经从概念落到了具体链路。接下来可以做的扩展方向有几个一是增加更多工具比如读本地文件、查数据库、调内部 API每个工具都按同样的模式注册二是把 Server 从 stdio 模式换成 SSE 或 HTTP 模式方便远程调用三是把模型出口固定成一套配置在不同客户端之间复用。如果你打算长期做编码类或 Agent 类的工作建议把模型出口和 MCP Server 的配置统一管理起来。TaoToken 的 Coding Plan 适合需要长期稳定调用模型的场景接入文档里有不同客户端的配置示例API Keys 页面可以管理多个 Key 做环境隔离。模型对话页面则适合在写 Server 之前先验证模型本身的行为是否符合预期。MCP 的价值不在于协议本身有多复杂而在于它把集成这件事从“每次重写”变成了“一次写好、到处复用”。你现在写的这个 weather Server换一个支持 MCP 的客户端配置改一下就能直接用模型换成别的也不用动 Server 代码。这种解耦才是它真正省时间的地方。