lynkr 9.9.0 → 9.10.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.
- package/README.md +92 -23
- package/bin/cli.js +13 -7
- package/bin/lynkr-init.js +34 -12
- package/bin/lynkr-reset.js +71 -0
- package/config/model-tiers.json +199 -52
- package/package.json +3 -3
- package/scripts/build-eval-set.js +256 -0
- package/scripts/mine-difficulty-anchors.js +288 -0
- package/scripts/validate-difficulty-classifier.js +144 -0
- package/scripts/validate-intent-anchors.js +186 -0
- package/src/api/providers-handler.js +0 -1
- package/src/api/router.js +292 -160
- package/src/clients/databricks.js +95 -6
- package/src/config/index.js +3 -43
- package/src/context/tool-result-compressor.js +883 -40
- package/src/orchestrator/index.js +117 -1235
- package/src/orchestrator/passthrough-stream.js +382 -0
- package/src/orchestrator/sse-transformer.js +408 -0
- package/src/routing/affinity-store.js +17 -3
- package/src/routing/classifier-setup.js +207 -0
- package/src/routing/complexity-analyzer.js +40 -4
- package/src/routing/difficulty-classifier.js +261 -0
- package/src/routing/index.js +70 -7
- package/src/routing/intent-score.js +132 -12
- package/src/routing/session-affinity.js +8 -1
- package/src/routing/side-channel-detector.js +103 -0
- package/src/server.js +20 -46
- package/src/agents/context-manager.js +0 -236
- package/src/agents/decomposition/dispatcher.js +0 -185
- package/src/agents/decomposition/gate.js +0 -136
- package/src/agents/decomposition/index.js +0 -183
- package/src/agents/decomposition/model-call.js +0 -75
- package/src/agents/decomposition/planner.js +0 -223
- package/src/agents/decomposition/synthesizer.js +0 -89
- package/src/agents/decomposition/telemetry.js +0 -55
- package/src/agents/definitions/loader.js +0 -653
- package/src/agents/executor.js +0 -457
- package/src/agents/index.js +0 -165
- package/src/agents/parallel-coordinator.js +0 -68
- package/src/agents/reflector.js +0 -331
- package/src/agents/skillbook.js +0 -331
- package/src/agents/store.js +0 -259
- package/src/edits/index.js +0 -171
- package/src/indexer/babel-parser.js +0 -213
- package/src/indexer/index.js +0 -1629
- package/src/indexer/navigation/index.js +0 -32
- package/src/indexer/navigation/providers/treeSitter.js +0 -36
- package/src/indexer/parser.js +0 -443
- package/src/tasks/store.js +0 -349
- package/src/tests/coverage.js +0 -173
- package/src/tests/index.js +0 -171
- package/src/tests/store.js +0 -213
- package/src/tools/agent-task.js +0 -145
- package/src/tools/code-mode.js +0 -304
- package/src/tools/decompose.js +0 -91
- package/src/tools/edits.js +0 -94
- package/src/tools/execution.js +0 -171
- package/src/tools/git.js +0 -1346
- package/src/tools/index.js +0 -306
- package/src/tools/indexer.js +0 -360
- package/src/tools/lazy-loader.js +0 -366
- package/src/tools/mcp-remote.js +0 -88
- package/src/tools/mcp.js +0 -116
- package/src/tools/process.js +0 -167
- package/src/tools/smart-selection.js +0 -180
- package/src/tools/stubs.js +0 -55
- package/src/tools/tasks.js +0 -260
- package/src/tools/tests.js +0 -132
- package/src/tools/tinyfish.js +0 -358
- package/src/tools/truncate.js +0 -106
- package/src/tools/web-client.js +0 -71
- package/src/tools/web.js +0 -415
- package/src/tools/workspace.js +0 -204
package/src/tools/lazy-loader.js
DELETED
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Lazy Tool Loader
|
|
3
|
-
*
|
|
4
|
-
* Loads tool categories on-demand based on prompt content analysis.
|
|
5
|
-
* Reduces startup time and memory by only loading tools when needed.
|
|
6
|
-
*
|
|
7
|
-
* @module tools/lazy-loader
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const logger = require('../logger');
|
|
11
|
-
|
|
12
|
-
// Track which tool categories have been loaded
|
|
13
|
-
const loadedCategories = new Set();
|
|
14
|
-
|
|
15
|
-
// Core tools that are always loaded at startup
|
|
16
|
-
const CORE_CATEGORIES = ['stubs', 'workspace', 'execution'];
|
|
17
|
-
|
|
18
|
-
// Tool categories with their registration functions and keyword triggers
|
|
19
|
-
const TOOL_CATEGORIES = {
|
|
20
|
-
stubs: {
|
|
21
|
-
keywords: [], // Always loaded
|
|
22
|
-
loader: () => require('./stubs').registerStubTools,
|
|
23
|
-
priority: 0,
|
|
24
|
-
},
|
|
25
|
-
workspace: {
|
|
26
|
-
keywords: ['file', 'read', 'write', 'edit', 'create', 'delete', 'list', 'directory', 'folder', 'path'],
|
|
27
|
-
loader: () => require('./workspace').registerWorkspaceTools,
|
|
28
|
-
priority: 0,
|
|
29
|
-
},
|
|
30
|
-
execution: {
|
|
31
|
-
keywords: ['run', 'execute', 'shell', 'bash', 'command', 'terminal', 'npm', 'node', 'python', 'script'],
|
|
32
|
-
loader: () => require('./execution').registerExecutionTools,
|
|
33
|
-
priority: 0,
|
|
34
|
-
},
|
|
35
|
-
web: {
|
|
36
|
-
keywords: ['web', 'search', 'fetch', 'url', 'http', 'https', 'api', 'request', 'internet', 'online', 'browse', 'website'],
|
|
37
|
-
loader: () => require('./web').registerWebTools,
|
|
38
|
-
priority: 1,
|
|
39
|
-
},
|
|
40
|
-
indexer: {
|
|
41
|
-
keywords: ['index', 'search', 'find', 'symbol', 'reference', 'grep', 'scan', 'codebase'],
|
|
42
|
-
loader: () => require('./indexer').registerIndexerTools,
|
|
43
|
-
priority: 1,
|
|
44
|
-
},
|
|
45
|
-
edits: {
|
|
46
|
-
keywords: ['edit', 'patch', 'modify', 'change', 'update', 'replace', 'refactor'],
|
|
47
|
-
loader: () => require('./edits').registerEditTools,
|
|
48
|
-
priority: 1,
|
|
49
|
-
},
|
|
50
|
-
git: {
|
|
51
|
-
keywords: ['git', 'commit', 'push', 'pull', 'branch', 'merge', 'rebase', 'stash', 'checkout', 'clone', 'diff', 'status', 'log', 'remote', 'fetch', 'pr', 'pull request'],
|
|
52
|
-
loader: () => require('./git').registerGitTools,
|
|
53
|
-
priority: 2,
|
|
54
|
-
},
|
|
55
|
-
tasks: {
|
|
56
|
-
keywords: ['task', 'todo', 'subtask', 'agent', 'spawn', 'background'],
|
|
57
|
-
loader: () => require('./tasks').registerTaskTools,
|
|
58
|
-
priority: 2,
|
|
59
|
-
},
|
|
60
|
-
tinyfish: {
|
|
61
|
-
keywords: ['tinyfish', 'web_agent', 'automate', 'scrape', 'extract', 'crawl', 'browser'],
|
|
62
|
-
loader: () => require('./tinyfish').registerTinyFishTools,
|
|
63
|
-
priority: 2,
|
|
64
|
-
},
|
|
65
|
-
tests: {
|
|
66
|
-
keywords: ['test', 'jest', 'mocha', 'pytest', 'unittest', 'spec', 'coverage', 'assert'],
|
|
67
|
-
loader: () => require('./tests').registerTestTools,
|
|
68
|
-
priority: 2,
|
|
69
|
-
},
|
|
70
|
-
mcp: {
|
|
71
|
-
keywords: ['mcp', 'server', 'sandbox', 'container', 'docker'],
|
|
72
|
-
loader: () => require('./mcp').registerMcpTools,
|
|
73
|
-
priority: 3,
|
|
74
|
-
},
|
|
75
|
-
agentTask: {
|
|
76
|
-
keywords: ['agent', 'subagent', 'spawn', 'delegate', 'parallel'],
|
|
77
|
-
loader: () => require('./agent-task').registerAgentTaskTool,
|
|
78
|
-
priority: 2,
|
|
79
|
-
},
|
|
80
|
-
decompose: {
|
|
81
|
-
keywords: ['decompose', 'subtask', 'break down', 'break into', 'split task', 'plan and execute'],
|
|
82
|
-
loader: () => require('./decompose').registerDecomposeTool,
|
|
83
|
-
priority: 2,
|
|
84
|
-
},
|
|
85
|
-
'code-mode': {
|
|
86
|
-
keywords: ['mcp', 'execute', 'server', 'tool', 'code mode'],
|
|
87
|
-
loader: () => require('./code-mode').registerCodeModeTools,
|
|
88
|
-
priority: 3,
|
|
89
|
-
},
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Load a specific tool category
|
|
94
|
-
* @param {string} category - Category name
|
|
95
|
-
* @returns {boolean} - True if loaded, false if already loaded or failed
|
|
96
|
-
*/
|
|
97
|
-
function loadCategory(category) {
|
|
98
|
-
if (loadedCategories.has(category)) {
|
|
99
|
-
return false;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const config = TOOL_CATEGORIES[category];
|
|
103
|
-
if (!config) {
|
|
104
|
-
logger.warn({ category }, '[LazyLoader] Unknown tool category');
|
|
105
|
-
return false;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
try {
|
|
109
|
-
const registerFn = config.loader();
|
|
110
|
-
if (typeof registerFn === 'function') {
|
|
111
|
-
registerFn();
|
|
112
|
-
}
|
|
113
|
-
loadedCategories.add(category);
|
|
114
|
-
logger.debug({ category }, '[LazyLoader] Tool category loaded');
|
|
115
|
-
return true;
|
|
116
|
-
} catch (err) {
|
|
117
|
-
logger.error({ category, error: err.message }, '[LazyLoader] Failed to load tool category');
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Load core tools (called at startup)
|
|
124
|
-
*/
|
|
125
|
-
function loadCoreTools() {
|
|
126
|
-
const startTime = Date.now();
|
|
127
|
-
|
|
128
|
-
for (const category of CORE_CATEGORIES) {
|
|
129
|
-
loadCategory(category);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
logger.info({
|
|
133
|
-
loadedCategories: Array.from(loadedCategories),
|
|
134
|
-
duration: Date.now() - startTime,
|
|
135
|
-
}, '[LazyLoader] Core tools loaded');
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Load all tools (for backwards compatibility or when lazy loading is disabled)
|
|
140
|
-
*/
|
|
141
|
-
function loadAllTools() {
|
|
142
|
-
const startTime = Date.now();
|
|
143
|
-
|
|
144
|
-
for (const category of Object.keys(TOOL_CATEGORIES)) {
|
|
145
|
-
loadCategory(category);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
logger.info({
|
|
149
|
-
loadedCategories: Array.from(loadedCategories),
|
|
150
|
-
duration: Date.now() - startTime,
|
|
151
|
-
}, '[LazyLoader] All tools loaded');
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Analyze prompt content and determine which tool categories are needed
|
|
156
|
-
* @param {string|Array} content - Prompt content (string or messages array)
|
|
157
|
-
* @returns {string[]} - List of category names that should be loaded
|
|
158
|
-
*/
|
|
159
|
-
function analyzePromptForTools(content) {
|
|
160
|
-
// Extract text from various input formats
|
|
161
|
-
let text = '';
|
|
162
|
-
|
|
163
|
-
if (typeof content === 'string') {
|
|
164
|
-
text = content.toLowerCase();
|
|
165
|
-
} else if (Array.isArray(content)) {
|
|
166
|
-
// Extract from messages array
|
|
167
|
-
text = content
|
|
168
|
-
.map(msg => {
|
|
169
|
-
if (typeof msg.content === 'string') {
|
|
170
|
-
return msg.content;
|
|
171
|
-
}
|
|
172
|
-
if (Array.isArray(msg.content)) {
|
|
173
|
-
return msg.content
|
|
174
|
-
.filter(part => part.type === 'text' || part.type === 'input_text')
|
|
175
|
-
.map(part => part.text || part.input_text || '')
|
|
176
|
-
.join(' ');
|
|
177
|
-
}
|
|
178
|
-
return '';
|
|
179
|
-
})
|
|
180
|
-
.join(' ')
|
|
181
|
-
.toLowerCase();
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (!text) return [];
|
|
185
|
-
|
|
186
|
-
const neededCategories = new Set();
|
|
187
|
-
|
|
188
|
-
// Check each category's keywords
|
|
189
|
-
for (const [category, config] of Object.entries(TOOL_CATEGORIES)) {
|
|
190
|
-
// Skip already loaded categories
|
|
191
|
-
if (loadedCategories.has(category)) continue;
|
|
192
|
-
|
|
193
|
-
// Check if any keyword matches
|
|
194
|
-
for (const keyword of config.keywords) {
|
|
195
|
-
if (text.includes(keyword.toLowerCase())) {
|
|
196
|
-
neededCategories.add(category);
|
|
197
|
-
break;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Sort by priority (lower = load first)
|
|
203
|
-
return Array.from(neededCategories).sort((a, b) => {
|
|
204
|
-
return (TOOL_CATEGORIES[a]?.priority ?? 99) - (TOOL_CATEGORIES[b]?.priority ?? 99);
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Ensure tools needed for a prompt are loaded
|
|
210
|
-
* @param {string|Array} content - Prompt content
|
|
211
|
-
* @returns {{ loaded: string[], alreadyLoaded: string[] }}
|
|
212
|
-
*/
|
|
213
|
-
function ensureToolsForPrompt(content) {
|
|
214
|
-
const neededCategories = analyzePromptForTools(content);
|
|
215
|
-
const loaded = [];
|
|
216
|
-
const alreadyLoaded = [];
|
|
217
|
-
|
|
218
|
-
for (const category of neededCategories) {
|
|
219
|
-
if (loadedCategories.has(category)) {
|
|
220
|
-
alreadyLoaded.push(category);
|
|
221
|
-
} else if (loadCategory(category)) {
|
|
222
|
-
loaded.push(category);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (loaded.length > 0) {
|
|
227
|
-
logger.info({ loaded, triggered: content?.substring?.(0, 100) }, '[LazyLoader] Loaded tools for prompt');
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return { loaded, alreadyLoaded };
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Load a tool category by tool name (called when a tool is requested but not found)
|
|
235
|
-
* @param {string} toolName - Name of the tool being requested
|
|
236
|
-
* @returns {boolean} - True if a category was loaded
|
|
237
|
-
*/
|
|
238
|
-
function loadCategoryForTool(toolName) {
|
|
239
|
-
if (!toolName) return false;
|
|
240
|
-
|
|
241
|
-
const lowerName = toolName.toLowerCase();
|
|
242
|
-
|
|
243
|
-
// Map tool names to categories
|
|
244
|
-
const toolToCategory = {
|
|
245
|
-
// Git tools
|
|
246
|
-
'workspace_git_status': 'git',
|
|
247
|
-
'workspace_git_stage': 'git',
|
|
248
|
-
'workspace_git_unstage': 'git',
|
|
249
|
-
'workspace_git_commit': 'git',
|
|
250
|
-
'workspace_git_push': 'git',
|
|
251
|
-
'workspace_git_pull': 'git',
|
|
252
|
-
'workspace_git_branches': 'git',
|
|
253
|
-
'workspace_git_checkout': 'git',
|
|
254
|
-
'workspace_git_stash': 'git',
|
|
255
|
-
'workspace_git_merge': 'git',
|
|
256
|
-
'workspace_git_rebase': 'git',
|
|
257
|
-
'workspace_git_conflicts': 'git',
|
|
258
|
-
'workspace_diff': 'git',
|
|
259
|
-
'workspace_diff_review': 'git',
|
|
260
|
-
'workspace_diff_summary': 'git',
|
|
261
|
-
'workspace_diff_by_commit': 'git',
|
|
262
|
-
'workspace_release_notes': 'git',
|
|
263
|
-
'workspace_changelog_generate': 'git',
|
|
264
|
-
'workspace_pr_template_generate': 'git',
|
|
265
|
-
'workspace_git_patch_plan': 'git',
|
|
266
|
-
|
|
267
|
-
// Web tools
|
|
268
|
-
'web_search': 'web',
|
|
269
|
-
'web_fetch': 'web',
|
|
270
|
-
|
|
271
|
-
// Indexer tools
|
|
272
|
-
'workspace_search': 'indexer',
|
|
273
|
-
'workspace_symbol_search': 'indexer',
|
|
274
|
-
'workspace_symbol_references': 'indexer',
|
|
275
|
-
'workspace_index_rebuild': 'indexer',
|
|
276
|
-
|
|
277
|
-
// Edit tools
|
|
278
|
-
'edit_patch': 'edits',
|
|
279
|
-
|
|
280
|
-
// Task tools
|
|
281
|
-
'task_create': 'tasks',
|
|
282
|
-
'task_update': 'tasks',
|
|
283
|
-
'task_list': 'tasks',
|
|
284
|
-
|
|
285
|
-
// Test tools
|
|
286
|
-
'workspace_test_run': 'tests',
|
|
287
|
-
'workspace_test_summary': 'tests',
|
|
288
|
-
'workspace_test_history': 'tests',
|
|
289
|
-
|
|
290
|
-
// MCP tools
|
|
291
|
-
'workspace_sandbox_sessions': 'mcp',
|
|
292
|
-
'workspace_mcp_servers': 'mcp',
|
|
293
|
-
|
|
294
|
-
// Code Mode meta-tools
|
|
295
|
-
'mcp_list_tools': 'code-mode',
|
|
296
|
-
'mcp_tool_info': 'code-mode',
|
|
297
|
-
'mcp_tool_docs': 'code-mode',
|
|
298
|
-
'mcp_execute': 'code-mode',
|
|
299
|
-
|
|
300
|
-
// Agent task
|
|
301
|
-
// TinyFish (web agent)
|
|
302
|
-
'web_agent': 'tinyfish',
|
|
303
|
-
'agent_task': 'agentTask',
|
|
304
|
-
|
|
305
|
-
// Task decomposition
|
|
306
|
-
'decomposetask': 'decompose',
|
|
307
|
-
};
|
|
308
|
-
|
|
309
|
-
// Direct mapping
|
|
310
|
-
const category = toolToCategory[lowerName];
|
|
311
|
-
if (category && !loadedCategories.has(category)) {
|
|
312
|
-
return loadCategory(category);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Fuzzy matching by prefix
|
|
316
|
-
for (const [toolPattern, cat] of Object.entries(toolToCategory)) {
|
|
317
|
-
if (lowerName.startsWith(toolPattern.split('_')[0]) && !loadedCategories.has(cat)) {
|
|
318
|
-
return loadCategory(cat);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
return false;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Get statistics about loaded tools
|
|
327
|
-
*/
|
|
328
|
-
function getLoaderStats() {
|
|
329
|
-
const allCategories = Object.keys(TOOL_CATEGORIES);
|
|
330
|
-
return {
|
|
331
|
-
loaded: Array.from(loadedCategories),
|
|
332
|
-
notLoaded: allCategories.filter(c => !loadedCategories.has(c)),
|
|
333
|
-
totalCategories: allCategories.length,
|
|
334
|
-
loadedCount: loadedCategories.size,
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* Check if a category is loaded
|
|
340
|
-
* @param {string} category
|
|
341
|
-
* @returns {boolean}
|
|
342
|
-
*/
|
|
343
|
-
function isCategoryLoaded(category) {
|
|
344
|
-
return loadedCategories.has(category);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Reset loader state (for testing)
|
|
349
|
-
*/
|
|
350
|
-
function resetLoader() {
|
|
351
|
-
loadedCategories.clear();
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
module.exports = {
|
|
355
|
-
loadCoreTools,
|
|
356
|
-
loadAllTools,
|
|
357
|
-
loadCategory,
|
|
358
|
-
loadCategoryForTool,
|
|
359
|
-
analyzePromptForTools,
|
|
360
|
-
ensureToolsForPrompt,
|
|
361
|
-
getLoaderStats,
|
|
362
|
-
isCategoryLoaded,
|
|
363
|
-
resetLoader,
|
|
364
|
-
TOOL_CATEGORIES,
|
|
365
|
-
CORE_CATEGORIES,
|
|
366
|
-
};
|
package/src/tools/mcp-remote.js
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
const { registerTool } = require(".");
|
|
2
|
-
const { listServers, ensureClient } = require("../mcp");
|
|
3
|
-
const config = require("../config");
|
|
4
|
-
const logger = require("../logger");
|
|
5
|
-
|
|
6
|
-
const REMOTE_TOOL_PREFIX = "mcp";
|
|
7
|
-
|
|
8
|
-
function sanitiseName(value) {
|
|
9
|
-
return String(value)
|
|
10
|
-
.replace(/[^A-Za-z0-9_]/g, "_")
|
|
11
|
-
.replace(/_+/g, "_")
|
|
12
|
-
.replace(/^_+|_+$/g, "");
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async function registerRemoteTools() {
|
|
16
|
-
// Code Mode: register 4 meta-tools instead of individual remote tools
|
|
17
|
-
if (config.mcp?.codeMode?.enabled) {
|
|
18
|
-
const { registerCodeModeTools } = require("./code-mode");
|
|
19
|
-
registerCodeModeTools();
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
const servers = listServers();
|
|
23
|
-
await Promise.all(
|
|
24
|
-
servers.map(async (server) => {
|
|
25
|
-
try {
|
|
26
|
-
const client = await ensureClient(server.id);
|
|
27
|
-
if (!client) return;
|
|
28
|
-
|
|
29
|
-
let result;
|
|
30
|
-
try {
|
|
31
|
-
result = await client.request("tools/list", {});
|
|
32
|
-
} catch (err) {
|
|
33
|
-
logger.warn(
|
|
34
|
-
{ err, server: server.id },
|
|
35
|
-
"MCP server did not respond to tools/list",
|
|
36
|
-
);
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const tools = Array.isArray(result?.tools) ? result.tools : [];
|
|
41
|
-
tools.forEach((tool) => {
|
|
42
|
-
if (!tool || typeof tool !== "object") return;
|
|
43
|
-
const remoteName = tool.name ?? tool.method;
|
|
44
|
-
if (!remoteName) return;
|
|
45
|
-
const localName = `${REMOTE_TOOL_PREFIX}_${sanitiseName(server.id)}_${sanitiseName(remoteName)}`;
|
|
46
|
-
const descriptionParts = [];
|
|
47
|
-
if (server.name) descriptionParts.push(`[${server.name}]`);
|
|
48
|
-
if (tool.description) descriptionParts.push(tool.description);
|
|
49
|
-
const description = descriptionParts.join(" ");
|
|
50
|
-
const method = tool.method ?? tool.name;
|
|
51
|
-
|
|
52
|
-
registerTool(
|
|
53
|
-
localName,
|
|
54
|
-
async ({ args = {} }) => {
|
|
55
|
-
const payload =
|
|
56
|
-
typeof args === "object" && args !== null ? args : {};
|
|
57
|
-
const response = await client.request(method, payload);
|
|
58
|
-
return {
|
|
59
|
-
ok: true,
|
|
60
|
-
status: 200,
|
|
61
|
-
content: JSON.stringify(
|
|
62
|
-
{
|
|
63
|
-
server: server.id,
|
|
64
|
-
tool: remoteName,
|
|
65
|
-
result: response,
|
|
66
|
-
},
|
|
67
|
-
null,
|
|
68
|
-
2,
|
|
69
|
-
),
|
|
70
|
-
metadata: {
|
|
71
|
-
server: server.id,
|
|
72
|
-
tool: remoteName,
|
|
73
|
-
},
|
|
74
|
-
};
|
|
75
|
-
},
|
|
76
|
-
{ category: "mcp", description },
|
|
77
|
-
);
|
|
78
|
-
});
|
|
79
|
-
} catch (err) {
|
|
80
|
-
logger.warn({ err, server: server.id }, "Failed to register MCP remote tools");
|
|
81
|
-
}
|
|
82
|
-
}),
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
module.exports = {
|
|
87
|
-
registerRemoteTools,
|
|
88
|
-
};
|
package/src/tools/mcp.js
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
const { registerTool } = require(".");
|
|
2
|
-
const { listServers, loadConfiguredServers, ensureClient } = require("../mcp");
|
|
3
|
-
const { listSessions, releaseSession, isSandboxEnabled } = require("../mcp/sandbox");
|
|
4
|
-
const { registerRemoteTools } = require("./mcp-remote");
|
|
5
|
-
const logger = require("../logger");
|
|
6
|
-
|
|
7
|
-
function formatJson(payload) {
|
|
8
|
-
return JSON.stringify(payload, null, 2);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function registerMcpTools() {
|
|
12
|
-
registerTool(
|
|
13
|
-
"workspace_mcp_servers",
|
|
14
|
-
async () => {
|
|
15
|
-
loadConfiguredServers();
|
|
16
|
-
registerRemoteTools().catch((err) => {
|
|
17
|
-
logger.warn({ err }, "Failed to refresh MCP remote tools");
|
|
18
|
-
});
|
|
19
|
-
const servers = listServers().map((server) => ({
|
|
20
|
-
id: server.id,
|
|
21
|
-
name: server.name,
|
|
22
|
-
description: server.description,
|
|
23
|
-
command: server.command,
|
|
24
|
-
args: server.args,
|
|
25
|
-
transport: server.transport,
|
|
26
|
-
metadata: server.metadata,
|
|
27
|
-
}));
|
|
28
|
-
return {
|
|
29
|
-
ok: true,
|
|
30
|
-
status: 200,
|
|
31
|
-
content: formatJson({
|
|
32
|
-
sandboxEnabled: isSandboxEnabled(),
|
|
33
|
-
servers,
|
|
34
|
-
}),
|
|
35
|
-
};
|
|
36
|
-
},
|
|
37
|
-
{ category: "mcp" },
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
registerTool("workspace_sandbox_sessions", async ({ args = {} }) => {
|
|
41
|
-
const sessions = listSessions();
|
|
42
|
-
if (args.release === true) {
|
|
43
|
-
const target = typeof args.session_id === "string" ? args.session_id : null;
|
|
44
|
-
if (target) {
|
|
45
|
-
releaseSession(target);
|
|
46
|
-
} else {
|
|
47
|
-
sessions.forEach((session) => releaseSession(session.id));
|
|
48
|
-
}
|
|
49
|
-
return {
|
|
50
|
-
ok: true,
|
|
51
|
-
status: 200,
|
|
52
|
-
content: formatJson({
|
|
53
|
-
released: true,
|
|
54
|
-
target: target ?? "all",
|
|
55
|
-
}),
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
return {
|
|
59
|
-
ok: true,
|
|
60
|
-
status: 200,
|
|
61
|
-
content: formatJson({
|
|
62
|
-
sandboxEnabled: isSandboxEnabled(),
|
|
63
|
-
sessions,
|
|
64
|
-
}),
|
|
65
|
-
};
|
|
66
|
-
}, { category: "mcp" });
|
|
67
|
-
|
|
68
|
-
registerTool(
|
|
69
|
-
"workspace_mcp_call",
|
|
70
|
-
async ({ args = {} }) => {
|
|
71
|
-
const serverId = args.server ?? args.server_id ?? args.serverId;
|
|
72
|
-
const method = args.method ?? args.call;
|
|
73
|
-
const params =
|
|
74
|
-
typeof args.params === "object" && args.params !== null
|
|
75
|
-
? args.params
|
|
76
|
-
: {};
|
|
77
|
-
if (typeof serverId !== "string" || !serverId.trim()) {
|
|
78
|
-
throw new Error("workspace_mcp_call requires a server id.");
|
|
79
|
-
}
|
|
80
|
-
if (typeof method !== "string" || !method.trim()) {
|
|
81
|
-
throw new Error("workspace_mcp_call requires a method name.");
|
|
82
|
-
}
|
|
83
|
-
const client = await ensureClient(serverId.trim());
|
|
84
|
-
if (!client) {
|
|
85
|
-
throw new Error(`MCP server "${serverId}" is not available.`);
|
|
86
|
-
}
|
|
87
|
-
const result = await client.request(method.trim(), params);
|
|
88
|
-
return {
|
|
89
|
-
ok: true,
|
|
90
|
-
status: 200,
|
|
91
|
-
content: formatJson({
|
|
92
|
-
server: serverId,
|
|
93
|
-
method: method.trim(),
|
|
94
|
-
result,
|
|
95
|
-
}),
|
|
96
|
-
metadata: {
|
|
97
|
-
server: serverId,
|
|
98
|
-
method: method.trim(),
|
|
99
|
-
},
|
|
100
|
-
};
|
|
101
|
-
},
|
|
102
|
-
{ category: "mcp" },
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
registerRemoteTools()
|
|
106
|
-
.then(() => {
|
|
107
|
-
logger.info("Registered MCP remote tools");
|
|
108
|
-
})
|
|
109
|
-
.catch((err) => {
|
|
110
|
-
logger.warn({ err }, "Failed to register MCP remote tools");
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
module.exports = {
|
|
115
|
-
registerMcpTools,
|
|
116
|
-
};
|