openlore 2.0.7 → 2.0.9
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 +160 -25
- package/dist/api/init.d.ts.map +1 -1
- package/dist/api/init.js +9 -2
- package/dist/api/init.js.map +1 -1
- package/dist/cli/commands/analyze.d.ts.map +1 -1
- package/dist/cli/commands/analyze.js +5 -3
- package/dist/cli/commands/analyze.js.map +1 -1
- package/dist/cli/commands/decisions.d.ts +0 -1
- package/dist/cli/commands/decisions.d.ts.map +1 -1
- package/dist/cli/commands/decisions.js +19 -31
- package/dist/cli/commands/decisions.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.js +85 -23
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +16 -3
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/mcp.d.ts +766 -0
- package/dist/cli/commands/mcp.d.ts.map +1 -1
- package/dist/cli/commands/mcp.js +444 -244
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/orient.d.ts.map +1 -1
- package/dist/cli/commands/orient.js +14 -2
- package/dist/cli/commands/orient.js.map +1 -1
- package/dist/cli/commands/prove.d.ts +29 -0
- package/dist/cli/commands/prove.d.ts.map +1 -0
- package/dist/cli/commands/prove.js +160 -0
- package/dist/cli/commands/prove.js.map +1 -0
- package/dist/cli/commands/setup.d.ts.map +1 -1
- package/dist/cli/commands/setup.js +5 -3
- package/dist/cli/commands/setup.js.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/install/adapters/claude-code.d.ts +8 -2
- package/dist/cli/install/adapters/claude-code.d.ts.map +1 -1
- package/dist/cli/install/adapters/claude-code.js +99 -35
- package/dist/cli/install/adapters/claude-code.js.map +1 -1
- package/dist/cli/install/detect.d.ts.map +1 -1
- package/dist/cli/install/detect.js +14 -0
- package/dist/cli/install/detect.js.map +1 -1
- package/dist/cli/install/templates/agent-instructions.md +4 -2
- package/dist/constants.d.ts +16 -3
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +24 -3
- package/dist/constants.js.map +1 -1
- package/dist/core/agent-eval/measure.d.ts +57 -0
- package/dist/core/agent-eval/measure.d.ts.map +1 -0
- package/dist/core/agent-eval/measure.js +85 -0
- package/dist/core/agent-eval/measure.js.map +1 -0
- package/dist/core/agent-eval/scorecard.d.ts +37 -0
- package/dist/core/agent-eval/scorecard.d.ts.map +1 -0
- package/dist/core/agent-eval/scorecard.js +70 -0
- package/dist/core/agent-eval/scorecard.js.map +1 -0
- package/dist/core/agent-eval/tasks.d.ts +37 -0
- package/dist/core/agent-eval/tasks.d.ts.map +1 -0
- package/dist/core/agent-eval/tasks.js +76 -0
- package/dist/core/agent-eval/tasks.js.map +1 -0
- package/dist/core/analyzer/architecture-writer.d.ts +5 -3
- package/dist/core/analyzer/architecture-writer.d.ts.map +1 -1
- package/dist/core/analyzer/architecture-writer.js +6 -4
- package/dist/core/analyzer/architecture-writer.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 +130 -28
- package/dist/core/analyzer/call-graph.js.map +1 -1
- package/dist/core/analyzer/spec-vector-index.d.ts.map +1 -1
- package/dist/core/analyzer/spec-vector-index.js +6 -2
- package/dist/core/analyzer/spec-vector-index.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/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/config-manager.d.ts +17 -0
- package/dist/core/services/config-manager.d.ts.map +1 -1
- package/dist/core/services/config-manager.js +35 -1
- package/dist/core/services/config-manager.js.map +1 -1
- 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 +1 -1
- package/dist/core/services/mcp-handlers/orient.d.ts.map +1 -1
- package/dist/core/services/mcp-handlers/orient.js +196 -39
- package/dist/core/services/mcp-handlers/orient.js.map +1 -1
- package/dist/core/services/mcp-handlers/progressive.d.ts +46 -0
- package/dist/core/services/mcp-handlers/progressive.d.ts.map +1 -0
- package/dist/core/services/mcp-handlers/progressive.js +0 -0
- package/dist/core/services/mcp-handlers/progressive.js.map +1 -0
- 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/semantic.d.ts +1 -1
- package/dist/core/services/mcp-handlers/semantic.d.ts.map +1 -1
- package/dist/core/services/mcp-handlers/semantic.js +39 -26
- package/dist/core/services/mcp-handlers/semantic.js.map +1 -1
- 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 +7 -0
- package/dist/core/services/mcp-watcher.d.ts.map +1 -1
- package/dist/core/services/mcp-watcher.js +46 -0
- package/dist/core/services/mcp-watcher.js.map +1 -1
- package/dist/core/services/project-detector.d.ts.map +1 -1
- package/dist/core/services/project-detector.js +45 -2
- package/dist/core/services/project-detector.js.map +1 -1
- package/package.json +8 -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';
|
|
@@ -40,6 +46,11 @@ export { handleGetCallGraph, handleGetSubgraph, handleAnalyzeImpact, handleGetLo
|
|
|
40
46
|
// ============================================================================
|
|
41
47
|
// TOOL DEFINITIONS
|
|
42
48
|
// ============================================================================
|
|
49
|
+
// Spec 28 — the `directory` param is the one input every tool shares (50×). Its
|
|
50
|
+
// description was a verbatim 38-char repeat on each tool; a single short shared
|
|
51
|
+
// constant trims that lossless repeat from the cached tools/list prefix without
|
|
52
|
+
// dropping the one fact that matters (it must be absolute, not relative).
|
|
53
|
+
const DIR_DESC = 'Absolute project path';
|
|
43
54
|
export const TOOL_DEFINITIONS = [
|
|
44
55
|
{
|
|
45
56
|
name: 'orient',
|
|
@@ -53,7 +64,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
53
64
|
properties: {
|
|
54
65
|
directory: {
|
|
55
66
|
type: 'string',
|
|
56
|
-
description:
|
|
67
|
+
description: DIR_DESC,
|
|
57
68
|
},
|
|
58
69
|
task: {
|
|
59
70
|
type: 'string',
|
|
@@ -63,6 +74,14 @@ export const TOOL_DEFINITIONS = [
|
|
|
63
74
|
type: 'number',
|
|
64
75
|
description: 'Number of relevant functions to return (default: 5)',
|
|
65
76
|
},
|
|
77
|
+
tokenBudget: {
|
|
78
|
+
type: 'number',
|
|
79
|
+
description: 'Optional: cap relevantFunctions to ~this many tokens (highest-scored kept, exact duplicates collapsed); each item carries an `expand` handle for get_function_body',
|
|
80
|
+
},
|
|
81
|
+
lean: {
|
|
82
|
+
type: 'boolean',
|
|
83
|
+
description: 'Return only the navigation core (relevantFunctions + callPaths + specDomains); drop provenance/change-coupling/insertion-points/specs/decisions enrichment (each reachable via expand handles or dedicated tools). Lower per-call cost for shallow "who/where" lookups.',
|
|
84
|
+
},
|
|
66
85
|
},
|
|
67
86
|
required: ['directory', 'task'],
|
|
68
87
|
},
|
|
@@ -91,8 +110,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
91
110
|
{
|
|
92
111
|
name: 'get_architecture_overview',
|
|
93
112
|
description: 'USE THIS WHEN: onboarding to an unknown codebase, or before planning a large feature. ' +
|
|
94
|
-
'Returns domain clusters, cross-cluster dependencies, global entry points, and critical hubs
|
|
95
|
-
'
|
|
113
|
+
'Returns domain clusters, cross-cluster dependencies, global entry points, and critical hubs. ' +
|
|
114
|
+
'Run analyze_codebase first.',
|
|
96
115
|
inputSchema: {
|
|
97
116
|
type: 'object',
|
|
98
117
|
properties: {
|
|
@@ -125,14 +144,13 @@ export const TOOL_DEFINITIONS = [
|
|
|
125
144
|
name: 'get_call_graph',
|
|
126
145
|
description: 'Return the call graph for a project: hub functions (high fan-in), ' +
|
|
127
146
|
'entry points (no internal callers), and architectural layer violations. ' +
|
|
128
|
-
'Supports TypeScript, JavaScript, Python, Go, Rust, Ruby, Java, C++. ' +
|
|
129
147
|
'Run analyze_codebase first.',
|
|
130
148
|
inputSchema: {
|
|
131
149
|
type: 'object',
|
|
132
150
|
properties: {
|
|
133
151
|
directory: {
|
|
134
152
|
type: 'string',
|
|
135
|
-
description:
|
|
153
|
+
description: DIR_DESC,
|
|
136
154
|
},
|
|
137
155
|
},
|
|
138
156
|
required: ['directory'],
|
|
@@ -144,7 +162,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
144
162
|
'Detects Type 1 (exact clones — identical after whitespace/comment normalization), ' +
|
|
145
163
|
'Type 2 (structural clones — same structure with renamed variables), and ' +
|
|
146
164
|
'Type 3 (near-clones with Jaccard similarity ≥ 0.7 on token n-grams). ' +
|
|
147
|
-
'
|
|
165
|
+
'Run analyze_codebase first.',
|
|
148
166
|
inputSchema: {
|
|
149
167
|
type: 'object',
|
|
150
168
|
properties: {
|
|
@@ -166,7 +184,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
166
184
|
properties: {
|
|
167
185
|
directory: {
|
|
168
186
|
type: 'string',
|
|
169
|
-
description:
|
|
187
|
+
description: DIR_DESC,
|
|
170
188
|
},
|
|
171
189
|
filePattern: {
|
|
172
190
|
type: 'string',
|
|
@@ -189,7 +207,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
189
207
|
properties: {
|
|
190
208
|
directory: {
|
|
191
209
|
type: 'string',
|
|
192
|
-
description:
|
|
210
|
+
description: DIR_DESC,
|
|
193
211
|
},
|
|
194
212
|
functionName: {
|
|
195
213
|
type: 'string',
|
|
@@ -227,7 +245,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
227
245
|
properties: {
|
|
228
246
|
directory: {
|
|
229
247
|
type: 'string',
|
|
230
|
-
description:
|
|
248
|
+
description: DIR_DESC,
|
|
231
249
|
},
|
|
232
250
|
entryFunction: {
|
|
233
251
|
type: 'string',
|
|
@@ -260,7 +278,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
260
278
|
properties: {
|
|
261
279
|
directory: {
|
|
262
280
|
type: 'string',
|
|
263
|
-
description:
|
|
281
|
+
description: DIR_DESC,
|
|
264
282
|
},
|
|
265
283
|
domain: {
|
|
266
284
|
type: 'string',
|
|
@@ -325,7 +343,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
325
343
|
properties: {
|
|
326
344
|
directory: {
|
|
327
345
|
type: 'string',
|
|
328
|
-
description:
|
|
346
|
+
description: DIR_DESC,
|
|
329
347
|
},
|
|
330
348
|
symbol: {
|
|
331
349
|
type: 'string',
|
|
@@ -339,6 +357,107 @@ export const TOOL_DEFINITIONS = [
|
|
|
339
357
|
required: ['directory', 'symbol'],
|
|
340
358
|
},
|
|
341
359
|
},
|
|
360
|
+
{
|
|
361
|
+
name: 'select_tests',
|
|
362
|
+
description: 'USE THIS WHEN: you changed code and want to know which tests to run — ' +
|
|
363
|
+
'"which tests cover parseConfig?", "what should I run for this diff?". ' +
|
|
364
|
+
'Walks the call graph BACKWARD from the change to every test that transitively reaches it, ' +
|
|
365
|
+
'with the reaching path per test. Deterministic, offline, no test run. ' +
|
|
366
|
+
'It is an over-approximate PRIORITIZER (run these first), not a sound replacement for the full ' +
|
|
367
|
+
'suite — the response states its confidence and coverage. Run analyze_codebase first.',
|
|
368
|
+
inputSchema: {
|
|
369
|
+
type: 'object',
|
|
370
|
+
properties: {
|
|
371
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
372
|
+
changedSymbols: {
|
|
373
|
+
type: 'array',
|
|
374
|
+
items: { type: 'string' },
|
|
375
|
+
description: 'Changed function/method names. Provide this OR diffRef.',
|
|
376
|
+
},
|
|
377
|
+
diffRef: {
|
|
378
|
+
type: 'string',
|
|
379
|
+
description: 'Git ref to diff the working tree against (e.g. "HEAD", "main"). Provide this OR changedSymbols.',
|
|
380
|
+
},
|
|
381
|
+
maxDepth: { type: 'number', description: 'Backward reachability depth (default 12)' },
|
|
382
|
+
},
|
|
383
|
+
required: ['directory'],
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
name: 'find_dead_code',
|
|
388
|
+
description: 'USE THIS WHEN: "what code is unreachable / dead?", "is anything calling X?", or ' +
|
|
389
|
+
'"what becomes dead if I delete X?". Cross-language mark-and-sweep reachability from roots ' +
|
|
390
|
+
'(tests, imported symbols, route handlers, main) over the call graph. ' +
|
|
391
|
+
'Pass ifDeleted to get the downstream-only-reachable set for a symbol. ' +
|
|
392
|
+
'Results are confidence-tagged CANDIDATES, never deletion authority — dynamic dispatch, DI, ' +
|
|
393
|
+
'and external consumers cause false positives, stated in the response. Run analyze_codebase first.',
|
|
394
|
+
inputSchema: {
|
|
395
|
+
type: 'object',
|
|
396
|
+
properties: {
|
|
397
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
398
|
+
ifDeleted: { type: 'string', description: 'Symbol name — returns what becomes dead if it is deleted (delete-impact mode)' },
|
|
399
|
+
maxResults: { type: 'number', description: 'Max candidate-dead results (default 100)' },
|
|
400
|
+
filePattern: { type: 'string', description: 'Only report candidates whose file path contains this substring' },
|
|
401
|
+
},
|
|
402
|
+
required: ['directory'],
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
name: 'structural_diff',
|
|
407
|
+
description: 'USE THIS WHEN reviewing or refactoring a change: "what changed structurally?", ' +
|
|
408
|
+
'"whose callers are now stale?". A graph diff (complement to git diff) between two states ' +
|
|
409
|
+
'(working tree vs a ref, or two refs): functions/edges added & removed, signature changes, ' +
|
|
410
|
+
'and the existing callers now STALE because a callee signature moved under them. ' +
|
|
411
|
+
'Rename/move ambiguity is flagged, not guessed. Deterministic, offline. Run analyze_codebase ' +
|
|
412
|
+
'first for stale-caller analysis.',
|
|
413
|
+
inputSchema: {
|
|
414
|
+
type: 'object',
|
|
415
|
+
properties: {
|
|
416
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
417
|
+
baseRef: { type: 'string', description: 'Old state to diff against (default "HEAD")' },
|
|
418
|
+
headRef: { type: 'string', description: 'New state (a git ref). Omit to use the working tree.' },
|
|
419
|
+
maxResults: { type: 'number', description: 'Cap reported items per category (default 200)' },
|
|
420
|
+
},
|
|
421
|
+
required: ['directory'],
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
name: 'get_change_coupling',
|
|
426
|
+
description: 'USE THIS WHEN: "what changes together with this file?" or "what is the most volatile code?". ' +
|
|
427
|
+
'Mined from local git history (not the call graph): co-change coupling surfaces invisible ' +
|
|
428
|
+
'coupling with no import/call edge (the config + parser that move in lockstep), and ' +
|
|
429
|
+
'volatility/churn flags risky high-change code. Pass a file for its coupling, or omit for the ' +
|
|
430
|
+
'most-volatile overview. Advisory signal (correlation, not causation); bulk commits filtered. ' +
|
|
431
|
+
'Run analyze_codebase first.',
|
|
432
|
+
inputSchema: {
|
|
433
|
+
type: 'object',
|
|
434
|
+
properties: {
|
|
435
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
436
|
+
file: { type: 'string', description: 'A file to query its coupling/volatility. Omit for the most-volatile overview.' },
|
|
437
|
+
limit: { type: 'number', description: 'Cap results (default 20)' },
|
|
438
|
+
},
|
|
439
|
+
required: ['directory'],
|
|
440
|
+
},
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
name: 'check_architecture',
|
|
444
|
+
description: 'USE THIS BEFORE adding an import to check it against the repo\'s architecture rules, or to ' +
|
|
445
|
+
'list current architecture violations. Opt-in and inert unless the repo declares rules in ' +
|
|
446
|
+
'.openlore/architecture.json (layers / forbidden / allowedOnly) or via an "Invariant:" marker ' +
|
|
447
|
+
'in a synced ADR. Pre-edit mode: pass {from, to} ("may a file under <from> import <to>?") for a ' +
|
|
448
|
+
'deterministic allowed/denied + the governing rule + why, BEFORE you write the code. Scan mode: ' +
|
|
449
|
+
'pass only {directory} for the full current-violations report. Cross-language, offline, ' +
|
|
450
|
+
'deterministic; complements (does not replace) CI linters. Run analyze_codebase first.',
|
|
451
|
+
inputSchema: {
|
|
452
|
+
type: 'object',
|
|
453
|
+
properties: {
|
|
454
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
455
|
+
from: { type: 'string', description: 'Pre-edit mode: the file that would gain the import (relative or absolute). Requires "to".' },
|
|
456
|
+
to: { type: 'string', description: 'Pre-edit mode: the target file path or exported symbol being imported. Requires "from".' },
|
|
457
|
+
},
|
|
458
|
+
required: ['directory'],
|
|
459
|
+
},
|
|
460
|
+
},
|
|
342
461
|
{
|
|
343
462
|
name: 'get_low_risk_refactor_candidates',
|
|
344
463
|
description: 'Return the safest functions to refactor first: low fan-in (few callers), ' +
|
|
@@ -350,7 +469,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
350
469
|
properties: {
|
|
351
470
|
directory: {
|
|
352
471
|
type: 'string',
|
|
353
|
-
description:
|
|
472
|
+
description: DIR_DESC,
|
|
354
473
|
},
|
|
355
474
|
limit: {
|
|
356
475
|
type: 'number',
|
|
@@ -375,7 +494,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
375
494
|
properties: {
|
|
376
495
|
directory: {
|
|
377
496
|
type: 'string',
|
|
378
|
-
description:
|
|
497
|
+
description: DIR_DESC,
|
|
379
498
|
},
|
|
380
499
|
limit: {
|
|
381
500
|
type: 'number',
|
|
@@ -405,7 +524,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
405
524
|
properties: {
|
|
406
525
|
directory: {
|
|
407
526
|
type: 'string',
|
|
408
|
-
description:
|
|
527
|
+
description: DIR_DESC,
|
|
409
528
|
},
|
|
410
529
|
limit: {
|
|
411
530
|
type: 'number',
|
|
@@ -431,7 +550,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
431
550
|
properties: {
|
|
432
551
|
directory: {
|
|
433
552
|
type: 'string',
|
|
434
|
-
description:
|
|
553
|
+
description: DIR_DESC,
|
|
435
554
|
},
|
|
436
555
|
filePath: {
|
|
437
556
|
type: 'string',
|
|
@@ -452,7 +571,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
452
571
|
properties: {
|
|
453
572
|
directory: {
|
|
454
573
|
type: 'string',
|
|
455
|
-
description:
|
|
574
|
+
description: DIR_DESC,
|
|
456
575
|
},
|
|
457
576
|
filePath: {
|
|
458
577
|
type: 'string',
|
|
@@ -478,7 +597,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
478
597
|
properties: {
|
|
479
598
|
directory: {
|
|
480
599
|
type: 'string',
|
|
481
|
-
description:
|
|
600
|
+
description: DIR_DESC,
|
|
482
601
|
},
|
|
483
602
|
description: {
|
|
484
603
|
type: 'string',
|
|
@@ -509,7 +628,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
509
628
|
properties: {
|
|
510
629
|
directory: {
|
|
511
630
|
type: 'string',
|
|
512
|
-
description:
|
|
631
|
+
description: DIR_DESC,
|
|
513
632
|
},
|
|
514
633
|
query: {
|
|
515
634
|
type: 'string',
|
|
@@ -527,6 +646,10 @@ export const TOOL_DEFINITIONS = [
|
|
|
527
646
|
type: 'number',
|
|
528
647
|
description: 'Only return functions with at least this many callers (hub filter)',
|
|
529
648
|
},
|
|
649
|
+
tokenBudget: {
|
|
650
|
+
type: 'number',
|
|
651
|
+
description: 'Optional: cap results to ~this many tokens (highest-scored kept, exact duplicates collapsed); each hit carries an `expand` handle for get_function_body',
|
|
652
|
+
},
|
|
530
653
|
},
|
|
531
654
|
required: ['directory', 'query'],
|
|
532
655
|
},
|
|
@@ -538,7 +661,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
538
661
|
inputSchema: {
|
|
539
662
|
type: 'object',
|
|
540
663
|
properties: {
|
|
541
|
-
directory: { type: 'string', description:
|
|
664
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
542
665
|
},
|
|
543
666
|
required: ['directory'],
|
|
544
667
|
},
|
|
@@ -554,7 +677,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
554
677
|
properties: {
|
|
555
678
|
directory: {
|
|
556
679
|
type: 'string',
|
|
557
|
-
description:
|
|
680
|
+
description: DIR_DESC,
|
|
558
681
|
},
|
|
559
682
|
query: {
|
|
560
683
|
type: 'string',
|
|
@@ -589,7 +712,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
589
712
|
properties: {
|
|
590
713
|
directory: {
|
|
591
714
|
type: 'string',
|
|
592
|
-
description:
|
|
715
|
+
description: DIR_DESC,
|
|
593
716
|
},
|
|
594
717
|
query: {
|
|
595
718
|
type: 'string',
|
|
@@ -624,7 +747,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
624
747
|
inputSchema: {
|
|
625
748
|
type: 'object',
|
|
626
749
|
properties: {
|
|
627
|
-
directory: { type: 'string', description:
|
|
750
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
628
751
|
domain: {
|
|
629
752
|
type: 'string',
|
|
630
753
|
description: 'Domain name as returned by list_spec_domains (e.g. "auth", "analyzer")',
|
|
@@ -642,7 +765,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
642
765
|
inputSchema: {
|
|
643
766
|
type: 'object',
|
|
644
767
|
properties: {
|
|
645
|
-
directory: { type: 'string', description:
|
|
768
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
646
769
|
filePath: {
|
|
647
770
|
type: 'string',
|
|
648
771
|
description: 'File path relative to the project directory, e.g. "src/auth/jwt.ts"',
|
|
@@ -664,7 +787,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
664
787
|
inputSchema: {
|
|
665
788
|
type: 'object',
|
|
666
789
|
properties: {
|
|
667
|
-
directory: { type: 'string', description:
|
|
790
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
668
791
|
filePath: {
|
|
669
792
|
type: 'string',
|
|
670
793
|
description: 'File path relative to the project root, e.g. "src/core/analyzer/vector-index.ts"',
|
|
@@ -691,7 +814,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
691
814
|
properties: {
|
|
692
815
|
directory: {
|
|
693
816
|
type: 'string',
|
|
694
|
-
description:
|
|
817
|
+
description: DIR_DESC,
|
|
695
818
|
},
|
|
696
819
|
description: {
|
|
697
820
|
type: 'string',
|
|
@@ -725,7 +848,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
725
848
|
properties: {
|
|
726
849
|
directory: {
|
|
727
850
|
type: 'string',
|
|
728
|
-
description:
|
|
851
|
+
description: DIR_DESC,
|
|
729
852
|
},
|
|
730
853
|
storyFilePath: {
|
|
731
854
|
type: 'string',
|
|
@@ -750,7 +873,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
750
873
|
inputSchema: {
|
|
751
874
|
type: 'object',
|
|
752
875
|
properties: {
|
|
753
|
-
directory: { type: 'string', description:
|
|
876
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
754
877
|
query: {
|
|
755
878
|
type: 'string',
|
|
756
879
|
description: 'Optional text filter — returns only ADRs whose title or content contains this string',
|
|
@@ -771,7 +894,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
771
894
|
inputSchema: {
|
|
772
895
|
type: 'object',
|
|
773
896
|
properties: {
|
|
774
|
-
directory: { type: 'string', description:
|
|
897
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
775
898
|
},
|
|
776
899
|
required: ['directory'],
|
|
777
900
|
},
|
|
@@ -788,7 +911,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
788
911
|
inputSchema: {
|
|
789
912
|
type: 'object',
|
|
790
913
|
properties: {
|
|
791
|
-
directory: { type: 'string', description:
|
|
914
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
792
915
|
},
|
|
793
916
|
required: ['directory'],
|
|
794
917
|
},
|
|
@@ -804,7 +927,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
804
927
|
inputSchema: {
|
|
805
928
|
type: 'object',
|
|
806
929
|
properties: {
|
|
807
|
-
directory: { type: 'string', description:
|
|
930
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
808
931
|
},
|
|
809
932
|
required: ['directory'],
|
|
810
933
|
},
|
|
@@ -820,7 +943,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
820
943
|
inputSchema: {
|
|
821
944
|
type: 'object',
|
|
822
945
|
properties: {
|
|
823
|
-
directory: { type: 'string', description:
|
|
946
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
824
947
|
},
|
|
825
948
|
required: ['directory'],
|
|
826
949
|
},
|
|
@@ -837,7 +960,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
837
960
|
inputSchema: {
|
|
838
961
|
type: 'object',
|
|
839
962
|
properties: {
|
|
840
|
-
directory: { type: 'string', description:
|
|
963
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
841
964
|
},
|
|
842
965
|
required: ['directory'],
|
|
843
966
|
},
|
|
@@ -852,7 +975,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
852
975
|
inputSchema: {
|
|
853
976
|
type: 'object',
|
|
854
977
|
properties: {
|
|
855
|
-
directory: { type: 'string', description:
|
|
978
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
856
979
|
},
|
|
857
980
|
required: ['directory'],
|
|
858
981
|
},
|
|
@@ -869,7 +992,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
869
992
|
inputSchema: {
|
|
870
993
|
type: 'object',
|
|
871
994
|
properties: {
|
|
872
|
-
directory: { type: 'string', description:
|
|
995
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
873
996
|
maxUncovered: {
|
|
874
997
|
type: 'number',
|
|
875
998
|
description: 'Maximum uncovered functions to return (default: 50)',
|
|
@@ -897,7 +1020,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
897
1020
|
properties: {
|
|
898
1021
|
directory: {
|
|
899
1022
|
type: 'string',
|
|
900
|
-
description:
|
|
1023
|
+
description: DIR_DESC,
|
|
901
1024
|
},
|
|
902
1025
|
domains: {
|
|
903
1026
|
type: 'array',
|
|
@@ -934,7 +1057,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
934
1057
|
properties: {
|
|
935
1058
|
directory: {
|
|
936
1059
|
type: 'string',
|
|
937
|
-
description:
|
|
1060
|
+
description: DIR_DESC,
|
|
938
1061
|
},
|
|
939
1062
|
domains: {
|
|
940
1063
|
type: 'array',
|
|
@@ -959,7 +1082,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
959
1082
|
inputSchema: {
|
|
960
1083
|
type: 'object',
|
|
961
1084
|
properties: {
|
|
962
|
-
directory: { type: 'string', description:
|
|
1085
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
963
1086
|
functionName: { type: 'string', description: 'Exact function or method name' },
|
|
964
1087
|
filePath: {
|
|
965
1088
|
type: 'string',
|
|
@@ -979,7 +1102,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
979
1102
|
inputSchema: {
|
|
980
1103
|
type: 'object',
|
|
981
1104
|
properties: {
|
|
982
|
-
directory: { type: 'string', description:
|
|
1105
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
983
1106
|
functionName: { type: 'string', description: 'Function name to look up the community for' },
|
|
984
1107
|
},
|
|
985
1108
|
required: ['directory', 'functionName'],
|
|
@@ -996,7 +1119,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
996
1119
|
inputSchema: {
|
|
997
1120
|
type: 'object',
|
|
998
1121
|
properties: {
|
|
999
|
-
directory: { type: 'string', description:
|
|
1122
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
1000
1123
|
base: {
|
|
1001
1124
|
type: 'string',
|
|
1002
1125
|
description: 'Git ref to diff against (default: HEAD). Use "HEAD~1" for last commit, "main" for branch diff.',
|
|
@@ -1015,7 +1138,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
1015
1138
|
inputSchema: {
|
|
1016
1139
|
type: 'object',
|
|
1017
1140
|
properties: {
|
|
1018
|
-
directory: { type: 'string', description:
|
|
1141
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
1019
1142
|
title: { type: 'string', description: 'Short imperative statement, e.g. "Use UUIDs for decision IDs"' },
|
|
1020
1143
|
rationale: { type: 'string', description: 'Why this decision was made' },
|
|
1021
1144
|
consequences: { type: 'string', description: 'What changes as a result (optional)' },
|
|
@@ -1046,7 +1169,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
1046
1169
|
inputSchema: {
|
|
1047
1170
|
type: 'object',
|
|
1048
1171
|
properties: {
|
|
1049
|
-
directory: { type: 'string', description:
|
|
1172
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
1050
1173
|
status: {
|
|
1051
1174
|
type: 'string',
|
|
1052
1175
|
enum: ['draft', 'consolidated', 'verified', 'phantom', 'approved', 'rejected', 'synced'],
|
|
@@ -1066,7 +1189,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
1066
1189
|
inputSchema: {
|
|
1067
1190
|
type: 'object',
|
|
1068
1191
|
properties: {
|
|
1069
|
-
directory: { type: 'string', description:
|
|
1192
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
1070
1193
|
id: { type: 'string', description: '8-character decision ID from list_decisions' },
|
|
1071
1194
|
note: { type: 'string', description: 'Optional review note' },
|
|
1072
1195
|
},
|
|
@@ -1079,7 +1202,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
1079
1202
|
inputSchema: {
|
|
1080
1203
|
type: 'object',
|
|
1081
1204
|
properties: {
|
|
1082
|
-
directory: { type: 'string', description:
|
|
1205
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
1083
1206
|
id: { type: 'string', description: '8-character decision ID from list_decisions' },
|
|
1084
1207
|
note: { type: 'string', description: 'Optional reason for rejection' },
|
|
1085
1208
|
},
|
|
@@ -1096,7 +1219,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
1096
1219
|
inputSchema: {
|
|
1097
1220
|
type: 'object',
|
|
1098
1221
|
properties: {
|
|
1099
|
-
directory: { type: 'string', description:
|
|
1222
|
+
directory: { type: 'string', description: DIR_DESC },
|
|
1100
1223
|
dryRun: { type: 'boolean', description: 'Preview without writing files (default: false)' },
|
|
1101
1224
|
id: { type: 'string', description: 'Sync only this specific decision ID (default: all approved)' },
|
|
1102
1225
|
},
|
|
@@ -1116,7 +1239,7 @@ const TOOL_ANNOTATIONS = {
|
|
|
1116
1239
|
orient: _RO, analyze_codebase: _RWI, get_architecture_overview: _RO,
|
|
1117
1240
|
get_refactor_report: _RO, get_call_graph: _RO, get_duplicate_report: _RO,
|
|
1118
1241
|
get_signatures: _RO, get_subgraph: _RO, trace_execution_path: _RO,
|
|
1119
|
-
get_mapping: _RO, check_spec_drift: _RO, analyze_impact: _RO,
|
|
1242
|
+
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
1243
|
get_low_risk_refactor_candidates: _RO, get_leaf_functions: _RO,
|
|
1121
1244
|
get_critical_hubs: _RO, get_function_skeleton: _RO, get_god_functions: _RO,
|
|
1122
1245
|
suggest_insertion_points: _RO, search_code: _RO, list_spec_domains: _RO,
|
|
@@ -1129,6 +1252,21 @@ const TOOL_ANNOTATIONS = {
|
|
|
1129
1252
|
detect_changes: _RO, record_decision: _RW, list_decisions: _RO,
|
|
1130
1253
|
approve_decision: _RWI, reject_decision: _RWI, sync_decisions: _RWI,
|
|
1131
1254
|
};
|
|
1255
|
+
// Tools that touch external entities (LLM / network) → openWorldHint: true.
|
|
1256
|
+
// Everything else is local, deterministic, closed-world analysis.
|
|
1257
|
+
const OPEN_WORLD_TOOLS = new Set(['generate_tests', 'generate_change_proposal', 'annotate_story']);
|
|
1258
|
+
/** Human-readable title from a snake_case tool name (spec-11 annotations). */
|
|
1259
|
+
function toolTitle(name) {
|
|
1260
|
+
return name.split('_').map(w => (w ? w[0].toUpperCase() + w.slice(1) : w)).join(' ');
|
|
1261
|
+
}
|
|
1262
|
+
/** Full MCP `annotations` for a tool: read/write hints + title + openWorldHint (spec-11). */
|
|
1263
|
+
export function toolAnnotations(name) {
|
|
1264
|
+
return {
|
|
1265
|
+
title: toolTitle(name),
|
|
1266
|
+
...(TOOL_ANNOTATIONS[name] ?? _RO),
|
|
1267
|
+
openWorldHint: OPEN_WORLD_TOOLS.has(name),
|
|
1268
|
+
};
|
|
1269
|
+
}
|
|
1132
1270
|
const MINIMAL_TOOLS = new Set([
|
|
1133
1271
|
'orient', 'search_code', 'record_decision', 'detect_changes', 'check_spec_drift',
|
|
1134
1272
|
]);
|
|
@@ -1184,7 +1322,7 @@ async function startMcpServer(options = {}) {
|
|
|
1184
1322
|
const { version: pkgVersion } = _require('../../../package.json');
|
|
1185
1323
|
const server = new Server({ name: 'openlore', version: pkgVersion }, { capabilities: { tools: {} } });
|
|
1186
1324
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
1187
|
-
tools: activeTools.map(t => ({ ...t, annotations:
|
|
1325
|
+
tools: activeTools.map(t => ({ ...t, annotations: toolAnnotations(t.name) })),
|
|
1188
1326
|
}));
|
|
1189
1327
|
// Per-session epistemic lease tracker — re-initialized when directory changes.
|
|
1190
1328
|
let tracker;
|
|
@@ -1197,8 +1335,16 @@ async function startMcpServer(options = {}) {
|
|
|
1197
1335
|
server.setRequestHandler(InitializeRequestSchema, async (request) => {
|
|
1198
1336
|
agentName = request.params.clientInfo?.name ?? 'unknown';
|
|
1199
1337
|
agentVersion = request.params.clientInfo?.version ?? 'unknown';
|
|
1338
|
+
// Protocol negotiation (spec-12): echo the client's requested version when we
|
|
1339
|
+
// support it (per the SDK's pinned set), else offer our latest supported one.
|
|
1340
|
+
const requested = request.params.protocolVersion;
|
|
1341
|
+
const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.includes(requested)
|
|
1342
|
+
? requested
|
|
1343
|
+
: LATEST_PROTOCOL_VERSION;
|
|
1200
1344
|
return {
|
|
1201
|
-
protocolVersion
|
|
1345
|
+
protocolVersion,
|
|
1346
|
+
// Honest capabilities: only `tools`. No `listChanged` — the tool list is
|
|
1347
|
+
// static per session, so we don't advertise a capability we don't implement.
|
|
1202
1348
|
capabilities: { tools: {} },
|
|
1203
1349
|
serverInfo: { name: 'openlore', version: _pkgVersion },
|
|
1204
1350
|
};
|
|
@@ -1225,6 +1371,17 @@ async function startMcpServer(options = {}) {
|
|
|
1225
1371
|
const _dir = args.directory;
|
|
1226
1372
|
const directory = typeof _dir === 'string' ? _dir : '';
|
|
1227
1373
|
const _t0 = Date.now();
|
|
1374
|
+
// Input validation (spec-10) against the tool's own declared inputSchema, before
|
|
1375
|
+
// dispatch. Invalid args become a JSON-RPC -32602 error (spec-12), not an
|
|
1376
|
+
// isError tool result — a malformed request is a protocol error, not a tool failure.
|
|
1377
|
+
{
|
|
1378
|
+
const toolDef = TOOL_DEFINITIONS.find(t => t.name === name);
|
|
1379
|
+
const argError = toolDef ? validateToolArgs(args, toolDef.inputSchema) : null;
|
|
1380
|
+
if (argError) {
|
|
1381
|
+
emit(directory, 'mcp', { event: 'tool_error', tool: name, ms: Date.now() - _t0, agent: agentName, code: 'INVALID_ARGS', error: argError });
|
|
1382
|
+
throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for "${name}": ${argError}`);
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1228
1385
|
try {
|
|
1229
1386
|
const filePath = args.filePath;
|
|
1230
1387
|
// Init (or re-init when project directory changes between calls)
|
|
@@ -1236,205 +1393,240 @@ async function startMcpServer(options = {}) {
|
|
|
1236
1393
|
if (tracker && directory)
|
|
1237
1394
|
updateTracker(tracker, name, directory, typeof filePath === 'string' ? filePath : undefined);
|
|
1238
1395
|
let result;
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1396
|
+
let _unknownTool = false;
|
|
1397
|
+
// Per-tool timeout (spec-10): race the dispatch against the tool's budget so a
|
|
1398
|
+
// pathological hang can never wedge the server. Slow tools (analysis, LLM) have
|
|
1399
|
+
// generous overrides in MCP_TOOL_TIMEOUT_OVERRIDES.
|
|
1400
|
+
await withToolTimeout((async () => {
|
|
1401
|
+
if (name === 'orient') {
|
|
1402
|
+
const { task, limit = 5, tokenBudget, lean } = args;
|
|
1403
|
+
result = await handleOrient(directory, task, limit, tokenBudget, lean);
|
|
1404
|
+
if (result && typeof result === 'object') {
|
|
1405
|
+
const r = result;
|
|
1406
|
+
emit(directory, 'orient', {
|
|
1407
|
+
event: 'orient_call',
|
|
1408
|
+
agent: agentName,
|
|
1409
|
+
functions: Array.isArray(r['relevantFunctions']) ? r['relevantFunctions'].length : 0,
|
|
1410
|
+
files: Array.isArray(r['relevantFiles']) ? r['relevantFiles'].length : 0,
|
|
1411
|
+
spec_domains: Array.isArray(r['specDomains']) ? r['specDomains'].length : 0,
|
|
1412
|
+
insertion_points: Array.isArray(r['insertionPoints']) ? r['insertionPoints'].length : 0,
|
|
1413
|
+
});
|
|
1414
|
+
}
|
|
1252
1415
|
}
|
|
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
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1416
|
+
else if (name === 'analyze_codebase') {
|
|
1417
|
+
const { directory, force = false } = args;
|
|
1418
|
+
result = await handleAnalyzeCodebase(directory, force);
|
|
1419
|
+
}
|
|
1420
|
+
else if (name === 'get_architecture_overview') {
|
|
1421
|
+
const { directory } = args;
|
|
1422
|
+
result = await handleGetArchitectureOverview(directory);
|
|
1423
|
+
}
|
|
1424
|
+
else if (name === 'get_refactor_report') {
|
|
1425
|
+
const { directory } = args;
|
|
1426
|
+
result = await handleGetRefactorReport(directory);
|
|
1427
|
+
}
|
|
1428
|
+
else if (name === 'get_call_graph') {
|
|
1429
|
+
const { directory } = args;
|
|
1430
|
+
result = await handleGetCallGraph(directory);
|
|
1431
|
+
}
|
|
1432
|
+
else if (name === 'get_signatures') {
|
|
1433
|
+
const { directory, filePattern } = args;
|
|
1434
|
+
result = await handleGetSignatures(directory, filePattern);
|
|
1435
|
+
}
|
|
1436
|
+
else if (name === 'get_subgraph') {
|
|
1437
|
+
const { directory, functionName, direction = 'downstream', maxDepth = 3, format = 'json' } = args;
|
|
1438
|
+
result = await handleGetSubgraph(directory, functionName, direction, maxDepth, format);
|
|
1439
|
+
}
|
|
1440
|
+
else if (name === 'trace_execution_path') {
|
|
1441
|
+
const { directory, entryFunction, targetFunction, maxDepth = 6, maxPaths = 10 } = args;
|
|
1442
|
+
result = await handleTraceExecutionPath(directory, entryFunction, targetFunction, maxDepth, maxPaths);
|
|
1443
|
+
}
|
|
1444
|
+
else if (name === 'get_mapping') {
|
|
1445
|
+
const { directory, domain, orphansOnly } = args;
|
|
1446
|
+
result = await handleGetMapping(directory, domain, orphansOnly);
|
|
1447
|
+
}
|
|
1448
|
+
else if (name === 'analyze_impact') {
|
|
1449
|
+
const { directory, symbol, depth = 2 } = args;
|
|
1450
|
+
result = await handleAnalyzeImpact(directory, symbol, depth);
|
|
1451
|
+
}
|
|
1452
|
+
else if (name === 'select_tests') {
|
|
1453
|
+
const { directory, changedSymbols, diffRef, maxDepth } = args;
|
|
1454
|
+
result = await handleSelectTests({ directory, changedSymbols, diffRef, maxDepth });
|
|
1455
|
+
}
|
|
1456
|
+
else if (name === 'find_dead_code') {
|
|
1457
|
+
const { directory, ifDeleted, maxResults, filePattern } = args;
|
|
1458
|
+
result = await handleFindDeadCode({ directory, ifDeleted, maxResults, filePattern });
|
|
1459
|
+
}
|
|
1460
|
+
else if (name === 'structural_diff') {
|
|
1461
|
+
const { directory, baseRef, headRef, maxResults } = args;
|
|
1462
|
+
result = await handleStructuralDiff({ directory, baseRef, headRef, maxResults });
|
|
1463
|
+
}
|
|
1464
|
+
else if (name === 'get_change_coupling') {
|
|
1465
|
+
const { directory, file, limit } = args;
|
|
1466
|
+
result = await handleGetChangeCoupling({ directory, file, limit });
|
|
1467
|
+
}
|
|
1468
|
+
else if (name === 'check_architecture') {
|
|
1469
|
+
const { directory, from, to } = args;
|
|
1470
|
+
result = await handleCheckArchitecture({ directory, from, to });
|
|
1471
|
+
}
|
|
1472
|
+
else if (name === 'get_low_risk_refactor_candidates') {
|
|
1473
|
+
const { directory, limit = 5, filePattern } = args;
|
|
1474
|
+
result = await handleGetLowRiskRefactorCandidates(directory, limit, filePattern);
|
|
1475
|
+
}
|
|
1476
|
+
else if (name === 'get_leaf_functions') {
|
|
1477
|
+
const { directory, limit = 20, filePattern, sortBy = 'fanIn' } = args;
|
|
1478
|
+
result = await handleGetLeafFunctions(directory, limit, filePattern, sortBy);
|
|
1479
|
+
}
|
|
1480
|
+
else if (name === 'get_critical_hubs') {
|
|
1481
|
+
const { directory, limit = 10, minFanIn = 3 } = args;
|
|
1482
|
+
result = await handleGetCriticalHubs(directory, limit, minFanIn);
|
|
1483
|
+
}
|
|
1484
|
+
else if (name === 'get_duplicate_report') {
|
|
1485
|
+
const { directory } = args;
|
|
1486
|
+
result = await handleGetDuplicateReport(directory);
|
|
1487
|
+
}
|
|
1488
|
+
else if (name === 'get_function_skeleton') {
|
|
1489
|
+
const { directory, filePath } = args;
|
|
1490
|
+
result = await handleGetFunctionSkeleton(directory, filePath);
|
|
1491
|
+
}
|
|
1492
|
+
else if (name === 'get_god_functions') {
|
|
1493
|
+
const { directory, filePath, fanOutThreshold = 8 } = args;
|
|
1494
|
+
result = await handleGetGodFunctions(directory, filePath, fanOutThreshold);
|
|
1495
|
+
}
|
|
1496
|
+
else if (name === 'check_spec_drift') {
|
|
1497
|
+
const { directory, base = 'auto', files = [], domains = [], failOn = 'warning', maxFiles = DEFAULT_DRIFT_MAX_FILES } = args;
|
|
1498
|
+
result = await handleCheckSpecDrift(directory, base, files, domains, failOn, maxFiles);
|
|
1499
|
+
}
|
|
1500
|
+
else if (name === 'search_code') {
|
|
1501
|
+
const { directory, query, limit = 10, language, minFanIn, tokenBudget } = args;
|
|
1502
|
+
result = await handleSearchCode(directory, query, limit, language, minFanIn, tokenBudget);
|
|
1503
|
+
}
|
|
1504
|
+
else if (name === 'suggest_insertion_points') {
|
|
1505
|
+
const { directory, description, limit = 5, language } = args;
|
|
1506
|
+
result = await handleSuggestInsertionPoints(directory, description, limit, language);
|
|
1507
|
+
}
|
|
1508
|
+
else if (name === 'search_specs') {
|
|
1509
|
+
const { directory, query, limit = 10, domain, section } = args;
|
|
1510
|
+
result = await handleSearchSpecs(directory, query, limit, domain, section);
|
|
1511
|
+
}
|
|
1512
|
+
else if (name === 'search_unified') {
|
|
1513
|
+
const { directory, query, limit = 10, language, domain, section } = args;
|
|
1514
|
+
result = await handleUnifiedSearch(directory, query, limit, language, domain, section);
|
|
1515
|
+
}
|
|
1516
|
+
else if (name === 'list_spec_domains') {
|
|
1517
|
+
const { directory } = args;
|
|
1518
|
+
result = await handleListSpecDomains(directory);
|
|
1519
|
+
}
|
|
1520
|
+
else if (name === 'get_spec') {
|
|
1521
|
+
const { directory, domain } = args;
|
|
1522
|
+
result = await handleGetSpec(directory, domain);
|
|
1523
|
+
}
|
|
1524
|
+
else if (name === 'get_function_body') {
|
|
1525
|
+
const { directory, filePath, functionName } = args;
|
|
1526
|
+
result = await handleGetFunctionBody(directory, filePath, functionName);
|
|
1527
|
+
}
|
|
1528
|
+
else if (name === 'get_file_dependencies') {
|
|
1529
|
+
const { directory, filePath, direction = 'both' } = args;
|
|
1530
|
+
result = await handleGetFileDependencies(directory, filePath, direction);
|
|
1531
|
+
}
|
|
1532
|
+
else if (name === 'generate_change_proposal') {
|
|
1533
|
+
const { directory, description, slug, storyContent } = args;
|
|
1534
|
+
result = await handleGenerateChangeProposal(directory, description, slug, storyContent);
|
|
1535
|
+
}
|
|
1536
|
+
else if (name === 'annotate_story') {
|
|
1537
|
+
const { directory, storyFilePath, description } = args;
|
|
1538
|
+
result = await handleAnnotateStory(directory, storyFilePath, description);
|
|
1539
|
+
}
|
|
1540
|
+
else if (name === 'get_decisions') {
|
|
1541
|
+
const { directory, query } = args;
|
|
1542
|
+
result = await handleGetDecisions(directory, query);
|
|
1543
|
+
}
|
|
1544
|
+
else if (name === 'get_route_inventory') {
|
|
1545
|
+
const { directory } = args;
|
|
1546
|
+
result = await handleGetRouteInventory(directory);
|
|
1547
|
+
}
|
|
1548
|
+
else if (name === 'get_middleware_inventory') {
|
|
1549
|
+
const { directory } = args;
|
|
1550
|
+
result = await handleGetMiddlewareInventory(directory);
|
|
1551
|
+
}
|
|
1552
|
+
else if (name === 'get_schema_inventory') {
|
|
1553
|
+
const { directory } = args;
|
|
1554
|
+
result = await handleGetSchemaInventory(directory);
|
|
1555
|
+
}
|
|
1556
|
+
else if (name === 'get_ui_components') {
|
|
1557
|
+
const { directory } = args;
|
|
1558
|
+
result = await handleGetUIComponents(directory);
|
|
1559
|
+
}
|
|
1560
|
+
else if (name === 'get_env_vars') {
|
|
1561
|
+
const { directory } = args;
|
|
1562
|
+
result = await handleGetEnvVars(directory);
|
|
1563
|
+
}
|
|
1564
|
+
else if (name === 'get_external_packages') {
|
|
1565
|
+
const { directory } = args;
|
|
1566
|
+
result = await handleGetExternalPackages(directory);
|
|
1567
|
+
}
|
|
1568
|
+
else if (name === 'audit_spec_coverage') {
|
|
1569
|
+
const { directory, maxUncovered = 50, hubThreshold = 5 } = args;
|
|
1570
|
+
result = await handleAuditSpecCoverage(directory, maxUncovered, hubThreshold);
|
|
1571
|
+
}
|
|
1572
|
+
else if (name === 'generate_tests') {
|
|
1573
|
+
const { directory, domains, framework, useLlm, dryRun } = args;
|
|
1574
|
+
result = await handleGenerateTests({ directory, domains, framework, useLlm, dryRun });
|
|
1575
|
+
}
|
|
1576
|
+
else if (name === 'get_test_coverage') {
|
|
1577
|
+
const { directory, domains, minCoverage } = args;
|
|
1578
|
+
result = await handleGetTestCoverage({ directory, domains, minCoverage });
|
|
1579
|
+
}
|
|
1580
|
+
else if (name === 'get_minimal_context') {
|
|
1581
|
+
const { directory, functionName, filePath } = args;
|
|
1582
|
+
result = await handleGetMinimalContext(directory, functionName, filePath);
|
|
1583
|
+
}
|
|
1584
|
+
else if (name === 'get_cluster') {
|
|
1585
|
+
const { directory, functionName } = args;
|
|
1586
|
+
result = await handleGetCluster(directory, functionName);
|
|
1587
|
+
}
|
|
1588
|
+
else if (name === 'detect_changes') {
|
|
1589
|
+
const { directory, base } = args;
|
|
1590
|
+
result = await handleDetectChanges(directory, base);
|
|
1591
|
+
}
|
|
1592
|
+
else if (name === 'record_decision') {
|
|
1593
|
+
const { directory, title, rationale, consequences, affectedFiles, supersedes, scope } = args;
|
|
1594
|
+
result = await handleRecordDecision(directory, title, rationale, consequences, affectedFiles, supersedes, scope);
|
|
1595
|
+
}
|
|
1596
|
+
else if (name === 'list_decisions') {
|
|
1597
|
+
const { directory, status } = args;
|
|
1598
|
+
result = await handleListDecisions(directory, status);
|
|
1599
|
+
}
|
|
1600
|
+
else if (name === 'approve_decision') {
|
|
1601
|
+
const { directory, id, note } = args;
|
|
1602
|
+
result = await handleApproveDecision(directory, id, note);
|
|
1603
|
+
}
|
|
1604
|
+
else if (name === 'reject_decision') {
|
|
1605
|
+
const { directory, id, note } = args;
|
|
1606
|
+
result = await handleRejectDecision(directory, id, note);
|
|
1607
|
+
}
|
|
1608
|
+
else if (name === 'sync_decisions') {
|
|
1609
|
+
const { directory, dryRun = false, id } = args;
|
|
1610
|
+
result = await handleSyncDecisions(directory, dryRun, id);
|
|
1611
|
+
}
|
|
1612
|
+
else {
|
|
1613
|
+
_unknownTool = true;
|
|
1614
|
+
}
|
|
1615
|
+
})(), name);
|
|
1616
|
+
if (_unknownTool) {
|
|
1431
1617
|
return {
|
|
1432
1618
|
content: [{ type: 'text', text: `Unknown tool: ${name}` }],
|
|
1433
1619
|
isError: true,
|
|
1434
1620
|
};
|
|
1435
1621
|
}
|
|
1436
|
-
|
|
1437
|
-
|
|
1622
|
+
const rawText = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
|
|
1623
|
+
// Output cap (spec-10): truncate deterministically with a how-to-narrow note
|
|
1624
|
+
// rather than silently dropping data or blowing the agent's context.
|
|
1625
|
+
const { text, truncated } = capOutput(rawText, MCP_TOOL_MAX_BYTES);
|
|
1626
|
+
emit(directory, 'mcp', {
|
|
1627
|
+
event: 'tool_call', tool: name, ms: Date.now() - _t0, agent: agentName, agent_version: agentVersion,
|
|
1628
|
+
bytes: Buffer.byteLength(text, 'utf8'), outcome: truncated ? 'truncated' : 'ok',
|
|
1629
|
+
});
|
|
1438
1630
|
const signal = tracker ? getFreshnessSignal(tracker) : null;
|
|
1439
1631
|
// Freshness signal is a separate content item — never concatenated into
|
|
1440
1632
|
// the result body — so structured outputs (JSON, patches) are not corrupted.
|
|
@@ -1446,9 +1638,17 @@ async function startMcpServer(options = {}) {
|
|
|
1446
1638
|
return { content };
|
|
1447
1639
|
}
|
|
1448
1640
|
catch (err) {
|
|
1449
|
-
|
|
1641
|
+
// A thrown McpError is a protocol-level error (e.g. -32602) — let the SDK
|
|
1642
|
+
// serialize it as a JSON-RPC error response, not a tool isError result.
|
|
1643
|
+
if (err instanceof McpError)
|
|
1644
|
+
throw err;
|
|
1645
|
+
// Error normalization (spec-10): a stable code taxonomy, distinguishing
|
|
1646
|
+
// "repo not analyzed yet" (actionable) from real failures and timeouts.
|
|
1647
|
+
const code = classifyToolError(err);
|
|
1648
|
+
const message = sanitizeMcpError(err);
|
|
1649
|
+
emit(directory, 'mcp', { event: 'tool_error', tool: name, ms: Date.now() - _t0, agent: agentName, code, outcome: 'error', error: message });
|
|
1450
1650
|
return {
|
|
1451
|
-
content: [{ type: 'text', text: `Tool error: ${
|
|
1651
|
+
content: [{ type: 'text', text: `Tool error [${code}]: ${message}` }],
|
|
1452
1652
|
isError: true,
|
|
1453
1653
|
};
|
|
1454
1654
|
}
|