openlore 2.0.6 → 2.0.8
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 +140 -23
- package/dist/cli/commands/decisions.d.ts.map +1 -1
- package/dist/cli/commands/decisions.js +187 -174
- package/dist/cli/commands/decisions.js.map +1 -1
- package/dist/cli/commands/mcp.d.ts +694 -0
- package/dist/cli/commands/mcp.d.ts.map +1 -1
- package/dist/cli/commands/mcp.js +417 -203
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/constants.d.ts +6 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +14 -0
- package/dist/constants.js.map +1 -1
- package/dist/core/analyzer/artifact-generator.d.ts.map +1 -1
- package/dist/core/analyzer/artifact-generator.js +59 -4
- package/dist/core/analyzer/artifact-generator.js.map +1 -1
- package/dist/core/analyzer/call-graph.d.ts +19 -1
- package/dist/core/analyzer/call-graph.d.ts.map +1 -1
- package/dist/core/analyzer/call-graph.js +128 -28
- package/dist/core/analyzer/call-graph.js.map +1 -1
- package/dist/core/architecture/check.d.ts +63 -0
- package/dist/core/architecture/check.d.ts.map +1 -0
- package/dist/core/architecture/check.js +192 -0
- package/dist/core/architecture/check.js.map +1 -0
- package/dist/core/architecture/rules.d.ts +73 -0
- package/dist/core/architecture/rules.d.ts.map +1 -0
- package/dist/core/architecture/rules.js +201 -0
- package/dist/core/architecture/rules.js.map +1 -0
- package/dist/core/decisions/lock.d.ts +10 -0
- package/dist/core/decisions/lock.d.ts.map +1 -0
- package/dist/core/decisions/lock.js +77 -0
- package/dist/core/decisions/lock.js.map +1 -0
- package/dist/core/decisions/project.d.ts +59 -0
- package/dist/core/decisions/project.d.ts.map +1 -0
- package/dist/core/decisions/project.js +68 -0
- package/dist/core/decisions/project.js.map +1 -0
- package/dist/core/decisions/verifier.d.ts +10 -0
- package/dist/core/decisions/verifier.d.ts.map +1 -1
- package/dist/core/decisions/verifier.js +48 -5
- package/dist/core/decisions/verifier.js.map +1 -1
- package/dist/core/provenance/change-coupling.d.ts +68 -0
- package/dist/core/provenance/change-coupling.d.ts.map +1 -0
- package/dist/core/provenance/change-coupling.js +134 -0
- package/dist/core/provenance/change-coupling.js.map +1 -0
- package/dist/core/provenance/git-provenance.d.ts +67 -0
- package/dist/core/provenance/git-provenance.d.ts.map +1 -0
- package/dist/core/provenance/git-provenance.js +177 -0
- package/dist/core/provenance/git-provenance.js.map +1 -0
- package/dist/core/provenance/project.d.ts +37 -0
- package/dist/core/provenance/project.d.ts.map +1 -0
- package/dist/core/provenance/project.js +46 -0
- package/dist/core/provenance/project.js.map +1 -0
- package/dist/core/services/edge-store.d.ts +41 -0
- package/dist/core/services/edge-store.d.ts.map +1 -1
- package/dist/core/services/edge-store.js +251 -3
- package/dist/core/services/edge-store.js.map +1 -1
- package/dist/core/services/llm-service.d.ts +9 -0
- package/dist/core/services/llm-service.d.ts.map +1 -1
- package/dist/core/services/llm-service.js +15 -4
- package/dist/core/services/llm-service.js.map +1 -1
- package/dist/core/services/mcp-handlers/architecture.d.ts +19 -0
- package/dist/core/services/mcp-handlers/architecture.d.ts.map +1 -0
- package/dist/core/services/mcp-handlers/architecture.js +104 -0
- package/dist/core/services/mcp-handlers/architecture.js.map +1 -0
- package/dist/core/services/mcp-handlers/change-coupling.d.ts +16 -0
- package/dist/core/services/mcp-handlers/change-coupling.d.ts.map +1 -0
- package/dist/core/services/mcp-handlers/change-coupling.js +57 -0
- package/dist/core/services/mcp-handlers/change-coupling.js.map +1 -0
- package/dist/core/services/mcp-handlers/graph.d.ts +27 -0
- package/dist/core/services/mcp-handlers/graph.d.ts.map +1 -1
- package/dist/core/services/mcp-handlers/graph.js +98 -16
- package/dist/core/services/mcp-handlers/graph.js.map +1 -1
- package/dist/core/services/mcp-handlers/orient.d.ts.map +1 -1
- package/dist/core/services/mcp-handlers/orient.js +122 -2
- package/dist/core/services/mcp-handlers/orient.js.map +1 -1
- package/dist/core/services/mcp-handlers/reachability.d.ts +30 -0
- package/dist/core/services/mcp-handlers/reachability.d.ts.map +1 -0
- package/dist/core/services/mcp-handlers/reachability.js +222 -0
- package/dist/core/services/mcp-handlers/reachability.js.map +1 -0
- package/dist/core/services/mcp-handlers/structural-diff.d.ts +31 -0
- package/dist/core/services/mcp-handlers/structural-diff.d.ts.map +1 -0
- package/dist/core/services/mcp-handlers/structural-diff.js +268 -0
- package/dist/core/services/mcp-handlers/structural-diff.js.map +1 -0
- package/dist/core/services/mcp-handlers/test-impact.d.ts +34 -0
- package/dist/core/services/mcp-handlers/test-impact.d.ts.map +1 -0
- package/dist/core/services/mcp-handlers/test-impact.js +221 -0
- package/dist/core/services/mcp-handlers/test-impact.js.map +1 -0
- package/dist/core/services/mcp-handlers/tool-guard.d.ts +45 -0
- package/dist/core/services/mcp-handlers/tool-guard.d.ts.map +1 -0
- package/dist/core/services/mcp-handlers/tool-guard.js +81 -0
- package/dist/core/services/mcp-handlers/tool-guard.js.map +1 -0
- package/dist/core/services/mcp-handlers/utils.d.ts.map +1 -1
- package/dist/core/services/mcp-handlers/utils.js +15 -1
- package/dist/core/services/mcp-handlers/utils.js.map +1 -1
- package/dist/core/services/mcp-watcher.d.ts.map +1 -1
- package/dist/core/services/mcp-watcher.js +9 -0
- package/dist/core/services/mcp-watcher.js.map +1 -1
- package/package.json +9 -8
package/dist/cli/commands/mcp.js
CHANGED
|
@@ -22,14 +22,20 @@ const _pkgVersion = _require('../../../package.json').version;
|
|
|
22
22
|
import { Command } from 'commander';
|
|
23
23
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
24
24
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
25
|
-
import { CallToolRequestSchema, InitializeRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
25
|
+
import { CallToolRequestSchema, InitializeRequestSchema, ListToolsRequestSchema, McpError, ErrorCode, LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, } from '@modelcontextprotocol/sdk/types.js';
|
|
26
|
+
import { validateToolArgs, withToolTimeout, capOutput, classifyToolError, } from '../../core/services/mcp-handlers/tool-guard.js';
|
|
26
27
|
import { sanitizeMcpError, validateDirectory } from '../../core/services/mcp-handlers/utils.js';
|
|
27
28
|
import { createTracker, updateTracker, getFreshnessSignal } from '../../core/services/mcp-handlers/epistemic-lease.js';
|
|
28
29
|
import { emit } from '../../core/services/telemetry.js';
|
|
29
|
-
import { DEFAULT_DRIFT_MAX_FILES } from '../../constants.js';
|
|
30
|
+
import { DEFAULT_DRIFT_MAX_FILES, MCP_TOOL_MAX_BYTES } from '../../constants.js';
|
|
30
31
|
import { handleGetCallGraph, handleGetSubgraph, handleAnalyzeImpact, handleGetLowRiskRefactorCandidates, handleGetLeafFunctions, handleGetCriticalHubs, handleGetGodFunctions, handleGetFileDependencies, handleTraceExecutionPath, } from '../../core/services/mcp-handlers/graph.js';
|
|
31
32
|
import { handleSearchCode, handleSuggestInsertionPoints, handleSearchSpecs, handleListSpecDomains, handleGetSpec, handleUnifiedSearch, } from '../../core/services/mcp-handlers/semantic.js';
|
|
32
33
|
import { handleOrient } from '../../core/services/mcp-handlers/orient.js';
|
|
34
|
+
import { handleSelectTests } from '../../core/services/mcp-handlers/test-impact.js';
|
|
35
|
+
import { handleFindDeadCode } from '../../core/services/mcp-handlers/reachability.js';
|
|
36
|
+
import { handleStructuralDiff } from '../../core/services/mcp-handlers/structural-diff.js';
|
|
37
|
+
import { handleGetChangeCoupling } from '../../core/services/mcp-handlers/change-coupling.js';
|
|
38
|
+
import { handleCheckArchitecture } from '../../core/services/mcp-handlers/architecture.js';
|
|
33
39
|
import { handleGenerateChangeProposal, handleAnnotateStory } from '../../core/services/mcp-handlers/change.js';
|
|
34
40
|
import { handleRecordDecision, handleListDecisions, handleApproveDecision, handleRejectDecision, handleSyncDecisions, } from '../../core/services/mcp-handlers/decisions.js';
|
|
35
41
|
import { handleAnalyzeCodebase, handleGetArchitectureOverview, handleGetRefactorReport, handleGetDuplicateReport, handleGetSignatures, handleGetMapping, handleCheckSpecDrift, handleGetFunctionSkeleton, handleGetFunctionBody, handleGetDecisions, handleGetRouteInventory, handleGetMiddlewareInventory, handleGetSchemaInventory, handleGetUIComponents, handleGetEnvVars, handleGetExternalPackages, handleAuditSpecCoverage, handleGenerateTests, handleGetTestCoverage, handleGetMinimalContext, handleGetCluster, handleDetectChanges, } from '../../core/services/mcp-handlers/analysis.js';
|
|
@@ -339,6 +345,107 @@ export const TOOL_DEFINITIONS = [
|
|
|
339
345
|
required: ['directory', 'symbol'],
|
|
340
346
|
},
|
|
341
347
|
},
|
|
348
|
+
{
|
|
349
|
+
name: 'select_tests',
|
|
350
|
+
description: 'USE THIS WHEN: you changed code and want to know which tests to run — ' +
|
|
351
|
+
'"which tests cover parseConfig?", "what should I run for this diff?". ' +
|
|
352
|
+
'Walks the call graph BACKWARD from the change to every test that transitively reaches it, ' +
|
|
353
|
+
'with the reaching path per test. Deterministic, offline, no test run. ' +
|
|
354
|
+
'It is an over-approximate PRIORITIZER (run these first), not a sound replacement for the full ' +
|
|
355
|
+
'suite — the response states its confidence and coverage. Run analyze_codebase first.',
|
|
356
|
+
inputSchema: {
|
|
357
|
+
type: 'object',
|
|
358
|
+
properties: {
|
|
359
|
+
directory: { type: 'string', description: 'Absolute path to the project directory' },
|
|
360
|
+
changedSymbols: {
|
|
361
|
+
type: 'array',
|
|
362
|
+
items: { type: 'string' },
|
|
363
|
+
description: 'Changed function/method names. Provide this OR diffRef.',
|
|
364
|
+
},
|
|
365
|
+
diffRef: {
|
|
366
|
+
type: 'string',
|
|
367
|
+
description: 'Git ref to diff the working tree against (e.g. "HEAD", "main"). Provide this OR changedSymbols.',
|
|
368
|
+
},
|
|
369
|
+
maxDepth: { type: 'number', description: 'Backward reachability depth (default 12)' },
|
|
370
|
+
},
|
|
371
|
+
required: ['directory'],
|
|
372
|
+
},
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
name: 'find_dead_code',
|
|
376
|
+
description: 'USE THIS WHEN: "what code is unreachable / dead?", "is anything calling X?", or ' +
|
|
377
|
+
'"what becomes dead if I delete X?". Cross-language mark-and-sweep reachability from roots ' +
|
|
378
|
+
'(tests, imported symbols, route handlers, main) over the call graph. ' +
|
|
379
|
+
'Pass ifDeleted to get the downstream-only-reachable set for a symbol. ' +
|
|
380
|
+
'Results are confidence-tagged CANDIDATES, never deletion authority — dynamic dispatch, DI, ' +
|
|
381
|
+
'and external consumers cause false positives, stated in the response. Run analyze_codebase first.',
|
|
382
|
+
inputSchema: {
|
|
383
|
+
type: 'object',
|
|
384
|
+
properties: {
|
|
385
|
+
directory: { type: 'string', description: 'Absolute path to the project directory' },
|
|
386
|
+
ifDeleted: { type: 'string', description: 'Symbol name — returns what becomes dead if it is deleted (delete-impact mode)' },
|
|
387
|
+
maxResults: { type: 'number', description: 'Max candidate-dead results (default 100)' },
|
|
388
|
+
filePattern: { type: 'string', description: 'Only report candidates whose file path contains this substring' },
|
|
389
|
+
},
|
|
390
|
+
required: ['directory'],
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
name: 'structural_diff',
|
|
395
|
+
description: 'USE THIS WHEN reviewing or refactoring a change: "what changed structurally?", ' +
|
|
396
|
+
'"whose callers are now stale?". A graph diff (complement to git diff) between two states ' +
|
|
397
|
+
'(working tree vs a ref, or two refs): functions/edges added & removed, signature changes, ' +
|
|
398
|
+
'and the existing callers now STALE because a callee signature moved under them. ' +
|
|
399
|
+
'Rename/move ambiguity is flagged, not guessed. Deterministic, offline. Run analyze_codebase ' +
|
|
400
|
+
'first for stale-caller analysis.',
|
|
401
|
+
inputSchema: {
|
|
402
|
+
type: 'object',
|
|
403
|
+
properties: {
|
|
404
|
+
directory: { type: 'string', description: 'Absolute path to the project directory' },
|
|
405
|
+
baseRef: { type: 'string', description: 'Old state to diff against (default "HEAD")' },
|
|
406
|
+
headRef: { type: 'string', description: 'New state (a git ref). Omit to use the working tree.' },
|
|
407
|
+
maxResults: { type: 'number', description: 'Cap reported items per category (default 200)' },
|
|
408
|
+
},
|
|
409
|
+
required: ['directory'],
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
name: 'get_change_coupling',
|
|
414
|
+
description: 'USE THIS WHEN: "what changes together with this file?" or "what is the most volatile code?". ' +
|
|
415
|
+
'Mined from local git history (not the call graph): co-change coupling surfaces invisible ' +
|
|
416
|
+
'coupling with no import/call edge (the config + parser that move in lockstep), and ' +
|
|
417
|
+
'volatility/churn flags risky high-change code. Pass a file for its coupling, or omit for the ' +
|
|
418
|
+
'most-volatile overview. Advisory signal (correlation, not causation); bulk commits filtered. ' +
|
|
419
|
+
'Run analyze_codebase first.',
|
|
420
|
+
inputSchema: {
|
|
421
|
+
type: 'object',
|
|
422
|
+
properties: {
|
|
423
|
+
directory: { type: 'string', description: 'Absolute path to the project directory' },
|
|
424
|
+
file: { type: 'string', description: 'A file to query its coupling/volatility. Omit for the most-volatile overview.' },
|
|
425
|
+
limit: { type: 'number', description: 'Cap results (default 20)' },
|
|
426
|
+
},
|
|
427
|
+
required: ['directory'],
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
name: 'check_architecture',
|
|
432
|
+
description: 'USE THIS BEFORE adding an import to check it against the repo\'s architecture rules, or to ' +
|
|
433
|
+
'list current architecture violations. Opt-in and inert unless the repo declares rules in ' +
|
|
434
|
+
'.openlore/architecture.json (layers / forbidden / allowedOnly) or via an "Invariant:" marker ' +
|
|
435
|
+
'in a synced ADR. Pre-edit mode: pass {from, to} ("may a file under <from> import <to>?") for a ' +
|
|
436
|
+
'deterministic allowed/denied + the governing rule + why, BEFORE you write the code. Scan mode: ' +
|
|
437
|
+
'pass only {directory} for the full current-violations report. Cross-language, offline, ' +
|
|
438
|
+
'deterministic; complements (does not replace) CI linters. Run analyze_codebase first.',
|
|
439
|
+
inputSchema: {
|
|
440
|
+
type: 'object',
|
|
441
|
+
properties: {
|
|
442
|
+
directory: { type: 'string', description: 'Absolute path to the project directory' },
|
|
443
|
+
from: { type: 'string', description: 'Pre-edit mode: the file that would gain the import (relative or absolute). Requires "to".' },
|
|
444
|
+
to: { type: 'string', description: 'Pre-edit mode: the target file path or exported symbol being imported. Requires "from".' },
|
|
445
|
+
},
|
|
446
|
+
required: ['directory'],
|
|
447
|
+
},
|
|
448
|
+
},
|
|
342
449
|
{
|
|
343
450
|
name: 'get_low_risk_refactor_candidates',
|
|
344
451
|
description: 'Return the safest functions to refactor first: low fan-in (few callers), ' +
|
|
@@ -1116,7 +1223,7 @@ const TOOL_ANNOTATIONS = {
|
|
|
1116
1223
|
orient: _RO, analyze_codebase: _RWI, get_architecture_overview: _RO,
|
|
1117
1224
|
get_refactor_report: _RO, get_call_graph: _RO, get_duplicate_report: _RO,
|
|
1118
1225
|
get_signatures: _RO, get_subgraph: _RO, trace_execution_path: _RO,
|
|
1119
|
-
get_mapping: _RO, check_spec_drift: _RO, analyze_impact: _RO,
|
|
1226
|
+
get_mapping: _RO, check_spec_drift: _RO, analyze_impact: _RO, select_tests: _RO, find_dead_code: _RO, structural_diff: _RO, get_change_coupling: _RO, check_architecture: _RO,
|
|
1120
1227
|
get_low_risk_refactor_candidates: _RO, get_leaf_functions: _RO,
|
|
1121
1228
|
get_critical_hubs: _RO, get_function_skeleton: _RO, get_god_functions: _RO,
|
|
1122
1229
|
suggest_insertion_points: _RO, search_code: _RO, list_spec_domains: _RO,
|
|
@@ -1129,9 +1236,55 @@ const TOOL_ANNOTATIONS = {
|
|
|
1129
1236
|
detect_changes: _RO, record_decision: _RW, list_decisions: _RO,
|
|
1130
1237
|
approve_decision: _RWI, reject_decision: _RWI, sync_decisions: _RWI,
|
|
1131
1238
|
};
|
|
1239
|
+
// Tools that touch external entities (LLM / network) → openWorldHint: true.
|
|
1240
|
+
// Everything else is local, deterministic, closed-world analysis.
|
|
1241
|
+
const OPEN_WORLD_TOOLS = new Set(['generate_tests', 'generate_change_proposal', 'annotate_story']);
|
|
1242
|
+
/** Human-readable title from a snake_case tool name (spec-11 annotations). */
|
|
1243
|
+
function toolTitle(name) {
|
|
1244
|
+
return name.split('_').map(w => (w ? w[0].toUpperCase() + w.slice(1) : w)).join(' ');
|
|
1245
|
+
}
|
|
1246
|
+
/** Full MCP `annotations` for a tool: read/write hints + title + openWorldHint (spec-11). */
|
|
1247
|
+
export function toolAnnotations(name) {
|
|
1248
|
+
return {
|
|
1249
|
+
title: toolTitle(name),
|
|
1250
|
+
...(TOOL_ANNOTATIONS[name] ?? _RO),
|
|
1251
|
+
openWorldHint: OPEN_WORLD_TOOLS.has(name),
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1132
1254
|
const MINIMAL_TOOLS = new Set([
|
|
1133
1255
|
'orient', 'search_code', 'record_decision', 'detect_changes', 'check_spec_drift',
|
|
1134
1256
|
]);
|
|
1257
|
+
// Named tool presets (Spec 14). A small, navigation-focused surface keeps the
|
|
1258
|
+
// per-request tool-schema overhead low (the MCP best-practice: schemas for tools
|
|
1259
|
+
// the agent never calls are pure overhead) while still exposing the graph-
|
|
1260
|
+
// traversal tools a "how does X reach Y" task actually needs — which `minimal`
|
|
1261
|
+
// (orient + search + governance) omits. CodeGraph wins its benchmark with a
|
|
1262
|
+
// surface like this; openlore's was either too lean (no traversal) or all ~45.
|
|
1263
|
+
export const TOOL_PRESETS = {
|
|
1264
|
+
minimal: MINIMAL_TOOLS,
|
|
1265
|
+
// Graph-navigation core: orient to enter, then traverse/trace/impact + compact
|
|
1266
|
+
// symbol bodies — no governance tools, no inventories.
|
|
1267
|
+
navigation: new Set([
|
|
1268
|
+
'orient', 'search_code', 'get_subgraph', 'trace_execution_path',
|
|
1269
|
+
'analyze_impact', 'suggest_insertion_points', 'get_function_skeleton',
|
|
1270
|
+
]),
|
|
1271
|
+
};
|
|
1272
|
+
/**
|
|
1273
|
+
* Resolve which tools an MCP session exposes (Spec 14). `--preset` wins over the
|
|
1274
|
+
* legacy `--minimal` (= the 'minimal' preset); no selector = all tools. Throws on
|
|
1275
|
+
* an unknown preset so a typo fails loudly instead of silently exposing all 45.
|
|
1276
|
+
* Pure + exported for unit testing.
|
|
1277
|
+
*/
|
|
1278
|
+
export function selectActiveTools(allTools, opts) {
|
|
1279
|
+
const presetName = opts.preset ?? (opts.minimal ? 'minimal' : undefined);
|
|
1280
|
+
if (!presetName)
|
|
1281
|
+
return allTools;
|
|
1282
|
+
const preset = TOOL_PRESETS[presetName];
|
|
1283
|
+
if (!preset) {
|
|
1284
|
+
throw new Error(`Unknown --preset "${presetName}". Known presets: ${Object.keys(TOOL_PRESETS).join(', ')}.`);
|
|
1285
|
+
}
|
|
1286
|
+
return allTools.filter(t => preset.has(t.name));
|
|
1287
|
+
}
|
|
1135
1288
|
async function startMcpServer(options = {}) {
|
|
1136
1289
|
// The MCP stdio transport uses stdout EXCLUSIVELY for the JSON-RPC stream.
|
|
1137
1290
|
// Any stray write to stdout — e.g. logger.success("Successfully validated
|
|
@@ -1147,15 +1300,13 @@ async function startMcpServer(options = {}) {
|
|
|
1147
1300
|
console.info = toStderr;
|
|
1148
1301
|
console.warn = toStderr;
|
|
1149
1302
|
console.debug = toStderr;
|
|
1150
|
-
const activeTools = options.minimal
|
|
1151
|
-
? TOOL_DEFINITIONS.filter(t => MINIMAL_TOOLS.has(t.name))
|
|
1152
|
-
: TOOL_DEFINITIONS;
|
|
1303
|
+
const activeTools = selectActiveTools(TOOL_DEFINITIONS, { minimal: options.minimal, preset: options.preset });
|
|
1153
1304
|
// Report the real package version in the MCP initialize handshake rather
|
|
1154
1305
|
// than a stale hardcoded string.
|
|
1155
1306
|
const { version: pkgVersion } = _require('../../../package.json');
|
|
1156
1307
|
const server = new Server({ name: 'openlore', version: pkgVersion }, { capabilities: { tools: {} } });
|
|
1157
1308
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
1158
|
-
tools: activeTools.map(t => ({ ...t, annotations:
|
|
1309
|
+
tools: activeTools.map(t => ({ ...t, annotations: toolAnnotations(t.name) })),
|
|
1159
1310
|
}));
|
|
1160
1311
|
// Per-session epistemic lease tracker — re-initialized when directory changes.
|
|
1161
1312
|
let tracker;
|
|
@@ -1168,8 +1319,16 @@ async function startMcpServer(options = {}) {
|
|
|
1168
1319
|
server.setRequestHandler(InitializeRequestSchema, async (request) => {
|
|
1169
1320
|
agentName = request.params.clientInfo?.name ?? 'unknown';
|
|
1170
1321
|
agentVersion = request.params.clientInfo?.version ?? 'unknown';
|
|
1322
|
+
// Protocol negotiation (spec-12): echo the client's requested version when we
|
|
1323
|
+
// support it (per the SDK's pinned set), else offer our latest supported one.
|
|
1324
|
+
const requested = request.params.protocolVersion;
|
|
1325
|
+
const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.includes(requested)
|
|
1326
|
+
? requested
|
|
1327
|
+
: LATEST_PROTOCOL_VERSION;
|
|
1171
1328
|
return {
|
|
1172
|
-
protocolVersion
|
|
1329
|
+
protocolVersion,
|
|
1330
|
+
// Honest capabilities: only `tools`. No `listChanged` — the tool list is
|
|
1331
|
+
// static per session, so we don't advertise a capability we don't implement.
|
|
1173
1332
|
capabilities: { tools: {} },
|
|
1174
1333
|
serverInfo: { name: 'openlore', version: _pkgVersion },
|
|
1175
1334
|
};
|
|
@@ -1196,6 +1355,17 @@ async function startMcpServer(options = {}) {
|
|
|
1196
1355
|
const _dir = args.directory;
|
|
1197
1356
|
const directory = typeof _dir === 'string' ? _dir : '';
|
|
1198
1357
|
const _t0 = Date.now();
|
|
1358
|
+
// Input validation (spec-10) against the tool's own declared inputSchema, before
|
|
1359
|
+
// dispatch. Invalid args become a JSON-RPC -32602 error (spec-12), not an
|
|
1360
|
+
// isError tool result — a malformed request is a protocol error, not a tool failure.
|
|
1361
|
+
{
|
|
1362
|
+
const toolDef = TOOL_DEFINITIONS.find(t => t.name === name);
|
|
1363
|
+
const argError = toolDef ? validateToolArgs(args, toolDef.inputSchema) : null;
|
|
1364
|
+
if (argError) {
|
|
1365
|
+
emit(directory, 'mcp', { event: 'tool_error', tool: name, ms: Date.now() - _t0, agent: agentName, code: 'INVALID_ARGS', error: argError });
|
|
1366
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for "${name}": ${argError}`);
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1199
1369
|
try {
|
|
1200
1370
|
const filePath = args.filePath;
|
|
1201
1371
|
// Init (or re-init when project directory changes between calls)
|
|
@@ -1207,205 +1377,240 @@ async function startMcpServer(options = {}) {
|
|
|
1207
1377
|
if (tracker && directory)
|
|
1208
1378
|
updateTracker(tracker, name, directory, typeof filePath === 'string' ? filePath : undefined);
|
|
1209
1379
|
let result;
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1380
|
+
let _unknownTool = false;
|
|
1381
|
+
// Per-tool timeout (spec-10): race the dispatch against the tool's budget so a
|
|
1382
|
+
// pathological hang can never wedge the server. Slow tools (analysis, LLM) have
|
|
1383
|
+
// generous overrides in MCP_TOOL_TIMEOUT_OVERRIDES.
|
|
1384
|
+
await withToolTimeout((async () => {
|
|
1385
|
+
if (name === 'orient') {
|
|
1386
|
+
const { task, limit = 5 } = args;
|
|
1387
|
+
result = await handleOrient(directory, task, limit);
|
|
1388
|
+
if (result && typeof result === 'object') {
|
|
1389
|
+
const r = result;
|
|
1390
|
+
emit(directory, 'orient', {
|
|
1391
|
+
event: 'orient_call',
|
|
1392
|
+
agent: agentName,
|
|
1393
|
+
functions: Array.isArray(r['relevantFunctions']) ? r['relevantFunctions'].length : 0,
|
|
1394
|
+
files: Array.isArray(r['relevantFiles']) ? r['relevantFiles'].length : 0,
|
|
1395
|
+
spec_domains: Array.isArray(r['specDomains']) ? r['specDomains'].length : 0,
|
|
1396
|
+
insertion_points: Array.isArray(r['insertionPoints']) ? r['insertionPoints'].length : 0,
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1223
1399
|
}
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1400
|
+
else if (name === 'analyze_codebase') {
|
|
1401
|
+
const { directory, force = false } = args;
|
|
1402
|
+
result = await handleAnalyzeCodebase(directory, force);
|
|
1403
|
+
}
|
|
1404
|
+
else if (name === 'get_architecture_overview') {
|
|
1405
|
+
const { directory } = args;
|
|
1406
|
+
result = await handleGetArchitectureOverview(directory);
|
|
1407
|
+
}
|
|
1408
|
+
else if (name === 'get_refactor_report') {
|
|
1409
|
+
const { directory } = args;
|
|
1410
|
+
result = await handleGetRefactorReport(directory);
|
|
1411
|
+
}
|
|
1412
|
+
else if (name === 'get_call_graph') {
|
|
1413
|
+
const { directory } = args;
|
|
1414
|
+
result = await handleGetCallGraph(directory);
|
|
1415
|
+
}
|
|
1416
|
+
else if (name === 'get_signatures') {
|
|
1417
|
+
const { directory, filePattern } = args;
|
|
1418
|
+
result = await handleGetSignatures(directory, filePattern);
|
|
1419
|
+
}
|
|
1420
|
+
else if (name === 'get_subgraph') {
|
|
1421
|
+
const { directory, functionName, direction = 'downstream', maxDepth = 3, format = 'json' } = args;
|
|
1422
|
+
result = await handleGetSubgraph(directory, functionName, direction, maxDepth, format);
|
|
1423
|
+
}
|
|
1424
|
+
else if (name === 'trace_execution_path') {
|
|
1425
|
+
const { directory, entryFunction, targetFunction, maxDepth = 6, maxPaths = 10 } = args;
|
|
1426
|
+
result = await handleTraceExecutionPath(directory, entryFunction, targetFunction, maxDepth, maxPaths);
|
|
1427
|
+
}
|
|
1428
|
+
else if (name === 'get_mapping') {
|
|
1429
|
+
const { directory, domain, orphansOnly } = args;
|
|
1430
|
+
result = await handleGetMapping(directory, domain, orphansOnly);
|
|
1431
|
+
}
|
|
1432
|
+
else if (name === 'analyze_impact') {
|
|
1433
|
+
const { directory, symbol, depth = 2 } = args;
|
|
1434
|
+
result = await handleAnalyzeImpact(directory, symbol, depth);
|
|
1435
|
+
}
|
|
1436
|
+
else if (name === 'select_tests') {
|
|
1437
|
+
const { directory, changedSymbols, diffRef, maxDepth } = args;
|
|
1438
|
+
result = await handleSelectTests({ directory, changedSymbols, diffRef, maxDepth });
|
|
1439
|
+
}
|
|
1440
|
+
else if (name === 'find_dead_code') {
|
|
1441
|
+
const { directory, ifDeleted, maxResults, filePattern } = args;
|
|
1442
|
+
result = await handleFindDeadCode({ directory, ifDeleted, maxResults, filePattern });
|
|
1443
|
+
}
|
|
1444
|
+
else if (name === 'structural_diff') {
|
|
1445
|
+
const { directory, baseRef, headRef, maxResults } = args;
|
|
1446
|
+
result = await handleStructuralDiff({ directory, baseRef, headRef, maxResults });
|
|
1447
|
+
}
|
|
1448
|
+
else if (name === 'get_change_coupling') {
|
|
1449
|
+
const { directory, file, limit } = args;
|
|
1450
|
+
result = await handleGetChangeCoupling({ directory, file, limit });
|
|
1451
|
+
}
|
|
1452
|
+
else if (name === 'check_architecture') {
|
|
1453
|
+
const { directory, from, to } = args;
|
|
1454
|
+
result = await handleCheckArchitecture({ directory, from, to });
|
|
1455
|
+
}
|
|
1456
|
+
else if (name === 'get_low_risk_refactor_candidates') {
|
|
1457
|
+
const { directory, limit = 5, filePattern } = args;
|
|
1458
|
+
result = await handleGetLowRiskRefactorCandidates(directory, limit, filePattern);
|
|
1459
|
+
}
|
|
1460
|
+
else if (name === 'get_leaf_functions') {
|
|
1461
|
+
const { directory, limit = 20, filePattern, sortBy = 'fanIn' } = args;
|
|
1462
|
+
result = await handleGetLeafFunctions(directory, limit, filePattern, sortBy);
|
|
1463
|
+
}
|
|
1464
|
+
else if (name === 'get_critical_hubs') {
|
|
1465
|
+
const { directory, limit = 10, minFanIn = 3 } = args;
|
|
1466
|
+
result = await handleGetCriticalHubs(directory, limit, minFanIn);
|
|
1467
|
+
}
|
|
1468
|
+
else if (name === 'get_duplicate_report') {
|
|
1469
|
+
const { directory } = args;
|
|
1470
|
+
result = await handleGetDuplicateReport(directory);
|
|
1471
|
+
}
|
|
1472
|
+
else if (name === 'get_function_skeleton') {
|
|
1473
|
+
const { directory, filePath } = args;
|
|
1474
|
+
result = await handleGetFunctionSkeleton(directory, filePath);
|
|
1475
|
+
}
|
|
1476
|
+
else if (name === 'get_god_functions') {
|
|
1477
|
+
const { directory, filePath, fanOutThreshold = 8 } = args;
|
|
1478
|
+
result = await handleGetGodFunctions(directory, filePath, fanOutThreshold);
|
|
1479
|
+
}
|
|
1480
|
+
else if (name === 'check_spec_drift') {
|
|
1481
|
+
const { directory, base = 'auto', files = [], domains = [], failOn = 'warning', maxFiles = DEFAULT_DRIFT_MAX_FILES } = args;
|
|
1482
|
+
result = await handleCheckSpecDrift(directory, base, files, domains, failOn, maxFiles);
|
|
1483
|
+
}
|
|
1484
|
+
else if (name === 'search_code') {
|
|
1485
|
+
const { directory, query, limit = 10, language, minFanIn } = args;
|
|
1486
|
+
result = await handleSearchCode(directory, query, limit, language, minFanIn);
|
|
1487
|
+
}
|
|
1488
|
+
else if (name === 'suggest_insertion_points') {
|
|
1489
|
+
const { directory, description, limit = 5, language } = args;
|
|
1490
|
+
result = await handleSuggestInsertionPoints(directory, description, limit, language);
|
|
1491
|
+
}
|
|
1492
|
+
else if (name === 'search_specs') {
|
|
1493
|
+
const { directory, query, limit = 10, domain, section } = args;
|
|
1494
|
+
result = await handleSearchSpecs(directory, query, limit, domain, section);
|
|
1495
|
+
}
|
|
1496
|
+
else if (name === 'search_unified') {
|
|
1497
|
+
const { directory, query, limit = 10, language, domain, section } = args;
|
|
1498
|
+
result = await handleUnifiedSearch(directory, query, limit, language, domain, section);
|
|
1499
|
+
}
|
|
1500
|
+
else if (name === 'list_spec_domains') {
|
|
1501
|
+
const { directory } = args;
|
|
1502
|
+
result = await handleListSpecDomains(directory);
|
|
1503
|
+
}
|
|
1504
|
+
else if (name === 'get_spec') {
|
|
1505
|
+
const { directory, domain } = args;
|
|
1506
|
+
result = await handleGetSpec(directory, domain);
|
|
1507
|
+
}
|
|
1508
|
+
else if (name === 'get_function_body') {
|
|
1509
|
+
const { directory, filePath, functionName } = args;
|
|
1510
|
+
result = await handleGetFunctionBody(directory, filePath, functionName);
|
|
1511
|
+
}
|
|
1512
|
+
else if (name === 'get_file_dependencies') {
|
|
1513
|
+
const { directory, filePath, direction = 'both' } = args;
|
|
1514
|
+
result = await handleGetFileDependencies(directory, filePath, direction);
|
|
1515
|
+
}
|
|
1516
|
+
else if (name === 'generate_change_proposal') {
|
|
1517
|
+
const { directory, description, slug, storyContent } = args;
|
|
1518
|
+
result = await handleGenerateChangeProposal(directory, description, slug, storyContent);
|
|
1519
|
+
}
|
|
1520
|
+
else if (name === 'annotate_story') {
|
|
1521
|
+
const { directory, storyFilePath, description } = args;
|
|
1522
|
+
result = await handleAnnotateStory(directory, storyFilePath, description);
|
|
1523
|
+
}
|
|
1524
|
+
else if (name === 'get_decisions') {
|
|
1525
|
+
const { directory, query } = args;
|
|
1526
|
+
result = await handleGetDecisions(directory, query);
|
|
1527
|
+
}
|
|
1528
|
+
else if (name === 'get_route_inventory') {
|
|
1529
|
+
const { directory } = args;
|
|
1530
|
+
result = await handleGetRouteInventory(directory);
|
|
1531
|
+
}
|
|
1532
|
+
else if (name === 'get_middleware_inventory') {
|
|
1533
|
+
const { directory } = args;
|
|
1534
|
+
result = await handleGetMiddlewareInventory(directory);
|
|
1535
|
+
}
|
|
1536
|
+
else if (name === 'get_schema_inventory') {
|
|
1537
|
+
const { directory } = args;
|
|
1538
|
+
result = await handleGetSchemaInventory(directory);
|
|
1539
|
+
}
|
|
1540
|
+
else if (name === 'get_ui_components') {
|
|
1541
|
+
const { directory } = args;
|
|
1542
|
+
result = await handleGetUIComponents(directory);
|
|
1543
|
+
}
|
|
1544
|
+
else if (name === 'get_env_vars') {
|
|
1545
|
+
const { directory } = args;
|
|
1546
|
+
result = await handleGetEnvVars(directory);
|
|
1547
|
+
}
|
|
1548
|
+
else if (name === 'get_external_packages') {
|
|
1549
|
+
const { directory } = args;
|
|
1550
|
+
result = await handleGetExternalPackages(directory);
|
|
1551
|
+
}
|
|
1552
|
+
else if (name === 'audit_spec_coverage') {
|
|
1553
|
+
const { directory, maxUncovered = 50, hubThreshold = 5 } = args;
|
|
1554
|
+
result = await handleAuditSpecCoverage(directory, maxUncovered, hubThreshold);
|
|
1555
|
+
}
|
|
1556
|
+
else if (name === 'generate_tests') {
|
|
1557
|
+
const { directory, domains, framework, useLlm, dryRun } = args;
|
|
1558
|
+
result = await handleGenerateTests({ directory, domains, framework, useLlm, dryRun });
|
|
1559
|
+
}
|
|
1560
|
+
else if (name === 'get_test_coverage') {
|
|
1561
|
+
const { directory, domains, minCoverage } = args;
|
|
1562
|
+
result = await handleGetTestCoverage({ directory, domains, minCoverage });
|
|
1563
|
+
}
|
|
1564
|
+
else if (name === 'get_minimal_context') {
|
|
1565
|
+
const { directory, functionName, filePath } = args;
|
|
1566
|
+
result = await handleGetMinimalContext(directory, functionName, filePath);
|
|
1567
|
+
}
|
|
1568
|
+
else if (name === 'get_cluster') {
|
|
1569
|
+
const { directory, functionName } = args;
|
|
1570
|
+
result = await handleGetCluster(directory, functionName);
|
|
1571
|
+
}
|
|
1572
|
+
else if (name === 'detect_changes') {
|
|
1573
|
+
const { directory, base } = args;
|
|
1574
|
+
result = await handleDetectChanges(directory, base);
|
|
1575
|
+
}
|
|
1576
|
+
else if (name === 'record_decision') {
|
|
1577
|
+
const { directory, title, rationale, consequences, affectedFiles, supersedes, scope } = args;
|
|
1578
|
+
result = await handleRecordDecision(directory, title, rationale, consequences, affectedFiles, supersedes, scope);
|
|
1579
|
+
}
|
|
1580
|
+
else if (name === 'list_decisions') {
|
|
1581
|
+
const { directory, status } = args;
|
|
1582
|
+
result = await handleListDecisions(directory, status);
|
|
1583
|
+
}
|
|
1584
|
+
else if (name === 'approve_decision') {
|
|
1585
|
+
const { directory, id, note } = args;
|
|
1586
|
+
result = await handleApproveDecision(directory, id, note);
|
|
1587
|
+
}
|
|
1588
|
+
else if (name === 'reject_decision') {
|
|
1589
|
+
const { directory, id, note } = args;
|
|
1590
|
+
result = await handleRejectDecision(directory, id, note);
|
|
1591
|
+
}
|
|
1592
|
+
else if (name === 'sync_decisions') {
|
|
1593
|
+
const { directory, dryRun = false, id } = args;
|
|
1594
|
+
result = await handleSyncDecisions(directory, dryRun, id);
|
|
1595
|
+
}
|
|
1596
|
+
else {
|
|
1597
|
+
_unknownTool = true;
|
|
1598
|
+
}
|
|
1599
|
+
})(), name);
|
|
1600
|
+
if (_unknownTool) {
|
|
1402
1601
|
return {
|
|
1403
1602
|
content: [{ type: 'text', text: `Unknown tool: ${name}` }],
|
|
1404
1603
|
isError: true,
|
|
1405
1604
|
};
|
|
1406
1605
|
}
|
|
1407
|
-
|
|
1408
|
-
|
|
1606
|
+
const rawText = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
|
|
1607
|
+
// Output cap (spec-10): truncate deterministically with a how-to-narrow note
|
|
1608
|
+
// rather than silently dropping data or blowing the agent's context.
|
|
1609
|
+
const { text, truncated } = capOutput(rawText, MCP_TOOL_MAX_BYTES);
|
|
1610
|
+
emit(directory, 'mcp', {
|
|
1611
|
+
event: 'tool_call', tool: name, ms: Date.now() - _t0, agent: agentName, agent_version: agentVersion,
|
|
1612
|
+
bytes: Buffer.byteLength(text, 'utf8'), outcome: truncated ? 'truncated' : 'ok',
|
|
1613
|
+
});
|
|
1409
1614
|
const signal = tracker ? getFreshnessSignal(tracker) : null;
|
|
1410
1615
|
// Freshness signal is a separate content item — never concatenated into
|
|
1411
1616
|
// the result body — so structured outputs (JSON, patches) are not corrupted.
|
|
@@ -1417,9 +1622,17 @@ async function startMcpServer(options = {}) {
|
|
|
1417
1622
|
return { content };
|
|
1418
1623
|
}
|
|
1419
1624
|
catch (err) {
|
|
1420
|
-
|
|
1625
|
+
// A thrown McpError is a protocol-level error (e.g. -32602) — let the SDK
|
|
1626
|
+
// serialize it as a JSON-RPC error response, not a tool isError result.
|
|
1627
|
+
if (err instanceof McpError)
|
|
1628
|
+
throw err;
|
|
1629
|
+
// Error normalization (spec-10): a stable code taxonomy, distinguishing
|
|
1630
|
+
// "repo not analyzed yet" (actionable) from real failures and timeouts.
|
|
1631
|
+
const code = classifyToolError(err);
|
|
1632
|
+
const message = sanitizeMcpError(err);
|
|
1633
|
+
emit(directory, 'mcp', { event: 'tool_error', tool: name, ms: Date.now() - _t0, agent: agentName, code, outcome: 'error', error: message });
|
|
1421
1634
|
return {
|
|
1422
|
-
content: [{ type: 'text', text: `Tool error: ${
|
|
1635
|
+
content: [{ type: 'text', text: `Tool error [${code}]: ${message}` }],
|
|
1423
1636
|
isError: true,
|
|
1424
1637
|
};
|
|
1425
1638
|
}
|
|
@@ -1452,5 +1665,6 @@ export const mcpCommand = new Command('mcp')
|
|
|
1452
1665
|
.option('--watch-debounce <ms>', 'Debounce delay in ms before re-indexing after a file change (default: 400)', '400')
|
|
1453
1666
|
.option('--watch-no-embed', 'Watch signatures only — skip live vector re-embedding (embeddings refresh at commit). Large repos auto-degrade to this.')
|
|
1454
1667
|
.option('--minimal', 'Expose only core 5 tools (orient, search_code, record_decision, detect_changes, check_spec_drift). Pair with alwaysLoad: true in Claude Code for always-visible core tools.')
|
|
1668
|
+
.option('--preset <name>', 'Expose a named tool preset instead of all ~45. "minimal" = orient+search+governance; "navigation" = graph-traversal core (orient, search_code, get_subgraph, trace_execution_path, analyze_impact, suggest_insertion_points, get_function_skeleton) for low-overhead code navigation. Takes precedence over --minimal.')
|
|
1455
1669
|
.action((options) => startMcpServer(options));
|
|
1456
1670
|
//# sourceMappingURL=mcp.js.map
|