tiny-engine/docs/extension-capabilities-tuto.../mcpService.md

698 lines
16 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 服务扩展能力
## 概述
MCP (Model Context Protocol) 服务是 tiny-engine 智能化的核心扩展能力之一,它基于 [Model Context Protocol](https://modelcontextprotocol.io/introduction) 标准协议提供了工具Tools的管理和执行能力。
关于 MCP 协议的详细介绍,请参考官方文档:[https://modelcontextprotocol.io/introduction](https://modelcontextprotocol.io/introduction)
## 快速开始
### 基本使用
```typescript
import { getMetaApi, META_SERVICE } from '@opentiny/tiny-engine-meta-register'
import type { ToolItem } from '@opentiny/tiny-engine-common'
import { z } from 'zod'
// 获取 MCP 服务实例
const mcpService = getMetaApi(META_SERVICE.McpService)
// 注册一个简单的工具
const helloTool: ToolItem = {
name: 'hello_world',
title: 'Hello World 工具',
description: '一个简单的问候工具',
inputSchema: {
name: z.string().optional()
},
outputSchema: {
content: z.string()
},
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false
},
callback: async (params) => {
return { content: `Hello, ${params.name || 'World'}!` }
}
}
// 注册工具
mcpService.registerTool(helloTool)
// 获取工具列表
const toolList = mcpService.getToolList()
console.log('已注册的工具:', toolList)
```
### 高级配置
```typescript
// 配置 MCP 服务选项
const mcpOptions = {
proxyUrl: 'https://your-agent-server.com/mcp',
connectToAgentServer: true,
reconnectAttempts: 5,
reconnectInterval: 2000
}
// 通过 setOptions 设置自定义配置
const mcpService = getMetaApi('engine.service.mcp')
mcpService.setOptions(mcpOptions)
```
## API 参考
### 服务配置接口
```typescript
interface IOptions {
// 代理服务器 URL用于连接远程 Agent 服务器
proxyUrl: string | null
// 是否连接到 Agent 服务器
connectToAgentServer: boolean
// 重连尝试次数默认3
reconnectAttempts?: number
// 重连间隔时间毫秒默认1000
reconnectInterval?: number
}
```
### 工具定义接口
```typescript
interface ToolItem {
// 工具唯一标识符
name: string
// 工具显示名称
title?: string
// 工具描述
description?: string
// 输入参数 schemaZod
inputSchema?: ZodRawShape
// 输出结果 schemaZod
outputSchema?: ZodRawShape
// 工具注解信息
annotations?: ToolAnnotations
// 工具执行回调函数
callback: ToolCallback<ZodRawShape | undefined>
}
```
### 连接状态类型
```typescript
type ServerConnectionStatus =
| 'connected' // 已连接
| 'disconnected' // 已断开
| 'connecting' // 连接中
| 'disconnecting' // 断开中
| 'error' // 连接错误
```
### 主要 API 方法
#### 服务器管理
```typescript
// 获取 MCP 服务器实例
getMcpServer(): McpServer | null
// 获取 MCP 客户端实例
getMcpClient(): Client | null
// 获取远程传输实例
getRemoteTransport(): any
// 获取服务器连接状态
getServerConnectionStatus(): ServerConnectionStatus
```
#### 连接管理
```typescript
// 连接到远程服务器(遥控端)
connectToRemoteServer(): Promise<void>
// 重新连接到远程服务器(遥控端)
reconnectToRemoteServer(): Promise<void>
// 关闭远程服务器连接(遥控端)
closeRemoteServer(): Promise<void>
// 关闭传输连接(遥控端)
closeTransport(): Promise<void>
```
#### 工具管理
```typescript
// 注册工具
registerTool(tool: ToolItem): void
// 获取所有工具列表
getToolList(): ToolItem[]
// 根据名称获取工具
getToolByName(name: string): ToolItem | undefined
// 获取工具实例
getToolInstance(name: string): RegisteredTool | undefined
// 启用工具
enableTool(name: string): void
// 禁用工具
disableTool(name: string): void
// 移除工具
removeTool(name: string): void
// 更新工具配置
updateTool(name: string, config?: UpdateToolConfig): void
```
## 工具管理详解
### 工具注册
工具注册是 MCP 服务的核心功能。每个工具都需要定义清晰的接口和回调函数:
```typescript
import { z } from 'zod'
import { getMetaApi } from '@opentiny/tiny-engine-meta-register'
// 定义工具的输入 schema
const calculateInputSchema = {
operation: z.enum(['add', 'subtract', 'multiply', 'divide']),
a: z.number(),
b: z.number()
}
// 定义工具的输出 schema
const calculateOutputSchema = {
result: z.number(),
operation: z.string()
}
// 创建计算器工具
const calculatorTool: ToolItem = {
name: 'calculator',
title: '数学计算器',
description: '执行基本的数学运算',
inputSchema: calculateInputSchema,
outputSchema: calculateOutputSchema,
callback: async (params) => {
const { operation, a, b } = params
let result: number
switch (operation) {
case 'add':
result = a + b
break
case 'subtract':
result = a - b
break
case 'multiply':
result = a * b
break
case 'divide':
if (b === 0) {
throw new Error('除数不能为零')
}
result = a / b
break
default:
throw new Error('不支持的操作')
}
return {
result,
operation: `${a} ${operation} ${b} = ${result}`
}
}
}
// 获取 MCP 服务并注册工具
const mcpService = getMetaApi('engine.service.mcp')
mcpService.registerTool(calculatorTool)
```
### 工具生命周期管理
```typescript
import { getMetaApi } from '@opentiny/tiny-engine-meta-register'
const mcpService = getMetaApi('engine.service.mcp')
// 检查工具是否存在
const tool = mcpService.getToolByName('calculator')
if (tool) {
console.log('工具已存在:', tool.title)
}
// 临时禁用工具
mcpService.disableTool('calculator')
// 重新启用工具
mcpService.enableTool('calculator')
// 更新工具配置
mcpService.updateTool('calculator', {
description: '更新后的计算器描述',
title: '高级计算器'
})
// 移除工具
mcpService.removeTool('calculator')
```
### 批量工具注册
```typescript
// 从注册表中自动收集工具
// MCP 服务会自动扫描所有注册的 meta 数据中的 mcp.tools 字段
// 在插件的 meta 数据中定义工具
const pluginMeta = {
mcp: {
tools: [
{
name: 'file_reader',
title: '文件读取器',
description: '读取文件内容',
callback: async (params) => {
// 实现文件读取逻辑
return { content: '文件内容' }
}
},
{
name: 'data_processor',
title: '数据处理器',
description: '处理数据',
callback: async (params) => {
// 实现数据处理逻辑
return { processed: true }
}
}
]
}
}
// 工具会在服务初始化时自动注册
```
## 连接管理详解
### 连接状态监控
```typescript
import { useMessage } from '@opentiny/tiny-engine-meta-register'
import { getMetaApi } from '@opentiny/tiny-engine-meta-register'
// 监听连接状态变化
const { subscribe } = useMessage()
subscribe({
topic: 'serverConnectionStatusChanged',
callback: ({ status, error }) => {
console.log('连接状态变化:', status)
switch (status) {
case 'connecting':
console.log('正在连接到服务器...')
break
case 'connected':
console.log('已成功连接到服务器')
break
case 'disconnected':
console.log('与服务器断开连接')
break
case 'error':
console.error('连接错误:', error)
break
}
}
})
// 获取当前连接状态
const mcpService = getMetaApi('engine.service.mcp')
const currentStatus = mcpService.getServerConnectionStatus()
console.log('当前连接状态:', currentStatus)
```
### 手动连接管理
```typescript
import { getMetaApi } from '@opentiny/tiny-engine-meta-register'
const mcpService = getMetaApi('engine.service.mcp')
// 手动连接到远程服务器
try {
await mcpService.connectToRemoteServer()
console.log('连接成功')
} catch (error) {
console.error('连接失败:', error)
}
// 重新连接(会先断开现有连接)
try {
await mcpService.reconnectToRemoteServer()
console.log('重连成功')
} catch (error) {
console.error('重连失败:', error)
}
// 关闭连接
await mcpService.closeRemoteServer()
```
### 会话持久化
MCP 服务支持会话持久化,确保页面刷新后能够恢复连接:
```typescript
// 会话 ID 会自动保存到 sessionStorage
// 页面刷新后会尝试使用相同的会话 ID 重新连接
// 手动获取当前会话 ID
const sessionId = sessionStorage.getItem('mcp-session-id')
console.log('当前会话 ID:', sessionId)
// 清除会话(强制创建新会话)
sessionStorage.removeItem('mcp-session-id')
```
## 配置选项详解
### 基本配置
```typescript
import { getMetaApi } from '@opentiny/tiny-engine-meta-register'
const mcpService = getMetaApi('engine.service.mcp')
// 不连接远程服务器,仅使用本地模式
mcpService.setOptions({
connectToAgentServer: false
})
```
### 远程服务器配置
```typescript
import { getMetaApi } from '@opentiny/tiny-engine-meta-register'
const mcpService = getMetaApi('engine.service.mcp')
const remoteConfig = {
proxyUrl: 'https://api.example.com/mcp',
connectToAgentServer: true,
reconnectAttempts: 3, // 重连尝试次数
reconnectInterval: 1000 // 重连间隔(毫秒)
}
mcpService.setOptions(remoteConfig)
```
### 高级配置
<pre>
<code>
import { getMetaApi } from '@opentiny/tiny-engine-meta-register'
const mcpService = getMetaApi('engine.service.mcp')
const advancedConfig = {
proxyUrl: process<wbr>.env.MCP_PROXY_URL || 'http://localhost:3000/mcp',
connectToAgentServer: process<wbr>.env.NODE_ENV === 'production',
reconnectAttempts: 5,
reconnectInterval: 2000
}
mcpService.setOptions(advancedConfig)
</code>
</pre>
## 最佳实践
### 1. 工具设计原则
```typescript
// ✅ 好的工具设计
const goodTool: ToolItem = {
name: 'format_text', // 使用描述性的名称
title: '文本格式化工具', // 提供清晰的标题
description: '将文本格式化为指定的样式', // 详细的描述
inputSchema: { // 明确的输入验证
text: z.string().min(1),
format: z.enum(['uppercase', 'lowercase', 'capitalize'])
},
outputSchema: { // 明确的输出结构
formatted: z.string(),
original: z.string()
},
callback: async (params) => {
// 实现具体的逻辑
const { text, format } = params
let formatted: string
switch (format) {
case 'uppercase':
formatted = text.toUpperCase()
break
case 'lowercase':
formatted = text.toLowerCase()
break
case 'capitalize':
formatted = text.charAt(0).toUpperCase() + text.slice(1)
break
default:
formatted = text
}
return { formatted, original: text }
}
}
// ❌ 避免的设计
const badTool: ToolItem = {
name: 'tool1', // 名称不够描述性
description: '处理文本', // 描述太模糊
callback: async (params) => {
// 没有输入验证
return params.text.toUpperCase() // 返回格式不一致
}
}
```
### 2. 错误处理
```typescript
const robustTool: ToolItem = {
name: 'file_processor',
title: '文件处理器',
description: '处理各种格式的文件',
callback: async (params) => {
try {
// 验证输入
if (!params.filePath) {
throw new Error('文件路径不能为空')
}
// 执行处理逻辑
const result = await processFile(params.filePath)
return {
success: true,
result,
message: '文件处理成功'
}
} catch (error) {
// 统一的错误处理
return {
success: false,
error: error.message,
message: '文件处理失败'
}
}
}
}
```
### 3. 异步操作处理
```typescript
const asyncTool: ToolItem = {
name: 'data_fetcher',
title: '数据获取器',
description: '从远程 API 获取数据',
callback: async (params) => {
const { url, timeout = 5000 } = params
// 使用 Promise.race 实现超时控制
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('请求超时')), timeout)
})
const fetchPromise = fetch(url).then(res => res.json())
try {
const data = await Promise.race([fetchPromise, timeoutPromise])
return {
success: true,
data,
timestamp: Date.now()
}
} catch (error) {
return {
success: false,
error: error.message,
timestamp: Date.now()
}
}
}
}
```
### 4. 资源管理
```typescript
import { getMetaApi } from '@opentiny/tiny-engine-meta-register'
const mcpService = getMetaApi('engine.service.mcp')
// 在页面卸载时清理资源
window.addEventListener('beforeunload', async () => {
await mcpService.closeTransport()
})
// 组件卸载时清理工具
const cleanup = () => {
// 移除临时工具
mcpService.removeTool('temporary_tool')
// 关闭连接
mcpService.closeRemoteServer()
}
```
## 故障排除
### 常见问题
#### 1. 工具注册失败
**问题**:工具注册时报错或没有生效
**解决方案**
```typescript
import { getMetaApi } from '@opentiny/tiny-engine-meta-register'
const mcpService = getMetaApi('engine.service.mcp')
// 检查工具名称是否重复
const existingTool = mcpService.getToolByName('my_tool')
if (existingTool) {
console.log('工具已存在,先移除再注册')
mcpService.removeTool('my_tool')
}
// 确保 callback 函数正确
const tool: ToolItem = {
name: 'my_tool',
title: '我的工具',
callback: async (params) => { // 必须是 async 函数
return { result: 'success' } // 必须返回对象
}
}
mcpService.registerTool(tool)
```
#### 2. 连接失败
**问题**:无法连接到远程服务器
**解决方案**
```typescript
import { getMetaApi, useMessage } from '@opentiny/tiny-engine-meta-register'
const mcpService = getMetaApi('engine.service.mcp')
const { subscribe } = useMessage()
// 检查配置
mcpService.setOptions({
proxyUrl: 'https://your-server.com/mcp', // 确保 URL 正确
connectToAgentServer: true
})
// 监听连接状态
subscribe({
topic: 'serverConnectionStatusChanged',
callback: ({ status, error }) => {
if (status === 'error') {
console.error('连接错误详情:', error)
// 检查网络连接
if (error.message.includes('网络')) {
console.log('请检查网络连接')
}
// 检查认证
if (error.message.includes('401')) {
console.log('请检查认证配置')
}
}
}
})
```
#### 3. 工具执行错误
**问题**:工具执行时出现异常
**解决方案**
```typescript
// 添加详细的错误日志
const debugTool: ToolItem = {
name: 'debug_tool',
callback: async (params) => {
console.log('工具执行开始,参数:', params)
try {
const result = await yourToolLogic(params)
console.log('工具执行成功,结果:', result)
return result
} catch (error) {
console.error('工具执行失败:', error)
console.error('错误堆栈:', error.stack)
throw error // 重新抛出错误以便上层处理
}
}
}
```
## 完整示例
### 综合示例
TODO
## 总结
MCP 服务为 tiny-engine 智能化提供了强大的驱动。通过合理的增加工具、prompts 等 mcp 能力,它能够让 AI 理解TinyEngine以及我们自定义的插件扩展让 AI 为我们提供更加智能化的服务。