不需要,而且通常不应该由 MCP Server 自己调用大模型。
这是一个非常重要的概念区分。让我来解释一下 MCP 的架构和分工:
MCP 的核心分工
MCP Server 的角色(你写的这个程序)
-
数据提供者: 提供结构化的数据、资源访问
-
工具执行者: 执行具体的操作和计算
-
服务连接器: 连接外部系统和API
-
内容处理器: 处理和转换信息
MCP Client 的角色(如 Claude AI、Cursor AI 等)
-
大模型调用者: 负责实际调用和理解大模型
-
决策制定者: 决定何时使用什么工具/资源
-
结果解释者: 解释和呈现工具执行的结果
-
用户交互者: 与最终用户进行对话交互
正确的交互流程
你应该在 MCP Server 中做什么
✅ 应该做的事情
-
提供数据访问
// 提供数据库查询
server.AddResourceHandler("database", func(ctx context.Context, method string, params map[string]any) (mcp.ResourceResult, error) {
// 查询数据库并返回结果
rows := queryDatabase("SELECT * FROM users WHERE id = ?", params["user_id"])
return mcp.ResourceResult{Contents: convertToResource(rows)}, nil
})
-
执行具体工具
// 执行系统命令
server.AddToolHandler("run-command", func(ctx context.Context, params map[string]any) (mcp.ToolResult, error) {
cmd := exec.Command(params["command"].(string))
output, err := cmd.CombinedOutput()
return mcp.ToolResult{Content: []mcp.Content{{Type: "text", Text: string(output)}}}, err
})
-
连接外部API
// 调用天气预报API
server.AddToolHandler("get-weather", func(ctx context.Context, params map[string]any) (mcp.ToolResult, error) {
data, err := fetchWeatherAPI(params["city"].(string))
return mcp.ToolResult{Content: []mcp.Content{{Type: "text", Text: formatWeatherData(data)}}}, err
})
❌ 不应该做的事情
// 错误:在 MCP Server 中直接调用大模型
server.AddToolHandler("ask-ai", func(ctx context.Context, params map[string]any) (mcp.ToolResult, error) {
// ❌ 这是错误的方式!
response, err := openaiClient.ChatCompletion(params["question"].(string))
return mcp.ToolResult{Content: []mcp.Content{{Type: "text", Text: response}}}, err
})
正确的模式:提供数据,让客户端决策
好的例子:数据分析服务器
// 提供销售数据查询
server.AddResourceHandler("sales-data", func(ctx context.Context, method string, params map[string]string) (mcp.ResourceResult, error) {
// 只返回原始数据,让AI来分析
data := querySalesData(params["period"], params["region"])
return mcp.ResourceResult{
Contents: []mcp.ResourceContent{
{
URI: "sales:///latest",
Content: formatAsJSON(data), // 返回结构化数据
MimeType: "application/json"
}
}
}, nil
})
// 提供数据统计工具
server.AddToolHandler("calculate-metrics", func(ctx context.Context, params map[string]any) (mcp.ToolResult, error) {
// 只做数学计算,不做解释
salesData := getSalesData(params["period"].(string))
metrics := calculateMetrics(salesData) // 返回数值结果
return mcp.ToolResult{
Content: []mcp.Content{
{
Type: "text",
Text: fmt.Sprintf("Total: $%.2f, Average: $%.2f", metrics.Total, metrics.Average)
}
}
}, nil
})
为什么这样设计?
-
效率: 大模型只需要一个实例(在客户端)
-
一致性: 所有AI推理由同一个模型完成
-
成本: 避免重复的API调用费用
-
专注: 每个组件做自己最擅长的事情
特殊情况:链式调用
在某些高级场景中,你可能需要让一个 MCP Server 调用另一个服务,但仍然不应该直接调用大模型:
server.AddToolHandler("analyze-with-external", func(ctx context.Context, params map[string]any) (mcp.ToolResult, error) {
// 调用专门的数据分析服务(不是大模型!)
result, err := callDataAnalysisService(params["data"])
return mcp.ToolResult{Content: []mcp.Content{{Type: "text", Text: result}}}, err
})
总结
你的 MCP Server 应该:
-
提供原始数据和处理能力
-
执行具体的、确定性的操作
-
连接外部系统和服务
-
返回结构化的结果
让 MCP Client(AI)负责:
-
理解用户意图
-
决定使用哪些工具/资源
-
分析和解释返回的数据
-
生成最终的自然语言回复
这样就能充分发挥各自组件的优势,构建出高效可靠的 AI 应用生态系统。
- THE END -
最后修改:2025年9月17日
非特殊说明,本博所有文章均为博主原创。