@juspay/neurolink 9.26.2 → 9.28.0

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.
Files changed (125) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +59 -9
  3. package/dist/cli/commands/config.d.ts +4 -4
  4. package/dist/cli/commands/mcp.d.ts +87 -0
  5. package/dist/cli/commands/mcp.js +1524 -0
  6. package/dist/cli/loop/optionsSchema.js +4 -0
  7. package/dist/core/modules/ToolsManager.js +29 -2
  8. package/dist/index.d.ts +2 -1
  9. package/dist/index.js +27 -1
  10. package/dist/lib/core/modules/ToolsManager.js +29 -2
  11. package/dist/lib/index.d.ts +2 -1
  12. package/dist/lib/index.js +27 -1
  13. package/dist/lib/mcp/agentExposure.d.ts +228 -0
  14. package/dist/lib/mcp/agentExposure.js +357 -0
  15. package/dist/lib/mcp/batching/index.d.ts +11 -0
  16. package/dist/lib/mcp/batching/index.js +11 -0
  17. package/dist/lib/mcp/batching/requestBatcher.d.ts +202 -0
  18. package/dist/lib/mcp/batching/requestBatcher.js +442 -0
  19. package/dist/lib/mcp/caching/index.d.ts +11 -0
  20. package/dist/lib/mcp/caching/index.js +11 -0
  21. package/dist/lib/mcp/caching/toolCache.d.ts +221 -0
  22. package/dist/lib/mcp/caching/toolCache.js +434 -0
  23. package/dist/lib/mcp/elicitation/elicitationManager.d.ts +169 -0
  24. package/dist/lib/mcp/elicitation/elicitationManager.js +377 -0
  25. package/dist/lib/mcp/elicitation/index.d.ts +11 -0
  26. package/dist/lib/mcp/elicitation/index.js +12 -0
  27. package/dist/lib/mcp/elicitation/types.d.ts +278 -0
  28. package/dist/lib/mcp/elicitation/types.js +11 -0
  29. package/dist/lib/mcp/elicitationProtocol.d.ts +228 -0
  30. package/dist/lib/mcp/elicitationProtocol.js +376 -0
  31. package/dist/lib/mcp/enhancedToolDiscovery.d.ts +205 -0
  32. package/dist/lib/mcp/enhancedToolDiscovery.js +482 -0
  33. package/dist/lib/mcp/index.d.ts +38 -1
  34. package/dist/lib/mcp/index.js +36 -3
  35. package/dist/lib/mcp/mcpRegistryClient.d.ts +332 -0
  36. package/dist/lib/mcp/mcpRegistryClient.js +489 -0
  37. package/dist/lib/mcp/mcpServerBase.d.ts +227 -0
  38. package/dist/lib/mcp/mcpServerBase.js +374 -0
  39. package/dist/lib/mcp/multiServerManager.d.ts +310 -0
  40. package/dist/lib/mcp/multiServerManager.js +580 -0
  41. package/dist/lib/mcp/routing/index.d.ts +11 -0
  42. package/dist/lib/mcp/routing/index.js +11 -0
  43. package/dist/lib/mcp/routing/toolRouter.d.ts +219 -0
  44. package/dist/lib/mcp/routing/toolRouter.js +417 -0
  45. package/dist/lib/mcp/serverCapabilities.d.ts +341 -0
  46. package/dist/lib/mcp/serverCapabilities.js +503 -0
  47. package/dist/lib/mcp/toolAnnotations.d.ts +154 -0
  48. package/dist/lib/mcp/toolAnnotations.js +240 -0
  49. package/dist/lib/mcp/toolConverter.d.ts +178 -0
  50. package/dist/lib/mcp/toolConverter.js +259 -0
  51. package/dist/lib/mcp/toolIntegration.d.ts +136 -0
  52. package/dist/lib/mcp/toolIntegration.js +335 -0
  53. package/dist/lib/memory/hippocampusInitializer.d.ts +2 -2
  54. package/dist/lib/memory/hippocampusInitializer.js +1 -1
  55. package/dist/lib/neurolink.d.ts +275 -2
  56. package/dist/lib/neurolink.js +596 -56
  57. package/dist/lib/providers/litellm.d.ts +10 -0
  58. package/dist/lib/providers/litellm.js +104 -2
  59. package/dist/lib/types/configTypes.d.ts +56 -0
  60. package/dist/lib/types/conversation.d.ts +2 -2
  61. package/dist/lib/types/generateTypes.d.ts +4 -0
  62. package/dist/lib/types/index.d.ts +2 -1
  63. package/dist/lib/types/modelTypes.d.ts +6 -6
  64. package/dist/lib/types/streamTypes.d.ts +2 -0
  65. package/dist/lib/types/tools.d.ts +2 -0
  66. package/dist/lib/utils/pricing.js +177 -17
  67. package/dist/lib/utils/schemaConversion.d.ts +6 -1
  68. package/dist/lib/utils/schemaConversion.js +50 -28
  69. package/dist/lib/workflow/config.d.ts +16 -16
  70. package/dist/mcp/agentExposure.d.ts +228 -0
  71. package/dist/mcp/agentExposure.js +356 -0
  72. package/dist/mcp/batching/index.d.ts +11 -0
  73. package/dist/mcp/batching/index.js +10 -0
  74. package/dist/mcp/batching/requestBatcher.d.ts +202 -0
  75. package/dist/mcp/batching/requestBatcher.js +441 -0
  76. package/dist/mcp/caching/index.d.ts +11 -0
  77. package/dist/mcp/caching/index.js +10 -0
  78. package/dist/mcp/caching/toolCache.d.ts +221 -0
  79. package/dist/mcp/caching/toolCache.js +433 -0
  80. package/dist/mcp/elicitation/elicitationManager.d.ts +169 -0
  81. package/dist/mcp/elicitation/elicitationManager.js +376 -0
  82. package/dist/mcp/elicitation/index.d.ts +11 -0
  83. package/dist/mcp/elicitation/index.js +11 -0
  84. package/dist/mcp/elicitation/types.d.ts +278 -0
  85. package/dist/mcp/elicitation/types.js +10 -0
  86. package/dist/mcp/elicitationProtocol.d.ts +228 -0
  87. package/dist/mcp/elicitationProtocol.js +375 -0
  88. package/dist/mcp/enhancedToolDiscovery.d.ts +205 -0
  89. package/dist/mcp/enhancedToolDiscovery.js +481 -0
  90. package/dist/mcp/index.d.ts +38 -1
  91. package/dist/mcp/index.js +36 -3
  92. package/dist/mcp/mcpRegistryClient.d.ts +332 -0
  93. package/dist/mcp/mcpRegistryClient.js +488 -0
  94. package/dist/mcp/mcpServerBase.d.ts +227 -0
  95. package/dist/mcp/mcpServerBase.js +373 -0
  96. package/dist/mcp/multiServerManager.d.ts +310 -0
  97. package/dist/mcp/multiServerManager.js +579 -0
  98. package/dist/mcp/routing/index.d.ts +11 -0
  99. package/dist/mcp/routing/index.js +10 -0
  100. package/dist/mcp/routing/toolRouter.d.ts +219 -0
  101. package/dist/mcp/routing/toolRouter.js +416 -0
  102. package/dist/mcp/serverCapabilities.d.ts +341 -0
  103. package/dist/mcp/serverCapabilities.js +502 -0
  104. package/dist/mcp/toolAnnotations.d.ts +154 -0
  105. package/dist/mcp/toolAnnotations.js +239 -0
  106. package/dist/mcp/toolConverter.d.ts +178 -0
  107. package/dist/mcp/toolConverter.js +258 -0
  108. package/dist/mcp/toolIntegration.d.ts +136 -0
  109. package/dist/mcp/toolIntegration.js +334 -0
  110. package/dist/memory/hippocampusInitializer.d.ts +2 -2
  111. package/dist/memory/hippocampusInitializer.js +1 -1
  112. package/dist/neurolink.d.ts +275 -2
  113. package/dist/neurolink.js +596 -56
  114. package/dist/providers/litellm.d.ts +10 -0
  115. package/dist/providers/litellm.js +104 -2
  116. package/dist/types/configTypes.d.ts +56 -0
  117. package/dist/types/conversation.d.ts +2 -2
  118. package/dist/types/generateTypes.d.ts +4 -0
  119. package/dist/types/index.d.ts +2 -1
  120. package/dist/types/streamTypes.d.ts +2 -0
  121. package/dist/types/tools.d.ts +2 -0
  122. package/dist/utils/pricing.js +177 -17
  123. package/dist/utils/schemaConversion.d.ts +6 -1
  124. package/dist/utils/schemaConversion.js +50 -28
  125. package/package.json +2 -2
@@ -0,0 +1,502 @@
1
+ /**
2
+ * MCP Server Capabilities - Resources and Prompts
3
+ *
4
+ * Extends MCP server functionality with resource and prompt handling
5
+ * according to MCP specification. This module provides:
6
+ * - Resource registration and management
7
+ * - Prompt template registration and execution
8
+ * - Resource subscription support
9
+ *
10
+ * @module mcp/serverCapabilities
11
+ * @since 8.39.0
12
+ */
13
+ import { EventEmitter } from "events";
14
+ import { logger } from "../utils/logger.js";
15
+ import { ErrorFactory } from "../utils/errorHandling.js";
16
+ import { withTimeout } from "../utils/async/withTimeout.js";
17
+ /**
18
+ * Server Capabilities Manager
19
+ *
20
+ * Manages resources and prompts for MCP servers.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * const capabilities = new ServerCapabilitiesManager({
25
+ * resources: true,
26
+ * prompts: true,
27
+ * });
28
+ *
29
+ * // Register a resource
30
+ * capabilities.registerResource({
31
+ * uri: "file:///data/config.json",
32
+ * name: "Configuration",
33
+ * mimeType: "application/json",
34
+ * reader: async (uri) => ({
35
+ * uri,
36
+ * mimeType: "application/json",
37
+ * text: JSON.stringify({ key: "value" }),
38
+ * }),
39
+ * });
40
+ *
41
+ * // Register a prompt
42
+ * capabilities.registerPrompt({
43
+ * name: "summarize",
44
+ * description: "Summarize text content",
45
+ * arguments: [{ name: "text", required: true }],
46
+ * generator: async (args) => ({
47
+ * messages: [
48
+ * { role: "user", content: { type: "text", text: `Summarize: ${args.text}` } },
49
+ * ],
50
+ * }),
51
+ * });
52
+ * ```
53
+ */
54
+ export class ServerCapabilitiesManager extends EventEmitter {
55
+ config;
56
+ resources = new Map();
57
+ prompts = new Map();
58
+ subscriptions = new Map();
59
+ resourceTemplates = new Map();
60
+ constructor(config = {}) {
61
+ super();
62
+ this.config = {
63
+ resources: config.resources ?? true,
64
+ prompts: config.prompts ?? true,
65
+ resourceSubscriptions: config.resourceSubscriptions ?? true,
66
+ };
67
+ }
68
+ // ========================================
69
+ // RESOURCE MANAGEMENT
70
+ // ========================================
71
+ /**
72
+ * Register a resource
73
+ */
74
+ registerResource(resource) {
75
+ if (!this.config.resources) {
76
+ throw ErrorFactory.invalidConfiguration("resources", "Resource support is not enabled");
77
+ }
78
+ this.validateResourceUri(resource.uri);
79
+ this.resources.set(resource.uri, resource);
80
+ this.emit("resourceRegistered", {
81
+ uri: resource.uri,
82
+ name: resource.name,
83
+ timestamp: new Date(),
84
+ });
85
+ logger.debug(`[ServerCapabilities] Registered resource: ${resource.uri}`);
86
+ return this;
87
+ }
88
+ /**
89
+ * Register a resource template (with URI pattern)
90
+ */
91
+ registerResourceTemplate(pattern, template) {
92
+ if (!this.config.resources) {
93
+ throw ErrorFactory.invalidConfiguration("resources", "Resource support is not enabled");
94
+ }
95
+ this.resourceTemplates.set(pattern, {
96
+ ...template,
97
+ uri: template.uriPattern,
98
+ });
99
+ this.emit("resourceTemplateRegistered", {
100
+ pattern,
101
+ timestamp: new Date(),
102
+ });
103
+ return this;
104
+ }
105
+ /**
106
+ * Unregister a resource
107
+ */
108
+ unregisterResource(uri) {
109
+ const removed = this.resources.delete(uri);
110
+ if (removed) {
111
+ // Clear subscriptions
112
+ this.subscriptions.delete(uri);
113
+ this.emit("resourceUnregistered", {
114
+ uri,
115
+ timestamp: new Date(),
116
+ });
117
+ }
118
+ return removed;
119
+ }
120
+ /**
121
+ * List all resources
122
+ */
123
+ listResources() {
124
+ return Array.from(this.resources.values()).map((r) => ({
125
+ uri: r.uri,
126
+ name: r.name,
127
+ description: r.description,
128
+ mimeType: r.mimeType,
129
+ size: r.size,
130
+ dynamic: r.dynamic,
131
+ annotations: r.annotations,
132
+ }));
133
+ }
134
+ /**
135
+ * Read a resource
136
+ */
137
+ async readResource(uri, context) {
138
+ // Check direct resources
139
+ let resource = this.resources.get(uri);
140
+ // Check templates if not found
141
+ if (!resource) {
142
+ resource = this.findResourceTemplate(uri);
143
+ }
144
+ if (!resource) {
145
+ throw ErrorFactory.invalidConfiguration("resource", `Resource not found: ${uri}`);
146
+ }
147
+ const startTime = Date.now();
148
+ const resourceTimeoutMs = 30_000;
149
+ try {
150
+ const content = await withTimeout(resource.reader(uri, context), resourceTimeoutMs, `Resource read timed out after ${resourceTimeoutMs}ms for URI: ${uri}`);
151
+ const duration = Date.now() - startTime;
152
+ this.emit("resourceRead", {
153
+ uri,
154
+ duration,
155
+ success: true,
156
+ timestamp: new Date(),
157
+ });
158
+ return content;
159
+ }
160
+ catch (error) {
161
+ const duration = Date.now() - startTime;
162
+ this.emit("resourceRead", {
163
+ uri,
164
+ duration,
165
+ success: false,
166
+ error: error instanceof Error ? error.message : String(error),
167
+ timestamp: new Date(),
168
+ });
169
+ throw error;
170
+ }
171
+ }
172
+ /**
173
+ * Subscribe to resource changes
174
+ */
175
+ subscribeToResource(uri, callback) {
176
+ if (!this.config.resourceSubscriptions) {
177
+ throw ErrorFactory.invalidConfiguration("resourceSubscriptions", "Resource subscriptions are not enabled");
178
+ }
179
+ if (!this.subscriptions.has(uri)) {
180
+ this.subscriptions.set(uri, new Set());
181
+ }
182
+ const subs = this.subscriptions.get(uri);
183
+ if (subs) {
184
+ subs.add(callback);
185
+ }
186
+ this.emit("resourceSubscribed", {
187
+ uri,
188
+ timestamp: new Date(),
189
+ });
190
+ // Return unsubscribe function
191
+ return () => {
192
+ const subs = this.subscriptions.get(uri);
193
+ if (subs) {
194
+ subs.delete(callback);
195
+ if (subs.size === 0) {
196
+ this.subscriptions.delete(uri);
197
+ }
198
+ }
199
+ this.emit("resourceUnsubscribed", {
200
+ uri,
201
+ timestamp: new Date(),
202
+ });
203
+ };
204
+ }
205
+ /**
206
+ * Notify subscribers of resource change
207
+ */
208
+ async notifyResourceChanged(uri) {
209
+ const subscribers = this.subscriptions.get(uri);
210
+ if (!subscribers || subscribers.size === 0) {
211
+ return;
212
+ }
213
+ try {
214
+ const content = await this.readResource(uri);
215
+ for (const callback of subscribers) {
216
+ try {
217
+ const result = callback(uri, content);
218
+ // Handle async callbacks that return promises
219
+ if (result && typeof result.catch === "function") {
220
+ result.catch((error) => {
221
+ logger.error(`[ServerCapabilities] Async error notifying subscriber for ${uri}:`, error);
222
+ });
223
+ }
224
+ }
225
+ catch (error) {
226
+ logger.error(`[ServerCapabilities] Error notifying subscriber for ${uri}:`, error);
227
+ }
228
+ }
229
+ this.emit("resourceChanged", {
230
+ uri,
231
+ subscriberCount: subscribers.size,
232
+ timestamp: new Date(),
233
+ });
234
+ }
235
+ catch (error) {
236
+ logger.error(`[ServerCapabilities] Error reading resource for notification: ${uri}`, error);
237
+ }
238
+ }
239
+ /**
240
+ * Get resource by URI
241
+ */
242
+ getResource(uri) {
243
+ return this.resources.get(uri) ?? this.findResourceTemplate(uri);
244
+ }
245
+ /**
246
+ * Validate resource URI
247
+ */
248
+ validateResourceUri(uri) {
249
+ try {
250
+ new URL(uri);
251
+ }
252
+ catch {
253
+ // Allow non-URL URIs but warn
254
+ logger.warn(`[ServerCapabilities] Resource URI is not a valid URL: ${uri}`);
255
+ }
256
+ }
257
+ /**
258
+ * Find matching resource template
259
+ */
260
+ findResourceTemplate(uri) {
261
+ for (const [pattern, template] of this.resourceTemplates) {
262
+ if (this.matchesPattern(uri, pattern)) {
263
+ return {
264
+ ...template,
265
+ uri,
266
+ };
267
+ }
268
+ }
269
+ return undefined;
270
+ }
271
+ /**
272
+ * Check if URI matches a pattern
273
+ */
274
+ matchesPattern(uri, pattern) {
275
+ // Guard against excessively long patterns that could cause ReDoS
276
+ if (pattern.length > 200) {
277
+ return false;
278
+ }
279
+ // Escape regex metacharacters, then replace glob wildcards
280
+ const regexPattern = pattern
281
+ .replace(/[.+^${}()|[\]\\]/g, "\\$&")
282
+ .replace(/\*/g, ".*")
283
+ .replace(/\?/g, ".")
284
+ // Restore URI template placeholders: \{...\} -> [^/]+
285
+ .replace(/\\\{[^}]*\\\}/g, "[^/]+");
286
+ try {
287
+ return new RegExp(`^${regexPattern}$`).test(uri);
288
+ }
289
+ catch {
290
+ // Invalid regex pattern — treat as non-match
291
+ return false;
292
+ }
293
+ }
294
+ // ========================================
295
+ // PROMPT MANAGEMENT
296
+ // ========================================
297
+ /**
298
+ * Register a prompt
299
+ */
300
+ registerPrompt(prompt) {
301
+ if (!this.config.prompts) {
302
+ throw ErrorFactory.invalidConfiguration("prompts", "Prompt support is not enabled");
303
+ }
304
+ this.validatePromptName(prompt.name);
305
+ this.prompts.set(prompt.name, prompt);
306
+ this.emit("promptRegistered", {
307
+ name: prompt.name,
308
+ timestamp: new Date(),
309
+ });
310
+ logger.debug(`[ServerCapabilities] Registered prompt: ${prompt.name}`);
311
+ return this;
312
+ }
313
+ /**
314
+ * Unregister a prompt
315
+ */
316
+ unregisterPrompt(name) {
317
+ const removed = this.prompts.delete(name);
318
+ if (removed) {
319
+ this.emit("promptUnregistered", {
320
+ name,
321
+ timestamp: new Date(),
322
+ });
323
+ }
324
+ return removed;
325
+ }
326
+ /**
327
+ * List all prompts
328
+ */
329
+ listPrompts() {
330
+ return Array.from(this.prompts.values()).map((p) => ({
331
+ name: p.name,
332
+ description: p.description,
333
+ arguments: p.arguments,
334
+ }));
335
+ }
336
+ /**
337
+ * Get a prompt
338
+ */
339
+ async getPrompt(name, args = {}, context) {
340
+ const prompt = this.prompts.get(name);
341
+ if (!prompt) {
342
+ throw ErrorFactory.invalidConfiguration("prompt", `Prompt not found: ${name}`);
343
+ }
344
+ // Validate required arguments
345
+ for (const arg of prompt.arguments ?? []) {
346
+ if (arg.required && args[arg.name] === undefined) {
347
+ throw ErrorFactory.invalidConfiguration("promptArgument", `Missing required argument: ${arg.name}`);
348
+ }
349
+ }
350
+ const startTime = Date.now();
351
+ const promptTimeoutMs = 30_000;
352
+ try {
353
+ const result = await withTimeout(prompt.generator(args, context), promptTimeoutMs, `Prompt generation timed out after ${promptTimeoutMs}ms for prompt: ${name}`);
354
+ const duration = Date.now() - startTime;
355
+ this.emit("promptGenerated", {
356
+ name,
357
+ duration,
358
+ success: true,
359
+ messageCount: result.messages.length,
360
+ timestamp: new Date(),
361
+ });
362
+ return result;
363
+ }
364
+ catch (error) {
365
+ const duration = Date.now() - startTime;
366
+ this.emit("promptGenerated", {
367
+ name,
368
+ duration,
369
+ success: false,
370
+ error: error instanceof Error ? error.message : String(error),
371
+ timestamp: new Date(),
372
+ });
373
+ throw error;
374
+ }
375
+ }
376
+ /**
377
+ * Get prompt by name
378
+ */
379
+ getPromptDefinition(name) {
380
+ return this.prompts.get(name);
381
+ }
382
+ /**
383
+ * Validate prompt name
384
+ */
385
+ validatePromptName(name) {
386
+ if (!name || typeof name !== "string") {
387
+ throw ErrorFactory.invalidConfiguration("promptName", "Prompt name is required and must be a string");
388
+ }
389
+ if (!/^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(name)) {
390
+ throw ErrorFactory.invalidConfiguration("promptName", "Prompt name must start with a letter or underscore and contain only alphanumeric characters, underscores, and hyphens");
391
+ }
392
+ if (this.prompts.has(name)) {
393
+ throw ErrorFactory.invalidConfiguration("promptName", `Prompt '${name}' is already registered`);
394
+ }
395
+ }
396
+ // ========================================
397
+ // UTILITY METHODS
398
+ // ========================================
399
+ /**
400
+ * Get capabilities object for MCP protocol
401
+ */
402
+ getCapabilities() {
403
+ const capabilities = {};
404
+ if (this.config.resources) {
405
+ capabilities.resources = {
406
+ subscribe: this.config.resourceSubscriptions,
407
+ listChanged: true,
408
+ };
409
+ }
410
+ if (this.config.prompts) {
411
+ capabilities.prompts = {
412
+ listChanged: true,
413
+ };
414
+ }
415
+ return capabilities;
416
+ }
417
+ /**
418
+ * Get statistics
419
+ */
420
+ getStatistics() {
421
+ let subscriptionCount = 0;
422
+ for (const subs of this.subscriptions.values()) {
423
+ subscriptionCount += subs.size;
424
+ }
425
+ return {
426
+ resourceCount: this.resources.size,
427
+ templateCount: this.resourceTemplates.size,
428
+ promptCount: this.prompts.size,
429
+ subscriptionCount,
430
+ };
431
+ }
432
+ /**
433
+ * Clear all resources and prompts
434
+ */
435
+ clear() {
436
+ this.resources.clear();
437
+ this.resourceTemplates.clear();
438
+ this.prompts.clear();
439
+ this.subscriptions.clear();
440
+ this.emit("cleared", { timestamp: new Date() });
441
+ }
442
+ }
443
+ /**
444
+ * Create a simple text resource
445
+ */
446
+ export function createTextResource(uri, name, content, options) {
447
+ return {
448
+ uri,
449
+ name,
450
+ description: options?.description,
451
+ mimeType: "text/plain",
452
+ dynamic: options?.dynamic ?? typeof content === "function",
453
+ reader: async (requestUri) => ({
454
+ uri: requestUri,
455
+ mimeType: "text/plain",
456
+ text: typeof content === "function" ? await content() : content,
457
+ }),
458
+ };
459
+ }
460
+ /**
461
+ * Create a JSON resource
462
+ */
463
+ export function createJsonResource(uri, name, content, options) {
464
+ return {
465
+ uri,
466
+ name,
467
+ description: options?.description,
468
+ mimeType: "application/json",
469
+ dynamic: options?.dynamic ?? typeof content === "function",
470
+ reader: async (requestUri) => ({
471
+ uri: requestUri,
472
+ mimeType: "application/json",
473
+ text: JSON.stringify(typeof content === "function" ? await content() : content, null, 2),
474
+ }),
475
+ };
476
+ }
477
+ /**
478
+ * Create a simple prompt template
479
+ */
480
+ export function createPrompt(name, template, options) {
481
+ return {
482
+ name,
483
+ description: options?.description,
484
+ arguments: options?.arguments,
485
+ generator: async (args) => {
486
+ // Simple template substitution
487
+ let text = template;
488
+ for (const [key, value] of Object.entries(args)) {
489
+ const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
490
+ text = text.replace(new RegExp(`\\{${escapedKey}\\}`, "g"), () => String(value));
491
+ }
492
+ return {
493
+ messages: [
494
+ {
495
+ role: "user",
496
+ content: { type: "text", text },
497
+ },
498
+ ],
499
+ };
500
+ },
501
+ };
502
+ }
@@ -0,0 +1,154 @@
1
+ /**
2
+ * MCP Tool Annotations System
3
+ *
4
+ * Enhanced tool annotations for MCP tools providing hints to AI models
5
+ * about tool behavior, safety, and execution characteristics.
6
+ *
7
+ * Implements MCP 2024-11-05 specification tool annotations including:
8
+ * - readOnlyHint: Tool only reads data
9
+ * - destructiveHint: Tool performs destructive operations
10
+ * - idempotentHint: Tool can be safely retried
11
+ * - requiresConfirmation: Tool needs user confirmation
12
+ *
13
+ * @module mcp/toolAnnotations
14
+ * @since 8.39.0
15
+ */
16
+ import type { JsonObject } from "../types/common.js";
17
+ import type { NeuroLinkExecutionContext, ToolResult } from "../types/mcpTypes.js";
18
+ /**
19
+ * Tool annotation metadata for MCP tools
20
+ * Provides hints to AI models about tool behavior and safety
21
+ */
22
+ export type MCPToolAnnotations = {
23
+ /**
24
+ * Human-readable title for the tool
25
+ */
26
+ title?: string;
27
+ /**
28
+ * Whether the tool only reads data without side effects
29
+ * When true, AI models may call more freely for information gathering
30
+ */
31
+ readOnlyHint?: boolean;
32
+ /**
33
+ * Whether the tool performs destructive operations
34
+ * When true, requires additional confirmation before execution
35
+ */
36
+ destructiveHint?: boolean;
37
+ /**
38
+ * Whether the tool can be safely retried without side effects
39
+ * When true, automatic retry is safe on failure
40
+ */
41
+ idempotentHint?: boolean;
42
+ /**
43
+ * Whether the tool requires user confirmation before execution
44
+ * When true, triggers HITL confirmation flow
45
+ */
46
+ requiresConfirmation?: boolean;
47
+ /**
48
+ * Whether the tool operates on an open world of resources
49
+ * From the MCP protocol specification
50
+ */
51
+ openWorldHint?: boolean;
52
+ /**
53
+ * Custom tags for categorization and filtering
54
+ */
55
+ tags?: string[];
56
+ /**
57
+ * Estimated execution time in milliseconds
58
+ */
59
+ estimatedDuration?: number;
60
+ /**
61
+ * Rate limit hint (calls per minute)
62
+ */
63
+ rateLimitHint?: number;
64
+ /**
65
+ * Cost hint (arbitrary units for comparison)
66
+ */
67
+ costHint?: number;
68
+ /**
69
+ * Complexity level for UI display
70
+ */
71
+ complexity?: "simple" | "medium" | "complex";
72
+ /**
73
+ * Whether tool execution should be audited/logged
74
+ */
75
+ auditRequired?: boolean;
76
+ /**
77
+ * Security classification for the tool
78
+ */
79
+ securityLevel?: "public" | "internal" | "restricted";
80
+ };
81
+ /**
82
+ * Enhanced tool definition with annotations
83
+ */
84
+ export type MCPServerTool = {
85
+ /**
86
+ * Tool name (must be unique within server)
87
+ */
88
+ name: string;
89
+ /**
90
+ * Tool description for AI models
91
+ */
92
+ description: string;
93
+ /**
94
+ * JSON Schema for tool input parameters
95
+ */
96
+ inputSchema?: JsonObject;
97
+ /**
98
+ * Output schema for result validation
99
+ */
100
+ outputSchema?: JsonObject;
101
+ /**
102
+ * Tool behavior annotations
103
+ */
104
+ annotations?: MCPToolAnnotations;
105
+ /**
106
+ * Tool execution function
107
+ */
108
+ execute: (params: unknown, context?: NeuroLinkExecutionContext) => Promise<ToolResult | unknown>;
109
+ /**
110
+ * Additional metadata
111
+ */
112
+ metadata?: Record<string, unknown>;
113
+ };
114
+ /**
115
+ * Infer annotations from tool definition
116
+ * Uses heuristics based on tool description and name
117
+ */
118
+ export declare function inferAnnotations(tool: Pick<MCPServerTool, "name" | "description">): MCPToolAnnotations;
119
+ /**
120
+ * Merge multiple annotation objects with precedence
121
+ * Later annotations override earlier ones
122
+ */
123
+ export declare function mergeAnnotations(...annotationSets: (MCPToolAnnotations | undefined)[]): MCPToolAnnotations;
124
+ /**
125
+ * Validate tool annotations
126
+ * Returns list of validation errors (empty if valid)
127
+ */
128
+ export declare function validateAnnotations(annotations: MCPToolAnnotations): string[];
129
+ /**
130
+ * Create a tool with default annotations inferred
131
+ */
132
+ export declare function createAnnotatedTool(tool: Omit<MCPServerTool, "annotations"> & {
133
+ annotations?: MCPToolAnnotations;
134
+ }): MCPServerTool;
135
+ /**
136
+ * Check if a tool requires confirmation based on annotations
137
+ */
138
+ export declare function requiresConfirmation(tool: MCPServerTool): boolean;
139
+ /**
140
+ * Check if a tool is safe for automatic retry
141
+ */
142
+ export declare function isSafeToRetry(tool: MCPServerTool): boolean;
143
+ /**
144
+ * Get tool safety level based on annotations
145
+ */
146
+ export declare function getToolSafetyLevel(tool: MCPServerTool): "safe" | "moderate" | "dangerous";
147
+ /**
148
+ * Filter tools by annotation predicates
149
+ */
150
+ export declare function filterToolsByAnnotations(tools: MCPServerTool[], predicate: (annotations: MCPToolAnnotations) => boolean): MCPServerTool[];
151
+ /**
152
+ * Get human-readable summary of tool annotations
153
+ */
154
+ export declare function getAnnotationSummary(annotations: MCPToolAnnotations): string;