ARTICLE DETAIL

资讯详情

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

【MCP教程系列】用Python打造基于UVX的MCP服务:从零到一完整指南

【MCP教程系列】用Python打造基于UVX的MCP服务:从零到一完整指南 1. 为什么我要用 Python UVX 重写一遍 MCP 服务MCP 是 Model Context Protocol 的缩写简单说就是一套让大模型能调用外部工具的通信协议。你可以把它理解成给模型装了一个 USB 接口模型本身只会聊天但通过 MCP它能读文件、查数据库、调接口、跑脚本。适合谁适合所有想让自己的 Python 脚本被 Claude、Cursor、Cline 这类客户端直接调用的开发者。我最早接触 MCP 时用的是传统pip install 手动配python xxx.py的方式。问题很明显换台机器就得重装依赖虚拟环境路径一改就报错团队里别人拿到你的代码还得问“你 Python 几点几”。后来我把服务改成基于 UVX 分发情况完全变了——客户端只要写一行uvx your-package它会自动拉取、自动隔离依赖、自动执行不需要你提前装任何东西。这篇要做的是一个能跑通的 UVX 版 MCP 服务从项目结构、pyproject.toml配置、server.py编写到本地用 stdio 调用验证再到接入 TaoToken 的模型做一次真实请求。全程可复制你跟着敲就能得到一个属于自己的 MCP 服务。热词里的 MCP、Python、UVX 三个点我会在每一步都落到具体文件和命令上不空谈概念。先说清楚 UVX 是什么。它是uv工具链里的执行器类似npx之于 Node。uvx package-name会临时创建一个隔离环境装好包再运行它的入口命令跑完不留垃圾。对 MCP 来说这太合适了MCP 客户端启动服务时就是执行一条命令UVX 让这条命令变成“自包含”的用户零配置。我试过把同一个服务分别用 pip 和 uvx 分发pip 版本在别人机器上因为 Python 版本差异挂了两次uvx 版本一次过。这就是我坚持用 UVX 的原因。2. 前置准备TaoToken 接入与 uv 环境安装在写代码之前先把两件事办了装 uv以及拿到调用模型需要的 API Key。MCP 服务本身可以只做本地工具比如读文件但大多数实用场景都要调模型所以这一步不能省。2.1 安装 uv 与验证版本uv 支持 macOS、Linux、Windows。macOS/Linux 一行命令curl -LsSf https://astral.sh/uv/install.sh | shWindows 用 PowerShellpowershell -ExecutionPolicy ByPass -c irm https://astral.sh/uv/install.ps1 | iex装完验证uv --version uvx --version正常会输出类似uv 0.5.x和uvx 0.5.x。如果提示 command not found把~/.local/bin加进 PATH 再开一个新终端。这一步踩坑最多的是 Windows 用户忘了重开终端环境变量没生效。2.2 获取 TaoToken API KeyTaoToken 是一个模型调用聚合入口兼容 OpenAI 风格的接口MCP 服务里调模型时把 Base URL 指向它就行。注册和拿 Key 的入口在这里控制台与 API Key 管理https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite登录后创建一个 Key复制保存。注意 Key 只在创建时完整显示一次丢了就重新建。拿到后先别急着写进代码用环境变量管理后面配置里我会写成TAOTOKEN_API_KEY。Base URL 用https://taotoken.net/api这个地址不加任何查询参数。模型 ID 按你账号里可用的填比如gpt-4o-mini这类通用对话模型都行具体以控制台模型列表为准。2.3 初始化项目骨架建目录并初始化mkdir mcp-uvx-demo cd mcp-uvx-demo uv init --package mcp-uvx-demouv init --package会生成一个标准包结构包含pyproject.toml和src/mcp_uvx_demo/__init__.py。我建议保留这个 src 布局因为 UVX 打包时对入口点识别更稳。目录长这样mcp-uvx-demo/ ├── pyproject.toml ├── README.md └── src/ └── mcp_uvx_demo/ └── __init__.py接下来加依赖。MCP 官方 Python SDK 叫mcp我们还需要httpx来调 TaoToken 的接口uv add mcp httpxuv add会自动写进pyproject.toml的 dependencies 并生成uv.lock。锁文件很重要它保证别人 uvx 运行时装到的依赖版本和你一致避免“我这能跑你那报错”。3. 可复制配置pyproject.toml 与 server.py 完整写法这一节是核心两个文件决定服务能不能被 UVX 正确拉起。我先把pyproject.toml的完整内容贴出来再逐段解释。3.1 pyproject.toml 入口点配置[project] name mcp-uvx-demo version 0.1.0 description A demo MCP server built with Python and distributed via uvx readme README.md requires-python 3.10 dependencies [ mcp1.2.0, httpx0.27.0, ] [project.scripts] mcp-uvx-demo mcp_uvx_demo.server:main [build-system] requires [hatchling] build-backend hatchling.build关键在[project.scripts]。这一行告诉 uvx当用户执行uvx mcp-uvx-demo时去调用mcp_uvx_demo.server模块里的main函数。名字mcp-uvx-demo就是将来客户端配置里写的命令名。requires-python 3.10是因为 mcp SDK 用到了较新的类型语法低于 3.10 会报语法错误。build-system用 hatchlinguv 默认支持不用额外装。3.2 server.py 实现工具与模型调用在src/mcp_uvx_demo/下新建server.pyimport os import httpx from mcp.server.fastmcp import FastMCP mcp FastMCP(mcp-uvx-demo) TAOTOKEN_BASE_URL os.environ.get(TAOTOKEN_BASE_URL, https://taotoken.net/api) TAOTOKEN_API_KEY os.environ.get(TAOTOKEN_API_KEY, ) MODEL_ID os.environ.get(TAOTOKEN_MODEL, gpt-4o-mini) mcp.tool() def add(a: int, b: int) - int: 两数相加用于验证 MCP 工具调用链路是否通畅。 return a b mcp.tool() async def ask_model(prompt: str) - str: 把 prompt 发给 TaoToken 上的模型并返回文本结果。 if not TAOTOKEN_API_KEY: return 缺少 TAOTOKEN_API_KEY请先在环境变量中配置。 url f{TAOTOKEN_BASE_URL}/v1/chat/completions headers { Authorization: fBearer {TAOTOKEN_API_KEY}, Content-Type: application/json, } payload { model: MODEL_ID, messages: [{role: user, content: prompt}], } async with httpx.AsyncClient(timeout60) as client: resp await client.post(url, headersheaders, jsonpayload) resp.raise_for_status() data resp.json() return data[choices][0][message][content] def main(): mcp.run(transportstdio) if __name__ __main__: main()几个要点。FastMCP是官方 SDK 的高层封装mcp.tool()装饰器把普通函数注册成 MCP 工具函数签名和 docstring 会自动变成工具的参数描述模型靠这个决定怎么调。add是纯本地工具用来验证协议链路ask_model走网络验证 TaoToken 接入。main()里transportstdio表示用标准输入输出通信这是 MCP 客户端最常用的方式uvx 启动的进程天然支持。注意ask_model是 async 函数FastMCP 支持异步工具不用额外包一层。环境变量三个TAOTOKEN_API_KEY必填TAOTOKEN_BASE_URL和TAOTOKEN_MODEL有默认值。这样设计是为了让服务在没配 Key 时也能启动只是调模型会返回提示方便你先验证本地工具。3.3 本地安装与入口验证在项目根目录执行uv sync uv run mcp-uvx-demouv sync按锁文件装依赖uv run会执行[project.scripts]里定义的入口。如果服务正常终端会停住等待 stdio 输入没有报错就是成功。按 CtrlC 退出。想模拟 uvx 的隔离执行效果可以uvx --from . mcp-uvx-demo--from .表示从当前目录构建并运行等价于用户从 PyPI 装你的包。这一步能过说明打包配置没问题。4. 验证请求用 stdio 手动调一次 MCP 服务服务能启动不代表工具能被调用。MCP 协议是 JSON-RPC 格式我教你用最原始的方式发一条请求确认add和ask_model都正常响应。4.1 手动发送 initialize 与 tools/listMCP 的 stdio 通信是每行一个 JSON。先发初始化请求{jsonrpc:2.0,id:1,method:initialize,params:{protocolVersion:2024-11-05,capabilities:{},clientInfo:{name:manual-test,version:1.0}}}把服务跑起来后把这行粘进终端回车会收到包含serverInfo的响应。接着发{jsonrpc:2.0,id:2,method:tools/list,params:{}}正常返回里能看到add和ask_model两个工具以及它们的 inputSchema。如果这里 tools 是空的八成是mcp.tool()装饰器没生效或者模块没被正确导入。4.2 调用 add 工具{jsonrpc:2.0,id:3,method:tools/call,params:{name:add,arguments:{a:3,b:4}}}返回内容里content数组的 text 字段应该是7。这一步过了说明 MCP 协议链路完全通。4.3 调用 ask_model 验证 TaoToken先确保环境变量已导出export TAOTOKEN_API_KEY你的Key export TAOTOKEN_MODELgpt-4o-mini uv run mcp-uvx-demo然后发{jsonrpc:2.0,id:4,method:tools/call,params:{name:ask_model,arguments:{prompt:用一句话解释什么是MCP}}}如果返回一段模型生成的文本说明 TaoToken 接入成功。如果返回“缺少 TAOTOKEN_API_KEY”检查环境变量是否在启动服务的那个终端里生效。想更直观地看模型对话效果也可以直接在网页端试模型对话体验https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite4.4 接入真实客户端手动测通后把它配进 Cline 或 Claude Code。以 Cline 的 MCP 配置为例在设置里加一段{ mcpServers: { mcp-uvx-demo: { command: uvx, args: [--from, /绝对路径/mcp-uvx-demo, mcp-uvx-demo], env: { TAOTOKEN_API_KEY: 你的Key, TAOTOKEN_MODEL: gpt-4o-mini } } } }三件套齐了Base URL 走默认的https://taotoken.net/apiKey 在 env 里Model ID 用TAOTOKEN_MODEL指定。发布到 PyPI 后args可以简化成[mcp-uvx-demo]连路径都不用写。5. 常见报错排查401、local proxy failed 与 reading choices这一节按真实报错来都是我或读者遇到过的。401 Unauthorized。调ask_model时返回 401基本是 Key 问题。三种可能Key 没导出到启动服务的终端Key 复制时带了空格或换行Key 被删除或过期。排查方法是在同一个终端echo $TAOTOKEN_API_KEY看有没有值。注意 MCP 客户端配置里的 env 和终端环境变量是两套客户端启动的服务读的是配置里的 env。local proxy failed / connection refused。这个报错通常出现在客户端侧意思是它连不上你配置的服务进程。原因多是command写错比如写了uvx但系统 PATH 里没有或者args里的路径不对。解决在终端手动执行一遍配置里的完整命令看能不能起来。能起来说明是客户端配置问题起不来就是命令本身错。reading choices 报 KeyError。这个错来自data[choices][0]说明返回的 JSON 里没有 choices 字段。常见原因是 Base URL 写错比如多加了/v1导致路径变成/v1/v1/chat/completions或者模型 ID 不存在返回了错误结构。排查时先把resp.text打出来看原始返回。正确路径是https://taotoken.net/api/v1/chat/completions代码里我用f{TAOTOKEN_BASE_URL}/v1/chat/completions拼的Base URL 不要带/v1。OAuth 相关报错。如果你接的是需要 OAuth 的客户端比如某些 Claude Code 场景报错里出现OAuth或token exchange failed说明客户端在走授权流程而不是 stdio。MCP 的 stdio 服务不需要 OAuth检查客户端是不是把它当成了远程 HTTP 服务。stdio 类型配置里不应该有url字段。uvx 找不到包。uvx mcp-uvx-demo报No solution found说明包没发布到 PyPI 或者名字被占用。本地测试用--from .绕过。发布前先在pyproject.toml里确认 name 唯一。工具列表为空。服务起来了但tools/list返回空数组。检查server.py里装饰器是不是mcp.tool()以及main()里mcp.run有没有被真正调用。还有一种情况是模块导入时抛了异常被吞掉把uv run mcp-uvx-demo的输出完整看一遍。6. 从本地到长期运行把 MCP 服务用起来服务跑通只是起点。真正要长期用有几个方向可以走。一是发布到 PyPI让uvx mcp-uvx-demo在任何机器上一条命令可用。打包命令是uv build产物在dist/下用uv publish上传。发布前把版本号、README、license 补全否则审核会卡。二是把工具做厚。现在只有add和ask_model你可以加文件读写、HTTP 请求、数据库查询。每个工具一个mcp.tool()函数docstring 写清楚用途和参数模型靠它决定调用时机。工具粒度别太粗一个函数干一件事模型更容易选对。三是环境变量管理。生产环境别把 Key 写死在配置里用客户端的 env 字段或者系统级环境变量。TaoToken 的 Key 可以在控制台随时轮换轮换后更新配置重启服务即可。四是如果你要做的是长期编码或 Agent 类场景单次调用模型不够需要稳定的额度和并发。这种情况可以看下 Coding PlanCoding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite接入文档里有各客户端的完整配置示例包括 Claude Code、Cline、Codex 的写法接入文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite最后说个实用技巧调试 MCP 服务时别一上来就配客户端先用第 4 节的手动 JSON-RPC 方式把每个工具单独测通。客户端报错信息往往被包装过直接看原始响应最快。等tools/list和tools/call都正常了再往客户端里塞能省掉大量来回折腾的时间。
返回列表