@xiaozhi-client/mcp-core 1.10.5 → 1.10.6-beta.1
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/package.json +2 -1
- package/dist/index.d.ts +0 -681
- package/dist/index.js +0 -922
- package/dist/index.js.map +0 -1
package/dist/index.js
DELETED
|
@@ -1,922 +0,0 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
-
|
|
4
|
-
// src/types.ts
|
|
5
|
-
var MCPTransportType = /* @__PURE__ */ ((MCPTransportType2) => {
|
|
6
|
-
MCPTransportType2["STDIO"] = "stdio";
|
|
7
|
-
MCPTransportType2["SSE"] = "sse";
|
|
8
|
-
MCPTransportType2["HTTP"] = "http";
|
|
9
|
-
return MCPTransportType2;
|
|
10
|
-
})(MCPTransportType || {});
|
|
11
|
-
var ConnectionState = /* @__PURE__ */ ((ConnectionState2) => {
|
|
12
|
-
ConnectionState2["DISCONNECTED"] = "disconnected";
|
|
13
|
-
ConnectionState2["CONNECTING"] = "connecting";
|
|
14
|
-
ConnectionState2["CONNECTED"] = "connected";
|
|
15
|
-
ConnectionState2["RECONNECTING"] = "reconnecting";
|
|
16
|
-
ConnectionState2["FAILED"] = "failed";
|
|
17
|
-
ConnectionState2["ERROR"] = "error";
|
|
18
|
-
return ConnectionState2;
|
|
19
|
-
})(ConnectionState || {});
|
|
20
|
-
function isValidToolJSONSchema(obj) {
|
|
21
|
-
return typeof obj === "object" && obj !== null && "type" in obj && obj.type === "object";
|
|
22
|
-
}
|
|
23
|
-
__name(isValidToolJSONSchema, "isValidToolJSONSchema");
|
|
24
|
-
function ensureToolJSONSchema(schema) {
|
|
25
|
-
if (isValidToolJSONSchema(schema)) {
|
|
26
|
-
return schema;
|
|
27
|
-
}
|
|
28
|
-
return {
|
|
29
|
-
type: "object",
|
|
30
|
-
properties: {},
|
|
31
|
-
required: [],
|
|
32
|
-
additionalProperties: true
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
__name(ensureToolJSONSchema, "ensureToolJSONSchema");
|
|
36
|
-
var ToolCallErrorCode = /* @__PURE__ */ ((ToolCallErrorCode2) => {
|
|
37
|
-
ToolCallErrorCode2[ToolCallErrorCode2["INVALID_PARAMS"] = -32602] = "INVALID_PARAMS";
|
|
38
|
-
ToolCallErrorCode2[ToolCallErrorCode2["TOOL_NOT_FOUND"] = -32601] = "TOOL_NOT_FOUND";
|
|
39
|
-
ToolCallErrorCode2[ToolCallErrorCode2["SERVICE_UNAVAILABLE"] = -32001] = "SERVICE_UNAVAILABLE";
|
|
40
|
-
ToolCallErrorCode2[ToolCallErrorCode2["TIMEOUT"] = -32002] = "TIMEOUT";
|
|
41
|
-
ToolCallErrorCode2[ToolCallErrorCode2["TOOL_EXECUTION_ERROR"] = -32e3] = "TOOL_EXECUTION_ERROR";
|
|
42
|
-
return ToolCallErrorCode2;
|
|
43
|
-
})(ToolCallErrorCode || {});
|
|
44
|
-
var ToolCallError = class extends Error {
|
|
45
|
-
constructor(code, message, data) {
|
|
46
|
-
super(message);
|
|
47
|
-
this.code = code;
|
|
48
|
-
this.data = data;
|
|
49
|
-
this.name = "ToolCallError";
|
|
50
|
-
}
|
|
51
|
-
static {
|
|
52
|
-
__name(this, "ToolCallError");
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
// src/connection.ts
|
|
57
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
58
|
-
|
|
59
|
-
// src/transport-factory.ts
|
|
60
|
-
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
61
|
-
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
62
|
-
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
63
|
-
import { EventSource } from "eventsource";
|
|
64
|
-
var globalThisAny = typeof globalThis !== "undefined" ? globalThis : global;
|
|
65
|
-
if (typeof globalThisAny !== "undefined" && !globalThisAny.EventSource) {
|
|
66
|
-
globalThisAny.EventSource = EventSource;
|
|
67
|
-
}
|
|
68
|
-
function createTransport(config) {
|
|
69
|
-
switch (config.type) {
|
|
70
|
-
case "stdio" /* STDIO */:
|
|
71
|
-
return createStdioTransport(config);
|
|
72
|
-
case "sse" /* SSE */:
|
|
73
|
-
return createSSETransport(config);
|
|
74
|
-
case "http" /* HTTP */:
|
|
75
|
-
return createHTTPTransport(config);
|
|
76
|
-
default:
|
|
77
|
-
throw new Error(`\u4E0D\u652F\u6301\u7684\u4F20\u8F93\u7C7B\u578B: ${config.type}`);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
__name(createTransport, "createTransport");
|
|
81
|
-
function createStdioTransport(config) {
|
|
82
|
-
if (!config.command) {
|
|
83
|
-
throw new Error("stdio transport \u9700\u8981 command \u914D\u7F6E");
|
|
84
|
-
}
|
|
85
|
-
return new StdioClientTransport({
|
|
86
|
-
command: config.command,
|
|
87
|
-
args: config.args || [],
|
|
88
|
-
env: config.env
|
|
89
|
-
// 传递环境变量
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
__name(createStdioTransport, "createStdioTransport");
|
|
93
|
-
function createSSETransport(config) {
|
|
94
|
-
if (!config.url) {
|
|
95
|
-
throw new Error("SSE transport \u9700\u8981 URL \u914D\u7F6E");
|
|
96
|
-
}
|
|
97
|
-
const url = new URL(config.url);
|
|
98
|
-
const options = createSSEOptions(config);
|
|
99
|
-
return new SSEClientTransport(url, options);
|
|
100
|
-
}
|
|
101
|
-
__name(createSSETransport, "createSSETransport");
|
|
102
|
-
function createHTTPTransport(config) {
|
|
103
|
-
if (!config.url) {
|
|
104
|
-
throw new Error("HTTP transport \u9700\u8981 URL \u914D\u7F6E");
|
|
105
|
-
}
|
|
106
|
-
const url = new URL(config.url);
|
|
107
|
-
const options = createStreamableHTTPOptions(config);
|
|
108
|
-
return new StreamableHTTPClientTransport(url, options);
|
|
109
|
-
}
|
|
110
|
-
__name(createHTTPTransport, "createHTTPTransport");
|
|
111
|
-
function createSSEOptions(config) {
|
|
112
|
-
const options = {};
|
|
113
|
-
if (config.apiKey) {
|
|
114
|
-
options.requestInit = {
|
|
115
|
-
headers: {
|
|
116
|
-
Authorization: `Bearer ${config.apiKey}`,
|
|
117
|
-
...config.headers
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
} else if (config.headers) {
|
|
121
|
-
options.requestInit = {
|
|
122
|
-
headers: config.headers
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
return options;
|
|
126
|
-
}
|
|
127
|
-
__name(createSSEOptions, "createSSEOptions");
|
|
128
|
-
function createStreamableHTTPOptions(config) {
|
|
129
|
-
const options = {};
|
|
130
|
-
if (config.apiKey) {
|
|
131
|
-
options.requestInit = {
|
|
132
|
-
headers: {
|
|
133
|
-
Authorization: `Bearer ${config.apiKey}`,
|
|
134
|
-
...config.headers
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
} else if (config.headers) {
|
|
138
|
-
options.requestInit = {
|
|
139
|
-
headers: config.headers
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
return options;
|
|
143
|
-
}
|
|
144
|
-
__name(createStreamableHTTPOptions, "createStreamableHTTPOptions");
|
|
145
|
-
function validateConfig(config) {
|
|
146
|
-
if (config.type && !Object.values(MCPTransportType).includes(config.type)) {
|
|
147
|
-
throw new Error(`\u4E0D\u652F\u6301\u7684\u4F20\u8F93\u7C7B\u578B: ${config.type}`);
|
|
148
|
-
}
|
|
149
|
-
if (!config.type) {
|
|
150
|
-
throw new Error("\u4F20\u8F93\u7C7B\u578B\u672A\u8BBE\u7F6E\uFF0C\u8FD9\u5E94\u8BE5\u5728 inferTransportType \u4E2D\u5904\u7406");
|
|
151
|
-
}
|
|
152
|
-
switch (config.type) {
|
|
153
|
-
case "stdio" /* STDIO */:
|
|
154
|
-
if (!config.command) {
|
|
155
|
-
throw new Error("stdio \u7C7B\u578B\u9700\u8981 command \u5B57\u6BB5");
|
|
156
|
-
}
|
|
157
|
-
break;
|
|
158
|
-
case "sse" /* SSE */:
|
|
159
|
-
if (config.url === void 0 || config.url === null) {
|
|
160
|
-
throw new Error(`${config.type} \u7C7B\u578B\u9700\u8981 url \u5B57\u6BB5`);
|
|
161
|
-
}
|
|
162
|
-
break;
|
|
163
|
-
case "http" /* HTTP */:
|
|
164
|
-
if (config.url === void 0 || config.url === null) {
|
|
165
|
-
throw new Error(`${config.type} \u7C7B\u578B\u9700\u8981 url \u5B57\u6BB5`);
|
|
166
|
-
}
|
|
167
|
-
break;
|
|
168
|
-
default:
|
|
169
|
-
throw new Error(`\u4E0D\u652F\u6301\u7684\u4F20\u8F93\u7C7B\u578B: ${config.type}`);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
__name(validateConfig, "validateConfig");
|
|
173
|
-
function getSupportedTypes() {
|
|
174
|
-
return [
|
|
175
|
-
"stdio" /* STDIO */,
|
|
176
|
-
"sse" /* SSE */,
|
|
177
|
-
"http" /* HTTP */
|
|
178
|
-
];
|
|
179
|
-
}
|
|
180
|
-
__name(getSupportedTypes, "getSupportedTypes");
|
|
181
|
-
var TransportFactory = {
|
|
182
|
-
create: createTransport,
|
|
183
|
-
validateConfig,
|
|
184
|
-
getSupportedTypes
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
// src/utils/type-normalizer.ts
|
|
188
|
-
var TypeFieldNormalizer;
|
|
189
|
-
((TypeFieldNormalizer2) => {
|
|
190
|
-
function normalizeTypeField2(config) {
|
|
191
|
-
if (!config || typeof config !== "object") {
|
|
192
|
-
return config;
|
|
193
|
-
}
|
|
194
|
-
const normalizedConfig = JSON.parse(JSON.stringify(config));
|
|
195
|
-
if (!("type" in normalizedConfig)) {
|
|
196
|
-
return normalizedConfig;
|
|
197
|
-
}
|
|
198
|
-
const originalType = normalizedConfig.type;
|
|
199
|
-
if (originalType === "stdio" || originalType === "sse" || originalType === "http") {
|
|
200
|
-
return normalizedConfig;
|
|
201
|
-
}
|
|
202
|
-
const normalizedType = normalizeTypeValue(originalType);
|
|
203
|
-
if (normalizedType === "stdio" || normalizedType === "sse" || normalizedType === "http") {
|
|
204
|
-
normalizedConfig.type = normalizedType;
|
|
205
|
-
}
|
|
206
|
-
return normalizedConfig;
|
|
207
|
-
}
|
|
208
|
-
TypeFieldNormalizer2.normalizeTypeField = normalizeTypeField2;
|
|
209
|
-
__name(normalizeTypeField2, "normalizeTypeField");
|
|
210
|
-
function normalizeTypeValue(type) {
|
|
211
|
-
if (type === "http" || type === "streamable-http" || type === "streamable_http" || type === "streamableHttp") {
|
|
212
|
-
return "http";
|
|
213
|
-
}
|
|
214
|
-
if (type === "sse" || type === "s_se" || type === "s-se") {
|
|
215
|
-
return "sse";
|
|
216
|
-
}
|
|
217
|
-
if (type === "stdio") {
|
|
218
|
-
return "stdio";
|
|
219
|
-
}
|
|
220
|
-
return convertToStandardFormat(type);
|
|
221
|
-
}
|
|
222
|
-
TypeFieldNormalizer2.normalizeTypeValue = normalizeTypeValue;
|
|
223
|
-
__name(normalizeTypeValue, "normalizeTypeValue");
|
|
224
|
-
function convertToStandardFormat(str) {
|
|
225
|
-
const lowered = str.toLowerCase();
|
|
226
|
-
if (lowered.includes("http") || lowered.includes("streamable")) {
|
|
227
|
-
return "http";
|
|
228
|
-
}
|
|
229
|
-
if (lowered.includes("sse")) {
|
|
230
|
-
return "sse";
|
|
231
|
-
}
|
|
232
|
-
if (lowered.includes("stdio")) {
|
|
233
|
-
return "stdio";
|
|
234
|
-
}
|
|
235
|
-
return str;
|
|
236
|
-
}
|
|
237
|
-
__name(convertToStandardFormat, "convertToStandardFormat");
|
|
238
|
-
})(TypeFieldNormalizer || (TypeFieldNormalizer = {}));
|
|
239
|
-
function normalizeTypeField(config) {
|
|
240
|
-
return TypeFieldNormalizer.normalizeTypeField(config);
|
|
241
|
-
}
|
|
242
|
-
__name(normalizeTypeField, "normalizeTypeField");
|
|
243
|
-
|
|
244
|
-
// src/utils/validators.ts
|
|
245
|
-
function inferTransportTypeFromUrl(url, options) {
|
|
246
|
-
try {
|
|
247
|
-
const parsedUrl = new URL(url);
|
|
248
|
-
const pathname = parsedUrl.pathname;
|
|
249
|
-
if (pathname.endsWith("/sse")) {
|
|
250
|
-
return "sse" /* SSE */;
|
|
251
|
-
}
|
|
252
|
-
if (pathname.endsWith("/mcp")) {
|
|
253
|
-
return "http" /* HTTP */;
|
|
254
|
-
}
|
|
255
|
-
if (options?.serviceName) {
|
|
256
|
-
}
|
|
257
|
-
return "http" /* HTTP */;
|
|
258
|
-
} catch (error) {
|
|
259
|
-
if (options?.serviceName) {
|
|
260
|
-
}
|
|
261
|
-
return "http" /* HTTP */;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
__name(inferTransportTypeFromUrl, "inferTransportTypeFromUrl");
|
|
265
|
-
function inferTransportTypeFromConfig(config, serviceName) {
|
|
266
|
-
if (config.type) {
|
|
267
|
-
const normalizedConfig = TypeFieldNormalizer.normalizeTypeField(config);
|
|
268
|
-
return normalizedConfig;
|
|
269
|
-
}
|
|
270
|
-
if (config.command) {
|
|
271
|
-
return {
|
|
272
|
-
...config,
|
|
273
|
-
type: "stdio" /* STDIO */
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
if (config.url !== void 0 && config.url !== null) {
|
|
277
|
-
const inferredType = inferTransportTypeFromUrl(config.url, {
|
|
278
|
-
serviceName
|
|
279
|
-
});
|
|
280
|
-
return {
|
|
281
|
-
...config,
|
|
282
|
-
type: inferredType
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
throw new Error(
|
|
286
|
-
`\u65E0\u6CD5\u4E3A\u670D\u52A1 ${serviceName || "\u672A\u77E5"} \u63A8\u65AD\u4F20\u8F93\u7C7B\u578B\u3002\u8BF7\u663E\u5F0F\u6307\u5B9A type \u5B57\u6BB5\uFF0C\u6216\u63D0\u4F9B command/url \u914D\u7F6E`
|
|
287
|
-
);
|
|
288
|
-
}
|
|
289
|
-
__name(inferTransportTypeFromConfig, "inferTransportTypeFromConfig");
|
|
290
|
-
function validateToolCallParams(params, options) {
|
|
291
|
-
const opts = {
|
|
292
|
-
validateName: true,
|
|
293
|
-
validateArguments: true,
|
|
294
|
-
allowEmptyArguments: true,
|
|
295
|
-
...options
|
|
296
|
-
};
|
|
297
|
-
if (!params || typeof params !== "object") {
|
|
298
|
-
throw new ToolCallError(
|
|
299
|
-
-32602 /* INVALID_PARAMS */,
|
|
300
|
-
"\u8BF7\u6C42\u53C2\u6570\u5FC5\u987B\u662F\u5BF9\u8C61"
|
|
301
|
-
);
|
|
302
|
-
}
|
|
303
|
-
const paramsObj = params;
|
|
304
|
-
if (opts.validateName) {
|
|
305
|
-
if (!paramsObj.name || typeof paramsObj.name !== "string") {
|
|
306
|
-
throw new ToolCallError(
|
|
307
|
-
-32602 /* INVALID_PARAMS */,
|
|
308
|
-
"\u5DE5\u5177\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32"
|
|
309
|
-
);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
if (opts.validateArguments && paramsObj.arguments !== void 0 && paramsObj.arguments !== null) {
|
|
313
|
-
if (typeof paramsObj.arguments !== "object" || Array.isArray(paramsObj.arguments)) {
|
|
314
|
-
throw new ToolCallError(
|
|
315
|
-
-32602 /* INVALID_PARAMS */,
|
|
316
|
-
"\u5DE5\u5177\u53C2\u6570\u5FC5\u987B\u662F\u5BF9\u8C61"
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
if (!opts.allowEmptyArguments && paramsObj.arguments !== void 0 && paramsObj.arguments !== null) {
|
|
321
|
-
const argsObj = paramsObj.arguments;
|
|
322
|
-
if (Object.keys(argsObj).length === 0) {
|
|
323
|
-
throw new ToolCallError(
|
|
324
|
-
-32602 /* INVALID_PARAMS */,
|
|
325
|
-
"\u5DE5\u5177\u53C2\u6570\u4E0D\u80FD\u4E3A\u7A7A"
|
|
326
|
-
);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
if (opts.customValidator) {
|
|
330
|
-
const error = opts.customValidator(paramsObj);
|
|
331
|
-
if (error) {
|
|
332
|
-
throw new ToolCallError(-32602 /* INVALID_PARAMS */, error);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
return {
|
|
336
|
-
name: paramsObj.name,
|
|
337
|
-
arguments: paramsObj.arguments
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
__name(validateToolCallParams, "validateToolCallParams");
|
|
341
|
-
|
|
342
|
-
// src/connection.ts
|
|
343
|
-
var MCPConnection = class {
|
|
344
|
-
static {
|
|
345
|
-
__name(this, "MCPConnection");
|
|
346
|
-
}
|
|
347
|
-
name;
|
|
348
|
-
// 服务名称(独立字段)
|
|
349
|
-
config;
|
|
350
|
-
client = null;
|
|
351
|
-
transport = null;
|
|
352
|
-
tools = /* @__PURE__ */ new Map();
|
|
353
|
-
connectionState = "disconnected" /* DISCONNECTED */;
|
|
354
|
-
connectionTimeout = null;
|
|
355
|
-
initialized = false;
|
|
356
|
-
callbacks;
|
|
357
|
-
// 心跳检测相关
|
|
358
|
-
heartbeatTimer = null;
|
|
359
|
-
heartbeatConfig;
|
|
360
|
-
constructor(name, config, callbacks) {
|
|
361
|
-
this.name = name;
|
|
362
|
-
this.config = inferTransportTypeFromConfig(config, name);
|
|
363
|
-
this.callbacks = callbacks;
|
|
364
|
-
this.heartbeatConfig = {
|
|
365
|
-
enabled: config.heartbeat?.enabled ?? true,
|
|
366
|
-
// 默认启用
|
|
367
|
-
interval: config.heartbeat?.interval ?? 30 * 1e3
|
|
368
|
-
// 默认 30 秒
|
|
369
|
-
};
|
|
370
|
-
this.validateConfig();
|
|
371
|
-
}
|
|
372
|
-
/**
|
|
373
|
-
* 验证配置
|
|
374
|
-
*/
|
|
375
|
-
validateConfig() {
|
|
376
|
-
if (!this.name || typeof this.name !== "string") {
|
|
377
|
-
throw new Error("\u670D\u52A1\u540D\u79F0\u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32");
|
|
378
|
-
}
|
|
379
|
-
const fullConfig = {
|
|
380
|
-
name: this.name,
|
|
381
|
-
...this.config
|
|
382
|
-
};
|
|
383
|
-
TransportFactory.validateConfig(fullConfig);
|
|
384
|
-
}
|
|
385
|
-
/**
|
|
386
|
-
* 连接到 MCP 服务
|
|
387
|
-
*/
|
|
388
|
-
async connect() {
|
|
389
|
-
if (this.connectionState === "connecting" /* CONNECTING */) {
|
|
390
|
-
throw new Error("\u8FDE\u63A5\u6B63\u5728\u8FDB\u884C\u4E2D\uFF0C\u8BF7\u7B49\u5F85\u8FDE\u63A5\u5B8C\u6210");
|
|
391
|
-
}
|
|
392
|
-
this.cleanupConnection();
|
|
393
|
-
return this.attemptConnection();
|
|
394
|
-
}
|
|
395
|
-
/**
|
|
396
|
-
* 尝试建立连接
|
|
397
|
-
*/
|
|
398
|
-
async attemptConnection() {
|
|
399
|
-
this.connectionState = "connecting" /* CONNECTING */;
|
|
400
|
-
return new Promise((resolve, reject) => {
|
|
401
|
-
const CONNECTION_TIMEOUT = 3e4;
|
|
402
|
-
this.connectionTimeout = setTimeout(() => {
|
|
403
|
-
const error = new Error(`\u8FDE\u63A5\u8D85\u65F6 (${CONNECTION_TIMEOUT}ms)`);
|
|
404
|
-
this.handleConnectionError(error);
|
|
405
|
-
reject(error);
|
|
406
|
-
}, CONNECTION_TIMEOUT);
|
|
407
|
-
try {
|
|
408
|
-
this.client = new Client(
|
|
409
|
-
{
|
|
410
|
-
name: `xiaozhi-${this.name}-client`,
|
|
411
|
-
version: "1.0.0"
|
|
412
|
-
},
|
|
413
|
-
{
|
|
414
|
-
capabilities: {}
|
|
415
|
-
}
|
|
416
|
-
);
|
|
417
|
-
const fullConfig = {
|
|
418
|
-
name: this.name,
|
|
419
|
-
...this.config
|
|
420
|
-
};
|
|
421
|
-
this.transport = TransportFactory.create(fullConfig);
|
|
422
|
-
this.client.connect(this.transport).then(async () => {
|
|
423
|
-
this.handleConnectionSuccess();
|
|
424
|
-
await this.refreshTools();
|
|
425
|
-
this.callbacks?.onConnected?.({
|
|
426
|
-
serviceName: this.name,
|
|
427
|
-
tools: this.getTools(),
|
|
428
|
-
connectionTime: /* @__PURE__ */ new Date()
|
|
429
|
-
});
|
|
430
|
-
resolve();
|
|
431
|
-
}).catch((error) => {
|
|
432
|
-
this.handleConnectionError(error);
|
|
433
|
-
reject(error);
|
|
434
|
-
});
|
|
435
|
-
} catch (error) {
|
|
436
|
-
this.handleConnectionError(error);
|
|
437
|
-
reject(error);
|
|
438
|
-
}
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
/**
|
|
442
|
-
* 处理连接成功
|
|
443
|
-
*/
|
|
444
|
-
handleConnectionSuccess() {
|
|
445
|
-
if (this.connectionTimeout) {
|
|
446
|
-
clearTimeout(this.connectionTimeout);
|
|
447
|
-
this.connectionTimeout = null;
|
|
448
|
-
}
|
|
449
|
-
this.connectionState = "connected" /* CONNECTED */;
|
|
450
|
-
this.initialized = true;
|
|
451
|
-
this.startHeartbeat();
|
|
452
|
-
}
|
|
453
|
-
/**
|
|
454
|
-
* 处理连接错误
|
|
455
|
-
*/
|
|
456
|
-
handleConnectionError(error) {
|
|
457
|
-
this.connectionState = "disconnected" /* DISCONNECTED */;
|
|
458
|
-
this.initialized = false;
|
|
459
|
-
if (this.connectionTimeout) {
|
|
460
|
-
clearTimeout(this.connectionTimeout);
|
|
461
|
-
this.connectionTimeout = null;
|
|
462
|
-
}
|
|
463
|
-
this.cleanupConnection();
|
|
464
|
-
this.callbacks?.onConnectionFailed?.({
|
|
465
|
-
serviceName: this.name,
|
|
466
|
-
error,
|
|
467
|
-
attempt: 0
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
/**
|
|
471
|
-
* 清理连接资源
|
|
472
|
-
*/
|
|
473
|
-
cleanupConnection() {
|
|
474
|
-
this.stopHeartbeat();
|
|
475
|
-
if (this.client) {
|
|
476
|
-
try {
|
|
477
|
-
this.client.close().catch(() => {
|
|
478
|
-
});
|
|
479
|
-
} catch (error) {
|
|
480
|
-
}
|
|
481
|
-
this.client = null;
|
|
482
|
-
}
|
|
483
|
-
this.transport = null;
|
|
484
|
-
if (this.connectionTimeout) {
|
|
485
|
-
clearTimeout(this.connectionTimeout);
|
|
486
|
-
this.connectionTimeout = null;
|
|
487
|
-
}
|
|
488
|
-
this.initialized = false;
|
|
489
|
-
}
|
|
490
|
-
/**
|
|
491
|
-
* 刷新工具列表
|
|
492
|
-
*/
|
|
493
|
-
async refreshTools() {
|
|
494
|
-
if (!this.client) {
|
|
495
|
-
throw new Error("\u5BA2\u6237\u7AEF\u672A\u521D\u59CB\u5316");
|
|
496
|
-
}
|
|
497
|
-
try {
|
|
498
|
-
const toolsResult = await this.client.listTools();
|
|
499
|
-
const tools = toolsResult.tools || [];
|
|
500
|
-
this.tools.clear();
|
|
501
|
-
for (const tool of tools) {
|
|
502
|
-
this.tools.set(tool.name, tool);
|
|
503
|
-
}
|
|
504
|
-
} catch (error) {
|
|
505
|
-
throw error;
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
/**
|
|
509
|
-
* 断开连接
|
|
510
|
-
*/
|
|
511
|
-
async disconnect() {
|
|
512
|
-
this.cleanupConnection();
|
|
513
|
-
this.connectionState = "disconnected" /* DISCONNECTED */;
|
|
514
|
-
this.callbacks?.onDisconnected?.({
|
|
515
|
-
serviceName: this.name,
|
|
516
|
-
reason: "\u624B\u52A8\u65AD\u5F00",
|
|
517
|
-
disconnectionTime: /* @__PURE__ */ new Date()
|
|
518
|
-
});
|
|
519
|
-
}
|
|
520
|
-
/**
|
|
521
|
-
* 获取工具列表
|
|
522
|
-
*/
|
|
523
|
-
getTools() {
|
|
524
|
-
return Array.from(this.tools.values());
|
|
525
|
-
}
|
|
526
|
-
/**
|
|
527
|
-
* 检测是否为会话过期错误
|
|
528
|
-
*/
|
|
529
|
-
isSessionExpiredError(error) {
|
|
530
|
-
if (error instanceof Error) {
|
|
531
|
-
const message = error.message.toLowerCase();
|
|
532
|
-
return message.includes("session expired") || message.includes("\u4F1A\u8BDD\u8FC7\u671F") || message.includes("401") || message.includes("unauthorized");
|
|
533
|
-
}
|
|
534
|
-
return false;
|
|
535
|
-
}
|
|
536
|
-
/**
|
|
537
|
-
* 自动重连
|
|
538
|
-
*/
|
|
539
|
-
async reconnect() {
|
|
540
|
-
this.connectionState = "reconnecting" /* RECONNECTING */;
|
|
541
|
-
this.cleanupConnection();
|
|
542
|
-
return this.attemptConnection();
|
|
543
|
-
}
|
|
544
|
-
/**
|
|
545
|
-
* 启动心跳检测
|
|
546
|
-
*/
|
|
547
|
-
async startHeartbeat() {
|
|
548
|
-
if (this.config.type === "stdio") {
|
|
549
|
-
return;
|
|
550
|
-
}
|
|
551
|
-
if (!this.heartbeatConfig?.enabled) {
|
|
552
|
-
return;
|
|
553
|
-
}
|
|
554
|
-
const interval = this.heartbeatConfig?.interval ?? 30 * 1e3;
|
|
555
|
-
this.heartbeatTimer = setInterval(() => {
|
|
556
|
-
this.performHeartbeat().catch((error) => {
|
|
557
|
-
});
|
|
558
|
-
}, interval);
|
|
559
|
-
}
|
|
560
|
-
/**
|
|
561
|
-
* 执行一次心跳检查
|
|
562
|
-
*/
|
|
563
|
-
async performHeartbeat() {
|
|
564
|
-
if (!this.client) {
|
|
565
|
-
return;
|
|
566
|
-
}
|
|
567
|
-
try {
|
|
568
|
-
await this.client.ping();
|
|
569
|
-
} catch (error) {
|
|
570
|
-
await this.reconnect();
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
/**
|
|
574
|
-
* 停止心跳检测
|
|
575
|
-
*/
|
|
576
|
-
stopHeartbeat() {
|
|
577
|
-
if (this.heartbeatTimer) {
|
|
578
|
-
clearInterval(this.heartbeatTimer);
|
|
579
|
-
this.heartbeatTimer = null;
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
/**
|
|
583
|
-
* 调用工具
|
|
584
|
-
*/
|
|
585
|
-
async callTool(name, arguments_) {
|
|
586
|
-
if (!this.client) {
|
|
587
|
-
throw new Error(`\u670D\u52A1 ${this.name} \u672A\u8FDE\u63A5`);
|
|
588
|
-
}
|
|
589
|
-
if (!this.tools.has(name)) {
|
|
590
|
-
throw new Error(`\u5DE5\u5177 ${name} \u5728\u670D\u52A1 ${this.name} \u4E2D\u4E0D\u5B58\u5728`);
|
|
591
|
-
}
|
|
592
|
-
try {
|
|
593
|
-
const result = await this.client.callTool({
|
|
594
|
-
name,
|
|
595
|
-
arguments: arguments_ || {}
|
|
596
|
-
});
|
|
597
|
-
return result;
|
|
598
|
-
} catch (error) {
|
|
599
|
-
if (this.isSessionExpiredError(error)) {
|
|
600
|
-
await this.reconnect();
|
|
601
|
-
return await this.client.callTool({
|
|
602
|
-
name,
|
|
603
|
-
arguments: arguments_ || {}
|
|
604
|
-
});
|
|
605
|
-
}
|
|
606
|
-
throw error;
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
/**
|
|
610
|
-
* 获取服务配置
|
|
611
|
-
*/
|
|
612
|
-
getConfig() {
|
|
613
|
-
return {
|
|
614
|
-
name: this.name,
|
|
615
|
-
...this.config
|
|
616
|
-
};
|
|
617
|
-
}
|
|
618
|
-
/**
|
|
619
|
-
* 获取服务状态
|
|
620
|
-
*/
|
|
621
|
-
getStatus() {
|
|
622
|
-
return {
|
|
623
|
-
name: this.name,
|
|
624
|
-
connected: this.connectionState === "connected" /* CONNECTED */,
|
|
625
|
-
initialized: this.initialized,
|
|
626
|
-
transportType: this.config.type || "http" /* HTTP */,
|
|
627
|
-
toolCount: this.tools.size,
|
|
628
|
-
connectionState: this.connectionState
|
|
629
|
-
};
|
|
630
|
-
}
|
|
631
|
-
/**
|
|
632
|
-
* 检查是否已连接
|
|
633
|
-
*/
|
|
634
|
-
isConnected() {
|
|
635
|
-
return this.connectionState === "connected" /* CONNECTED */ && this.initialized;
|
|
636
|
-
}
|
|
637
|
-
};
|
|
638
|
-
|
|
639
|
-
// src/manager.ts
|
|
640
|
-
import { EventEmitter } from "events";
|
|
641
|
-
var MCPManager = class extends EventEmitter {
|
|
642
|
-
static {
|
|
643
|
-
__name(this, "MCPManager");
|
|
644
|
-
}
|
|
645
|
-
connections = /* @__PURE__ */ new Map();
|
|
646
|
-
configs = /* @__PURE__ */ new Map();
|
|
647
|
-
constructor() {
|
|
648
|
-
super();
|
|
649
|
-
}
|
|
650
|
-
/**
|
|
651
|
-
* 添加 MCP 服务器配置
|
|
652
|
-
* @param name 服务器名称
|
|
653
|
-
* @param config 服务器配置
|
|
654
|
-
*
|
|
655
|
-
* @example
|
|
656
|
-
* ```typescript
|
|
657
|
-
* // 添加 stdio 服务
|
|
658
|
-
* manager.addServer('calculator', {
|
|
659
|
-
* type: 'stdio',
|
|
660
|
-
* command: 'node',
|
|
661
|
-
* args: ['calculator.js']
|
|
662
|
-
* });
|
|
663
|
-
*
|
|
664
|
-
* // 添加 HTTP 服务
|
|
665
|
-
* manager.addServer('web-search', {
|
|
666
|
-
* type: 'http',
|
|
667
|
-
* url: 'https://api.example.com/mcp',
|
|
668
|
-
* headers: {
|
|
669
|
-
* Authorization: 'Bearer your-api-key'
|
|
670
|
-
* }
|
|
671
|
-
* });
|
|
672
|
-
* ```
|
|
673
|
-
*/
|
|
674
|
-
addServer(name, config) {
|
|
675
|
-
if (this.configs.has(name)) {
|
|
676
|
-
throw new Error(`\u670D\u52A1 ${name} \u5DF2\u5B58\u5728`);
|
|
677
|
-
}
|
|
678
|
-
const normalizedConfig = { ...config };
|
|
679
|
-
if (config.type) {
|
|
680
|
-
const typeStr = String(config.type);
|
|
681
|
-
if (typeStr === "http") {
|
|
682
|
-
normalizedConfig.type = "http" /* HTTP */;
|
|
683
|
-
} else if (typeStr === "sse") {
|
|
684
|
-
normalizedConfig.type = "sse" /* SSE */;
|
|
685
|
-
} else {
|
|
686
|
-
normalizedConfig.type = config.type;
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
this.configs.set(name, normalizedConfig);
|
|
690
|
-
}
|
|
691
|
-
/**
|
|
692
|
-
* 移除服务器配置
|
|
693
|
-
* @param name 服务器名称
|
|
694
|
-
*/
|
|
695
|
-
removeServer(name) {
|
|
696
|
-
return this.configs.delete(name);
|
|
697
|
-
}
|
|
698
|
-
/**
|
|
699
|
-
* 连接所有已添加的 MCP 服务
|
|
700
|
-
* 所有服务并行连接,单个服务失败不会影响其他服务
|
|
701
|
-
*
|
|
702
|
-
* @example
|
|
703
|
-
* ```typescript
|
|
704
|
-
* await manager.connect();
|
|
705
|
-
* ```
|
|
706
|
-
*/
|
|
707
|
-
async connect() {
|
|
708
|
-
this.emit("connect");
|
|
709
|
-
const promises = Array.from(this.configs.entries()).map(
|
|
710
|
-
async ([name, config]) => {
|
|
711
|
-
try {
|
|
712
|
-
const connection = new MCPConnection(name, config, {
|
|
713
|
-
onConnected: /* @__PURE__ */ __name((data) => {
|
|
714
|
-
this.emit("connected", {
|
|
715
|
-
serverName: data.serviceName,
|
|
716
|
-
tools: data.tools
|
|
717
|
-
});
|
|
718
|
-
}, "onConnected"),
|
|
719
|
-
onDisconnected: /* @__PURE__ */ __name((data) => {
|
|
720
|
-
this.emit("disconnected", {
|
|
721
|
-
serverName: data.serviceName,
|
|
722
|
-
reason: data.reason
|
|
723
|
-
});
|
|
724
|
-
}, "onDisconnected"),
|
|
725
|
-
onConnectionFailed: /* @__PURE__ */ __name((data) => {
|
|
726
|
-
this.emit("error", {
|
|
727
|
-
serverName: data.serviceName,
|
|
728
|
-
error: data.error
|
|
729
|
-
});
|
|
730
|
-
}, "onConnectionFailed")
|
|
731
|
-
});
|
|
732
|
-
await connection.connect();
|
|
733
|
-
this.connections.set(name, connection);
|
|
734
|
-
} catch (error) {
|
|
735
|
-
this.emit("error", { serverName: name, error });
|
|
736
|
-
throw error;
|
|
737
|
-
}
|
|
738
|
-
}
|
|
739
|
-
);
|
|
740
|
-
await Promise.allSettled(promises);
|
|
741
|
-
}
|
|
742
|
-
/**
|
|
743
|
-
* 断开所有 MCP 服务连接
|
|
744
|
-
*
|
|
745
|
-
* @example
|
|
746
|
-
* ```typescript
|
|
747
|
-
* await manager.disconnect();
|
|
748
|
-
* ```
|
|
749
|
-
*/
|
|
750
|
-
async disconnect() {
|
|
751
|
-
const promises = Array.from(this.connections.values()).map(
|
|
752
|
-
(conn) => conn.disconnect()
|
|
753
|
-
);
|
|
754
|
-
await Promise.allSettled(promises);
|
|
755
|
-
this.connections.clear();
|
|
756
|
-
this.emit("disconnect");
|
|
757
|
-
}
|
|
758
|
-
/**
|
|
759
|
-
* 调用指定服务的工具
|
|
760
|
-
* @param serverName 服务名称
|
|
761
|
-
* @param toolName 工具名称
|
|
762
|
-
* @param args 工具参数
|
|
763
|
-
*
|
|
764
|
-
* @example
|
|
765
|
-
* ```typescript
|
|
766
|
-
* const result = await manager.callTool('datetime', 'get_current_time', {
|
|
767
|
-
* format: 'YYYY-MM-DD HH:mm:ss'
|
|
768
|
-
* });
|
|
769
|
-
* ```
|
|
770
|
-
*/
|
|
771
|
-
async callTool(serverName, toolName, args) {
|
|
772
|
-
const connection = this.connections.get(serverName);
|
|
773
|
-
if (!connection) {
|
|
774
|
-
throw new Error(`\u670D\u52A1 ${serverName} \u4E0D\u5B58\u5728`);
|
|
775
|
-
}
|
|
776
|
-
if (!connection.isConnected()) {
|
|
777
|
-
throw new Error(`\u670D\u52A1 ${serverName} \u672A\u8FDE\u63A5`);
|
|
778
|
-
}
|
|
779
|
-
return connection.callTool(toolName, args);
|
|
780
|
-
}
|
|
781
|
-
/**
|
|
782
|
-
* 列出所有可用的工具
|
|
783
|
-
* @returns 工具列表,格式为 [{ name, serverName, description, inputSchema }]
|
|
784
|
-
*
|
|
785
|
-
* @example
|
|
786
|
-
* ```typescript
|
|
787
|
-
* const tools = manager.listTools();
|
|
788
|
-
* console.log('可用工具:', tools.map(t => `${t.serverName}/${t.name}`));
|
|
789
|
-
* ```
|
|
790
|
-
*/
|
|
791
|
-
listTools() {
|
|
792
|
-
const allTools = [];
|
|
793
|
-
for (const [serverName, connection] of this.connections) {
|
|
794
|
-
if (connection.isConnected()) {
|
|
795
|
-
const tools = connection.getTools();
|
|
796
|
-
for (const tool of tools) {
|
|
797
|
-
allTools.push({
|
|
798
|
-
name: tool.name,
|
|
799
|
-
serverName,
|
|
800
|
-
description: tool.description || "",
|
|
801
|
-
inputSchema: tool.inputSchema
|
|
802
|
-
});
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
return allTools;
|
|
807
|
-
}
|
|
808
|
-
/**
|
|
809
|
-
* 获取服务状态
|
|
810
|
-
* @param serverName 服务名称
|
|
811
|
-
* @returns 服务状态,如果服务不存在则返回 null
|
|
812
|
-
*
|
|
813
|
-
* @example
|
|
814
|
-
* ```typescript
|
|
815
|
-
* const status = manager.getServerStatus('datetime');
|
|
816
|
-
* if (status) {
|
|
817
|
-
* console.log(`已连接: ${status.connected}, 工具数: ${status.toolCount}`);
|
|
818
|
-
* }
|
|
819
|
-
* ```
|
|
820
|
-
*/
|
|
821
|
-
getServerStatus(serverName) {
|
|
822
|
-
const connection = this.connections.get(serverName);
|
|
823
|
-
if (!connection) {
|
|
824
|
-
return null;
|
|
825
|
-
}
|
|
826
|
-
const status = connection.getStatus();
|
|
827
|
-
return {
|
|
828
|
-
connected: status.connected,
|
|
829
|
-
toolCount: status.toolCount
|
|
830
|
-
};
|
|
831
|
-
}
|
|
832
|
-
/**
|
|
833
|
-
* 获取所有服务的状态
|
|
834
|
-
* @returns 所有服务的状态映射
|
|
835
|
-
*
|
|
836
|
-
* @example
|
|
837
|
-
* ```typescript
|
|
838
|
-
* const statuses = manager.getAllServerStatus();
|
|
839
|
-
* console.log(statuses);
|
|
840
|
-
* // {
|
|
841
|
-
* // datetime: { connected: true, toolCount: 3 },
|
|
842
|
-
* // calculator: { connected: true, toolCount: 1 }
|
|
843
|
-
* // }
|
|
844
|
-
* ```
|
|
845
|
-
*/
|
|
846
|
-
getAllServerStatus() {
|
|
847
|
-
const statuses = {};
|
|
848
|
-
for (const [serverName, connection] of this.connections) {
|
|
849
|
-
const status = connection.getStatus();
|
|
850
|
-
statuses[serverName] = {
|
|
851
|
-
connected: status.connected,
|
|
852
|
-
toolCount: status.toolCount
|
|
853
|
-
};
|
|
854
|
-
}
|
|
855
|
-
return statuses;
|
|
856
|
-
}
|
|
857
|
-
/**
|
|
858
|
-
* 检查服务是否已连接
|
|
859
|
-
* @param serverName 服务名称
|
|
860
|
-
*
|
|
861
|
-
* @example
|
|
862
|
-
* ```typescript
|
|
863
|
-
* if (manager.isConnected('datetime')) {
|
|
864
|
-
* console.log('datetime 服务已连接');
|
|
865
|
-
* }
|
|
866
|
-
* ```
|
|
867
|
-
*/
|
|
868
|
-
isConnected(serverName) {
|
|
869
|
-
const connection = this.connections.get(serverName);
|
|
870
|
-
return connection ? connection.isConnected() : false;
|
|
871
|
-
}
|
|
872
|
-
/**
|
|
873
|
-
* 获取已配置的服务列表
|
|
874
|
-
* @returns 服务名称数组
|
|
875
|
-
*
|
|
876
|
-
* @example
|
|
877
|
-
* ```typescript
|
|
878
|
-
* const servers = manager.getServerNames();
|
|
879
|
-
* console.log('已配置的服务:', servers);
|
|
880
|
-
* ```
|
|
881
|
-
*/
|
|
882
|
-
getServerNames() {
|
|
883
|
-
return Array.from(this.configs.keys());
|
|
884
|
-
}
|
|
885
|
-
/**
|
|
886
|
-
* 获取已连接的服务列表
|
|
887
|
-
* @returns 已连接的服务名称数组
|
|
888
|
-
*
|
|
889
|
-
* @example
|
|
890
|
-
* ```typescript
|
|
891
|
-
* const connectedServers = manager.getConnectedServerNames();
|
|
892
|
-
* console.log('已连接的服务:', connectedServers);
|
|
893
|
-
* ```
|
|
894
|
-
*/
|
|
895
|
-
getConnectedServerNames() {
|
|
896
|
-
const connected = [];
|
|
897
|
-
for (const [serverName, connection] of this.connections) {
|
|
898
|
-
if (connection.isConnected()) {
|
|
899
|
-
connected.push(serverName);
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
return connected;
|
|
903
|
-
}
|
|
904
|
-
};
|
|
905
|
-
export {
|
|
906
|
-
ConnectionState,
|
|
907
|
-
MCPConnection,
|
|
908
|
-
MCPManager,
|
|
909
|
-
MCPManager as MCPServiceManager,
|
|
910
|
-
MCPTransportType,
|
|
911
|
-
ToolCallError,
|
|
912
|
-
ToolCallErrorCode,
|
|
913
|
-
TransportFactory,
|
|
914
|
-
TypeFieldNormalizer,
|
|
915
|
-
ensureToolJSONSchema,
|
|
916
|
-
inferTransportTypeFromConfig,
|
|
917
|
-
inferTransportTypeFromUrl,
|
|
918
|
-
isValidToolJSONSchema,
|
|
919
|
-
normalizeTypeField,
|
|
920
|
-
validateToolCallParams
|
|
921
|
-
};
|
|
922
|
-
//# sourceMappingURL=index.js.map
|