@aigne/afs-mcp 1.11.0-beta.6 → 1.11.0-beta.8

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.cjs CHANGED
@@ -1,6 +1,6 @@
1
+ Object.defineProperty(exports, '__esModule', { value: true });
1
2
  const require_kinds = require('./kinds.cjs');
2
3
  const require_decorate = require('./_virtual/_@oxc-project_runtime@0.108.0/helpers/decorate.cjs');
3
- let node_path = require("node:path");
4
4
  let _aigne_afs = require("@aigne/afs");
5
5
  let _aigne_afs_provider = require("@aigne/afs/provider");
6
6
  let _aigne_afs_utils_zod = require("@aigne/afs/utils/zod");
@@ -12,14 +12,6 @@ let zod = require("zod");
12
12
 
13
13
  //#region src/index.ts
14
14
  /**
15
- * AFS MCP Provider
16
- *
17
- * 将 MCP Server 挂载为 AFS 可访问的世界。
18
- * - Tools → 可执行的 AFS entries(通过 `exec()`)
19
- * - Prompts → 可读取的世界描述
20
- * - Resources → 可读取的世界状态(展开为 AFS 目录结构)
21
- */
22
- /**
23
15
  * Zod schema for options validation
24
16
  */
25
17
  const afsMCPOptionsSchema = (0, _aigne_afs_utils_zod.camelize)(zod.z.object({
@@ -32,9 +24,9 @@ const afsMCPOptionsSchema = (0, _aigne_afs_utils_zod.camelize)(zod.z.object({
32
24
  ]),
33
25
  command: (0, _aigne_afs_utils_zod.optionalize)(zod.z.string()),
34
26
  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())),
27
+ env: (0, _aigne_afs_utils_zod.optionalize)(zod.z.record(zod.z.string(), zod.z.string())),
36
28
  url: (0, _aigne_afs_utils_zod.optionalize)(zod.z.string()),
37
- headers: (0, _aigne_afs_utils_zod.optionalize)(zod.z.record(zod.z.string())),
29
+ headers: (0, _aigne_afs_utils_zod.optionalize)(zod.z.record(zod.z.string(), zod.z.string())),
38
30
  timeout: (0, _aigne_afs_utils_zod.optionalize)(zod.z.number()),
39
31
  maxReconnects: (0, _aigne_afs_utils_zod.optionalize)(zod.z.number())
40
32
  }).refine((data) => {
@@ -55,13 +47,57 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
55
47
  static schema() {
56
48
  return afsMCPOptionsSchema;
57
49
  }
50
+ static manifest() {
51
+ return [
52
+ {
53
+ name: "mcp-stdio",
54
+ description: "MCP server via stdio transport",
55
+ uriTemplate: "mcp+stdio://{command+}",
56
+ category: "integration",
57
+ schema: zod.z.object({
58
+ command: zod.z.string(),
59
+ args: zod.z.array(zod.z.string()).optional(),
60
+ env: zod.z.record(zod.z.string(), zod.z.string()).optional()
61
+ }),
62
+ tags: [
63
+ "mcp",
64
+ "stdio",
65
+ "integration"
66
+ ]
67
+ },
68
+ {
69
+ name: "mcp-http",
70
+ description: "MCP server via HTTP transport",
71
+ uriTemplate: "mcp+http://{url+}",
72
+ category: "integration",
73
+ schema: zod.z.object({ url: zod.z.string() }),
74
+ tags: [
75
+ "mcp",
76
+ "http",
77
+ "integration"
78
+ ]
79
+ },
80
+ {
81
+ name: "mcp-sse",
82
+ description: "MCP server via SSE transport",
83
+ uriTemplate: "mcp+sse://{url+}",
84
+ category: "integration",
85
+ schema: zod.z.object({ url: zod.z.string() }),
86
+ tags: [
87
+ "mcp",
88
+ "sse",
89
+ "integration"
90
+ ]
91
+ }
92
+ ];
93
+ }
58
94
  /**
59
95
  * Load module from configuration file
60
96
  */
61
- static async load({ filepath, parsed }) {
97
+ static async load({ basePath, config } = {}) {
62
98
  return new AFSMCP({
63
- ...await AFSMCP.schema().parseAsync(parsed),
64
- cwd: (0, node_path.dirname)(filepath)
99
+ ...await AFSMCP.schema().parseAsync(config),
100
+ cwd: basePath
65
101
  });
66
102
  }
67
103
  /**
@@ -73,7 +109,7 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
73
109
  * - "github://repos/owner/repo" -> { scheme: "github", path: "/repos/owner/repo" }
74
110
  */
75
111
  static parseResourceUri(uri) {
76
- const match = uri.match(/^(\w+):\/\/\/?(.*)$/);
112
+ const match = uri.match(/^([\w+.-]+):\/\/\/?(.*)$/);
77
113
  if (match) {
78
114
  const scheme = match[1];
79
115
  const rest = match[2];
@@ -151,59 +187,130 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
151
187
  constructor(options) {
152
188
  super();
153
189
  this.options = options;
190
+ if (!options.transport && options.uri) {
191
+ const uri = options.uri;
192
+ if (uri.startsWith("mcp+stdio://")) {
193
+ options.transport = "stdio";
194
+ if (!options.command) options.command = uri.slice(12).split("?")[0];
195
+ } else if (uri.startsWith("mcp+http://")) {
196
+ options.transport = "http";
197
+ if (!options.url) options.url = uri.replace("mcp+", "");
198
+ } else if (uri.startsWith("mcp+sse://")) {
199
+ options.transport = "sse";
200
+ if (!options.url) options.url = uri.replace("mcp+sse://", "http://");
201
+ }
202
+ }
203
+ if (typeof options.args === "string") {
204
+ const argsStr = options.args;
205
+ options.args = argsStr.includes(",") ? argsStr.split(",") : [argsStr];
206
+ } else if (Array.isArray(options.args)) options.args = options.args.flatMap((a) => a.includes(",") ? a.split(",") : [a]);
207
+ if (options.env && !Array.isArray(options.env) && typeof options.env === "string") {
208
+ const [key, ...rest] = options.env.split("=");
209
+ options.env = key ? { [key]: rest.join("=") } : {};
210
+ } else if (Array.isArray(options.env)) {
211
+ const envRecord = {};
212
+ for (const entry of options.env) {
213
+ const [key, ...rest] = entry.split("=");
214
+ if (key) envRecord[key] = rest.join("=");
215
+ }
216
+ options.env = envRecord;
217
+ }
154
218
  (0, _aigne_afs_utils_zod.zodParse)(afsMCPOptionsSchema, options);
155
219
  this.name = options.name || "mcp";
156
220
  this.description = options.description;
157
221
  }
158
222
  /**
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: {
223
+ * List root directory children.
224
+ * Note: list() returns only children, never the path itself (per new semantics)
225
+ */
226
+ async listRootHandler(_ctx) {
227
+ await this.ensureConnected();
228
+ const entries = [];
229
+ entries.push({
230
+ id: "/WORLD.md",
231
+ path: "/WORLD.md",
232
+ summary: "MCP Server World Documentation",
233
+ meta: {
234
+ kind: "afs:document",
235
+ kinds: require_kinds.getKindsArray("afs:document"),
236
+ description: "MCP Server World Documentation",
237
+ mimeType: "text/markdown",
238
+ mcp: { type: "world" }
239
+ }
240
+ });
241
+ entries.push({
242
+ id: "/tools",
243
+ path: "/tools",
244
+ summary: `${this._tools.length} tools available`,
245
+ meta: {
184
246
  kind: "afs:node",
185
247
  kinds: require_kinds.getKindsArray("afs:node"),
186
248
  description: `${this._tools.length} tools available`,
187
249
  childrenCount: this._tools.length
188
- } }
250
+ }
251
+ });
252
+ if (this._prompts.length > 0) entries.push({
253
+ id: "/prompts",
254
+ path: "/prompts",
255
+ summary: `${this._prompts.length} prompts available`,
256
+ meta: {
257
+ kind: "afs:node",
258
+ kinds: require_kinds.getKindsArray("afs:node"),
259
+ description: `${this._prompts.length} prompts available`,
260
+ childrenCount: this._prompts.length
261
+ }
262
+ });
263
+ if (this._resources.length > 0 || this._resourceTemplates.length > 0) entries.push({
264
+ id: "/resources",
265
+ path: "/resources",
266
+ summary: `${this._resources.length} resources available`,
267
+ meta: {
268
+ kind: "afs:node",
269
+ kinds: require_kinds.getKindsArray("afs:node"),
270
+ description: `${this._resources.length} resources available`,
271
+ childrenCount: this._resources.length
272
+ }
273
+ });
274
+ return { data: entries };
275
+ }
276
+ /**
277
+ * Read root directory entry
278
+ */
279
+ async readRootHandler(_ctx) {
280
+ await this.ensureConnected();
281
+ return {
282
+ id: "/",
283
+ path: "/",
284
+ summary: this.description || "MCP Server",
285
+ meta: {
286
+ kind: "mcp:module",
287
+ kinds: require_kinds.getKindsArray("mcp:module"),
288
+ description: this.description || "MCP Server",
289
+ childrenCount: 2 + (this._prompts.length > 0 ? 1 : 0) + (this._resources.length > 0 ? 1 : 0),
290
+ mcp: {
291
+ server: { name: this.name },
292
+ capabilities: {
293
+ tools: this._tools.length > 0,
294
+ prompts: this._prompts.length > 0,
295
+ resources: this._resources.length > 0
296
+ }
297
+ }
298
+ }
189
299
  };
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
- } };
300
+ }
301
+ /**
302
+ * Read root metadata
303
+ */
304
+ async readRootMeta(_ctx) {
305
+ await this.ensureConnected();
202
306
  return {
203
- metadata: {
307
+ id: "/.meta",
308
+ path: "/.meta",
309
+ meta: {
204
310
  kind: "mcp:module",
205
311
  kinds: require_kinds.getKindsArray("mcp:module"),
206
312
  description: this.description || "MCP Server",
313
+ childrenCount: 2 + (this._prompts.length > 0 ? 1 : 0) + (this._resources.length > 0 ? 1 : 0),
207
314
  mcp: {
208
315
  server: { name: this.name },
209
316
  capabilities: {
@@ -212,10 +319,361 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
212
319
  resources: this._resources.length > 0
213
320
  }
214
321
  }
322
+ }
323
+ };
324
+ }
325
+ /**
326
+ * Read capabilities manifest
327
+ *
328
+ * Returns all MCP tools as ToolDefinition objects.
329
+ * MCP has no node-level actions, so actions is always empty.
330
+ */
331
+ async readCapabilities(_ctx) {
332
+ await this.ensureConnected();
333
+ const tools = this._tools.filter((tool) => tool.name).map((tool) => ({
334
+ name: tool.name,
335
+ description: tool.description,
336
+ path: `/tools/${tool.name}`,
337
+ inputSchema: tool.inputSchema
338
+ }));
339
+ return {
340
+ id: "/.meta/.capabilities",
341
+ path: "/.meta/.capabilities",
342
+ content: {
343
+ schemaVersion: 1,
344
+ provider: this.name,
345
+ version: "1.0.0",
346
+ description: this.description,
347
+ tools,
348
+ actions: [],
349
+ operations: this.getOperationsDeclaration()
215
350
  },
216
- children
351
+ meta: {
352
+ kind: "afs:capabilities",
353
+ description: "MCP Provider capabilities manifest"
354
+ }
355
+ };
356
+ }
357
+ /**
358
+ * Stat root directory
359
+ */
360
+ async statRootHandler(_ctx) {
361
+ await this.ensureConnected();
362
+ const childrenCount = 2 + (this._prompts.length > 0 ? 1 : 0) + (this._resources.length > 0 ? 1 : 0);
363
+ return { data: {
364
+ id: "/",
365
+ path: "/",
366
+ meta: {
367
+ kind: "mcp:module",
368
+ kinds: require_kinds.getKindsArray("mcp:module"),
369
+ description: this.description || "MCP Server",
370
+ childrenCount
371
+ }
372
+ } };
373
+ }
374
+ /**
375
+ * Read WORLD.md file
376
+ */
377
+ async readWorldMdHandler(_ctx) {
378
+ await this.ensureConnected();
379
+ return {
380
+ id: "/WORLD.md",
381
+ path: "/WORLD.md",
382
+ content: this.generateWorldMd(),
383
+ meta: {
384
+ kind: "afs:document",
385
+ kinds: require_kinds.getKindsArray("afs:document"),
386
+ description: "MCP Server World Documentation",
387
+ mimeType: "text/markdown",
388
+ mcp: { type: "world" }
389
+ }
390
+ };
391
+ }
392
+ /**
393
+ * List WORLD.md - files have no children
394
+ * Note: list() returns only children, never the path itself (per new semantics)
395
+ */
396
+ async listWorldMdHandler(_ctx) {
397
+ await this.ensureConnected();
398
+ return { data: [] };
399
+ }
400
+ /**
401
+ * Read WORLD.md metadata
402
+ */
403
+ async readWorldMdMeta(_ctx) {
404
+ await this.ensureConnected();
405
+ return {
406
+ id: "/WORLD.md/.meta",
407
+ path: "/WORLD.md/.meta",
408
+ meta: {
409
+ kind: "afs:document",
410
+ kinds: require_kinds.getKindsArray("afs:document"),
411
+ description: "MCP Server World Documentation",
412
+ mimeType: "text/markdown",
413
+ mcp: { type: "world" }
414
+ }
415
+ };
416
+ }
417
+ /**
418
+ * Read /tools directory entry
419
+ */
420
+ async readToolsDir(_ctx) {
421
+ await this.ensureConnected();
422
+ return {
423
+ id: "/tools",
424
+ path: "/tools",
425
+ summary: `${this._tools.length} tools available`,
426
+ meta: {
427
+ kind: "afs:node",
428
+ kinds: require_kinds.getKindsArray("afs:node"),
429
+ description: `${this._tools.length} tools available`,
430
+ childrenCount: this._tools.length
431
+ }
432
+ };
433
+ }
434
+ /**
435
+ * Read /tools metadata
436
+ */
437
+ async readToolsMeta(_ctx) {
438
+ await this.ensureConnected();
439
+ return {
440
+ id: "/tools/.meta",
441
+ path: "/tools/.meta",
442
+ meta: {
443
+ kind: "afs:node",
444
+ kinds: require_kinds.getKindsArray("afs:node"),
445
+ description: `${this._tools.length} tools available`,
446
+ childrenCount: this._tools.length
447
+ }
448
+ };
449
+ }
450
+ /**
451
+ * Read /prompts directory entry
452
+ */
453
+ async readPromptsDir(_ctx) {
454
+ await this.ensureConnected();
455
+ if (this._prompts.length === 0) throw new _aigne_afs.AFSNotFoundError("/prompts");
456
+ return {
457
+ id: "/prompts",
458
+ path: "/prompts",
459
+ summary: `${this._prompts.length} prompts available`,
460
+ meta: {
461
+ kind: "afs:node",
462
+ kinds: require_kinds.getKindsArray("afs:node"),
463
+ description: `${this._prompts.length} prompts available`,
464
+ childrenCount: this._prompts.length
465
+ }
217
466
  };
218
467
  }
468
+ /**
469
+ * Read /prompts metadata
470
+ */
471
+ async readPromptsMeta(_ctx) {
472
+ await this.ensureConnected();
473
+ if (this._prompts.length === 0) throw new _aigne_afs.AFSNotFoundError("/prompts/.meta");
474
+ return {
475
+ id: "/prompts/.meta",
476
+ path: "/prompts/.meta",
477
+ meta: {
478
+ kind: "afs:node",
479
+ kinds: require_kinds.getKindsArray("afs:node"),
480
+ description: `${this._prompts.length} prompts available`,
481
+ childrenCount: this._prompts.length
482
+ }
483
+ };
484
+ }
485
+ /**
486
+ * Read /resources directory entry
487
+ */
488
+ async readResourcesDir(_ctx) {
489
+ await this.ensureConnected();
490
+ if (this._resources.length === 0 && this._resourceTemplates.length === 0) throw new _aigne_afs.AFSNotFoundError("/resources");
491
+ const immediateChildrenCount = this.getResourcesImmediateChildrenCount();
492
+ return {
493
+ id: "/resources",
494
+ path: "/resources",
495
+ summary: `${this._resources.length} resources available`,
496
+ meta: {
497
+ kind: "afs:node",
498
+ kinds: require_kinds.getKindsArray("afs:node"),
499
+ description: `${this._resources.length} resources available`,
500
+ childrenCount: immediateChildrenCount
501
+ }
502
+ };
503
+ }
504
+ /**
505
+ * Read /resources metadata
506
+ */
507
+ async readResourcesMeta(_ctx) {
508
+ await this.ensureConnected();
509
+ if (this._resources.length === 0 && this._resourceTemplates.length === 0) throw new _aigne_afs.AFSNotFoundError("/resources/.meta");
510
+ const immediateChildrenCount = this.getResourcesImmediateChildrenCount();
511
+ return {
512
+ id: "/resources/.meta",
513
+ path: "/resources/.meta",
514
+ meta: {
515
+ kind: "afs:node",
516
+ kinds: require_kinds.getKindsArray("afs:node"),
517
+ description: `${this._resources.length} resources available`,
518
+ childrenCount: immediateChildrenCount
519
+ }
520
+ };
521
+ }
522
+ /**
523
+ * Calculate immediate children count for /resources directory
524
+ */
525
+ getResourcesImmediateChildrenCount() {
526
+ const immediateChildren = /* @__PURE__ */ new Set();
527
+ for (const resource of this._resources) {
528
+ const resourcePath = this.resourceUriToPath(resource.uri);
529
+ if (!resourcePath) continue;
530
+ const segments = resourcePath.split("/").filter(Boolean);
531
+ if (segments.length > 0) immediateChildren.add(segments[0]);
532
+ }
533
+ return immediateChildren.size;
534
+ }
535
+ /**
536
+ * Stat WORLD.md file
537
+ */
538
+ async statWorldMdHandler(_ctx) {
539
+ await this.ensureConnected();
540
+ return { data: {
541
+ id: "WORLD.md",
542
+ path: "/WORLD.md",
543
+ meta: {
544
+ kind: "afs:document",
545
+ kinds: require_kinds.getKindsArray("afs:document"),
546
+ description: "MCP Server World Documentation",
547
+ mimeType: "text/markdown",
548
+ childrenCount: 0
549
+ }
550
+ } };
551
+ }
552
+ /**
553
+ * Stat /tools directory
554
+ */
555
+ async statToolsHandler(_ctx) {
556
+ await this.ensureConnected();
557
+ return { data: {
558
+ id: "tools",
559
+ path: "/tools",
560
+ meta: {
561
+ kind: "afs:node",
562
+ kinds: require_kinds.getKindsArray("afs:node"),
563
+ description: `${this._tools.length} tools available`,
564
+ childrenCount: this._tools.length
565
+ }
566
+ } };
567
+ }
568
+ /**
569
+ * Stat /prompts directory
570
+ */
571
+ async statPromptsHandler(_ctx) {
572
+ await this.ensureConnected();
573
+ if (this._prompts.length === 0) throw new _aigne_afs.AFSNotFoundError("/prompts");
574
+ return { data: {
575
+ id: "prompts",
576
+ path: "/prompts",
577
+ meta: {
578
+ kind: "afs:node",
579
+ kinds: require_kinds.getKindsArray("afs:node"),
580
+ description: `${this._prompts.length} prompts available`,
581
+ childrenCount: this._prompts.length
582
+ }
583
+ } };
584
+ }
585
+ /**
586
+ * Stat /resources directory
587
+ */
588
+ async statResourcesHandler(_ctx) {
589
+ await this.ensureConnected();
590
+ if (this._resources.length === 0 && this._resourceTemplates.length === 0) throw new _aigne_afs.AFSNotFoundError("/resources");
591
+ const immediateChildrenCount = this.getResourcesImmediateChildrenCount();
592
+ return { data: {
593
+ id: "resources",
594
+ path: "/resources",
595
+ meta: {
596
+ kind: "afs:node",
597
+ kinds: require_kinds.getKindsArray("afs:node"),
598
+ description: `${this._resources.length} resources available`,
599
+ childrenCount: immediateChildrenCount
600
+ }
601
+ } };
602
+ }
603
+ /**
604
+ * Stat specific tool
605
+ */
606
+ async statToolHandler(ctx) {
607
+ await this.ensureConnected();
608
+ const tool = this._tools.find((t) => t.name === ctx.params.name);
609
+ if (!tool) throw new _aigne_afs.AFSNotFoundError(ctx.path);
610
+ const { content: _content, ...statData } = this.toolToEntry(tool);
611
+ return { data: statData };
612
+ }
613
+ /**
614
+ * Stat specific prompt
615
+ */
616
+ async statPromptHandler(ctx) {
617
+ await this.ensureConnected();
618
+ const prompt = this._prompts.find((p) => p.name === ctx.params.name);
619
+ if (!prompt) throw new _aigne_afs.AFSNotFoundError(ctx.path);
620
+ const { content: _content, ...statData } = this.promptToEntry(prompt);
621
+ return { data: statData };
622
+ }
623
+ /**
624
+ * Stat resource paths (wildcard handler under /resources)
625
+ */
626
+ async statResourceHandler(ctx) {
627
+ await this.ensureConnected();
628
+ const resourcePath = `/${ctx.params.path}`;
629
+ const afsPath = `/resources${resourcePath}`;
630
+ const resourceId = ctx.params.path.split("/").pop() || ctx.params.path;
631
+ const resourceMatch = this.findResourceForPath(resourcePath);
632
+ if (resourceMatch) {
633
+ if (resourceMatch.resource) return { data: {
634
+ id: resourceId,
635
+ path: afsPath,
636
+ meta: {
637
+ kind: "mcp:resource",
638
+ kinds: require_kinds.getKindsArray("mcp:resource"),
639
+ description: resourceMatch.resource.description,
640
+ mimeType: resourceMatch.resource.mimeType,
641
+ childrenCount: 0
642
+ }
643
+ } };
644
+ else if (resourceMatch.template) return { data: {
645
+ id: resourceId,
646
+ path: afsPath,
647
+ meta: {
648
+ kind: "mcp:resource-template",
649
+ kinds: require_kinds.getKindsArray("mcp:resource"),
650
+ description: resourceMatch.template.description,
651
+ mimeType: resourceMatch.template.mimeType,
652
+ childrenCount: 0
653
+ }
654
+ } };
655
+ }
656
+ const immediateChildren = this.getImmediateResourceChildren(resourcePath);
657
+ if (immediateChildren.size > 0) return { data: {
658
+ id: resourceId,
659
+ path: afsPath,
660
+ meta: {
661
+ kind: "afs:node",
662
+ kinds: require_kinds.getKindsArray("afs:node"),
663
+ childrenCount: immediateChildren.size
664
+ }
665
+ } };
666
+ if (this.isTemplateBasePath(resourcePath)) return { data: {
667
+ id: resourceId,
668
+ path: afsPath,
669
+ meta: {
670
+ kind: "afs:node",
671
+ kinds: require_kinds.getKindsArray("afs:node"),
672
+ childrenCount: 0
673
+ }
674
+ } };
675
+ throw new _aigne_afs.AFSNotFoundError(ctx.path);
676
+ }
219
677
  get isConnected() {
220
678
  return this._isConnected;
221
679
  }
@@ -377,6 +835,35 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
377
835
  return basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
378
836
  }
379
837
  /**
838
+ * Check if a resource path is a template base path (has dynamic children).
839
+ */
840
+ isTemplateBasePath(resourcePath) {
841
+ return this._resourceTemplates.some((template) => {
842
+ return this.getTemplateBasePath(template.uriTemplate) === resourcePath;
843
+ });
844
+ }
845
+ /**
846
+ * Get immediate child segments under a resource parent path,
847
+ * scanning both static resources and template base paths.
848
+ */
849
+ getImmediateResourceChildren(parentPath) {
850
+ const depth = parentPath.split("/").filter(Boolean).length;
851
+ const childSegments = /* @__PURE__ */ new Set();
852
+ for (const resource of this._resources) {
853
+ const rPath = this.resourceUriToPath(resource.uri);
854
+ if (!rPath || !rPath.startsWith(`${parentPath}/`)) continue;
855
+ const segments = rPath.split("/").filter(Boolean);
856
+ if (segments.length > depth) childSegments.add(segments[depth]);
857
+ }
858
+ for (const template of this._resourceTemplates) {
859
+ const basePath = this.getTemplateBasePath(template.uriTemplate);
860
+ if (!basePath || !basePath.startsWith(`${parentPath}/`)) continue;
861
+ const segments = basePath.split("/").filter(Boolean);
862
+ if (segments.length > depth) childSegments.add(segments[depth]);
863
+ }
864
+ return childSegments;
865
+ }
866
+ /**
380
867
  * Find a resource or template that matches a given path
381
868
  */
382
869
  findResourceForPath(path) {
@@ -398,7 +885,7 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
398
885
  id: `/tools/${tool.name}`,
399
886
  path: `/tools/${tool.name}`,
400
887
  summary: tool.description,
401
- metadata: {
888
+ meta: {
402
889
  kind: "mcp:tool",
403
890
  kinds: require_kinds.getKindsArray("mcp:tool"),
404
891
  description: tool.description,
@@ -409,13 +896,15 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
409
896
  }
410
897
  /**
411
898
  * Convert a Prompt to an AFSEntry (Meta Spec compliant)
899
+ * Note: inputSchema is NOT included in prompt entry meta.
900
+ * For prompts with arguments, use the action system (/.actions/get) to execute.
412
901
  */
413
902
  promptToEntry(prompt) {
414
903
  return {
415
904
  id: `/prompts/${prompt.name}`,
416
905
  path: `/prompts/${prompt.name}`,
417
906
  summary: prompt.description,
418
- metadata: {
907
+ meta: {
419
908
  kind: "mcp:prompt",
420
909
  kinds: require_kinds.getKindsArray("mcp:prompt"),
421
910
  description: prompt.description,
@@ -427,6 +916,41 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
427
916
  };
428
917
  }
429
918
  /**
919
+ * Convert prompt arguments to JSON Schema (for action inputSchema)
920
+ */
921
+ promptArgsToSchema(args) {
922
+ if (!args || args.length === 0) return {
923
+ type: "object",
924
+ properties: {}
925
+ };
926
+ const properties = {};
927
+ const required = [];
928
+ for (const arg of args) {
929
+ properties[arg.name] = {
930
+ type: "string",
931
+ ...arg.description ? { description: arg.description } : {}
932
+ };
933
+ if (arg.required) required.push(arg.name);
934
+ }
935
+ return {
936
+ type: "object",
937
+ properties,
938
+ ...required.length > 0 ? { required } : {}
939
+ };
940
+ }
941
+ /**
942
+ * Extract text content from MCP prompt messages
943
+ */
944
+ extractTextFromMessages(messages) {
945
+ const textParts = [];
946
+ for (const msg of messages) {
947
+ const c = msg.content;
948
+ if (typeof c === "string") textParts.push(c);
949
+ else if (c && typeof c === "object" && "text" in c) textParts.push(c.text);
950
+ }
951
+ return textParts.join("\n");
952
+ }
953
+ /**
430
954
  * Convert a Resource to an AFSEntry (Meta Spec compliant)
431
955
  */
432
956
  resourceToEntry(resource, path) {
@@ -434,7 +958,7 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
434
958
  id: path,
435
959
  path,
436
960
  summary: resource.description || resource.name,
437
- metadata: {
961
+ meta: {
438
962
  kind: "mcp:resource",
439
963
  kinds: require_kinds.getKindsArray("mcp:resource"),
440
964
  description: resource.description,
@@ -454,7 +978,7 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
454
978
  id: path,
455
979
  path,
456
980
  summary: template.description || template.name,
457
- metadata: {
981
+ meta: {
458
982
  kind: "mcp:resource-template",
459
983
  kinds: require_kinds.getKindsArray("mcp:resource"),
460
984
  description: template.description,
@@ -468,9 +992,7 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
468
992
  }
469
993
  /**
470
994
  * List tools.
471
- *
472
- * Returns tool children only. The /tools directory entry comes from @StaticEntries
473
- * and will be merged with dynamic childrenCount.
995
+ * Note: list() returns only children, never the path itself (per new semantics)
474
996
  */
475
997
  async listToolsHandler(_ctx) {
476
998
  await this.ensureConnected();
@@ -478,39 +1000,38 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
478
1000
  }
479
1001
  /**
480
1002
  * List prompts.
481
- *
482
- * Returns prompt children only. The /prompts directory entry comes from @StaticEntries
483
- * and will be merged with dynamic childrenCount.
1003
+ * Note: list() returns only children, never the path itself (per new semantics)
484
1004
  */
485
1005
  async listPromptsHandler(_ctx) {
486
1006
  await this.ensureConnected();
1007
+ if (this._prompts.length === 0) throw new _aigne_afs.AFSNotFoundError("/prompts");
487
1008
  return { data: this._prompts.map((prompt) => this.promptToEntry(prompt)) };
488
1009
  }
489
1010
  /**
490
- * List specific tool
1011
+ * List specific tool - tools are leaf nodes with no children
1012
+ * Note: list() returns only children, never the path itself (per new semantics)
491
1013
  */
492
1014
  async listToolHandler(ctx) {
493
1015
  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)] };
1016
+ if (!this._tools.find((t) => t.name === ctx.params.name)) throw new _aigne_afs.AFSNotFoundError(ctx.path);
1017
+ return { data: [] };
497
1018
  }
498
1019
  /**
499
- * List specific prompt
1020
+ * List specific prompt - prompts are leaf nodes with no children
1021
+ * Note: list() returns only children, never the path itself (per new semantics)
500
1022
  */
501
1023
  async listPromptHandler(ctx) {
502
1024
  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)] };
1025
+ if (!this._prompts.find((p) => p.name === ctx.params.name)) throw new _aigne_afs.AFSNotFoundError(ctx.path);
1026
+ return { data: [] };
506
1027
  }
507
1028
  /**
508
1029
  * List resources directory.
509
- *
510
- * Returns all resources as children of /resources.
1030
+ * Note: list() returns only children, never the path itself (per new semantics)
511
1031
  */
512
1032
  async listResourcesHandler(_ctx) {
513
1033
  await this.ensureConnected();
1034
+ if (this._resources.length === 0 && this._resourceTemplates.length === 0) throw new _aigne_afs.AFSNotFoundError("/resources");
514
1035
  const immediateChildren = /* @__PURE__ */ new Map();
515
1036
  for (const resource of this._resources) {
516
1037
  const resourcePath = this.resourceUriToPath(resource.uri);
@@ -524,27 +1045,39 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
524
1045
  });
525
1046
  else if (!immediateChildren.has(childPath)) immediateChildren.set(childPath, { isDir: true });
526
1047
  }
1048
+ for (const template of this._resourceTemplates) {
1049
+ const basePath = this.getTemplateBasePath(template.uriTemplate);
1050
+ if (!basePath) continue;
1051
+ const segments = basePath.split("/").filter(Boolean);
1052
+ if (segments.length === 0) continue;
1053
+ const childPath = `/resources/${segments[0]}`;
1054
+ if (!immediateChildren.has(childPath)) immediateChildren.set(childPath, { isDir: true });
1055
+ }
527
1056
  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));
1057
+ for (const [path, info] of immediateChildren) if (info.isDir) {
1058
+ const resourceSubPath = path.replace("/resources", "");
1059
+ const childrenCount = this.getImmediateResourceChildren(resourceSubPath).size;
1060
+ entries.push({
1061
+ id: path,
1062
+ path,
1063
+ summary: `Resource directory: ${resourceSubPath}`,
1064
+ meta: {
1065
+ kind: "afs:node",
1066
+ kinds: require_kinds.getKindsArray("afs:node"),
1067
+ childrenCount,
1068
+ mcp: { isResource: true }
1069
+ }
1070
+ });
1071
+ } else if (info.resource) entries.push(this.resourceToEntry(info.resource, path));
539
1072
  return { data: entries };
540
1073
  }
541
1074
  /**
542
1075
  * List resource paths (wildcard handler under /resources)
1076
+ * Note: list() returns only children, never the path itself (per new semantics)
543
1077
  */
544
1078
  async listResourceHandler(ctx) {
545
1079
  await this.ensureConnected();
546
1080
  const resourcePath = `/${ctx.params.path}`;
547
- const afsPath = `/resources${resourcePath}`;
548
1081
  const entries = [];
549
1082
  let exactMatch = null;
550
1083
  const immediateChildren = /* @__PURE__ */ new Map();
@@ -564,32 +1097,37 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
564
1097
  else if (!immediateChildren.has(childPath)) immediateChildren.set(childPath, { isDir: true });
565
1098
  }
566
1099
  }
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 };
1100
+ for (const template of this._resourceTemplates) {
1101
+ const basePath = this.getTemplateBasePath(template.uriTemplate);
1102
+ if (!basePath) continue;
1103
+ if (basePath === resourcePath) return { data: [] };
1104
+ else if (basePath.startsWith(`${resourcePath}/`)) {
1105
+ const segments = basePath.split("/").filter(Boolean);
1106
+ if (segments.length <= depth) continue;
1107
+ const childPath = `/resources${resourcePath}/${segments[depth]}`;
1108
+ if (!immediateChildren.has(childPath)) immediateChildren.set(childPath, { isDir: true });
1109
+ }
1110
+ }
1111
+ if (exactMatch) return { data: [] };
1112
+ if (immediateChildren.size > 0) {
1113
+ for (const [path, info] of immediateChildren) if (info.isDir) {
1114
+ const resourceSubPath = path.replace("/resources", "");
1115
+ const childrenCount = this.getImmediateResourceChildren(resourceSubPath).size;
1116
+ entries.push({
1117
+ id: path,
1118
+ path,
1119
+ summary: `Resource directory: ${resourceSubPath}`,
1120
+ meta: {
1121
+ kind: "afs:node",
1122
+ kinds: require_kinds.getKindsArray("afs:node"),
1123
+ childrenCount,
1124
+ mcp: { isResource: true }
1125
+ }
1126
+ });
1127
+ } else if (info.resource) entries.push(this.resourceToEntry(info.resource, path));
1128
+ return { data: entries };
1129
+ }
1130
+ throw new _aigne_afs.AFSNotFoundError(ctx.path);
593
1131
  }
594
1132
  /**
595
1133
  * Read metadata for tools (dynamic entries not in static tree).
@@ -603,7 +1141,7 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
603
1141
  return {
604
1142
  id: `/tools/${ctx.params.name}/.meta`,
605
1143
  path: `/tools/${ctx.params.name}/.meta`,
606
- metadata: entry.metadata
1144
+ meta: entry.meta
607
1145
  };
608
1146
  }
609
1147
  /**
@@ -617,7 +1155,7 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
617
1155
  return {
618
1156
  id: `/prompts/${ctx.params.name}/.meta`,
619
1157
  path: `/prompts/${ctx.params.name}/.meta`,
620
- metadata: entry.metadata
1158
+ meta: entry.meta
621
1159
  };
622
1160
  }
623
1161
  /**
@@ -633,25 +1171,31 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
633
1171
  if (resourceMatch.resource) return {
634
1172
  id: metaPath,
635
1173
  path: metaPath,
636
- metadata: this.resourceToEntry(resourceMatch.resource, `/resources${resourcePath}`).metadata
1174
+ meta: this.resourceToEntry(resourceMatch.resource, `/resources${resourcePath}`).meta
637
1175
  };
638
1176
  else if (resourceMatch.template) return {
639
1177
  id: metaPath,
640
1178
  path: metaPath,
641
- metadata: this.resourceTemplateToEntry(resourceMatch.template, `/resources${resourcePath}`).metadata
1179
+ meta: this.resourceTemplateToEntry(resourceMatch.template, `/resources${resourcePath}`).meta
642
1180
  };
643
1181
  }
644
- if (this._resources.some((resource) => {
645
- return this.resourceUriToPath(resource.uri)?.startsWith(`${resourcePath}/`);
646
- })) return {
1182
+ if (this.getImmediateResourceChildren(resourcePath).size > 0) return {
647
1183
  id: metaPath,
648
1184
  path: metaPath,
649
- metadata: {
1185
+ meta: {
650
1186
  kind: "afs:node",
651
1187
  kinds: require_kinds.getKindsArray("afs:node"),
652
1188
  mcp: { isResource: true }
653
1189
  }
654
1190
  };
1191
+ if (this.isTemplateBasePath(resourcePath)) {
1192
+ const template = this._resourceTemplates.find((t) => this.getTemplateBasePath(t.uriTemplate) === resourcePath);
1193
+ if (template) return {
1194
+ id: metaPath,
1195
+ path: metaPath,
1196
+ meta: this.resourceTemplateToEntry(template, `/resources${resourcePath}`).meta
1197
+ };
1198
+ }
655
1199
  throw new _aigne_afs.AFSNotFoundError(ctx.path);
656
1200
  }
657
1201
  /**
@@ -665,12 +1209,195 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
665
1209
  }
666
1210
  /**
667
1211
  * Read prompt
1212
+ *
1213
+ * Behavior:
1214
+ * - Prompts with NO arguments: returns content directly
1215
+ * - Prompts with ONLY optional arguments: returns content (empty params)
1216
+ * - Prompts with REQUIRED arguments: returns metadata only (use /.actions/get to execute)
668
1217
  */
669
1218
  async readPromptHandler(ctx) {
670
1219
  await this.ensureConnected();
671
1220
  const prompt = this._prompts.find((p) => p.name === ctx.params.name);
672
1221
  if (!prompt) throw new _aigne_afs.AFSNotFoundError(ctx.path);
673
- return this.promptToEntry(prompt);
1222
+ const entry = this.promptToEntry(prompt);
1223
+ const hasRequiredArgs = prompt.arguments?.some((arg) => arg.required) ?? false;
1224
+ if (this.client && !hasRequiredArgs) try {
1225
+ const result = await this.client.getPrompt({
1226
+ name: prompt.name,
1227
+ arguments: {}
1228
+ });
1229
+ const content = this.extractTextFromMessages(result.messages);
1230
+ if (content) entry.content = content;
1231
+ } catch {}
1232
+ return entry;
1233
+ }
1234
+ /**
1235
+ * List actions for a prompt
1236
+ *
1237
+ * Only prompts with arguments expose a "get" action.
1238
+ */
1239
+ async listPromptActions(ctx) {
1240
+ await this.ensureConnected();
1241
+ const prompt = this._prompts.find((p) => p.name === ctx.params.name);
1242
+ if (!prompt) throw new _aigne_afs.AFSNotFoundError(ctx.path);
1243
+ if (!prompt.arguments || prompt.arguments.length === 0) return { data: [] };
1244
+ return { data: [{
1245
+ id: "get",
1246
+ path: `/prompts/${prompt.name}/.actions/get`,
1247
+ summary: `Get ${prompt.name} prompt content with arguments`,
1248
+ meta: {
1249
+ kind: "afs:executable",
1250
+ kinds: require_kinds.getKindsArray("afs:executable"),
1251
+ name: "get",
1252
+ description: prompt.description,
1253
+ inputSchema: this.promptArgsToSchema(prompt.arguments)
1254
+ }
1255
+ }] };
1256
+ }
1257
+ /**
1258
+ * Execute prompt "get" action
1259
+ *
1260
+ * Fetches prompt content with provided arguments.
1261
+ */
1262
+ async execPromptGetHandler(ctx, params) {
1263
+ await this.ensureConnected();
1264
+ const prompt = this._prompts.find((p) => p.name === ctx.params.name);
1265
+ if (!prompt) return {
1266
+ success: false,
1267
+ error: {
1268
+ code: "NOT_FOUND",
1269
+ message: `Prompt not found: ${ctx.params.name}`
1270
+ }
1271
+ };
1272
+ if (!this.client) return {
1273
+ success: false,
1274
+ error: {
1275
+ code: "NOT_CONNECTED",
1276
+ message: "MCP client not connected"
1277
+ }
1278
+ };
1279
+ try {
1280
+ const result = await this.client.getPrompt({
1281
+ name: prompt.name,
1282
+ arguments: params
1283
+ });
1284
+ return {
1285
+ success: true,
1286
+ data: {
1287
+ content: this.extractTextFromMessages(result.messages),
1288
+ meta: { mcp: {
1289
+ name: prompt.name,
1290
+ arguments: prompt.arguments
1291
+ } }
1292
+ }
1293
+ };
1294
+ } catch (error) {
1295
+ return {
1296
+ success: false,
1297
+ error: {
1298
+ code: "EXECUTION_ERROR",
1299
+ message: error.message
1300
+ }
1301
+ };
1302
+ }
1303
+ }
1304
+ /**
1305
+ * List actions for a resource template
1306
+ *
1307
+ * Only resource templates expose a "get" action.
1308
+ * Static resources do not have actions.
1309
+ */
1310
+ async listResourceActions(ctx) {
1311
+ await this.ensureConnected();
1312
+ const resourcePath = `/${ctx.params.path}`;
1313
+ if (!this.isTemplateBasePath(resourcePath)) return { data: [] };
1314
+ const template = this._resourceTemplates.find((t) => this.getTemplateBasePath(t.uriTemplate) === resourcePath);
1315
+ if (!template) return { data: [] };
1316
+ const vars = AFSMCP.parseUriTemplate(template.uriTemplate);
1317
+ if (vars.length === 0) return { data: [] };
1318
+ const properties = {};
1319
+ for (const v of vars) properties[v] = {
1320
+ type: "string",
1321
+ description: `Template variable: ${v}`
1322
+ };
1323
+ const inputSchema = {
1324
+ type: "object",
1325
+ properties,
1326
+ required: vars
1327
+ };
1328
+ return { data: [{
1329
+ id: "get",
1330
+ path: `${`/resources${resourcePath}`}/.actions/get`,
1331
+ summary: `Get resource with template parameters`,
1332
+ meta: {
1333
+ kind: "afs:executable",
1334
+ kinds: require_kinds.getKindsArray("afs:executable"),
1335
+ name: "get",
1336
+ description: template.description,
1337
+ inputSchema
1338
+ }
1339
+ }] };
1340
+ }
1341
+ /**
1342
+ * Execute resource template "get" action
1343
+ *
1344
+ * Fetches resource content with provided template parameters.
1345
+ */
1346
+ async execResourceGetHandler(ctx, params) {
1347
+ await this.ensureConnected();
1348
+ const resourcePath = `/${ctx.params.path}`;
1349
+ if (!this.isTemplateBasePath(resourcePath)) return {
1350
+ success: false,
1351
+ error: {
1352
+ code: "NOT_TEMPLATE",
1353
+ message: `Not a resource template: ${resourcePath}`
1354
+ }
1355
+ };
1356
+ const template = this._resourceTemplates.find((t) => this.getTemplateBasePath(t.uriTemplate) === resourcePath);
1357
+ if (!template) return {
1358
+ success: false,
1359
+ error: {
1360
+ code: "NOT_FOUND",
1361
+ message: `Resource template not found: ${resourcePath}`
1362
+ }
1363
+ };
1364
+ if (!this.client) return {
1365
+ success: false,
1366
+ error: {
1367
+ code: "NOT_CONNECTED",
1368
+ message: "MCP client not connected"
1369
+ }
1370
+ };
1371
+ try {
1372
+ const uri = AFSMCP.buildUriFromTemplate(template.uriTemplate, params);
1373
+ const result = await this.readResourceByUri(uri);
1374
+ if (!result.data) return {
1375
+ success: false,
1376
+ error: {
1377
+ code: "NOT_FOUND",
1378
+ message: result.message || "Resource not found"
1379
+ }
1380
+ };
1381
+ return {
1382
+ success: true,
1383
+ data: {
1384
+ content: result.data.content,
1385
+ meta: { mcp: {
1386
+ uri,
1387
+ name: template.name,
1388
+ uriTemplate: template.uriTemplate
1389
+ } }
1390
+ }
1391
+ };
1392
+ } catch (error) {
1393
+ return {
1394
+ success: false,
1395
+ error: {
1396
+ code: "EXECUTION_ERROR",
1397
+ message: error.message
1398
+ }
1399
+ };
1400
+ }
674
1401
  }
675
1402
  /**
676
1403
  * Read resource (wildcard handler under /resources)
@@ -696,18 +1423,38 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
696
1423
  return result.data;
697
1424
  }
698
1425
  }
699
- if (this._resources.some((resource) => {
700
- return this.resourceUriToPath(resource.uri)?.startsWith(`${resourcePath}/`);
701
- })) return {
1426
+ const immediateChildrenSet = this.getImmediateResourceChildren(resourcePath);
1427
+ if (immediateChildrenSet.size > 0) return {
702
1428
  id: afsPath,
703
1429
  path: afsPath,
704
1430
  summary: `Resource directory: ${resourcePath}`,
705
- metadata: {
1431
+ meta: {
706
1432
  kind: "afs:node",
707
1433
  kinds: require_kinds.getKindsArray("afs:node"),
1434
+ childrenCount: immediateChildrenSet.size,
708
1435
  mcp: { isResource: true }
709
1436
  }
710
1437
  };
1438
+ if (this.isTemplateBasePath(resourcePath)) {
1439
+ const template = this._resourceTemplates.find((t) => this.getTemplateBasePath(t.uriTemplate) === resourcePath);
1440
+ return {
1441
+ id: afsPath,
1442
+ path: afsPath,
1443
+ summary: template?.description || `Resource template: ${resourcePath}`,
1444
+ meta: {
1445
+ kind: "mcp:resource-template",
1446
+ kinds: require_kinds.getKindsArray("mcp:resource"),
1447
+ description: template?.description,
1448
+ mimeType: template?.mimeType,
1449
+ childrenCount: 0,
1450
+ mcp: {
1451
+ uriTemplate: template?.uriTemplate,
1452
+ name: template?.name,
1453
+ parameters: template ? AFSMCP.parseUriTemplate(template.uriTemplate) : []
1454
+ }
1455
+ }
1456
+ };
1457
+ }
711
1458
  throw new _aigne_afs.AFSNotFoundError(ctx.path);
712
1459
  }
713
1460
  /**
@@ -733,7 +1480,7 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
733
1480
  id: path,
734
1481
  path,
735
1482
  content,
736
- metadata: { mcp: {
1483
+ meta: { mcp: {
737
1484
  uri,
738
1485
  mimeType
739
1486
  } }
@@ -777,7 +1524,7 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
777
1524
  path: `/prompts/${promptName}`,
778
1525
  summary: prompt.description,
779
1526
  content: result.messages,
780
- metadata: {
1527
+ meta: {
781
1528
  arguments: prompt.arguments,
782
1529
  mcp: {
783
1530
  name: prompt.name,
@@ -898,6 +1645,191 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
898
1645
  return lines.join("\n");
899
1646
  }
900
1647
  /**
1648
+ * Explain root → server name, tools/prompts/resources counts
1649
+ */
1650
+ async explainRoot(_ctx) {
1651
+ await this.ensureConnected();
1652
+ const lines = [];
1653
+ lines.push(`# ${this.name}`);
1654
+ lines.push("");
1655
+ if (this.description) {
1656
+ lines.push(this.description);
1657
+ lines.push("");
1658
+ }
1659
+ lines.push("## Overview");
1660
+ lines.push("");
1661
+ lines.push(`- **Tool count**: ${this._tools.length}`);
1662
+ lines.push(`- **Prompt count**: ${this._prompts.length}`);
1663
+ lines.push(`- **Resource count**: ${this._resources.length}`);
1664
+ lines.push(`- **Resource template count**: ${this._resourceTemplates.length}`);
1665
+ lines.push("");
1666
+ if (this._tools.length > 0) {
1667
+ lines.push("## Tools");
1668
+ lines.push("");
1669
+ for (const tool of this._tools) lines.push(`- **${tool.name}**: ${tool.description ?? "(no description)"}`);
1670
+ lines.push("");
1671
+ }
1672
+ if (this._prompts.length > 0) {
1673
+ lines.push("## Prompts");
1674
+ lines.push("");
1675
+ for (const prompt of this._prompts) {
1676
+ const argNames = prompt.arguments?.map((a) => a.name).join(", ") ?? "";
1677
+ lines.push(`- **${prompt.name}**: ${prompt.description ?? "(no description)"}${argNames ? ` (args: ${argNames})` : ""}`);
1678
+ }
1679
+ lines.push("");
1680
+ }
1681
+ if (this._resources.length > 0) {
1682
+ lines.push("## Resources");
1683
+ lines.push("");
1684
+ for (const resource of this._resources) lines.push(`- **${resource.name}**: ${resource.description ?? resource.uri}`);
1685
+ lines.push("");
1686
+ }
1687
+ return {
1688
+ format: "markdown",
1689
+ content: lines.join("\n")
1690
+ };
1691
+ }
1692
+ /**
1693
+ * Explain a specific tool → name, description, inputSchema
1694
+ */
1695
+ async explainTool(ctx) {
1696
+ await this.ensureConnected();
1697
+ const tool = this._tools.find((t) => t.name === ctx.params.name);
1698
+ if (!tool) throw new _aigne_afs.AFSNotFoundError(ctx.path, `Tool not found: ${ctx.params.name}`);
1699
+ const lines = [];
1700
+ lines.push(`# Tool: ${tool.name}`);
1701
+ lines.push("");
1702
+ if (tool.description) {
1703
+ lines.push(tool.description);
1704
+ lines.push("");
1705
+ }
1706
+ if (tool.inputSchema) {
1707
+ lines.push("## Input Schema");
1708
+ lines.push("");
1709
+ lines.push("```json");
1710
+ lines.push(JSON.stringify(tool.inputSchema, null, 2));
1711
+ lines.push("```");
1712
+ lines.push("");
1713
+ const props = tool.inputSchema.properties;
1714
+ if (props) {
1715
+ lines.push("## Parameters");
1716
+ lines.push("");
1717
+ for (const [key, val] of Object.entries(props)) {
1718
+ const prop = val;
1719
+ lines.push(`- **${key}** (${prop.type ?? "unknown"}): ${prop.description ?? "(no description)"}`);
1720
+ }
1721
+ lines.push("");
1722
+ }
1723
+ }
1724
+ return {
1725
+ format: "markdown",
1726
+ content: lines.join("\n")
1727
+ };
1728
+ }
1729
+ /**
1730
+ * Explain a specific prompt → name, description, arguments
1731
+ */
1732
+ async explainPrompt(ctx) {
1733
+ await this.ensureConnected();
1734
+ const prompt = this._prompts.find((p) => p.name === ctx.params.name);
1735
+ if (!prompt) throw new _aigne_afs.AFSNotFoundError(ctx.path, `Prompt not found: ${ctx.params.name}`);
1736
+ const lines = [];
1737
+ lines.push(`# Prompt: ${prompt.name}`);
1738
+ lines.push("");
1739
+ if (prompt.description) {
1740
+ lines.push(prompt.description);
1741
+ lines.push("");
1742
+ }
1743
+ if (prompt.arguments && prompt.arguments.length > 0) {
1744
+ lines.push("## Arguments");
1745
+ lines.push("");
1746
+ for (const arg of prompt.arguments) {
1747
+ const required = arg.required ? " (required)" : " (optional)";
1748
+ lines.push(`- **${arg.name}**${required}: ${arg.description ?? "(no description)"}`);
1749
+ }
1750
+ lines.push("");
1751
+ } else {
1752
+ lines.push("*No arguments required.*");
1753
+ lines.push("");
1754
+ }
1755
+ return {
1756
+ format: "markdown",
1757
+ content: lines.join("\n")
1758
+ };
1759
+ }
1760
+ /**
1761
+ * Explain a specific resource → name, URI, description
1762
+ */
1763
+ async explainResource(ctx) {
1764
+ await this.ensureConnected();
1765
+ const resourcePath = `/${ctx.params.path}`;
1766
+ const resourceMatch = this.findResourceForPath(resourcePath);
1767
+ if (resourceMatch?.resource) {
1768
+ const resource = resourceMatch.resource;
1769
+ const lines = [];
1770
+ lines.push(`# Resource: ${resource.name}`);
1771
+ lines.push("");
1772
+ if (resource.description) {
1773
+ lines.push(resource.description);
1774
+ lines.push("");
1775
+ }
1776
+ lines.push(`- **URI**: ${resource.uri}`);
1777
+ if (resource.mimeType) lines.push(`- **MIME Type**: ${resource.mimeType}`);
1778
+ lines.push("");
1779
+ return {
1780
+ format: "markdown",
1781
+ content: lines.join("\n")
1782
+ };
1783
+ }
1784
+ if (resourceMatch?.template) {
1785
+ const template = resourceMatch.template;
1786
+ const lines = [];
1787
+ lines.push(`# Resource Template: ${template.name}`);
1788
+ lines.push("");
1789
+ if (template.description) {
1790
+ lines.push(template.description);
1791
+ lines.push("");
1792
+ }
1793
+ lines.push(`- **URI Template**: ${template.uriTemplate}`);
1794
+ const vars = AFSMCP.parseUriTemplate(template.uriTemplate);
1795
+ if (vars.length > 0) lines.push(`- **Variables**: ${vars.join(", ")}`);
1796
+ lines.push("");
1797
+ return {
1798
+ format: "markdown",
1799
+ content: lines.join("\n")
1800
+ };
1801
+ }
1802
+ throw new _aigne_afs.AFSNotFoundError(ctx.path, `Resource not found: ${ctx.path}`);
1803
+ }
1804
+ /**
1805
+ * Search tools, prompts, and resources by name or description
1806
+ */
1807
+ async searchHandler(_ctx, query, options) {
1808
+ await this.ensureConnected();
1809
+ const results = [];
1810
+ const limit = options?.limit;
1811
+ const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1812
+ const flags = options?.caseSensitive ? "" : "i";
1813
+ const pattern = new RegExp(escapedQuery, flags);
1814
+ const matchAll = query === "";
1815
+ for (const tool of this._tools) {
1816
+ if (limit && results.length >= limit) break;
1817
+ if (matchAll || pattern.test(tool.name) || tool.description && pattern.test(tool.description)) results.push(this.toolToEntry(tool));
1818
+ }
1819
+ for (const prompt of this._prompts) {
1820
+ if (limit && results.length >= limit) break;
1821
+ if (matchAll || pattern.test(prompt.name) || prompt.description && pattern.test(prompt.description)) results.push(this.promptToEntry(prompt));
1822
+ }
1823
+ for (const resource of this._resources) {
1824
+ if (limit && results.length >= limit) break;
1825
+ if (matchAll || pattern.test(resource.name) || resource.description && pattern.test(resource.description)) {
1826
+ const path = this.resourceUriToPath(resource.uri);
1827
+ results.push(this.resourceToEntry(resource, `/resources${path}`));
1828
+ }
1829
+ }
1830
+ return { data: results };
1831
+ }
1832
+ /**
901
1833
  * Execute a tool
902
1834
  */
903
1835
  async execToolHandler(ctx, args) {
@@ -913,7 +1845,27 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
913
1845
  };
914
1846
  }
915
1847
  };
916
- require_decorate.__decorate([(0, _aigne_afs_provider.StaticEntries)()], AFSMCP.prototype, "defineEntries", null);
1848
+ require_decorate.__decorate([(0, _aigne_afs_provider.List)("/")], AFSMCP.prototype, "listRootHandler", null);
1849
+ require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/")], AFSMCP.prototype, "readRootHandler", null);
1850
+ require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/")], AFSMCP.prototype, "readRootMeta", null);
1851
+ require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/.meta/.capabilities")], AFSMCP.prototype, "readCapabilities", null);
1852
+ require_decorate.__decorate([(0, _aigne_afs_provider.Stat)("/")], AFSMCP.prototype, "statRootHandler", null);
1853
+ require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/WORLD.md")], AFSMCP.prototype, "readWorldMdHandler", null);
1854
+ require_decorate.__decorate([(0, _aigne_afs_provider.List)("/WORLD.md")], AFSMCP.prototype, "listWorldMdHandler", null);
1855
+ require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/WORLD.md")], AFSMCP.prototype, "readWorldMdMeta", null);
1856
+ require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/tools")], AFSMCP.prototype, "readToolsDir", null);
1857
+ require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/tools")], AFSMCP.prototype, "readToolsMeta", null);
1858
+ require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/prompts")], AFSMCP.prototype, "readPromptsDir", null);
1859
+ require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/prompts")], AFSMCP.prototype, "readPromptsMeta", null);
1860
+ require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/resources")], AFSMCP.prototype, "readResourcesDir", null);
1861
+ require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/resources")], AFSMCP.prototype, "readResourcesMeta", null);
1862
+ require_decorate.__decorate([(0, _aigne_afs_provider.Stat)("/WORLD.md")], AFSMCP.prototype, "statWorldMdHandler", null);
1863
+ require_decorate.__decorate([(0, _aigne_afs_provider.Stat)("/tools")], AFSMCP.prototype, "statToolsHandler", null);
1864
+ require_decorate.__decorate([(0, _aigne_afs_provider.Stat)("/prompts")], AFSMCP.prototype, "statPromptsHandler", null);
1865
+ require_decorate.__decorate([(0, _aigne_afs_provider.Stat)("/resources")], AFSMCP.prototype, "statResourcesHandler", null);
1866
+ require_decorate.__decorate([(0, _aigne_afs_provider.Stat)("/tools/:name")], AFSMCP.prototype, "statToolHandler", null);
1867
+ require_decorate.__decorate([(0, _aigne_afs_provider.Stat)("/prompts/:name")], AFSMCP.prototype, "statPromptHandler", null);
1868
+ require_decorate.__decorate([(0, _aigne_afs_provider.Stat)("/resources/:path+")], AFSMCP.prototype, "statResourceHandler", null);
917
1869
  require_decorate.__decorate([(0, _aigne_afs_provider.List)("/tools")], AFSMCP.prototype, "listToolsHandler", null);
918
1870
  require_decorate.__decorate([(0, _aigne_afs_provider.List)("/prompts")], AFSMCP.prototype, "listPromptsHandler", null);
919
1871
  require_decorate.__decorate([(0, _aigne_afs_provider.List)("/tools/:name")], AFSMCP.prototype, "listToolHandler", null);
@@ -925,8 +1877,19 @@ require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/prompts/:name")], A
925
1877
  require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/resources/:path+")], AFSMCP.prototype, "readResourceMeta", null);
926
1878
  require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/tools/:name")], AFSMCP.prototype, "readToolHandler", null);
927
1879
  require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/prompts/:name")], AFSMCP.prototype, "readPromptHandler", null);
1880
+ require_decorate.__decorate([(0, _aigne_afs_provider.Actions)("/prompts/:name")], AFSMCP.prototype, "listPromptActions", null);
1881
+ require_decorate.__decorate([_aigne_afs_provider.Actions.Exec("/prompts/:name", "get")], AFSMCP.prototype, "execPromptGetHandler", null);
1882
+ require_decorate.__decorate([(0, _aigne_afs_provider.Actions)("/resources/:path+")], AFSMCP.prototype, "listResourceActions", null);
1883
+ require_decorate.__decorate([_aigne_afs_provider.Actions.Exec("/resources/:path+", "get")], AFSMCP.prototype, "execResourceGetHandler", null);
928
1884
  require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/resources/:path+")], AFSMCP.prototype, "readResourceHandler", null);
1885
+ require_decorate.__decorate([(0, _aigne_afs_provider.Explain)("/")], AFSMCP.prototype, "explainRoot", null);
1886
+ require_decorate.__decorate([(0, _aigne_afs_provider.Explain)("/tools/:name")], AFSMCP.prototype, "explainTool", null);
1887
+ require_decorate.__decorate([(0, _aigne_afs_provider.Explain)("/prompts/:name")], AFSMCP.prototype, "explainPrompt", null);
1888
+ require_decorate.__decorate([(0, _aigne_afs_provider.Explain)("/resources/:path+")], AFSMCP.prototype, "explainResource", null);
1889
+ require_decorate.__decorate([(0, _aigne_afs_provider.Search)("/:path*")], AFSMCP.prototype, "searchHandler", null);
929
1890
  require_decorate.__decorate([(0, _aigne_afs_provider.Exec)("/tools/:name")], AFSMCP.prototype, "execToolHandler", null);
1891
+ var src_default = AFSMCP;
930
1892
 
931
1893
  //#endregion
932
- exports.AFSMCP = AFSMCP;
1894
+ exports.AFSMCP = AFSMCP;
1895
+ exports.default = src_default;