5.4 补全:为参数提供自动建议


文档摘要

5.4 补全:为参数提供自动建议 本节摘要:本节讲三大原语之外的一个「体验增强」能力——补全(Completions)。资源模板与提示词的参数经常有固定候选值(项目名、语言、环境名),用户填这些参数时,如果宿主能弹出自动建议,体验会好很多。补全就是干这个的——你注册一个补全处理器,告诉 SDK「当用户在填这个参数时,这些建议值得推荐」。我们会讲清它的注册方式、它为何需要单独的能力声明( ),以及它与资源/提示词的协作。读完本章,你能让你的服务端在宿主 UI 里获得自动建议能力。 一、补全解决什么问题 先看一个体验痛点。假设你有个资源模板 ,用户在宿主 UI 里要填 : 补全就是把「这个参数可能填什么」提前告诉宿主,让宿主在用户输入时弹出建议。

5.4 补全:为参数提供自动建议

本节摘要:本节讲三大原语之外的一个「体验增强」能力——补全(Completions)。资源模板与提示词的参数经常有固定候选值(项目名、语言、环境名),用户填这些参数时,如果宿主能弹出自动建议,体验会好很多。补全就是干这个的——你注册一个补全处理器,告诉 SDK「当用户在填这个参数时,这些建议值得推荐」。我们会讲清它的注册方式、它为何需要单独的能力声明(completions),以及它与资源/提示词的协作。读完本章,你能让你的服务端在宿主 UI 里获得自动建议能力。

一、补全解决什么问题

先看一个体验痛点。假设你有个资源模板 db://users/{user_id},用户在宿主 UI 里要填 user_id:

没有补全: 用户填 user_id: _____ → 用户要记住 ID 是什么(u123?user_123?123?) → 容易填错,体验差 有补全: 用户填 user_id: u_| (光标停在这) → 宿主弹建议:[u123, u124, u125] → 用户选一个,准确填入

补全就是把「这个参数可能填什么」提前告诉宿主,让宿主在用户输入时弹出建议。这对参数有固定候选值(项目名、语言、环境、用户 ID)的场景特别有用。

二、补全的注册

补全处理器用 @mcp.completion() 注册(注意不是 @mcp.tool),它针对某个资源模板或提示词的某个参数提供候选:

@mcp.resource("db://users/{user_id}") def get_user(user_id: str) -> dict: """Get user by ID.""" ... # 为上面这个模板的 user_id 参数提供补全 @mcp.completion("db://users/{user_id}", "user_id") def complete_user_id(prefix: str) -> list[str]: """Suggest user IDs matching the prefix.""" all_ids = list_all_user_ids() # 查你的数据源 return [uid for uid in all_ids if uid.startswith(prefix)][:20]

注册的要素:

  • 目标:为哪个资源模板/提示词的哪个参数补全(前两个参数)
  • 处理器:函数,接 prefix(用户已输入的前缀),返回候选列表

处理器逻辑很简单:根据前缀,从你的数据源筛出匹配的候选值。SDK 把这个候选列表返回给宿主,宿主据此弹出建议。

三、补全如何与宿主协作

补全的完整流程涉及客户端(宿主侧):

用户在宿主 UI 填参数 user_id,输入了 "u1" │ ▼ 宿主发 completions/complete 请求 请求内容: {ref: "db://users/{user_id}", argument: "user_id", prefix: "u1"} │ ▼ 客户端转发到服务端 服务端找到 complete_user_id 处理器 调用 complete_user_id("u1") │ ▼ 处理器返回候选 ["u123", "u124", "u125"] │ ▼ 候选回传宿主 宿主在 UI 弹出建议:[u123, u124, u125] │ ▼ 用户选 u123 参数填入 user_id=u123

注意补全是按需触发的——用户每输入一个字符,宿主可能就请求一次补全。所以处理器要(快速查数据源)、限量(返回前 N 个,别全返回)。

四、补全为何需要单独的能力声明

回顾第 3.3 节的能力声明规则——completions 能力只有当你注册了补全处理器才声明。这一点与其他原语不同:

能力 声明条件
tools 注册任意工具
resources 注册任意资源
prompts 注册任意提示词
completions 注册补全处理器才声明

为什么补全要单独声明?因为不是所有服务端都支持补全——它是个可选的体验增强能力。如果服务端没注册补全处理器,客户端就不该问「补全建议」,否则浪费往返。所以协议设计成「有补全处理器才声明 completions 能力,客户端看到声明才发补全请求」。

第 1.3 节我们看到 server_capabilities 字典里没有 completions,正是因为那个服务端没注册补全处理器。如果你加了 @mcp.completion(...),字典里就会出现 completions

五、补全处理器的最佳实践

写补全处理器时,几个要点:

要点 说明
响应要快 用户每输一个字符可能触发一次,慢了 UI 会卡
限量返回 返回前 N 个(如 20),别全返回,UI 装不下
按前缀筛选 只返回以 prefix 开头的,提高相关性
数据源要实时 用户可能刚加了新项目,补全要反映最新数据
@mcp.completion("project://{name}", "name") def complete_project(prefix: str) -> list[str]: """Suggest project names.""" # 快:用索引或缓存,别全表扫描 # 限:取前 20 个 # 筛:按前缀 return [p for p in get_project_index() if p.startswith(prefix)][:20]

⚠️ 注意:补全处理器不要做昂贵操作(如全表扫描、调慢速 API)。它是「用户输入时实时触发」的,慢了直接拖累宿主 UI 体验。如果数据源本身慢,考虑维护一个内存索引或缓存。

六、补全的典型应用场景

哪些参数适合做补全?有固定或可枚举候选值的:

参数类型 例子 补全来源
项目名 my-appbackend 项目列表
语言 pythonjavascript 支持的语言列表
环境名 devstagingprod 环境列表
用户 ID u123u124 用户表
文件路径 src/...docs/... 文件系统

不适合做补全的:自由文本(如「搜索关键词」「问题描述」)——这些没有固定候选,补全没意义。

💡 技巧:补全的价值在于「用户不知道该填什么时给提示」。如果一个参数的候选值用户都能记住(如 true / false),补全意义不大;如果候选值很多或用户记不住(如用户 ID、项目名),补全就很有用。

七、补全与资源/提示词的协作

补全不是孤立的,它与资源模板、提示词协作:

资源模板:db://users/{user_id} │ ├── 读取:resources/read db://users/u123 │ └── 补全:completions/complete (填 user_id 时弹建议) └── complete_user_id("u1") → [u123, u124, ...] 提示词:translate(text, target_lang) │ ├── 获取:prompts/get translate │ └── 补全:completions/complete (填 target_lang 时弹建议) └── complete_lang("py") → [python, polish]

补全让资源模板与提示词的填参数体验大幅提升——用户不用记住 ID、语言名,系统会提示。这是把「能用」提升到「好用」的关键细节。

本节要点回顾

  1. 补全是体验增强能力,为资源模板/提示词参数提供自动建议。
  2. 注册用 @mcp.completion(目标, 参数名),处理器接前缀、返回候选列表。
  3. 补全按需触发,用户每输一个字符可能请求一次,处理器要快、要限量。
  4. completions 能力只有注册补全处理器才声明,客户端看到声明才发请求。
  5. 处理器最佳实践:快、限量、按前缀筛、数据源实时。
  6. 适合补全的参数:有固定或可枚举候选值(项目名、语言、ID);自由文本不适合。
  7. 补全让资源/提示词的填参数体验提升,从「能用」到「好用」。

第 5 章结束。你已经掌握了三大原语(工具/资源/提示词)与补全。第 6 章我们深入「一次请求的内部」——上下文、依赖与生命周期。


发布者: 作者: 灏天文库 转发
评论区 (0)
U