如何在 MCP 中管理和使用提示词(Prompts)?
MCP(Model Context Protocol)定义了三个核心原语:工具(Tools)、资源(Resources)和提示词(Prompts)。其中,提示词原语允许服务器向客户端暴露可复用、参数化的提示词模板,将提示工程从宿主应用转移到拥有领域知识的服务器端。本文将系统讲解 MCP 提示词的协议机制、实现方式和最佳实践。
MCP 提示词的协议机制
MCP 提示词不是简单的字符串拼接,而是有完整的协议生命周期:
- 发现阶段:客户端通过
prompts/list请求获取服务器上所有可用的提示词模板列表 - 获取阶段:用户选择某个提示词后,客户端通过
prompts/get请求获取具体的提示词内容 - 变更通知:当提示词列表发生变化时,服务器通过通知机制告知客户端刷新(需启用
listChanged能力)
每个提示词的定义包含三个字段:
name:提示词的唯一标识符description:人类可读的描述,用于客户端展示arguments:参数列表,每个参数包含name、description和required标志
json{ "name": "code_review", "description": "代码审查提示词模板", "arguments": [ { "name": "language", "description": "编程语言", "required": true }, { "name": "code", "description": "待审查的代码", "required": true } ] }
在 MCP 服务器中注册提示词
使用 Python SDK 注册提示词非常简洁:
pythonfrom mcp.server import Server server = Server("my-mcp-server") @server.prompt( name="code_review", description="代码审查提示词模板" ) async def code_review_prompt( code: str, language: str = "python" ) -> str: """生成代码审查提示词""" return f"请审查以下 {language} 代码,关注代码质量、性能、安全性和最佳实践:\n\n{code}"
注册后,客户端调用 prompts/list 就能看到这个提示词,调用 prompts/get 并传入 language 和 code 参数即可获取渲染后的提示词内容。
多消息提示词与资源嵌入
MCP 提示词不仅支持单条消息,还支持返回多消息数组,每条消息可以有不同的角色(user、assistant)和内容类型。更重要的是,提示词可以嵌入资源引用,将服务器暴露的只读数据直接注入提示词上下文:
python@server.prompt( name="analyze_project", description="分析项目日志和代码" ) async def analyze_project_prompt( timeframe: str, file_uri: str ) -> list: return [ { "role": "user", "content": { "type": "text", "text": f"分析近 {timeframe} 的系统日志和指定代码文件:" } }, { "role": "user", "content": { "type": "resource", "resource": { "uri": f"logs://recent?timeframe={timeframe}", "mimeType": "text/plain" } } }, { "role": "user", "content": { "type": "resource", "resource": { "uri": file_uri, "mimeType": "text/x-python" } } } ]
这种模式让提示词能够组合上下文信息,构建复杂的工作流。例如,一个日志分析提示词可以同时嵌入日志文件和源代码,让 LLM 在分析错误时直接参考相关实现。
提示词的参数验证与安全
MCP 规范要求服务器对所有提示词的输入和输出进行严格验证,防止注入攻击或未授权的资源访问。实现时应注意:
- 必填参数校验:在渲染前检查所有
required: true的参数是否已提供 - 输出内容过滤:防止提示词渲染结果中包含恶意指令
- 资源访问控制:嵌入的资源必须在服务器已声明的资源范围内
python@server.prompt( name="safe_analysis", description="安全分析提示词" ) async def safe_analysis_prompt(code: str) -> str: # 验证输入长度,防止超长输入 if len(code) > 10000: raise ValueError("代码长度超过限制(最大 10000 字符)") # 验证输入不包含可疑模式 if "ignore previous" in code.lower(): raise ValueError("输入包含可疑内容") return f"请分析以下代码的安全性:\n\n{code}"
提示词变更通知
当服务器支持 listChanged 能力时,提示词列表变化后应主动通知客户端:
python# 服务器初始化时声明能力 server = Server( "my-mcp-server", capabilities={"prompts": {"listChanged": True}} ) # 提示词变更后发送通知 await server.send_notification("notifications/prompts/list_changed")
客户端收到通知后应重新调用 prompts/list 刷新本地缓存。
提示词 vs 工具 vs 资源:何时用哪个?
MCP 三个原语各有定位,选择正确的是关键:
| 原语 | 定位 | 典型场景 | 是否有副作用 |
|---|---|---|---|
| 资源(Resources) | 提供只读数据 | 查询文件内容、数据库记录 | 否 |
| 工具(Tools) | 执行操作 | 发送邮件、修改文件 | 是 |
| 提示词(Prompts) | 模板化交互 | 代码审查模板、分析工作流 | 否 |
简单记忆:资源用于查询,工具用于操作,提示词用于标准化。
最佳实践
- 保持确定性:相同输入应产生相同输出,避免在提示词中引入随机性
- 嵌入资源而非引用:直接将上下文数据嵌入提示词消息,减少 LLM 的额外请求
- 提供清晰的描述:
description字段直接影响客户端 UI 中的可发现性 - 使用多消息结构:复杂场景拆分为多条消息,每条消息职责单一
- 实施版本管理:对重要提示词进行版本控制,确保客户端行为一致
- 验证所有输入输出:严格校验参数,防止注入攻击和越权访问
总结
MCP 提示词原语将提示工程从应用层下放到服务器层,让领域专家可以直接定义和优化提示词模板。通过 prompts/list 发现、prompts/get 获取、多消息与资源嵌入组合、变更通知这一完整机制,MCP 提示词实现了跨客户端的标准化交互模式。掌握提示词的协议机制和安全实践,是构建高质量 MCP 服务器的关键一步。