@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.
- package/CHANGELOG.md +12 -0
- package/README.md +78 -25
- package/dist/adapters/providerImageAdapter.js +9 -1
- package/dist/cli/commands/config.d.ts +4 -4
- package/dist/cli/commands/mcp.d.ts +87 -0
- package/dist/cli/commands/mcp.js +1524 -0
- package/dist/cli/loop/optionsSchema.js +4 -0
- package/dist/constants/contextWindows.js +18 -7
- package/dist/constants/enums.d.ts +36 -6
- package/dist/constants/enums.js +38 -5
- package/dist/constants/tokens.d.ts +4 -0
- package/dist/constants/tokens.js +4 -0
- package/dist/core/modules/ToolsManager.js +29 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +27 -1
- package/dist/lib/adapters/providerImageAdapter.js +9 -1
- package/dist/lib/constants/contextWindows.js +18 -7
- package/dist/lib/constants/enums.d.ts +36 -6
- package/dist/lib/constants/enums.js +38 -5
- package/dist/lib/constants/tokens.d.ts +4 -0
- package/dist/lib/constants/tokens.js +4 -0
- package/dist/lib/core/modules/ToolsManager.js +29 -2
- package/dist/lib/index.d.ts +2 -1
- package/dist/lib/index.js +27 -1
- package/dist/lib/mcp/agentExposure.d.ts +228 -0
- package/dist/lib/mcp/agentExposure.js +357 -0
- package/dist/lib/mcp/batching/index.d.ts +11 -0
- package/dist/lib/mcp/batching/index.js +11 -0
- package/dist/lib/mcp/batching/requestBatcher.d.ts +202 -0
- package/dist/lib/mcp/batching/requestBatcher.js +442 -0
- package/dist/lib/mcp/caching/index.d.ts +11 -0
- package/dist/lib/mcp/caching/index.js +11 -0
- package/dist/lib/mcp/caching/toolCache.d.ts +221 -0
- package/dist/lib/mcp/caching/toolCache.js +434 -0
- package/dist/lib/mcp/elicitation/elicitationManager.d.ts +169 -0
- package/dist/lib/mcp/elicitation/elicitationManager.js +377 -0
- package/dist/lib/mcp/elicitation/index.d.ts +11 -0
- package/dist/lib/mcp/elicitation/index.js +12 -0
- package/dist/lib/mcp/elicitation/types.d.ts +278 -0
- package/dist/lib/mcp/elicitation/types.js +11 -0
- package/dist/lib/mcp/elicitationProtocol.d.ts +228 -0
- package/dist/lib/mcp/elicitationProtocol.js +376 -0
- package/dist/lib/mcp/enhancedToolDiscovery.d.ts +205 -0
- package/dist/lib/mcp/enhancedToolDiscovery.js +482 -0
- package/dist/lib/mcp/index.d.ts +38 -1
- package/dist/lib/mcp/index.js +36 -3
- package/dist/lib/mcp/mcpRegistryClient.d.ts +332 -0
- package/dist/lib/mcp/mcpRegistryClient.js +489 -0
- package/dist/lib/mcp/mcpServerBase.d.ts +227 -0
- package/dist/lib/mcp/mcpServerBase.js +374 -0
- package/dist/lib/mcp/multiServerManager.d.ts +310 -0
- package/dist/lib/mcp/multiServerManager.js +580 -0
- package/dist/lib/mcp/routing/index.d.ts +11 -0
- package/dist/lib/mcp/routing/index.js +11 -0
- package/dist/lib/mcp/routing/toolRouter.d.ts +219 -0
- package/dist/lib/mcp/routing/toolRouter.js +417 -0
- package/dist/lib/mcp/serverCapabilities.d.ts +341 -0
- package/dist/lib/mcp/serverCapabilities.js +503 -0
- package/dist/lib/mcp/toolAnnotations.d.ts +154 -0
- package/dist/lib/mcp/toolAnnotations.js +240 -0
- package/dist/lib/mcp/toolConverter.d.ts +178 -0
- package/dist/lib/mcp/toolConverter.js +259 -0
- package/dist/lib/mcp/toolIntegration.d.ts +136 -0
- package/dist/lib/mcp/toolIntegration.js +335 -0
- package/dist/lib/neurolink.d.ts +275 -2
- package/dist/lib/neurolink.js +596 -56
- package/dist/lib/providers/amazonBedrock.js +1 -1
- package/dist/lib/providers/anthropic.js +1 -1
- package/dist/lib/providers/litellm.d.ts +10 -0
- package/dist/lib/providers/litellm.js +104 -2
- package/dist/lib/types/configTypes.d.ts +56 -0
- package/dist/lib/types/generateTypes.d.ts +4 -0
- package/dist/lib/types/index.d.ts +1 -1
- package/dist/lib/types/modelTypes.d.ts +6 -6
- package/dist/lib/types/streamTypes.d.ts +2 -0
- package/dist/lib/types/tools.d.ts +2 -0
- package/dist/lib/utils/pricing.js +177 -17
- package/dist/lib/utils/schemaConversion.d.ts +6 -1
- package/dist/lib/utils/schemaConversion.js +50 -28
- package/dist/lib/workflow/config.d.ts +16 -16
- package/dist/mcp/agentExposure.d.ts +228 -0
- package/dist/mcp/agentExposure.js +356 -0
- package/dist/mcp/batching/index.d.ts +11 -0
- package/dist/mcp/batching/index.js +10 -0
- package/dist/mcp/batching/requestBatcher.d.ts +202 -0
- package/dist/mcp/batching/requestBatcher.js +441 -0
- package/dist/mcp/caching/index.d.ts +11 -0
- package/dist/mcp/caching/index.js +10 -0
- package/dist/mcp/caching/toolCache.d.ts +221 -0
- package/dist/mcp/caching/toolCache.js +433 -0
- package/dist/mcp/elicitation/elicitationManager.d.ts +169 -0
- package/dist/mcp/elicitation/elicitationManager.js +376 -0
- package/dist/mcp/elicitation/index.d.ts +11 -0
- package/dist/mcp/elicitation/index.js +11 -0
- package/dist/mcp/elicitation/types.d.ts +278 -0
- package/dist/mcp/elicitation/types.js +10 -0
- package/dist/mcp/elicitationProtocol.d.ts +228 -0
- package/dist/mcp/elicitationProtocol.js +375 -0
- package/dist/mcp/enhancedToolDiscovery.d.ts +205 -0
- package/dist/mcp/enhancedToolDiscovery.js +481 -0
- package/dist/mcp/index.d.ts +38 -1
- package/dist/mcp/index.js +36 -3
- package/dist/mcp/mcpRegistryClient.d.ts +332 -0
- package/dist/mcp/mcpRegistryClient.js +488 -0
- package/dist/mcp/mcpServerBase.d.ts +227 -0
- package/dist/mcp/mcpServerBase.js +373 -0
- package/dist/mcp/multiServerManager.d.ts +310 -0
- package/dist/mcp/multiServerManager.js +579 -0
- package/dist/mcp/routing/index.d.ts +11 -0
- package/dist/mcp/routing/index.js +10 -0
- package/dist/mcp/routing/toolRouter.d.ts +219 -0
- package/dist/mcp/routing/toolRouter.js +416 -0
- package/dist/mcp/serverCapabilities.d.ts +341 -0
- package/dist/mcp/serverCapabilities.js +502 -0
- package/dist/mcp/toolAnnotations.d.ts +154 -0
- package/dist/mcp/toolAnnotations.js +239 -0
- package/dist/mcp/toolConverter.d.ts +178 -0
- package/dist/mcp/toolConverter.js +258 -0
- package/dist/mcp/toolIntegration.d.ts +136 -0
- package/dist/mcp/toolIntegration.js +334 -0
- package/dist/neurolink.d.ts +275 -2
- package/dist/neurolink.js +596 -56
- package/dist/providers/amazonBedrock.js +1 -1
- package/dist/providers/anthropic.js +1 -1
- package/dist/providers/litellm.d.ts +10 -0
- package/dist/providers/litellm.js +104 -2
- package/dist/types/configTypes.d.ts +56 -0
- package/dist/types/generateTypes.d.ts +4 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/streamTypes.d.ts +2 -0
- package/dist/types/tools.d.ts +2 -0
- package/dist/utils/pricing.js +177 -17
- package/dist/utils/schemaConversion.d.ts +6 -1
- package/dist/utils/schemaConversion.js +50 -28
- 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
|
+
};
|