ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

构建MCP Server实战:用FastMCP与uv打通Cline Agent工具链

构建MCP Server实战:用FastMCP与uv打通Cline Agent工具链 1. 从零构建 MCP Server 的完整场景与踩坑起点MCPModel Context Protocol是让大模型从“只会聊天”变成“能动手干活”的协议层。你可以把它理解成 AI 世界的 USB-C 接口不管对面是 Cline、Claude Code 还是别的 Agent 客户端只要你的服务端按 MCP 规范暴露工具Agent 就能自动发现并调用。FastMCP 是 Python 生态里最省心的 MCP 服务端框架uv 则是比 pip 快一个数量级的包管理器两者搭配能在十分钟内跑通一个可被 Cline 调用的自定义工具服务。这篇文章面向的是本地工具链集成场景你有一台开发机想把自己的脚本、查询接口、内部 API 封装成 Agent 能直接调用的工具而不是每次手动复制粘贴。适合谁适合已经在用 Cline 做编码辅助、想进一步把本地能力接进 Agent 工作流的开发者也适合刚接触 MCP、想找一个能完整跟做的入门案例的人。我试过在 M1 芯片的 MacBook 上从零走一遍中间遇到 uv 路径混乱、Cline 配置文件扫描不到、工具注册后 Agent 不调用等问题。下面把可复制的配置、依赖管理、接入参数和一次完整的工具调用验证动作全部拆开写你照着做就能复现。核心检索词先明确FastMCP 构建 MCP Server、uv 管理 Python 依赖、Cline MCP 接入配置、Agent 工具发现与执行。这四个点贯穿全文每一步都围绕它们展开。环境前提Python 3.10 以上FastMCP 要求一个可用的 Cline 客户端VS Code 插件即可以及一个能访问外网 API 的网络环境。我用的是 conda 隔离环境加 uv 虚拟环境的双层结构你也可以直接用 uv 管理全部。2. TaoToken 前置准备与 API Key 获取在开始写 FastMCP 代码之前需要先解决模型调用侧的凭证问题。Cline 作为 Agent 客户端背后需要一个能对话的模型服务。TaoToken 提供 OpenAI 兼容的接口层你可以在官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 了解整体能力API 入口是 https://taotoken.net/api注意这个地址不加 UTM 参数直接用于代码里的 Base URL。具体操作路径进入控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 在 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 创建一个新的 Key。创建时建议给 Key 起一个能区分用途的名字比如 cline-mcp-local方便后续排查是哪个客户端在调用。Key 只显示一次复制后先存到本地环境变量文件里不要直接硬编码进 Git 仓库。拿到 Key 之后你需要确认三件套Base URL、API Key、Model ID。Base URL 填 https://taotoken.net/apiModel ID 根据你实际要用的模型填写比如 claude-sonnet 系列或 gpt 系列以控制台模型列表为准。这三件套在 Cline 的模型配置和后续 MCP 服务里都会用到。如果你只是想先验证模型对话是否通可以打开模型对话页面 https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 发一条测试消息确认返回正常再继续。这一步能排除掉大部分“Key 无效”或“Base URL 写错”的低级问题。对于长期做编码和 Agent 集成的场景Coding Plan 页面 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 有更详细的套餐说明适合需要稳定调用量的开发者。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 遇到参数格式问题优先查这里。需要强调的是TaoToken 在这里的角色是模型服务提供方不是 MCP 服务本身。MCP Server 是你自己用 FastMCP 写的本地服务两者通过 Cline 这个客户端串联起来。理解这个分层后面排查问题会清晰很多。3. 可复制的 FastMCP 服务端与 uv 依赖配置这一节是全文技术核心给出完整的项目结构、依赖声明和 FastMCP 代码。你直接复制就能跑。先建项目目录并初始化 uv 环境mkdir weather-mcp cd weather-mcp uv venv source .venv/bin/activate uv add mcp[cli] httpxuv 会自动生成 pyproject.toml 和 uv.lock。pyproject.toml 里应该能看到类似这样的依赖声明[project] name weather-mcp version 0.1.0 requires-python 3.10 dependencies [ mcp[cli]1.0.0, httpx0.27.0, ]注意 requires-python 必须 3.10否则 FastMCP 的部分异步特性会报错。如果你用 conda 建了 3.12 的环境这里保持一致即可。接下来是 weather.py这是 MCP Server 的主文件。核心逻辑用 FastMCP 实例注册两个工具函数分别查询美国州级天气预警和经纬度天气预报。from typing import Any import httpx from mcp.server.fastmcp import FastMCP mcp FastMCP(weather, log_levelERROR) NWS_API_BASE https://api.weather.gov USER_AGENT weather-app/1.0 async def make_nws_request(url: str) - dict[str, Any] | None: headers { User-Agent: USER_AGENT, Accept: application/geojson } async with httpx.AsyncClient() as client: try: response await client.get(url, headersheaders, timeout30.0) response.raise_for_status() return response.json() except Exception: return None def format_alert(feature: dict) - str: props feature[properties] return f Event: {props.get(event, Unknown)} Area: {props.get(areaDesc, Unknown)} Severity: {props.get(severity, Unknown)} Description: {props.get(description, No description available)} Instructions: {props.get(instruction, No specific instructions provided)} mcp.tool() async def get_alerts(state: str) - str: Get weather alerts for a US state. Args: state: Two-letter US state code (e.g. CA, NY) url f{NWS_API_BASE}/alerts/active/area/{state} data await make_nws_request(url) if not data or features not in data: return Unable to fetch alerts or no alerts found. if not data[features]: return No active alerts for this state. alerts [format_alert(feature) for feature in data[features]] return \n---\n.join(alerts) mcp.tool() async def get_forecast(latitude: float, longitude: float) - str: Get weather forecast for a location. Args: latitude: Latitude of the location longitude: Longitude of the location points_url f{NWS_API_BASE}/points/{latitude},{longitude} points_data await make_nws_request(points_url) if not points_data: return Unable to fetch forecast data for this location. forecast_url points_data[properties][forecast] forecast_data await make_nws_request(forecast_url) if not forecast_data: return Unable to fetch detailed forecast. periods forecast_data[properties][periods] forecasts [] for period in periods[:5]: forecast f {period[name]}: Temperature: {period[temperature]}°{period[temperatureUnit]} Wind: {period[windSpeed]} {period[windDirection]} Forecast: {period[detailedForecast]} forecasts.append(forecast) return \n---\n.join(forecasts) if __name__ __main__: mcp.run(transportstdio)关键点说明mcp.tool()装饰器把普通异步函数注册成 MCP 工具函数的 docstring 会被自动提取成工具的元数据名称、描述、参数说明。Cline 在连接后会读取这些元数据更新自己的 system prompt从而知道有哪些工具可用、每个工具需要什么参数。所以 docstring 里的 Args 部分必须写清楚否则 Agent 可能传错参数。mcp.run(transportstdio)表示用标准输入输出通信这是目前大多数 MCP 客户端包括 Cline默认支持的方式。stdio 模式下MCP Server 作为子进程被客户端启动通过 stdin/stdout 交换 JSON-RPC 消息。依赖管理上uv 的锁文件 uv.lock 保证了每次安装的版本一致。如果你在团队里协作把 pyproject.toml 和 uv.lock 一起提交别人uv sync就能复现完全相同的环境。4. Cline MCP 接入配置与工具调用验证代码写完后需要在 Cline 里配置 MCP Server。Cline 的 MCP 配置文件通常位于 VS Code 的用户设置目录下插件会自动扫描这个文件的变动。配置内容是一个 JSON结构如下{ mcpServers: { weather: { command: /path/to/your/venv/bin/uv, args: [ --directory, /path/to/weather-mcp, run, weather.py ], timeout: 60 } } }这里最容易踩坑的是 command 路径。如果你在 conda 环境里又套了 uv 虚拟环境系统 PATH 里的 uv 可能指向旧版本或错误位置。正确做法是在激活虚拟环境后执行which uv把输出的绝对路径填进 command。我实测时就是因为 PATH 混乱Cline 一直报“无法启动 MCP Server”换成绝对路径后立刻正常。args 里的--directory指定项目根目录run weather.py让 uv 在该目录下执行脚本。timeout 设 60 秒给网络请求留足时间。保存配置后Cline 左侧的 MCP 面板会出现 weather 服务展开能看到 get_alerts 和 get_forecast 两个工具及其参数说明。如果没出现先检查 JSON 格式是否合法再看 Cline 的输出日志里有没有子进程启动失败的报错。验证调用在 Cline 对话框里输入“查一下加州当前的天气预警”。Cline 会先思考Think决定调用 get_alerts 工具参数 stateCA然后执行Act拿到返回结果后观察Observation最后用自然语言总结给你。如果加州没有活跃预警它会返回“No active alerts for this state.”然后 Cline 可能会进一步尝试用 get_forecast 查具体城市的预报。再试一个“纽约现在天气怎么样”Cline 会先尝试 get_alerts(stateNY)发现没有预警后它会“反思”并决定用 get_forecast但需要经纬度。这时候它会利用模型自身的常识把“纽约”转换成大致经纬度比如 40.71, -74.01然后调用 get_forecast。这个过程展示了 Agent 的 ReAct 模式思考、行动、观察循环。成功标志Cline 的输出里能看到工具调用记录包括工具名、入参、返回的原始数据以及最终的自然语言总结。如果只看到模型在“编造”天气而没有工具调用记录说明 MCP Server 没被正确加载回到配置步骤检查。5. 本篇常见错误排查与真实报错对照这一节列出我在复现过程中实际遇到的报错和排查路径你大概率也会碰到其中几个。报错一401 Unauthorized 或 invalid api key这个通常出现在 Cline 的模型配置环节不是 MCP Server 本身的问题。检查三件套Base URL 是否写成 https://taotoken.net/api不要多加斜杠或路径API Key 是否完整复制注意前后空格Model ID 是否在控制台模型列表里存在。如果用的是 Claude Code 类客户端OAuth 流程走完后 token 过期也会报 401重新走一遍授权即可。报错二local proxy failed 或 connection refusedCline 启动 MCP Server 子进程失败时会报这个。原因通常是 command 路径不对或者 uv 不在该路径下。用绝对路径替换 command并在终端里手动执行一遍uv --directory /path/to/weather-mcp run weather.py看是否能正常启动。如果手动能跑但 Cline 报错检查 Cline 的运行用户是否有该路径的执行权限。报错三reading choices 或 response format error这是模型返回格式不符合 OpenAI 兼容规范时的报错。检查 Base URL 是否指向了正确的 API 端点以及请求体里的 model 字段是否拼写正确。有些客户端会默认加一些额外参数如果服务端不支持就会返回非标准格式。查接入文档确认参数白名单。报错四MCP Server 启动了但工具不出现Cline 扫描配置文件有延迟保存后等几秒再刷新面板。如果还不出现检查 weather.py 里mcp.tool()装饰的函数是否有语法错误导致进程崩溃。在终端手动运行脚本看有没有 traceback。另外docstring 格式不对也可能导致工具元数据解析失败确保 Args 部分用标准格式。报错五工具被调用但返回空或超时网络问题居多。NWS API 在国内访问可能不稳定timeout 设 60 秒是必要的。如果持续超时可以换一个更稳定的测试 API或者把 make_nws_request 里的异常捕获加上日志输出确认是 DNS 解析失败还是连接超时。关于 CC Switch / Cline MCP / Codex auth.json 的三件套如果你同时用多个客户端每个客户端的配置文件里都要写全 Base URL、API Key、Model ID。Cline 的 MCP 配置只管 MCP Server 启动模型凭证在 Cline 的模型设置里单独配。Codex 的 auth.json 是另一套格式不要混用。CC Switch 类工具切换配置时确认它改的是正确的文件路径。6. 语义一致的 CTA 与后续扩展方向走到这里你已经完成了一个可被 Cline 发现并调用的 FastMCP 服务端用 uv 管理了依赖验证了一次完整的工具调用链路。接下来可以往几个方向扩展。第一把工具从“查询天气”换成你自己的业务接口。比如封装一个查询内部工单系统的工具或者一个读取本地日志文件的工具。FastMCP 的注册方式完全一样只需要改函数体和 docstring。第二考虑把重负载逻辑放到独立的 HTTP 服务里MCP Server 只做轻量代理。这样核心逻辑不暴露在本地脚本中MCP 层只负责参数校验和转发。实现方式是在工具函数里用 httpx 异步调用内部 API返回结果给 Agent。第三如果你需要长期跑编码类 Agent 任务可以了解 Coding Plan 的调用方式把模型调用量稳定下来。接入文档里有完整的参数说明和示例代码。遇到配置问题优先查 API Keys 页面确认 Key 状态再对照接入文档检查参数格式。模型对话页面可以用来快速验证模型是否正常响应排除掉模型侧的问题后再排查 MCP 侧。最后提醒一点MCP Server 的 docstring 规范不只是给 Agent 看的也是给你自己后续复用看的。每个工具函数的参数说明写清楚三个月后你还能直接拿来做新项目的基础。
返回列表