在 MCP 逐渐成为 AI 应用与外部系统之间通用接口的背景下,Prompt 不再只是“写一段提示词”这么简单,而是服务端可以注册、客户端可以发现、还能按参数动态生成内容的能力。下面结合 Go 官方 SDK 的类型设计与示例代码,拆开讲清楚 Prompt 到底解决什么问题、该怎么实现,以及在动态更新和安全控制上应该注意哪些边界。
MCP 里的 Prompt,到底和普通提示词有什么区别
在 Anthropic 推出的 MCP(Model Context Protocol)里,常见的三个核心概念分别是:
- Prompt:定义可复用的提示模板。
- Tool:扩展 AI 的执行能力。
- Resource:提供数据访问接口。
其中,Prompt 更适合处理“把某一类任务封装成统一模板”这件事。它的作用不是单纯存一段文案,而是让服务端把提示结构标准化,客户端再通过参数填充,生成实际要发给模型的消息内容。
从使用价值看,MCP Prompt 主要有四个特点:
- 标准化:模板定义格式统一,便于不同客户端接入。
- 可复用:同一份模板可以多次调用,减少重复编写。
- 参数化:支持动态参数,能适配不同任务输入。
- 可发现:客户端可以直接发现服务器已经提供的 Prompt。
这里还需要分清一个常见误区:MCP Prompt 并不等于系统 Prompt。
- 系统 Prompt 更像对模型身份和行为的持续约束,例如“你是一个专业的代码审查专家”,它会贯穿整个对话过程。
- MCP Prompt 则是服务端定义的任务模板机制,用来按需生成具体提示内容,支持参数化、支持动态返回消息,也可以在内部包含系统 Prompt 风格的内容。
简单说,系统 Prompt 更接近“AI 是谁”,而 MCP Prompt 更接近“这次要它完成什么任务,以及任务模板长什么样”。
用 Go SDK 实现 Prompt,先看清三个核心类型
如果使用官方 Go SDK github.com/modelcontextprotocol/go-sdk/mcp,实现 Prompt 时最先要理解的是 Prompt 定义、参数定义和处理函数三部分。

1. Prompt 结构体:描述模板本身
Prompt 负责定义一个模板的元信息:

type Prompt struct {
Name string // 唯一标识符
Title string // 显示标题
Description string // 功能描述
Arguments []*PromptArgument // 参数列表
}
这里面最关键的是 Name。它是模板的唯一标识,客户端后续调用时依赖的也是这个名字。Title 和 Description 则更偏向展示和说明,帮助客户端或模型理解这个模板是干什么的。Arguments 定义了模板允许接收哪些参数。
2. PromptArgument:把模板做成可配置接口
模板是否能真正复用,取决于参数设计是否合理。SDK 中对应的结构是:

type PromptArgument struct {
Name string // 参数名称
Description string // 参数描述
Required bool // 是否必填
}
一个 Prompt 可以有多个参数。通过把任务中的可变部分抽成参数,就能把一类操作做成稳定模板,例如代码审查里的“代码内容”“编程语言”,技术问答里的“问题”“领域”等。
3. PromptHandler:真正决定返回什么消息
当客户端请求某个 Prompt 的具体内容时,会进入处理函数:
type PromptHandler func(context.Context, *GetPromptRequest) (*GetPromptResult, error)
也就是说,Prompt 只负责声明“我是什么模板、接受哪些参数”,而 PromptHandler 才负责根据请求参数,生成最终返回给客户端的消息内容。实际项目里,很多逻辑都会放在这里,比如参数兜底、权限判断、上下文拼装,甚至根据用户身份切换不同模板结果。
实战示例:做一个代码审查 Prompt
理解完基础类型后,可以直接看一个更接近实际使用场景的例子。这里的目标是实现一个代码审查模板:客户端传入代码内容,服务端返回一段结构化的代码审查提示。
先创建基础 MCP Server
服务端初始化方式比较直接:
server := mcp.NewServer(&mcp.Implementation{
Name: "prompt-demo", Version: "1.0.0",
}, nil)
// 添加Prompt...
if err := server.Run(ctx, &mcp.StdioTransport{}); err != nil {
log.Fatal(err)
}
这里注册了一个名为 prompt-demo、版本为 1.0.0 的 MCP 服务,并通过 mcp.StdioTransport{} 运行。对示例工程来说,这已经足够承载 Prompt 注册与调用。
注册 Prompt 模板
接着通过 AddPrompt 注册模板定义和处理函数:
server.AddPrompt(&mcp.Prompt{
Name: "code_review", Title: "代码审查助手",
Description: "对代码进行专业的质量审查",
Arguments: []*mcp.PromptArgument{
{Name: "code", Description: "需要审查的代码", Required: true},
{Name: "language", Description: "编程语言", Required: false},
},
}, handleCodeReview)
这个模板的设计比较典型:
code是核心输入,因此设为必填。language只是增强上下文,不一定每次都提供,所以设为可选。Description明确写出用途,有利于客户端展示,也有助于调用方理解这个模板的使用场景。
在 Handler 中根据参数生成消息
真正的内容拼装发生在处理函数里:
func handleCodeReview(ctx context.Context, req *mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
code := areq.Params.Argumentsrgs["code"]
language := arreq.Params.Argumentsgs["language"]
if language == "" { language = "未知" }
promptText := fmt.Sprintf("请对以下%s代码进行审查:n%s", language, code)
return &mcp.GetPromptResult{
Messages: []*mcp.PromptMessage{{
Role: mcp.RoleUser,
Content: &mcp.TextContent{Text: promptText},
}},
}, nil
}
这段代码传达出的实现思路很清楚:
- 先从请求参数中取出
code和language。 - 如果调用方没传
language,就在服务端补一个默认值。 - 再把这些参数拼成一条用户消息,放进
GetPromptResult的Messages里返回。
换句话说,Prompt 并不是只返回一段字符串,而是返回一组可直接参与对话的消息结构。这样做的好处是,模板可以继续扩展成更复杂的消息序列,而不是被限制在单一文本输出里。
不止单条消息:多消息 Prompt 和动态更新怎么做
如果你的场景需要预设对话上下文,Prompt 还可以返回多条消息。它并不局限于“给模型发一句话”,而是能把一个完整的对话起手式封装起来。
多消息 Prompt 适合哪些场景
常见情况包括技术问答、角色预设、流程化分析等。示例里给出了一个技术问答 Prompt:
server.AddPrompt(&mcp.Prompt{
Name: "tech_qa", Title: "技术问答助手",
Arguments: []*mcp.PromptArgument{
{Name: "question", Required: true},
{Name: "domain", Required: false},
},
}, handleTechQA)
func handleTechQA(ctx context.Context, req *mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
args := req.Params.Arguments
return &mcp.GetPromptResult{
Messages: []*mcp.PromptMessage{
{Role: mcp.RoleUser, Content: &mcp.TextContent{Text: args["question"]}},
},
}, nil
}
虽然示例中只放了一条消息,但这里的返回类型已经说明了扩展空间:你完全可以继续加入更多消息,用来预设上下文、限制回答范围,或者构造多轮对话起点。
Prompt 列表可以在运行时调整
MCP 的另一个实用点是,Prompt 不必在服务启动时一次性定死。运行中的服务也可以动态增删模板。
移除某个 Prompt:
// 移除指定的Prompt
server.RemovePrompts("old_prompt_name")
运行时添加新 Prompt:
server.AddPrompt(&mcp.Prompt{Name: "advanced_analysis"}, handler)
一旦 Prompt 列表发生变化,SDK 会自动向已连接客户端发送 notifications/prompts/list_changed 通知。对客户端来说,这意味着它可以感知服务端模板集合已经变化,并重新同步可用 Prompt 列表。
这类能力很适合做按租户开放功能、按权限暴露模板,或者根据业务状态临时启用某类分析 Prompt 的场景。
落地时更重要的,是参数设计和安全边界
示例代码能帮助理解接口,但真正上线时,Prompt 是否好用、是否可维护,往往取决于一些更基础的设计细节。
参数设计要尽量克制
- 必填参数最小化:只保留真正不可缺少的输入,减少调用门槛。
- 为可选参数设置默认值:把兜底逻辑放在 Handler 里,比要求调用方每次都传完整参数更稳妥。
- 参数说明写清楚:不仅是给人看,也是给接入方和模型理解模板用途。
一个常见问题是,把模板设计成“看起来很强大”,结果参数多到调用方难以下手。与其做一个参数繁杂的大模板,不如把任务边界收紧,保持 Prompt 的职责清晰。
安全问题不能留到后面补
- 输入验证:所有参数都要做校验,尤其是文本拼接场景,需要防止注入式输入破坏提示结构。
- 敏感信息过滤:不要把秘钥、内部路径、用户隐私等内容直接拼进 Prompt。
- 权限控制:不同用户看到的 Prompt 列表未必相同,必要时应按身份动态暴露。
因为 Prompt 本质上会进入模型上下文,所以它不仅是一个“模板接口”,也是一层输入面。凡是会进入模型的内容,都值得用服务端思维去处理,而不只是按字符串拼接来看待。
从模板能力出发,才能真正用好 MCP Prompt
用 Go 官方 MCP SDK 实现 Prompt,本质上并不复杂:先声明模板元信息,再定义参数,最后在 Handler 中根据请求动态返回消息。难点不在 API 本身,而在于你是否把 Prompt 当成可管理、可发现、可演进的服务能力来设计。
从单条消息的代码审查模板,到支持上下文预设的技术问答,再到运行时动态增删 Prompt,MCP 给出的并不是“提示词写法”,而是一套面向服务端的组织方式。对正在构建 AI 应用的人来说,掌握这套机制,意味着你可以把提示能力沉淀成稳定接口,而不是散落在各处的临时字符串。









