1. 参数补全(Completions) #
本章主要介绍 MCP 的“参数补全(Completions)”机制,包括服务端如何为 Prompt 参数和资源模板参数提供智能补全建议,以及客户端如何请求补全并展示结果。你将学会:
- 如何在服务端实现支持参数补全的 Prompt 和资源模板;
- 如何编写补全处理器,动态返回参数候选项;
- 客户端如何发起补全请求,并获取和展示补全结果。
2. 服务器:支持参数补全的 Prompt 与资源模板 #
新建 C:\mcp-project\completions_server.py:
# 导入 FastMCP 主类
from mcp.server.fastmcp import FastMCP
# 导入类型定义
from mcp.types import (
Completion, # 补全结果
CompletionArgument, # 补全参数
CompletionContext, # 补全上下文
PromptReference, # 提示参考
ResourceTemplateReference, # 资源模板参考
)
# 创建 FastMCP 实例,命名为 "Completions Server"
mcp = FastMCP(name="Completions Server") # 服务器名称
# 定义一个 Prompt,描述为“代码风格指导”,其参数支持补全
@mcp.prompt(description="代码风格指导")
def code_style_guide(language: str, style: str) -> str:
# 根据编程语言和风格偏好生成代码指导
return f"请为 {language} 语言提供 {style} 风格的代码指导。"
# 定义一个资源模板,格式为 github://{owner}/{repo},其参数支持补全
@mcp.resource("github://{owner}/{repo}")
def github_repo_info(owner: str, repo: str) -> str:
# 获取 GitHub 仓库信息(示例资源)
return f"GitHub 仓库 {owner}/{repo} 的信息:这是一个示例仓库。"
# 使用 @mcp.completion() 装饰器注册补全处理器
@mcp.completion()
async def handle_completion(
ref: PromptReference | ResourceTemplateReference, # 提示或资源模板参考
argument: CompletionArgument, # 补全参数
context: CompletionContext | None, # 补全上下文
) -> Completion | None:
# 为 prompts 和 resources 提供补全建议
# 如果是 Prompt 参数补全
if isinstance(ref, PromptReference):
# 针对 code_style_guide Prompt
if ref.name == "code_style_guide":
# 如果补全参数为 language
if argument.name == "language":
# 预设编程语言列表
languages = ["Python", "JavaScript", "Java", "C++", "Go", "Rust"]
# 根据输入值过滤匹配的语言
filtered_languages = [
lang
for lang in languages
if lang.lower().startswith(argument.value.lower())
]
# 返回补全结果
return Completion(
values=filtered_languages,
hasMore=False,
)
# 如果补全参数为 style
elif argument.name == "style":
# 预设代码风格列表
styles = [
"clean",
"functional",
"object-oriented",
"procedural",
"declarative",
]
# 根据输入值过滤匹配的风格
filtered_styles = [
style
for style in styles
if style.lower().startswith(argument.value.lower())
]
# 返回补全结果
return Completion(
values=filtered_styles,
hasMore=False,
)
# 如果是资源模板参数补全
if isinstance(ref, ResourceTemplateReference):
# 针对 github://{owner}/{repo} 资源模板
if ref.uri == "github://{owner}/{repo}":
# 如果补全参数为 owner
if argument.name == "owner":
# 预设 GitHub 用户名列表
owners = [
"microsoft",
"google",
"facebook",
"netflix",
"modelcontextprotocol",
]
# 根据输入值过滤匹配的用户名
filtered_owners = [
owner
for owner in owners
if owner.lower().startswith(argument.value.lower())
]
# 返回补全结果
return Completion(
values=filtered_owners,
hasMore=False,
)
# 如果补全参数为 repo
elif argument.name == "repo":
# 仓库名补全,需考虑 owner 上下文
if context and context.arguments and context.arguments.get("owner"):
owner = context.arguments.get("owner")
# 针对不同 owner 提供不同仓库名列表
if owner == "microsoft":
repos = ["vscode", "typescript", "dotnet", "azure-sdk"]
elif owner == "google":
repos = ["tensorflow", "kubernetes", "go", "chromium"]
elif owner == "modelcontextprotocol":
repos = ["python-sdk", "servers", "specification", "website"]
else:
repos = ["main", "master", "develop", "feature"]
# 根据输入值过滤匹配的仓库名
filtered_repos = [
repo
for repo in repos
if repo.lower().startswith(argument.value.lower())
]
# 返回补全结果
return Completion(
values=filtered_repos,
hasMore=False,
)
# 如果没有匹配的补全规则,返回 None
return None
# 主入口:以 stdio 方式运行服务器
if __name__ == "__main__":
mcp.run(transport="stdio")
说明:
- 补全回调函数接收参数名、当前值、上下文参数,返回建议列表。
- 上下文参数可用于提供相关补全(如根据 owner 补全 repo)。
- 实际实现中,你可能需要扩展 FastMCP 或使用低层 Server 来完全支持补全。
3. 客户端:请求并展示补全结果 #
新建 C:\mcp-project\test_client_completions.py:
# 连接到 completions_server.py,演示如何请求参数补全
# 导入 asyncio,用于运行异步逻辑
import asyncio
# 导入 os,用于计算服务器脚本路径
import os
# 从 mcp 包导入 ClientSession、StdioServerParameters 和 types,用于会话、stdio 参数与类型定义
from mcp import ClientSession, StdioServerParameters, types
# 从 mcp.client.stdio 导入 stdio_client,作为 stdio 客户端工厂
from mcp.client.stdio import stdio_client
# 定义主异步函数
async def main() -> None:
# 计算当前文件的绝对路径所在目录
base_dir = os.path.dirname(os.path.abspath(__file__))
# 拼接得到 completions_server.py 的绝对路径
server_path = os.path.join(base_dir, "completions_server.py")
# 配置以 stdio 方式启动服务器
server_params = StdioServerParameters(
command="python", # 使用 python 命令启动
args=[server_path], # 启动参数为服务器脚本路径
env={}, # 环境变量为空
)
# 建立 stdio 连接并创建客户端会话
async with stdio_client(server_params) as (read, write):
# 使用 read 和 write 创建 ClientSession
async with ClientSession(read, write) as session:
# 初始化会话
await session.initialize()
# 列出所有 Prompt
prompts = await session.list_prompts()
# 打印所有 Prompt 的名称
print("[Prompts]", [p.name for p in prompts.prompts])
# 列出所有资源模板
templates = await session.list_resource_templates()
# 打印所有资源模板的 uriTemplate
print(
"[ResourceTemplates]",
[t.uriTemplate for t in templates.resourceTemplates],
)
# 如果存在 Prompt,则进行参数补全演示
if prompts.prompts:
# 取第一个 Prompt 的名称
prompt_name = prompts.prompts[0].name
# 打印 Prompt 补全示例标题
print(f"\n=== Prompt 补全示例:{prompt_name} ===")
# 打印当前 Prompt 名称
print(f"prompt_name: {prompt_name}")
# 请求 language 参数的补全,已输入 "py"
language_completion = await session.complete(
ref=types.PromptReference(type="ref/prompt", name=prompt_name),
argument={"name": "language", "value": "py"},
)
# 打印 language 参数的补全结果
print(f"language_completion: {language_completion}")
# 打印 language='py' 的补全建议
print(
f"language='py' 的补全建议:{language_completion.completion.values}"
)
# 请求 style 参数的补全,已输入 "func"
style_completion = await session.complete(
ref=types.PromptReference(type="ref/prompt", name=prompt_name),
argument={"name": "style", "value": "func"},
)
# 打印 style='func' 的补全建议
print(f"style='func' 的补全建议:{style_completion.completion.values}")
# 如果存在资源模板,则进行资源模板参数补全演示
if templates.resourceTemplates:
# 取第一个资源模板
template = templates.resourceTemplates[0]
# 打印资源模板补全示例标题
print(f"\n=== 资源模板补全示例:{template.uriTemplate} ===")
# 请求 owner 参数的补全,已输入 "mod"
owner_completion = await session.complete(
ref=types.ResourceTemplateReference(
type="ref/resource", uri=template.uriTemplate
),
argument={"name": "owner", "value": "mod"},
)
# 打印 owner='mod' 的补全建议
print(f"owner='mod' 的补全建议:{owner_completion.completion.values}")
# 请求 repo 参数的补全,已输入 "py",并提供 owner 上下文
repo_completion = await session.complete(
ref=types.ResourceTemplateReference(
type="ref/resource", uri=template.uriTemplate
),
argument={"name": "repo", "value": "py"},
context_arguments={"owner": "modelcontextprotocol"},
)
# 打印 repo='py' 的补全建议(owner=modelcontextprotocol)
print(
f"repo='py' 的补全建议(owner=modelcontextprotocol):{repo_completion.completion.values}"
)
# 判断是否为主程序入口
if __name__ == "__main__":
# 运行主异步函数
asyncio.run(main())说明:
session.complete()用于请求参数补全。PromptReference引用 Prompt,ResourceTemplateReference引用资源模板。context_arguments提供上下文信息,影响补全结果。
4. 运行与验证 #
cd C:\mcp-project
call .venv\Scripts\activate
python test_client_completions.py预期输出:
[Prompts] ['code_style_guide']
[ResourceTemplates] ['github://{owner}/{repo}']
=== Prompt 补全示例:code_style_guide ===
prompt_name: code_style_guide
language_completion: meta=None completion=Completion(values=['Python'], total=None, hasMore=False)
language='py' 的补全建议:['Python']
style='func' 的补全建议:['functional']
=== 资源模板补全示例:github://{owner}/{repo} ===
owner='mod' 的补全建议:['modelcontextprotocol']
repo='py' 的补全建议(owner=modelcontextprotocol):['python-sdk']