提示词
提示词是由用户挑选的消息模板。
工具是给模型用的。提示词正好相反:用户在客户端的菜单里(比如斜杠命令或按钮)选一个,填好参数,渲染出来的消息就进入对话,就像是用户自己打出来的一样。
在一个返回文本的函数上加 @mcp.prompt(),就声明了一个提示词。
第一个提示词
from mcp.server import MCPServer
mcp = MCPServer("Code Helper")
@mcp.prompt()
def review_code(code: str) -> str:
"""Review a piece of code."""
return f"Please review this code:\n\n{code}"
SDK 从中读取的三样东西和工具一样:
- 名称就是函数名:
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 Schema。提示词的参数是一个扁平的具名字符串值列表:是给人填的表单,而不是由模型构造的载荷。
渲染
客户端用 prompts/get 渲染模板,并传入参数。你的函数运行后,返回的 str 会变成一条用户消息:
{
"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"
}
提示词的完整流程就是这样:按名称列出,按需渲染,放进对话。
Check
required 的检查发生在你的函数运行之前。渲染 review_code 时不传 code,请求本身就会失败,并返回一个 JSON-RPC 错误(错误码 -32603):
mcp.shared.exceptions.MCPError: Internal server error
这里没有工具那种可以交回给模型的错误结果,因为整个环节里根本没有模型:调用会直接抛出异常。原因(Missing required arguments: {'code'})会记在服务器的日志里。
试一试
用 MCP Inspector 运行服务器:
uv run mcp dev server.py
打开 Prompts 标签页,选择 review_code。Inspector 会画出一个表单,带一个必填的 code 字段。填好、渲染,返回的正是上面那条用户消息。
不止一条消息
代码审查只要一条消息。调试则是一段对话,而提示词可以把整段对话的开头都铺好。
把返回值从 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 f"Please review this code:\n\n{code}"
@mcp.prompt()
def debug_error(error: str) -> list[Message]:
"""Start a debugging conversation."""
return [
UserMessage("I'm seeing this error:"),
UserMessage(error),
AssistantMessage("I'll help debug that. What have you tried so far?"),
]
UserMessage和AssistantMessage来自mcp.server.mcpserver.prompts.base。给它们一个str,它们会替你包装成TextContent。角色由类名决定。Message是它们的公共基类。用它作返回值注解。
现在渲染 debug_error 会按顺序产生三条消息:
{
"description": "Start a debugging conversation.",
"messages": [
{"role": "user", "content": {"type": "text", "text": "I'm seeing this error:"}},
{"role": "user", "content": {"type": "text", "text": "TypeError: 'int' object is not iterable"}},
{
"role": "assistant",
"content": {"type": "text", "text": "I'll help debug that. What have you tried so far?"}
}
],
"resultType": "complete"
}
注意最后一条。预先填入一轮 assistant 发言,就能引导模型的下一条回复,而不用让用户自己把引导的话敲出来。
标题和参数描述
review_code 是函数名,不是标签。给客户端一个更适合放在按钮上的名字,并给每个参数加上描述,让表单一目了然:
from typing import Annotated
from pydantic import Field
from mcp.server import MCPServer
mcp = MCPServer("Code Helper")
@mcp.prompt(title="Code review")
def review_code(
code: Annotated[str, Field(description="The code to review.")],
language: Annotated[str, Field(description="The language the code is written in.")] = "python",
) -> str:
"""Review a piece of code."""
return f"Please review this {language} code:\n\n{code}"
title="Code review"是给人看的名称,和工具的title一模一样。Annotated[str, Field(description=...)]和 工具 用来描述工具参数的是同一种写法。这里描述直接落在参数上,而不是写进模式里。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}
]
}
Info
如果读过 工具,这一页的内容你其实都已经会了。装饰器一样,用 docstring 作描述一样,Annotated/Field 也一样。变的只有两点:由谁触发(用户),以及结果去哪儿(进入对话)。
回顾
- 在函数上加
@mcp.prompt(),它就成了提示词。名称取自函数名,描述取自 docstring。 - 提示词由用户控制:客户端列出它们,用户选一个并填好参数。
- 参数是一个扁平的具名字符串列表(没有模式)。有默认值的形参是可选的。
- 返回
str,它就变成一条用户消息。返回UserMessage/AssistantMessage的列表,可以为多轮对话铺好开头。 title=和Field(description=...)是客户端放进 UI 里的内容。- 缺少必填参数会让整个请求失败。没有针对单个提示词的错误结果。
要在服务器端为提示词(或资源模板)的参数提供自动补全,见 补全。