@aigne/afs-mcp 1.11.0-beta.6
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/LICENSE.md +26 -0
- package/README.md +297 -0
- package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs +11 -0
- package/dist/_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.mjs +10 -0
- package/dist/index.cjs +932 -0
- package/dist/index.d.cts +363 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +363 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +933 -0
- package/dist/index.mjs.map +1 -0
- package/dist/kinds.cjs +181 -0
- package/dist/kinds.mjs +182 -0
- package/dist/kinds.mjs.map +1 -0
- package/package.json +57 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,932 @@
|
|
|
1
|
+
const require_kinds = require('./kinds.cjs');
|
|
2
|
+
const require_decorate = require('./_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs');
|
|
3
|
+
let node_path = require("node:path");
|
|
4
|
+
let _aigne_afs = require("@aigne/afs");
|
|
5
|
+
let _aigne_afs_provider = require("@aigne/afs/provider");
|
|
6
|
+
let _aigne_afs_utils_zod = require("@aigne/afs/utils/zod");
|
|
7
|
+
let _modelcontextprotocol_sdk_client_index_js = require("@modelcontextprotocol/sdk/client/index.js");
|
|
8
|
+
let _modelcontextprotocol_sdk_client_sse_js = require("@modelcontextprotocol/sdk/client/sse.js");
|
|
9
|
+
let _modelcontextprotocol_sdk_client_stdio_js = require("@modelcontextprotocol/sdk/client/stdio.js");
|
|
10
|
+
let _modelcontextprotocol_sdk_client_streamableHttp_js = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
11
|
+
let zod = require("zod");
|
|
12
|
+
|
|
13
|
+
//#region src/index.ts
|
|
14
|
+
/**
|
|
15
|
+
* AFS MCP Provider
|
|
16
|
+
*
|
|
17
|
+
* 将 MCP Server 挂载为 AFS 可访问的世界。
|
|
18
|
+
* - Tools → 可执行的 AFS entries(通过 `exec()`)
|
|
19
|
+
* - Prompts → 可读取的世界描述
|
|
20
|
+
* - Resources → 可读取的世界状态(展开为 AFS 目录结构)
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Zod schema for options validation
|
|
24
|
+
*/
|
|
25
|
+
const afsMCPOptionsSchema = (0, _aigne_afs_utils_zod.camelize)(zod.z.object({
|
|
26
|
+
name: (0, _aigne_afs_utils_zod.optionalize)(zod.z.string()),
|
|
27
|
+
description: (0, _aigne_afs_utils_zod.optionalize)(zod.z.string()),
|
|
28
|
+
transport: zod.z.enum([
|
|
29
|
+
"stdio",
|
|
30
|
+
"http",
|
|
31
|
+
"sse"
|
|
32
|
+
]),
|
|
33
|
+
command: (0, _aigne_afs_utils_zod.optionalize)(zod.z.string()),
|
|
34
|
+
args: (0, _aigne_afs_utils_zod.optionalize)(zod.z.array(zod.z.string())),
|
|
35
|
+
env: (0, _aigne_afs_utils_zod.optionalize)(zod.z.record(zod.z.string())),
|
|
36
|
+
url: (0, _aigne_afs_utils_zod.optionalize)(zod.z.string()),
|
|
37
|
+
headers: (0, _aigne_afs_utils_zod.optionalize)(zod.z.record(zod.z.string())),
|
|
38
|
+
timeout: (0, _aigne_afs_utils_zod.optionalize)(zod.z.number()),
|
|
39
|
+
maxReconnects: (0, _aigne_afs_utils_zod.optionalize)(zod.z.number())
|
|
40
|
+
}).refine((data) => {
|
|
41
|
+
if (data.transport === "stdio" && !data.command) return false;
|
|
42
|
+
if ((data.transport === "http" || data.transport === "sse") && !data.url) return false;
|
|
43
|
+
return true;
|
|
44
|
+
}, { message: "stdio transport requires 'command', http/sse transport requires 'url'" }));
|
|
45
|
+
/**
|
|
46
|
+
* AFS Module for MCP Server integration
|
|
47
|
+
*/
|
|
48
|
+
var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
|
|
49
|
+
name;
|
|
50
|
+
description;
|
|
51
|
+
accessMode = "readwrite";
|
|
52
|
+
/**
|
|
53
|
+
* Get the Zod schema for options validation
|
|
54
|
+
*/
|
|
55
|
+
static schema() {
|
|
56
|
+
return afsMCPOptionsSchema;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Load module from configuration file
|
|
60
|
+
*/
|
|
61
|
+
static async load({ filepath, parsed }) {
|
|
62
|
+
return new AFSMCP({
|
|
63
|
+
...await AFSMCP.schema().parseAsync(parsed),
|
|
64
|
+
cwd: (0, node_path.dirname)(filepath)
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Parse a resource URI into its components
|
|
69
|
+
*
|
|
70
|
+
* Examples:
|
|
71
|
+
* - "file:///path/to/file.txt" -> { scheme: "file", path: "/path/to/file.txt" }
|
|
72
|
+
* - "sqlite://posts" -> { scheme: "sqlite", path: "/posts" }
|
|
73
|
+
* - "github://repos/owner/repo" -> { scheme: "github", path: "/repos/owner/repo" }
|
|
74
|
+
*/
|
|
75
|
+
static parseResourceUri(uri) {
|
|
76
|
+
const match = uri.match(/^(\w+):\/\/\/?(.*)$/);
|
|
77
|
+
if (match) {
|
|
78
|
+
const scheme = match[1];
|
|
79
|
+
const rest = match[2];
|
|
80
|
+
return {
|
|
81
|
+
scheme,
|
|
82
|
+
path: rest.startsWith("/") ? rest : `/${rest}`
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
scheme: "unknown",
|
|
87
|
+
path: uri
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Parse a URI template and extract variable names
|
|
92
|
+
*
|
|
93
|
+
* Examples:
|
|
94
|
+
* - "sqlite://posts/{id}" -> ["id"]
|
|
95
|
+
* - "github://repos/{owner}/{repo}/issues/{number}" -> ["owner", "repo", "number"]
|
|
96
|
+
*/
|
|
97
|
+
static parseUriTemplate(template) {
|
|
98
|
+
const vars = [];
|
|
99
|
+
const regex = /\{(\w+)\}/g;
|
|
100
|
+
let match = regex.exec(template);
|
|
101
|
+
while (match !== null) {
|
|
102
|
+
vars.push(match[1]);
|
|
103
|
+
match = regex.exec(template);
|
|
104
|
+
}
|
|
105
|
+
return vars;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Match a path against a URI template and extract parameters
|
|
109
|
+
*
|
|
110
|
+
* Examples:
|
|
111
|
+
* - matchPathToTemplate("/posts/123", "sqlite://posts/{id}") -> { id: "123" }
|
|
112
|
+
* - matchPathToTemplate("/repos/arcblock/afs/issues/42", "github://repos/{owner}/{repo}/issues/{number}")
|
|
113
|
+
* -> { owner: "arcblock", repo: "afs", number: "42" }
|
|
114
|
+
*
|
|
115
|
+
* Returns null if path doesn't match the template
|
|
116
|
+
*/
|
|
117
|
+
static matchPathToTemplate(path, uriTemplate) {
|
|
118
|
+
const templatePath = AFSMCP.parseResourceUri(uriTemplate).path;
|
|
119
|
+
const vars = [];
|
|
120
|
+
const regexStr = templatePath.replace(/\{(\w+)\}/g, (_, varName) => {
|
|
121
|
+
vars.push(varName);
|
|
122
|
+
return "([^/]+)";
|
|
123
|
+
});
|
|
124
|
+
const regex = /* @__PURE__ */ new RegExp(`^${regexStr}$`);
|
|
125
|
+
const match = path.match(regex);
|
|
126
|
+
if (!match) return null;
|
|
127
|
+
const params = {};
|
|
128
|
+
for (let i = 0; i < vars.length; i++) {
|
|
129
|
+
const varName = vars[i];
|
|
130
|
+
const value = match[i + 1];
|
|
131
|
+
if (varName && value) params[varName] = value;
|
|
132
|
+
}
|
|
133
|
+
return params;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Build a complete URI from a template and parameters
|
|
137
|
+
*/
|
|
138
|
+
static buildUriFromTemplate(template, params) {
|
|
139
|
+
let uri = template;
|
|
140
|
+
for (const [key, value] of Object.entries(params)) uri = uri.replace(`{${key}}`, value);
|
|
141
|
+
return uri;
|
|
142
|
+
}
|
|
143
|
+
client = null;
|
|
144
|
+
transport = null;
|
|
145
|
+
_tools = [];
|
|
146
|
+
_prompts = [];
|
|
147
|
+
_resources = [];
|
|
148
|
+
_resourceTemplates = [];
|
|
149
|
+
_resourcePathMap = /* @__PURE__ */ new Map();
|
|
150
|
+
_isConnected = false;
|
|
151
|
+
constructor(options) {
|
|
152
|
+
super();
|
|
153
|
+
this.options = options;
|
|
154
|
+
(0, _aigne_afs_utils_zod.zodParse)(afsMCPOptionsSchema, options);
|
|
155
|
+
this.name = options.name || "mcp";
|
|
156
|
+
this.description = options.description;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Define static entry tree for MCP provider structure.
|
|
160
|
+
*
|
|
161
|
+
* This async method calls ensureConnected() to initialize provider state,
|
|
162
|
+
* then returns the tree with dynamic values based on connected data.
|
|
163
|
+
*
|
|
164
|
+
* Static entries:
|
|
165
|
+
* - /WORLD.md: Always present
|
|
166
|
+
* - /tools: With dynamic childrenCount
|
|
167
|
+
* - /prompts: Only if prompts exist, with dynamic childrenCount
|
|
168
|
+
* - /resources: Only if resources exist, with dynamic childrenCount
|
|
169
|
+
*/
|
|
170
|
+
async defineEntries() {
|
|
171
|
+
await this.ensureConnected();
|
|
172
|
+
const children = {
|
|
173
|
+
"WORLD.md": {
|
|
174
|
+
content: this.generateWorldMd(),
|
|
175
|
+
metadata: {
|
|
176
|
+
kind: "afs:document",
|
|
177
|
+
kinds: require_kinds.getKindsArray("afs:document"),
|
|
178
|
+
description: "MCP Server World Documentation",
|
|
179
|
+
mimeType: "text/markdown",
|
|
180
|
+
mcp: { type: "world" }
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
tools: { metadata: {
|
|
184
|
+
kind: "afs:node",
|
|
185
|
+
kinds: require_kinds.getKindsArray("afs:node"),
|
|
186
|
+
description: `${this._tools.length} tools available`,
|
|
187
|
+
childrenCount: this._tools.length
|
|
188
|
+
} }
|
|
189
|
+
};
|
|
190
|
+
if (this._prompts.length > 0) children.prompts = { metadata: {
|
|
191
|
+
kind: "afs:node",
|
|
192
|
+
kinds: require_kinds.getKindsArray("afs:node"),
|
|
193
|
+
description: `${this._prompts.length} prompts available`,
|
|
194
|
+
childrenCount: this._prompts.length
|
|
195
|
+
} };
|
|
196
|
+
if (this._resources.length > 0 || this._resourceTemplates.length > 0) children.resources = { metadata: {
|
|
197
|
+
kind: "afs:node",
|
|
198
|
+
kinds: require_kinds.getKindsArray("afs:node"),
|
|
199
|
+
description: `${this._resources.length} resources available`,
|
|
200
|
+
childrenCount: this._resources.length
|
|
201
|
+
} };
|
|
202
|
+
return {
|
|
203
|
+
metadata: {
|
|
204
|
+
kind: "mcp:module",
|
|
205
|
+
kinds: require_kinds.getKindsArray("mcp:module"),
|
|
206
|
+
description: this.description || "MCP Server",
|
|
207
|
+
mcp: {
|
|
208
|
+
server: { name: this.name },
|
|
209
|
+
capabilities: {
|
|
210
|
+
tools: this._tools.length > 0,
|
|
211
|
+
prompts: this._prompts.length > 0,
|
|
212
|
+
resources: this._resources.length > 0
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
children
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
get isConnected() {
|
|
220
|
+
return this._isConnected;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Get cached tools
|
|
224
|
+
*/
|
|
225
|
+
get tools() {
|
|
226
|
+
return this._tools;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Get cached prompts
|
|
230
|
+
*/
|
|
231
|
+
get prompts() {
|
|
232
|
+
return this._prompts;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Get cached resources
|
|
236
|
+
*/
|
|
237
|
+
get resources() {
|
|
238
|
+
return this._resources;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Get cached resource templates
|
|
242
|
+
*/
|
|
243
|
+
get resourceTemplates() {
|
|
244
|
+
return this._resourceTemplates;
|
|
245
|
+
}
|
|
246
|
+
/** Promise for in-progress connection */
|
|
247
|
+
_connectPromise = null;
|
|
248
|
+
/**
|
|
249
|
+
* Ensure connection is established (lazy connect)
|
|
250
|
+
*/
|
|
251
|
+
async ensureConnected() {
|
|
252
|
+
if (this._isConnected) return;
|
|
253
|
+
if (this._connectPromise) return this._connectPromise;
|
|
254
|
+
this._connectPromise = this.connect();
|
|
255
|
+
try {
|
|
256
|
+
await this._connectPromise;
|
|
257
|
+
} finally {
|
|
258
|
+
this._connectPromise = null;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Connect to the MCP server
|
|
263
|
+
*/
|
|
264
|
+
async connect() {
|
|
265
|
+
if (this._isConnected) return;
|
|
266
|
+
this.transport = this.createTransport();
|
|
267
|
+
this.client = new _modelcontextprotocol_sdk_client_index_js.Client({
|
|
268
|
+
name: "afs-mcp-client",
|
|
269
|
+
version: "1.0.0"
|
|
270
|
+
}, { capabilities: {} });
|
|
271
|
+
await this.client.connect(this.transport);
|
|
272
|
+
this._isConnected = true;
|
|
273
|
+
await this.refreshCapabilities();
|
|
274
|
+
this.buildResourcePathMap();
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Disconnect from the MCP server
|
|
278
|
+
*/
|
|
279
|
+
async disconnect() {
|
|
280
|
+
if (!this._isConnected) return;
|
|
281
|
+
try {
|
|
282
|
+
await this.client?.close();
|
|
283
|
+
} catch (error) {
|
|
284
|
+
if (!(error instanceof Error && (error.message.includes("EPIPE") || error.code === "EPIPE"))) throw error;
|
|
285
|
+
}
|
|
286
|
+
this.client = null;
|
|
287
|
+
this.transport = null;
|
|
288
|
+
this._isConnected = false;
|
|
289
|
+
this._tools = [];
|
|
290
|
+
this._prompts = [];
|
|
291
|
+
this._resources = [];
|
|
292
|
+
this._resourceTemplates = [];
|
|
293
|
+
this._resourcePathMap.clear();
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Create transport based on configuration
|
|
297
|
+
*/
|
|
298
|
+
createTransport() {
|
|
299
|
+
switch (this.options.transport) {
|
|
300
|
+
case "stdio": return new _modelcontextprotocol_sdk_client_stdio_js.StdioClientTransport({
|
|
301
|
+
command: this.options.command,
|
|
302
|
+
args: this.options.args,
|
|
303
|
+
env: {
|
|
304
|
+
...process.env,
|
|
305
|
+
...this.options.env
|
|
306
|
+
},
|
|
307
|
+
stderr: "pipe"
|
|
308
|
+
});
|
|
309
|
+
case "http": return new _modelcontextprotocol_sdk_client_streamableHttp_js.StreamableHTTPClientTransport(new URL(this.options.url), { requestInit: { headers: this.options.headers } });
|
|
310
|
+
case "sse": return new _modelcontextprotocol_sdk_client_sse_js.SSEClientTransport(new URL(this.options.url), { requestInit: { headers: this.options.headers } });
|
|
311
|
+
default: throw new Error(`Unknown transport: ${this.options.transport}`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Refresh cached capabilities from the server
|
|
316
|
+
*/
|
|
317
|
+
async refreshCapabilities() {
|
|
318
|
+
if (!this.client) return;
|
|
319
|
+
try {
|
|
320
|
+
this._tools = (await this.client.listTools()).tools || [];
|
|
321
|
+
} catch {
|
|
322
|
+
this._tools = [];
|
|
323
|
+
}
|
|
324
|
+
try {
|
|
325
|
+
this._prompts = (await this.client.listPrompts()).prompts || [];
|
|
326
|
+
} catch {
|
|
327
|
+
this._prompts = [];
|
|
328
|
+
}
|
|
329
|
+
try {
|
|
330
|
+
this._resources = (await this.client.listResources()).resources || [];
|
|
331
|
+
} catch {
|
|
332
|
+
this._resources = [];
|
|
333
|
+
}
|
|
334
|
+
try {
|
|
335
|
+
this._resourceTemplates = (await this.client.listResourceTemplates()).resourceTemplates || [];
|
|
336
|
+
} catch {
|
|
337
|
+
this._resourceTemplates = [];
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Build the resource path mapping from cached resources
|
|
342
|
+
*/
|
|
343
|
+
buildResourcePathMap() {
|
|
344
|
+
this._resourcePathMap.clear();
|
|
345
|
+
for (const resource of this._resources) {
|
|
346
|
+
const path = this.resourceUriToPath(resource.uri);
|
|
347
|
+
if (path) this._resourcePathMap.set(path, { resource });
|
|
348
|
+
}
|
|
349
|
+
for (const template of this._resourceTemplates) {
|
|
350
|
+
const basePath = this.getTemplateBasePath(template.uriTemplate);
|
|
351
|
+
if (basePath) this._resourcePathMap.set(basePath, { template });
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Convert a resource URI to an AFS path
|
|
356
|
+
*
|
|
357
|
+
* Examples:
|
|
358
|
+
* - "file:///path/to/file.txt" -> "/path/to/file.txt"
|
|
359
|
+
* - "sqlite://posts" -> "/posts"
|
|
360
|
+
* - "github://repos" -> "/repos"
|
|
361
|
+
*/
|
|
362
|
+
resourceUriToPath(uri) {
|
|
363
|
+
return AFSMCP.parseResourceUri(uri).path;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Get the base path from a URI template (path before first variable)
|
|
367
|
+
*
|
|
368
|
+
* Examples:
|
|
369
|
+
* - "sqlite://posts/{id}" -> "/posts"
|
|
370
|
+
* - "github://repos/{owner}/{repo}" -> "/repos"
|
|
371
|
+
*/
|
|
372
|
+
getTemplateBasePath(uriTemplate) {
|
|
373
|
+
const parsed = AFSMCP.parseResourceUri(uriTemplate);
|
|
374
|
+
const varIndex = parsed.path.indexOf("{");
|
|
375
|
+
if (varIndex === -1) return parsed.path;
|
|
376
|
+
const basePath = parsed.path.substring(0, varIndex);
|
|
377
|
+
return basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Find a resource or template that matches a given path
|
|
381
|
+
*/
|
|
382
|
+
findResourceForPath(path) {
|
|
383
|
+
for (const resource of this._resources) if (this.resourceUriToPath(resource.uri) === path) return { resource };
|
|
384
|
+
for (const template of this._resourceTemplates) {
|
|
385
|
+
const params = AFSMCP.matchPathToTemplate(path, template.uriTemplate);
|
|
386
|
+
if (params) return {
|
|
387
|
+
template,
|
|
388
|
+
params
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Convert a Tool to an AFSEntry (Meta Spec compliant)
|
|
395
|
+
*/
|
|
396
|
+
toolToEntry(tool) {
|
|
397
|
+
return {
|
|
398
|
+
id: `/tools/${tool.name}`,
|
|
399
|
+
path: `/tools/${tool.name}`,
|
|
400
|
+
summary: tool.description,
|
|
401
|
+
metadata: {
|
|
402
|
+
kind: "mcp:tool",
|
|
403
|
+
kinds: require_kinds.getKindsArray("mcp:tool"),
|
|
404
|
+
description: tool.description,
|
|
405
|
+
inputSchema: tool.inputSchema,
|
|
406
|
+
mcp: { name: tool.name }
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Convert a Prompt to an AFSEntry (Meta Spec compliant)
|
|
412
|
+
*/
|
|
413
|
+
promptToEntry(prompt) {
|
|
414
|
+
return {
|
|
415
|
+
id: `/prompts/${prompt.name}`,
|
|
416
|
+
path: `/prompts/${prompt.name}`,
|
|
417
|
+
summary: prompt.description,
|
|
418
|
+
metadata: {
|
|
419
|
+
kind: "mcp:prompt",
|
|
420
|
+
kinds: require_kinds.getKindsArray("mcp:prompt"),
|
|
421
|
+
description: prompt.description,
|
|
422
|
+
mcp: {
|
|
423
|
+
name: prompt.name,
|
|
424
|
+
arguments: prompt.arguments
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Convert a Resource to an AFSEntry (Meta Spec compliant)
|
|
431
|
+
*/
|
|
432
|
+
resourceToEntry(resource, path) {
|
|
433
|
+
return {
|
|
434
|
+
id: path,
|
|
435
|
+
path,
|
|
436
|
+
summary: resource.description || resource.name,
|
|
437
|
+
metadata: {
|
|
438
|
+
kind: "mcp:resource",
|
|
439
|
+
kinds: require_kinds.getKindsArray("mcp:resource"),
|
|
440
|
+
description: resource.description,
|
|
441
|
+
mimeType: resource.mimeType,
|
|
442
|
+
mcp: {
|
|
443
|
+
uri: resource.uri,
|
|
444
|
+
name: resource.name
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Convert a ResourceTemplate to an AFSEntry (Meta Spec compliant)
|
|
451
|
+
*/
|
|
452
|
+
resourceTemplateToEntry(template, path) {
|
|
453
|
+
return {
|
|
454
|
+
id: path,
|
|
455
|
+
path,
|
|
456
|
+
summary: template.description || template.name,
|
|
457
|
+
metadata: {
|
|
458
|
+
kind: "mcp:resource-template",
|
|
459
|
+
kinds: require_kinds.getKindsArray("mcp:resource"),
|
|
460
|
+
description: template.description,
|
|
461
|
+
mimeType: template.mimeType,
|
|
462
|
+
mcp: {
|
|
463
|
+
uriTemplate: template.uriTemplate,
|
|
464
|
+
name: template.name
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* List tools.
|
|
471
|
+
*
|
|
472
|
+
* Returns tool children only. The /tools directory entry comes from @StaticEntries
|
|
473
|
+
* and will be merged with dynamic childrenCount.
|
|
474
|
+
*/
|
|
475
|
+
async listToolsHandler(_ctx) {
|
|
476
|
+
await this.ensureConnected();
|
|
477
|
+
return { data: this._tools.map((tool) => this.toolToEntry(tool)) };
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* List prompts.
|
|
481
|
+
*
|
|
482
|
+
* Returns prompt children only. The /prompts directory entry comes from @StaticEntries
|
|
483
|
+
* and will be merged with dynamic childrenCount.
|
|
484
|
+
*/
|
|
485
|
+
async listPromptsHandler(_ctx) {
|
|
486
|
+
await this.ensureConnected();
|
|
487
|
+
return { data: this._prompts.map((prompt) => this.promptToEntry(prompt)) };
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* List specific tool
|
|
491
|
+
*/
|
|
492
|
+
async listToolHandler(ctx) {
|
|
493
|
+
await this.ensureConnected();
|
|
494
|
+
const tool = this._tools.find((t) => t.name === ctx.params.name);
|
|
495
|
+
if (!tool) throw new _aigne_afs.AFSNotFoundError(ctx.path);
|
|
496
|
+
return { data: [this.toolToEntry(tool)] };
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* List specific prompt
|
|
500
|
+
*/
|
|
501
|
+
async listPromptHandler(ctx) {
|
|
502
|
+
await this.ensureConnected();
|
|
503
|
+
const prompt = this._prompts.find((p) => p.name === ctx.params.name);
|
|
504
|
+
if (!prompt) throw new _aigne_afs.AFSNotFoundError(ctx.path);
|
|
505
|
+
return { data: [this.promptToEntry(prompt)] };
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* List resources directory.
|
|
509
|
+
*
|
|
510
|
+
* Returns all resources as children of /resources.
|
|
511
|
+
*/
|
|
512
|
+
async listResourcesHandler(_ctx) {
|
|
513
|
+
await this.ensureConnected();
|
|
514
|
+
const immediateChildren = /* @__PURE__ */ new Map();
|
|
515
|
+
for (const resource of this._resources) {
|
|
516
|
+
const resourcePath = this.resourceUriToPath(resource.uri);
|
|
517
|
+
if (!resourcePath) continue;
|
|
518
|
+
const segments = resourcePath.split("/").filter(Boolean);
|
|
519
|
+
if (segments.length === 0) continue;
|
|
520
|
+
const childPath = `/resources/${segments[0]}`;
|
|
521
|
+
if (segments.length === 1) immediateChildren.set(childPath, {
|
|
522
|
+
isDir: false,
|
|
523
|
+
resource
|
|
524
|
+
});
|
|
525
|
+
else if (!immediateChildren.has(childPath)) immediateChildren.set(childPath, { isDir: true });
|
|
526
|
+
}
|
|
527
|
+
const entries = [];
|
|
528
|
+
for (const [path, info] of immediateChildren) if (info.isDir) entries.push({
|
|
529
|
+
id: path,
|
|
530
|
+
path,
|
|
531
|
+
summary: `Resource directory: ${path.replace("/resources", "")}`,
|
|
532
|
+
metadata: {
|
|
533
|
+
kind: "afs:node",
|
|
534
|
+
kinds: require_kinds.getKindsArray("afs:node"),
|
|
535
|
+
mcp: { isResource: true }
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
else if (info.resource) entries.push(this.resourceToEntry(info.resource, path));
|
|
539
|
+
return { data: entries };
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* List resource paths (wildcard handler under /resources)
|
|
543
|
+
*/
|
|
544
|
+
async listResourceHandler(ctx) {
|
|
545
|
+
await this.ensureConnected();
|
|
546
|
+
const resourcePath = `/${ctx.params.path}`;
|
|
547
|
+
const afsPath = `/resources${resourcePath}`;
|
|
548
|
+
const entries = [];
|
|
549
|
+
let exactMatch = null;
|
|
550
|
+
const immediateChildren = /* @__PURE__ */ new Map();
|
|
551
|
+
const depth = resourcePath.split("/").filter(Boolean).length;
|
|
552
|
+
for (const resource of this._resources) {
|
|
553
|
+
const rPath = this.resourceUriToPath(resource.uri);
|
|
554
|
+
if (!rPath) continue;
|
|
555
|
+
if (rPath === resourcePath) exactMatch = resource;
|
|
556
|
+
else if (rPath.startsWith(`${resourcePath}/`)) {
|
|
557
|
+
const segments = rPath.split("/").filter(Boolean);
|
|
558
|
+
if (segments.length <= depth) continue;
|
|
559
|
+
const childPath = `/resources${resourcePath}/${segments[depth]}`;
|
|
560
|
+
if (segments.length === depth + 1) immediateChildren.set(childPath, {
|
|
561
|
+
isDir: false,
|
|
562
|
+
resource
|
|
563
|
+
});
|
|
564
|
+
else if (!immediateChildren.has(childPath)) immediateChildren.set(childPath, { isDir: true });
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
if (exactMatch) entries.push(this.resourceToEntry(exactMatch, afsPath));
|
|
568
|
+
else if (immediateChildren.size > 0) {
|
|
569
|
+
entries.push({
|
|
570
|
+
id: afsPath,
|
|
571
|
+
path: afsPath,
|
|
572
|
+
summary: `Resource directory: ${resourcePath}`,
|
|
573
|
+
metadata: {
|
|
574
|
+
kind: "afs:node",
|
|
575
|
+
kinds: require_kinds.getKindsArray("afs:node"),
|
|
576
|
+
childrenCount: immediateChildren.size,
|
|
577
|
+
mcp: { isResource: true }
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
for (const [path, info] of immediateChildren) if (info.isDir) entries.push({
|
|
581
|
+
id: path,
|
|
582
|
+
path,
|
|
583
|
+
summary: `Resource directory: ${path.replace("/resources", "")}`,
|
|
584
|
+
metadata: {
|
|
585
|
+
kind: "afs:node",
|
|
586
|
+
kinds: require_kinds.getKindsArray("afs:node"),
|
|
587
|
+
mcp: { isResource: true }
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
else if (info.resource) entries.push(this.resourceToEntry(info.resource, path));
|
|
591
|
+
} else throw new _aigne_afs.AFSNotFoundError(ctx.path);
|
|
592
|
+
return { data: entries };
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Read metadata for tools (dynamic entries not in static tree).
|
|
596
|
+
* Static entries metadata is handled by AFSBaseProvider.
|
|
597
|
+
*/
|
|
598
|
+
async readToolMeta(ctx) {
|
|
599
|
+
await this.ensureConnected();
|
|
600
|
+
const tool = this._tools.find((t) => t.name === ctx.params.name);
|
|
601
|
+
if (!tool) throw new _aigne_afs.AFSNotFoundError(ctx.path);
|
|
602
|
+
const entry = this.toolToEntry(tool);
|
|
603
|
+
return {
|
|
604
|
+
id: `/tools/${ctx.params.name}/.meta`,
|
|
605
|
+
path: `/tools/${ctx.params.name}/.meta`,
|
|
606
|
+
metadata: entry.metadata
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Read metadata for prompts (dynamic entries not in static tree).
|
|
611
|
+
*/
|
|
612
|
+
async readPromptMeta(ctx) {
|
|
613
|
+
await this.ensureConnected();
|
|
614
|
+
const prompt = this._prompts.find((p) => p.name === ctx.params.name);
|
|
615
|
+
if (!prompt) throw new _aigne_afs.AFSNotFoundError(ctx.path);
|
|
616
|
+
const entry = this.promptToEntry(prompt);
|
|
617
|
+
return {
|
|
618
|
+
id: `/prompts/${ctx.params.name}/.meta`,
|
|
619
|
+
path: `/prompts/${ctx.params.name}/.meta`,
|
|
620
|
+
metadata: entry.metadata
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Read metadata for resources (dynamic entries not in static tree).
|
|
625
|
+
* Handles both actual resources and synthesized intermediate directories.
|
|
626
|
+
*/
|
|
627
|
+
async readResourceMeta(ctx) {
|
|
628
|
+
await this.ensureConnected();
|
|
629
|
+
const resourcePath = `/${ctx.params.path}`;
|
|
630
|
+
const metaPath = `/resources${resourcePath}/.meta`;
|
|
631
|
+
const resourceMatch = this.findResourceForPath(resourcePath);
|
|
632
|
+
if (resourceMatch) {
|
|
633
|
+
if (resourceMatch.resource) return {
|
|
634
|
+
id: metaPath,
|
|
635
|
+
path: metaPath,
|
|
636
|
+
metadata: this.resourceToEntry(resourceMatch.resource, `/resources${resourcePath}`).metadata
|
|
637
|
+
};
|
|
638
|
+
else if (resourceMatch.template) return {
|
|
639
|
+
id: metaPath,
|
|
640
|
+
path: metaPath,
|
|
641
|
+
metadata: this.resourceTemplateToEntry(resourceMatch.template, `/resources${resourcePath}`).metadata
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
if (this._resources.some((resource) => {
|
|
645
|
+
return this.resourceUriToPath(resource.uri)?.startsWith(`${resourcePath}/`);
|
|
646
|
+
})) return {
|
|
647
|
+
id: metaPath,
|
|
648
|
+
path: metaPath,
|
|
649
|
+
metadata: {
|
|
650
|
+
kind: "afs:node",
|
|
651
|
+
kinds: require_kinds.getKindsArray("afs:node"),
|
|
652
|
+
mcp: { isResource: true }
|
|
653
|
+
}
|
|
654
|
+
};
|
|
655
|
+
throw new _aigne_afs.AFSNotFoundError(ctx.path);
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Read tool
|
|
659
|
+
*/
|
|
660
|
+
async readToolHandler(ctx) {
|
|
661
|
+
await this.ensureConnected();
|
|
662
|
+
const tool = this._tools.find((t) => t.name === ctx.params.name);
|
|
663
|
+
if (!tool) throw new _aigne_afs.AFSNotFoundError(ctx.path);
|
|
664
|
+
return this.toolToEntry(tool);
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Read prompt
|
|
668
|
+
*/
|
|
669
|
+
async readPromptHandler(ctx) {
|
|
670
|
+
await this.ensureConnected();
|
|
671
|
+
const prompt = this._prompts.find((p) => p.name === ctx.params.name);
|
|
672
|
+
if (!prompt) throw new _aigne_afs.AFSNotFoundError(ctx.path);
|
|
673
|
+
return this.promptToEntry(prompt);
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* Read resource (wildcard handler under /resources)
|
|
677
|
+
*/
|
|
678
|
+
async readResourceHandler(ctx) {
|
|
679
|
+
await this.ensureConnected();
|
|
680
|
+
const resourcePath = `/${ctx.params.path}`;
|
|
681
|
+
const afsPath = `/resources${resourcePath}`;
|
|
682
|
+
const resourceMatch = this.findResourceForPath(resourcePath);
|
|
683
|
+
if (resourceMatch) {
|
|
684
|
+
if (resourceMatch.resource) {
|
|
685
|
+
const result = await this.readResourceByUri(resourceMatch.resource.uri);
|
|
686
|
+
if (!result.data) throw new _aigne_afs.AFSNotFoundError(ctx.path, result.message || `Resource not found: ${ctx.path}`);
|
|
687
|
+
result.data.path = `/resources${result.data.path}`;
|
|
688
|
+
result.data.id = result.data.path;
|
|
689
|
+
return result.data;
|
|
690
|
+
} else if (resourceMatch.template && resourceMatch.params) {
|
|
691
|
+
const uri = AFSMCP.buildUriFromTemplate(resourceMatch.template.uriTemplate, resourceMatch.params);
|
|
692
|
+
const result = await this.readResourceByUri(uri);
|
|
693
|
+
if (!result.data) throw new _aigne_afs.AFSNotFoundError(ctx.path, result.message || `Resource not found: ${ctx.path}`);
|
|
694
|
+
result.data.path = `/resources${result.data.path}`;
|
|
695
|
+
result.data.id = result.data.path;
|
|
696
|
+
return result.data;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
if (this._resources.some((resource) => {
|
|
700
|
+
return this.resourceUriToPath(resource.uri)?.startsWith(`${resourcePath}/`);
|
|
701
|
+
})) return {
|
|
702
|
+
id: afsPath,
|
|
703
|
+
path: afsPath,
|
|
704
|
+
summary: `Resource directory: ${resourcePath}`,
|
|
705
|
+
metadata: {
|
|
706
|
+
kind: "afs:node",
|
|
707
|
+
kinds: require_kinds.getKindsArray("afs:node"),
|
|
708
|
+
mcp: { isResource: true }
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
throw new _aigne_afs.AFSNotFoundError(ctx.path);
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Read a resource by its URI (internal helper)
|
|
715
|
+
*/
|
|
716
|
+
async readResourceByUri(uri) {
|
|
717
|
+
if (!this.client) return {
|
|
718
|
+
data: void 0,
|
|
719
|
+
message: "MCP client not connected"
|
|
720
|
+
};
|
|
721
|
+
try {
|
|
722
|
+
const result = await this.client.readResource({ uri });
|
|
723
|
+
const path = this.resourceUriToPath(uri) || uri;
|
|
724
|
+
let content;
|
|
725
|
+
let mimeType;
|
|
726
|
+
if (result.contents && result.contents.length > 0) {
|
|
727
|
+
const firstContent = result.contents[0];
|
|
728
|
+
mimeType = firstContent.mimeType;
|
|
729
|
+
if ("text" in firstContent) content = firstContent.text;
|
|
730
|
+
else if ("blob" in firstContent) content = firstContent.blob;
|
|
731
|
+
}
|
|
732
|
+
return { data: {
|
|
733
|
+
id: path,
|
|
734
|
+
path,
|
|
735
|
+
content,
|
|
736
|
+
metadata: { mcp: {
|
|
737
|
+
uri,
|
|
738
|
+
mimeType
|
|
739
|
+
} }
|
|
740
|
+
} };
|
|
741
|
+
} catch (error) {
|
|
742
|
+
return {
|
|
743
|
+
data: void 0,
|
|
744
|
+
message: `Failed to read resource: ${error.message}`
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* Read a prompt with arguments, returning the prompt content
|
|
750
|
+
*
|
|
751
|
+
* This is a specialized method that calls the MCP getPrompt API
|
|
752
|
+
* to get the actual prompt content with substituted arguments.
|
|
753
|
+
*/
|
|
754
|
+
async readPrompt(path, args) {
|
|
755
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
756
|
+
if (!normalizedPath.startsWith("/prompts/")) return {
|
|
757
|
+
data: void 0,
|
|
758
|
+
message: `readPrompt only supported on /prompts/* paths, got: ${normalizedPath}`
|
|
759
|
+
};
|
|
760
|
+
if (!this.client) return {
|
|
761
|
+
data: void 0,
|
|
762
|
+
message: "MCP client not connected"
|
|
763
|
+
};
|
|
764
|
+
const promptName = normalizedPath.slice(9);
|
|
765
|
+
const prompt = this._prompts.find((p) => p.name === promptName);
|
|
766
|
+
if (!prompt) return {
|
|
767
|
+
data: void 0,
|
|
768
|
+
message: `Prompt not found: ${promptName}`
|
|
769
|
+
};
|
|
770
|
+
try {
|
|
771
|
+
const result = await this.client.getPrompt({
|
|
772
|
+
name: promptName,
|
|
773
|
+
arguments: args
|
|
774
|
+
});
|
|
775
|
+
return { data: {
|
|
776
|
+
id: `/prompts/${promptName}`,
|
|
777
|
+
path: `/prompts/${promptName}`,
|
|
778
|
+
summary: prompt.description,
|
|
779
|
+
content: result.messages,
|
|
780
|
+
metadata: {
|
|
781
|
+
arguments: prompt.arguments,
|
|
782
|
+
mcp: {
|
|
783
|
+
name: prompt.name,
|
|
784
|
+
description: prompt.description,
|
|
785
|
+
arguments: prompt.arguments
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
} };
|
|
789
|
+
} catch (error) {
|
|
790
|
+
return {
|
|
791
|
+
data: void 0,
|
|
792
|
+
message: `Failed to get prompt: ${error.message}`
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Generate WORLD.md content describing this MCP server's capabilities
|
|
798
|
+
*/
|
|
799
|
+
generateWorldMd() {
|
|
800
|
+
const lines = [];
|
|
801
|
+
lines.push(`# ${this.name}`);
|
|
802
|
+
lines.push("");
|
|
803
|
+
if (this.description) {
|
|
804
|
+
lines.push(this.description);
|
|
805
|
+
lines.push("");
|
|
806
|
+
}
|
|
807
|
+
lines.push("## Server Information");
|
|
808
|
+
lines.push("");
|
|
809
|
+
lines.push(`- **Name**: ${this.name}`);
|
|
810
|
+
lines.push(`- **Transport**: ${this.options.transport}`);
|
|
811
|
+
if (this.options.transport === "stdio") lines.push(`- **Command**: ${this.options.command}`);
|
|
812
|
+
else lines.push(`- **URL**: ${this.options.url}`);
|
|
813
|
+
lines.push("");
|
|
814
|
+
lines.push("## Capabilities");
|
|
815
|
+
lines.push("");
|
|
816
|
+
lines.push(`- Tools: ${this._tools.length}`);
|
|
817
|
+
lines.push(`- Prompts: ${this._prompts.length}`);
|
|
818
|
+
lines.push(`- Resources: ${this._resources.length}`);
|
|
819
|
+
lines.push(`- Resource Templates: ${this._resourceTemplates.length}`);
|
|
820
|
+
lines.push("");
|
|
821
|
+
if (this._tools.length > 0) {
|
|
822
|
+
lines.push("## Tools");
|
|
823
|
+
lines.push("");
|
|
824
|
+
for (const tool of this._tools) {
|
|
825
|
+
lines.push(`### ${tool.name}`);
|
|
826
|
+
lines.push("");
|
|
827
|
+
if (tool.description) {
|
|
828
|
+
lines.push(tool.description);
|
|
829
|
+
lines.push("");
|
|
830
|
+
}
|
|
831
|
+
lines.push(`**Path**: \`/tools/${tool.name}\``);
|
|
832
|
+
lines.push("");
|
|
833
|
+
if (tool.inputSchema) {
|
|
834
|
+
lines.push("**Input Schema**:");
|
|
835
|
+
lines.push("```json");
|
|
836
|
+
lines.push(JSON.stringify(tool.inputSchema, null, 2));
|
|
837
|
+
lines.push("```");
|
|
838
|
+
lines.push("");
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
if (this._prompts.length > 0) {
|
|
843
|
+
lines.push("## Prompts");
|
|
844
|
+
lines.push("");
|
|
845
|
+
for (const prompt of this._prompts) {
|
|
846
|
+
lines.push(`### ${prompt.name}`);
|
|
847
|
+
lines.push("");
|
|
848
|
+
if (prompt.description) {
|
|
849
|
+
lines.push(prompt.description);
|
|
850
|
+
lines.push("");
|
|
851
|
+
}
|
|
852
|
+
lines.push(`**Path**: \`/prompts/${prompt.name}\``);
|
|
853
|
+
lines.push("");
|
|
854
|
+
if (prompt.arguments && prompt.arguments.length > 0) {
|
|
855
|
+
lines.push("**Arguments**:");
|
|
856
|
+
for (const arg of prompt.arguments) {
|
|
857
|
+
const required = arg.required ? " (required)" : " (optional)";
|
|
858
|
+
lines.push(`- \`${arg.name}\`${required}: ${arg.description || ""}`);
|
|
859
|
+
}
|
|
860
|
+
lines.push("");
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
if (this._resources.length > 0) {
|
|
865
|
+
lines.push("## Resources");
|
|
866
|
+
lines.push("");
|
|
867
|
+
for (const resource of this._resources) {
|
|
868
|
+
lines.push(`### ${resource.name}`);
|
|
869
|
+
lines.push("");
|
|
870
|
+
if (resource.description) {
|
|
871
|
+
lines.push(resource.description);
|
|
872
|
+
lines.push("");
|
|
873
|
+
}
|
|
874
|
+
lines.push(`**URI**: \`${resource.uri}\``);
|
|
875
|
+
const afsPath = this.resourceUriToPath(resource.uri);
|
|
876
|
+
if (afsPath) lines.push(`**AFS Path**: \`${afsPath}\``);
|
|
877
|
+
if (resource.mimeType) lines.push(`**MIME Type**: ${resource.mimeType}`);
|
|
878
|
+
lines.push("");
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
if (this._resourceTemplates.length > 0) {
|
|
882
|
+
lines.push("## Resource Templates");
|
|
883
|
+
lines.push("");
|
|
884
|
+
for (const template of this._resourceTemplates) {
|
|
885
|
+
lines.push(`### ${template.name}`);
|
|
886
|
+
lines.push("");
|
|
887
|
+
if (template.description) {
|
|
888
|
+
lines.push(template.description);
|
|
889
|
+
lines.push("");
|
|
890
|
+
}
|
|
891
|
+
lines.push(`**URI Template**: \`${template.uriTemplate}\``);
|
|
892
|
+
const vars = AFSMCP.parseUriTemplate(template.uriTemplate);
|
|
893
|
+
if (vars.length > 0) lines.push(`**Variables**: ${vars.map((v) => `\`{${v}}\``).join(", ")}`);
|
|
894
|
+
if (template.mimeType) lines.push(`**MIME Type**: ${template.mimeType}`);
|
|
895
|
+
lines.push("");
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
return lines.join("\n");
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Execute a tool
|
|
902
|
+
*/
|
|
903
|
+
async execToolHandler(ctx, args) {
|
|
904
|
+
await this.ensureConnected();
|
|
905
|
+
if (!this.client) throw new Error("MCP client not connected");
|
|
906
|
+
if (!this._tools.find((t) => t.name === ctx.params.name)) throw new Error(`Tool not found: ${ctx.params.name}`);
|
|
907
|
+
return {
|
|
908
|
+
success: true,
|
|
909
|
+
data: await this.client.callTool({
|
|
910
|
+
name: ctx.params.name,
|
|
911
|
+
arguments: args
|
|
912
|
+
})
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.StaticEntries)()], AFSMCP.prototype, "defineEntries", null);
|
|
917
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.List)("/tools")], AFSMCP.prototype, "listToolsHandler", null);
|
|
918
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.List)("/prompts")], AFSMCP.prototype, "listPromptsHandler", null);
|
|
919
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.List)("/tools/:name")], AFSMCP.prototype, "listToolHandler", null);
|
|
920
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.List)("/prompts/:name")], AFSMCP.prototype, "listPromptHandler", null);
|
|
921
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.List)("/resources")], AFSMCP.prototype, "listResourcesHandler", null);
|
|
922
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.List)("/resources/:path+")], AFSMCP.prototype, "listResourceHandler", null);
|
|
923
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/tools/:name")], AFSMCP.prototype, "readToolMeta", null);
|
|
924
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/prompts/:name")], AFSMCP.prototype, "readPromptMeta", null);
|
|
925
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/resources/:path+")], AFSMCP.prototype, "readResourceMeta", null);
|
|
926
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/tools/:name")], AFSMCP.prototype, "readToolHandler", null);
|
|
927
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/prompts/:name")], AFSMCP.prototype, "readPromptHandler", null);
|
|
928
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/resources/:path+")], AFSMCP.prototype, "readResourceHandler", null);
|
|
929
|
+
require_decorate.__decorate([(0, _aigne_afs_provider.Exec)("/tools/:name")], AFSMCP.prototype, "execToolHandler", null);
|
|
930
|
+
|
|
931
|
+
//#endregion
|
|
932
|
+
exports.AFSMCP = AFSMCP;
|