读完这篇你能:把一份整理好的 prompt 注册到服务端,让客户端直接取用。
前置:提供 Resources 知识库。
为什么要预置提示词
主线案例上线后,日常用法是这样:在 Cursor 里让模型去查日志、分析原因。

但很快会发现,错误信息一复杂,模型自主分析的效果就飘。于是你总结了一份分析套路,写成了 prompt——就是这份模板。
一开始是每次手动粘贴。等同事也遇到同样的问题、一堆人来找你要模板时,就该把它注册到服务端了:谁要用谁自己拉,不用再手动粘。
顺便厘清一个容易误解的点:预置提示词是「快速生成一段提示词」,不是让 AI 自主决定调用。所以客户端里它是用户主动选的一个动作,不是模型自己挑的工具。
完整代码
// MCP Prompt 示例:统一错误分析。
// 提示词正文来自同目录的 error_analysis_prompt.md,编译时嵌入,运行时渲染。
package main
import (
"context"
"embed"
"fmt"
"log"
"net/http"
"strings"
"text/template"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
//go:embed error_analysis_prompt.md
var promptFS embed.FS
var errorAnalysisTmpl = template.Must(
template.New("error_analysis_prompt.md").ParseFS(promptFS, "error_analysis_prompt.md"),
)
type errorAnalysisPromptData struct {
ServiceName string
TimeRange string
}
func promptHandler(_ context.Context, req *mcp.GetPromptRequest) (*mcp.GetPromptResult, error) {
args := req.Params.Arguments
data := errorAnalysisPromptData{
ServiceName: strings.TrimSpace(args["service_name"]),
TimeRange: strings.TrimSpace(args["time_range"]),
}
var buf strings.Builder
if err := errorAnalysisTmpl.Execute(&buf, data); err != nil {
return nil, fmt.Errorf("渲染提示词模板: %w", err)
}
return &mcp.GetPromptResult{
Description: "错误分析:根据服务名和时间范围,自动查日志并分析根因",
Messages: []*mcp.PromptMessage{
{Role: "user", Content: &mcp.TextContent{Text: strings.TrimSpace(buf.String()) + "\n"}},
},
}, nil
}
func main() {
s := mcp.NewServer(&mcp.Implementation{Name: "log-mcp", Version: "0.1.0"}, nil)
// 工具定义见 02-工具的参数.md
mcp.AddTool(s, &mcp.Tool{
Name: "list_services",
Description: "列出所有服务名",
}, listServicesTool)
mcp.AddTool(s, &mcp.Tool{
Name: "query_logs",
Description: "按服务名和时间范围查询日志,可筛选日志级别",
InputSchema: queryLogsInputSchema,
}, queryLogsTool)
// 注册预置提示词
s.AddPrompt(&mcp.Prompt{
Name: "error-analysis",
Description: "错误分析:根据服务名和时间范围,自动查日志并分析根因",
Arguments: []*mcp.PromptArgument{
{Name: "service_name", Description: "服务名称"},
{Name: "time_range", Description: `时间范围,如 "30m"、"1h" 或 "start/end"`},
},
}, promptHandler)
handler := mcp.NewStreamableHTTPHandler(func(*http.Request) *mcp.Server { return s }, nil)
if err := http.ListenAndServe("127.0.0.1:8000", handler); err != nil {
log.Fatal(err)
}
}
// --- list_services 的实现放这里;query_logs 见 02-工具的参数.md,不重复 ---
type listServicesOutput struct {
Services []string `json:"services" jsonschema:"服务名列表"`
}
func listServicesTool(_ context.Context, _ *mcp.CallToolRequest, _ any) (*mcp.CallToolResult, listServicesOutput, error) {
return nil, listServicesOutput{Services: []string{"billing-service", "order-service"}}, nil
}
接口调试里能看到这个 prompt 和它的参数:


添加:AddPrompt
s.AddPrompt(&mcp.Prompt{
Name: "error-analysis",
Description: "错误分析:根据服务名和时间范围,自动查日志并分析根因",
Arguments: []*mcp.PromptArgument{
{Name: "service_name", Description: "服务名称"},
{Name: "time_range", Description: `时间范围,如 "30m"、"1h" 或 "start/end"`},
},
}, promptHandler)
注册的字段和工具几乎一样:
| 字段 | 作用 |
|---|---|
Name |
唯一标识,客户端靠它调用 |
Description |
让客户端和用户知道这个 prompt 是干什么的 |
Arguments |
声明模板需要哪些参数 |
参数默认是可选的;必填的设 Required: true。
注意函数归属不同:添加工具是
mcp.AddTool(s, …),添加提示词是s.AddPrompt(…)——一个在包上,一个在 server 实例上。这个不对称容易被记混。
提示词正文:单独一个文件
模板用的是 Go 自带的 text/template,正文存放在独立的 markdown 文件里,通过 //go:embed 在编译时打进二进制。这样正文可以用 markdown 高亮和预览,不用挤在字符串里。
//go:embed error_analysis_prompt.md
var promptFS embed.FS
var errorAnalysisTmpl = template.Must(
template.New("error_analysis_prompt.md").ParseFS(promptFS, "error_analysis_prompt.md"),
)
go:embed 的细节见 go:embed 速查。用 %s 占位符直接拼字符串当然也行,模板只是让分支和循环好写一点。
正文内容见分析日志 Prompt 模板。
handler 返回什么
func promptHandler(_ context.Context, req *mcp.GetPromptRequest) (*mcp.GetPromptResult, error)
入参 *mcp.GetPromptRequest 里只有两样东西:
Params.Name——客户端要的是哪个 promptParams.Arguments——模板参数,键值对,值都是字符串
返回值里 Description 可以补一句说明,核心是 Messages——展开后的对话消息列表,可以多条、可以多角色。
PromptMessage 只有两个字段:
| 字段 | 作用 |
|---|---|
Role |
说话方角色,协议上为 user 或 assistant |
Content |
单条消息的内容块,类型由 JSON 的 type 字段区分 |
Content 是个公共定义,工具结果、资源内容、提示词消息都用它,Content 里列了全部七种。
一个技巧:在提示词里附一张图
提示词正文是纯文本,但 Messages 可以塞多段内容。比如附一张分析流程图,让模型按固定流程走:

//go:embed error_analysis_flowchart.png
var errorAnalysisFlowchartPNG []byte
return &mcp.GetPromptResult{
Description: "错误分析:根据服务名和时间范围,自动查日志并分析根因",
Messages: []*mcp.PromptMessage{
{Role: "user", Content: &mcp.TextContent{Text: text}},
{
Role: "user",
Content: &mcp.ImageContent{
MIMEType: "image/png",
Data: errorAnalysisFlowchartPNG,
},
},
},
}, nil
模型收到图和文字后,会更容易按图上的步骤走:

MIMEType 的取值见 MIME 类型。
下一篇:错误返回与重试——分清协议错误和业务错误。