@xiaozhi-client/mcp-core 1.9.7-beta.5 → 1.9.7-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +750 -0
- package/dist/index.d.ts +343 -39
- package/dist/index.js +742 -80
- package/dist/index.js.map +1 -1
- package/package.json +6 -4
package/README.md
ADDED
|
@@ -0,0 +1,750 @@
|
|
|
1
|
+
# @xiaozhi-client/mcp-core
|
|
2
|
+
|
|
3
|
+
> MCP 协议核心实现库,提供服务连接和管理功能
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@xiaozhi-client/mcp-core)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## 简介
|
|
9
|
+
|
|
10
|
+
`@xiaozhi-client/mcp-core` 是一个完全独立的 MCP(Model Context Protocol)核心实现库,提供:
|
|
11
|
+
|
|
12
|
+
- **零业务耦合** - 纯粹的 MCP 协议实现,无任何业务逻辑依赖
|
|
13
|
+
- **多传输协议支持** - 支持 STDIO、SSE、StreamableHTTP 三种传输方式
|
|
14
|
+
- **灵活的事件系统** - 基于回调的事件机制,易于集成
|
|
15
|
+
- **完整的类型定义** - TypeScript 严格模式,提供完整的类型支持
|
|
16
|
+
- **生产就绪** - 经过充分测试,可直接用于生产环境
|
|
17
|
+
|
|
18
|
+
## 特性
|
|
19
|
+
|
|
20
|
+
### 核心功能
|
|
21
|
+
|
|
22
|
+
- **MCP 服务连接管理** - 自动连接、重连、断开处理
|
|
23
|
+
- **工具发现与管理** - 自动发现和管理 MCP 工具
|
|
24
|
+
- **工具调用** - 简单易用的工具调用接口
|
|
25
|
+
- **参数验证** - 内置参数验证和类型检查
|
|
26
|
+
- **事件通知** - 连接状态变化、工具更新的实时通知
|
|
27
|
+
|
|
28
|
+
### 传输协议
|
|
29
|
+
|
|
30
|
+
| 协议 | 说明 | 使用场景 |
|
|
31
|
+
|------|------|----------|
|
|
32
|
+
| **STDIO** | 标准输入输出通信 | 本地进程通信 |
|
|
33
|
+
| **SSE** | Server-Sent Events | 单向服务器推送 |
|
|
34
|
+
| **StreamableHTTP** | 流式 HTTP 通信 | 现代 HTTP/2 场景 |
|
|
35
|
+
|
|
36
|
+
## 安装
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# 使用 npm
|
|
40
|
+
npm install @xiaozhi-client/mcp-core
|
|
41
|
+
|
|
42
|
+
# 使用 pnpm
|
|
43
|
+
pnpm add @xiaozhi-client/mcp-core
|
|
44
|
+
|
|
45
|
+
# 使用 yarn
|
|
46
|
+
yarn add @xiaozhi-client/mcp-core
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 依赖要求
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"@modelcontextprotocol/sdk": "^1.24.0"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
请确保项目中安装了 `@modelcontextprotocol/sdk`:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm install @modelcontextprotocol/sdk
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## 快速开始
|
|
66
|
+
|
|
67
|
+
### 使用 MCPManager(推荐)
|
|
68
|
+
|
|
69
|
+
MCPManager 提供了简洁的 API 来管理多个 MCP 服务:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { MCPManager } from '@xiaozhi-client/mcp-core';
|
|
73
|
+
|
|
74
|
+
const manager = new MCPManager();
|
|
75
|
+
|
|
76
|
+
// 添加 STDIO 服务
|
|
77
|
+
manager.addServer('datetime', {
|
|
78
|
+
type: 'stdio',
|
|
79
|
+
command: 'node',
|
|
80
|
+
args: ['datetime.js']
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// 添加 HTTP 服务
|
|
84
|
+
manager.addServer('web-search', {
|
|
85
|
+
type: 'http',
|
|
86
|
+
url: 'https://api.example.com/mcp',
|
|
87
|
+
headers: {
|
|
88
|
+
Authorization: 'Bearer your-api-key'
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// 添加 SSE 服务
|
|
93
|
+
manager.addServer('sse-server', {
|
|
94
|
+
type: 'sse',
|
|
95
|
+
url: 'https://api.example.com/sse'
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// 添加带环境变量的 STDIO 服务
|
|
99
|
+
manager.addServer('custom-server', {
|
|
100
|
+
type: 'stdio',
|
|
101
|
+
command: 'npx',
|
|
102
|
+
args: ['-y', '@example/mcp-server'],
|
|
103
|
+
env: {
|
|
104
|
+
API_KEY: 'your-api-key',
|
|
105
|
+
MODE: 'production'
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// 连接所有服务
|
|
110
|
+
await manager.connect();
|
|
111
|
+
|
|
112
|
+
// 调用工具
|
|
113
|
+
const result = await manager.callTool('datetime', 'get_current_time', {
|
|
114
|
+
format: 'YYYY-MM-DD HH:mm:ss'
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// 列出所有工具
|
|
118
|
+
const tools = manager.listTools();
|
|
119
|
+
console.log('可用工具:', tools.map(t => `${t.serverName}/${t.name}`));
|
|
120
|
+
|
|
121
|
+
// 获取服务状态
|
|
122
|
+
const status = manager.getServerStatus('datetime');
|
|
123
|
+
console.log('datetime 服务状态:', status);
|
|
124
|
+
|
|
125
|
+
// 断开连接
|
|
126
|
+
await manager.disconnect();
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### 事件监听
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
// 监听连接事件
|
|
133
|
+
manager.on('connect', () => {
|
|
134
|
+
console.log('开始连接所有服务');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
manager.on('connected', ({ serverName, tools }) => {
|
|
138
|
+
console.log(`服务 ${serverName} 已连接,发现 ${tools.length} 个工具`);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
manager.on('disconnected', ({ serverName, reason }) => {
|
|
142
|
+
console.log(`服务 ${serverName} 已断开: ${reason}`);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
manager.on('error', ({ serverName, error }) => {
|
|
146
|
+
console.error(`服务 ${serverName} 出错:`, error.message);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
manager.on('disconnect', () => {
|
|
150
|
+
console.log('所有服务已断开');
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
#### MCPManager API 方法
|
|
155
|
+
|
|
156
|
+
| 方法 | 说明 | 返回类型 |
|
|
157
|
+
|------|------|----------|
|
|
158
|
+
| `addServer(name, config)` | 添加服务器配置 | `void` |
|
|
159
|
+
| `removeServer(name)` | 移除服务器配置 | `boolean` |
|
|
160
|
+
| `connect()` | 连接所有服务 | `Promise<void>` |
|
|
161
|
+
| `disconnect()` | 断开所有服务 | `Promise<void>` |
|
|
162
|
+
| `callTool(serverName, toolName, args)` | 调用指定服务的工具 | `Promise<ToolCallResult>` |
|
|
163
|
+
| `listTools()` | 列出所有可用工具 | `ToolInfo[]` |
|
|
164
|
+
| `getServerStatus(name)` | 获取服务状态 | `ServiceStatus \| null` |
|
|
165
|
+
| `getAllServerStatus()` | 获取所有服务状态 | `Record<string, ServiceStatus>` |
|
|
166
|
+
| `isConnected(name)` | 检查服务是否已连接 | `boolean` |
|
|
167
|
+
| `getServerNames()` | 获取已配置的服务名列表 | `string[]` |
|
|
168
|
+
| `getConnectedServerNames()` | 获取已连接的服务名列表 | `string[]` |
|
|
169
|
+
|
|
170
|
+
### 使用 MCPConnection(底层 API)
|
|
171
|
+
|
|
172
|
+
如果你需要更细粒度的控制,可以直接使用 `MCPConnection`:
|
|
173
|
+
|
|
174
|
+
#### 基础用法
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import { MCPConnection, MCPTransportType } from '@xiaozhi-client/mcp-core';
|
|
178
|
+
|
|
179
|
+
// 1. 创建连接配置
|
|
180
|
+
const config = {
|
|
181
|
+
name: 'my-mcp-service',
|
|
182
|
+
type: MCPTransportType.STREAMABLE_HTTP,
|
|
183
|
+
url: 'https://api.example.com/mcp',
|
|
184
|
+
apiKey: 'your-api-key'
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// 2. 创建事件回调
|
|
188
|
+
const callbacks = {
|
|
189
|
+
onConnected: (data) => {
|
|
190
|
+
console.log(`服务 ${data.serviceName} 已连接,发现 ${data.tools.length} 个工具`);
|
|
191
|
+
},
|
|
192
|
+
onDisconnected: (data) => {
|
|
193
|
+
console.log(`服务 ${data.serviceName} 已断开: ${data.reason}`);
|
|
194
|
+
},
|
|
195
|
+
onConnectionFailed: (data) => {
|
|
196
|
+
console.error(`连接失败: ${data.error.message}`);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// 3. 创建并连接
|
|
201
|
+
const connection = new MCPConnection(config, callbacks);
|
|
202
|
+
await connection.connect();
|
|
203
|
+
|
|
204
|
+
// 4. 调用工具
|
|
205
|
+
const result = await connection.callTool('my_tool', {
|
|
206
|
+
param1: 'value1',
|
|
207
|
+
param2: 'value2'
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// 5. 获取工具列表
|
|
211
|
+
const tools = connection.getTools();
|
|
212
|
+
console.log('可用工具:', tools.map(t => t.name));
|
|
213
|
+
|
|
214
|
+
// 6. 断开连接
|
|
215
|
+
await connection.disconnect();
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### STDIO 连接示例
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
import { MCPConnection, MCPTransportType } from '@xiaozhi-client/mcp-core';
|
|
222
|
+
|
|
223
|
+
const config = {
|
|
224
|
+
name: 'local-mcp-server',
|
|
225
|
+
type: MCPTransportType.STDIO,
|
|
226
|
+
command: 'node',
|
|
227
|
+
args: ['path/to/server.js'],
|
|
228
|
+
env: {
|
|
229
|
+
NODE_ENV: 'production'
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const connection = new MCPConnection(config);
|
|
234
|
+
await connection.connect();
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### SSE 连接示例
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
import { MCPConnection, MCPTransportType } from '@xiaozhi-client/mcp-core';
|
|
241
|
+
|
|
242
|
+
const config = {
|
|
243
|
+
name: 'sse-server',
|
|
244
|
+
type: MCPTransportType.SSE,
|
|
245
|
+
url: 'https://api.example.com/sse',
|
|
246
|
+
headers: {
|
|
247
|
+
'X-Custom-Header': 'value'
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const connection = new MCPConnection(config);
|
|
252
|
+
await connection.connect();
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## 核心概念
|
|
256
|
+
|
|
257
|
+
### MCPConnection
|
|
258
|
+
|
|
259
|
+
`MCPConnection` 是核心连接类,负责单个 MCP 服务的完整生命周期管理。
|
|
260
|
+
|
|
261
|
+
#### 主要方法
|
|
262
|
+
|
|
263
|
+
| 方法 | 说明 | 返回类型 |
|
|
264
|
+
|------|------|----------|
|
|
265
|
+
| `connect()` | 建立连接 | `Promise<void>` |
|
|
266
|
+
| `disconnect()` | 断开连接 | `Promise<void>` |
|
|
267
|
+
| `callTool(name, args)` | 调用工具 | `Promise<ToolCallResult>` |
|
|
268
|
+
| `getTools()` | 获取工具列表 | `Tool[]` |
|
|
269
|
+
| `getConfig()` | 获取配置 | `MCPServiceConfig` |
|
|
270
|
+
| `getStatus()` | 获取状态 | `MCPServiceStatus` |
|
|
271
|
+
| `isConnected()` | 检查连接状态 | `boolean` |
|
|
272
|
+
|
|
273
|
+
#### 连接状态
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
enum ConnectionState {
|
|
277
|
+
DISCONNECTED = 'disconnected',
|
|
278
|
+
CONNECTING = 'connecting',
|
|
279
|
+
CONNECTED = 'connected',
|
|
280
|
+
RECONNECTING = 'reconnecting',
|
|
281
|
+
FAILED = 'failed',
|
|
282
|
+
ERROR = 'error'
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### 事件回调
|
|
287
|
+
|
|
288
|
+
使用回调函数模式处理连接事件:
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
interface MCPServiceEventCallbacks {
|
|
292
|
+
onConnected?: (data: {
|
|
293
|
+
serviceName: string;
|
|
294
|
+
tools: Tool[];
|
|
295
|
+
connectionTime: Date;
|
|
296
|
+
}) => void;
|
|
297
|
+
|
|
298
|
+
onDisconnected?: (data: {
|
|
299
|
+
serviceName: string;
|
|
300
|
+
reason?: string;
|
|
301
|
+
disconnectionTime: Date;
|
|
302
|
+
}) => void;
|
|
303
|
+
|
|
304
|
+
onConnectionFailed?: (data: {
|
|
305
|
+
serviceName: string;
|
|
306
|
+
error: Error;
|
|
307
|
+
attempt: number;
|
|
308
|
+
}) => void;
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## 配置选项
|
|
313
|
+
|
|
314
|
+
### MCPServiceConfig
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
interface MCPServiceConfig {
|
|
318
|
+
// 基础配置
|
|
319
|
+
name: string; // 服务名称(必需)
|
|
320
|
+
type?: MCPTransportType; // 传输类型(可选,自动推断)
|
|
321
|
+
|
|
322
|
+
// STDIO 配置
|
|
323
|
+
command?: string; // 执行命令
|
|
324
|
+
args?: string[]; // 命令参数
|
|
325
|
+
env?: Record<string, string>; // 环境变量
|
|
326
|
+
|
|
327
|
+
// 网络配置
|
|
328
|
+
url?: string; // 服务 URL
|
|
329
|
+
|
|
330
|
+
// 认证配置
|
|
331
|
+
apiKey?: string; // API 密钥
|
|
332
|
+
headers?: Record<string, string>; // 自定义请求头
|
|
333
|
+
|
|
334
|
+
// 超时配置
|
|
335
|
+
timeout?: number; // 连接超时(毫秒,默认 10000)
|
|
336
|
+
|
|
337
|
+
// 重试配置
|
|
338
|
+
retryAttempts?: number; // 重试次数
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### 传输类型推断
|
|
343
|
+
|
|
344
|
+
如果不指定 `type`,库会自动根据配置推断:
|
|
345
|
+
|
|
346
|
+
- 有 `command` 字段 → `MCPTransportType.STDIO`
|
|
347
|
+
- 有 `url` 字段 → `MCPTransportType.STREAMABLE_HTTP` 或 `MCPTransportType.SSE`
|
|
348
|
+
|
|
349
|
+
## 工具调用
|
|
350
|
+
|
|
351
|
+
### 基础调用
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
const result = await connection.callTool('tool_name', {
|
|
355
|
+
parameter1: 'value1',
|
|
356
|
+
parameter2: 123
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
// 结果格式
|
|
360
|
+
interface ToolCallResult {
|
|
361
|
+
content: Array<{
|
|
362
|
+
type: string;
|
|
363
|
+
text: string;
|
|
364
|
+
}>;
|
|
365
|
+
isError?: boolean;
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### 参数验证
|
|
370
|
+
|
|
371
|
+
使用内置验证器确保参数正确:
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
import { validateToolCallParams, ToolCallValidationOptions } from '@xiaozhi-client/mcp-core';
|
|
375
|
+
|
|
376
|
+
const options: ToolCallValidationOptions = {
|
|
377
|
+
validateName: true,
|
|
378
|
+
validateArguments: true,
|
|
379
|
+
allowEmptyArguments: false
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
const validated = validateToolCallParams(params, options);
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### 错误处理
|
|
386
|
+
|
|
387
|
+
```typescript
|
|
388
|
+
import { ToolCallError, ToolCallErrorCode } from '@xiaozhi-client/mcp-core';
|
|
389
|
+
|
|
390
|
+
try {
|
|
391
|
+
await connection.callTool('tool_name', params);
|
|
392
|
+
} catch (error) {
|
|
393
|
+
if (error instanceof ToolCallError) {
|
|
394
|
+
switch (error.code) {
|
|
395
|
+
case ToolCallErrorCode.INVALID_PARAMS:
|
|
396
|
+
console.error('参数无效');
|
|
397
|
+
break;
|
|
398
|
+
case ToolCallErrorCode.TOOL_NOT_FOUND:
|
|
399
|
+
console.error('工具不存在');
|
|
400
|
+
break;
|
|
401
|
+
case ToolCallErrorCode.SERVICE_UNAVAILABLE:
|
|
402
|
+
console.error('服务不可用');
|
|
403
|
+
break;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
## 工具函数
|
|
410
|
+
|
|
411
|
+
### TypeFieldNormalizer
|
|
412
|
+
|
|
413
|
+
标准化类型字段格式:
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
import { normalizeTypeField } from '@xiaozhi-client/mcp-core';
|
|
417
|
+
|
|
418
|
+
// 自动转换各种格式到标准 kebab-case
|
|
419
|
+
normalizeTypeField({ type: 'streamableHttp' });
|
|
420
|
+
// => { type: 'streamable-http' }
|
|
421
|
+
|
|
422
|
+
normalizeTypeField({ type: 'S_SE' });
|
|
423
|
+
// => { type: 'sse' }
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### TransportFactory
|
|
427
|
+
|
|
428
|
+
直接创建传输层:
|
|
429
|
+
|
|
430
|
+
```typescript
|
|
431
|
+
import { TransportFactory } from '@xiaozhi-client/mcp-core';
|
|
432
|
+
|
|
433
|
+
const transport = TransportFactory.create(config);
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
## 完整示例
|
|
437
|
+
|
|
438
|
+
### 带完整错误处理的 MCP 客户端
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
import {
|
|
442
|
+
MCPConnection,
|
|
443
|
+
MCPTransportType,
|
|
444
|
+
ToolCallError,
|
|
445
|
+
ToolCallErrorCode
|
|
446
|
+
} from '@xiaozhi-client/mcp-core';
|
|
447
|
+
|
|
448
|
+
class MyMCPClient {
|
|
449
|
+
private connection: MCPConnection;
|
|
450
|
+
|
|
451
|
+
constructor(url: string, apiKey: string) {
|
|
452
|
+
const config = {
|
|
453
|
+
name: 'my-service',
|
|
454
|
+
type: MCPTransportType.STREAMABLE_HTTP,
|
|
455
|
+
url,
|
|
456
|
+
apiKey,
|
|
457
|
+
timeout: 30000
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
const callbacks = {
|
|
461
|
+
onConnected: (data) => {
|
|
462
|
+
console.log(`✅ 已连接,发现 ${data.tools.length} 个工具`);
|
|
463
|
+
},
|
|
464
|
+
onDisconnected: (data) => {
|
|
465
|
+
console.log(`❌ 已断开: ${data.reason}`);
|
|
466
|
+
},
|
|
467
|
+
onConnectionFailed: (data) => {
|
|
468
|
+
console.error(`⚠️ 连接失败: ${data.error.message}`);
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
this.connection = new MCPConnection(config, callbacks);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
async connect() {
|
|
476
|
+
try {
|
|
477
|
+
await this.connection.connect();
|
|
478
|
+
} catch (error) {
|
|
479
|
+
console.error('连接错误:', error);
|
|
480
|
+
throw error;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
async callTool(name: string, params: Record<string, unknown>) {
|
|
485
|
+
try {
|
|
486
|
+
const result = await this.connection.callTool(name, params);
|
|
487
|
+
|
|
488
|
+
if (result.isError) {
|
|
489
|
+
throw new Error(`工具调用失败: ${result.content[0]?.text}`);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return result.content[0]?.text;
|
|
493
|
+
} catch (error) {
|
|
494
|
+
if (error instanceof ToolCallError) {
|
|
495
|
+
this.handleToolCallError(error);
|
|
496
|
+
}
|
|
497
|
+
throw error;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
private handleToolCallError(error: ToolCallError) {
|
|
502
|
+
switch (error.code) {
|
|
503
|
+
case ToolCallErrorCode.INVALID_PARAMS:
|
|
504
|
+
console.error('❌ 参数错误:', error.message);
|
|
505
|
+
break;
|
|
506
|
+
case ToolCallErrorCode.TOOL_NOT_FOUND:
|
|
507
|
+
console.error('❌ 工具不存在:', error.message);
|
|
508
|
+
break;
|
|
509
|
+
case ToolCallErrorCode.TIMEOUT:
|
|
510
|
+
console.error('⏱️ 调用超时:', error.message);
|
|
511
|
+
break;
|
|
512
|
+
default:
|
|
513
|
+
console.error('❌ 未知错误:', error.message);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
async disconnect() {
|
|
518
|
+
await this.connection.disconnect();
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// 使用示例
|
|
523
|
+
const client = new MyMCPClient(
|
|
524
|
+
'https://api.example.com/mcp',
|
|
525
|
+
'your-api-key'
|
|
526
|
+
);
|
|
527
|
+
|
|
528
|
+
await client.connect();
|
|
529
|
+
const result = await client.callTool('calculate', {
|
|
530
|
+
expression: '2 + 2'
|
|
531
|
+
});
|
|
532
|
+
console.log('结果:', result);
|
|
533
|
+
await client.disconnect();
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
## API 参考
|
|
537
|
+
|
|
538
|
+
### 导出的类
|
|
539
|
+
|
|
540
|
+
```typescript
|
|
541
|
+
// 连接管理
|
|
542
|
+
export { MCPConnection } from './connection.js';
|
|
543
|
+
|
|
544
|
+
// 服务管理器
|
|
545
|
+
export { MCPManager, MCPServiceManager } from './manager.js';
|
|
546
|
+
|
|
547
|
+
// 错误处理
|
|
548
|
+
export { ToolCallError } from './types.js';
|
|
549
|
+
|
|
550
|
+
// 传输工厂
|
|
551
|
+
export { TransportFactory } from './transport-factory.js';
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
### 导出的类型
|
|
555
|
+
|
|
556
|
+
```typescript
|
|
557
|
+
export type {
|
|
558
|
+
// 配置
|
|
559
|
+
MCPServiceConfig,
|
|
560
|
+
ModelScopeSSEOptions,
|
|
561
|
+
UnifiedServerConfig,
|
|
562
|
+
|
|
563
|
+
// 状态
|
|
564
|
+
MCPServiceStatus,
|
|
565
|
+
MCPServiceConnectionStatus,
|
|
566
|
+
ManagerStatus,
|
|
567
|
+
UnifiedServerStatus,
|
|
568
|
+
|
|
569
|
+
// 工具
|
|
570
|
+
ToolInfo,
|
|
571
|
+
EnhancedToolInfo,
|
|
572
|
+
ToolCallResult,
|
|
573
|
+
ToolCallParams,
|
|
574
|
+
ValidatedToolCallParams,
|
|
575
|
+
ToolCallValidationOptions,
|
|
576
|
+
CustomMCPTool,
|
|
577
|
+
JSONSchema,
|
|
578
|
+
|
|
579
|
+
// 传输
|
|
580
|
+
MCPServerTransport,
|
|
581
|
+
|
|
582
|
+
// 事件
|
|
583
|
+
MCPServiceEventCallbacks,
|
|
584
|
+
} from './types.js';
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
### 导出的枚举
|
|
588
|
+
|
|
589
|
+
```typescript
|
|
590
|
+
export {
|
|
591
|
+
MCPTransportType, // 传输类型
|
|
592
|
+
ConnectionState, // 连接状态
|
|
593
|
+
ToolCallErrorCode, // 错误码
|
|
594
|
+
} from './types.js';
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
### 导出的工具函数
|
|
598
|
+
|
|
599
|
+
```typescript
|
|
600
|
+
export {
|
|
601
|
+
TypeFieldNormalizer,
|
|
602
|
+
normalizeTypeField,
|
|
603
|
+
validateToolCallParams,
|
|
604
|
+
inferTransportTypeFromUrl,
|
|
605
|
+
inferTransportTypeFromConfig,
|
|
606
|
+
} from './utils/index.js';
|
|
607
|
+
|
|
608
|
+
export {
|
|
609
|
+
isValidToolJSONSchema,
|
|
610
|
+
ensureToolJSONSchema,
|
|
611
|
+
} from './types.js';
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
## 开发指南
|
|
615
|
+
|
|
616
|
+
### 本地开发
|
|
617
|
+
|
|
618
|
+
```bash
|
|
619
|
+
# 克隆仓库
|
|
620
|
+
git clone https://github.com/shenjingnan/xiaozhi-client.git
|
|
621
|
+
cd xiaozhi-client/packages/mcp-core
|
|
622
|
+
|
|
623
|
+
# 安装依赖
|
|
624
|
+
pnpm install
|
|
625
|
+
|
|
626
|
+
# 开发模式(监听文件变化)
|
|
627
|
+
pnpm dev
|
|
628
|
+
|
|
629
|
+
# 构建
|
|
630
|
+
pnpm build
|
|
631
|
+
|
|
632
|
+
# 运行测试
|
|
633
|
+
pnpm test
|
|
634
|
+
|
|
635
|
+
# 类型检查
|
|
636
|
+
pnpm type-check
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
### 构建产物
|
|
640
|
+
|
|
641
|
+
```bash
|
|
642
|
+
dist/
|
|
643
|
+
├── index.js # ESM 格式的编译产物
|
|
644
|
+
├── index.d.ts # TypeScript 类型声明
|
|
645
|
+
└── index.js.map # Source Map
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
## 最佳实践
|
|
649
|
+
|
|
650
|
+
### 1. 连接管理
|
|
651
|
+
|
|
652
|
+
```typescript
|
|
653
|
+
// ✅ 推荐:使用 try-finally 确保断开连接
|
|
654
|
+
try {
|
|
655
|
+
await connection.connect();
|
|
656
|
+
// 使用连接...
|
|
657
|
+
} finally {
|
|
658
|
+
await connection.disconnect();
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// ❌ 避免:不处理断开连接
|
|
662
|
+
await connection.connect();
|
|
663
|
+
// 使用连接...
|
|
664
|
+
// 忘记断开
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
### 2. 错误处理
|
|
668
|
+
|
|
669
|
+
```typescript
|
|
670
|
+
// ✅ 推荐:捕获并处理特定错误
|
|
671
|
+
try {
|
|
672
|
+
await connection.callTool(name, params);
|
|
673
|
+
} catch (error) {
|
|
674
|
+
if (error instanceof ToolCallError) {
|
|
675
|
+
// 处理特定错误
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// ❌ 避免:忽略错误
|
|
680
|
+
await connection.callTool(name, params); // 可能抛出异常
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
### 3. 配置验证
|
|
684
|
+
|
|
685
|
+
```typescript
|
|
686
|
+
// ✅ 推荐:使用工厂函数推断类型
|
|
687
|
+
const config = {
|
|
688
|
+
name: 'service',
|
|
689
|
+
url: 'https://api.example.com'
|
|
690
|
+
// type 会自动推断为 STREAMABLE_HTTP
|
|
691
|
+
};
|
|
692
|
+
|
|
693
|
+
// ❌ 避免:手动指定容易出错的类型
|
|
694
|
+
const config = {
|
|
695
|
+
name: 'service',
|
|
696
|
+
type: MCPTransportType.SSE, // 如果 URL 不支持 SSE 会失败
|
|
697
|
+
url: 'https://api.example.com'
|
|
698
|
+
};
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
## 常见问题
|
|
702
|
+
|
|
703
|
+
### Q: 如何选择传输协议?
|
|
704
|
+
|
|
705
|
+
**A:**
|
|
706
|
+
- **本地进程** → 使用 `STDIO`
|
|
707
|
+
- **单向推送** → 使用 `SSE`
|
|
708
|
+
- **现代 HTTP** → 使用 `StreamableHTTP`(推荐)
|
|
709
|
+
|
|
710
|
+
### Q: 连接超时如何处理?
|
|
711
|
+
|
|
712
|
+
**A:** 在配置中设置 `timeout` 字段(单位:毫秒):
|
|
713
|
+
|
|
714
|
+
```typescript
|
|
715
|
+
{
|
|
716
|
+
timeout: 30000 // 30 秒超时
|
|
717
|
+
}
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
### Q: 如何处理连接失败?
|
|
721
|
+
|
|
722
|
+
**A:** 使用 `onConnectionFailed` 回调:
|
|
723
|
+
|
|
724
|
+
```typescript
|
|
725
|
+
const callbacks = {
|
|
726
|
+
onConnectionFailed: (data) => {
|
|
727
|
+
console.error(`第 ${data.attempt} 次尝试失败`);
|
|
728
|
+
// 实现重连逻辑
|
|
729
|
+
}
|
|
730
|
+
};
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
### Q: 工具调用支持流式响应吗?
|
|
734
|
+
|
|
735
|
+
**A:** 这取决于具体的 MCP 服务实现。`@xiaozhi-client/mcp-core` 支持所有 MCP SDK 定义的功能,包括流式响应。
|
|
736
|
+
|
|
737
|
+
## 许可证
|
|
738
|
+
|
|
739
|
+
[MIT](LICENSE)
|
|
740
|
+
|
|
741
|
+
## 相关资源
|
|
742
|
+
|
|
743
|
+
- [MCP 协议规范](https://modelcontextprotocol.io)
|
|
744
|
+
- [@xiaozhi-client/mcp-sdk](https://github.com/shenjingnan/xiaozhi-client)
|
|
745
|
+
- [问题反馈](https://github.com/shenjingnan/xiaozhi-client/issues)
|
|
746
|
+
|
|
747
|
+
---
|
|
748
|
+
|
|
749
|
+
**作者**: xiaozhi-client
|
|
750
|
+
**版本**: 1.9.7-beta.3
|