@nextclaw/mcp 0.1.64 → 0.1.66
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/dist/index.d.ts +186 -176
- package/dist/index.js +563 -679
- package/package.json +3 -4
package/dist/index.js
CHANGED
|
@@ -1,723 +1,607 @@
|
|
|
1
|
-
|
|
1
|
+
import { Agent, fetch } from "undici";
|
|
2
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
3
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
4
|
+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
5
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
6
|
+
//#region src/types.ts
|
|
2
7
|
function normalizeMcpServerName(name) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
if (!/^[A-Za-z0-9._-]+$/.test(normalized)) {
|
|
8
|
-
throw new Error("MCP server name may only contain letters, numbers, dot, underscore, and dash.");
|
|
9
|
-
}
|
|
10
|
-
return normalized;
|
|
8
|
+
const normalized = name.trim();
|
|
9
|
+
if (!normalized) throw new Error("MCP server name is required.");
|
|
10
|
+
if (!/^[A-Za-z0-9._-]+$/.test(normalized)) throw new Error("MCP server name may only contain letters, numbers, dot, underscore, and dash.");
|
|
11
|
+
return normalized;
|
|
11
12
|
}
|
|
12
13
|
function readDefaultAgentId(config) {
|
|
13
|
-
|
|
14
|
+
return config.agents.list.find((entry) => entry.default)?.id?.trim() || config.agents.list[0]?.id?.trim() || "main";
|
|
14
15
|
}
|
|
15
16
|
function isServerAccessibleToAgent(params) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (explicitAgents.length > 0) {
|
|
22
|
-
return explicitAgents.includes(targetAgentId);
|
|
23
|
-
}
|
|
24
|
-
return targetAgentId === readDefaultAgentId(params.config);
|
|
17
|
+
if (params.scope.allAgents) return true;
|
|
18
|
+
const targetAgentId = params.agentId?.trim() || readDefaultAgentId(params.config);
|
|
19
|
+
const explicitAgents = params.scope.agents.map((agentId) => agentId.trim()).filter(Boolean);
|
|
20
|
+
if (explicitAgents.length > 0) return explicitAgents.includes(targetAgentId);
|
|
21
|
+
return targetAgentId === readDefaultAgentId(params.config);
|
|
25
22
|
}
|
|
26
23
|
function buildQualifiedMcpToolName(serverName, toolName) {
|
|
27
|
-
|
|
24
|
+
return `mcp_${sanitizeMcpSegment(serverName)}__${sanitizeMcpSegment(toolName)}`;
|
|
28
25
|
}
|
|
29
26
|
function sanitizeMcpSegment(value) {
|
|
30
|
-
|
|
31
|
-
return sanitized || "unnamed";
|
|
27
|
+
return value.trim().replace(/[^A-Za-z0-9_]+/g, "_").replace(/^_+|_+$/g, "") || "unnamed";
|
|
32
28
|
}
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/config/mcp-config-normalizer.ts
|
|
35
31
|
function listMcpServers(config) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
return Object.entries(config.mcp.servers).map(([name, definition]) => ({
|
|
33
|
+
name: normalizeMcpServerName(name),
|
|
34
|
+
definition
|
|
35
|
+
})).sort((left, right) => left.name.localeCompare(right.name));
|
|
40
36
|
}
|
|
41
37
|
function getMcpServer(config, name) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
definition
|
|
50
|
-
};
|
|
38
|
+
const normalizedName = normalizeMcpServerName(name);
|
|
39
|
+
const definition = config.mcp.servers[normalizedName];
|
|
40
|
+
if (!definition) return;
|
|
41
|
+
return {
|
|
42
|
+
name: normalizedName,
|
|
43
|
+
definition
|
|
44
|
+
};
|
|
51
45
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
import { Agent, fetch as undiciFetch } from "undici";
|
|
55
|
-
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
56
|
-
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
57
|
-
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
58
|
-
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/client/mcp-client-factory.ts
|
|
59
48
|
function createClient() {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
49
|
+
return new Client({
|
|
50
|
+
name: "nextclaw-mcp-client",
|
|
51
|
+
version: "0.1.0"
|
|
52
|
+
});
|
|
64
53
|
}
|
|
65
54
|
function buildFetch(transport) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
} finally {
|
|
82
|
-
clearTimeout(timeout);
|
|
83
|
-
}
|
|
84
|
-
};
|
|
55
|
+
const timeoutMs = transport.timeoutMs;
|
|
56
|
+
const insecureAgent = transport.verifyTls ? null : new Agent({ connect: { rejectUnauthorized: false } });
|
|
57
|
+
return async (input, init) => {
|
|
58
|
+
const controller = new AbortController();
|
|
59
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
60
|
+
try {
|
|
61
|
+
return await fetch(input, {
|
|
62
|
+
...init,
|
|
63
|
+
dispatcher: insecureAgent ?? init?.dispatcher,
|
|
64
|
+
signal: controller.signal
|
|
65
|
+
});
|
|
66
|
+
} finally {
|
|
67
|
+
clearTimeout(timeout);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
85
70
|
}
|
|
86
71
|
function createStdioTransport(transport) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
72
|
+
return new StdioClientTransport({
|
|
73
|
+
command: transport.command,
|
|
74
|
+
args: transport.args,
|
|
75
|
+
cwd: transport.cwd,
|
|
76
|
+
env: Object.keys(transport.env).length > 0 ? transport.env : void 0,
|
|
77
|
+
stderr: transport.stderr
|
|
78
|
+
});
|
|
94
79
|
}
|
|
95
80
|
function createHttpTransport(transport) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
fetch: buildFetch(transport)
|
|
101
|
-
});
|
|
81
|
+
return new StreamableHTTPClientTransport(new URL(transport.url), {
|
|
82
|
+
requestInit: { headers: transport.headers },
|
|
83
|
+
fetch: buildFetch(transport)
|
|
84
|
+
});
|
|
102
85
|
}
|
|
103
86
|
function createSseTransport(transport) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
fetch: buildFetch(transport)
|
|
113
|
-
});
|
|
87
|
+
return new SSEClientTransport(new URL(transport.url), {
|
|
88
|
+
requestInit: { headers: transport.headers },
|
|
89
|
+
eventSourceInit: {
|
|
90
|
+
fetch: buildFetch(transport),
|
|
91
|
+
headers: transport.headers
|
|
92
|
+
},
|
|
93
|
+
fetch: buildFetch(transport)
|
|
94
|
+
});
|
|
114
95
|
}
|
|
115
96
|
var McpClientFactory = class {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
client: createClient(),
|
|
135
|
-
transport
|
|
136
|
-
};
|
|
137
|
-
}
|
|
97
|
+
constructor(_config) {
|
|
98
|
+
this._config = _config;
|
|
99
|
+
this._config;
|
|
100
|
+
}
|
|
101
|
+
create(record) {
|
|
102
|
+
const transport = (() => {
|
|
103
|
+
switch (record.definition.transport.type) {
|
|
104
|
+
case "stdio": return createStdioTransport(record.definition.transport);
|
|
105
|
+
case "http": return createHttpTransport(record.definition.transport);
|
|
106
|
+
case "sse": return createSseTransport(record.definition.transport);
|
|
107
|
+
default: throw new Error(`Unsupported MCP transport: ${String(record.definition.transport.type)}`);
|
|
108
|
+
}
|
|
109
|
+
})();
|
|
110
|
+
return {
|
|
111
|
+
client: createClient(),
|
|
112
|
+
transport
|
|
113
|
+
};
|
|
114
|
+
}
|
|
138
115
|
};
|
|
139
|
-
|
|
140
|
-
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/lifecycle/mcp-server-lifecycle-manager.ts
|
|
141
118
|
var McpServerLifecycleManager = class {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const now = (this.options.now ?? (() => /* @__PURE__ */ new Date()))().toISOString();
|
|
239
|
-
return {
|
|
240
|
-
record,
|
|
241
|
-
tools,
|
|
242
|
-
clientReady: true,
|
|
243
|
-
lastReadyAt: now,
|
|
244
|
-
client,
|
|
245
|
-
transport
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
stripConnection(connection) {
|
|
249
|
-
return {
|
|
250
|
-
record: connection.record,
|
|
251
|
-
tools: connection.tools,
|
|
252
|
-
clientReady: connection.clientReady,
|
|
253
|
-
lastReadyAt: connection.lastReadyAt,
|
|
254
|
-
lastError: connection.lastError
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
normalizeToolResult(result) {
|
|
258
|
-
if ("toolResult" in result) {
|
|
259
|
-
return result.toolResult;
|
|
260
|
-
}
|
|
261
|
-
return {
|
|
262
|
-
content: result.content,
|
|
263
|
-
structuredContent: result.structuredContent,
|
|
264
|
-
isError: result.isError ?? false
|
|
265
|
-
};
|
|
266
|
-
}
|
|
119
|
+
pendingConnections = /* @__PURE__ */ new Map();
|
|
120
|
+
readyConnections = /* @__PURE__ */ new Map();
|
|
121
|
+
constructor(options) {
|
|
122
|
+
this.options = options;
|
|
123
|
+
}
|
|
124
|
+
async warmServer(record) {
|
|
125
|
+
const connection = await this.ensureConnection(record);
|
|
126
|
+
return this.stripConnection(connection);
|
|
127
|
+
}
|
|
128
|
+
getCachedCatalog(serverName) {
|
|
129
|
+
return this.readyConnections.get(serverName)?.tools ?? [];
|
|
130
|
+
}
|
|
131
|
+
getCachedState(serverName) {
|
|
132
|
+
const ready = this.readyConnections.get(serverName);
|
|
133
|
+
if (!ready) return;
|
|
134
|
+
return this.stripConnection(ready);
|
|
135
|
+
}
|
|
136
|
+
async callTool(record, toolName, args) {
|
|
137
|
+
const result = await (await this.ensureConnection(record)).client.callTool({
|
|
138
|
+
name: toolName,
|
|
139
|
+
arguments: args
|
|
140
|
+
});
|
|
141
|
+
return this.normalizeToolResult(result);
|
|
142
|
+
}
|
|
143
|
+
async closeAll() {
|
|
144
|
+
const pending = await Promise.allSettled(this.pendingConnections.values());
|
|
145
|
+
for (const result of pending) if (result.status === "fulfilled") this.readyConnections.set(result.value.record.name, result.value);
|
|
146
|
+
for (const connection of this.readyConnections.values()) await connection.transport.close().catch(() => {});
|
|
147
|
+
this.pendingConnections.clear();
|
|
148
|
+
this.readyConnections.clear();
|
|
149
|
+
}
|
|
150
|
+
async closeServer(serverName) {
|
|
151
|
+
const ready = this.readyConnections.get(serverName);
|
|
152
|
+
this.readyConnections.delete(serverName);
|
|
153
|
+
const pending = this.pendingConnections.get(serverName);
|
|
154
|
+
this.pendingConnections.delete(serverName);
|
|
155
|
+
if (ready) await ready.transport.close().catch(() => {});
|
|
156
|
+
if (!pending) return;
|
|
157
|
+
const settled = (await Promise.allSettled([pending]))[0];
|
|
158
|
+
if (settled?.status === "fulfilled") await settled.value.transport.close().catch(() => {});
|
|
159
|
+
this.readyConnections.delete(serverName);
|
|
160
|
+
this.pendingConnections.delete(serverName);
|
|
161
|
+
}
|
|
162
|
+
async ensureConnection(record) {
|
|
163
|
+
const ready = this.readyConnections.get(record.name);
|
|
164
|
+
if (ready) return ready;
|
|
165
|
+
const existing = this.pendingConnections.get(record.name);
|
|
166
|
+
if (existing) return existing;
|
|
167
|
+
const task = this.connect(record);
|
|
168
|
+
this.pendingConnections.set(record.name, task);
|
|
169
|
+
try {
|
|
170
|
+
const connection = await task;
|
|
171
|
+
this.readyConnections.set(record.name, connection);
|
|
172
|
+
this.pendingConnections.delete(record.name);
|
|
173
|
+
return connection;
|
|
174
|
+
} catch (error) {
|
|
175
|
+
this.pendingConnections.delete(record.name);
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
async connect(record) {
|
|
180
|
+
const config = this.options.getConfig();
|
|
181
|
+
const { client, transport } = (this.options.clientFactory ?? new McpClientFactory(config)).create(record);
|
|
182
|
+
await client.connect(transport);
|
|
183
|
+
return {
|
|
184
|
+
record,
|
|
185
|
+
tools: (await client.listTools()).tools.map((tool) => ({
|
|
186
|
+
qualifiedName: buildQualifiedMcpToolName(record.name, tool.name),
|
|
187
|
+
serverName: record.name,
|
|
188
|
+
toolName: tool.name,
|
|
189
|
+
description: tool.description,
|
|
190
|
+
parameters: tool.inputSchema
|
|
191
|
+
})),
|
|
192
|
+
clientReady: true,
|
|
193
|
+
lastReadyAt: (this.options.now ?? (() => /* @__PURE__ */ new Date()))().toISOString(),
|
|
194
|
+
client,
|
|
195
|
+
transport
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
stripConnection(connection) {
|
|
199
|
+
return {
|
|
200
|
+
record: connection.record,
|
|
201
|
+
tools: connection.tools,
|
|
202
|
+
clientReady: connection.clientReady,
|
|
203
|
+
lastReadyAt: connection.lastReadyAt,
|
|
204
|
+
lastError: connection.lastError
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
normalizeToolResult(result) {
|
|
208
|
+
if ("toolResult" in result) return result.toolResult;
|
|
209
|
+
return {
|
|
210
|
+
content: result.content,
|
|
211
|
+
structuredContent: result.structuredContent,
|
|
212
|
+
isError: result.isError ?? false
|
|
213
|
+
};
|
|
214
|
+
}
|
|
267
215
|
};
|
|
268
|
-
|
|
269
|
-
|
|
216
|
+
//#endregion
|
|
217
|
+
//#region src/registry/mcp-registry-service.ts
|
|
270
218
|
var McpRegistryService = class {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
async warmRecord(record) {
|
|
400
|
-
try {
|
|
401
|
-
const state = await this.lifecycleManager.warmServer(record);
|
|
402
|
-
return {
|
|
403
|
-
name: record.name,
|
|
404
|
-
ok: true,
|
|
405
|
-
toolCount: state.tools.length
|
|
406
|
-
};
|
|
407
|
-
} catch (error) {
|
|
408
|
-
return {
|
|
409
|
-
name: record.name,
|
|
410
|
-
ok: false,
|
|
411
|
-
toolCount: 0,
|
|
412
|
-
error: toErrorMessage(error)
|
|
413
|
-
};
|
|
414
|
-
}
|
|
415
|
-
}
|
|
219
|
+
lifecycleManager;
|
|
220
|
+
constructor(options) {
|
|
221
|
+
this.options = options;
|
|
222
|
+
this.lifecycleManager = this.options.lifecycleManager ?? new McpServerLifecycleManager({ getConfig: this.options.getConfig });
|
|
223
|
+
}
|
|
224
|
+
listServers() {
|
|
225
|
+
return listMcpServers(this.options.getConfig());
|
|
226
|
+
}
|
|
227
|
+
getServer(name) {
|
|
228
|
+
return getMcpServer(this.options.getConfig(), name);
|
|
229
|
+
}
|
|
230
|
+
async prewarmEnabledServers() {
|
|
231
|
+
return (await Promise.all(this.listServers().filter((record) => record.definition.enabled).map(async (record) => {
|
|
232
|
+
try {
|
|
233
|
+
const state = await this.lifecycleManager.warmServer(record);
|
|
234
|
+
return {
|
|
235
|
+
name: record.name,
|
|
236
|
+
ok: true,
|
|
237
|
+
toolCount: state.tools.length
|
|
238
|
+
};
|
|
239
|
+
} catch (error) {
|
|
240
|
+
return {
|
|
241
|
+
name: record.name,
|
|
242
|
+
ok: false,
|
|
243
|
+
toolCount: 0,
|
|
244
|
+
error: toErrorMessage(error)
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
}))).sort((left, right) => left.name.localeCompare(right.name));
|
|
248
|
+
}
|
|
249
|
+
async warmServer(name) {
|
|
250
|
+
const server = this.getServer(name);
|
|
251
|
+
if (!server) throw new Error(`Unknown MCP server: ${name}`);
|
|
252
|
+
try {
|
|
253
|
+
const state = await this.lifecycleManager.warmServer(server);
|
|
254
|
+
return {
|
|
255
|
+
name: server.name,
|
|
256
|
+
ok: true,
|
|
257
|
+
toolCount: state.tools.length
|
|
258
|
+
};
|
|
259
|
+
} catch (error) {
|
|
260
|
+
return {
|
|
261
|
+
name: server.name,
|
|
262
|
+
ok: false,
|
|
263
|
+
toolCount: 0,
|
|
264
|
+
error: toErrorMessage(error)
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
async reconcileConfig(params) {
|
|
269
|
+
const previousServers = new Map(listMcpServers(params.prevConfig).map((record) => [record.name, record]));
|
|
270
|
+
const nextServers = new Map(listMcpServers(params.nextConfig).map((record) => [record.name, record]));
|
|
271
|
+
const serverNames = Array.from(new Set([...previousServers.keys(), ...nextServers.keys()])).sort((left, right) => left.localeCompare(right));
|
|
272
|
+
const result = {
|
|
273
|
+
added: [],
|
|
274
|
+
removed: [],
|
|
275
|
+
enabled: [],
|
|
276
|
+
disabled: [],
|
|
277
|
+
restarted: [],
|
|
278
|
+
warmed: []
|
|
279
|
+
};
|
|
280
|
+
for (const serverName of serverNames) {
|
|
281
|
+
const previous = previousServers.get(serverName);
|
|
282
|
+
const next = nextServers.get(serverName);
|
|
283
|
+
if (!next) {
|
|
284
|
+
await this.lifecycleManager.closeServer(serverName);
|
|
285
|
+
result.removed.push(serverName);
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
if (!previous) {
|
|
289
|
+
result.added.push(serverName);
|
|
290
|
+
if (next.definition.enabled) result.warmed.push(await this.warmRecord(next));
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
if (previous.definition.enabled && !next.definition.enabled) {
|
|
294
|
+
await this.lifecycleManager.closeServer(serverName);
|
|
295
|
+
result.disabled.push(serverName);
|
|
296
|
+
continue;
|
|
297
|
+
}
|
|
298
|
+
if (!previous.definition.enabled && next.definition.enabled) {
|
|
299
|
+
result.enabled.push(serverName);
|
|
300
|
+
result.warmed.push(await this.warmRecord(next));
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (hasReconnectRelevantChange(previous.definition, next.definition)) {
|
|
304
|
+
await this.lifecycleManager.closeServer(serverName);
|
|
305
|
+
result.restarted.push(serverName);
|
|
306
|
+
if (next.definition.enabled) result.warmed.push(await this.warmRecord(next));
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return result;
|
|
310
|
+
}
|
|
311
|
+
listAccessibleTools(filter = {}) {
|
|
312
|
+
const config = this.options.getConfig();
|
|
313
|
+
return this.listServers().filter((record) => record.definition.enabled).filter((record) => isServerAccessibleToAgent({
|
|
314
|
+
config,
|
|
315
|
+
scope: record.definition.scope,
|
|
316
|
+
agentId: filter.agentId
|
|
317
|
+
})).flatMap((record) => this.lifecycleManager.getCachedCatalog(record.name)).sort((left, right) => left.qualifiedName.localeCompare(right.qualifiedName));
|
|
318
|
+
}
|
|
319
|
+
getCachedState(serverName) {
|
|
320
|
+
return this.lifecycleManager.getCachedState(serverName);
|
|
321
|
+
}
|
|
322
|
+
async callTool(params) {
|
|
323
|
+
const server = this.getServer(params.serverName);
|
|
324
|
+
if (!server) throw new Error(`Unknown MCP server: ${params.serverName}`);
|
|
325
|
+
return this.lifecycleManager.callTool(server, params.toolName, params.args);
|
|
326
|
+
}
|
|
327
|
+
async close() {
|
|
328
|
+
await this.lifecycleManager.closeAll();
|
|
329
|
+
}
|
|
330
|
+
async warmRecord(record) {
|
|
331
|
+
try {
|
|
332
|
+
const state = await this.lifecycleManager.warmServer(record);
|
|
333
|
+
return {
|
|
334
|
+
name: record.name,
|
|
335
|
+
ok: true,
|
|
336
|
+
toolCount: state.tools.length
|
|
337
|
+
};
|
|
338
|
+
} catch (error) {
|
|
339
|
+
return {
|
|
340
|
+
name: record.name,
|
|
341
|
+
ok: false,
|
|
342
|
+
toolCount: 0,
|
|
343
|
+
error: toErrorMessage(error)
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
}
|
|
416
347
|
};
|
|
417
348
|
function toErrorMessage(error) {
|
|
418
|
-
|
|
349
|
+
return error instanceof Error ? error.message : String(error);
|
|
419
350
|
}
|
|
420
351
|
function hasReconnectRelevantChange(left, right) {
|
|
421
|
-
|
|
352
|
+
return JSON.stringify(left.transport) !== JSON.stringify(right.transport);
|
|
422
353
|
}
|
|
423
|
-
|
|
424
|
-
|
|
354
|
+
//#endregion
|
|
355
|
+
//#region src/doctor/mcp-doctor-service.ts
|
|
425
356
|
var McpDoctorService = class {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
};
|
|
446
|
-
})
|
|
447
|
-
);
|
|
448
|
-
}
|
|
357
|
+
constructor(options) {
|
|
358
|
+
this.options = options;
|
|
359
|
+
}
|
|
360
|
+
async inspect(name) {
|
|
361
|
+
const registry = this.options.registryService ?? new McpRegistryService({ getConfig: this.options.getConfig });
|
|
362
|
+
const specificServer = name ? registry.getServer(name) : void 0;
|
|
363
|
+
const servers = specificServer ? [specificServer] : name ? [] : registry.listServers();
|
|
364
|
+
return await Promise.all(servers.map(async (server) => {
|
|
365
|
+
const warmResult = await registry.warmServer(server.name);
|
|
366
|
+
return {
|
|
367
|
+
name: server.name,
|
|
368
|
+
enabled: server.definition.enabled,
|
|
369
|
+
transport: server.definition.transport.type,
|
|
370
|
+
accessible: warmResult.ok,
|
|
371
|
+
toolCount: warmResult.toolCount,
|
|
372
|
+
...warmResult.error ? { error: warmResult.error } : {}
|
|
373
|
+
};
|
|
374
|
+
}));
|
|
375
|
+
}
|
|
449
376
|
};
|
|
450
|
-
|
|
451
|
-
|
|
377
|
+
//#endregion
|
|
378
|
+
//#region src/doctor/mcp-doctor-facade.ts
|
|
452
379
|
var McpDoctorFacade = class {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
}
|
|
380
|
+
doctorService;
|
|
381
|
+
constructor(options) {
|
|
382
|
+
this.options = options;
|
|
383
|
+
this.doctorService = new McpDoctorService({
|
|
384
|
+
getConfig: this.options.getConfig,
|
|
385
|
+
registryService: this.options.registryService
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
async inspect(name) {
|
|
389
|
+
return await this.doctorService.inspect(name);
|
|
390
|
+
}
|
|
391
|
+
async inspectOne(name) {
|
|
392
|
+
return (await this.inspect(name))[0] ?? null;
|
|
393
|
+
}
|
|
468
394
|
};
|
|
469
|
-
|
|
470
|
-
|
|
395
|
+
//#endregion
|
|
396
|
+
//#region src/install/mcp-install-template-materializer.ts
|
|
471
397
|
var McpInstallTemplateMaterializer = class {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
docsUrl: override?.docsUrl ?? current?.docsUrl,
|
|
518
|
-
homepage: override?.homepage ?? current?.homepage,
|
|
519
|
-
trustLevel: override?.trustLevel ?? current?.trustLevel ?? "official",
|
|
520
|
-
installedAt: override?.installedAt ?? current?.installedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
521
|
-
...override ?? {}
|
|
522
|
-
};
|
|
523
|
-
}
|
|
398
|
+
materialize(params) {
|
|
399
|
+
const name = normalizeMcpServerName(params.name?.trim() || params.template.defaultName);
|
|
400
|
+
const inputs = params.inputs ?? {};
|
|
401
|
+
const definition = this.applyInputs(params.template.template, inputs);
|
|
402
|
+
definition.enabled = params.enabled ?? definition.enabled;
|
|
403
|
+
definition.scope = this.mergeScope(definition.scope, params.scope);
|
|
404
|
+
definition.metadata = this.mergeMetadata(definition.metadata, params.template, params.metadata);
|
|
405
|
+
return {
|
|
406
|
+
name,
|
|
407
|
+
definition
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
applyInputs(definition, inputs) {
|
|
411
|
+
const replacer = (value) => value.replace(/\{\{\s*([A-Za-z0-9._-]+)\s*\}\}/g, (_, key) => inputs[key]?.trim() ?? "");
|
|
412
|
+
const transform = (value) => {
|
|
413
|
+
if (typeof value === "string") return replacer(value);
|
|
414
|
+
if (Array.isArray(value)) return value.map((entry) => transform(entry));
|
|
415
|
+
if (!value || typeof value !== "object") return value;
|
|
416
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, transform(entry)]));
|
|
417
|
+
};
|
|
418
|
+
return transform(structuredClone(definition));
|
|
419
|
+
}
|
|
420
|
+
mergeScope(current, override) {
|
|
421
|
+
if (!override) return current;
|
|
422
|
+
const agents = Array.from(new Set((override.agents ?? current.agents ?? []).map((entry) => entry.trim()).filter(Boolean)));
|
|
423
|
+
const allAgents = override.allAgents ?? current.allAgents ?? agents.length === 0;
|
|
424
|
+
return {
|
|
425
|
+
allAgents,
|
|
426
|
+
agents: allAgents ? [] : agents
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
mergeMetadata(current, template, override) {
|
|
430
|
+
return {
|
|
431
|
+
...current,
|
|
432
|
+
source: "marketplace",
|
|
433
|
+
catalogSlug: template.spec,
|
|
434
|
+
displayName: override?.displayName ?? current?.displayName,
|
|
435
|
+
vendor: override?.vendor ?? current?.vendor,
|
|
436
|
+
docsUrl: override?.docsUrl ?? current?.docsUrl,
|
|
437
|
+
homepage: override?.homepage ?? current?.homepage,
|
|
438
|
+
trustLevel: override?.trustLevel ?? current?.trustLevel ?? "official",
|
|
439
|
+
installedAt: override?.installedAt ?? current?.installedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
440
|
+
...override ?? {}
|
|
441
|
+
};
|
|
442
|
+
}
|
|
524
443
|
};
|
|
525
|
-
|
|
526
|
-
|
|
444
|
+
//#endregion
|
|
445
|
+
//#region src/manage/mcp-mutation-service.ts
|
|
527
446
|
var McpMutationService = class {
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
}
|
|
654
|
-
if (config.mcp.servers[nextName]) {
|
|
655
|
-
return {
|
|
656
|
-
changed: false,
|
|
657
|
-
name: nextName,
|
|
658
|
-
message: `MCP server already exists: ${nextName}. Use another name.`
|
|
659
|
-
};
|
|
660
|
-
}
|
|
661
|
-
delete config.mcp.servers[source.name];
|
|
662
|
-
config.mcp.servers[nextName] = source.definition;
|
|
663
|
-
this.options.saveConfig(config);
|
|
664
|
-
return {
|
|
665
|
-
changed: true,
|
|
666
|
-
name: nextName,
|
|
667
|
-
message: `Renamed MCP server ${source.name} -> ${nextName}.`,
|
|
668
|
-
definition: source.definition
|
|
669
|
-
};
|
|
670
|
-
}
|
|
447
|
+
materializer;
|
|
448
|
+
constructor(options) {
|
|
449
|
+
this.options = options;
|
|
450
|
+
this.materializer = this.options.materializer ?? new McpInstallTemplateMaterializer();
|
|
451
|
+
}
|
|
452
|
+
listServerNames() {
|
|
453
|
+
return listMcpServers(this.options.getConfig()).map((record) => record.name);
|
|
454
|
+
}
|
|
455
|
+
addServer(name, definition) {
|
|
456
|
+
const normalizedName = normalizeMcpServerName(name);
|
|
457
|
+
const config = this.options.getConfig();
|
|
458
|
+
if (config.mcp.servers[normalizedName]) return {
|
|
459
|
+
changed: false,
|
|
460
|
+
name: normalizedName,
|
|
461
|
+
message: `MCP server already exists: ${normalizedName}. Use 'mcp list' or remove it first.`,
|
|
462
|
+
definition: config.mcp.servers[normalizedName]
|
|
463
|
+
};
|
|
464
|
+
config.mcp.servers[normalizedName] = definition;
|
|
465
|
+
this.options.saveConfig(config);
|
|
466
|
+
return {
|
|
467
|
+
changed: true,
|
|
468
|
+
name: normalizedName,
|
|
469
|
+
message: `Added MCP server ${normalizedName}.`,
|
|
470
|
+
definition
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
updateServer(name, definition) {
|
|
474
|
+
const normalizedName = normalizeMcpServerName(name);
|
|
475
|
+
const config = this.options.getConfig();
|
|
476
|
+
if (!config.mcp.servers[normalizedName]) return {
|
|
477
|
+
changed: false,
|
|
478
|
+
name: normalizedName,
|
|
479
|
+
message: `Unknown MCP server: ${normalizedName}`
|
|
480
|
+
};
|
|
481
|
+
config.mcp.servers[normalizedName] = definition;
|
|
482
|
+
this.options.saveConfig(config);
|
|
483
|
+
return {
|
|
484
|
+
changed: true,
|
|
485
|
+
name: normalizedName,
|
|
486
|
+
message: `Updated MCP server ${normalizedName}.`,
|
|
487
|
+
definition
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
removeServer(name) {
|
|
491
|
+
const normalizedName = normalizeMcpServerName(name);
|
|
492
|
+
const config = this.options.getConfig();
|
|
493
|
+
if (!config.mcp.servers[normalizedName]) return {
|
|
494
|
+
changed: false,
|
|
495
|
+
name: normalizedName,
|
|
496
|
+
message: `Unknown MCP server: ${normalizedName}`
|
|
497
|
+
};
|
|
498
|
+
delete config.mcp.servers[normalizedName];
|
|
499
|
+
this.options.saveConfig(config);
|
|
500
|
+
return {
|
|
501
|
+
changed: true,
|
|
502
|
+
name: normalizedName,
|
|
503
|
+
message: `Removed MCP server ${normalizedName}.`
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
toggleEnabled(name, enabled) {
|
|
507
|
+
const normalizedName = normalizeMcpServerName(name);
|
|
508
|
+
const config = this.options.getConfig();
|
|
509
|
+
const current = config.mcp.servers[normalizedName];
|
|
510
|
+
if (!current) return {
|
|
511
|
+
changed: false,
|
|
512
|
+
name: normalizedName,
|
|
513
|
+
message: `Unknown MCP server: ${normalizedName}`
|
|
514
|
+
};
|
|
515
|
+
current.enabled = enabled;
|
|
516
|
+
this.options.saveConfig(config);
|
|
517
|
+
return {
|
|
518
|
+
changed: true,
|
|
519
|
+
name: normalizedName,
|
|
520
|
+
message: `${enabled ? "Enabled" : "Disabled"} MCP server ${normalizedName}.`,
|
|
521
|
+
definition: current
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
installFromTemplate(params) {
|
|
525
|
+
const materialized = this.materializer.materialize(params);
|
|
526
|
+
return this.addServer(materialized.name, materialized.definition);
|
|
527
|
+
}
|
|
528
|
+
duplicateServer(sourceName, targetName) {
|
|
529
|
+
const source = getMcpServer(this.options.getConfig(), sourceName);
|
|
530
|
+
if (!source) return {
|
|
531
|
+
changed: false,
|
|
532
|
+
name: normalizeMcpServerName(sourceName),
|
|
533
|
+
message: `Unknown MCP server: ${normalizeMcpServerName(sourceName)}`
|
|
534
|
+
};
|
|
535
|
+
const definition = structuredClone(source.definition);
|
|
536
|
+
if (definition.metadata) definition.metadata = {
|
|
537
|
+
...definition.metadata,
|
|
538
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
539
|
+
};
|
|
540
|
+
return this.addServer(targetName, definition);
|
|
541
|
+
}
|
|
542
|
+
renameServer(sourceName, targetName) {
|
|
543
|
+
const source = getMcpServer(this.options.getConfig(), sourceName);
|
|
544
|
+
if (!source) return {
|
|
545
|
+
changed: false,
|
|
546
|
+
name: normalizeMcpServerName(sourceName),
|
|
547
|
+
message: `Unknown MCP server: ${normalizeMcpServerName(sourceName)}`
|
|
548
|
+
};
|
|
549
|
+
const nextName = normalizeMcpServerName(targetName);
|
|
550
|
+
const config = this.options.getConfig();
|
|
551
|
+
if (source.name === nextName) return {
|
|
552
|
+
changed: false,
|
|
553
|
+
name: nextName,
|
|
554
|
+
message: `MCP server already named ${nextName}.`,
|
|
555
|
+
definition: source.definition
|
|
556
|
+
};
|
|
557
|
+
if (config.mcp.servers[nextName]) return {
|
|
558
|
+
changed: false,
|
|
559
|
+
name: nextName,
|
|
560
|
+
message: `MCP server already exists: ${nextName}. Use another name.`
|
|
561
|
+
};
|
|
562
|
+
delete config.mcp.servers[source.name];
|
|
563
|
+
config.mcp.servers[nextName] = source.definition;
|
|
564
|
+
this.options.saveConfig(config);
|
|
565
|
+
return {
|
|
566
|
+
changed: true,
|
|
567
|
+
name: nextName,
|
|
568
|
+
message: `Renamed MCP server ${source.name} -> ${nextName}.`,
|
|
569
|
+
definition: source.definition
|
|
570
|
+
};
|
|
571
|
+
}
|
|
671
572
|
};
|
|
672
|
-
|
|
673
|
-
|
|
573
|
+
//#endregion
|
|
574
|
+
//#region src/view/mcp-installed-view-service.ts
|
|
674
575
|
var McpInstalledViewService = class {
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
});
|
|
705
|
-
}
|
|
706
|
-
};
|
|
707
|
-
export {
|
|
708
|
-
McpClientFactory,
|
|
709
|
-
McpDoctorFacade,
|
|
710
|
-
McpDoctorService,
|
|
711
|
-
McpInstallTemplateMaterializer,
|
|
712
|
-
McpInstalledViewService,
|
|
713
|
-
McpMutationService,
|
|
714
|
-
McpRegistryService,
|
|
715
|
-
McpServerLifecycleManager,
|
|
716
|
-
buildQualifiedMcpToolName,
|
|
717
|
-
getMcpServer,
|
|
718
|
-
isServerAccessibleToAgent,
|
|
719
|
-
listMcpServers,
|
|
720
|
-
normalizeMcpServerName,
|
|
721
|
-
readDefaultAgentId,
|
|
722
|
-
sanitizeMcpSegment
|
|
576
|
+
registryService;
|
|
577
|
+
constructor(options) {
|
|
578
|
+
this.options = options;
|
|
579
|
+
this.registryService = this.options.registryService ?? new McpRegistryService({ getConfig: this.options.getConfig });
|
|
580
|
+
}
|
|
581
|
+
listInstalled() {
|
|
582
|
+
return this.registryService.listServers().map((server) => {
|
|
583
|
+
const metadata = server.definition.metadata;
|
|
584
|
+
const cached = this.registryService.getCachedState(server.name);
|
|
585
|
+
return {
|
|
586
|
+
name: server.name,
|
|
587
|
+
enabled: server.definition.enabled,
|
|
588
|
+
transport: server.definition.transport.type,
|
|
589
|
+
scope: server.definition.scope,
|
|
590
|
+
source: metadata?.source === "marketplace" ? "marketplace" : "manual",
|
|
591
|
+
catalogSlug: metadata?.catalogSlug,
|
|
592
|
+
displayName: metadata?.displayName,
|
|
593
|
+
vendor: metadata?.vendor,
|
|
594
|
+
docsUrl: metadata?.docsUrl,
|
|
595
|
+
homepage: metadata?.homepage,
|
|
596
|
+
trustLevel: metadata?.trustLevel,
|
|
597
|
+
installedAt: metadata?.installedAt,
|
|
598
|
+
accessible: cached?.clientReady,
|
|
599
|
+
toolCount: cached?.tools.length,
|
|
600
|
+
lastReadyAt: cached?.lastReadyAt,
|
|
601
|
+
lastError: cached?.lastError
|
|
602
|
+
};
|
|
603
|
+
});
|
|
604
|
+
}
|
|
723
605
|
};
|
|
606
|
+
//#endregion
|
|
607
|
+
export { McpClientFactory, McpDoctorFacade, McpDoctorService, McpInstallTemplateMaterializer, McpInstalledViewService, McpMutationService, McpRegistryService, McpServerLifecycleManager, buildQualifiedMcpToolName, getMcpServer, isServerAccessibleToAgent, listMcpServers, normalizeMcpServerName, readDefaultAgentId, sanitizeMcpSegment };
|