在 python-sdk 中使用 MCP Prompts 编写用户驱动消息模板的完整指南 在 python-sdk 中使用 MCP Prompts 编写用户驱动消息模板的完整指南【免费下载链接】python-sdkThe official Python SDK for Model Context Protocol servers and clients项目地址: https://gitcode.com/gh_mirrors/pythonsd/python-sdkPrompts 是 MCPModel Context Protocol中一种由人驱动的消息模板与 Tools 面向模型调用不同Prompt 由客户端用户从菜单中主动选择如斜杠命令或按钮填写参数后渲染成消息并注入对话。本文以 python-sdk 官方文档为主线结合 Prompts 模块源码 与 文档配套测试系统讲解mcp.prompt()的声明、渲染、参数校验、多消息模板、文档/图片附件以及运行时动态增删 Prompt 的完整实战方案读完即可在自己的 MCP 服务器中落地可用的 Prompt 功能。Prompt 的本质给人用的模板不是给模型用的工具在 MCP 中Tools 是给模型调用的能力接口Prompt 恰好相反它是一份消息模板由用户在客户端里挑选通常是菜单里的斜杠命令或按钮填入参数渲染出的消息就像用户自己亲手敲进对话一样。你只需在一个返回文本的函数上加上mcp.prompt()装饰器即可声明一个 Promptfrom mcp.server import MCPServer mcp MCPServer(Code Helper) mcp.prompt() def review_code(code: str) - str: Review a piece of code. return fPlease review this code:\n\n{code}代码对应 docs_src/prompts/tutorial001.py。SDK 会像解析 Tool 一样从函数中提取三样信息名称函数名即review_code描述客户端展示的说明取自 docstring即Review a piece of code.参数来自函数参数。code没有默认值因此是必需参数。客户端调用prompts/list拿到的就是这份清单{ name: review_code, description: Review a piece of code., arguments: [ {name: code, required: true} ] }注意这里没有 JSON SchemaPrompt 参数是一份扁平的具名字符串值列表——它是一张给人填写的表单而不是由模型组装的载荷。从源码看Prompt.from_function通过func_metadata提取函数参数模型后逐个转换为PromptArgument(name, description, required)见 base.pyrequired直接取决于参数是否有默认值。渲染流程prompts/get 与单条 user 消息客户端通过prompts/get传入参数来渲染模板你的函数被调用返回的str会成为一条 user 消息{ description: Review a piece of code., messages: [ { role: user, content: { type: text, text: Please review this code:\n\ndef add(a, b): return a b } } ], resultType: complete }这就是一个 Prompt 的完整生命周期按名字被列出 → 按需渲染 → 被丢进对话。在渲染层Prompt.render()base.py先校验必需参数再把结果规范化为Message列表同步函数通过anyio.to_thread.run_sync执行异步函数直接await因此同步/异步返回皆可。必需参数缺失整个请求直接失败required在函数运行之前就会被强制校验。如果调用prompts/get渲染review_code却不传code请求本身就会以 JSON-RPC 错误code-32603失败mcp.shared.exceptions.MCPError: Internal server error这里不存在 Tool 那种把错误结果交还给模型的机制因为整个调用链里根本没有模型参与调用直接抛出异常具体原因Missing required arguments: {code}会写进你服务器的日志。源码中校验逻辑为missing required - provided一旦缺失即raise ValueError(...)随后被包装成通用异常base.py文档配套测试 test_prompts.py 精确断言了MCPError的 code 为-32603、message 为Internal server error与文档引用的输出逐字一致。动手试试MCP Inspector启动服务器并打开 MCP Inspectoruv run mcp dev server.py打开Prompts标签页并选择review_codeInspector 会画出一张带一个必填字段code的表单。填入内容并渲染返回的就是上面那条 user 消息。多消息模板一条 Prompt 撑起整场对话一次代码评审只是一条消息而一次调试会话是一段对话——Prompt 完全可以播种整段对话。返回消息列表而非str即可from mcp.server import MCPServer from mcp.server.mcpserver.prompts.base import AssistantMessage, Message, UserMessage mcp MCPServer(Code Helper) mcp.prompt() def review_code(code: str) - str: Review a piece of code. return fPlease review this code:\n\n{code} mcp.prompt() def debug_error(error: str) - list[Message]: Start a debugging conversation. return [ UserMessage(Im seeing this error:), UserMessage(error), AssistantMessage(Ill help debug that. What have you tried so far?), ]代码对应 docs_src/prompts/tutorial002.py。关键点UserMessage和AssistantMessage来自mcp.server.mcpserver.prompts.base。传入str后它们会替你把它包装成TextContent角色就是类名Message是两者的公共基类用作返回值注解。渲染debug_error会按顺序产出三条消息{ description: Start a debugging conversation., messages: [ {role: user, content: {type: text, text: Im seeing this error:}}, {role: user, content: {type: text, text: TypeError: int object is not iterable}}, { role: assistant, content: {type: text, text: Ill help debug that. What have you tried so far?} } ], resultType: complete }注意最后一条预置一条 assistant 消息是引导模型下一句回答方向的手段——用户不必自己输入引导语。实现上Message.__init__会在内容为str时自动包装TextContent并对Image/Audio助手做相应转换base.py测试 test_prompts.py 验证了角色与顺序的完整保留。标题与参数描述让客户端画出更好的表单review_code是函数名不是给人看的标签。为按钮提供更好的文案并为每个参数写描述让表单自我解释from typing import Annotated from pydantic import Field from mcp.server import MCPServer mcp MCPServer(Code Helper) mcp.prompt(titleCode review) def review_code( code: Annotated[str, Field(descriptionThe code to review.)], language: Annotated[str, Field(descriptionThe language the code is written in.)] python, ) - str: Review a piece of code. return fPlease review this {language} code:\n\n{code}代码对应 docs_src/prompts/tutorial003.py。要点titleCode review是给人读的名称语义与 Tool 的title完全一致Annotated[str, Field(description...)]与 Tools 教程 描述 Tool 参数的模式相同区别只是描述落在了参数上而不是 Schema 里language有默认值因此不再是必填。此时prompts/list条目已经包含客户端画好表单所需的全部信息{ name: review_code, title: Code review, description: Review a piece of code., arguments: [ {name: code, description: The code to review., required: true}, {name: language, description: The language the code is written in., required: false} ] }如果你已读过 Tools 教程到这里会发现一切都很熟悉同一个装饰器、同一个docstring 即描述、同一个Annotated/Field。唯一变化的只有两点——谁触发它人和结果去哪对话。配套测试 test_prompts.py 分别断言了 title/description 落地与默认值生效。不止文本嵌入文档与附加图片UserMessage和AssistantMessage在能接收str的位置同样可以接收一个 content block或Image/Audio助手。Prompt 场景下最常见的两种用法是附带一份文档和附带一张图片。嵌入一份文件EmbeddedResourcefrom pathlib import Path from mcp.server import MCPServer from mcp.server.mcpserver import Message, UserMessage from mcp.types import EmbeddedResource, TextResourceContents mcp MCPServer(Code Helper) STYLE_GUIDE_FILE Path(__file__).parent / style-guide.md # or the path to your file on disk mcp.resource(style://python, mime_typetext/markdown) def style_guide() - str: The teams Python style guide. return STYLE_GUIDE_FILE.read_text(encodingutf-8) mcp.prompt() def review_code(code: str) - list[Message]: Review a piece of code against the team style guide. guide TextResourceContents(uristyle://python, mime_typetext/markdown, textstyle_guide()) return [ UserMessage(EmbeddedResource(resourceguide)), UserMessage(fReview this code against the style guide above:\n\n{code}), ]代码对应 docs_src/prompts/tutorial004.py。要点风格指南是style://python下的一个资源资源体系见 Resources 教程从server.py旁的style-guide.md读取——你可以在那里放任意 Markdown 文件EmbeddedResource(resourceTextResourceContents(...))两者都来自mcp.types把文件连同 URI 和 MIME 类型作为第一条消息携带引用它的指令随后以纯文本形式出现嵌入而非把指南拼进 f-string的好处客户端可以把文件显示为附件、稍后重新打开style://python模型拿到的也是未经改动的原文。对于二进制文件改用带 base64blob的BlobResourceContents。渲染后第一条消息的content是一个resource块{type: resource, resource: {uri: style://python, mimeType: text/markdown, text: * Prefer early returns.\n...}}附加一张图片Image 助手from pathlib import Path from mcp.server import MCPServer from mcp.server.mcpserver import Image, Message, UserMessage mcp MCPServer(Code Helper) DIAGRAM_FILE Path(__file__).parent / architecture.png # or the path to your file on disk mcp.prompt() def explain_component(component: str) - list[Message]: Explain one component using the architecture diagram. return [ UserMessage(Image(pathDIAGRAM_FILE)), UserMessage(fWhere does {component} sit in this architecture, and what does it talk to?), ]代码对应 docs_src/prompts/tutorial005.py。要点Image是 图片、音频与图标教程 介绍的助手。渲染 Prompt 时UserMessage会把它转换为ImageContent块文件做 base64 编码MIME 类型从.png推断Audio以同样方式变成AudioContent。底层实现见 types.py 的to_image_content在server.py旁放任意名为architecture.png的 PNG 即可。因为 Prompt 参数都是字符串图片永远来自服务器端component只提供文字描述。渲染结果中的image块{type: image, data: iVBORw0KGgoAAAANSUhEUg..., mimeType: image/png}运行时动态增删让用户把指令存成菜单项Prompt 可以在客户端已连接的情况下被添加——例如允许用户把自己的一条指令保存成专属菜单项。做法是注册 Prompt然后发送变更通知。from contextlib import suppress from mcp.server import MCPServer from mcp.server.mcpserver import Context from mcp.server.mcpserver.prompts import Prompt mcp MCPServer(Code Helper) mcp.prompt() def review_code(code: str) - str: Review a piece of code. return fPlease review this code:\n\n{code} mcp.tool() async def save_template(name: str, instruction: str, ctx: Context) - str: Save an instruction as a prompt the user can pick from the menu. def template(code: str) - str: return f{instruction}\n\n{code} with suppress(ValueError): # replace an existing entry of the same name mcp.remove_prompt(name) mcp.add_prompt(Prompt.from_function(template, namename, descriptioninstruction)) await ctx.notify_prompts_changed() await ctx.session.send_prompt_list_changed() return fSaved {name} to the prompt menu.代码对应 docs_src/prompts/tutorial006.py。逐层拆解mcp.add_prompt(Prompt.from_function(fn, name..., description...))与mcp.prompt()的注册路径完全一致mcp.remove_prompt(name)是其逆操作。注意add_prompt遇到同名条目会保留旧条目而不是覆盖见 manager.py所以上面的工具先remove_prompt再add_prompt从而把保存实现成替换。注册后prompts/list会立即反映变更await ctx.notify_prompts_changed()向订阅了subscriptions/listen流的每个2026-07-28协议客户端发送notifications/prompts/list_changed见 Subscriptions 教程实现上它向内部事件总线发布PromptsListChanged事件context.pyawait ctx.session.send_prompt_list_changed()则向调用方客户端发送同样的通知——适用于协议版本早于 2026 的旧客户端见 Legacy Clients 教程。两个都调用即可没有任何需要通知的对象时各自静默无事收到通知的客户端会重新调用prompts/list。在 PythonClient端对应的写法是async with client.listen(prompts_list_changedTrue) as sub:会产出PromptsListChanged事件相关实现见 client.py 与 subscriptions.py。总结在函数上加mcp.prompt()即成为一个 Prompt名称取自函数名描述取自 docstringPrompt 是由人驱动的客户端列出它们用户挑选并填写参数参数是扁平的具名字符串列表无 Schema带默认值的参数即为可选参数返回str会变成一条 user 消息返回UserMessage/AssistantMessage列表可以播种一段多轮对话title与Field(description...)是客户端 UI 展示的内容来源缺少必需参数会使整个请求失败——Prompt 没有逐条的错误结果机制把EmbeddedResource或Image包进UserMessage即可附带文档或图片运行时可用mcp.add_prompt(...)/mcp.remove_prompt(...)增删 Prompt随后调用await ctx.notify_prompts_changed()与await ctx.session.send_prompt_list_changed()广播变更。Prompt或资源模板参数的服务端自动补全属于另一块能力详见 Completions 教程。所有配套可运行示例均可从 docs_src/prompts 目录获得相关行为验证测试集中在 tests/docs_src/test_prompts.py。【免费下载链接】python-sdkThe official Python SDK for Model Context Protocol servers and clients项目地址: https://gitcode.com/gh_mirrors/pythonsd/python-sdk创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考