ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

FastMCP 2.x 干货笔记:服务端用户引导的 3 个可复制配置与验证动作

FastMCP 2.x 干货笔记:服务端用户引导的 3 个可复制配置与验证动作 1. FastMCP 2.x 用户引导到底解决什么问题FastMCP 2.x 的服务端用户引导elicitation能力简单说就是让 MCP 工具在运行过程中主动向用户追问参数、确认操作而不是要求调用方一次性把所有输入都塞进来。这个能力从 2.10.0 版本开始提供适合需要交互式工具流程的开发者尤其是那些工具参数在调用前无法完全确定、或者需要用户二次确认才能继续执行的场景。传统 MCP 工具的工作方式是客户端把参数准备好一次性传给工具函数工具执行完返回结果。这种模式在参数明确时没问题但遇到下面几种情况就很别扭。比如文件管理工具需要知道在哪个目录下创建文件但调用方可能只给了文件名再比如数据分析工具需要用户指定日期范围但调用方只说了“分析一下数据”还有删除操作、支付操作这类高风险动作工具执行前必须让用户确认。这些场景下如果强行要求调用方一次性提供所有信息要么参数冗余要么工具直接报错。用户引导功能让工具可以暂停执行通过ctx.elicit()方法向用户发起一个结构化请求用户响应后再继续。请求可以是一个字符串、一个整数、一个布尔值也可以是一个包含多个字段的数据类。用户可以选择接受accept、拒绝decline或取消cancel工具根据不同的 action 走不同分支。这样一来工具就从“被动接收参数”变成了“主动收集信息”交互流程更自然。我试过在一个内部运维工具里用这个能力做危险操作确认效果比预想的顺手。工具在执行重启命令前先弹一个确认请求用户点接受才继续点拒绝就返回“操作已拒绝”点取消就返回“操作已取消”。整个过程不需要调用方在参数里额外传一个 confirm 字段逻辑更干净。这个能力适合谁如果你正在用 FastMCP 2.x 写 MCP 服务端工具需要跟用户来回交互或者需要在高风险操作前加一道确认那用户引导就是你要找的东西。如果你只是写一个纯计算、参数完全确定的工具那暂时用不上但了解它的存在对设计工具边界有帮助。需要说明的是用户引导依赖客户端实现引导处理器。如果客户端不支持这个能力调用ctx.elicit()会直接抛错。所以落地之前得先确认你用的客户端比如 Cline MCP是否支持。下面会给出完整的配置和验证步骤。2. TaoToken 前置准备与 FastMCP 环境搭建在开始写引导逻辑之前需要先把 FastMCP 的运行环境和模型接入准备好。FastMCP 本身是一个 Python 库负责 MCP 服务端的工具声明和协议处理而工具内部如果涉及调用大模型比如让模型根据用户输入生成后续问题就需要一个稳定的模型 API 入口。这里我用 TaoToken 作为模型接入层它的 API 地址是https://taotoken.net/api兼容常见的模型调用格式配置起来比较直接。先装 FastMCP。建议用 Python 3.10 以上版本创建一个干净的虚拟环境python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install fastmcp装完之后验证一下版本确保是 2.10.0 或更高python -c import fastmcp; print(fastmcp.__version__)如果输出低于 2.10.0用pip install --upgrade fastmcp升级。用户引导的ctx.elicit()方法在这个版本才稳定可用低版本会报AttributeError。接下来准备 TaoToken 的 API Key。访问https://taotoken.net/api-keys创建一个 Key复制保存。这个 Key 后面会用在两个地方一是 FastMCP 工具内部如果需要调用模型通过环境变量传入二是 Cline MCP 的配置里作为模型服务的认证凭据。把 Key 写到环境变量里避免硬编码export TAOTOKEN_API_KEY你的KeyWindows 下用set TAOTOKEN_API_KEY你的Key或者写到.env文件里用python-dotenv加载。我习惯在项目根目录放一个.env内容如下TAOTOKEN_API_KEYsk-xxxxxxxx TAOTOKEN_BASE_URLhttps://taotoken.net/api然后在代码里用os.getenv读取。这样本地开发和部署时都不用改代码。如果你还没有 Key先去https://taotoken.net/api-keys注册并创建。创建时注意选择对应的权限范围如果只是做工具调用普通读写权限就够了。Key 只显示一次记得当场保存。环境准备好之后创建一个server.py先写一个最小的 FastMCP 服务端确认能跑起来from fastmcp import FastMCP mcp FastMCP(Elicitation Demo) mcp.tool async def ping() - str: 测试服务端是否正常。 return pong if __name__ __main__: mcp.run()运行python server.py如果看到服务端启动日志说明 FastMCP 环境没问题。接下来就可以在这个基础上加用户引导逻辑了。这里要提醒一点FastMCP 服务端本身不依赖 TaoTokenTaoToken 是在工具内部需要调用模型时才用到。如果你的引导逻辑只是收集用户输入、做本地处理那不需要模型 API但如果引导的问题需要模型动态生成或者用户输入后要交给模型处理那就得把 TaoToken 的配置接进来。下面第三节的配置示例会覆盖这两种情况。3. 三个可复制的用户引导配置与工具声明这一节给出三个可以直接复制到项目里的配置片段分别对应标量引导、结构化引导和多轮引导。每个片段都包含完整的工具声明和ctx.elicit()调用路径和参数名保持与 FastMCP 2.x 官方一致。3.1 标量引导配置单字段追问第一个配置适合只需要用户提供一个简单值的场景比如文件名、数量、是否确认。FastMCP 会自动把标量类型包装成 MCP 兼容的对象模式客户端看到的是一个带value字段的对象返回时自动解包。from fastmcp import FastMCP, Context mcp FastMCP(Scalar Elicitation) mcp.tool async def create_file(ctx: Context) - str: 创建文件前询问文件名。 result await ctx.elicit( message请输入要创建的文件名含扩展名, response_typestr ) if result.action accept: filename result.data return f已准备创建文件{filename} elif result.action decline: return 用户拒绝提供文件名 else: return 操作已取消这段代码的关键点是response_typestr。FastMCP 会把它包装成{value: string}的模式发给客户端客户端返回{value: report.txt}FastMCP 再解包成report.txt赋给result.data。你不需要手动处理对象结构。如果要请求整数或布尔值把response_type换成int或bool即可。比如确认操作mcp.tool async def delete_record(ctx: Context, record_id: str) - str: 删除记录前确认。 result await ctx.elicit( messagef确认删除记录 {record_id} 吗, response_typebool ) if result.action accept and result.data is True: return f记录 {record_id} 已删除 return 删除操作未执行注意result.action accept和result.data is True要同时判断。用户可能接受请求但选了false这时候不应该执行删除。3.2 结构化引导配置多字段一次性收集第二个配置适合需要用户一次性提供多个相关字段的场景。用 dataclass 定义响应结构FastMCP 会生成对应的 JSON Schema。MCP 规范只支持浅层对象属性类型限 string、number、integer、boolean 和 enum所以不要嵌套太深。from dataclasses import dataclass from typing import Literal from fastmcp import FastMCP, Context mcp FastMCP(Structured Elicitation) dataclass class TaskDetails: title: str description: str priority: Literal[low, medium, high] due_date: str mcp.tool async def create_task(ctx: Context) - str: 收集任务详情并创建任务。 result await ctx.elicit( message请提供任务详细信息, response_typeTaskDetails ) if result.action accept: task result.data return ( f已创建任务{task.title} f优先级 {task.priority} f截止日期 {task.due_date} ) return 任务创建已取消这里的Literal[low, medium, high]会生成 enum 约束客户端会渲染成下拉选项而不是自由输入。如果你用 Python 枚举写法也类似from enum import Enum class Priority(Enum): LOW low MEDIUM medium HIGH high dataclass class TaskDetails: title: str priority: Priority用枚举时result.data.priority拿到的是枚举成员取值要用.value。用 Literal 时直接是字符串。两种方式都符合 MCP 规范按团队习惯选。3.3 多轮引导配置逐步收集信息第三个配置适合信息量大、需要分步询问的场景。工具可以多次调用ctx.elicit()每次收集一部分信息前一步的结果可以影响后一步的问题。from typing import Literal from fastmcp import FastMCP, Context mcp FastMCP(Multi-turn Elicitation) mcp.tool async def plan_meeting(ctx: Context) - str: 通过多轮引导规划会议。 title_result await ctx.elicit( message会议标题是什么, response_typestr ) if title_result.action ! accept: return 会议规划已取消 duration_result await ctx.elicit( message持续多少分钟, response_typeint ) if duration_result.action ! accept: return 会议规划已取消 priority_result await ctx.elicit( message这个会议紧急吗, response_typeLiteral[yes, no] ) if priority_result.action ! accept: return 会议规划已取消 urgent priority_result.data yes return ( f会议 {title_result.data} 已规划为 f{duration_result.data} 分钟紧急{urgent} )多轮引导的每一步都要检查action任何一步不是accept就提前返回。这样用户在任何一步拒绝或取消整个流程都会干净地终止不会留下半成品状态。如果你需要把这三个配置放到同一个服务端文件里注意mcp实例只能创建一次所有mcp.tool装饰器都挂在同一个实例上。工具名不能重复否则后注册的会覆盖前面的。配置写完之后下一步是在 Cline MCP 里接入这个服务端并触发引导请求验证返回结果。4. 在 Cline MCP 中触发引导并校验返回结果Cline MCP 是 Cline 编辑器的 MCP 客户端配置入口支持通过 JSON 配置连接本地或远程的 MCP 服务端。要让 Cline 能调用我们写的 FastMCP 服务端需要在 Cline 的 MCP 配置里加上服务端启动命令。先确认 Cline 的 MCP 配置文件位置。在 Cline 设置里找到 MCP Servers点击 Configure MCP Servers会打开一个 JSON 文件。路径通常是 Cline 扩展的全局存储目录不同系统位置不同但通过设置界面打开最稳妥。在配置文件里加入我们的服务端{ mcpServers: { elicitation-demo: { command: python, args: [/absolute/path/to/server.py], env: { TAOTOKEN_API_KEY: sk-xxxxxxxx, TAOTOKEN_BASE_URL: https://taotoken.net/api } } } }三个关键字段要写全command是启动命令args是脚本路径env是环境变量。如果你的 FastMCP 服务端需要调用模型TAOTOKEN_API_KEY和TAOTOKEN_BASE_URL必须在这里传入否则工具内部读不到。路径用绝对路径相对路径在 Cline 启动子进程时容易找不到文件。保存配置后Cline 会尝试启动这个 MCP 服务端。如果启动成功在 MCP Servers 列表里会看到elicitation-demo处于 connected 状态。如果显示 failed先看 Cline 的输出日志常见原因是 Python 路径不对或依赖没装。服务端连上之后在 Cline 的对话里触发工具调用。比如输入“帮我创建一个文件”Cline 会识别到create_file工具并调用它。这时候服务端执行到ctx.elicit()会向 Cline 发起一个引导请求。Cline 如果支持引导处理器会在界面上弹出输入框让你填写文件名。填写test.txt并提交服务端收到action: accept和data: test.txt返回“已准备创建文件test.txt”。Cline 把这个结果展示在对话里。整个链路就通了。校验返回结果时重点看三个地方。第一Cline 界面上是否出现了引导输入框这说明客户端实现了引导处理器。第二提交后服务端返回的字符串是否包含你输入的值这说明result.data解包正确。第三如果你点拒绝或取消服务端是否返回对应的提示这说明 action 分支处理正确。对于结构化引导Cline 会渲染一个包含多个字段的表单。填完提交后服务端拿到的result.data是一个TaskDetails实例字段值跟你填的一致。如果某个字段类型不对比如 priority 填了urgent客户端会在提交前做校验因为 schema 里限定了 enum。多轮引导在 Cline 里表现为连续弹出多个输入框每次提交后服务端继续下一步。如果中途点取消后续步骤不再执行服务端直接返回“会议规划已取消”。如果 Cline 没有弹出引导输入框而是直接报错说客户端不支持 elicitation那说明当前 Cline 版本还没实现引导处理器。这时候可以换用支持该能力的客户端或者先用 FastMCP 自带的客户端测试。FastMCP 客户端可以通过Client类连接服务端并注册引导处理器适合在本地做单元测试。验证通过之后你就可以把这个服务端接到实际工作流里了。比如在 Cline 里配置多个 MCP 服务端把需要交互的工具集中到 elicitation-demo 这个服务端其他纯计算工具放到另一个服务端职责更清晰。5. 本篇常见报错与排查对照用户引导落地过程中报错主要集中在客户端不支持、schema 不兼容、action 处理遗漏这几类。下面按真实报错信息对照排查。报错一Elicitation not supported by client这是最常见的报错出现在ctx.elicit()调用时。原因是当前 MCP 客户端没有实现引导处理器。MCP 规范里引导是可选能力客户端可以不支持。排查方法是看客户端文档或版本说明确认是否实现了 elicitation handler。Cline MCP 较新版本支持旧版本可能没有。如果客户端确实不支持要么升级客户端要么改用支持该能力的客户端要么在服务端做降级处理——先检查客户端能力不支持时改用普通参数传递。报错二Invalid schema for elicitation response这个报错说明response_type生成的 schema 不符合 MCP 规范。MCP 只支持浅层对象属性类型限 string、number、integer、boolean 和 enum。如果你用了嵌套 dataclass、列表、字典或者自定义复杂类型就会触发这个错误。排查方法是把response_type简化成扁平结构所有字段都是标量或 enum。比如把List[str]改成用逗号分隔的str把嵌套对象拆成多个平级字段。报错三result.data is None但 action 是 accept这种情况通常出现在response_typeNone的确认场景。当你不请求具体数据、只让用户批准或拒绝时response_type传None用户接受后result.data就是None这是预期行为。如果你在代码里直接访问result.data.some_field就会报AttributeError。排查方法是确认response_typeNone时不要访问 data 的字段只判断 action。如果需要数据就传具体的 response_type。报错四local proxy failed或连接超时这个报错跟引导逻辑无关是 MCP 服务端启动或连接问题。常见原因是 Cline 配置里的command或args路径不对子进程启动失败。排查方法是先在终端手动运行python /absolute/path/to/server.py确认服务端能独立启动。如果手动能跑但 Cline 里报错检查 Cline 配置里的 Python 路径是否是虚拟环境里的那个以及env里的环境变量是否传对。另外如果服务端启动时依赖某些包确保这些包装在 Cline 使用的 Python 环境里。报错五401 Unauthorized调用模型时如果工具内部调用 TaoToken 的模型 API 返回 401说明 API Key 无效或没传进去。排查方法是检查env里的TAOTOKEN_API_KEY是否与https://taotoken.net/api-keys里创建的一致以及代码里是否用os.getenv(TAOTOKEN_API_KEY)正确读取。注意 Key 不要有多余空格环境变量名大小写要一致。如果 Key 没问题检查TAOTOKEN_BASE_URL是否设为https://taotoken.net/api路径不要多加斜杠。报错六reading choices解析失败这个报错出现在客户端渲染 enum 选项时。如果你用Literal或枚举定义了选项但客户端拿到的 schema 里 choices 字段格式不对就会解析失败。排查方法是确认 FastMCP 版本在 2.10.0 以上低版本对 enum 的 schema 生成可能有 bug。另外选项值不要包含特殊字符或中文先用纯英文小写测试确认链路通了再改。报错七OAuth 相关错误如果你在 MCP 服务端配置了 OAuth 认证但客户端没带 token 或 token 过期会报 OAuth 错误。这跟引导功能本身无关但会阻断工具调用。排查方法是先临时关掉认证确认引导逻辑本身没问题再逐步加回认证。如果必须用 OAuth确保客户端配置了正确的 token 获取流程。排查顺序建议从外到内先确认服务端能独立启动再确认客户端能连上然后确认工具能被调用最后确认引导请求能弹出。每一步都通了再处理 action 分支和数据类型。这样定位问题最快。6. 接入与排障资源指引引导功能跑通之后如果你想把模型调用也接进来让工具在引导过程中动态生成问题或处理用户输入可以用 TaoToken 的 API 作为模型入口。API 地址是https://taotoken.net/api兼容常见的调用格式配置方式跟前面env里写的一样。需要创建或管理 API Key 的话访问https://taotoken.net/api-keys。接入文档在https://taotoken.net/doc里面有不同语言和框架的调用示例。如果你在排查引导相关的报错比如客户端不支持、schema 不兼容可以先看接入文档里的客户端能力说明确认当前客户端是否实现了 elicitation handler。对于需要长期跑编码任务或 Agent 工作流的场景可以了解 Coding Plan地址是https://taotoken.net/coding-plan。它适合需要稳定模型调用、频繁交互的工具链跟 FastMCP 的引导能力配合起来能做出比较自然的交互式工具。如果只是想先验证模型返回是否符合预期可以用模型对话页面快速测试地址是https://taotoken.net/chat。把引导请求里要问的问题先在这里试一遍确认模型输出格式稳定再写进工具代码。控制台入口在https://taotoken.net/console可以查看调用量、管理 Key、调整配置。Claude Code 相关的接入指南在https://taotoken.net/claude-code-anthropic如果你用 Claude Code 作为 MCP 客户端可以参考里面的配置方式。最后提醒一点用户引导的验证动作一定要在真实客户端里跑一遍不要只在单元测试里过。因为引导请求的渲染和响应处理是客户端行为不同客户端实现差异较大。Cline MCP 里跑通之后再换其他客户端测试确认兼容性。遇到报错先对照第五节的排查表大部分问题都能定位到具体原因。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进