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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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({
@@ -58,10 +50,10 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
58
50
  /**
59
51
  * Load module from configuration file
60
52
  */
61
- static async load({ filepath, parsed }) {
53
+ static async load({ basePath, config } = {}) {
62
54
  return new AFSMCP({
63
- ...await AFSMCP.schema().parseAsync(parsed),
64
- cwd: (0, node_path.dirname)(filepath)
55
+ ...await AFSMCP.schema().parseAsync(config),
56
+ cwd: basePath
65
57
  });
66
58
  }
67
59
  /**
@@ -73,7 +65,7 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
73
65
  * - "github://repos/owner/repo" -> { scheme: "github", path: "/repos/owner/repo" }
74
66
  */
75
67
  static parseResourceUri(uri) {
76
- const match = uri.match(/^(\w+):\/\/\/?(.*)$/);
68
+ const match = uri.match(/^([\w+.-]+):\/\/\/?(.*)$/);
77
69
  if (match) {
78
70
  const scheme = match[1];
79
71
  const rest = match[2];
@@ -156,54 +148,97 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
156
148
  this.description = options.description;
157
149
  }
158
150
  /**
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: {
151
+ * List root directory children.
152
+ * Note: list() returns only children, never the path itself (per new semantics)
153
+ */
154
+ async listRootHandler(_ctx) {
155
+ await this.ensureConnected();
156
+ const entries = [];
157
+ entries.push({
158
+ id: "/WORLD.md",
159
+ path: "/WORLD.md",
160
+ summary: "MCP Server World Documentation",
161
+ meta: {
162
+ kind: "afs:document",
163
+ kinds: require_kinds.getKindsArray("afs:document"),
164
+ description: "MCP Server World Documentation",
165
+ mimeType: "text/markdown",
166
+ mcp: { type: "world" }
167
+ }
168
+ });
169
+ entries.push({
170
+ id: "/tools",
171
+ path: "/tools",
172
+ summary: `${this._tools.length} tools available`,
173
+ meta: {
184
174
  kind: "afs:node",
185
175
  kinds: require_kinds.getKindsArray("afs:node"),
186
176
  description: `${this._tools.length} tools available`,
187
177
  childrenCount: this._tools.length
188
- } }
178
+ }
179
+ });
180
+ if (this._prompts.length > 0) entries.push({
181
+ id: "/prompts",
182
+ path: "/prompts",
183
+ summary: `${this._prompts.length} prompts available`,
184
+ meta: {
185
+ kind: "afs:node",
186
+ kinds: require_kinds.getKindsArray("afs:node"),
187
+ description: `${this._prompts.length} prompts available`,
188
+ childrenCount: this._prompts.length
189
+ }
190
+ });
191
+ if (this._resources.length > 0 || this._resourceTemplates.length > 0) entries.push({
192
+ id: "/resources",
193
+ path: "/resources",
194
+ summary: `${this._resources.length} resources available`,
195
+ meta: {
196
+ kind: "afs:node",
197
+ kinds: require_kinds.getKindsArray("afs:node"),
198
+ description: `${this._resources.length} resources available`,
199
+ childrenCount: this._resources.length
200
+ }
201
+ });
202
+ return { data: entries };
203
+ }
204
+ /**
205
+ * Read root directory entry
206
+ */
207
+ async readRootHandler(_ctx) {
208
+ await this.ensureConnected();
209
+ return {
210
+ id: "/",
211
+ path: "/",
212
+ summary: this.description || "MCP Server",
213
+ meta: {
214
+ kind: "mcp:module",
215
+ kinds: require_kinds.getKindsArray("mcp:module"),
216
+ description: this.description || "MCP Server",
217
+ childrenCount: 2 + (this._prompts.length > 0 ? 1 : 0) + (this._resources.length > 0 ? 1 : 0),
218
+ mcp: {
219
+ server: { name: this.name },
220
+ capabilities: {
221
+ tools: this._tools.length > 0,
222
+ prompts: this._prompts.length > 0,
223
+ resources: this._resources.length > 0
224
+ }
225
+ }
226
+ }
189
227
  };
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
- } };
228
+ }
229
+ /**
230
+ * Read root metadata
231
+ */
232
+ async readRootMeta(_ctx) {
233
+ await this.ensureConnected();
202
234
  return {
203
- metadata: {
235
+ id: "/.meta",
236
+ path: "/.meta",
237
+ meta: {
204
238
  kind: "mcp:module",
205
239
  kinds: require_kinds.getKindsArray("mcp:module"),
206
240
  description: this.description || "MCP Server",
241
+ childrenCount: 2 + (this._prompts.length > 0 ? 1 : 0) + (this._resources.length > 0 ? 1 : 0),
207
242
  mcp: {
208
243
  server: { name: this.name },
209
244
  capabilities: {
@@ -212,10 +247,361 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
212
247
  resources: this._resources.length > 0
213
248
  }
214
249
  }
250
+ }
251
+ };
252
+ }
253
+ /**
254
+ * Read capabilities manifest
255
+ *
256
+ * Returns all MCP tools as ToolDefinition objects.
257
+ * MCP has no node-level actions, so actions is always empty.
258
+ */
259
+ async readCapabilities(_ctx) {
260
+ await this.ensureConnected();
261
+ const tools = this._tools.filter((tool) => tool.name).map((tool) => ({
262
+ name: tool.name,
263
+ description: tool.description,
264
+ path: `/tools/${tool.name}`,
265
+ inputSchema: tool.inputSchema
266
+ }));
267
+ return {
268
+ id: "/.meta/.capabilities",
269
+ path: "/.meta/.capabilities",
270
+ content: {
271
+ schemaVersion: 1,
272
+ provider: this.name,
273
+ version: "1.0.0",
274
+ description: this.description,
275
+ tools,
276
+ actions: [],
277
+ operations: this.getOperationsDeclaration()
215
278
  },
216
- children
279
+ meta: {
280
+ kind: "afs:capabilities",
281
+ description: "MCP Provider capabilities manifest"
282
+ }
283
+ };
284
+ }
285
+ /**
286
+ * Stat root directory
287
+ */
288
+ async statRootHandler(_ctx) {
289
+ await this.ensureConnected();
290
+ const childrenCount = 2 + (this._prompts.length > 0 ? 1 : 0) + (this._resources.length > 0 ? 1 : 0);
291
+ return { data: {
292
+ id: "/",
293
+ path: "/",
294
+ meta: {
295
+ kind: "mcp:module",
296
+ kinds: require_kinds.getKindsArray("mcp:module"),
297
+ description: this.description || "MCP Server",
298
+ childrenCount
299
+ }
300
+ } };
301
+ }
302
+ /**
303
+ * Read WORLD.md file
304
+ */
305
+ async readWorldMdHandler(_ctx) {
306
+ await this.ensureConnected();
307
+ return {
308
+ id: "/WORLD.md",
309
+ path: "/WORLD.md",
310
+ content: this.generateWorldMd(),
311
+ meta: {
312
+ kind: "afs:document",
313
+ kinds: require_kinds.getKindsArray("afs:document"),
314
+ description: "MCP Server World Documentation",
315
+ mimeType: "text/markdown",
316
+ mcp: { type: "world" }
317
+ }
318
+ };
319
+ }
320
+ /**
321
+ * List WORLD.md - files have no children
322
+ * Note: list() returns only children, never the path itself (per new semantics)
323
+ */
324
+ async listWorldMdHandler(_ctx) {
325
+ await this.ensureConnected();
326
+ return { data: [] };
327
+ }
328
+ /**
329
+ * Read WORLD.md metadata
330
+ */
331
+ async readWorldMdMeta(_ctx) {
332
+ await this.ensureConnected();
333
+ return {
334
+ id: "/WORLD.md/.meta",
335
+ path: "/WORLD.md/.meta",
336
+ meta: {
337
+ kind: "afs:document",
338
+ kinds: require_kinds.getKindsArray("afs:document"),
339
+ description: "MCP Server World Documentation",
340
+ mimeType: "text/markdown",
341
+ mcp: { type: "world" }
342
+ }
343
+ };
344
+ }
345
+ /**
346
+ * Read /tools directory entry
347
+ */
348
+ async readToolsDir(_ctx) {
349
+ await this.ensureConnected();
350
+ return {
351
+ id: "/tools",
352
+ path: "/tools",
353
+ summary: `${this._tools.length} tools available`,
354
+ meta: {
355
+ kind: "afs:node",
356
+ kinds: require_kinds.getKindsArray("afs:node"),
357
+ description: `${this._tools.length} tools available`,
358
+ childrenCount: this._tools.length
359
+ }
360
+ };
361
+ }
362
+ /**
363
+ * Read /tools metadata
364
+ */
365
+ async readToolsMeta(_ctx) {
366
+ await this.ensureConnected();
367
+ return {
368
+ id: "/tools/.meta",
369
+ path: "/tools/.meta",
370
+ meta: {
371
+ kind: "afs:node",
372
+ kinds: require_kinds.getKindsArray("afs:node"),
373
+ description: `${this._tools.length} tools available`,
374
+ childrenCount: this._tools.length
375
+ }
376
+ };
377
+ }
378
+ /**
379
+ * Read /prompts directory entry
380
+ */
381
+ async readPromptsDir(_ctx) {
382
+ await this.ensureConnected();
383
+ if (this._prompts.length === 0) throw new _aigne_afs.AFSNotFoundError("/prompts");
384
+ return {
385
+ id: "/prompts",
386
+ path: "/prompts",
387
+ summary: `${this._prompts.length} prompts available`,
388
+ meta: {
389
+ kind: "afs:node",
390
+ kinds: require_kinds.getKindsArray("afs:node"),
391
+ description: `${this._prompts.length} prompts available`,
392
+ childrenCount: this._prompts.length
393
+ }
394
+ };
395
+ }
396
+ /**
397
+ * Read /prompts metadata
398
+ */
399
+ async readPromptsMeta(_ctx) {
400
+ await this.ensureConnected();
401
+ if (this._prompts.length === 0) throw new _aigne_afs.AFSNotFoundError("/prompts/.meta");
402
+ return {
403
+ id: "/prompts/.meta",
404
+ path: "/prompts/.meta",
405
+ meta: {
406
+ kind: "afs:node",
407
+ kinds: require_kinds.getKindsArray("afs:node"),
408
+ description: `${this._prompts.length} prompts available`,
409
+ childrenCount: this._prompts.length
410
+ }
411
+ };
412
+ }
413
+ /**
414
+ * Read /resources directory entry
415
+ */
416
+ async readResourcesDir(_ctx) {
417
+ await this.ensureConnected();
418
+ if (this._resources.length === 0 && this._resourceTemplates.length === 0) throw new _aigne_afs.AFSNotFoundError("/resources");
419
+ const immediateChildrenCount = this.getResourcesImmediateChildrenCount();
420
+ return {
421
+ id: "/resources",
422
+ path: "/resources",
423
+ summary: `${this._resources.length} resources available`,
424
+ meta: {
425
+ kind: "afs:node",
426
+ kinds: require_kinds.getKindsArray("afs:node"),
427
+ description: `${this._resources.length} resources available`,
428
+ childrenCount: immediateChildrenCount
429
+ }
430
+ };
431
+ }
432
+ /**
433
+ * Read /resources metadata
434
+ */
435
+ async readResourcesMeta(_ctx) {
436
+ await this.ensureConnected();
437
+ if (this._resources.length === 0 && this._resourceTemplates.length === 0) throw new _aigne_afs.AFSNotFoundError("/resources/.meta");
438
+ const immediateChildrenCount = this.getResourcesImmediateChildrenCount();
439
+ return {
440
+ id: "/resources/.meta",
441
+ path: "/resources/.meta",
442
+ meta: {
443
+ kind: "afs:node",
444
+ kinds: require_kinds.getKindsArray("afs:node"),
445
+ description: `${this._resources.length} resources available`,
446
+ childrenCount: immediateChildrenCount
447
+ }
217
448
  };
218
449
  }
450
+ /**
451
+ * Calculate immediate children count for /resources directory
452
+ */
453
+ getResourcesImmediateChildrenCount() {
454
+ const immediateChildren = /* @__PURE__ */ new Set();
455
+ for (const resource of this._resources) {
456
+ const resourcePath = this.resourceUriToPath(resource.uri);
457
+ if (!resourcePath) continue;
458
+ const segments = resourcePath.split("/").filter(Boolean);
459
+ if (segments.length > 0) immediateChildren.add(segments[0]);
460
+ }
461
+ return immediateChildren.size;
462
+ }
463
+ /**
464
+ * Stat WORLD.md file
465
+ */
466
+ async statWorldMdHandler(_ctx) {
467
+ await this.ensureConnected();
468
+ return { data: {
469
+ id: "WORLD.md",
470
+ path: "/WORLD.md",
471
+ meta: {
472
+ kind: "afs:document",
473
+ kinds: require_kinds.getKindsArray("afs:document"),
474
+ description: "MCP Server World Documentation",
475
+ mimeType: "text/markdown",
476
+ childrenCount: 0
477
+ }
478
+ } };
479
+ }
480
+ /**
481
+ * Stat /tools directory
482
+ */
483
+ async statToolsHandler(_ctx) {
484
+ await this.ensureConnected();
485
+ return { data: {
486
+ id: "tools",
487
+ path: "/tools",
488
+ meta: {
489
+ kind: "afs:node",
490
+ kinds: require_kinds.getKindsArray("afs:node"),
491
+ description: `${this._tools.length} tools available`,
492
+ childrenCount: this._tools.length
493
+ }
494
+ } };
495
+ }
496
+ /**
497
+ * Stat /prompts directory
498
+ */
499
+ async statPromptsHandler(_ctx) {
500
+ await this.ensureConnected();
501
+ if (this._prompts.length === 0) throw new _aigne_afs.AFSNotFoundError("/prompts");
502
+ return { data: {
503
+ id: "prompts",
504
+ path: "/prompts",
505
+ meta: {
506
+ kind: "afs:node",
507
+ kinds: require_kinds.getKindsArray("afs:node"),
508
+ description: `${this._prompts.length} prompts available`,
509
+ childrenCount: this._prompts.length
510
+ }
511
+ } };
512
+ }
513
+ /**
514
+ * Stat /resources directory
515
+ */
516
+ async statResourcesHandler(_ctx) {
517
+ await this.ensureConnected();
518
+ if (this._resources.length === 0 && this._resourceTemplates.length === 0) throw new _aigne_afs.AFSNotFoundError("/resources");
519
+ const immediateChildrenCount = this.getResourcesImmediateChildrenCount();
520
+ return { data: {
521
+ id: "resources",
522
+ path: "/resources",
523
+ meta: {
524
+ kind: "afs:node",
525
+ kinds: require_kinds.getKindsArray("afs:node"),
526
+ description: `${this._resources.length} resources available`,
527
+ childrenCount: immediateChildrenCount
528
+ }
529
+ } };
530
+ }
531
+ /**
532
+ * Stat specific tool
533
+ */
534
+ async statToolHandler(ctx) {
535
+ await this.ensureConnected();
536
+ const tool = this._tools.find((t) => t.name === ctx.params.name);
537
+ if (!tool) throw new _aigne_afs.AFSNotFoundError(ctx.path);
538
+ const { content: _content, ...statData } = this.toolToEntry(tool);
539
+ return { data: statData };
540
+ }
541
+ /**
542
+ * Stat specific prompt
543
+ */
544
+ async statPromptHandler(ctx) {
545
+ await this.ensureConnected();
546
+ const prompt = this._prompts.find((p) => p.name === ctx.params.name);
547
+ if (!prompt) throw new _aigne_afs.AFSNotFoundError(ctx.path);
548
+ const { content: _content, ...statData } = this.promptToEntry(prompt);
549
+ return { data: statData };
550
+ }
551
+ /**
552
+ * Stat resource paths (wildcard handler under /resources)
553
+ */
554
+ async statResourceHandler(ctx) {
555
+ await this.ensureConnected();
556
+ const resourcePath = `/${ctx.params.path}`;
557
+ const afsPath = `/resources${resourcePath}`;
558
+ const resourceId = ctx.params.path.split("/").pop() || ctx.params.path;
559
+ const resourceMatch = this.findResourceForPath(resourcePath);
560
+ if (resourceMatch) {
561
+ if (resourceMatch.resource) return { data: {
562
+ id: resourceId,
563
+ path: afsPath,
564
+ meta: {
565
+ kind: "mcp:resource",
566
+ kinds: require_kinds.getKindsArray("mcp:resource"),
567
+ description: resourceMatch.resource.description,
568
+ mimeType: resourceMatch.resource.mimeType,
569
+ childrenCount: 0
570
+ }
571
+ } };
572
+ else if (resourceMatch.template) return { data: {
573
+ id: resourceId,
574
+ path: afsPath,
575
+ meta: {
576
+ kind: "mcp:resource-template",
577
+ kinds: require_kinds.getKindsArray("mcp:resource"),
578
+ description: resourceMatch.template.description,
579
+ mimeType: resourceMatch.template.mimeType,
580
+ childrenCount: 0
581
+ }
582
+ } };
583
+ }
584
+ const immediateChildren = this.getImmediateResourceChildren(resourcePath);
585
+ if (immediateChildren.size > 0) return { data: {
586
+ id: resourceId,
587
+ path: afsPath,
588
+ meta: {
589
+ kind: "afs:node",
590
+ kinds: require_kinds.getKindsArray("afs:node"),
591
+ childrenCount: immediateChildren.size
592
+ }
593
+ } };
594
+ if (this.isTemplateBasePath(resourcePath)) return { data: {
595
+ id: resourceId,
596
+ path: afsPath,
597
+ meta: {
598
+ kind: "afs:node",
599
+ kinds: require_kinds.getKindsArray("afs:node"),
600
+ childrenCount: 0
601
+ }
602
+ } };
603
+ throw new _aigne_afs.AFSNotFoundError(ctx.path);
604
+ }
219
605
  get isConnected() {
220
606
  return this._isConnected;
221
607
  }
@@ -377,6 +763,35 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
377
763
  return basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
378
764
  }
379
765
  /**
766
+ * Check if a resource path is a template base path (has dynamic children).
767
+ */
768
+ isTemplateBasePath(resourcePath) {
769
+ return this._resourceTemplates.some((template) => {
770
+ return this.getTemplateBasePath(template.uriTemplate) === resourcePath;
771
+ });
772
+ }
773
+ /**
774
+ * Get immediate child segments under a resource parent path,
775
+ * scanning both static resources and template base paths.
776
+ */
777
+ getImmediateResourceChildren(parentPath) {
778
+ const depth = parentPath.split("/").filter(Boolean).length;
779
+ const childSegments = /* @__PURE__ */ new Set();
780
+ for (const resource of this._resources) {
781
+ const rPath = this.resourceUriToPath(resource.uri);
782
+ if (!rPath || !rPath.startsWith(`${parentPath}/`)) continue;
783
+ const segments = rPath.split("/").filter(Boolean);
784
+ if (segments.length > depth) childSegments.add(segments[depth]);
785
+ }
786
+ for (const template of this._resourceTemplates) {
787
+ const basePath = this.getTemplateBasePath(template.uriTemplate);
788
+ if (!basePath || !basePath.startsWith(`${parentPath}/`)) continue;
789
+ const segments = basePath.split("/").filter(Boolean);
790
+ if (segments.length > depth) childSegments.add(segments[depth]);
791
+ }
792
+ return childSegments;
793
+ }
794
+ /**
380
795
  * Find a resource or template that matches a given path
381
796
  */
382
797
  findResourceForPath(path) {
@@ -398,7 +813,7 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
398
813
  id: `/tools/${tool.name}`,
399
814
  path: `/tools/${tool.name}`,
400
815
  summary: tool.description,
401
- metadata: {
816
+ meta: {
402
817
  kind: "mcp:tool",
403
818
  kinds: require_kinds.getKindsArray("mcp:tool"),
404
819
  description: tool.description,
@@ -409,13 +824,15 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
409
824
  }
410
825
  /**
411
826
  * Convert a Prompt to an AFSEntry (Meta Spec compliant)
827
+ * Note: inputSchema is NOT included in prompt entry meta.
828
+ * For prompts with arguments, use the action system (/.actions/get) to execute.
412
829
  */
413
830
  promptToEntry(prompt) {
414
831
  return {
415
832
  id: `/prompts/${prompt.name}`,
416
833
  path: `/prompts/${prompt.name}`,
417
834
  summary: prompt.description,
418
- metadata: {
835
+ meta: {
419
836
  kind: "mcp:prompt",
420
837
  kinds: require_kinds.getKindsArray("mcp:prompt"),
421
838
  description: prompt.description,
@@ -427,6 +844,41 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
427
844
  };
428
845
  }
429
846
  /**
847
+ * Convert prompt arguments to JSON Schema (for action inputSchema)
848
+ */
849
+ promptArgsToSchema(args) {
850
+ if (!args || args.length === 0) return {
851
+ type: "object",
852
+ properties: {}
853
+ };
854
+ const properties = {};
855
+ const required = [];
856
+ for (const arg of args) {
857
+ properties[arg.name] = {
858
+ type: "string",
859
+ ...arg.description ? { description: arg.description } : {}
860
+ };
861
+ if (arg.required) required.push(arg.name);
862
+ }
863
+ return {
864
+ type: "object",
865
+ properties,
866
+ ...required.length > 0 ? { required } : {}
867
+ };
868
+ }
869
+ /**
870
+ * Extract text content from MCP prompt messages
871
+ */
872
+ extractTextFromMessages(messages) {
873
+ const textParts = [];
874
+ for (const msg of messages) {
875
+ const c = msg.content;
876
+ if (typeof c === "string") textParts.push(c);
877
+ else if (c && typeof c === "object" && "text" in c) textParts.push(c.text);
878
+ }
879
+ return textParts.join("\n");
880
+ }
881
+ /**
430
882
  * Convert a Resource to an AFSEntry (Meta Spec compliant)
431
883
  */
432
884
  resourceToEntry(resource, path) {
@@ -434,7 +886,7 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
434
886
  id: path,
435
887
  path,
436
888
  summary: resource.description || resource.name,
437
- metadata: {
889
+ meta: {
438
890
  kind: "mcp:resource",
439
891
  kinds: require_kinds.getKindsArray("mcp:resource"),
440
892
  description: resource.description,
@@ -454,7 +906,7 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
454
906
  id: path,
455
907
  path,
456
908
  summary: template.description || template.name,
457
- metadata: {
909
+ meta: {
458
910
  kind: "mcp:resource-template",
459
911
  kinds: require_kinds.getKindsArray("mcp:resource"),
460
912
  description: template.description,
@@ -468,9 +920,7 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
468
920
  }
469
921
  /**
470
922
  * List tools.
471
- *
472
- * Returns tool children only. The /tools directory entry comes from @StaticEntries
473
- * and will be merged with dynamic childrenCount.
923
+ * Note: list() returns only children, never the path itself (per new semantics)
474
924
  */
475
925
  async listToolsHandler(_ctx) {
476
926
  await this.ensureConnected();
@@ -478,39 +928,38 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
478
928
  }
479
929
  /**
480
930
  * List prompts.
481
- *
482
- * Returns prompt children only. The /prompts directory entry comes from @StaticEntries
483
- * and will be merged with dynamic childrenCount.
931
+ * Note: list() returns only children, never the path itself (per new semantics)
484
932
  */
485
933
  async listPromptsHandler(_ctx) {
486
934
  await this.ensureConnected();
935
+ if (this._prompts.length === 0) throw new _aigne_afs.AFSNotFoundError("/prompts");
487
936
  return { data: this._prompts.map((prompt) => this.promptToEntry(prompt)) };
488
937
  }
489
938
  /**
490
- * List specific tool
939
+ * List specific tool - tools are leaf nodes with no children
940
+ * Note: list() returns only children, never the path itself (per new semantics)
491
941
  */
492
942
  async listToolHandler(ctx) {
493
943
  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)] };
944
+ if (!this._tools.find((t) => t.name === ctx.params.name)) throw new _aigne_afs.AFSNotFoundError(ctx.path);
945
+ return { data: [] };
497
946
  }
498
947
  /**
499
- * List specific prompt
948
+ * List specific prompt - prompts are leaf nodes with no children
949
+ * Note: list() returns only children, never the path itself (per new semantics)
500
950
  */
501
951
  async listPromptHandler(ctx) {
502
952
  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)] };
953
+ if (!this._prompts.find((p) => p.name === ctx.params.name)) throw new _aigne_afs.AFSNotFoundError(ctx.path);
954
+ return { data: [] };
506
955
  }
507
956
  /**
508
957
  * List resources directory.
509
- *
510
- * Returns all resources as children of /resources.
958
+ * Note: list() returns only children, never the path itself (per new semantics)
511
959
  */
512
960
  async listResourcesHandler(_ctx) {
513
961
  await this.ensureConnected();
962
+ if (this._resources.length === 0 && this._resourceTemplates.length === 0) throw new _aigne_afs.AFSNotFoundError("/resources");
514
963
  const immediateChildren = /* @__PURE__ */ new Map();
515
964
  for (const resource of this._resources) {
516
965
  const resourcePath = this.resourceUriToPath(resource.uri);
@@ -524,27 +973,39 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
524
973
  });
525
974
  else if (!immediateChildren.has(childPath)) immediateChildren.set(childPath, { isDir: true });
526
975
  }
976
+ for (const template of this._resourceTemplates) {
977
+ const basePath = this.getTemplateBasePath(template.uriTemplate);
978
+ if (!basePath) continue;
979
+ const segments = basePath.split("/").filter(Boolean);
980
+ if (segments.length === 0) continue;
981
+ const childPath = `/resources/${segments[0]}`;
982
+ if (!immediateChildren.has(childPath)) immediateChildren.set(childPath, { isDir: true });
983
+ }
527
984
  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));
985
+ for (const [path, info] of immediateChildren) if (info.isDir) {
986
+ const resourceSubPath = path.replace("/resources", "");
987
+ const childrenCount = this.getImmediateResourceChildren(resourceSubPath).size;
988
+ entries.push({
989
+ id: path,
990
+ path,
991
+ summary: `Resource directory: ${resourceSubPath}`,
992
+ meta: {
993
+ kind: "afs:node",
994
+ kinds: require_kinds.getKindsArray("afs:node"),
995
+ childrenCount,
996
+ mcp: { isResource: true }
997
+ }
998
+ });
999
+ } else if (info.resource) entries.push(this.resourceToEntry(info.resource, path));
539
1000
  return { data: entries };
540
1001
  }
541
1002
  /**
542
1003
  * List resource paths (wildcard handler under /resources)
1004
+ * Note: list() returns only children, never the path itself (per new semantics)
543
1005
  */
544
1006
  async listResourceHandler(ctx) {
545
1007
  await this.ensureConnected();
546
1008
  const resourcePath = `/${ctx.params.path}`;
547
- const afsPath = `/resources${resourcePath}`;
548
1009
  const entries = [];
549
1010
  let exactMatch = null;
550
1011
  const immediateChildren = /* @__PURE__ */ new Map();
@@ -564,32 +1025,37 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
564
1025
  else if (!immediateChildren.has(childPath)) immediateChildren.set(childPath, { isDir: true });
565
1026
  }
566
1027
  }
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 };
1028
+ for (const template of this._resourceTemplates) {
1029
+ const basePath = this.getTemplateBasePath(template.uriTemplate);
1030
+ if (!basePath) continue;
1031
+ if (basePath === resourcePath) return { data: [] };
1032
+ else if (basePath.startsWith(`${resourcePath}/`)) {
1033
+ const segments = basePath.split("/").filter(Boolean);
1034
+ if (segments.length <= depth) continue;
1035
+ const childPath = `/resources${resourcePath}/${segments[depth]}`;
1036
+ if (!immediateChildren.has(childPath)) immediateChildren.set(childPath, { isDir: true });
1037
+ }
1038
+ }
1039
+ if (exactMatch) return { data: [] };
1040
+ if (immediateChildren.size > 0) {
1041
+ for (const [path, info] of immediateChildren) if (info.isDir) {
1042
+ const resourceSubPath = path.replace("/resources", "");
1043
+ const childrenCount = this.getImmediateResourceChildren(resourceSubPath).size;
1044
+ entries.push({
1045
+ id: path,
1046
+ path,
1047
+ summary: `Resource directory: ${resourceSubPath}`,
1048
+ meta: {
1049
+ kind: "afs:node",
1050
+ kinds: require_kinds.getKindsArray("afs:node"),
1051
+ childrenCount,
1052
+ mcp: { isResource: true }
1053
+ }
1054
+ });
1055
+ } else if (info.resource) entries.push(this.resourceToEntry(info.resource, path));
1056
+ return { data: entries };
1057
+ }
1058
+ throw new _aigne_afs.AFSNotFoundError(ctx.path);
593
1059
  }
594
1060
  /**
595
1061
  * Read metadata for tools (dynamic entries not in static tree).
@@ -603,7 +1069,7 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
603
1069
  return {
604
1070
  id: `/tools/${ctx.params.name}/.meta`,
605
1071
  path: `/tools/${ctx.params.name}/.meta`,
606
- metadata: entry.metadata
1072
+ meta: entry.meta
607
1073
  };
608
1074
  }
609
1075
  /**
@@ -617,7 +1083,7 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
617
1083
  return {
618
1084
  id: `/prompts/${ctx.params.name}/.meta`,
619
1085
  path: `/prompts/${ctx.params.name}/.meta`,
620
- metadata: entry.metadata
1086
+ meta: entry.meta
621
1087
  };
622
1088
  }
623
1089
  /**
@@ -633,25 +1099,31 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
633
1099
  if (resourceMatch.resource) return {
634
1100
  id: metaPath,
635
1101
  path: metaPath,
636
- metadata: this.resourceToEntry(resourceMatch.resource, `/resources${resourcePath}`).metadata
1102
+ meta: this.resourceToEntry(resourceMatch.resource, `/resources${resourcePath}`).meta
637
1103
  };
638
1104
  else if (resourceMatch.template) return {
639
1105
  id: metaPath,
640
1106
  path: metaPath,
641
- metadata: this.resourceTemplateToEntry(resourceMatch.template, `/resources${resourcePath}`).metadata
1107
+ meta: this.resourceTemplateToEntry(resourceMatch.template, `/resources${resourcePath}`).meta
642
1108
  };
643
1109
  }
644
- if (this._resources.some((resource) => {
645
- return this.resourceUriToPath(resource.uri)?.startsWith(`${resourcePath}/`);
646
- })) return {
1110
+ if (this.getImmediateResourceChildren(resourcePath).size > 0) return {
647
1111
  id: metaPath,
648
1112
  path: metaPath,
649
- metadata: {
1113
+ meta: {
650
1114
  kind: "afs:node",
651
1115
  kinds: require_kinds.getKindsArray("afs:node"),
652
1116
  mcp: { isResource: true }
653
1117
  }
654
1118
  };
1119
+ if (this.isTemplateBasePath(resourcePath)) {
1120
+ const template = this._resourceTemplates.find((t) => this.getTemplateBasePath(t.uriTemplate) === resourcePath);
1121
+ if (template) return {
1122
+ id: metaPath,
1123
+ path: metaPath,
1124
+ meta: this.resourceTemplateToEntry(template, `/resources${resourcePath}`).meta
1125
+ };
1126
+ }
655
1127
  throw new _aigne_afs.AFSNotFoundError(ctx.path);
656
1128
  }
657
1129
  /**
@@ -665,12 +1137,195 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
665
1137
  }
666
1138
  /**
667
1139
  * Read prompt
1140
+ *
1141
+ * Behavior:
1142
+ * - Prompts with NO arguments: returns content directly
1143
+ * - Prompts with ONLY optional arguments: returns content (empty params)
1144
+ * - Prompts with REQUIRED arguments: returns metadata only (use /.actions/get to execute)
668
1145
  */
669
1146
  async readPromptHandler(ctx) {
670
1147
  await this.ensureConnected();
671
1148
  const prompt = this._prompts.find((p) => p.name === ctx.params.name);
672
1149
  if (!prompt) throw new _aigne_afs.AFSNotFoundError(ctx.path);
673
- return this.promptToEntry(prompt);
1150
+ const entry = this.promptToEntry(prompt);
1151
+ const hasRequiredArgs = prompt.arguments?.some((arg) => arg.required) ?? false;
1152
+ if (this.client && !hasRequiredArgs) try {
1153
+ const result = await this.client.getPrompt({
1154
+ name: prompt.name,
1155
+ arguments: {}
1156
+ });
1157
+ const content = this.extractTextFromMessages(result.messages);
1158
+ if (content) entry.content = content;
1159
+ } catch {}
1160
+ return entry;
1161
+ }
1162
+ /**
1163
+ * List actions for a prompt
1164
+ *
1165
+ * Only prompts with arguments expose a "get" action.
1166
+ */
1167
+ async listPromptActions(ctx) {
1168
+ await this.ensureConnected();
1169
+ const prompt = this._prompts.find((p) => p.name === ctx.params.name);
1170
+ if (!prompt) throw new _aigne_afs.AFSNotFoundError(ctx.path);
1171
+ if (!prompt.arguments || prompt.arguments.length === 0) return { data: [] };
1172
+ return { data: [{
1173
+ id: "get",
1174
+ path: `/prompts/${prompt.name}/.actions/get`,
1175
+ summary: `Get ${prompt.name} prompt content with arguments`,
1176
+ meta: {
1177
+ kind: "afs:executable",
1178
+ kinds: require_kinds.getKindsArray("afs:executable"),
1179
+ name: "get",
1180
+ description: prompt.description,
1181
+ inputSchema: this.promptArgsToSchema(prompt.arguments)
1182
+ }
1183
+ }] };
1184
+ }
1185
+ /**
1186
+ * Execute prompt "get" action
1187
+ *
1188
+ * Fetches prompt content with provided arguments.
1189
+ */
1190
+ async execPromptGetHandler(ctx, params) {
1191
+ await this.ensureConnected();
1192
+ const prompt = this._prompts.find((p) => p.name === ctx.params.name);
1193
+ if (!prompt) return {
1194
+ success: false,
1195
+ error: {
1196
+ code: "NOT_FOUND",
1197
+ message: `Prompt not found: ${ctx.params.name}`
1198
+ }
1199
+ };
1200
+ if (!this.client) return {
1201
+ success: false,
1202
+ error: {
1203
+ code: "NOT_CONNECTED",
1204
+ message: "MCP client not connected"
1205
+ }
1206
+ };
1207
+ try {
1208
+ const result = await this.client.getPrompt({
1209
+ name: prompt.name,
1210
+ arguments: params
1211
+ });
1212
+ return {
1213
+ success: true,
1214
+ data: {
1215
+ content: this.extractTextFromMessages(result.messages),
1216
+ meta: { mcp: {
1217
+ name: prompt.name,
1218
+ arguments: prompt.arguments
1219
+ } }
1220
+ }
1221
+ };
1222
+ } catch (error) {
1223
+ return {
1224
+ success: false,
1225
+ error: {
1226
+ code: "EXECUTION_ERROR",
1227
+ message: error.message
1228
+ }
1229
+ };
1230
+ }
1231
+ }
1232
+ /**
1233
+ * List actions for a resource template
1234
+ *
1235
+ * Only resource templates expose a "get" action.
1236
+ * Static resources do not have actions.
1237
+ */
1238
+ async listResourceActions(ctx) {
1239
+ await this.ensureConnected();
1240
+ const resourcePath = `/${ctx.params.path}`;
1241
+ if (!this.isTemplateBasePath(resourcePath)) return { data: [] };
1242
+ const template = this._resourceTemplates.find((t) => this.getTemplateBasePath(t.uriTemplate) === resourcePath);
1243
+ if (!template) return { data: [] };
1244
+ const vars = AFSMCP.parseUriTemplate(template.uriTemplate);
1245
+ if (vars.length === 0) return { data: [] };
1246
+ const properties = {};
1247
+ for (const v of vars) properties[v] = {
1248
+ type: "string",
1249
+ description: `Template variable: ${v}`
1250
+ };
1251
+ const inputSchema = {
1252
+ type: "object",
1253
+ properties,
1254
+ required: vars
1255
+ };
1256
+ return { data: [{
1257
+ id: "get",
1258
+ path: `${`/resources${resourcePath}`}/.actions/get`,
1259
+ summary: `Get resource with template parameters`,
1260
+ meta: {
1261
+ kind: "afs:executable",
1262
+ kinds: require_kinds.getKindsArray("afs:executable"),
1263
+ name: "get",
1264
+ description: template.description,
1265
+ inputSchema
1266
+ }
1267
+ }] };
1268
+ }
1269
+ /**
1270
+ * Execute resource template "get" action
1271
+ *
1272
+ * Fetches resource content with provided template parameters.
1273
+ */
1274
+ async execResourceGetHandler(ctx, params) {
1275
+ await this.ensureConnected();
1276
+ const resourcePath = `/${ctx.params.path}`;
1277
+ if (!this.isTemplateBasePath(resourcePath)) return {
1278
+ success: false,
1279
+ error: {
1280
+ code: "NOT_TEMPLATE",
1281
+ message: `Not a resource template: ${resourcePath}`
1282
+ }
1283
+ };
1284
+ const template = this._resourceTemplates.find((t) => this.getTemplateBasePath(t.uriTemplate) === resourcePath);
1285
+ if (!template) return {
1286
+ success: false,
1287
+ error: {
1288
+ code: "NOT_FOUND",
1289
+ message: `Resource template not found: ${resourcePath}`
1290
+ }
1291
+ };
1292
+ if (!this.client) return {
1293
+ success: false,
1294
+ error: {
1295
+ code: "NOT_CONNECTED",
1296
+ message: "MCP client not connected"
1297
+ }
1298
+ };
1299
+ try {
1300
+ const uri = AFSMCP.buildUriFromTemplate(template.uriTemplate, params);
1301
+ const result = await this.readResourceByUri(uri);
1302
+ if (!result.data) return {
1303
+ success: false,
1304
+ error: {
1305
+ code: "NOT_FOUND",
1306
+ message: result.message || "Resource not found"
1307
+ }
1308
+ };
1309
+ return {
1310
+ success: true,
1311
+ data: {
1312
+ content: result.data.content,
1313
+ meta: { mcp: {
1314
+ uri,
1315
+ name: template.name,
1316
+ uriTemplate: template.uriTemplate
1317
+ } }
1318
+ }
1319
+ };
1320
+ } catch (error) {
1321
+ return {
1322
+ success: false,
1323
+ error: {
1324
+ code: "EXECUTION_ERROR",
1325
+ message: error.message
1326
+ }
1327
+ };
1328
+ }
674
1329
  }
675
1330
  /**
676
1331
  * Read resource (wildcard handler under /resources)
@@ -696,18 +1351,38 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
696
1351
  return result.data;
697
1352
  }
698
1353
  }
699
- if (this._resources.some((resource) => {
700
- return this.resourceUriToPath(resource.uri)?.startsWith(`${resourcePath}/`);
701
- })) return {
1354
+ const immediateChildrenSet = this.getImmediateResourceChildren(resourcePath);
1355
+ if (immediateChildrenSet.size > 0) return {
702
1356
  id: afsPath,
703
1357
  path: afsPath,
704
1358
  summary: `Resource directory: ${resourcePath}`,
705
- metadata: {
1359
+ meta: {
706
1360
  kind: "afs:node",
707
1361
  kinds: require_kinds.getKindsArray("afs:node"),
1362
+ childrenCount: immediateChildrenSet.size,
708
1363
  mcp: { isResource: true }
709
1364
  }
710
1365
  };
1366
+ if (this.isTemplateBasePath(resourcePath)) {
1367
+ const template = this._resourceTemplates.find((t) => this.getTemplateBasePath(t.uriTemplate) === resourcePath);
1368
+ return {
1369
+ id: afsPath,
1370
+ path: afsPath,
1371
+ summary: template?.description || `Resource template: ${resourcePath}`,
1372
+ meta: {
1373
+ kind: "mcp:resource-template",
1374
+ kinds: require_kinds.getKindsArray("mcp:resource"),
1375
+ description: template?.description,
1376
+ mimeType: template?.mimeType,
1377
+ childrenCount: 0,
1378
+ mcp: {
1379
+ uriTemplate: template?.uriTemplate,
1380
+ name: template?.name,
1381
+ parameters: template ? AFSMCP.parseUriTemplate(template.uriTemplate) : []
1382
+ }
1383
+ }
1384
+ };
1385
+ }
711
1386
  throw new _aigne_afs.AFSNotFoundError(ctx.path);
712
1387
  }
713
1388
  /**
@@ -733,7 +1408,7 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
733
1408
  id: path,
734
1409
  path,
735
1410
  content,
736
- metadata: { mcp: {
1411
+ meta: { mcp: {
737
1412
  uri,
738
1413
  mimeType
739
1414
  } }
@@ -777,7 +1452,7 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
777
1452
  path: `/prompts/${promptName}`,
778
1453
  summary: prompt.description,
779
1454
  content: result.messages,
780
- metadata: {
1455
+ meta: {
781
1456
  arguments: prompt.arguments,
782
1457
  mcp: {
783
1458
  name: prompt.name,
@@ -898,6 +1573,191 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
898
1573
  return lines.join("\n");
899
1574
  }
900
1575
  /**
1576
+ * Explain root → server name, tools/prompts/resources counts
1577
+ */
1578
+ async explainRoot(_ctx) {
1579
+ await this.ensureConnected();
1580
+ const lines = [];
1581
+ lines.push(`# ${this.name}`);
1582
+ lines.push("");
1583
+ if (this.description) {
1584
+ lines.push(this.description);
1585
+ lines.push("");
1586
+ }
1587
+ lines.push("## Overview");
1588
+ lines.push("");
1589
+ lines.push(`- **Tool count**: ${this._tools.length}`);
1590
+ lines.push(`- **Prompt count**: ${this._prompts.length}`);
1591
+ lines.push(`- **Resource count**: ${this._resources.length}`);
1592
+ lines.push(`- **Resource template count**: ${this._resourceTemplates.length}`);
1593
+ lines.push("");
1594
+ if (this._tools.length > 0) {
1595
+ lines.push("## Tools");
1596
+ lines.push("");
1597
+ for (const tool of this._tools) lines.push(`- **${tool.name}**: ${tool.description ?? "(no description)"}`);
1598
+ lines.push("");
1599
+ }
1600
+ if (this._prompts.length > 0) {
1601
+ lines.push("## Prompts");
1602
+ lines.push("");
1603
+ for (const prompt of this._prompts) {
1604
+ const argNames = prompt.arguments?.map((a) => a.name).join(", ") ?? "";
1605
+ lines.push(`- **${prompt.name}**: ${prompt.description ?? "(no description)"}${argNames ? ` (args: ${argNames})` : ""}`);
1606
+ }
1607
+ lines.push("");
1608
+ }
1609
+ if (this._resources.length > 0) {
1610
+ lines.push("## Resources");
1611
+ lines.push("");
1612
+ for (const resource of this._resources) lines.push(`- **${resource.name}**: ${resource.description ?? resource.uri}`);
1613
+ lines.push("");
1614
+ }
1615
+ return {
1616
+ format: "markdown",
1617
+ content: lines.join("\n")
1618
+ };
1619
+ }
1620
+ /**
1621
+ * Explain a specific tool → name, description, inputSchema
1622
+ */
1623
+ async explainTool(ctx) {
1624
+ await this.ensureConnected();
1625
+ const tool = this._tools.find((t) => t.name === ctx.params.name);
1626
+ if (!tool) throw new _aigne_afs.AFSNotFoundError(ctx.path, `Tool not found: ${ctx.params.name}`);
1627
+ const lines = [];
1628
+ lines.push(`# Tool: ${tool.name}`);
1629
+ lines.push("");
1630
+ if (tool.description) {
1631
+ lines.push(tool.description);
1632
+ lines.push("");
1633
+ }
1634
+ if (tool.inputSchema) {
1635
+ lines.push("## Input Schema");
1636
+ lines.push("");
1637
+ lines.push("```json");
1638
+ lines.push(JSON.stringify(tool.inputSchema, null, 2));
1639
+ lines.push("```");
1640
+ lines.push("");
1641
+ const props = tool.inputSchema.properties;
1642
+ if (props) {
1643
+ lines.push("## Parameters");
1644
+ lines.push("");
1645
+ for (const [key, val] of Object.entries(props)) {
1646
+ const prop = val;
1647
+ lines.push(`- **${key}** (${prop.type ?? "unknown"}): ${prop.description ?? "(no description)"}`);
1648
+ }
1649
+ lines.push("");
1650
+ }
1651
+ }
1652
+ return {
1653
+ format: "markdown",
1654
+ content: lines.join("\n")
1655
+ };
1656
+ }
1657
+ /**
1658
+ * Explain a specific prompt → name, description, arguments
1659
+ */
1660
+ async explainPrompt(ctx) {
1661
+ await this.ensureConnected();
1662
+ const prompt = this._prompts.find((p) => p.name === ctx.params.name);
1663
+ if (!prompt) throw new _aigne_afs.AFSNotFoundError(ctx.path, `Prompt not found: ${ctx.params.name}`);
1664
+ const lines = [];
1665
+ lines.push(`# Prompt: ${prompt.name}`);
1666
+ lines.push("");
1667
+ if (prompt.description) {
1668
+ lines.push(prompt.description);
1669
+ lines.push("");
1670
+ }
1671
+ if (prompt.arguments && prompt.arguments.length > 0) {
1672
+ lines.push("## Arguments");
1673
+ lines.push("");
1674
+ for (const arg of prompt.arguments) {
1675
+ const required = arg.required ? " (required)" : " (optional)";
1676
+ lines.push(`- **${arg.name}**${required}: ${arg.description ?? "(no description)"}`);
1677
+ }
1678
+ lines.push("");
1679
+ } else {
1680
+ lines.push("*No arguments required.*");
1681
+ lines.push("");
1682
+ }
1683
+ return {
1684
+ format: "markdown",
1685
+ content: lines.join("\n")
1686
+ };
1687
+ }
1688
+ /**
1689
+ * Explain a specific resource → name, URI, description
1690
+ */
1691
+ async explainResource(ctx) {
1692
+ await this.ensureConnected();
1693
+ const resourcePath = `/${ctx.params.path}`;
1694
+ const resourceMatch = this.findResourceForPath(resourcePath);
1695
+ if (resourceMatch?.resource) {
1696
+ const resource = resourceMatch.resource;
1697
+ const lines = [];
1698
+ lines.push(`# Resource: ${resource.name}`);
1699
+ lines.push("");
1700
+ if (resource.description) {
1701
+ lines.push(resource.description);
1702
+ lines.push("");
1703
+ }
1704
+ lines.push(`- **URI**: ${resource.uri}`);
1705
+ if (resource.mimeType) lines.push(`- **MIME Type**: ${resource.mimeType}`);
1706
+ lines.push("");
1707
+ return {
1708
+ format: "markdown",
1709
+ content: lines.join("\n")
1710
+ };
1711
+ }
1712
+ if (resourceMatch?.template) {
1713
+ const template = resourceMatch.template;
1714
+ const lines = [];
1715
+ lines.push(`# Resource Template: ${template.name}`);
1716
+ lines.push("");
1717
+ if (template.description) {
1718
+ lines.push(template.description);
1719
+ lines.push("");
1720
+ }
1721
+ lines.push(`- **URI Template**: ${template.uriTemplate}`);
1722
+ const vars = AFSMCP.parseUriTemplate(template.uriTemplate);
1723
+ if (vars.length > 0) lines.push(`- **Variables**: ${vars.join(", ")}`);
1724
+ lines.push("");
1725
+ return {
1726
+ format: "markdown",
1727
+ content: lines.join("\n")
1728
+ };
1729
+ }
1730
+ throw new _aigne_afs.AFSNotFoundError(ctx.path, `Resource not found: ${ctx.path}`);
1731
+ }
1732
+ /**
1733
+ * Search tools, prompts, and resources by name or description
1734
+ */
1735
+ async searchHandler(_ctx, query, options) {
1736
+ await this.ensureConnected();
1737
+ const results = [];
1738
+ const limit = options?.limit;
1739
+ const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1740
+ const flags = options?.caseSensitive ? "" : "i";
1741
+ const pattern = new RegExp(escapedQuery, flags);
1742
+ const matchAll = query === "";
1743
+ for (const tool of this._tools) {
1744
+ if (limit && results.length >= limit) break;
1745
+ if (matchAll || pattern.test(tool.name) || tool.description && pattern.test(tool.description)) results.push(this.toolToEntry(tool));
1746
+ }
1747
+ for (const prompt of this._prompts) {
1748
+ if (limit && results.length >= limit) break;
1749
+ if (matchAll || pattern.test(prompt.name) || prompt.description && pattern.test(prompt.description)) results.push(this.promptToEntry(prompt));
1750
+ }
1751
+ for (const resource of this._resources) {
1752
+ if (limit && results.length >= limit) break;
1753
+ if (matchAll || pattern.test(resource.name) || resource.description && pattern.test(resource.description)) {
1754
+ const path = this.resourceUriToPath(resource.uri);
1755
+ results.push(this.resourceToEntry(resource, `/resources${path}`));
1756
+ }
1757
+ }
1758
+ return { data: results };
1759
+ }
1760
+ /**
901
1761
  * Execute a tool
902
1762
  */
903
1763
  async execToolHandler(ctx, args) {
@@ -913,7 +1773,27 @@ var AFSMCP = class AFSMCP extends _aigne_afs_provider.AFSBaseProvider {
913
1773
  };
914
1774
  }
915
1775
  };
916
- require_decorate.__decorate([(0, _aigne_afs_provider.StaticEntries)()], AFSMCP.prototype, "defineEntries", null);
1776
+ require_decorate.__decorate([(0, _aigne_afs_provider.List)("/")], AFSMCP.prototype, "listRootHandler", null);
1777
+ require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/")], AFSMCP.prototype, "readRootHandler", null);
1778
+ require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/")], AFSMCP.prototype, "readRootMeta", null);
1779
+ require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/.meta/.capabilities")], AFSMCP.prototype, "readCapabilities", null);
1780
+ require_decorate.__decorate([(0, _aigne_afs_provider.Stat)("/")], AFSMCP.prototype, "statRootHandler", null);
1781
+ require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/WORLD.md")], AFSMCP.prototype, "readWorldMdHandler", null);
1782
+ require_decorate.__decorate([(0, _aigne_afs_provider.List)("/WORLD.md")], AFSMCP.prototype, "listWorldMdHandler", null);
1783
+ require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/WORLD.md")], AFSMCP.prototype, "readWorldMdMeta", null);
1784
+ require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/tools")], AFSMCP.prototype, "readToolsDir", null);
1785
+ require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/tools")], AFSMCP.prototype, "readToolsMeta", null);
1786
+ require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/prompts")], AFSMCP.prototype, "readPromptsDir", null);
1787
+ require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/prompts")], AFSMCP.prototype, "readPromptsMeta", null);
1788
+ require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/resources")], AFSMCP.prototype, "readResourcesDir", null);
1789
+ require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/resources")], AFSMCP.prototype, "readResourcesMeta", null);
1790
+ require_decorate.__decorate([(0, _aigne_afs_provider.Stat)("/WORLD.md")], AFSMCP.prototype, "statWorldMdHandler", null);
1791
+ require_decorate.__decorate([(0, _aigne_afs_provider.Stat)("/tools")], AFSMCP.prototype, "statToolsHandler", null);
1792
+ require_decorate.__decorate([(0, _aigne_afs_provider.Stat)("/prompts")], AFSMCP.prototype, "statPromptsHandler", null);
1793
+ require_decorate.__decorate([(0, _aigne_afs_provider.Stat)("/resources")], AFSMCP.prototype, "statResourcesHandler", null);
1794
+ require_decorate.__decorate([(0, _aigne_afs_provider.Stat)("/tools/:name")], AFSMCP.prototype, "statToolHandler", null);
1795
+ require_decorate.__decorate([(0, _aigne_afs_provider.Stat)("/prompts/:name")], AFSMCP.prototype, "statPromptHandler", null);
1796
+ require_decorate.__decorate([(0, _aigne_afs_provider.Stat)("/resources/:path+")], AFSMCP.prototype, "statResourceHandler", null);
917
1797
  require_decorate.__decorate([(0, _aigne_afs_provider.List)("/tools")], AFSMCP.prototype, "listToolsHandler", null);
918
1798
  require_decorate.__decorate([(0, _aigne_afs_provider.List)("/prompts")], AFSMCP.prototype, "listPromptsHandler", null);
919
1799
  require_decorate.__decorate([(0, _aigne_afs_provider.List)("/tools/:name")], AFSMCP.prototype, "listToolHandler", null);
@@ -925,8 +1805,19 @@ require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/prompts/:name")], A
925
1805
  require_decorate.__decorate([(0, _aigne_afs_provider.Meta)("/resources/:path+")], AFSMCP.prototype, "readResourceMeta", null);
926
1806
  require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/tools/:name")], AFSMCP.prototype, "readToolHandler", null);
927
1807
  require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/prompts/:name")], AFSMCP.prototype, "readPromptHandler", null);
1808
+ require_decorate.__decorate([(0, _aigne_afs_provider.Actions)("/prompts/:name")], AFSMCP.prototype, "listPromptActions", null);
1809
+ require_decorate.__decorate([_aigne_afs_provider.Actions.Exec("/prompts/:name", "get")], AFSMCP.prototype, "execPromptGetHandler", null);
1810
+ require_decorate.__decorate([(0, _aigne_afs_provider.Actions)("/resources/:path+")], AFSMCP.prototype, "listResourceActions", null);
1811
+ require_decorate.__decorate([_aigne_afs_provider.Actions.Exec("/resources/:path+", "get")], AFSMCP.prototype, "execResourceGetHandler", null);
928
1812
  require_decorate.__decorate([(0, _aigne_afs_provider.Read)("/resources/:path+")], AFSMCP.prototype, "readResourceHandler", null);
1813
+ require_decorate.__decorate([(0, _aigne_afs_provider.Explain)("/")], AFSMCP.prototype, "explainRoot", null);
1814
+ require_decorate.__decorate([(0, _aigne_afs_provider.Explain)("/tools/:name")], AFSMCP.prototype, "explainTool", null);
1815
+ require_decorate.__decorate([(0, _aigne_afs_provider.Explain)("/prompts/:name")], AFSMCP.prototype, "explainPrompt", null);
1816
+ require_decorate.__decorate([(0, _aigne_afs_provider.Explain)("/resources/:path+")], AFSMCP.prototype, "explainResource", null);
1817
+ require_decorate.__decorate([(0, _aigne_afs_provider.Search)("/:path*")], AFSMCP.prototype, "searchHandler", null);
929
1818
  require_decorate.__decorate([(0, _aigne_afs_provider.Exec)("/tools/:name")], AFSMCP.prototype, "execToolHandler", null);
1819
+ var src_default = AFSMCP;
930
1820
 
931
1821
  //#endregion
932
- exports.AFSMCP = AFSMCP;
1822
+ exports.AFSMCP = AFSMCP;
1823
+ exports.default = src_default;