Files
map-client-vue/docs/mcp-tool-calling-example.md
2025-10-15 15:07:45 +08:00

513 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# MCP 工具调用完整示例
## 概述
本文档展示 Cherry Studio 架构风格的 MCP 工具调用流程,通过"发布小红书文章"的实际例子,详细说明 AI 如何理解用户意图、生成内容、并自动调用 MCP 工具。
## 实现架构
### 核心流程
```
用户输入
获取 MCP 工具 (带服务器名称前缀)
添加 System Prompt (指导 AI 使用工具)
AI 理解意图 + 生成内容
AI 调用工具 (OpenAI Function Calling)
解析工具名称 (serverName__toolName)
执行 MCP 工具
工具结果返回
AI 生成友好回复
```
### 关键创新点
1. **工具名称前缀**: `serverName__toolName` 格式避免多服务器工具名冲突
2. **System Prompt**: 详细的工具使用指南,让 AI 理解如何创作和调用
3. **参数自动注入**: AI 根据用户意图自动生成完整参数
4. **多轮对话**: 支持工具结果继续对话
## 完整示例:发布小红书文章
### 用户输入
```
用户: 帮我发布小红书文章,内容是:如何制作一道酸菜鱼
```
### 步骤 1: 获取 MCP 工具
假设连接了名为 `xiaohongshu` 的 MCP 服务器,提供以下工具:
```json
{
"name": "public_content",
"description": "发布内容到小红书平台",
"inputSchema": {
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "文章标题,吸引眼球且相关"
},
"content": {
"type": "string",
"description": "文章正文Markdown 格式"
},
"tags": {
"type": "array",
"description": "标签列表3-5个",
"items": { "type": "string" }
},
"category": {
"type": "string",
"description": "分类,如美食、生活、旅游等"
}
},
"required": ["title", "content", "tags", "category"]
}
}
```
### 步骤 2: 转换为 OpenAI 格式(带前缀)
```typescript
// chatService.ts - convertToolsToOpenAIFormat()
{
type: 'function',
function: {
name: 'xiaohongshu__public_content', // 添加服务器前缀
description: '发布内容到小红书平台',
parameters: { ...inputSchema }
}
}
```
### 步骤 3: 生成 System Prompt
```typescript
// chatService.ts - createSystemPromptWithTools()
你是一个智能助手,可以使用以下工具完成任务:
xiaohongshu__public_content
描述: 发布内容到小红书平台
参数:
- title [必填]: 文章标题,吸引眼球且相关
- content [必填]: 文章正文,Markdown 格式
- tags [必填]: 标签列表,3-5
- category [必填]: 分类,如美食、生活、旅游等
使用指南:
1. 当用户需要完成某个任务时,请分析哪个工具最合适
2. 如果需要发布内容(如文章、笔记等),请根据用户意图创作完整的内容
3. 为内容生成合适的标题、正文、标签等所有必需参数
4. 自动调用相应工具,将生成的内容作为参数传递
5. 根据工具执行结果,给用户友好的反馈
注意事项:
- 保持内容质量和平台特色
- 标签要相关且有吸引力
- 分类要准确
- 如果工具执行失败,给出明确的错误说明和建议
当前连接的 MCP 服务器: xiaohongshu
```
### 步骤 4: 发送请求到 LLM
```typescript
// modelServiceManager.ts - sendChatRequestStream()
const request = {
model: 'gpt-4',
messages: [
{
role: 'system',
content: '你是一个智能助手,可以使用以下工具...' // System Prompt
},
{
role: 'user',
content: '帮我发布小红书文章,内容是:如何制作一道酸菜鱼'
}
],
tools: [
{
type: 'function',
function: {
name: 'xiaohongshu__public_content',
description: '发布内容到小红书平台',
parameters: { ... }
}
}
],
tool_choice: 'auto',
stream: true
}
```
### 步骤 5: AI 理解 + 生成内容 + 调用工具
AI 响应SSE 流式返回):
```json
// 第一部分AI 思考过程(可选)
{
"choices": [{
"delta": {
"content": "好的,我来帮你创作一篇关于酸菜鱼制作的小红书文章并发布。"
}
}]
}
// 第二部分:工具调用
{
"choices": [{
"delta": {
"tool_calls": [
{
"id": "call_abc123",
"type": "function",
"function": {
"name": "xiaohongshu__public_content",
"arguments": {
"title": "🐟 超详细家常酸菜鱼做法10分钟学会",
"content": "# 酸菜鱼制作教程\n\n## 所需食材\n- 草鱼1条(约1.5kg)\n- 酸菜200g\n- 姜片、蒜瓣适量...\n\n## 制作步骤\n\n### 1. 处理鱼肉\n...",
"tags": ["美食教程", "酸菜鱼", "家常菜", "川菜", "烹饪技巧"],
"category": "美食"
}
}
}
]
},
"finish_reason": "tool_calls"
}]
}
```
### 步骤 6: 解析工具名称
```typescript
// chatService.ts - executeToolCalls()
const fullFunctionName = 'xiaohongshu__public_content'
const parts = fullFunctionName.split('__')
if (parts.length !== 2) {
console.error('工具名称格式错误')
return
}
const [serverName, toolName] = parts
// serverName = 'xiaohongshu'
// toolName = 'public_content'
```
### 步骤 7: 执行 MCP 工具
```typescript
// MCPClientService.ts - callTool()
const result = await mcpClient.callTool(
'xiaohongshu', // serverId
'public_content', // toolName (不带前缀)
{
title: '🐟 超详细家常酸菜鱼做法10分钟学会',
content: '# 酸菜鱼制作教程\n\n## 所需食材...',
tags: ['美食教程', '酸菜鱼', '家常菜', '川菜', '烹饪技巧'],
category: '美食'
}
)
// MCP Server 响应:
{
"success": true,
"article_id": "xhs_2024_001",
"url": "https://www.xiaohongshu.com/discovery/item/xhs_2024_001",
"views": 0,
"likes": 0
}
```
### 步骤 8: 工具结果返回 AI
```typescript
// chatService.ts - 继续对话
const messages = [
{
role: 'system',
content: '...' // System Prompt
},
{
role: 'user',
content: '帮我发布小红书文章,内容是:如何制作一道酸菜鱼'
},
{
role: 'assistant',
tool_calls: [{
id: 'call_abc123',
type: 'function',
function: {
name: 'xiaohongshu__public_content',
arguments: '{"title":"🐟 超详细家常酸菜鱼做法10分钟学会",...}'
}
}]
},
{
role: 'tool',
tool_call_id: 'call_abc123',
name: 'xiaohongshu__public_content', // 保持原名称(带前缀)
content: JSON.stringify({
success: true,
article_id: 'xhs_2024_001',
url: 'https://www.xiaohongshu.com/discovery/item/xhs_2024_001'
})
}
]
// 再次调用 LLM
```
### 步骤 9: AI 生成友好回复
```json
{
"choices": [{
"delta": {
"content": "✅ 文章已成功发布到小红书!\n\n📝 标题:🐟 超详细家常酸菜鱼做法10分钟学会\n🔗 链接https://www.xiaohongshu.com/discovery/item/xhs_2024_001\n\n你的酸菜鱼教程已经上线啦记得定期查看浏览和点赞数据哦~ 🎉"
},
"finish_reason": "stop"
}]
}
```
## 关键代码实现
### 1. System Prompt 生成 (chatService.ts)
```typescript
private createSystemPromptWithTools(tools: any[], serverName: string): string {
const toolDescriptions = tools.map(tool => {
const func = tool.function
const params = func.parameters?.properties || {}
const required = func.parameters?.required || []
const paramDesc = Object.entries(params).map(([name, schema]: [string, any]) => {
const isRequired = required.includes(name)
const requiredMark = isRequired ? '[必填]' : '[可选]'
return ` - ${name} ${requiredMark}: ${schema.description || schema.type}`
}).join('\n')
return `• ${func.name}\n 描述: ${func.description}\n 参数:\n${paramDesc || ' 无参数'}`
}).join('\n\n')
return `你是一个智能助手,可以使用以下工具完成任务:
${toolDescriptions}
使用指南:
1. 当用户需要完成某个任务时,请分析哪个工具最合适
2. 如果需要发布内容(如文章、笔记等),请根据用户意图创作完整的内容
3. 为内容生成合适的标题、正文、标签等所有必需参数
4. 自动调用相应工具,将生成的内容作为参数传递
5. 根据工具执行结果,给用户友好的反馈
注意事项:
- 保持内容质量和平台特色
- 标签要相关且有吸引力
- 分类要准确
- 如果工具执行失败,给出明确的错误说明和建议
当前连接的 MCP 服务器: ${serverName}`
}
```
### 2. 工具名称转换 (chatService.ts)
```typescript
private convertToolsToOpenAIFormat(mcpTools: any[], serverName: string): any[] {
return mcpTools.map(tool => ({
type: 'function',
function: {
name: `${serverName}__${tool.name}`, // 添加服务器前缀
description: tool.description || '',
parameters: tool.inputSchema || {
type: 'object',
properties: {},
required: []
}
}
}))
}
```
### 3. 工具名称解析 (chatService.ts)
```typescript
private async executeToolCalls(
conversation: Conversation,
toolCalls: any[],
model: string | undefined,
onChunk: (chunk: string) => void,
mcpServerId: string
): Promise<void> {
for (const toolCall of toolCalls) {
const fullFunctionName = toolCall.function.name
const args = JSON.parse(toolCall.function.arguments)
console.log('🔧 执行工具调用:', {
fullName: fullFunctionName,
id: toolCall.id,
arguments: args
})
// 解析 serverName__toolName 格式
const parts = fullFunctionName.split('__')
if (parts.length !== 2) {
console.error('❌ 工具名称格式错误,应为 serverName__toolName:', fullFunctionName)
continue
}
const toolName = parts[1]
console.log('🎯 提取工具名称:', toolName)
try {
// 调用 MCP 工具(使用不带前缀的工具名)
const result = await this.mcpClient.callTool(
mcpServerId,
toolName, // 使用原始工具名
args
)
// 添加工具结果到消息历史(使用完整名称)
const toolResultMessage: Message = {
id: Date.now().toString(),
role: 'tool',
content: JSON.stringify(result),
timestamp: new Date(),
status: 'success',
toolCallId: toolCall.id,
toolName: fullFunctionName // 保持完整名称
}
conversation.messages.push(toolResultMessage)
this.saveConversations()
// 继续对话
await this.callModelStream(conversation, model, onChunk, mcpServerId)
} catch (error) {
console.error('❌ 工具执行失败:', error)
// 错误处理...
}
}
}
```
## 测试场景
### 场景 1: 发布文章
```
用户: 帮我发布一篇关于"春季穿搭指南"的小红书笔记
AI 处理:
1. 识别需要使用 xiaohongshu__public_content 工具
2. 创作完整文章(标题、正文、标签、分类)
3. 调用工具发布
4. 返回发布结果和链接
```
### 场景 2: 多工具选择
假设有多个 MCP 服务器:
```
- xiaohongshu__public_content (发布小红书)
- weibo__post_status (发布微博)
- notion__create_page (创建 Notion 页面)
```
```
用户: 帮我把这篇文章同时发到小红书和微博
AI 处理:
1. 理解需要两个工具
2. 为小红书创作合适格式的内容
3. 为微博创作合适格式的内容(字数限制)
4. 依次调用两个工具
5. 返回两个平台的发布结果
```
### 场景 3: 错误处理
```
用户: 发布文章到小红书,标题是"测试"
AI 处理:
1. 识别内容不完整
2. 提示用户补充正文内容
3. 等待用户补充后再调用工具
```
## 优势总结
### 1. **智能参数生成**
- AI 自动创作内容,无需用户逐一填写参数
- 符合平台特色(小红书风格 vs 微博风格)
### 2. **工具名称隔离**
- `serverName__toolName` 避免多服务器冲突
- 清晰的工具来源
### 3. **友好的用户体验**
- 自然语言输入:"帮我发布..."
- 自动处理所有技术细节
- 结果友好呈现
### 4. **可扩展性**
- 轻松添加新 MCP 服务器
- 支持任意数量和类型的工具
- System Prompt 自动生成
### 5. **多轮对话支持**
- 工具结果自动传回 AI
- 可以追问、修改、重试
## 对比 Cherry Studio
| 特性 | mcp-client-vue | Cherry Studio |
|------|---------------|---------------|
| 工具名称格式 | ✅ `serverName__toolName` | ✅ `serverName__toolName` |
| System Prompt | ✅ 自动生成 | ✅ 自动生成 |
| 参数自动注入 | ✅ AI 生成 | ✅ AI 生成 |
| 多轮对话 | ✅ 完整支持 | ✅ 完整支持 |
| 流式响应 | ✅ SSE 真流式 | ✅ 真流式 |
| 错误处理 | ✅ 完善 | ✅ 完善 |
| UI 界面 | Vue 3 + Naive UI | Electron + React |
## 下一步优化
1. **批量工具调用**: 同时调用多个工具
2. **工具调用历史**: 记录和展示工具调用日志
3. **工具执行超时**: 防止长时间阻塞
4. **工具权限控制**: 敏感操作需要用户确认
5. **工具调用缓存**: 避免重复调用
## 相关文件
- `/web/src/services/chatService.ts` - 核心服务
- `/web/src/services/modelServiceManager.ts` - 模型管理
- `/web/src/services/MCPClientService.ts` - MCP 客户端
- `/web/src/components/Chat/ChatLayout.vue` - UI 组件
---
**版本**: v1.0.2+
**更新时间**: 2024-01
**作者**: MCP Client Vue Team