grepmax 0.26.44 → 0.26.46
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/.claude-plugin/marketplace.json +1 -1
- package/README.md +4 -0
- package/dist/commands/mcp.js +647 -547
- package/dist/lib/output/mcp-results.js +177 -0
- package/package.json +13 -11
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
package/dist/commands/mcp.js
CHANGED
|
@@ -69,8 +69,8 @@ exports.formatMcpSurprisingConnections = formatMcpSurprisingConnections;
|
|
|
69
69
|
exports.mcpLogQuery = mcpLogQuery;
|
|
70
70
|
const fs = __importStar(require("node:fs"));
|
|
71
71
|
const path = __importStar(require("node:path"));
|
|
72
|
-
const
|
|
73
|
-
const
|
|
72
|
+
const server_1 = require("@modelcontextprotocol/server");
|
|
73
|
+
const stdio_1 = require("@modelcontextprotocol/server/stdio");
|
|
74
74
|
const commander_1 = require("commander");
|
|
75
75
|
const zod_1 = require("zod");
|
|
76
76
|
const config_1 = require("../config");
|
|
@@ -85,6 +85,7 @@ const index_config_1 = require("../lib/index/index-config");
|
|
|
85
85
|
const syncer_1 = require("../lib/index/syncer");
|
|
86
86
|
const agent_search_formatter_1 = require("../lib/output/agent-search-formatter");
|
|
87
87
|
const index_state_footer_1 = require("../lib/output/index-state-footer");
|
|
88
|
+
const mcp_results_1 = require("../lib/output/mcp-results");
|
|
88
89
|
const searcher_1 = require("../lib/search/searcher");
|
|
89
90
|
const annotator_1 = require("../lib/skeleton/annotator");
|
|
90
91
|
const skeletonizer_1 = require("../lib/skeleton/skeletonizer");
|
|
@@ -123,8 +124,8 @@ function toStringArray(val) {
|
|
|
123
124
|
}
|
|
124
125
|
return [];
|
|
125
126
|
}
|
|
126
|
-
function ok(text) {
|
|
127
|
-
return { content: [{ type: "text", text }] };
|
|
127
|
+
function ok(text, structuredContent) {
|
|
128
|
+
return Object.assign({ content: [{ type: "text", text }] }, (structuredContent ? { structuredContent } : {}));
|
|
128
129
|
}
|
|
129
130
|
function err(text) {
|
|
130
131
|
return { content: [{ type: "text", text }], isError: true };
|
|
@@ -256,13 +257,13 @@ function searchResultPath(r) {
|
|
|
256
257
|
}
|
|
257
258
|
function searchResultStartLine(r) {
|
|
258
259
|
var _a;
|
|
259
|
-
var _b, _c;
|
|
260
|
-
return Number((_c = (_b = r === null || r === void 0 ? void 0 : r.
|
|
260
|
+
var _b, _c, _d;
|
|
261
|
+
return Number((_d = (_c = (_b = r === null || r === void 0 ? void 0 : r.startLine) !== null && _b !== void 0 ? _b : r === null || r === void 0 ? void 0 : r.start_line) !== null && _c !== void 0 ? _c : (_a = r === null || r === void 0 ? void 0 : r.generated_metadata) === null || _a === void 0 ? void 0 : _a.start_line) !== null && _d !== void 0 ? _d : 0);
|
|
261
262
|
}
|
|
262
263
|
function searchResultEndLine(r, fallbackStart = 0) {
|
|
263
264
|
var _a;
|
|
264
|
-
var _b, _c;
|
|
265
|
-
return Number((_c = (_b = r === null || r === void 0 ? void 0 : r.
|
|
265
|
+
var _b, _c, _d;
|
|
266
|
+
return Number((_d = (_c = (_b = r === null || r === void 0 ? void 0 : r.endLine) !== null && _b !== void 0 ? _b : r === null || r === void 0 ? void 0 : r.end_line) !== null && _c !== void 0 ? _c : (_a = r === null || r === void 0 ? void 0 : r.generated_metadata) === null || _a === void 0 ? void 0 : _a.end_line) !== null && _d !== void 0 ? _d : fallbackStart);
|
|
266
267
|
}
|
|
267
268
|
function filterMcpSearchResults(data, options = {}) {
|
|
268
269
|
var _a, _b;
|
|
@@ -373,12 +374,22 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
373
374
|
// an MCP session holds no reader marker under ~/.gmax/lancedb.lease/readers/
|
|
374
375
|
// while a daemon serving the verbs is up.
|
|
375
376
|
let _skeletonizer = null;
|
|
376
|
-
let
|
|
377
|
-
|
|
378
|
-
const
|
|
377
|
+
let stdio;
|
|
378
|
+
let watchTimer;
|
|
379
|
+
const watchedRoots = new Set();
|
|
380
|
+
let closing = false;
|
|
381
|
+
// Close the SDK connection before exit so pending requests are aborted.
|
|
382
|
+
const exit = () => {
|
|
383
|
+
if (closing)
|
|
384
|
+
return;
|
|
385
|
+
closing = true;
|
|
386
|
+
clearInterval(watchTimer);
|
|
387
|
+
setTimeout(() => process.exit(0), 1000).unref();
|
|
388
|
+
void Promise.resolve(stdio === null || stdio === void 0 ? void 0 : stdio.close()).finally(() => process.exit(0));
|
|
389
|
+
};
|
|
379
390
|
process.on("SIGINT", exit);
|
|
380
391
|
process.on("SIGTERM", exit);
|
|
381
|
-
//
|
|
392
|
+
// Exit promptly on disconnect, including sessions with a lease timer.
|
|
382
393
|
process.stdin.on("end", exit);
|
|
383
394
|
process.stdin.on("close", exit);
|
|
384
395
|
process.on("unhandledRejection", (reason, promise) => {
|
|
@@ -394,7 +405,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
394
405
|
console.debug = (..._args) => { };
|
|
395
406
|
// --- Project context ---
|
|
396
407
|
const projectRoot = (0, project_root_1.findProjectRoot)(process.cwd());
|
|
397
|
-
const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
|
|
408
|
+
const paths = (0, project_root_1.ensureProjectPaths)(projectRoot, { dryRun: true });
|
|
398
409
|
const currentRoot = () => { var _a; var _b; return (_b = (_a = (0, store_context_1.currentStoreContext)()) === null || _a === void 0 ? void 0 : _a.root) !== null && _b !== void 0 ? _b : projectRoot; };
|
|
399
410
|
const storeProjects = () => { var _a; var _b; return (_b = (_a = (0, store_context_1.currentStoreContext)()) === null || _a === void 0 ? void 0 : _a.projects) !== null && _b !== void 0 ? _b : (0, project_registry_1.listProjects)(); };
|
|
400
411
|
const storeConfig = () => { var _a; var _b; return (_b = (_a = (0, store_context_1.currentStoreContext)()) === null || _a === void 0 ? void 0 : _a.config) !== null && _b !== void 0 ? _b : (0, index_config_1.readGlobalConfig)(); };
|
|
@@ -597,18 +608,6 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
597
608
|
return _skeletonizer;
|
|
598
609
|
});
|
|
599
610
|
}
|
|
600
|
-
// --- Index sync ---
|
|
601
|
-
function ensureIndexReady() {
|
|
602
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
603
|
-
if (_indexReady)
|
|
604
|
-
return;
|
|
605
|
-
const projects = storeProjects();
|
|
606
|
-
const isRegistered = projects.some((p) => p.root === currentRoot());
|
|
607
|
-
if (isRegistered) {
|
|
608
|
-
_indexReady = true;
|
|
609
|
-
}
|
|
610
|
-
});
|
|
611
|
-
}
|
|
612
611
|
// --- Background watcher ---
|
|
613
612
|
// This server's watch lease. It names our PID, so the daemon drops it the
|
|
614
613
|
// minute this session exits; the TTL only covers a wedged server.
|
|
@@ -627,6 +626,17 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
627
626
|
(0, stores_1.matchStore)(watchRoot).kind !== "primary")
|
|
628
627
|
return;
|
|
629
628
|
const result = yield (0, watcher_launcher_1.launchWatcher)(watchRoot, WATCH_LEASE);
|
|
629
|
+
if (closing)
|
|
630
|
+
return;
|
|
631
|
+
if (result.ok) {
|
|
632
|
+
watchedRoots.add(watchRoot);
|
|
633
|
+
// Discovery/catalog requests never reach this path. Renew only roots
|
|
634
|
+
// actually read by this session, with one timer for the whole process.
|
|
635
|
+
watchTimer !== null && watchTimer !== void 0 ? watchTimer : (watchTimer = setInterval(() => {
|
|
636
|
+
for (const root of watchedRoots)
|
|
637
|
+
void ensureWatcher(root);
|
|
638
|
+
}, MCP_WATCH_LEASE_RENEW_MS).unref());
|
|
639
|
+
}
|
|
630
640
|
if (result.ok && !result.reused) {
|
|
631
641
|
console.log(`[MCP] Started background watcher for ${watchRoot} (PID: ${result.pid})`);
|
|
632
642
|
}
|
|
@@ -772,6 +782,26 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
772
782
|
return (candidate.length > 0 &&
|
|
773
783
|
allowedRoots.some((root) => (0, path_containment_1.isPathWithin)(root, candidate)));
|
|
774
784
|
});
|
|
785
|
+
const structuredSearch = (matches) => { var _a; return ({
|
|
786
|
+
schemaVersion: 1,
|
|
787
|
+
query,
|
|
788
|
+
roots: allowedRoots,
|
|
789
|
+
warnings: ((_a = result.warnings) !== null && _a !== void 0 ? _a : []).filter(Boolean),
|
|
790
|
+
matches: matches.map((r) => {
|
|
791
|
+
var _a;
|
|
792
|
+
const startLine = searchResultStartLine(r);
|
|
793
|
+
return {
|
|
794
|
+
path: chunkAbsPath(r),
|
|
795
|
+
startLine: startLine + 1,
|
|
796
|
+
endLine: searchResultEndLine(r, startLine) + 1,
|
|
797
|
+
symbols: chunkSymbols(r),
|
|
798
|
+
role: String((_a = r.role) !== null && _a !== void 0 ? _a : "IMPLEMENTATION"),
|
|
799
|
+
score: typeof r.score === "number" && Number.isFinite(r.score)
|
|
800
|
+
? r.score
|
|
801
|
+
: null,
|
|
802
|
+
};
|
|
803
|
+
}),
|
|
804
|
+
}); };
|
|
775
805
|
// Prepend any searcher warnings to whatever body we return.
|
|
776
806
|
const prefixNotes = (body) => {
|
|
777
807
|
var _a;
|
|
@@ -779,7 +809,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
779
809
|
return notes.length ? `${notes.join("\n")}\n\n${body}` : body;
|
|
780
810
|
};
|
|
781
811
|
if (!result.data || result.data.length === 0) {
|
|
782
|
-
return ok(prefixNotes("No matches found. Try broadening your query, using fewer keywords, or check `gmax status` to verify the project is indexed."));
|
|
812
|
+
return ok(prefixNotes("No matches found. Try broadening your query, using fewer keywords, or check `gmax status` to verify the project is indexed."), structuredSearch([]));
|
|
783
813
|
}
|
|
784
814
|
const minScore = typeof args.min_score === "number" ? args.min_score : 0;
|
|
785
815
|
const maxPerFile = typeof args.max_per_file === "number" ? args.max_per_file : 0;
|
|
@@ -809,21 +839,25 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
809
839
|
getImportsForFile,
|
|
810
840
|
query,
|
|
811
841
|
});
|
|
812
|
-
return ok(prefixNotes(output)
|
|
842
|
+
return ok(prefixNotes(output), structuredSearch(filterMcpSearchResults(result.data, {
|
|
843
|
+
minScore,
|
|
844
|
+
maxPerFile,
|
|
845
|
+
namePattern,
|
|
846
|
+
})));
|
|
813
847
|
}
|
|
814
848
|
let results = result.data.map((r) => {
|
|
815
|
-
var _a
|
|
816
|
-
var
|
|
817
|
-
const absPath = (
|
|
849
|
+
var _a;
|
|
850
|
+
var _b, _c, _d, _e, _f;
|
|
851
|
+
const absPath = (_c = (_b = r.path) !== null && _b !== void 0 ? _b : (_a = r.metadata) === null || _a === void 0 ? void 0 : _a.path) !== null && _c !== void 0 ? _c : "";
|
|
818
852
|
const relPath = absPath.startsWith(displayRoot)
|
|
819
853
|
? absPath.slice(displayRoot.length + 1)
|
|
820
854
|
: absPath;
|
|
821
|
-
const startLine = (
|
|
822
|
-
const endLine = (
|
|
823
|
-
const defs = toStringArray((
|
|
824
|
-
const refs = toStringArray((
|
|
855
|
+
const startLine = searchResultStartLine(r);
|
|
856
|
+
const endLine = searchResultEndLine(r, startLine);
|
|
857
|
+
const defs = toStringArray((_d = r.definedSymbols) !== null && _d !== void 0 ? _d : r.defined_symbols);
|
|
858
|
+
const refs = toStringArray((_e = r.referenced_symbols) !== null && _e !== void 0 ? _e : r.referencedSymbols);
|
|
825
859
|
const symbol = defs[0] || "(anonymous)";
|
|
826
|
-
const role = ((
|
|
860
|
+
const role = ((_f = r.role) !== null && _f !== void 0 ? _f : "IMPL").slice(0, 4).toUpperCase();
|
|
827
861
|
const exported = r.is_exported ? "exported " : "";
|
|
828
862
|
const complexity = typeof r.complexity === "number" && r.complexity > 0
|
|
829
863
|
? ` C:${Math.round(r.complexity)}`
|
|
@@ -855,7 +889,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
855
889
|
.map((l, i) => `${ctxStart + i + 1}│${l}`)
|
|
856
890
|
.join("\n");
|
|
857
891
|
}
|
|
858
|
-
catch (
|
|
892
|
+
catch (_g) {
|
|
859
893
|
// Fall through to chunk content
|
|
860
894
|
}
|
|
861
895
|
}
|
|
@@ -885,6 +919,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
885
919
|
}
|
|
886
920
|
}
|
|
887
921
|
return {
|
|
922
|
+
row: r,
|
|
888
923
|
absPath,
|
|
889
924
|
text,
|
|
890
925
|
score: typeof r.score === "number" ? r.score : 0,
|
|
@@ -957,7 +992,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
957
992
|
// Trace failed — return search results without trace
|
|
958
993
|
}
|
|
959
994
|
}
|
|
960
|
-
return ok(prefixNotes(output));
|
|
995
|
+
return ok(prefixNotes(output), structuredSearch(results.map((r) => r.row)));
|
|
961
996
|
}
|
|
962
997
|
catch (e) {
|
|
963
998
|
return toolError("Search failed", e);
|
|
@@ -1105,8 +1140,9 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1105
1140
|
render: (resp) => resp.graph,
|
|
1106
1141
|
inProcess: () => localStore((deps) => (0, graph_handler_1.runGraphTrace)(deps.vectorDb, { symbol, hops: depth, scope })),
|
|
1107
1142
|
});
|
|
1143
|
+
const structured = (0, mcp_results_1.traceResult)(symbol, root, graph);
|
|
1108
1144
|
if (!graph.center) {
|
|
1109
|
-
return ok(`Symbol '${symbol}' not found in the index. Check \`gmax status\` to see which projects are indexed, or try \`gmax search ${symbol}\` to find similar symbols
|
|
1145
|
+
return ok(`Symbol '${symbol}' not found in the index. Check \`gmax status\` to see which projects are indexed, or try \`gmax search ${symbol}\` to find similar symbols.`, structured);
|
|
1110
1146
|
}
|
|
1111
1147
|
const lines = [];
|
|
1112
1148
|
// Center
|
|
@@ -1165,7 +1201,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1165
1201
|
else {
|
|
1166
1202
|
lines.push("Calls: none");
|
|
1167
1203
|
}
|
|
1168
|
-
return ok(lines.join("\n"));
|
|
1204
|
+
return ok(lines.join("\n"), structured);
|
|
1169
1205
|
}
|
|
1170
1206
|
catch (e) {
|
|
1171
1207
|
return toolError("Trace failed", e);
|
|
@@ -1406,16 +1442,17 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1406
1442
|
render: (resp) => resp.dead,
|
|
1407
1443
|
inProcess: () => localStore((deps) => (0, graph_handler_1.runGraphDead)(deps.vectorDb, { symbol, scope })),
|
|
1408
1444
|
});
|
|
1445
|
+
const structured = (0, mcp_results_1.deadResult)(symbol, root, facts);
|
|
1409
1446
|
if (!facts.found) {
|
|
1410
|
-
return ok(`Symbol '${symbol}' not found in the index. Check \`gmax status\` to see which projects are indexed, or try \`gmax search ${symbol}\` to find similar symbols
|
|
1447
|
+
return ok(`Symbol '${symbol}' not found in the index. Check \`gmax status\` to see which projects are indexed, or try \`gmax search ${symbol}\` to find similar symbols.`, structured);
|
|
1411
1448
|
}
|
|
1412
1449
|
const rel = (p) => p.startsWith(root) ? p.slice(root.length + 1) : p;
|
|
1413
1450
|
const defLoc = `${rel(facts.defPath)}:${facts.defLine + 1}`;
|
|
1414
1451
|
if (facts.callerCount === 0) {
|
|
1415
1452
|
if (facts.isExported) {
|
|
1416
|
-
return ok(`PUBLIC EXPORT ${defLoc} defines ${symbol} — no internal callers found; check external usage
|
|
1453
|
+
return ok(`PUBLIC EXPORT ${defLoc} defines ${symbol} — no internal callers found; check external usage`, structured);
|
|
1417
1454
|
}
|
|
1418
|
-
return ok(`DEAD ${defLoc} defines ${symbol}
|
|
1455
|
+
return ok(`DEAD ${defLoc} defines ${symbol}`, structured);
|
|
1419
1456
|
}
|
|
1420
1457
|
const top = facts.topCallers;
|
|
1421
1458
|
const lines = [
|
|
@@ -1424,7 +1461,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1424
1461
|
for (const c of top) {
|
|
1425
1462
|
lines.push(` ${rel(c.file)}:${c.line + 1}`);
|
|
1426
1463
|
}
|
|
1427
|
-
return ok(lines.join("\n"));
|
|
1464
|
+
return ok(lines.join("\n"), structured);
|
|
1428
1465
|
}
|
|
1429
1466
|
catch (e) {
|
|
1430
1467
|
return toolError("Dead check failed", e);
|
|
@@ -1782,7 +1819,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1782
1819
|
}
|
|
1783
1820
|
function handleIndexStatus() {
|
|
1784
1821
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1785
|
-
var _a, _b;
|
|
1822
|
+
var _a, _b, _c, _d, _e;
|
|
1786
1823
|
try {
|
|
1787
1824
|
const globalConfig = storeConfig();
|
|
1788
1825
|
const projects = storeProjects();
|
|
@@ -1834,7 +1871,36 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1834
1871
|
(0, compaction_result_1.formatCompactionStatus)(daemonStatus.compaction),
|
|
1835
1872
|
`Projects: ${projects.length} indexed (call list_projects for names + per-project chunk counts)`,
|
|
1836
1873
|
].filter(Boolean);
|
|
1837
|
-
|
|
1874
|
+
const compaction = mcp_results_1.compactionSchema.safeParse(daemonStatus.compaction);
|
|
1875
|
+
return ok(lines.join("\n"), {
|
|
1876
|
+
schemaVersion: 1,
|
|
1877
|
+
root: currentRoot(),
|
|
1878
|
+
store: storeDir(),
|
|
1879
|
+
secondary: secondary(),
|
|
1880
|
+
chunks,
|
|
1881
|
+
files,
|
|
1882
|
+
projects: projects.length,
|
|
1883
|
+
embedding: {
|
|
1884
|
+
state: identity.state,
|
|
1885
|
+
configured: {
|
|
1886
|
+
tier: identity.configured.tier,
|
|
1887
|
+
vectorDim: identity.configured.vectorDim,
|
|
1888
|
+
fingerprint: identity.configured.fingerprint,
|
|
1889
|
+
},
|
|
1890
|
+
built: identity.built
|
|
1891
|
+
? {
|
|
1892
|
+
tier: identity.built.tier,
|
|
1893
|
+
vectorDim: identity.built.vectorDim,
|
|
1894
|
+
fingerprint: identity.built.fingerprint,
|
|
1895
|
+
}
|
|
1896
|
+
: null,
|
|
1897
|
+
},
|
|
1898
|
+
watcher: {
|
|
1899
|
+
status: (_d = (_c = daemonProject === null || daemonProject === void 0 ? void 0 : daemonProject.status) !== null && _c !== void 0 ? _c : watcher === null || watcher === void 0 ? void 0 : watcher.status) !== null && _d !== void 0 ? _d : (secondary() || !daemonStatus.ok ? "unobserved" : "not_running"),
|
|
1900
|
+
indexState: (_e = daemonProject === null || daemonProject === void 0 ? void 0 : daemonProject.indexState) !== null && _e !== void 0 ? _e : null,
|
|
1901
|
+
},
|
|
1902
|
+
compaction: compaction.success ? compaction.data : null,
|
|
1903
|
+
});
|
|
1838
1904
|
}
|
|
1839
1905
|
catch (e) {
|
|
1840
1906
|
return toolError("Status check failed", e);
|
|
@@ -2572,510 +2638,544 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
2572
2638
|
});
|
|
2573
2639
|
}
|
|
2574
2640
|
// --- MCP server setup ---
|
|
2575
|
-
//
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
"
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
const { logQuery } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/query-log")));
|
|
2604
|
-
const text = (_c = (_b = (_a = result.content) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.text) !== null && _c !== void 0 ? _c : "";
|
|
2605
|
-
const resultLines = text.split("\n").filter((l) => l.trim()).length;
|
|
2606
|
-
logQuery({
|
|
2607
|
-
ts: new Date().toISOString(),
|
|
2608
|
-
source: "mcp",
|
|
2609
|
-
tool: name,
|
|
2610
|
-
query: mcpLogQuery(name, toolArgs),
|
|
2611
|
-
project: currentRoot(),
|
|
2612
|
-
results: resultLines,
|
|
2613
|
-
ms: Date.now() - startMs,
|
|
2614
|
-
error: result.isError ? text.slice(0, 200) : undefined,
|
|
2615
|
-
});
|
|
2616
|
-
}
|
|
2617
|
-
catch (_d) { }
|
|
2618
|
-
});
|
|
2619
|
-
// Register a tool, wrapping its handler with timing + query logging. Zod raw
|
|
2620
|
-
// shapes give us free input validation (the SDK rejects calls that violate
|
|
2621
|
-
// the schema before the handler runs); handlers stay defensively coded.
|
|
2622
|
-
function tool(name, config, handler) {
|
|
2623
|
-
server.registerTool(name, Object.assign(Object.assign({}, config), { inputSchema: Object.assign({ root: zod_1.z.string().optional() }, config.inputSchema) }), (rawArgs) => __awaiter(this, void 0, void 0, function* () {
|
|
2624
|
-
let args = (rawArgs !== null && rawArgs !== void 0 ? rawArgs : {});
|
|
2625
|
-
const startMs = Date.now();
|
|
2626
|
-
let target = projectRoot;
|
|
2641
|
+
// One fresh catalog per pinned connection or disposable discovery probe.
|
|
2642
|
+
// Building it performs no daemon, model, store or watcher startup.
|
|
2643
|
+
function createServer() {
|
|
2644
|
+
const server = new server_1.McpServer({
|
|
2645
|
+
name: "gmax",
|
|
2646
|
+
version: JSON.parse(fs.readFileSync(path.join(__dirname, "../../package.json"), {
|
|
2647
|
+
encoding: "utf-8",
|
|
2648
|
+
})).version,
|
|
2649
|
+
}, {
|
|
2650
|
+
capabilities: {
|
|
2651
|
+
tools: {},
|
|
2652
|
+
},
|
|
2653
|
+
instructions: "gmax is a local semantic code-search index over one or more projects. " +
|
|
2654
|
+
"By default tools operate on the project at the server's working directory. " +
|
|
2655
|
+
"If you are NOT inside a specific repo, or want a different one: call " +
|
|
2656
|
+
'`list_projects` to see what\'s indexed, then pass `projects:"name"` (or ' +
|
|
2657
|
+
'`scope:"all"`) to `semantic_search`. When the working directory isn\'t an ' +
|
|
2658
|
+
"indexed project, `semantic_search` errors instead of searching everything " +
|
|
2659
|
+
"implicitly. Prefer `semantic_search` (detail:'pointer') for discovery, then " +
|
|
2660
|
+
"`extract_symbol`/`peek_symbol`/`code_skeleton` to read, and " +
|
|
2661
|
+
"`trace_calls`/`impact_analysis`/`find_tests` for call-graph questions. " +
|
|
2662
|
+
"Use `audit` and experimental `surprising_connections` for project orientation.",
|
|
2663
|
+
});
|
|
2664
|
+
// Best-effort query logging, applied uniformly to every tool exactly as the
|
|
2665
|
+
// old single CallToolRequestSchema dispatch did before each return.
|
|
2666
|
+
const logToolCall = (name, toolArgs, startMs, result) => __awaiter(this, void 0, void 0, function* () {
|
|
2667
|
+
var _a, _b;
|
|
2668
|
+
var _c;
|
|
2627
2669
|
try {
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2670
|
+
const { logQuery } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/query-log")));
|
|
2671
|
+
const text = (_c = (_b = (_a = result.content) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.text) !== null && _c !== void 0 ? _c : "";
|
|
2672
|
+
const resultLines = text.split("\n").filter((l) => l.trim()).length;
|
|
2673
|
+
logQuery({
|
|
2674
|
+
ts: new Date().toISOString(),
|
|
2675
|
+
source: "mcp",
|
|
2676
|
+
tool: name,
|
|
2677
|
+
query: mcpLogQuery(name, toolArgs),
|
|
2678
|
+
project: currentRoot(),
|
|
2679
|
+
results: resultLines,
|
|
2680
|
+
ms: Date.now() - startMs,
|
|
2681
|
+
error: result.isError ? text.slice(0, 200) : undefined,
|
|
2682
|
+
});
|
|
2683
|
+
}
|
|
2684
|
+
catch (_d) { }
|
|
2685
|
+
});
|
|
2686
|
+
// Register a tool, wrapping its handler with timing + query logging. Zod raw
|
|
2687
|
+
// shapes give us free input validation (the SDK rejects calls that violate
|
|
2688
|
+
// the schema before the handler runs); handlers stay defensively coded.
|
|
2689
|
+
function tool(name, config, handler) {
|
|
2690
|
+
server.registerTool(name, Object.assign(Object.assign(Object.assign({}, config), (mcp_results_1.MCP_READ_OUTPUT_SCHEMAS[name]
|
|
2691
|
+
? {
|
|
2692
|
+
outputSchema: zod_1.z.object(mcp_results_1.MCP_READ_OUTPUT_SCHEMAS[name]),
|
|
2693
|
+
annotations: {
|
|
2694
|
+
readOnlyHint: true,
|
|
2695
|
+
destructiveHint: false,
|
|
2696
|
+
openWorldHint: false,
|
|
2697
|
+
},
|
|
2698
|
+
}
|
|
2699
|
+
: {})), { inputSchema: zod_1.z.object(Object.assign({ root: zod_1.z.string().optional() }, config.inputSchema)) }), (rawArgs) => __awaiter(this, void 0, void 0, function* () {
|
|
2700
|
+
let args = (rawArgs !== null && rawArgs !== void 0 ? rawArgs : {});
|
|
2701
|
+
const startMs = Date.now();
|
|
2702
|
+
let target = projectRoot;
|
|
2703
|
+
try {
|
|
2704
|
+
if (typeof args.root === "string" && args.root.trim()) {
|
|
2705
|
+
const raw = args.root.trim();
|
|
2706
|
+
if (path.isAbsolute(raw) || raw.includes(path.sep))
|
|
2707
|
+
target = path.resolve(raw);
|
|
2708
|
+
else {
|
|
2709
|
+
const projects = [
|
|
2710
|
+
...(0, project_registry_1.listProjects)(),
|
|
2711
|
+
...(0, store_context_1.storeInventory)().flatMap((store) => store.projects),
|
|
2712
|
+
].filter((project, i, all) => all.findIndex((p) => p.root === project.root) === i);
|
|
2713
|
+
const selected = resolveMcpProject(raw, projectRoot, projects);
|
|
2714
|
+
if (!selected)
|
|
2715
|
+
return err(`Unknown or ambiguous registered project: ${raw}. Use list_projects or an exact root.`);
|
|
2716
|
+
target = selected.root;
|
|
2717
|
+
}
|
|
2641
2718
|
}
|
|
2719
|
+
return yield (0, store_context_1.withStoreContext)(target, () => __awaiter(this, void 0, void 0, function* () {
|
|
2720
|
+
if (args.root !== undefined) {
|
|
2721
|
+
const selected = resolveMcpProject(args.root, currentRoot(), storeProjects());
|
|
2722
|
+
if (!selected)
|
|
2723
|
+
return err(`Unknown registered project: ${args.root}. Use list_projects to select a project name or exact root.`);
|
|
2724
|
+
args = Object.assign(Object.assign({}, args), { root: selected.root });
|
|
2725
|
+
(0, store_context_1.currentStoreContext)().root = selected.root;
|
|
2726
|
+
}
|
|
2727
|
+
const result = yield handler(args);
|
|
2728
|
+
yield logToolCall(name, args, startMs, result);
|
|
2729
|
+
return result;
|
|
2730
|
+
}));
|
|
2642
2731
|
}
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2732
|
+
catch (error) {
|
|
2733
|
+
return toolError("Tool failed", error);
|
|
2734
|
+
}
|
|
2735
|
+
}));
|
|
2736
|
+
}
|
|
2737
|
+
tool("semantic_search", {
|
|
2738
|
+
description: "Search code by meaning. Use scope:'all' for cross-project. Prefer CLI: gmax \"query\" --plain",
|
|
2739
|
+
inputSchema: {
|
|
2740
|
+
query: zod_1.z
|
|
2741
|
+
.string()
|
|
2742
|
+
.describe("Natural language query (5+ words recommended)"),
|
|
2743
|
+
limit: zod_1.z
|
|
2744
|
+
.number()
|
|
2745
|
+
.describe("Max results (default 3, max 50)")
|
|
2746
|
+
.optional(),
|
|
2747
|
+
root: zod_1.z
|
|
2748
|
+
.string()
|
|
2749
|
+
.describe("Registered project name or exact root")
|
|
2750
|
+
.optional(),
|
|
2751
|
+
path: zod_1.z
|
|
2752
|
+
.string()
|
|
2753
|
+
.describe("Path prefix filter (e.g. 'src/auth/')")
|
|
2754
|
+
.optional(),
|
|
2755
|
+
detail: zod_1.z
|
|
2756
|
+
.string()
|
|
2757
|
+
.describe("'pointer' (default), 'code', or 'full'")
|
|
2758
|
+
.optional(),
|
|
2759
|
+
min_score: zod_1.z
|
|
2760
|
+
.number()
|
|
2761
|
+
.describe("Min score 0-1 (default 0)")
|
|
2762
|
+
.optional(),
|
|
2763
|
+
max_per_file: zod_1.z
|
|
2764
|
+
.number()
|
|
2765
|
+
.describe("Max results per file")
|
|
2766
|
+
.optional(),
|
|
2767
|
+
file: zod_1.z
|
|
2768
|
+
.string()
|
|
2769
|
+
.describe("Filename filter (e.g. 'syncer.ts')")
|
|
2770
|
+
.optional(),
|
|
2771
|
+
exclude: zod_1.z
|
|
2772
|
+
.string()
|
|
2773
|
+
.describe("Exclude path prefix (e.g. 'tests/')")
|
|
2774
|
+
.optional(),
|
|
2775
|
+
language: zod_1.z
|
|
2776
|
+
.string()
|
|
2777
|
+
.describe("Extension filter (e.g. 'ts', 'py')")
|
|
2778
|
+
.optional(),
|
|
2779
|
+
role: zod_1.z
|
|
2780
|
+
.string()
|
|
2781
|
+
.describe("'ORCHESTRATION', 'DEFINITION', or 'IMPLEMENTATION'")
|
|
2782
|
+
.optional(),
|
|
2783
|
+
context_lines: zod_1.z
|
|
2784
|
+
.number()
|
|
2785
|
+
.describe("Lines before/after chunk (max 20)")
|
|
2786
|
+
.optional(),
|
|
2787
|
+
mode: zod_1.z
|
|
2788
|
+
.string()
|
|
2789
|
+
.describe("'default' or 'symbol' (appends call graph)")
|
|
2790
|
+
.optional(),
|
|
2791
|
+
include_imports: zod_1.z
|
|
2792
|
+
.boolean()
|
|
2793
|
+
.describe("Prepend file imports to results")
|
|
2794
|
+
.optional(),
|
|
2795
|
+
name_pattern: zod_1.z
|
|
2796
|
+
.string()
|
|
2797
|
+
.describe("Regex filter on symbol name")
|
|
2798
|
+
.optional(),
|
|
2799
|
+
scope: zod_1.z
|
|
2800
|
+
.string()
|
|
2801
|
+
.describe("'project' (default) or 'all' (search everything)")
|
|
2802
|
+
.optional(),
|
|
2803
|
+
projects: zod_1.z
|
|
2804
|
+
.string()
|
|
2805
|
+
.describe("Project names to include (comma-separated)")
|
|
2806
|
+
.optional(),
|
|
2807
|
+
exclude_projects: zod_1.z
|
|
2808
|
+
.string()
|
|
2809
|
+
.describe("Project names to exclude (comma-separated)")
|
|
2810
|
+
.optional(),
|
|
2811
|
+
seed_files: zod_1.z
|
|
2812
|
+
.string()
|
|
2813
|
+
.describe("Bias results toward your working context: comma-separated paths you have open (e.g. 'src/lib/llm/server.ts'). On-topic chunks in these files get lifted; off-topic ones are not.")
|
|
2814
|
+
.optional(),
|
|
2815
|
+
seed_symbols: zod_1.z
|
|
2816
|
+
.string()
|
|
2817
|
+
.describe("Bias results toward identifiers you're working with: comma-separated symbol names. Chunks defining a seeded symbol are preferred over mere callers.")
|
|
2818
|
+
.optional(),
|
|
2819
|
+
},
|
|
2820
|
+
}, handleSemanticSearch);
|
|
2821
|
+
tool("code_skeleton", {
|
|
2822
|
+
description: "File structure with bodies collapsed (~4x fewer tokens). Accepts file, directory, or comma-separated paths.",
|
|
2823
|
+
inputSchema: {
|
|
2824
|
+
target: zod_1.z
|
|
2825
|
+
.string()
|
|
2826
|
+
.describe("File, directory, or comma-separated paths"),
|
|
2827
|
+
limit: zod_1.z
|
|
2828
|
+
.number()
|
|
2829
|
+
.describe("Max files for directory mode (default 10)")
|
|
2830
|
+
.optional(),
|
|
2831
|
+
format: zod_1.z
|
|
2832
|
+
.string()
|
|
2833
|
+
.describe("'text' (default) or 'json'")
|
|
2834
|
+
.optional(),
|
|
2835
|
+
},
|
|
2836
|
+
}, handleCodeSkeleton);
|
|
2837
|
+
tool("trace_calls", {
|
|
2838
|
+
description: "Call graph: importers, callers (multi-hop), callees with file:line.",
|
|
2839
|
+
inputSchema: {
|
|
2840
|
+
symbol: zod_1.z.string().describe("Function/class name to trace"),
|
|
2841
|
+
depth: zod_1.z
|
|
2842
|
+
.number()
|
|
2843
|
+
.describe("Caller depth (default 1, max 3)")
|
|
2844
|
+
.optional(),
|
|
2845
|
+
},
|
|
2846
|
+
}, handleTraceCalls);
|
|
2847
|
+
tool("extract_symbol", {
|
|
2848
|
+
description: "Extract complete function/class body by symbol name.",
|
|
2849
|
+
inputSchema: {
|
|
2850
|
+
symbol: zod_1.z.string().describe("Symbol name to extract"),
|
|
2851
|
+
root: zod_1.z
|
|
2852
|
+
.string()
|
|
2853
|
+
.describe("Project root (absolute path)")
|
|
2854
|
+
.optional(),
|
|
2855
|
+
include_imports: zod_1.z
|
|
2856
|
+
.boolean()
|
|
2857
|
+
.describe("Prepend file imports")
|
|
2858
|
+
.optional(),
|
|
2859
|
+
},
|
|
2860
|
+
}, handleExtractSymbol);
|
|
2861
|
+
tool("peek_symbol", {
|
|
2862
|
+
description: "Compact symbol overview: signature + callers + callees.",
|
|
2863
|
+
inputSchema: {
|
|
2864
|
+
symbol: zod_1.z.string().describe("Symbol name"),
|
|
2865
|
+
root: zod_1.z
|
|
2866
|
+
.string()
|
|
2867
|
+
.describe("Project root (absolute path)")
|
|
2868
|
+
.optional(),
|
|
2869
|
+
depth: zod_1.z
|
|
2870
|
+
.number()
|
|
2871
|
+
.describe("Caller depth (default 1, max 3)")
|
|
2872
|
+
.optional(),
|
|
2873
|
+
},
|
|
2874
|
+
}, handlePeekSymbol);
|
|
2875
|
+
tool("dead", {
|
|
2876
|
+
description: "Report whether a symbol has zero inbound callers in the indexed call graph. Returns DEAD, PUBLIC EXPORT (exported with no internal callers), or LIVE with caller count. Hypothesis, not proof: dynamic dispatch, reflection, and string-built call sites won't show up.",
|
|
2877
|
+
inputSchema: {
|
|
2878
|
+
symbol: zod_1.z.string().describe("Symbol name to check"),
|
|
2879
|
+
root: zod_1.z
|
|
2880
|
+
.string()
|
|
2881
|
+
.describe("Project root (absolute path)")
|
|
2882
|
+
.optional(),
|
|
2883
|
+
},
|
|
2884
|
+
}, handleDead);
|
|
2885
|
+
tool("audit", {
|
|
2886
|
+
description: "Graph-summary of the indexed project in one call: god nodes (most depended-upon symbols), hub files (most depended-upon files), symbol-derived file dependency cycles, and dead-code candidates (non-exported symbols with zero inbound references). Built from the static reference graph; dynamic dispatch, reflection, and eval are invisible, so dead candidates are hypotheses; verify with the `dead` tool before acting.",
|
|
2887
|
+
inputSchema: {
|
|
2888
|
+
root: zod_1.z
|
|
2889
|
+
.string()
|
|
2890
|
+
.describe("Project root (default: current)")
|
|
2891
|
+
.optional(),
|
|
2892
|
+
top: zod_1.z
|
|
2893
|
+
.number()
|
|
2894
|
+
.describe("How many of each category to return (default 10)")
|
|
2895
|
+
.optional(),
|
|
2896
|
+
},
|
|
2897
|
+
}, handleAudit);
|
|
2898
|
+
tool("surprising_connections", {
|
|
2899
|
+
description: "Experimental orientation signal: embedding-similar cross-directory file pairs that are not directly connected by the static symbol graph. Useful for spotting duplicate or parallel logic. Requires experimental:true.",
|
|
2900
|
+
inputSchema: {
|
|
2901
|
+
experimental: zod_1.z
|
|
2902
|
+
.boolean()
|
|
2903
|
+
.describe("Must be true to acknowledge experimental signal quality"),
|
|
2904
|
+
root: zod_1.z
|
|
2905
|
+
.string()
|
|
2906
|
+
.describe("Project root (default: current)")
|
|
2907
|
+
.optional(),
|
|
2908
|
+
sample: zod_1.z
|
|
2909
|
+
.number()
|
|
2910
|
+
.describe("Indexed code chunks to sample (default 160, max 10000)")
|
|
2911
|
+
.optional(),
|
|
2912
|
+
neighbors: zod_1.z
|
|
2913
|
+
.number()
|
|
2914
|
+
.describe("Nearest neighbors per sampled chunk (default 20, max 200)")
|
|
2915
|
+
.optional(),
|
|
2916
|
+
top: zod_1.z
|
|
2917
|
+
.number()
|
|
2918
|
+
.describe("Grouped findings to return (default 10, max 100)")
|
|
2919
|
+
.optional(),
|
|
2920
|
+
dir_depth: zod_1.z
|
|
2921
|
+
.number()
|
|
2922
|
+
.describe("Directory bucket depth considered unsurprising")
|
|
2923
|
+
.optional(),
|
|
2924
|
+
min_similarity: zod_1.z
|
|
2925
|
+
.number()
|
|
2926
|
+
.describe("Minimum similarity 0-1 (default 0)")
|
|
2927
|
+
.optional(),
|
|
2928
|
+
max_rows: zod_1.z
|
|
2929
|
+
.number()
|
|
2930
|
+
.describe(`Maximum indexed rows to scan (default 50000, max ${surprising_connections_1.MAX_SURPRISE_ROWS})`)
|
|
2931
|
+
.optional(),
|
|
2932
|
+
in: zod_1.z
|
|
2933
|
+
.string()
|
|
2934
|
+
.describe("Restrict to a sub-path of the project")
|
|
2935
|
+
.optional(),
|
|
2936
|
+
exclude: zod_1.z
|
|
2937
|
+
.string()
|
|
2938
|
+
.describe("Exclude a sub-path of the project")
|
|
2939
|
+
.optional(),
|
|
2940
|
+
include_tests: zod_1.z
|
|
2941
|
+
.boolean()
|
|
2942
|
+
.describe("Include test files")
|
|
2943
|
+
.optional(),
|
|
2944
|
+
include_eval: zod_1.z
|
|
2945
|
+
.boolean()
|
|
2946
|
+
.describe("Include eval/experiment/script files")
|
|
2947
|
+
.optional(),
|
|
2948
|
+
},
|
|
2949
|
+
}, handleSurprisingConnections);
|
|
2950
|
+
tool("get_neighbors", {
|
|
2951
|
+
description: "Graph primitive: symbols reachable from a node along call edges within N hops. direction 'callees' = what it calls (outbound), 'callers' = what calls it (inbound). Each result carries its hop distance and definition location. Static call graph — same caveats as `dead`/`audit`.",
|
|
2952
|
+
inputSchema: {
|
|
2953
|
+
symbol: zod_1.z.string().describe("Starting symbol"),
|
|
2954
|
+
direction: zod_1.z
|
|
2955
|
+
.enum(["callers", "callees"])
|
|
2956
|
+
.describe("Edge direction (default callees)")
|
|
2957
|
+
.optional(),
|
|
2958
|
+
max_hops: zod_1.z
|
|
2959
|
+
.number()
|
|
2960
|
+
.describe("Max hops (default 2, max 5)")
|
|
2961
|
+
.optional(),
|
|
2962
|
+
root: zod_1.z
|
|
2963
|
+
.string()
|
|
2964
|
+
.describe("Project root (absolute path)")
|
|
2965
|
+
.optional(),
|
|
2966
|
+
},
|
|
2967
|
+
}, handleGetNeighbors);
|
|
2968
|
+
tool("find_paths", {
|
|
2969
|
+
description: "Graph primitive: shortest call-graph path between two symbols, as a symbol sequence. direction 'callees' searches outward from `from` (does `from` transitively call `to`?), 'callers' searches inward. Returns 'no path' if unreachable within max_hops.",
|
|
2970
|
+
inputSchema: {
|
|
2971
|
+
from: zod_1.z.string().describe("Start symbol"),
|
|
2972
|
+
to: zod_1.z.string().describe("Target symbol"),
|
|
2973
|
+
direction: zod_1.z
|
|
2974
|
+
.enum(["callers", "callees"])
|
|
2975
|
+
.describe("Search direction (default callees)")
|
|
2976
|
+
.optional(),
|
|
2977
|
+
max_hops: zod_1.z
|
|
2978
|
+
.number()
|
|
2979
|
+
.describe("Max hops (default 6, max 10)")
|
|
2980
|
+
.optional(),
|
|
2981
|
+
root: zod_1.z
|
|
2982
|
+
.string()
|
|
2983
|
+
.describe("Project root (absolute path)")
|
|
2984
|
+
.optional(),
|
|
2985
|
+
},
|
|
2986
|
+
}, handleFindPaths);
|
|
2987
|
+
tool("subgraph_for_files", {
|
|
2988
|
+
description: "Graph primitive: the local dependency subgraph for a set of files — symbols they define, the call edges among those symbols, and their outbound external dependencies. Paths may be absolute or project-root-relative.",
|
|
2989
|
+
inputSchema: {
|
|
2990
|
+
files: zod_1.z
|
|
2991
|
+
.array(zod_1.z.string())
|
|
2992
|
+
.describe("File paths (absolute or root-relative)"),
|
|
2993
|
+
root: zod_1.z
|
|
2994
|
+
.string()
|
|
2995
|
+
.describe("Project root (absolute path)")
|
|
2996
|
+
.optional(),
|
|
2997
|
+
},
|
|
2998
|
+
}, handleSubgraphForFiles);
|
|
2999
|
+
tool("list_symbols", {
|
|
3000
|
+
description: "List indexed symbols with role and export status.",
|
|
3001
|
+
inputSchema: {
|
|
3002
|
+
pattern: zod_1.z
|
|
3003
|
+
.string()
|
|
3004
|
+
.describe("Name filter (case-insensitive)")
|
|
3005
|
+
.optional(),
|
|
3006
|
+
limit: zod_1.z.number().describe("Max results (default 20)").optional(),
|
|
3007
|
+
path: zod_1.z.string().describe("Path prefix filter").optional(),
|
|
3008
|
+
},
|
|
3009
|
+
}, handleListSymbols);
|
|
3010
|
+
tool("index_status", {
|
|
3011
|
+
description: "Index health: chunks, files, projects, watcher status.",
|
|
3012
|
+
inputSchema: {},
|
|
3013
|
+
_meta: { "anthropic/alwaysLoad": true },
|
|
3014
|
+
}, handleIndexStatus);
|
|
3015
|
+
tool("list_projects", {
|
|
3016
|
+
description: "List every gmax-indexed project (name, root, status, chunk count). Use this to pick a search scope when you're not inside a specific repo: feed a name into semantic_search's projects: param, or use scope:'all'. The working-directory project is marked (current).",
|
|
3017
|
+
inputSchema: {},
|
|
3018
|
+
_meta: { "anthropic/alwaysLoad": true },
|
|
3019
|
+
}, handleListProjects);
|
|
3020
|
+
tool("summarize_directory", {
|
|
3021
|
+
description: "Generate LLM summaries for indexed chunks.",
|
|
3022
|
+
inputSchema: {
|
|
3023
|
+
path: zod_1.z
|
|
3024
|
+
.string()
|
|
3025
|
+
.describe("Directory to summarize (default: project root)")
|
|
3026
|
+
.optional(),
|
|
3027
|
+
limit: zod_1.z
|
|
3028
|
+
.number()
|
|
3029
|
+
.describe("Max chunks (default 200, max 5000)")
|
|
3030
|
+
.optional(),
|
|
3031
|
+
},
|
|
3032
|
+
}, handleSummarizeDirectory);
|
|
3033
|
+
tool("summarize_project", {
|
|
3034
|
+
description: "Project overview: languages, structure, roles, key symbols, entry points.",
|
|
3035
|
+
inputSchema: {
|
|
3036
|
+
root: zod_1.z
|
|
3037
|
+
.string()
|
|
3038
|
+
.describe("Project root (default: current)")
|
|
3039
|
+
.optional(),
|
|
3040
|
+
},
|
|
3041
|
+
}, handleSummarizeProject);
|
|
3042
|
+
tool("related_files", {
|
|
3043
|
+
description: "Find dependencies and dependents of a file by shared symbols.",
|
|
3044
|
+
inputSchema: {
|
|
3045
|
+
file: zod_1.z.string().describe("File path relative to project root"),
|
|
3046
|
+
limit: zod_1.z
|
|
3047
|
+
.number()
|
|
3048
|
+
.describe("Max results per direction (default 10)")
|
|
3049
|
+
.optional(),
|
|
3050
|
+
},
|
|
3051
|
+
}, handleRelatedFiles);
|
|
3052
|
+
tool("recent_changes", {
|
|
3053
|
+
description: "Recently modified indexed files with timestamps.",
|
|
3054
|
+
inputSchema: {
|
|
3055
|
+
limit: zod_1.z
|
|
3056
|
+
.number()
|
|
3057
|
+
.describe("Max files to return (default 20)")
|
|
3058
|
+
.optional(),
|
|
3059
|
+
root: zod_1.z
|
|
3060
|
+
.string()
|
|
3061
|
+
.describe("Project root (defaults to current project)")
|
|
3062
|
+
.optional(),
|
|
3063
|
+
},
|
|
3064
|
+
}, handleRecentChanges);
|
|
3065
|
+
tool("diff_changes", {
|
|
3066
|
+
description: "Search code scoped to git changes. Omit ref for uncommitted changes.",
|
|
3067
|
+
inputSchema: {
|
|
3068
|
+
ref: zod_1.z
|
|
3069
|
+
.string()
|
|
3070
|
+
.describe("Git ref to diff against (e.g. main, HEAD~5)")
|
|
3071
|
+
.optional(),
|
|
3072
|
+
query: zod_1.z
|
|
3073
|
+
.string()
|
|
3074
|
+
.describe("Semantic search within changed files")
|
|
3075
|
+
.optional(),
|
|
3076
|
+
limit: zod_1.z.number().describe("Max results (default 10)").optional(),
|
|
3077
|
+
role: zod_1.z
|
|
3078
|
+
.string()
|
|
3079
|
+
.describe("Filter by role: ORCHESTRATION, DEFINITION, IMPLEMENTATION")
|
|
3080
|
+
.optional(),
|
|
3081
|
+
},
|
|
3082
|
+
}, handleDiffChanges);
|
|
3083
|
+
tool("find_tests", {
|
|
3084
|
+
description: "Find tests that exercise a symbol or file via reverse call graph.",
|
|
3085
|
+
inputSchema: {
|
|
3086
|
+
target: zod_1.z.string().describe("Symbol name or file path"),
|
|
3087
|
+
depth: zod_1.z
|
|
3088
|
+
.number()
|
|
3089
|
+
.describe("Caller traversal depth 1-3 (default 1)")
|
|
3090
|
+
.optional(),
|
|
3091
|
+
},
|
|
3092
|
+
}, handleFindTests);
|
|
3093
|
+
tool("impact_analysis", {
|
|
3094
|
+
description: "Change impact: dependents and affected tests for a symbol or file.",
|
|
3095
|
+
inputSchema: {
|
|
3096
|
+
target: zod_1.z.string().describe("Symbol name or file path"),
|
|
3097
|
+
depth: zod_1.z
|
|
3098
|
+
.number()
|
|
3099
|
+
.describe("Caller traversal depth 1-3 (default 1)")
|
|
3100
|
+
.optional(),
|
|
3101
|
+
rollup: zod_1.z
|
|
3102
|
+
.boolean()
|
|
3103
|
+
.describe("Return export/package rollup TSV rows")
|
|
3104
|
+
.optional(),
|
|
3105
|
+
top: zod_1.z
|
|
3106
|
+
.number()
|
|
3107
|
+
.describe("Max rows per rollup section (default 10)")
|
|
3108
|
+
.optional(),
|
|
3109
|
+
},
|
|
3110
|
+
}, handleImpactAnalysis);
|
|
3111
|
+
tool("find_similar", {
|
|
3112
|
+
description: "Find semantically similar code using vector similarity.",
|
|
3113
|
+
inputSchema: {
|
|
3114
|
+
target: zod_1.z.string().describe("Symbol name or file path"),
|
|
3115
|
+
limit: zod_1.z.number().describe("Max results (default 5)").optional(),
|
|
3116
|
+
threshold: zod_1.z
|
|
3117
|
+
.number()
|
|
3118
|
+
.describe("Min similarity 0-1 (default 0)")
|
|
3119
|
+
.optional(),
|
|
3120
|
+
},
|
|
3121
|
+
}, handleFindSimilar);
|
|
3122
|
+
tool("build_context", {
|
|
3123
|
+
description: "Token-budgeted topic summary (search + skeleton + extract).",
|
|
3124
|
+
inputSchema: {
|
|
3125
|
+
topic: zod_1.z
|
|
3126
|
+
.string()
|
|
3127
|
+
.describe("Natural language topic or directory path"),
|
|
3128
|
+
budget: zod_1.z.number().describe("Max tokens (default 4000)").optional(),
|
|
3129
|
+
limit: zod_1.z
|
|
3130
|
+
.number()
|
|
3131
|
+
.describe("Search result limit (default 10)")
|
|
3132
|
+
.optional(),
|
|
3133
|
+
},
|
|
3134
|
+
}, handleBuildContext);
|
|
3135
|
+
tool("investigate", {
|
|
3136
|
+
description: "Agentic codebase Q&A: a local LLM answers questions using search, trace, peek, impact, and related tools. Requires LLM to be enabled (gmax llm on).",
|
|
3137
|
+
inputSchema: {
|
|
3138
|
+
question: zod_1.z
|
|
3139
|
+
.string()
|
|
3140
|
+
.describe("Natural language question about the codebase"),
|
|
3141
|
+
max_rounds: zod_1.z
|
|
3142
|
+
.number()
|
|
3143
|
+
.describe("Max tool-call rounds (default 10)")
|
|
3144
|
+
.optional(),
|
|
3145
|
+
},
|
|
3146
|
+
}, handleInvestigate);
|
|
3147
|
+
tool("review_commit", {
|
|
3148
|
+
description: "Review a git commit for bugs, breaking changes, and security issues using local LLM + codebase context. Returns structured findings. Requires LLM to be enabled (gmax llm on).",
|
|
3149
|
+
inputSchema: {
|
|
3150
|
+
commit: zod_1.z
|
|
3151
|
+
.string()
|
|
3152
|
+
.describe("Git ref to review (default: HEAD)")
|
|
3153
|
+
.optional(),
|
|
3154
|
+
},
|
|
3155
|
+
}, handleReviewCommit);
|
|
3156
|
+
tool("review_report", {
|
|
3157
|
+
description: "Get the accumulated code review report for the current project. Returns findings from all reviewed commits.",
|
|
3158
|
+
inputSchema: {
|
|
3159
|
+
json: zod_1.z
|
|
3160
|
+
.boolean()
|
|
3161
|
+
.describe("Return raw JSON instead of text (default: false)")
|
|
3162
|
+
.optional(),
|
|
3163
|
+
},
|
|
3164
|
+
}, handleReviewReport);
|
|
3165
|
+
tool("review_risk", {
|
|
3166
|
+
description: "Deterministic risk ranking of the symbols a commit's diff touches, ordered by blast radius (inbound callers) × test presence × file churn. No LLM required — fast, graph + git only. Use to triage what a change endangers before a deep review.",
|
|
3167
|
+
inputSchema: {
|
|
3168
|
+
commit: zod_1.z
|
|
3169
|
+
.string()
|
|
3170
|
+
.describe("Git ref to analyze (default: HEAD)")
|
|
3171
|
+
.optional(),
|
|
3172
|
+
},
|
|
3173
|
+
}, handleReviewRisk);
|
|
3174
|
+
return server;
|
|
2660
3175
|
}
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
.describe("Natural language query (5+ words recommended)"),
|
|
2667
|
-
limit: zod_1.z
|
|
2668
|
-
.number()
|
|
2669
|
-
.describe("Max results (default 3, max 50)")
|
|
2670
|
-
.optional(),
|
|
2671
|
-
root: zod_1.z
|
|
2672
|
-
.string()
|
|
2673
|
-
.describe("Registered project name or exact root")
|
|
2674
|
-
.optional(),
|
|
2675
|
-
path: zod_1.z
|
|
2676
|
-
.string()
|
|
2677
|
-
.describe("Path prefix filter (e.g. 'src/auth/')")
|
|
2678
|
-
.optional(),
|
|
2679
|
-
detail: zod_1.z
|
|
2680
|
-
.string()
|
|
2681
|
-
.describe("'pointer' (default), 'code', or 'full'")
|
|
2682
|
-
.optional(),
|
|
2683
|
-
min_score: zod_1.z
|
|
2684
|
-
.number()
|
|
2685
|
-
.describe("Min score 0-1 (default 0)")
|
|
2686
|
-
.optional(),
|
|
2687
|
-
max_per_file: zod_1.z.number().describe("Max results per file").optional(),
|
|
2688
|
-
file: zod_1.z
|
|
2689
|
-
.string()
|
|
2690
|
-
.describe("Filename filter (e.g. 'syncer.ts')")
|
|
2691
|
-
.optional(),
|
|
2692
|
-
exclude: zod_1.z
|
|
2693
|
-
.string()
|
|
2694
|
-
.describe("Exclude path prefix (e.g. 'tests/')")
|
|
2695
|
-
.optional(),
|
|
2696
|
-
language: zod_1.z
|
|
2697
|
-
.string()
|
|
2698
|
-
.describe("Extension filter (e.g. 'ts', 'py')")
|
|
2699
|
-
.optional(),
|
|
2700
|
-
role: zod_1.z
|
|
2701
|
-
.string()
|
|
2702
|
-
.describe("'ORCHESTRATION', 'DEFINITION', or 'IMPLEMENTATION'")
|
|
2703
|
-
.optional(),
|
|
2704
|
-
context_lines: zod_1.z
|
|
2705
|
-
.number()
|
|
2706
|
-
.describe("Lines before/after chunk (max 20)")
|
|
2707
|
-
.optional(),
|
|
2708
|
-
mode: zod_1.z
|
|
2709
|
-
.string()
|
|
2710
|
-
.describe("'default' or 'symbol' (appends call graph)")
|
|
2711
|
-
.optional(),
|
|
2712
|
-
include_imports: zod_1.z
|
|
2713
|
-
.boolean()
|
|
2714
|
-
.describe("Prepend file imports to results")
|
|
2715
|
-
.optional(),
|
|
2716
|
-
name_pattern: zod_1.z
|
|
2717
|
-
.string()
|
|
2718
|
-
.describe("Regex filter on symbol name")
|
|
2719
|
-
.optional(),
|
|
2720
|
-
scope: zod_1.z
|
|
2721
|
-
.string()
|
|
2722
|
-
.describe("'project' (default) or 'all' (search everything)")
|
|
2723
|
-
.optional(),
|
|
2724
|
-
projects: zod_1.z
|
|
2725
|
-
.string()
|
|
2726
|
-
.describe("Project names to include (comma-separated)")
|
|
2727
|
-
.optional(),
|
|
2728
|
-
exclude_projects: zod_1.z
|
|
2729
|
-
.string()
|
|
2730
|
-
.describe("Project names to exclude (comma-separated)")
|
|
2731
|
-
.optional(),
|
|
2732
|
-
seed_files: zod_1.z
|
|
2733
|
-
.string()
|
|
2734
|
-
.describe("Bias results toward your working context: comma-separated paths you have open (e.g. 'src/lib/llm/server.ts'). On-topic chunks in these files get lifted; off-topic ones are not.")
|
|
2735
|
-
.optional(),
|
|
2736
|
-
seed_symbols: zod_1.z
|
|
2737
|
-
.string()
|
|
2738
|
-
.describe("Bias results toward identifiers you're working with: comma-separated symbol names. Chunks defining a seeded symbol are preferred over mere callers.")
|
|
2739
|
-
.optional(),
|
|
2740
|
-
},
|
|
2741
|
-
}, handleSemanticSearch);
|
|
2742
|
-
tool("code_skeleton", {
|
|
2743
|
-
description: "File structure with bodies collapsed (~4x fewer tokens). Accepts file, directory, or comma-separated paths.",
|
|
2744
|
-
inputSchema: {
|
|
2745
|
-
target: zod_1.z
|
|
2746
|
-
.string()
|
|
2747
|
-
.describe("File, directory, or comma-separated paths"),
|
|
2748
|
-
limit: zod_1.z
|
|
2749
|
-
.number()
|
|
2750
|
-
.describe("Max files for directory mode (default 10)")
|
|
2751
|
-
.optional(),
|
|
2752
|
-
format: zod_1.z.string().describe("'text' (default) or 'json'").optional(),
|
|
2753
|
-
},
|
|
2754
|
-
}, handleCodeSkeleton);
|
|
2755
|
-
tool("trace_calls", {
|
|
2756
|
-
description: "Call graph: importers, callers (multi-hop), callees with file:line.",
|
|
2757
|
-
inputSchema: {
|
|
2758
|
-
symbol: zod_1.z.string().describe("Function/class name to trace"),
|
|
2759
|
-
depth: zod_1.z
|
|
2760
|
-
.number()
|
|
2761
|
-
.describe("Caller depth (default 1, max 3)")
|
|
2762
|
-
.optional(),
|
|
2763
|
-
},
|
|
2764
|
-
}, handleTraceCalls);
|
|
2765
|
-
tool("extract_symbol", {
|
|
2766
|
-
description: "Extract complete function/class body by symbol name.",
|
|
2767
|
-
inputSchema: {
|
|
2768
|
-
symbol: zod_1.z.string().describe("Symbol name to extract"),
|
|
2769
|
-
root: zod_1.z.string().describe("Project root (absolute path)").optional(),
|
|
2770
|
-
include_imports: zod_1.z
|
|
2771
|
-
.boolean()
|
|
2772
|
-
.describe("Prepend file imports")
|
|
2773
|
-
.optional(),
|
|
2774
|
-
},
|
|
2775
|
-
}, handleExtractSymbol);
|
|
2776
|
-
tool("peek_symbol", {
|
|
2777
|
-
description: "Compact symbol overview: signature + callers + callees.",
|
|
2778
|
-
inputSchema: {
|
|
2779
|
-
symbol: zod_1.z.string().describe("Symbol name"),
|
|
2780
|
-
root: zod_1.z.string().describe("Project root (absolute path)").optional(),
|
|
2781
|
-
depth: zod_1.z
|
|
2782
|
-
.number()
|
|
2783
|
-
.describe("Caller depth (default 1, max 3)")
|
|
2784
|
-
.optional(),
|
|
2785
|
-
},
|
|
2786
|
-
}, handlePeekSymbol);
|
|
2787
|
-
tool("dead", {
|
|
2788
|
-
description: "Report whether a symbol has zero inbound callers in the indexed call graph. Returns DEAD, PUBLIC EXPORT (exported with no internal callers), or LIVE with caller count. Hypothesis, not proof: dynamic dispatch, reflection, and string-built call sites won't show up.",
|
|
2789
|
-
inputSchema: {
|
|
2790
|
-
symbol: zod_1.z.string().describe("Symbol name to check"),
|
|
2791
|
-
root: zod_1.z.string().describe("Project root (absolute path)").optional(),
|
|
2792
|
-
},
|
|
2793
|
-
}, handleDead);
|
|
2794
|
-
tool("audit", {
|
|
2795
|
-
description: "Graph-summary of the indexed project in one call: god nodes (most depended-upon symbols), hub files (most depended-upon files), symbol-derived file dependency cycles, and dead-code candidates (non-exported symbols with zero inbound references). Built from the static reference graph; dynamic dispatch, reflection, and eval are invisible, so dead candidates are hypotheses; verify with the `dead` tool before acting.",
|
|
2796
|
-
inputSchema: {
|
|
2797
|
-
root: zod_1.z
|
|
2798
|
-
.string()
|
|
2799
|
-
.describe("Project root (default: current)")
|
|
2800
|
-
.optional(),
|
|
2801
|
-
top: zod_1.z
|
|
2802
|
-
.number()
|
|
2803
|
-
.describe("How many of each category to return (default 10)")
|
|
2804
|
-
.optional(),
|
|
2805
|
-
},
|
|
2806
|
-
}, handleAudit);
|
|
2807
|
-
tool("surprising_connections", {
|
|
2808
|
-
description: "Experimental orientation signal: embedding-similar cross-directory file pairs that are not directly connected by the static symbol graph. Useful for spotting duplicate or parallel logic. Requires experimental:true.",
|
|
2809
|
-
inputSchema: {
|
|
2810
|
-
experimental: zod_1.z
|
|
2811
|
-
.boolean()
|
|
2812
|
-
.describe("Must be true to acknowledge experimental signal quality"),
|
|
2813
|
-
root: zod_1.z
|
|
2814
|
-
.string()
|
|
2815
|
-
.describe("Project root (default: current)")
|
|
2816
|
-
.optional(),
|
|
2817
|
-
sample: zod_1.z
|
|
2818
|
-
.number()
|
|
2819
|
-
.describe("Indexed code chunks to sample (default 160, max 10000)")
|
|
2820
|
-
.optional(),
|
|
2821
|
-
neighbors: zod_1.z
|
|
2822
|
-
.number()
|
|
2823
|
-
.describe("Nearest neighbors per sampled chunk (default 20, max 200)")
|
|
2824
|
-
.optional(),
|
|
2825
|
-
top: zod_1.z
|
|
2826
|
-
.number()
|
|
2827
|
-
.describe("Grouped findings to return (default 10, max 100)")
|
|
2828
|
-
.optional(),
|
|
2829
|
-
dir_depth: zod_1.z
|
|
2830
|
-
.number()
|
|
2831
|
-
.describe("Directory bucket depth considered unsurprising")
|
|
2832
|
-
.optional(),
|
|
2833
|
-
min_similarity: zod_1.z
|
|
2834
|
-
.number()
|
|
2835
|
-
.describe("Minimum similarity 0-1 (default 0)")
|
|
2836
|
-
.optional(),
|
|
2837
|
-
max_rows: zod_1.z
|
|
2838
|
-
.number()
|
|
2839
|
-
.describe(`Maximum indexed rows to scan (default 50000, max ${surprising_connections_1.MAX_SURPRISE_ROWS})`)
|
|
2840
|
-
.optional(),
|
|
2841
|
-
in: zod_1.z
|
|
2842
|
-
.string()
|
|
2843
|
-
.describe("Restrict to a sub-path of the project")
|
|
2844
|
-
.optional(),
|
|
2845
|
-
exclude: zod_1.z
|
|
2846
|
-
.string()
|
|
2847
|
-
.describe("Exclude a sub-path of the project")
|
|
2848
|
-
.optional(),
|
|
2849
|
-
include_tests: zod_1.z.boolean().describe("Include test files").optional(),
|
|
2850
|
-
include_eval: zod_1.z
|
|
2851
|
-
.boolean()
|
|
2852
|
-
.describe("Include eval/experiment/script files")
|
|
2853
|
-
.optional(),
|
|
2854
|
-
},
|
|
2855
|
-
}, handleSurprisingConnections);
|
|
2856
|
-
tool("get_neighbors", {
|
|
2857
|
-
description: "Graph primitive: symbols reachable from a node along call edges within N hops. direction 'callees' = what it calls (outbound), 'callers' = what calls it (inbound). Each result carries its hop distance and definition location. Static call graph — same caveats as `dead`/`audit`.",
|
|
2858
|
-
inputSchema: {
|
|
2859
|
-
symbol: zod_1.z.string().describe("Starting symbol"),
|
|
2860
|
-
direction: zod_1.z
|
|
2861
|
-
.enum(["callers", "callees"])
|
|
2862
|
-
.describe("Edge direction (default callees)")
|
|
2863
|
-
.optional(),
|
|
2864
|
-
max_hops: zod_1.z
|
|
2865
|
-
.number()
|
|
2866
|
-
.describe("Max hops (default 2, max 5)")
|
|
2867
|
-
.optional(),
|
|
2868
|
-
root: zod_1.z.string().describe("Project root (absolute path)").optional(),
|
|
2869
|
-
},
|
|
2870
|
-
}, handleGetNeighbors);
|
|
2871
|
-
tool("find_paths", {
|
|
2872
|
-
description: "Graph primitive: shortest call-graph path between two symbols, as a symbol sequence. direction 'callees' searches outward from `from` (does `from` transitively call `to`?), 'callers' searches inward. Returns 'no path' if unreachable within max_hops.",
|
|
2873
|
-
inputSchema: {
|
|
2874
|
-
from: zod_1.z.string().describe("Start symbol"),
|
|
2875
|
-
to: zod_1.z.string().describe("Target symbol"),
|
|
2876
|
-
direction: zod_1.z
|
|
2877
|
-
.enum(["callers", "callees"])
|
|
2878
|
-
.describe("Search direction (default callees)")
|
|
2879
|
-
.optional(),
|
|
2880
|
-
max_hops: zod_1.z
|
|
2881
|
-
.number()
|
|
2882
|
-
.describe("Max hops (default 6, max 10)")
|
|
2883
|
-
.optional(),
|
|
2884
|
-
root: zod_1.z.string().describe("Project root (absolute path)").optional(),
|
|
2885
|
-
},
|
|
2886
|
-
}, handleFindPaths);
|
|
2887
|
-
tool("subgraph_for_files", {
|
|
2888
|
-
description: "Graph primitive: the local dependency subgraph for a set of files — symbols they define, the call edges among those symbols, and their outbound external dependencies. Paths may be absolute or project-root-relative.",
|
|
2889
|
-
inputSchema: {
|
|
2890
|
-
files: zod_1.z
|
|
2891
|
-
.array(zod_1.z.string())
|
|
2892
|
-
.describe("File paths (absolute or root-relative)"),
|
|
2893
|
-
root: zod_1.z.string().describe("Project root (absolute path)").optional(),
|
|
2894
|
-
},
|
|
2895
|
-
}, handleSubgraphForFiles);
|
|
2896
|
-
tool("list_symbols", {
|
|
2897
|
-
description: "List indexed symbols with role and export status.",
|
|
2898
|
-
inputSchema: {
|
|
2899
|
-
pattern: zod_1.z
|
|
2900
|
-
.string()
|
|
2901
|
-
.describe("Name filter (case-insensitive)")
|
|
2902
|
-
.optional(),
|
|
2903
|
-
limit: zod_1.z.number().describe("Max results (default 20)").optional(),
|
|
2904
|
-
path: zod_1.z.string().describe("Path prefix filter").optional(),
|
|
2905
|
-
},
|
|
2906
|
-
}, handleListSymbols);
|
|
2907
|
-
tool("index_status", {
|
|
2908
|
-
description: "Index health: chunks, files, projects, watcher status.",
|
|
2909
|
-
inputSchema: {},
|
|
2910
|
-
_meta: { "anthropic/alwaysLoad": true },
|
|
2911
|
-
}, handleIndexStatus);
|
|
2912
|
-
tool("list_projects", {
|
|
2913
|
-
description: "List every gmax-indexed project (name, root, status, chunk count). Use this to pick a search scope when you're not inside a specific repo: feed a name into semantic_search's projects: param, or use scope:'all'. The working-directory project is marked (current).",
|
|
2914
|
-
inputSchema: {},
|
|
2915
|
-
_meta: { "anthropic/alwaysLoad": true },
|
|
2916
|
-
}, handleListProjects);
|
|
2917
|
-
tool("summarize_directory", {
|
|
2918
|
-
description: "Generate LLM summaries for indexed chunks.",
|
|
2919
|
-
inputSchema: {
|
|
2920
|
-
path: zod_1.z
|
|
2921
|
-
.string()
|
|
2922
|
-
.describe("Directory to summarize (default: project root)")
|
|
2923
|
-
.optional(),
|
|
2924
|
-
limit: zod_1.z
|
|
2925
|
-
.number()
|
|
2926
|
-
.describe("Max chunks (default 200, max 5000)")
|
|
2927
|
-
.optional(),
|
|
2928
|
-
},
|
|
2929
|
-
}, handleSummarizeDirectory);
|
|
2930
|
-
tool("summarize_project", {
|
|
2931
|
-
description: "Project overview: languages, structure, roles, key symbols, entry points.",
|
|
2932
|
-
inputSchema: {
|
|
2933
|
-
root: zod_1.z
|
|
2934
|
-
.string()
|
|
2935
|
-
.describe("Project root (default: current)")
|
|
2936
|
-
.optional(),
|
|
2937
|
-
},
|
|
2938
|
-
}, handleSummarizeProject);
|
|
2939
|
-
tool("related_files", {
|
|
2940
|
-
description: "Find dependencies and dependents of a file by shared symbols.",
|
|
2941
|
-
inputSchema: {
|
|
2942
|
-
file: zod_1.z.string().describe("File path relative to project root"),
|
|
2943
|
-
limit: zod_1.z
|
|
2944
|
-
.number()
|
|
2945
|
-
.describe("Max results per direction (default 10)")
|
|
2946
|
-
.optional(),
|
|
2947
|
-
},
|
|
2948
|
-
}, handleRelatedFiles);
|
|
2949
|
-
tool("recent_changes", {
|
|
2950
|
-
description: "Recently modified indexed files with timestamps.",
|
|
2951
|
-
inputSchema: {
|
|
2952
|
-
limit: zod_1.z
|
|
2953
|
-
.number()
|
|
2954
|
-
.describe("Max files to return (default 20)")
|
|
2955
|
-
.optional(),
|
|
2956
|
-
root: zod_1.z
|
|
2957
|
-
.string()
|
|
2958
|
-
.describe("Project root (defaults to current project)")
|
|
2959
|
-
.optional(),
|
|
2960
|
-
},
|
|
2961
|
-
}, handleRecentChanges);
|
|
2962
|
-
tool("diff_changes", {
|
|
2963
|
-
description: "Search code scoped to git changes. Omit ref for uncommitted changes.",
|
|
2964
|
-
inputSchema: {
|
|
2965
|
-
ref: zod_1.z
|
|
2966
|
-
.string()
|
|
2967
|
-
.describe("Git ref to diff against (e.g. main, HEAD~5)")
|
|
2968
|
-
.optional(),
|
|
2969
|
-
query: zod_1.z
|
|
2970
|
-
.string()
|
|
2971
|
-
.describe("Semantic search within changed files")
|
|
2972
|
-
.optional(),
|
|
2973
|
-
limit: zod_1.z.number().describe("Max results (default 10)").optional(),
|
|
2974
|
-
role: zod_1.z
|
|
2975
|
-
.string()
|
|
2976
|
-
.describe("Filter by role: ORCHESTRATION, DEFINITION, IMPLEMENTATION")
|
|
2977
|
-
.optional(),
|
|
2978
|
-
},
|
|
2979
|
-
}, handleDiffChanges);
|
|
2980
|
-
tool("find_tests", {
|
|
2981
|
-
description: "Find tests that exercise a symbol or file via reverse call graph.",
|
|
2982
|
-
inputSchema: {
|
|
2983
|
-
target: zod_1.z.string().describe("Symbol name or file path"),
|
|
2984
|
-
depth: zod_1.z
|
|
2985
|
-
.number()
|
|
2986
|
-
.describe("Caller traversal depth 1-3 (default 1)")
|
|
2987
|
-
.optional(),
|
|
2988
|
-
},
|
|
2989
|
-
}, handleFindTests);
|
|
2990
|
-
tool("impact_analysis", {
|
|
2991
|
-
description: "Change impact: dependents and affected tests for a symbol or file.",
|
|
2992
|
-
inputSchema: {
|
|
2993
|
-
target: zod_1.z.string().describe("Symbol name or file path"),
|
|
2994
|
-
depth: zod_1.z
|
|
2995
|
-
.number()
|
|
2996
|
-
.describe("Caller traversal depth 1-3 (default 1)")
|
|
2997
|
-
.optional(),
|
|
2998
|
-
rollup: zod_1.z
|
|
2999
|
-
.boolean()
|
|
3000
|
-
.describe("Return export/package rollup TSV rows")
|
|
3001
|
-
.optional(),
|
|
3002
|
-
top: zod_1.z
|
|
3003
|
-
.number()
|
|
3004
|
-
.describe("Max rows per rollup section (default 10)")
|
|
3005
|
-
.optional(),
|
|
3006
|
-
},
|
|
3007
|
-
}, handleImpactAnalysis);
|
|
3008
|
-
tool("find_similar", {
|
|
3009
|
-
description: "Find semantically similar code using vector similarity.",
|
|
3010
|
-
inputSchema: {
|
|
3011
|
-
target: zod_1.z.string().describe("Symbol name or file path"),
|
|
3012
|
-
limit: zod_1.z.number().describe("Max results (default 5)").optional(),
|
|
3013
|
-
threshold: zod_1.z
|
|
3014
|
-
.number()
|
|
3015
|
-
.describe("Min similarity 0-1 (default 0)")
|
|
3016
|
-
.optional(),
|
|
3017
|
-
},
|
|
3018
|
-
}, handleFindSimilar);
|
|
3019
|
-
tool("build_context", {
|
|
3020
|
-
description: "Token-budgeted topic summary (search + skeleton + extract).",
|
|
3021
|
-
inputSchema: {
|
|
3022
|
-
topic: zod_1.z
|
|
3023
|
-
.string()
|
|
3024
|
-
.describe("Natural language topic or directory path"),
|
|
3025
|
-
budget: zod_1.z.number().describe("Max tokens (default 4000)").optional(),
|
|
3026
|
-
limit: zod_1.z
|
|
3027
|
-
.number()
|
|
3028
|
-
.describe("Search result limit (default 10)")
|
|
3029
|
-
.optional(),
|
|
3030
|
-
},
|
|
3031
|
-
}, handleBuildContext);
|
|
3032
|
-
tool("investigate", {
|
|
3033
|
-
description: "Agentic codebase Q&A: a local LLM answers questions using search, trace, peek, impact, and related tools. Requires LLM to be enabled (gmax llm on).",
|
|
3034
|
-
inputSchema: {
|
|
3035
|
-
question: zod_1.z
|
|
3036
|
-
.string()
|
|
3037
|
-
.describe("Natural language question about the codebase"),
|
|
3038
|
-
max_rounds: zod_1.z
|
|
3039
|
-
.number()
|
|
3040
|
-
.describe("Max tool-call rounds (default 10)")
|
|
3041
|
-
.optional(),
|
|
3042
|
-
},
|
|
3043
|
-
}, handleInvestigate);
|
|
3044
|
-
tool("review_commit", {
|
|
3045
|
-
description: "Review a git commit for bugs, breaking changes, and security issues using local LLM + codebase context. Returns structured findings. Requires LLM to be enabled (gmax llm on).",
|
|
3046
|
-
inputSchema: {
|
|
3047
|
-
commit: zod_1.z
|
|
3048
|
-
.string()
|
|
3049
|
-
.describe("Git ref to review (default: HEAD)")
|
|
3050
|
-
.optional(),
|
|
3051
|
-
},
|
|
3052
|
-
}, handleReviewCommit);
|
|
3053
|
-
tool("review_report", {
|
|
3054
|
-
description: "Get the accumulated code review report for the current project. Returns findings from all reviewed commits.",
|
|
3055
|
-
inputSchema: {
|
|
3056
|
-
json: zod_1.z
|
|
3057
|
-
.boolean()
|
|
3058
|
-
.describe("Return raw JSON instead of text (default: false)")
|
|
3059
|
-
.optional(),
|
|
3060
|
-
},
|
|
3061
|
-
}, handleReviewReport);
|
|
3062
|
-
tool("review_risk", {
|
|
3063
|
-
description: "Deterministic risk ranking of the symbols a commit's diff touches, ordered by blast radius (inbound callers) × test presence × file churn. No LLM required — fast, graph + git only. Use to triage what a change endangers before a deep review.",
|
|
3064
|
-
inputSchema: {
|
|
3065
|
-
commit: zod_1.z
|
|
3066
|
-
.string()
|
|
3067
|
-
.describe("Git ref to analyze (default: HEAD)")
|
|
3068
|
-
.optional(),
|
|
3069
|
-
},
|
|
3070
|
-
}, handleReviewRisk);
|
|
3071
|
-
yield server.connect(transport);
|
|
3072
|
-
// Start the singleton daemon in the background without delaying MCP startup.
|
|
3073
|
-
void Promise.resolve().then(() => __importStar(require("../lib/utils/daemon-client"))).then(({ ensureDaemonRunning }) => ensureDaemonRunning())
|
|
3074
|
-
.catch((e) => console.error("[MCP] Daemon startup failed:", e));
|
|
3075
|
-
// Kick off index readiness check and watcher in background
|
|
3076
|
-
ensureIndexReady().catch((e) => console.error("[MCP] Index readiness check failed:", e));
|
|
3077
|
-
ensureWatcher();
|
|
3078
|
-
// Keep this session's project leased while the session lives. The daemon
|
|
3079
|
-
// only watches projects some session holds.
|
|
3080
|
-
setInterval(() => void ensureWatcher(), MCP_WATCH_LEASE_RENEW_MS).unref();
|
|
3176
|
+
// Local-only, dual-era MCP: client-owned stdin/stdout pipes only.
|
|
3177
|
+
stdio = (0, stdio_1.serveStdio)(createServer, {
|
|
3178
|
+
legacy: "serve",
|
|
3179
|
+
onerror: (error) => console.error("[MCP] Transport error:", error),
|
|
3180
|
+
});
|
|
3081
3181
|
}));
|