@juspay/neurolink 9.27.0 → 9.28.1

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 (135) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +78 -25
  3. package/dist/adapters/providerImageAdapter.js +9 -1
  4. package/dist/cli/commands/config.d.ts +4 -4
  5. package/dist/cli/commands/mcp.d.ts +87 -0
  6. package/dist/cli/commands/mcp.js +1524 -0
  7. package/dist/cli/loop/optionsSchema.js +4 -0
  8. package/dist/constants/contextWindows.js +18 -7
  9. package/dist/constants/enums.d.ts +36 -6
  10. package/dist/constants/enums.js +38 -5
  11. package/dist/constants/tokens.d.ts +4 -0
  12. package/dist/constants/tokens.js +4 -0
  13. package/dist/core/modules/ToolsManager.js +29 -2
  14. package/dist/index.d.ts +2 -1
  15. package/dist/index.js +27 -1
  16. package/dist/lib/adapters/providerImageAdapter.js +9 -1
  17. package/dist/lib/constants/contextWindows.js +18 -7
  18. package/dist/lib/constants/enums.d.ts +36 -6
  19. package/dist/lib/constants/enums.js +38 -5
  20. package/dist/lib/constants/tokens.d.ts +4 -0
  21. package/dist/lib/constants/tokens.js +4 -0
  22. package/dist/lib/core/modules/ToolsManager.js +29 -2
  23. package/dist/lib/index.d.ts +2 -1
  24. package/dist/lib/index.js +27 -1
  25. package/dist/lib/mcp/agentExposure.d.ts +228 -0
  26. package/dist/lib/mcp/agentExposure.js +357 -0
  27. package/dist/lib/mcp/batching/index.d.ts +11 -0
  28. package/dist/lib/mcp/batching/index.js +11 -0
  29. package/dist/lib/mcp/batching/requestBatcher.d.ts +202 -0
  30. package/dist/lib/mcp/batching/requestBatcher.js +442 -0
  31. package/dist/lib/mcp/caching/index.d.ts +11 -0
  32. package/dist/lib/mcp/caching/index.js +11 -0
  33. package/dist/lib/mcp/caching/toolCache.d.ts +221 -0
  34. package/dist/lib/mcp/caching/toolCache.js +434 -0
  35. package/dist/lib/mcp/elicitation/elicitationManager.d.ts +169 -0
  36. package/dist/lib/mcp/elicitation/elicitationManager.js +377 -0
  37. package/dist/lib/mcp/elicitation/index.d.ts +11 -0
  38. package/dist/lib/mcp/elicitation/index.js +12 -0
  39. package/dist/lib/mcp/elicitation/types.d.ts +278 -0
  40. package/dist/lib/mcp/elicitation/types.js +11 -0
  41. package/dist/lib/mcp/elicitationProtocol.d.ts +228 -0
  42. package/dist/lib/mcp/elicitationProtocol.js +376 -0
  43. package/dist/lib/mcp/enhancedToolDiscovery.d.ts +205 -0
  44. package/dist/lib/mcp/enhancedToolDiscovery.js +482 -0
  45. package/dist/lib/mcp/index.d.ts +38 -1
  46. package/dist/lib/mcp/index.js +36 -3
  47. package/dist/lib/mcp/mcpRegistryClient.d.ts +332 -0
  48. package/dist/lib/mcp/mcpRegistryClient.js +489 -0
  49. package/dist/lib/mcp/mcpServerBase.d.ts +227 -0
  50. package/dist/lib/mcp/mcpServerBase.js +374 -0
  51. package/dist/lib/mcp/multiServerManager.d.ts +310 -0
  52. package/dist/lib/mcp/multiServerManager.js +580 -0
  53. package/dist/lib/mcp/routing/index.d.ts +11 -0
  54. package/dist/lib/mcp/routing/index.js +11 -0
  55. package/dist/lib/mcp/routing/toolRouter.d.ts +219 -0
  56. package/dist/lib/mcp/routing/toolRouter.js +417 -0
  57. package/dist/lib/mcp/serverCapabilities.d.ts +341 -0
  58. package/dist/lib/mcp/serverCapabilities.js +503 -0
  59. package/dist/lib/mcp/toolAnnotations.d.ts +154 -0
  60. package/dist/lib/mcp/toolAnnotations.js +240 -0
  61. package/dist/lib/mcp/toolConverter.d.ts +178 -0
  62. package/dist/lib/mcp/toolConverter.js +259 -0
  63. package/dist/lib/mcp/toolIntegration.d.ts +136 -0
  64. package/dist/lib/mcp/toolIntegration.js +335 -0
  65. package/dist/lib/neurolink.d.ts +275 -2
  66. package/dist/lib/neurolink.js +596 -56
  67. package/dist/lib/providers/amazonBedrock.js +1 -1
  68. package/dist/lib/providers/anthropic.js +1 -1
  69. package/dist/lib/providers/litellm.d.ts +10 -0
  70. package/dist/lib/providers/litellm.js +104 -2
  71. package/dist/lib/types/configTypes.d.ts +56 -0
  72. package/dist/lib/types/generateTypes.d.ts +4 -0
  73. package/dist/lib/types/index.d.ts +1 -1
  74. package/dist/lib/types/modelTypes.d.ts +6 -6
  75. package/dist/lib/types/streamTypes.d.ts +2 -0
  76. package/dist/lib/types/tools.d.ts +2 -0
  77. package/dist/lib/utils/pricing.js +177 -17
  78. package/dist/lib/utils/schemaConversion.d.ts +6 -1
  79. package/dist/lib/utils/schemaConversion.js +50 -28
  80. package/dist/lib/workflow/config.d.ts +16 -16
  81. package/dist/mcp/agentExposure.d.ts +228 -0
  82. package/dist/mcp/agentExposure.js +356 -0
  83. package/dist/mcp/batching/index.d.ts +11 -0
  84. package/dist/mcp/batching/index.js +10 -0
  85. package/dist/mcp/batching/requestBatcher.d.ts +202 -0
  86. package/dist/mcp/batching/requestBatcher.js +441 -0
  87. package/dist/mcp/caching/index.d.ts +11 -0
  88. package/dist/mcp/caching/index.js +10 -0
  89. package/dist/mcp/caching/toolCache.d.ts +221 -0
  90. package/dist/mcp/caching/toolCache.js +433 -0
  91. package/dist/mcp/elicitation/elicitationManager.d.ts +169 -0
  92. package/dist/mcp/elicitation/elicitationManager.js +376 -0
  93. package/dist/mcp/elicitation/index.d.ts +11 -0
  94. package/dist/mcp/elicitation/index.js +11 -0
  95. package/dist/mcp/elicitation/types.d.ts +278 -0
  96. package/dist/mcp/elicitation/types.js +10 -0
  97. package/dist/mcp/elicitationProtocol.d.ts +228 -0
  98. package/dist/mcp/elicitationProtocol.js +375 -0
  99. package/dist/mcp/enhancedToolDiscovery.d.ts +205 -0
  100. package/dist/mcp/enhancedToolDiscovery.js +481 -0
  101. package/dist/mcp/index.d.ts +38 -1
  102. package/dist/mcp/index.js +36 -3
  103. package/dist/mcp/mcpRegistryClient.d.ts +332 -0
  104. package/dist/mcp/mcpRegistryClient.js +488 -0
  105. package/dist/mcp/mcpServerBase.d.ts +227 -0
  106. package/dist/mcp/mcpServerBase.js +373 -0
  107. package/dist/mcp/multiServerManager.d.ts +310 -0
  108. package/dist/mcp/multiServerManager.js +579 -0
  109. package/dist/mcp/routing/index.d.ts +11 -0
  110. package/dist/mcp/routing/index.js +10 -0
  111. package/dist/mcp/routing/toolRouter.d.ts +219 -0
  112. package/dist/mcp/routing/toolRouter.js +416 -0
  113. package/dist/mcp/serverCapabilities.d.ts +341 -0
  114. package/dist/mcp/serverCapabilities.js +502 -0
  115. package/dist/mcp/toolAnnotations.d.ts +154 -0
  116. package/dist/mcp/toolAnnotations.js +239 -0
  117. package/dist/mcp/toolConverter.d.ts +178 -0
  118. package/dist/mcp/toolConverter.js +258 -0
  119. package/dist/mcp/toolIntegration.d.ts +136 -0
  120. package/dist/mcp/toolIntegration.js +334 -0
  121. package/dist/neurolink.d.ts +275 -2
  122. package/dist/neurolink.js +596 -56
  123. package/dist/providers/amazonBedrock.js +1 -1
  124. package/dist/providers/anthropic.js +1 -1
  125. package/dist/providers/litellm.d.ts +10 -0
  126. package/dist/providers/litellm.js +104 -2
  127. package/dist/types/configTypes.d.ts +56 -0
  128. package/dist/types/generateTypes.d.ts +4 -0
  129. package/dist/types/index.d.ts +1 -1
  130. package/dist/types/streamTypes.d.ts +2 -0
  131. package/dist/types/tools.d.ts +2 -0
  132. package/dist/utils/pricing.js +177 -17
  133. package/dist/utils/schemaConversion.d.ts +6 -1
  134. package/dist/utils/schemaConversion.js +50 -28
  135. package/package.json +1 -1
@@ -0,0 +1,239 @@
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
+ /**
17
+ * Infer annotations from tool definition
18
+ * Uses heuristics based on tool description and name
19
+ */
20
+ export function inferAnnotations(tool) {
21
+ const name = tool.name;
22
+ const description = tool.description.toLowerCase();
23
+ const annotations = {};
24
+ // Helper: match keyword with word boundaries to avoid false positives
25
+ // (e.g., "get" should not match "forget", "together", "target")
26
+ // Also handles underscore/hyphen/camelCase-separated names (e.g., "delete_record", "readFile")
27
+ const matchesKeyword = (text, keyword) => {
28
+ // Standard word boundary match (works for space-separated text like descriptions)
29
+ if (new RegExp(`\\b${keyword}\\b`, "i").test(text)) {
30
+ return true;
31
+ }
32
+ // Also check underscore/hyphen/camelCase segments (common in tool names)
33
+ const normalized = text
34
+ .replace(/([a-z0-9])([A-Z])/g, "$1_$2")
35
+ .toLowerCase();
36
+ return normalized
37
+ .split(/[_-]/)
38
+ .some((segment) => segment === keyword.toLowerCase());
39
+ };
40
+ // Infer read-only from description keywords
41
+ const readOnlyKeywords = [
42
+ "get",
43
+ "list",
44
+ "read",
45
+ "fetch",
46
+ "query",
47
+ "search",
48
+ "find",
49
+ "show",
50
+ "display",
51
+ "view",
52
+ "retrieve",
53
+ "check",
54
+ "inspect",
55
+ "look",
56
+ ];
57
+ if (readOnlyKeywords.some((keyword) => matchesKeyword(description, keyword) || matchesKeyword(name, keyword))) {
58
+ annotations.readOnlyHint = true;
59
+ }
60
+ // Infer destructive from description keywords
61
+ const destructiveKeywords = [
62
+ "delete",
63
+ "remove",
64
+ "drop",
65
+ "destroy",
66
+ "clear",
67
+ "purge",
68
+ "erase",
69
+ "wipe",
70
+ "truncate",
71
+ "reset",
72
+ ];
73
+ if (destructiveKeywords.some((keyword) => matchesKeyword(description, keyword) || matchesKeyword(name, keyword))) {
74
+ annotations.destructiveHint = true;
75
+ annotations.requiresConfirmation = true;
76
+ }
77
+ // Infer idempotent from description keywords
78
+ const idempotentKeywords = ["set", "update", "put", "upsert", "replace"];
79
+ if (idempotentKeywords.some((keyword) => matchesKeyword(description, keyword) || matchesKeyword(name, keyword)) &&
80
+ !annotations.destructiveHint) {
81
+ annotations.idempotentHint = true;
82
+ }
83
+ // Infer complexity from description length and keywords
84
+ const complexKeywords = [
85
+ "complex",
86
+ "analyze",
87
+ "process",
88
+ "generate",
89
+ "transform",
90
+ "compute",
91
+ "calculate",
92
+ ];
93
+ if (complexKeywords.some((keyword) => matchesKeyword(description, keyword) || matchesKeyword(name, keyword))) {
94
+ annotations.complexity = "complex";
95
+ }
96
+ else if (description.length > 100) {
97
+ annotations.complexity = "medium";
98
+ }
99
+ else {
100
+ annotations.complexity = "simple";
101
+ }
102
+ return annotations;
103
+ }
104
+ /**
105
+ * Merge multiple annotation objects with precedence
106
+ * Later annotations override earlier ones
107
+ */
108
+ export function mergeAnnotations(...annotationSets) {
109
+ const result = {};
110
+ for (const annotations of annotationSets) {
111
+ if (annotations) {
112
+ const existingTags = result.tags ? [...result.tags] : [];
113
+ Object.assign(result, annotations);
114
+ // Special handling for arrays (merge instead of replace)
115
+ if (existingTags.length > 0 || annotations.tags) {
116
+ const newTags = annotations.tags ?? [];
117
+ result.tags = [...new Set([...existingTags, ...newTags])];
118
+ }
119
+ }
120
+ }
121
+ return result;
122
+ }
123
+ /**
124
+ * Validate tool annotations
125
+ * Returns list of validation errors (empty if valid)
126
+ */
127
+ export function validateAnnotations(annotations) {
128
+ const errors = [];
129
+ // Check for conflicting annotations
130
+ if (annotations.readOnlyHint && annotations.destructiveHint) {
131
+ errors.push("Tool cannot be both readOnly and destructive - these are conflicting hints");
132
+ }
133
+ // Validate rate limit hint
134
+ if (annotations.rateLimitHint !== undefined &&
135
+ (annotations.rateLimitHint < 0 ||
136
+ !Number.isFinite(annotations.rateLimitHint))) {
137
+ errors.push("rateLimitHint must be a non-negative number");
138
+ }
139
+ // Validate estimated duration
140
+ if (annotations.estimatedDuration !== undefined &&
141
+ (annotations.estimatedDuration < 0 ||
142
+ !Number.isFinite(annotations.estimatedDuration))) {
143
+ errors.push("estimatedDuration must be a non-negative number");
144
+ }
145
+ // Validate cost hint
146
+ if (annotations.costHint !== undefined &&
147
+ (annotations.costHint < 0 || !Number.isFinite(annotations.costHint))) {
148
+ errors.push("costHint must be a non-negative number");
149
+ }
150
+ // Validate tags are strings
151
+ if (annotations.tags) {
152
+ for (const tag of annotations.tags) {
153
+ if (typeof tag !== "string" || tag.length === 0) {
154
+ errors.push("All tags must be non-empty strings");
155
+ break;
156
+ }
157
+ }
158
+ }
159
+ return errors;
160
+ }
161
+ /**
162
+ * Create a tool with default annotations inferred
163
+ */
164
+ export function createAnnotatedTool(tool) {
165
+ const inferredAnnotations = inferAnnotations(tool);
166
+ const mergedAnnotations = mergeAnnotations(inferredAnnotations, tool.annotations);
167
+ return {
168
+ ...tool,
169
+ annotations: mergedAnnotations,
170
+ };
171
+ }
172
+ /**
173
+ * Check if a tool requires confirmation based on annotations
174
+ */
175
+ export function requiresConfirmation(tool) {
176
+ return !!(tool.annotations?.requiresConfirmation || tool.annotations?.destructiveHint);
177
+ }
178
+ /**
179
+ * Check if a tool is safe for automatic retry
180
+ */
181
+ export function isSafeToRetry(tool) {
182
+ return !!(tool.annotations?.idempotentHint || tool.annotations?.readOnlyHint);
183
+ }
184
+ /**
185
+ * Get tool safety level based on annotations
186
+ */
187
+ export function getToolSafetyLevel(tool) {
188
+ if (tool.annotations?.destructiveHint) {
189
+ return "dangerous";
190
+ }
191
+ if (tool.annotations?.readOnlyHint) {
192
+ return "safe";
193
+ }
194
+ if (tool.annotations?.idempotentHint) {
195
+ return "moderate";
196
+ }
197
+ // Default to moderate for unknown tools
198
+ return "moderate";
199
+ }
200
+ /**
201
+ * Filter tools by annotation predicates
202
+ */
203
+ export function filterToolsByAnnotations(tools, predicate) {
204
+ return tools.filter((tool) => {
205
+ const annotations = tool.annotations ?? {};
206
+ return predicate(annotations);
207
+ });
208
+ }
209
+ /**
210
+ * Get human-readable summary of tool annotations
211
+ */
212
+ export function getAnnotationSummary(annotations) {
213
+ const parts = [];
214
+ if (annotations.title) {
215
+ parts.push(annotations.title);
216
+ }
217
+ if (annotations.readOnlyHint) {
218
+ parts.push("read-only");
219
+ }
220
+ if (annotations.destructiveHint) {
221
+ parts.push("DESTRUCTIVE");
222
+ }
223
+ if (annotations.idempotentHint) {
224
+ parts.push("idempotent");
225
+ }
226
+ if (annotations.requiresConfirmation) {
227
+ parts.push("requires confirmation");
228
+ }
229
+ if (annotations.complexity) {
230
+ parts.push(`${annotations.complexity} complexity`);
231
+ }
232
+ if (annotations.estimatedDuration !== undefined) {
233
+ parts.push(`~${annotations.estimatedDuration}ms`);
234
+ }
235
+ if (annotations.tags?.length) {
236
+ parts.push(`tags: ${annotations.tags.join(", ")}`);
237
+ }
238
+ return parts.length > 0 ? `[${parts.join(" | ")}]` : "[no annotations]";
239
+ }
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Tool Converter Utilities
3
+ *
4
+ * Converts between NeuroLink tool format and MCP tool format.
5
+ * Enables seamless interoperability between NeuroLink's internal
6
+ * tool representation and the MCP protocol specification.
7
+ *
8
+ * @module mcp/toolConverter
9
+ * @since 8.39.0
10
+ */
11
+ import type { JsonObject, JsonValue } from "../types/common.js";
12
+ import type { NeuroLinkExecutionContext, ToolResult } from "../types/mcpTypes.js";
13
+ import type { MCPServerTool, MCPToolAnnotations } from "./toolAnnotations.js";
14
+ /**
15
+ * NeuroLink internal tool format
16
+ */
17
+ export type NeuroLinkTool = {
18
+ /**
19
+ * Tool name
20
+ */
21
+ name: string;
22
+ /**
23
+ * Tool description
24
+ */
25
+ description: string;
26
+ /**
27
+ * Input parameters schema
28
+ */
29
+ parameters?: JsonObject;
30
+ /**
31
+ * Tool execution function
32
+ */
33
+ execute: (params: unknown, context?: NeuroLinkExecutionContext) => Promise<ToolResult | unknown>;
34
+ /**
35
+ * Category for organization
36
+ */
37
+ category?: string;
38
+ /**
39
+ * Tags for filtering
40
+ */
41
+ tags?: string[];
42
+ /**
43
+ * Whether the tool is async
44
+ */
45
+ isAsync?: boolean;
46
+ /**
47
+ * Custom metadata
48
+ */
49
+ metadata?: Record<string, JsonValue>;
50
+ };
51
+ /**
52
+ * MCP protocol tool format (from @modelcontextprotocol/sdk)
53
+ */
54
+ export type MCPProtocolTool = {
55
+ /**
56
+ * Tool name
57
+ */
58
+ name: string;
59
+ /**
60
+ * Tool description
61
+ */
62
+ description?: string;
63
+ /**
64
+ * JSON Schema for input
65
+ */
66
+ inputSchema: {
67
+ type: "object";
68
+ properties?: Record<string, JsonObject>;
69
+ required?: string[];
70
+ };
71
+ /**
72
+ * Optional annotations (MCP 2024-11-05+)
73
+ */
74
+ annotations?: {
75
+ title?: string;
76
+ readOnlyHint?: boolean;
77
+ destructiveHint?: boolean;
78
+ idempotentHint?: boolean;
79
+ openWorldHint?: boolean;
80
+ };
81
+ };
82
+ /**
83
+ * Tool converter options
84
+ */
85
+ export type ToolConverterOptions = {
86
+ /**
87
+ * Automatically infer annotations from tool definition
88
+ */
89
+ inferAnnotations?: boolean;
90
+ /**
91
+ * Default annotations to apply
92
+ */
93
+ defaultAnnotations?: MCPToolAnnotations;
94
+ /**
95
+ * Whether to preserve original metadata
96
+ */
97
+ preserveMetadata?: boolean;
98
+ /**
99
+ * Namespace prefix for tool names
100
+ */
101
+ namespacePrefix?: string;
102
+ };
103
+ /**
104
+ * Convert NeuroLink tool to MCP server tool format
105
+ */
106
+ export declare function neuroLinkToolToMCP(tool: NeuroLinkTool, options?: ToolConverterOptions): MCPServerTool;
107
+ /**
108
+ * Convert MCP server tool to NeuroLink tool format
109
+ */
110
+ export declare function mcpToolToNeuroLink(tool: MCPServerTool, options?: {
111
+ removeNamespacePrefix?: string;
112
+ }): NeuroLinkTool;
113
+ /**
114
+ * Convert MCP protocol tool to MCPServerTool
115
+ * (For tools received from external MCP servers)
116
+ */
117
+ export declare function mcpProtocolToolToServerTool(protocolTool: MCPProtocolTool, executor: (params: unknown, context?: NeuroLinkExecutionContext) => Promise<ToolResult | unknown>, options?: ToolConverterOptions): MCPServerTool;
118
+ /**
119
+ * Convert MCPServerTool to MCP protocol tool format
120
+ * (For exposing tools to external MCP clients)
121
+ */
122
+ export declare function serverToolToMCPProtocol(tool: MCPServerTool): MCPProtocolTool;
123
+ /**
124
+ * Batch convert NeuroLink tools to MCP format
125
+ */
126
+ export declare function batchConvertToMCP(tools: NeuroLinkTool[], options?: ToolConverterOptions): MCPServerTool[];
127
+ /**
128
+ * Batch convert MCP tools to NeuroLink format
129
+ */
130
+ export declare function batchConvertToNeuroLink(tools: MCPServerTool[], options?: {
131
+ removeNamespacePrefix?: string;
132
+ }): NeuroLinkTool[];
133
+ /**
134
+ * Create a tool from a function with automatic schema inference
135
+ */
136
+ export declare function createToolFromFunction<TParams extends Record<string, unknown>>(name: string, description: string, fn: (params: TParams, context?: NeuroLinkExecutionContext) => Promise<unknown>, options?: {
137
+ parameters?: JsonObject;
138
+ annotations?: MCPToolAnnotations;
139
+ metadata?: Record<string, unknown>;
140
+ }): MCPServerTool;
141
+ /**
142
+ * Validate tool name according to MCP specification
143
+ */
144
+ export declare function validateToolName(name: string): {
145
+ valid: boolean;
146
+ errors: string[];
147
+ };
148
+ /**
149
+ * Sanitize tool name for MCP compatibility
150
+ */
151
+ export declare function sanitizeToolName(name: string): string;
152
+ /**
153
+ * Tool compatibility matrix
154
+ */
155
+ export declare const TOOL_COMPATIBILITY: {
156
+ /**
157
+ * Features supported by MCP 2024-11-05 specification
158
+ */
159
+ readonly MCP_2024_11_05: {
160
+ readonly annotations: true;
161
+ readonly inputSchema: true;
162
+ readonly outputSchema: false;
163
+ readonly streamingResults: false;
164
+ readonly batchExecution: false;
165
+ };
166
+ /**
167
+ * Features supported by NeuroLink
168
+ */
169
+ readonly NEUROLINK: {
170
+ readonly annotations: true;
171
+ readonly inputSchema: true;
172
+ readonly outputSchema: true;
173
+ readonly streamingResults: true;
174
+ readonly batchExecution: true;
175
+ readonly categories: true;
176
+ readonly tags: true;
177
+ };
178
+ };
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Tool Converter Utilities
3
+ *
4
+ * Converts between NeuroLink tool format and MCP tool format.
5
+ * Enables seamless interoperability between NeuroLink's internal
6
+ * tool representation and the MCP protocol specification.
7
+ *
8
+ * @module mcp/toolConverter
9
+ * @since 8.39.0
10
+ */
11
+ import { inferAnnotations } from "./toolAnnotations.js";
12
+ import { withTimeout } from "../utils/async/withTimeout.js";
13
+ /**
14
+ * Convert NeuroLink tool to MCP server tool format
15
+ */
16
+ export function neuroLinkToolToMCP(tool, options = {}) {
17
+ const { inferAnnotations: shouldInfer = true, defaultAnnotations = {}, preserveMetadata = true, namespacePrefix, } = options;
18
+ // Apply namespace prefix if provided
19
+ const toolName = namespacePrefix
20
+ ? `${namespacePrefix}_${tool.name}`
21
+ : tool.name;
22
+ // Infer annotations from tool definition
23
+ const inferredAnnotations = shouldInfer
24
+ ? inferAnnotations({ name: tool.name, description: tool.description })
25
+ : {};
26
+ // Build annotations
27
+ const annotations = {
28
+ ...defaultAnnotations,
29
+ ...inferredAnnotations,
30
+ };
31
+ // Add tags if present
32
+ if (tool.tags?.length) {
33
+ annotations.tags = [
34
+ ...new Set([...(annotations.tags ?? []), ...tool.tags]),
35
+ ];
36
+ }
37
+ // Build input schema
38
+ const inputSchema = tool.parameters ?? {
39
+ type: "object",
40
+ properties: {},
41
+ };
42
+ // Build metadata
43
+ const metadata = preserveMetadata
44
+ ? { ...tool.metadata }
45
+ : {};
46
+ if (tool.category) {
47
+ metadata.category = tool.category;
48
+ }
49
+ if (tool.isAsync !== undefined) {
50
+ metadata.isAsync = tool.isAsync;
51
+ }
52
+ return {
53
+ name: toolName,
54
+ description: tool.description,
55
+ inputSchema,
56
+ annotations,
57
+ execute: tool.execute,
58
+ metadata,
59
+ };
60
+ }
61
+ /**
62
+ * Convert MCP server tool to NeuroLink tool format
63
+ */
64
+ export function mcpToolToNeuroLink(tool, options = {}) {
65
+ const { removeNamespacePrefix } = options;
66
+ // Remove namespace prefix if provided
67
+ let toolName = tool.name;
68
+ if (removeNamespacePrefix &&
69
+ tool.name.startsWith(`${removeNamespacePrefix}_`)) {
70
+ toolName = tool.name.slice(removeNamespacePrefix.length + 1);
71
+ }
72
+ return {
73
+ name: toolName,
74
+ description: tool.description,
75
+ parameters: tool.inputSchema,
76
+ execute: tool.execute,
77
+ category: tool.metadata?.category,
78
+ tags: tool.annotations?.tags,
79
+ metadata: tool.metadata,
80
+ };
81
+ }
82
+ /**
83
+ * Convert MCP protocol tool to MCPServerTool
84
+ * (For tools received from external MCP servers)
85
+ */
86
+ export function mcpProtocolToolToServerTool(protocolTool, executor, options = {}) {
87
+ const { inferAnnotations: shouldInfer = true, defaultAnnotations = {} } = options;
88
+ // Convert protocol annotations to our format
89
+ const protocolAnnotations = protocolTool.annotations ?? {};
90
+ // Infer additional annotations
91
+ const inferredAnnotations = shouldInfer
92
+ ? inferAnnotations({
93
+ name: protocolTool.name,
94
+ description: protocolTool.description ?? "",
95
+ })
96
+ : {};
97
+ // Merge annotations with precedence: protocol > inferred > defaults
98
+ const annotations = {
99
+ ...defaultAnnotations,
100
+ ...inferredAnnotations,
101
+ title: protocolAnnotations.title ??
102
+ inferredAnnotations.title ??
103
+ defaultAnnotations.title,
104
+ readOnlyHint: protocolAnnotations.readOnlyHint ??
105
+ inferredAnnotations.readOnlyHint ??
106
+ defaultAnnotations.readOnlyHint,
107
+ destructiveHint: protocolAnnotations.destructiveHint ??
108
+ inferredAnnotations.destructiveHint ??
109
+ defaultAnnotations.destructiveHint,
110
+ idempotentHint: protocolAnnotations.idempotentHint ??
111
+ inferredAnnotations.idempotentHint ??
112
+ defaultAnnotations.idempotentHint,
113
+ openWorldHint: protocolAnnotations.openWorldHint ??
114
+ inferredAnnotations.openWorldHint ??
115
+ defaultAnnotations.openWorldHint,
116
+ };
117
+ return {
118
+ name: protocolTool.name,
119
+ description: protocolTool.description ?? "No description provided",
120
+ inputSchema: protocolTool.inputSchema,
121
+ annotations,
122
+ execute: executor,
123
+ };
124
+ }
125
+ /**
126
+ * Convert MCPServerTool to MCP protocol tool format
127
+ * (For exposing tools to external MCP clients)
128
+ */
129
+ export function serverToolToMCPProtocol(tool) {
130
+ // Build protocol annotations
131
+ const annotations = {};
132
+ if (tool.annotations?.title) {
133
+ annotations.title = tool.annotations.title;
134
+ }
135
+ if (tool.annotations?.readOnlyHint !== undefined) {
136
+ annotations.readOnlyHint = tool.annotations.readOnlyHint;
137
+ }
138
+ if (tool.annotations?.destructiveHint !== undefined) {
139
+ annotations.destructiveHint = tool.annotations.destructiveHint;
140
+ }
141
+ if (tool.annotations?.idempotentHint !== undefined) {
142
+ annotations.idempotentHint = tool.annotations.idempotentHint;
143
+ }
144
+ if (tool.annotations?.openWorldHint !== undefined) {
145
+ annotations.openWorldHint = tool.annotations.openWorldHint;
146
+ }
147
+ // Build input schema
148
+ const inputSchema = tool.inputSchema ?? {
149
+ type: "object",
150
+ properties: {},
151
+ };
152
+ return {
153
+ name: tool.name,
154
+ description: tool.description,
155
+ inputSchema: {
156
+ type: "object",
157
+ properties: (inputSchema.properties ?? {}),
158
+ required: ("required" in inputSchema
159
+ ? inputSchema.required
160
+ : undefined),
161
+ },
162
+ annotations: Object.keys(annotations).length > 0 ? annotations : undefined,
163
+ };
164
+ }
165
+ /**
166
+ * Batch convert NeuroLink tools to MCP format
167
+ */
168
+ export function batchConvertToMCP(tools, options = {}) {
169
+ return tools.map((tool) => neuroLinkToolToMCP(tool, options));
170
+ }
171
+ /**
172
+ * Batch convert MCP tools to NeuroLink format
173
+ */
174
+ export function batchConvertToNeuroLink(tools, options = {}) {
175
+ return tools.map((tool) => mcpToolToNeuroLink(tool, options));
176
+ }
177
+ /**
178
+ * Create a tool from a function with automatic schema inference
179
+ */
180
+ export function createToolFromFunction(name, description, fn, options) {
181
+ const inferredAnnotations = inferAnnotations({ name, description });
182
+ return {
183
+ name,
184
+ description,
185
+ inputSchema: options?.parameters ?? { type: "object", properties: {} },
186
+ annotations: {
187
+ ...inferredAnnotations,
188
+ ...options?.annotations,
189
+ },
190
+ execute: async (params, context) => {
191
+ const toolTimeoutMs = 30_000;
192
+ const result = await withTimeout(fn(params, context), toolTimeoutMs, `Tool '${name}' execution timed out after ${toolTimeoutMs}ms`);
193
+ return result;
194
+ },
195
+ metadata: options?.metadata,
196
+ };
197
+ }
198
+ /**
199
+ * Validate tool name according to MCP specification
200
+ */
201
+ export function validateToolName(name) {
202
+ const errors = [];
203
+ if (!name || typeof name !== "string") {
204
+ errors.push("Tool name is required and must be a string");
205
+ }
206
+ else {
207
+ if (name.length > 64) {
208
+ errors.push("Tool name must be 64 characters or less");
209
+ }
210
+ if (!/^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(name)) {
211
+ errors.push("Tool name must start with a letter or underscore and contain only alphanumeric characters, underscores, and hyphens");
212
+ }
213
+ }
214
+ return { valid: errors.length === 0, errors };
215
+ }
216
+ /**
217
+ * Sanitize tool name for MCP compatibility
218
+ */
219
+ export function sanitizeToolName(name) {
220
+ // Replace invalid characters with underscores
221
+ let sanitized = name.replace(/[^a-zA-Z0-9_-]/g, "_");
222
+ // Ensure starts with letter or underscore
223
+ if (!/^[a-zA-Z_]/.test(sanitized)) {
224
+ sanitized = `_${sanitized}`;
225
+ }
226
+ // Truncate to 64 characters
227
+ if (sanitized.length > 64) {
228
+ sanitized = sanitized.slice(0, 64);
229
+ }
230
+ return sanitized;
231
+ }
232
+ /**
233
+ * Tool compatibility matrix
234
+ */
235
+ export const TOOL_COMPATIBILITY = {
236
+ /**
237
+ * Features supported by MCP 2024-11-05 specification
238
+ */
239
+ MCP_2024_11_05: {
240
+ annotations: true,
241
+ inputSchema: true,
242
+ outputSchema: false,
243
+ streamingResults: false,
244
+ batchExecution: false,
245
+ },
246
+ /**
247
+ * Features supported by NeuroLink
248
+ */
249
+ NEUROLINK: {
250
+ annotations: true,
251
+ inputSchema: true,
252
+ outputSchema: true,
253
+ streamingResults: true,
254
+ batchExecution: true,
255
+ categories: true,
256
+ tags: true,
257
+ },
258
+ };