Python的MCP Server开发实战:用uv与Type Hints构建可调试的TaoToken工具服务
1. 从零写一个能调试的 MCP Server为什么我最后选了 uv Type HintsMCP Server 说白了就是给大模型外挂的一双手模型自己不会算数、不会查库、不会调你的内部接口但它能通过 MCP 协议发现你注册的 tool然后按你声明的参数结构发起调用。Python 的 MCP Server 开发核心就三件事——用 SDK 起一个服务、用装饰器注册工具、用 Type Hints 和 Docstring 把工具的语义讲清楚。适合谁适合已经会写 Python 函数、想让自己的脚本被 Claude Desktop、Cline、Codex 这类客户端直接调起来的同学。我一开始也是 pip 一把梭pip install mcp就开干结果在依赖解析和启动方式上反复踩坑本地能跑换台机器就报版本冲突mcp dev能起mcp install又挂。后来换成 uv 管理依赖配合 Type Hints 约束入参出参整个链路才稳定下来。这篇就按「初始化项目 → 写工具 → 本地 stdio 联调 → 接 TaoToken 统一 Key 通道 → 排错」的顺序走一遍代码都能直接复制。先说清楚 MCP Server 到底在干嘛。你可以把它理解成一个「函数注册中心」你写普通 Python 函数加个mcp.tool()装饰器SDK 会自动读取函数的类型注解和文档字符串生成一份 JSON Schema 描述告诉模型「这个工具叫什么、要什么参数、返回什么」。模型看到这份描述后决定什么时候调用它。所以 Type Hints 不是可选项它是模型理解你工具的唯一入口——你写a: int模型就知道要传整数你写a: str | None某些旧版本 typer 直接崩给你看这就是后面要讲的坑。uv 在这里的价值是「可复现」。MCP Server 通常要装进客户端配置里长期运行依赖一旦漂移客户端启动就失败。uv 用pyproject.tomluv.lock锁死版本uv run直接拉起虚拟环境不用手动 activate。对 MCP 这种「配置一次、长期被调用」的场景这点比 pip 省心太多。2. 用 uv 初始化项目并接入 TaoToken 统一 Key 通道这一节把地基打好装 uv、建项目、加依赖再把 TaoToken 的 API 通道配进来。TaoToken 在这里的角色是「统一 Key / API 通道」——你的 MCP 工具如果需要调用大模型能力比如做文本润色、意图识别不用每个客户端各配一套 Key走同一个 Base URL 和 Key 就行。官网入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 。先装 uv。macOS / Linux 用官方脚本Windows 用 pip 也行# macOS / Linux curl -LsSf https://astral.sh/uv/install.sh | sh # 或者已经有 Python 环境直接 pip 装 pip install uv装完验证一下uv --version # uv 0.5.x 之类然后初始化项目。注意uv init会生成pyproject.toml、.python-version和main.py我们把它改成 server 目录uv init mcp-tool-server cd mcp-tool-server加 MCP SDK 和 CLI 工具。mcp[cli]这个 extra 会带上mcp命令行用来跑 inspector 和安装到客户端uv add mcp[cli]这一步 uv 会自动创建.venv并写好uv.lock。装完你的pyproject.toml大概长这样注意requires-python和依赖版本[project] name mcp-tool-server version 0.1.0 description A debuggable MCP tool server built with uv and Type Hints readme README.md requires-python 3.10 dependencies [ mcp[cli]1.2.0, httpx0.27.0, ] [build-system] requires [hatchling] build-backend hatchling.build这里我额外加了httpx因为后面工具里要发 HTTP 请求到 TaoToken 的 API 通道。如果你暂时不接模型能力可以不加。接下来配置 TaoToken 的接入信息。MCP Server 本身不强制你用什么模型通道但一旦工具里要调模型就需要 Base URL Key Model ID 三件套。我习惯用环境变量避免把 Key 写进代码# 写入项目根目录的 .env记得加进 .gitignore TAOTOKEN_BASE_URLhttps://taotoken.net/api TAOTOKEN_API_KEYsk-你的Key TAOTOKEN_MODELclaude-sonnet-4-5Key 在控制台创建地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API Keys 管理页在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。模型对话调试页在 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。注意Base URL 用https://taotoken.net/api不要带末尾斜杠也不要在代码里拼/v1之外的路径具体以接入文档为准。如果你用的是 Claude Code 这类客户端配置片段settings 风格大致如下路径按你本机实际位置改{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: sk-你的Key, ANTHROPIC_MODEL: claude-sonnet-4-5 } }Cline / Roo 这类 VS Code 插件则是在设置里填 Base URL、API Key、Model ID 三项Base URL 同样是https://taotoken.net/api。Codex 的auth.json结构不同但三件套逻辑一致Base URL、Key、Model ID 缺一不可。这一步先把通道备好下一节写工具时就能直接调用。3. 用 Type Hints 注册工具可复制的 server.py 配置现在写核心的server.py。MCP Python SDK 提供FastMCP用装饰器注册工具SDK 会自动把类型注解和 Docstring 转成模型能读懂的 schema。先看一个最小可运行版本# server.py import sys import httpx from mcp.server.fastmcp import FastMCP mcp FastMCP(TaoTokenTools) mcp.tool() def add(a: int, b: int) - int: Add two integers and return the sum. return a b mcp.tool() def divide(a: float, b: float) - float: Divide a by b. b must not be zero. if b 0: raise ValueError(b must not be zero) return a / b if __name__ __main__: mcp.run()关键点在于mcp.tool()下面的函数签名。a: int, b: int - int会被 SDK 解析成参数类型和返回类型Docstring 变成工具描述。模型就是靠这些信息判断「什么时候该调 add、什么时候该调 divide」。我试过把 Docstring 写成和函数实际行为不一致的描述模型会按描述去调结果拿到意料之外的返回值——所以 Docstring 必须和实现一致。再写一个真正调用 TaoToken 通道的工具演示怎么在 MCP 工具里发 HTTP 请求import os import httpx from mcp.server.fastmcp import FastMCP mcp FastMCP(TaoTokenTools) BASE_URL os.environ.get(TAOTOKEN_BASE_URL, https://taotoken.net/api) API_KEY os.environ.get(TAOTOKEN_API_KEY, ) MODEL os.environ.get(TAOTOKEN_MODEL, claude-sonnet-4-5) mcp.tool() def polish_text(text: str, tone: str professional) - str: Polish the given text into the specified tone. Args: text: The raw text to polish. tone: Target tone, e.g. professional, casual, concise. if not API_KEY: raise RuntimeError(TAOTOKEN_API_KEY is not set) payload { model: MODEL, messages: [ {role: system, content: fRewrite the text in a {tone} tone.}, {role: user, content: text}, ], } headers { Authorization: fBearer {API_KEY}, Content-Type: application/json, } with httpx.Client(timeout60) as client: resp client.post(f{BASE_URL}/v1/messages, jsonpayload, headersheaders) resp.raise_for_status() data resp.json() return data[content][0][text]注意tone: str professional这种带默认值的参数SDK 会把它标成可选参数模型不传也能调。但默认值类型要和注解一致否则 schema 生成会出问题。如果你用 Cline MCP 或 Claude Desktop配置里要写全三件套。以 Claude Desktop 的claude_desktop_config.json为例{ mcpServers: { taotoken-tools: { command: uv, args: [--directory, /绝对路径/mcp-tool-server, run, server.py], env: { TAOTOKEN_BASE_URL: https://taotoken.net/api, TAOTOKEN_API_KEY: sk-你的Key, TAOTOKEN_MODEL: claude-sonnet-4-5 } } } }command用uvargs里--directory指向项目绝对路径这样客户端启动时会自动进虚拟环境跑server.py不用你手动 activate。这套配置对 Cline MCP 也基本通用只是配置文件位置不同。4. 本地 stdio 联调与一次成功请求验证写完代码别急着装进客户端先用mcp dev起 inspector 本地调试。stdio 模式下客户端和 server 通过标准输入输出通信inspector 会给你一个网页界面手动触发工具调用。uv run mcp dev server.py正常输出类似Starting MCP inspector... Proxy server listening on port 3000 MCP Inspector is up and running at http://localhost:5173浏览器打开http://localhost:5173左侧能看到你注册的工具列表点add填a1, b2点 Run右侧返回3。这一步验证的是「工具注册 类型解析 调用链路」全通。再验证polish_text。先在终端导出环境变量或者确认.env已被加载export TAOTOKEN_BASE_URLhttps://taotoken.net/api export TAOTOKEN_API_KEYsk-你的Key export TAOTOKEN_MODELclaude-sonnet-4-5 uv run mcp dev server.py在 inspector 里调polish_texttext填「这个功能挺好用的」tone填casual返回应该是润色后的句子。如果返回 401说明 Key 没读到或无效如果返回连接错误检查 Base URL 是否写成了https://taotoken.net/api。验证通过后装进 Claude Desktopuv run mcp install server.py输出里会看到Added server TaoTokenTools to Claude config。重启 Claude Desktop点输入框旁的工具图标就能看到你注册的工具。输入「帮我算一下 12 除以 4」模型会调用divide并返回3.0。这里有个细节mcp install默认把 server 名写进配置如果你改了FastMCP(TaoTokenTools)里的名字配置里的 key 也会跟着变。装完最好去claude_desktop_config.json核对一遍确认command、args、env三块都对。5. 常见报错排查401、local proxy failed、reading choices、OAuth这一节按真实报错来对。MCP Server 开发最容易卡在四类错误上我一个个拆。第一类401 Unauthorized。工具里调 TaoToken 通道返回 401九成是 Key 没传对。检查三处环境变量名是否和代码里os.environ.get(TAOTOKEN_API_KEY)一致客户端配置的env块里有没有这个变量Key 本身是否在控制台被禁用。注意Authorization头是Bearer sk-xxx别漏了Bearer前缀。第二类local proxy failed / connection refused。这种通常出现在mcp dev启动阶段端口 3000 或 5173 被占用。换个端口uv run mcp dev server.py --port 3001如果是客户端启动 server 时报这个检查args里的路径是不是绝对路径uv --directory后面有没有拼错。相对路径在客户端环境下经常解析失败。第三类reading choices of undefined。这个报错说明你拿到的响应结构不是预期的 OpenAI 风格。TaoToken 的/v1/messages返回的是 Anthropic 风格取文本要用data[content][0][text]如果你走的是/v1/chat/completions才是data[choices][0][message][content]。两种端点返回结构不同取错字段就会报reading choices of undefined或reading content of undefined。先确认你调的是哪个端点再对应取字段。第四类OAuth 相关报错。某些客户端在连接远程 MCP Server 时会走 OAuth 流程本地 stdio 模式一般用不到。如果你看到OAuth字样先确认是不是误配了远程 transport。本地开发用 stdio配置里不要写url字段只写commandargs。还有一个高频坑RuntimeError: Type not yet supported: str | None。这是 typer 旧版本不支持X | None联合类型导致的出现在mcp install或mcp dev启动时。解决办法是升级uv add --upgrade mcp[cli] typer升级后重启终端再跑。这个坑我在旧环境里踩过升级 typer 到 0.12 以上就好了。排查顺序建议先看终端完整 traceback定位是启动阶段还是调用阶段启动阶段多半是依赖/路径问题调用阶段多半是 Key/字段问题。把print(..., filesys.stderr)加在工具函数里日志会打到客户端日志或终端不影响 stdio 协议。6. 把工具服务接进长期编码流Coding Plan 与后续调试工具跑通之后下一步是让它进入日常编码流。如果你只是偶尔调一下inspector 手动触发就够了但如果你想让 MCP 工具长期挂在 Cline、Claude Code 里配合 Agent 做多步任务那就要考虑通道的稳定性和额度。TaoToken 的 Coding Plan 就是为这种长期编码场景准备的入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。接入方式还是三件套Base URL 用https://taotoken.net/apiKey 用 Coding Plan 对应的 KeyModel ID 按文档填。Claude Code 的配置片段和前面 settings 那段一致只是 Key 换成 Coding Plan 的。Cline MCP 里则是在 provider 设置里选自定义 Base URL填同样的地址和 Key。调试上我有个习惯给每个工具函数加一行 stderr 日志记录入参和耗时。stdio 模式下 stdout 被协议占用日志必须走 stderr否则会污染通信导致客户端解析失败。比如import sys import time mcp.tool() def add(a: int, b: int) - int: Add two integers and return the sum. start time.time() result a b print(f[add] a{a} b{b} result{result} cost{time.time()-start:.3f}s, filesys.stderr) return result这样在客户端日志里能看到每次调用的真实入参排查模型传参错误时特别有用。模型有时候会把字符串1传给int参数SDK 会尝试转换转不了就报错日志里一看便知。最后提醒一点MCP Server 的 Docstring 是给模型看的不是给人看的。写的时候用祈使句、说清楚边界条件比如「b must not be zero」模型在调用前就会自己判断要不要传零。这比在代码里抛异常再让模型重试要高效得多。工具描述写得好模型调用准确率能明显提升这是我在多个项目里验证过的经验。

相关新闻

FreeRTOS高优先级任务抢不到CPU?从状态机到调度机制一次讲透

FreeRTOS高优先级任务抢不到CPU?从状态机到调度机制一次讲透

几年前面试一位嵌入式候选人,我问他:系统里有个高优先级任务一直不执行,你会怎么排查?他脱口而出:把低优先级任务优先级调低。我再追问:如果这个“高优先级任务”连运行标志都没置位过呢?他愣住…

2026/10/11 19:09:19 阅读更多 →
阿里“千问突击队”全球对标ChatGPT:用TaoToken统一Key实测Qwen与GPT接口切换

阿里“千问突击队”全球对标ChatGPT:用TaoToken统一Key实测Qwen与GPT接口切换

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 20:33:04 阅读更多 →
7天入门STM32嵌入式开发:从点灯到完整项目的实战路线

7天入门STM32嵌入式开发:从点灯到完整项目的实战路线

嵌入式工程师的入门圈子,这些年冒出过不少吸睛口号,“7天精通STM32单片机”绝对算争议最大的一个。我第一眼看到也想吐槽,但冷静下来拆解,这句话背后其实有一个正向信号:STM32的生态已经成熟到不需要你花三个月时间在环…

2026/10/11 19:09:32 阅读更多 →

最新新闻

嵌入式Linux安卓驱动开发:供需、实战与面试全攻略

嵌入式Linux安卓驱动开发:供需、实战与面试全攻略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 2:53:39 阅读更多 →
共享Buffer却带宽没降?DDR流量的五大根因与排查实战

共享Buffer却带宽没降?DDR流量的五大根因与排查实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 2:53:39 阅读更多 →
OTFS信道估计实战:压缩感知与相位旋转在高速移动通信中的应用

OTFS信道估计实战:压缩感知与相位旋转在高速移动通信中的应用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 2:53:39 阅读更多 →
Qt5.9 C++开发指南章节代码实战:从环境搭建到工程避坑

Qt5.9 C++开发指南章节代码实战:从环境搭建到工程避坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 2:53:39 阅读更多 →
Linux进程虚拟地址空间:从页表映射到段错误排查

Linux进程虚拟地址空间:从页表映射到段错误排查

搞Linux服务端开发的人,迟早会遇到这么一幕:程序跑着跑着突然Segmentation Fault,或者free的时候报double free,又或者top里看到某个进程的VIRT高得离谱,但RES却很低。很多人第一反应是查代码、查日志,但真…

2026/10/12 2:53:39 阅读更多 →
ESP32 上实现 ONVIF 相机:从组件搭建到 NVR 添加实战

ESP32 上实现 ONVIF 相机:从组件搭建到 NVR 添加实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 2:52:39 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →