grepmax 0.26.45 → 0.26.47
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 +605 -556
- package/dist/lib/daemon/graph-handler.js +1 -1
- package/dist/lib/daemon/rows-handler.js +13 -9
- package/dist/lib/utils/daemon-client.js +4 -1
- package/dist/lib/utils/mcp-execution.js +67 -0
- package/dist/lib/utils/store-access.js +12 -5
- 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");
|
|
@@ -97,6 +97,7 @@ const daemon_client_1 = require("../lib/utils/daemon-client");
|
|
|
97
97
|
const file_utils_1 = require("../lib/utils/file-utils");
|
|
98
98
|
const format_helpers_1 = require("../lib/utils/format-helpers");
|
|
99
99
|
const import_extractor_1 = require("../lib/utils/import-extractor");
|
|
100
|
+
const mcp_execution_1 = require("../lib/utils/mcp-execution");
|
|
100
101
|
const path_containment_1 = require("../lib/utils/path-containment");
|
|
101
102
|
const project_registry_1 = require("../lib/utils/project-registry");
|
|
102
103
|
const project_root_1 = require("../lib/utils/project-root");
|
|
@@ -374,12 +375,22 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
374
375
|
// an MCP session holds no reader marker under ~/.gmax/lancedb.lease/readers/
|
|
375
376
|
// while a daemon serving the verbs is up.
|
|
376
377
|
let _skeletonizer = null;
|
|
377
|
-
let
|
|
378
|
-
|
|
379
|
-
const
|
|
378
|
+
let stdio;
|
|
379
|
+
let watchTimer;
|
|
380
|
+
const watchedRoots = new Set();
|
|
381
|
+
let closing = false;
|
|
382
|
+
// Close the SDK connection before exit so pending requests are aborted.
|
|
383
|
+
const exit = () => {
|
|
384
|
+
if (closing)
|
|
385
|
+
return;
|
|
386
|
+
closing = true;
|
|
387
|
+
clearInterval(watchTimer);
|
|
388
|
+
setTimeout(() => process.exit(0), 1000).unref();
|
|
389
|
+
void Promise.resolve(stdio === null || stdio === void 0 ? void 0 : stdio.close()).finally(() => process.exit(0));
|
|
390
|
+
};
|
|
380
391
|
process.on("SIGINT", exit);
|
|
381
392
|
process.on("SIGTERM", exit);
|
|
382
|
-
//
|
|
393
|
+
// Exit promptly on disconnect, including sessions with a lease timer.
|
|
383
394
|
process.stdin.on("end", exit);
|
|
384
395
|
process.stdin.on("close", exit);
|
|
385
396
|
process.on("unhandledRejection", (reason, promise) => {
|
|
@@ -395,7 +406,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
395
406
|
console.debug = (..._args) => { };
|
|
396
407
|
// --- Project context ---
|
|
397
408
|
const projectRoot = (0, project_root_1.findProjectRoot)(process.cwd());
|
|
398
|
-
const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
|
|
409
|
+
const paths = (0, project_root_1.ensureProjectPaths)(projectRoot, { dryRun: true });
|
|
399
410
|
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; };
|
|
400
411
|
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)(); };
|
|
401
412
|
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)(); };
|
|
@@ -404,20 +415,26 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
404
415
|
// Propagate project root to worker processes
|
|
405
416
|
process.env.GMAX_PROJECT_ROOT = paths.root;
|
|
406
417
|
// --- Store access ---
|
|
418
|
+
function sendMcpCommand(cmd, opts) {
|
|
419
|
+
return (0, mcp_execution_1.mcpOperation)(`Daemon: ${cmd.cmd}`, () => (0, daemon_client_1.sendDaemonCommand)(cmd, Object.assign(Object.assign({}, opts), { signal: (0, mcp_execution_1.mcpSignal)() })));
|
|
420
|
+
}
|
|
407
421
|
/**
|
|
408
422
|
* One read through the access policy, with MCP's fallback allowances. The
|
|
409
423
|
* `inProcess` branch is entered only when no daemon serves the verb.
|
|
410
424
|
*/
|
|
411
425
|
function readStore(name, opts) {
|
|
412
|
-
return (0, store_access_1.withStoreRead)(name, Object.assign(Object.assign(Object.assign({}, opts), MCP_STORE_FALLBACK), { skipDaemon: secondary() }));
|
|
426
|
+
return (0, store_access_1.withStoreRead)(name, Object.assign(Object.assign(Object.assign({ signal: (0, mcp_execution_1.mcpSignal)() }, opts), MCP_STORE_FALLBACK), { skipDaemon: secondary() }));
|
|
413
427
|
}
|
|
414
428
|
/** Open the shared store for the duration of one fallback, then close it. */
|
|
415
429
|
function localStore(fn) {
|
|
416
|
-
return (0, rows_handler_1.withLocalStore)(storeDir(),
|
|
430
|
+
return (0, mcp_execution_1.mcpOperation)("Reading selected local store", () => (0, rows_handler_1.withLocalStore)(storeDir(), (deps) => __awaiter(this, void 0, void 0, function* () {
|
|
431
|
+
(0, mcp_execution_1.checkMcpCancellation)();
|
|
432
|
+
return fn(deps);
|
|
433
|
+
}), storeConfig().vectorDim));
|
|
417
434
|
}
|
|
418
435
|
/** A graph verb with MCP's fallback allowances. */
|
|
419
436
|
function graphVerb(verb, call) {
|
|
420
|
-
return (0, graph_handler_1.callGraphVerb)(verb, Object.assign(Object.assign({}, call), { fallback: Object.assign(Object.assign({}, MCP_STORE_FALLBACK), (secondary() ? { skipDaemon: true } : {})) }));
|
|
437
|
+
return (0, mcp_execution_1.mcpOperation)(`Reading ${verb}`, () => (0, graph_handler_1.callGraphVerb)(verb, Object.assign(Object.assign({}, call), { signal: (0, mcp_execution_1.mcpSignal)(), fallback: Object.assign(Object.assign({}, MCP_STORE_FALLBACK), (secondary() ? { skipDaemon: true } : {})) })));
|
|
421
438
|
}
|
|
422
439
|
/**
|
|
423
440
|
* MCP tools take no `--in`/`--exclude`, so a tool's scope is always the
|
|
@@ -447,7 +464,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
447
464
|
*/
|
|
448
465
|
function searchStore(args) {
|
|
449
466
|
return readStore("search", {
|
|
450
|
-
daemon: () => { var _a; return (
|
|
467
|
+
daemon: () => { var _a; return sendMcpCommand(Object.assign({ cmd: ((_a = args.filters) === null || _a === void 0 ? void 0 : _a.projectRoots) ? "search-v2" : "search" }, args), { timeoutMs: 60000 }); },
|
|
451
468
|
render: (resp) => {
|
|
452
469
|
const warnings = Array.isArray(resp.warnings)
|
|
453
470
|
? resp.warnings.filter((w) => typeof w === "string")
|
|
@@ -466,7 +483,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
466
483
|
: undefined;
|
|
467
484
|
try {
|
|
468
485
|
const searcher = new searcher_1.Searcher(deps.vectorDb, pool);
|
|
469
|
-
return yield searcher.search(args.query, args.limit, { rerank: args.rerank, seeds: args.seeds }, args.filters, args.pathPrefix);
|
|
486
|
+
return yield searcher.search(args.query, args.limit, { rerank: args.rerank, seeds: args.seeds }, args.filters, args.pathPrefix, undefined, (0, mcp_execution_1.mcpSignal)());
|
|
470
487
|
}
|
|
471
488
|
finally {
|
|
472
489
|
yield pool === null || pool === void 0 ? void 0 : pool.destroy();
|
|
@@ -538,7 +555,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
538
555
|
* order. Batched so a per-symbol loop costs one round trip, not hundreds.
|
|
539
556
|
*/
|
|
540
557
|
function locateRows(root, req) {
|
|
541
|
-
return (0, rows_handler_1.readRows)(Object.assign({ name: "locate", projectRoot: root, lancedbDir: storeDir(), vectorDim: storeConfig().vectorDim, scope: projectScope(root), fallback: Object.assign(Object.assign({}, MCP_STORE_FALLBACK), { skipDaemon: secondary() }) }, req));
|
|
558
|
+
return (0, mcp_execution_1.mcpOperation)("Locating indexed rows", () => (0, rows_handler_1.readRows)(Object.assign({ signal: (0, mcp_execution_1.mcpSignal)(), name: "locate", projectRoot: root, lancedbDir: storeDir(), vectorDim: storeConfig().vectorDim, scope: projectScope(root), fallback: Object.assign(Object.assign({}, MCP_STORE_FALLBACK), { skipDaemon: secondary() }) }, req)));
|
|
542
559
|
}
|
|
543
560
|
/**
|
|
544
561
|
* Chunk and file totals for `index_status`, summed per registered project.
|
|
@@ -556,8 +573,9 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
556
573
|
let chunks = 0;
|
|
557
574
|
let files = 0;
|
|
558
575
|
for (const project of projects) {
|
|
576
|
+
(0, mcp_execution_1.checkMcpCancellation)();
|
|
559
577
|
const totals = yield readStore("project-stats", {
|
|
560
|
-
daemon: () => (
|
|
578
|
+
daemon: () => sendMcpCommand({ cmd: "project-stats", root: project.root }, { timeoutMs: 30000 }),
|
|
561
579
|
render: (resp) => ({
|
|
562
580
|
chunks: typeof resp.chunks === "number" ? resp.chunks : 0,
|
|
563
581
|
files: typeof resp.files === "number" ? resp.files : 0,
|
|
@@ -581,7 +599,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
581
599
|
/** `rows.skeleton`: the stored per-file skeleton (and the symbol lookup). */
|
|
582
600
|
function lookupSkeleton(root, req) {
|
|
583
601
|
return readStore("skeleton", {
|
|
584
|
-
daemon: () => (
|
|
602
|
+
daemon: () => sendMcpCommand(Object.assign({ cmd: "rows.skeleton", projectRoot: root }, req), { timeoutMs: 30000 }),
|
|
585
603
|
render: (resp) => { var _a, _b; return ({
|
|
586
604
|
path: ((_a = resp.path) !== null && _a !== void 0 ? _a : null),
|
|
587
605
|
skeleton: ((_b = resp.skeleton) !== null && _b !== void 0 ? _b : null),
|
|
@@ -598,18 +616,6 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
598
616
|
return _skeletonizer;
|
|
599
617
|
});
|
|
600
618
|
}
|
|
601
|
-
// --- Index sync ---
|
|
602
|
-
function ensureIndexReady() {
|
|
603
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
604
|
-
if (_indexReady)
|
|
605
|
-
return;
|
|
606
|
-
const projects = storeProjects();
|
|
607
|
-
const isRegistered = projects.some((p) => p.root === currentRoot());
|
|
608
|
-
if (isRegistered) {
|
|
609
|
-
_indexReady = true;
|
|
610
|
-
}
|
|
611
|
-
});
|
|
612
|
-
}
|
|
613
619
|
// --- Background watcher ---
|
|
614
620
|
// This server's watch lease. It names our PID, so the daemon drops it the
|
|
615
621
|
// minute this session exits; the TTL only covers a wedged server.
|
|
@@ -620,21 +626,34 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
620
626
|
};
|
|
621
627
|
function ensureWatcher(root) {
|
|
622
628
|
return __awaiter(this, void 0, void 0, function* () {
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
629
|
+
return (0, mcp_execution_1.withoutMcpExecution)(() => __awaiter(this, void 0, void 0, function* () {
|
|
630
|
+
var _a;
|
|
631
|
+
try {
|
|
632
|
+
const watchRoot = root !== null && root !== void 0 ? root : (_a = resolveMcpProject(undefined, currentRoot(), storeProjects())) === null || _a === void 0 ? void 0 : _a.root;
|
|
633
|
+
if (!watchRoot ||
|
|
634
|
+
secondary() ||
|
|
635
|
+
(0, stores_1.matchStore)(watchRoot).kind !== "primary")
|
|
636
|
+
return;
|
|
637
|
+
const result = yield (0, watcher_launcher_1.launchWatcher)(watchRoot, WATCH_LEASE);
|
|
638
|
+
if (closing)
|
|
639
|
+
return;
|
|
640
|
+
if (result.ok) {
|
|
641
|
+
watchedRoots.add(watchRoot);
|
|
642
|
+
// Discovery/catalog requests never reach this path. Renew only roots
|
|
643
|
+
// actually read by this session, with one timer for the whole process.
|
|
644
|
+
watchTimer !== null && watchTimer !== void 0 ? watchTimer : (watchTimer = setInterval(() => {
|
|
645
|
+
for (const root of watchedRoots)
|
|
646
|
+
void ensureWatcher(root);
|
|
647
|
+
}, MCP_WATCH_LEASE_RENEW_MS).unref());
|
|
648
|
+
}
|
|
649
|
+
if (result.ok && !result.reused) {
|
|
650
|
+
console.log(`[MCP] Started background watcher for ${watchRoot} (PID: ${result.pid})`);
|
|
651
|
+
}
|
|
633
652
|
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
}
|
|
653
|
+
catch (err) {
|
|
654
|
+
console.error("[MCP] Watcher startup failed:", err);
|
|
655
|
+
}
|
|
656
|
+
}));
|
|
638
657
|
});
|
|
639
658
|
}
|
|
640
659
|
// Resolve the registered project this server scopes to. The server pins to
|
|
@@ -1557,7 +1576,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1557
1576
|
// returns the summary plus the findings this will print, and drops the
|
|
1558
1577
|
// raw `pairs` array entirely.
|
|
1559
1578
|
const result = yield readStore("surprises", {
|
|
1560
|
-
daemon: () => (
|
|
1579
|
+
daemon: () => sendMcpCommand({
|
|
1561
1580
|
cmd: "vector.surprises",
|
|
1562
1581
|
projectRoot: root,
|
|
1563
1582
|
options,
|
|
@@ -1733,7 +1752,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1733
1752
|
? root
|
|
1734
1753
|
: `${root}/`;
|
|
1735
1754
|
const entries = yield readStore("symbols", {
|
|
1736
|
-
daemon: () => (
|
|
1755
|
+
daemon: () => sendMcpCommand({
|
|
1737
1756
|
cmd: "rows.symbols",
|
|
1738
1757
|
projectRoot: root,
|
|
1739
1758
|
pathPrefix: absPrefix,
|
|
@@ -1819,7 +1838,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1819
1838
|
const { chunks, files } = yield indexTotals(projects);
|
|
1820
1839
|
const daemonStatus = secondary()
|
|
1821
1840
|
? { ok: true, projects: [] }
|
|
1822
|
-
: yield (
|
|
1841
|
+
: yield sendMcpCommand({ cmd: "status" });
|
|
1823
1842
|
const watched = (Array.isArray(daemonStatus.projects) ? daemonStatus.projects : []);
|
|
1824
1843
|
const daemonProject = watched.find((p) => p.root === (currentProject === null || currentProject === void 0 ? void 0 : currentProject.root));
|
|
1825
1844
|
// Only use the registry when no daemon answered; sandboxed reads stay IPC-only.
|
|
@@ -1945,7 +1964,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1945
1964
|
// rows it was computed from — the same aggregation `gmax project` runs,
|
|
1946
1965
|
// in whichever process holds the store.
|
|
1947
1966
|
const overview = yield readStore("project", {
|
|
1948
|
-
daemon: () => (
|
|
1967
|
+
daemon: () => sendMcpCommand({ cmd: "rows.project", projectRoot: root }, { timeoutMs: 120000 }),
|
|
1949
1968
|
render: (resp) => resp.overview,
|
|
1950
1969
|
inProcess: () => localStore((deps) => (0, rows_handler_1.runProject)(deps, root)),
|
|
1951
1970
|
});
|
|
@@ -2381,7 +2400,7 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
2381
2400
|
// The source chunk's vector never leaves the store: the daemon looks it
|
|
2382
2401
|
// up, runs the vector search, and returns the ranked chunks.
|
|
2383
2402
|
const result = yield readStore("similar", {
|
|
2384
|
-
daemon: () => (
|
|
2403
|
+
daemon: () => sendMcpCommand({
|
|
2385
2404
|
cmd: "vector.similar",
|
|
2386
2405
|
projectRoot: root,
|
|
2387
2406
|
absPath,
|
|
@@ -2629,519 +2648,549 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
2629
2648
|
});
|
|
2630
2649
|
}
|
|
2631
2650
|
// --- MCP server setup ---
|
|
2632
|
-
//
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
"
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
const { logQuery } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/query-log")));
|
|
2661
|
-
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 : "";
|
|
2662
|
-
const resultLines = text.split("\n").filter((l) => l.trim()).length;
|
|
2663
|
-
logQuery({
|
|
2664
|
-
ts: new Date().toISOString(),
|
|
2665
|
-
source: "mcp",
|
|
2666
|
-
tool: name,
|
|
2667
|
-
query: mcpLogQuery(name, toolArgs),
|
|
2668
|
-
project: currentRoot(),
|
|
2669
|
-
results: resultLines,
|
|
2670
|
-
ms: Date.now() - startMs,
|
|
2671
|
-
error: result.isError ? text.slice(0, 200) : undefined,
|
|
2672
|
-
});
|
|
2673
|
-
}
|
|
2674
|
-
catch (_d) { }
|
|
2675
|
-
});
|
|
2676
|
-
// Register a tool, wrapping its handler with timing + query logging. Zod raw
|
|
2677
|
-
// shapes give us free input validation (the SDK rejects calls that violate
|
|
2678
|
-
// the schema before the handler runs); handlers stay defensively coded.
|
|
2679
|
-
function tool(name, config, handler) {
|
|
2680
|
-
server.registerTool(name, Object.assign(Object.assign(Object.assign({}, config), (mcp_results_1.MCP_READ_OUTPUT_SCHEMAS[name]
|
|
2681
|
-
? {
|
|
2682
|
-
outputSchema: mcp_results_1.MCP_READ_OUTPUT_SCHEMAS[name],
|
|
2683
|
-
annotations: {
|
|
2684
|
-
readOnlyHint: true,
|
|
2685
|
-
destructiveHint: false,
|
|
2686
|
-
openWorldHint: false,
|
|
2687
|
-
},
|
|
2688
|
-
}
|
|
2689
|
-
: {})), { inputSchema: Object.assign({ root: zod_1.z.string().optional() }, config.inputSchema) }), (rawArgs) => __awaiter(this, void 0, void 0, function* () {
|
|
2690
|
-
let args = (rawArgs !== null && rawArgs !== void 0 ? rawArgs : {});
|
|
2691
|
-
const startMs = Date.now();
|
|
2692
|
-
let target = projectRoot;
|
|
2651
|
+
// One fresh catalog per pinned connection or disposable discovery probe.
|
|
2652
|
+
// Building it performs no daemon, model, store or watcher startup.
|
|
2653
|
+
function createServer() {
|
|
2654
|
+
const server = new server_1.McpServer({
|
|
2655
|
+
name: "gmax",
|
|
2656
|
+
version: JSON.parse(fs.readFileSync(path.join(__dirname, "../../package.json"), {
|
|
2657
|
+
encoding: "utf-8",
|
|
2658
|
+
})).version,
|
|
2659
|
+
}, {
|
|
2660
|
+
capabilities: {
|
|
2661
|
+
tools: {},
|
|
2662
|
+
},
|
|
2663
|
+
instructions: "gmax is a local semantic code-search index over one or more projects. " +
|
|
2664
|
+
"By default tools operate on the project at the server's working directory. " +
|
|
2665
|
+
"If you are NOT inside a specific repo, or want a different one: call " +
|
|
2666
|
+
'`list_projects` to see what\'s indexed, then pass `projects:"name"` (or ' +
|
|
2667
|
+
'`scope:"all"`) to `semantic_search`. When the working directory isn\'t an ' +
|
|
2668
|
+
"indexed project, `semantic_search` errors instead of searching everything " +
|
|
2669
|
+
"implicitly. Prefer `semantic_search` (detail:'pointer') for discovery, then " +
|
|
2670
|
+
"`extract_symbol`/`peek_symbol`/`code_skeleton` to read, and " +
|
|
2671
|
+
"`trace_calls`/`impact_analysis`/`find_tests` for call-graph questions. " +
|
|
2672
|
+
"Use `audit` and experimental `surprising_connections` for project orientation.",
|
|
2673
|
+
});
|
|
2674
|
+
// Best-effort query logging, applied uniformly to every tool exactly as the
|
|
2675
|
+
// old single CallToolRequestSchema dispatch did before each return.
|
|
2676
|
+
const logToolCall = (name_1, toolArgs_1, startMs_1, result_1, ...args_1) => __awaiter(this, [name_1, toolArgs_1, startMs_1, result_1, ...args_1], void 0, function* (name, toolArgs, startMs, result, root = currentRoot()) {
|
|
2677
|
+
var _a, _b;
|
|
2678
|
+
var _c;
|
|
2693
2679
|
try {
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2680
|
+
const { logQuery } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/query-log")));
|
|
2681
|
+
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 : "";
|
|
2682
|
+
const resultLines = text.split("\n").filter((l) => l.trim()).length;
|
|
2683
|
+
logQuery({
|
|
2684
|
+
ts: new Date().toISOString(),
|
|
2685
|
+
source: "mcp",
|
|
2686
|
+
tool: name,
|
|
2687
|
+
query: mcpLogQuery(name, toolArgs),
|
|
2688
|
+
project: root,
|
|
2689
|
+
results: resultLines,
|
|
2690
|
+
ms: Date.now() - startMs,
|
|
2691
|
+
error: result.isError ? text.slice(0, 200) : undefined,
|
|
2692
|
+
});
|
|
2693
|
+
}
|
|
2694
|
+
catch (_d) { }
|
|
2695
|
+
});
|
|
2696
|
+
// Register a tool, wrapping its handler with timing + query logging. Zod raw
|
|
2697
|
+
// shapes give us free input validation (the SDK rejects calls that violate
|
|
2698
|
+
// the schema before the handler runs); handlers stay defensively coded.
|
|
2699
|
+
function tool(name, config, handler) {
|
|
2700
|
+
server.registerTool(name, Object.assign(Object.assign(Object.assign({}, config), (mcp_results_1.MCP_READ_OUTPUT_SCHEMAS[name]
|
|
2701
|
+
? {
|
|
2702
|
+
outputSchema: zod_1.z.object(mcp_results_1.MCP_READ_OUTPUT_SCHEMAS[name]),
|
|
2703
|
+
annotations: {
|
|
2704
|
+
readOnlyHint: true,
|
|
2705
|
+
destructiveHint: false,
|
|
2706
|
+
openWorldHint: false,
|
|
2707
|
+
},
|
|
2708
2708
|
}
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
(0,
|
|
2709
|
+
: {})), { inputSchema: zod_1.z.object(Object.assign({ root: zod_1.z.string().optional() }, config.inputSchema)) }), (rawArgs, context) => __awaiter(this, void 0, void 0, function* () {
|
|
2710
|
+
return (0, mcp_execution_1.withMcpExecution)(context === null || context === void 0 ? void 0 : context.mcpReq, () => __awaiter(this, void 0, void 0, function* () {
|
|
2711
|
+
let args = (rawArgs !== null && rawArgs !== void 0 ? rawArgs : {});
|
|
2712
|
+
const startMs = Date.now();
|
|
2713
|
+
let target = projectRoot;
|
|
2714
|
+
try {
|
|
2715
|
+
(0, mcp_execution_1.checkMcpCancellation)();
|
|
2716
|
+
if (typeof args.root === "string" && args.root.trim()) {
|
|
2717
|
+
const raw = args.root.trim();
|
|
2718
|
+
if (path.isAbsolute(raw) || raw.includes(path.sep))
|
|
2719
|
+
target = path.resolve(raw);
|
|
2720
|
+
else {
|
|
2721
|
+
const projects = [
|
|
2722
|
+
...(0, project_registry_1.listProjects)(),
|
|
2723
|
+
...(0, store_context_1.storeInventory)().flatMap((store) => store.projects),
|
|
2724
|
+
].filter((project, i, all) => all.findIndex((p) => p.root === project.root) === i);
|
|
2725
|
+
const selected = resolveMcpProject(raw, projectRoot, projects);
|
|
2726
|
+
if (!selected)
|
|
2727
|
+
return err(`Unknown or ambiguous registered project: ${raw}. Use list_projects or an exact root.`);
|
|
2728
|
+
target = selected.root;
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
return yield (0, store_context_1.withStoreContext)(target, () => __awaiter(this, void 0, void 0, function* () {
|
|
2732
|
+
if (args.root !== undefined) {
|
|
2733
|
+
const selected = resolveMcpProject(args.root, currentRoot(), storeProjects());
|
|
2734
|
+
if (!selected)
|
|
2735
|
+
return err(`Unknown registered project: ${args.root}. Use list_projects to select a project name or exact root.`);
|
|
2736
|
+
args = Object.assign(Object.assign({}, args), { root: selected.root });
|
|
2737
|
+
(0, store_context_1.currentStoreContext)().root = selected.root;
|
|
2738
|
+
}
|
|
2739
|
+
const result = yield (0, mcp_execution_1.mcpOperation)(`Starting ${name}`, () => handler(args));
|
|
2740
|
+
yield logToolCall(name, args, startMs, result);
|
|
2741
|
+
return result;
|
|
2742
|
+
}));
|
|
2743
|
+
}
|
|
2744
|
+
catch (error) {
|
|
2745
|
+
const result = toolError("Tool failed", error);
|
|
2746
|
+
yield logToolCall(name, args, startMs, result, target);
|
|
2747
|
+
return result;
|
|
2716
2748
|
}
|
|
2717
|
-
const result = yield handler(args);
|
|
2718
|
-
yield logToolCall(name, args, startMs, result);
|
|
2719
|
-
return result;
|
|
2720
2749
|
}));
|
|
2721
|
-
}
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2750
|
+
}));
|
|
2751
|
+
}
|
|
2752
|
+
tool("semantic_search", {
|
|
2753
|
+
description: "Search code by meaning. Use scope:'all' for cross-project. Prefer CLI: gmax \"query\" --plain",
|
|
2754
|
+
inputSchema: {
|
|
2755
|
+
query: zod_1.z
|
|
2756
|
+
.string()
|
|
2757
|
+
.describe("Natural language query (5+ words recommended)"),
|
|
2758
|
+
limit: zod_1.z
|
|
2759
|
+
.number()
|
|
2760
|
+
.describe("Max results (default 3, max 50)")
|
|
2761
|
+
.optional(),
|
|
2762
|
+
root: zod_1.z
|
|
2763
|
+
.string()
|
|
2764
|
+
.describe("Registered project name or exact root")
|
|
2765
|
+
.optional(),
|
|
2766
|
+
path: zod_1.z
|
|
2767
|
+
.string()
|
|
2768
|
+
.describe("Path prefix filter (e.g. 'src/auth/')")
|
|
2769
|
+
.optional(),
|
|
2770
|
+
detail: zod_1.z
|
|
2771
|
+
.string()
|
|
2772
|
+
.describe("'pointer' (default), 'code', or 'full'")
|
|
2773
|
+
.optional(),
|
|
2774
|
+
min_score: zod_1.z
|
|
2775
|
+
.number()
|
|
2776
|
+
.describe("Min score 0-1 (default 0)")
|
|
2777
|
+
.optional(),
|
|
2778
|
+
max_per_file: zod_1.z
|
|
2779
|
+
.number()
|
|
2780
|
+
.describe("Max results per file")
|
|
2781
|
+
.optional(),
|
|
2782
|
+
file: zod_1.z
|
|
2783
|
+
.string()
|
|
2784
|
+
.describe("Filename filter (e.g. 'syncer.ts')")
|
|
2785
|
+
.optional(),
|
|
2786
|
+
exclude: zod_1.z
|
|
2787
|
+
.string()
|
|
2788
|
+
.describe("Exclude path prefix (e.g. 'tests/')")
|
|
2789
|
+
.optional(),
|
|
2790
|
+
language: zod_1.z
|
|
2791
|
+
.string()
|
|
2792
|
+
.describe("Extension filter (e.g. 'ts', 'py')")
|
|
2793
|
+
.optional(),
|
|
2794
|
+
role: zod_1.z
|
|
2795
|
+
.string()
|
|
2796
|
+
.describe("'ORCHESTRATION', 'DEFINITION', or 'IMPLEMENTATION'")
|
|
2797
|
+
.optional(),
|
|
2798
|
+
context_lines: zod_1.z
|
|
2799
|
+
.number()
|
|
2800
|
+
.describe("Lines before/after chunk (max 20)")
|
|
2801
|
+
.optional(),
|
|
2802
|
+
mode: zod_1.z
|
|
2803
|
+
.string()
|
|
2804
|
+
.describe("'default' or 'symbol' (appends call graph)")
|
|
2805
|
+
.optional(),
|
|
2806
|
+
include_imports: zod_1.z
|
|
2807
|
+
.boolean()
|
|
2808
|
+
.describe("Prepend file imports to results")
|
|
2809
|
+
.optional(),
|
|
2810
|
+
name_pattern: zod_1.z
|
|
2811
|
+
.string()
|
|
2812
|
+
.describe("Regex filter on symbol name")
|
|
2813
|
+
.optional(),
|
|
2814
|
+
scope: zod_1.z
|
|
2815
|
+
.string()
|
|
2816
|
+
.describe("'project' (default) or 'all' (search everything)")
|
|
2817
|
+
.optional(),
|
|
2818
|
+
projects: zod_1.z
|
|
2819
|
+
.string()
|
|
2820
|
+
.describe("Project names to include (comma-separated)")
|
|
2821
|
+
.optional(),
|
|
2822
|
+
exclude_projects: zod_1.z
|
|
2823
|
+
.string()
|
|
2824
|
+
.describe("Project names to exclude (comma-separated)")
|
|
2825
|
+
.optional(),
|
|
2826
|
+
seed_files: zod_1.z
|
|
2827
|
+
.string()
|
|
2828
|
+
.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.")
|
|
2829
|
+
.optional(),
|
|
2830
|
+
seed_symbols: zod_1.z
|
|
2831
|
+
.string()
|
|
2832
|
+
.describe("Bias results toward identifiers you're working with: comma-separated symbol names. Chunks defining a seeded symbol are preferred over mere callers.")
|
|
2833
|
+
.optional(),
|
|
2834
|
+
},
|
|
2835
|
+
}, handleSemanticSearch);
|
|
2836
|
+
tool("code_skeleton", {
|
|
2837
|
+
description: "File structure with bodies collapsed (~4x fewer tokens). Accepts file, directory, or comma-separated paths.",
|
|
2838
|
+
inputSchema: {
|
|
2839
|
+
target: zod_1.z
|
|
2840
|
+
.string()
|
|
2841
|
+
.describe("File, directory, or comma-separated paths"),
|
|
2842
|
+
limit: zod_1.z
|
|
2843
|
+
.number()
|
|
2844
|
+
.describe("Max files for directory mode (default 10)")
|
|
2845
|
+
.optional(),
|
|
2846
|
+
format: zod_1.z
|
|
2847
|
+
.string()
|
|
2848
|
+
.describe("'text' (default) or 'json'")
|
|
2849
|
+
.optional(),
|
|
2850
|
+
},
|
|
2851
|
+
}, handleCodeSkeleton);
|
|
2852
|
+
tool("trace_calls", {
|
|
2853
|
+
description: "Call graph: importers, callers (multi-hop), callees with file:line.",
|
|
2854
|
+
inputSchema: {
|
|
2855
|
+
symbol: zod_1.z.string().describe("Function/class name to trace"),
|
|
2856
|
+
depth: zod_1.z
|
|
2857
|
+
.number()
|
|
2858
|
+
.describe("Caller depth (default 1, max 3)")
|
|
2859
|
+
.optional(),
|
|
2860
|
+
},
|
|
2861
|
+
}, handleTraceCalls);
|
|
2862
|
+
tool("extract_symbol", {
|
|
2863
|
+
description: "Extract complete function/class body by symbol name.",
|
|
2864
|
+
inputSchema: {
|
|
2865
|
+
symbol: zod_1.z.string().describe("Symbol name to extract"),
|
|
2866
|
+
root: zod_1.z
|
|
2867
|
+
.string()
|
|
2868
|
+
.describe("Project root (absolute path)")
|
|
2869
|
+
.optional(),
|
|
2870
|
+
include_imports: zod_1.z
|
|
2871
|
+
.boolean()
|
|
2872
|
+
.describe("Prepend file imports")
|
|
2873
|
+
.optional(),
|
|
2874
|
+
},
|
|
2875
|
+
}, handleExtractSymbol);
|
|
2876
|
+
tool("peek_symbol", {
|
|
2877
|
+
description: "Compact symbol overview: signature + callers + callees.",
|
|
2878
|
+
inputSchema: {
|
|
2879
|
+
symbol: zod_1.z.string().describe("Symbol name"),
|
|
2880
|
+
root: zod_1.z
|
|
2881
|
+
.string()
|
|
2882
|
+
.describe("Project root (absolute path)")
|
|
2883
|
+
.optional(),
|
|
2884
|
+
depth: zod_1.z
|
|
2885
|
+
.number()
|
|
2886
|
+
.describe("Caller depth (default 1, max 3)")
|
|
2887
|
+
.optional(),
|
|
2888
|
+
},
|
|
2889
|
+
}, handlePeekSymbol);
|
|
2890
|
+
tool("dead", {
|
|
2891
|
+
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.",
|
|
2892
|
+
inputSchema: {
|
|
2893
|
+
symbol: zod_1.z.string().describe("Symbol name to check"),
|
|
2894
|
+
root: zod_1.z
|
|
2895
|
+
.string()
|
|
2896
|
+
.describe("Project root (absolute path)")
|
|
2897
|
+
.optional(),
|
|
2898
|
+
},
|
|
2899
|
+
}, handleDead);
|
|
2900
|
+
tool("audit", {
|
|
2901
|
+
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.",
|
|
2902
|
+
inputSchema: {
|
|
2903
|
+
root: zod_1.z
|
|
2904
|
+
.string()
|
|
2905
|
+
.describe("Project root (default: current)")
|
|
2906
|
+
.optional(),
|
|
2907
|
+
top: zod_1.z
|
|
2908
|
+
.number()
|
|
2909
|
+
.describe("How many of each category to return (default 10)")
|
|
2910
|
+
.optional(),
|
|
2911
|
+
},
|
|
2912
|
+
}, handleAudit);
|
|
2913
|
+
tool("surprising_connections", {
|
|
2914
|
+
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.",
|
|
2915
|
+
inputSchema: {
|
|
2916
|
+
experimental: zod_1.z
|
|
2917
|
+
.boolean()
|
|
2918
|
+
.describe("Must be true to acknowledge experimental signal quality"),
|
|
2919
|
+
root: zod_1.z
|
|
2920
|
+
.string()
|
|
2921
|
+
.describe("Project root (default: current)")
|
|
2922
|
+
.optional(),
|
|
2923
|
+
sample: zod_1.z
|
|
2924
|
+
.number()
|
|
2925
|
+
.describe("Indexed code chunks to sample (default 160, max 10000)")
|
|
2926
|
+
.optional(),
|
|
2927
|
+
neighbors: zod_1.z
|
|
2928
|
+
.number()
|
|
2929
|
+
.describe("Nearest neighbors per sampled chunk (default 20, max 200)")
|
|
2930
|
+
.optional(),
|
|
2931
|
+
top: zod_1.z
|
|
2932
|
+
.number()
|
|
2933
|
+
.describe("Grouped findings to return (default 10, max 100)")
|
|
2934
|
+
.optional(),
|
|
2935
|
+
dir_depth: zod_1.z
|
|
2936
|
+
.number()
|
|
2937
|
+
.describe("Directory bucket depth considered unsurprising")
|
|
2938
|
+
.optional(),
|
|
2939
|
+
min_similarity: zod_1.z
|
|
2940
|
+
.number()
|
|
2941
|
+
.describe("Minimum similarity 0-1 (default 0)")
|
|
2942
|
+
.optional(),
|
|
2943
|
+
max_rows: zod_1.z
|
|
2944
|
+
.number()
|
|
2945
|
+
.describe(`Maximum indexed rows to scan (default 50000, max ${surprising_connections_1.MAX_SURPRISE_ROWS})`)
|
|
2946
|
+
.optional(),
|
|
2947
|
+
in: zod_1.z
|
|
2948
|
+
.string()
|
|
2949
|
+
.describe("Restrict to a sub-path of the project")
|
|
2950
|
+
.optional(),
|
|
2951
|
+
exclude: zod_1.z
|
|
2952
|
+
.string()
|
|
2953
|
+
.describe("Exclude a sub-path of the project")
|
|
2954
|
+
.optional(),
|
|
2955
|
+
include_tests: zod_1.z
|
|
2956
|
+
.boolean()
|
|
2957
|
+
.describe("Include test files")
|
|
2958
|
+
.optional(),
|
|
2959
|
+
include_eval: zod_1.z
|
|
2960
|
+
.boolean()
|
|
2961
|
+
.describe("Include eval/experiment/script files")
|
|
2962
|
+
.optional(),
|
|
2963
|
+
},
|
|
2964
|
+
}, handleSurprisingConnections);
|
|
2965
|
+
tool("get_neighbors", {
|
|
2966
|
+
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`.",
|
|
2967
|
+
inputSchema: {
|
|
2968
|
+
symbol: zod_1.z.string().describe("Starting symbol"),
|
|
2969
|
+
direction: zod_1.z
|
|
2970
|
+
.enum(["callers", "callees"])
|
|
2971
|
+
.describe("Edge direction (default callees)")
|
|
2972
|
+
.optional(),
|
|
2973
|
+
max_hops: zod_1.z
|
|
2974
|
+
.number()
|
|
2975
|
+
.describe("Max hops (default 2, max 5)")
|
|
2976
|
+
.optional(),
|
|
2977
|
+
root: zod_1.z
|
|
2978
|
+
.string()
|
|
2979
|
+
.describe("Project root (absolute path)")
|
|
2980
|
+
.optional(),
|
|
2981
|
+
},
|
|
2982
|
+
}, handleGetNeighbors);
|
|
2983
|
+
tool("find_paths", {
|
|
2984
|
+
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.",
|
|
2985
|
+
inputSchema: {
|
|
2986
|
+
from: zod_1.z.string().describe("Start symbol"),
|
|
2987
|
+
to: zod_1.z.string().describe("Target symbol"),
|
|
2988
|
+
direction: zod_1.z
|
|
2989
|
+
.enum(["callers", "callees"])
|
|
2990
|
+
.describe("Search direction (default callees)")
|
|
2991
|
+
.optional(),
|
|
2992
|
+
max_hops: zod_1.z
|
|
2993
|
+
.number()
|
|
2994
|
+
.describe("Max hops (default 6, max 10)")
|
|
2995
|
+
.optional(),
|
|
2996
|
+
root: zod_1.z
|
|
2997
|
+
.string()
|
|
2998
|
+
.describe("Project root (absolute path)")
|
|
2999
|
+
.optional(),
|
|
3000
|
+
},
|
|
3001
|
+
}, handleFindPaths);
|
|
3002
|
+
tool("subgraph_for_files", {
|
|
3003
|
+
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.",
|
|
3004
|
+
inputSchema: {
|
|
3005
|
+
files: zod_1.z
|
|
3006
|
+
.array(zod_1.z.string())
|
|
3007
|
+
.describe("File paths (absolute or root-relative)"),
|
|
3008
|
+
root: zod_1.z
|
|
3009
|
+
.string()
|
|
3010
|
+
.describe("Project root (absolute path)")
|
|
3011
|
+
.optional(),
|
|
3012
|
+
},
|
|
3013
|
+
}, handleSubgraphForFiles);
|
|
3014
|
+
tool("list_symbols", {
|
|
3015
|
+
description: "List indexed symbols with role and export status.",
|
|
3016
|
+
inputSchema: {
|
|
3017
|
+
pattern: zod_1.z
|
|
3018
|
+
.string()
|
|
3019
|
+
.describe("Name filter (case-insensitive)")
|
|
3020
|
+
.optional(),
|
|
3021
|
+
limit: zod_1.z.number().describe("Max results (default 20)").optional(),
|
|
3022
|
+
path: zod_1.z.string().describe("Path prefix filter").optional(),
|
|
3023
|
+
},
|
|
3024
|
+
}, handleListSymbols);
|
|
3025
|
+
tool("index_status", {
|
|
3026
|
+
description: "Index health: chunks, files, projects, watcher status.",
|
|
3027
|
+
inputSchema: {},
|
|
3028
|
+
_meta: { "anthropic/alwaysLoad": true },
|
|
3029
|
+
}, handleIndexStatus);
|
|
3030
|
+
tool("list_projects", {
|
|
3031
|
+
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).",
|
|
3032
|
+
inputSchema: {},
|
|
3033
|
+
_meta: { "anthropic/alwaysLoad": true },
|
|
3034
|
+
}, handleListProjects);
|
|
3035
|
+
tool("summarize_directory", {
|
|
3036
|
+
description: "Generate LLM summaries for indexed chunks.",
|
|
3037
|
+
inputSchema: {
|
|
3038
|
+
path: zod_1.z
|
|
3039
|
+
.string()
|
|
3040
|
+
.describe("Directory to summarize (default: project root)")
|
|
3041
|
+
.optional(),
|
|
3042
|
+
limit: zod_1.z
|
|
3043
|
+
.number()
|
|
3044
|
+
.describe("Max chunks (default 200, max 5000)")
|
|
3045
|
+
.optional(),
|
|
3046
|
+
},
|
|
3047
|
+
}, handleSummarizeDirectory);
|
|
3048
|
+
tool("summarize_project", {
|
|
3049
|
+
description: "Project overview: languages, structure, roles, key symbols, entry points.",
|
|
3050
|
+
inputSchema: {
|
|
3051
|
+
root: zod_1.z
|
|
3052
|
+
.string()
|
|
3053
|
+
.describe("Project root (default: current)")
|
|
3054
|
+
.optional(),
|
|
3055
|
+
},
|
|
3056
|
+
}, handleSummarizeProject);
|
|
3057
|
+
tool("related_files", {
|
|
3058
|
+
description: "Find dependencies and dependents of a file by shared symbols.",
|
|
3059
|
+
inputSchema: {
|
|
3060
|
+
file: zod_1.z.string().describe("File path relative to project root"),
|
|
3061
|
+
limit: zod_1.z
|
|
3062
|
+
.number()
|
|
3063
|
+
.describe("Max results per direction (default 10)")
|
|
3064
|
+
.optional(),
|
|
3065
|
+
},
|
|
3066
|
+
}, handleRelatedFiles);
|
|
3067
|
+
tool("recent_changes", {
|
|
3068
|
+
description: "Recently modified indexed files with timestamps.",
|
|
3069
|
+
inputSchema: {
|
|
3070
|
+
limit: zod_1.z
|
|
3071
|
+
.number()
|
|
3072
|
+
.describe("Max files to return (default 20)")
|
|
3073
|
+
.optional(),
|
|
3074
|
+
root: zod_1.z
|
|
3075
|
+
.string()
|
|
3076
|
+
.describe("Project root (defaults to current project)")
|
|
3077
|
+
.optional(),
|
|
3078
|
+
},
|
|
3079
|
+
}, handleRecentChanges);
|
|
3080
|
+
tool("diff_changes", {
|
|
3081
|
+
description: "Search code scoped to git changes. Omit ref for uncommitted changes.",
|
|
3082
|
+
inputSchema: {
|
|
3083
|
+
ref: zod_1.z
|
|
3084
|
+
.string()
|
|
3085
|
+
.describe("Git ref to diff against (e.g. main, HEAD~5)")
|
|
3086
|
+
.optional(),
|
|
3087
|
+
query: zod_1.z
|
|
3088
|
+
.string()
|
|
3089
|
+
.describe("Semantic search within changed files")
|
|
3090
|
+
.optional(),
|
|
3091
|
+
limit: zod_1.z.number().describe("Max results (default 10)").optional(),
|
|
3092
|
+
role: zod_1.z
|
|
3093
|
+
.string()
|
|
3094
|
+
.describe("Filter by role: ORCHESTRATION, DEFINITION, IMPLEMENTATION")
|
|
3095
|
+
.optional(),
|
|
3096
|
+
},
|
|
3097
|
+
}, handleDiffChanges);
|
|
3098
|
+
tool("find_tests", {
|
|
3099
|
+
description: "Find tests that exercise a symbol or file via reverse call graph.",
|
|
3100
|
+
inputSchema: {
|
|
3101
|
+
target: zod_1.z.string().describe("Symbol name or file path"),
|
|
3102
|
+
depth: zod_1.z
|
|
3103
|
+
.number()
|
|
3104
|
+
.describe("Caller traversal depth 1-3 (default 1)")
|
|
3105
|
+
.optional(),
|
|
3106
|
+
},
|
|
3107
|
+
}, handleFindTests);
|
|
3108
|
+
tool("impact_analysis", {
|
|
3109
|
+
description: "Change impact: dependents and affected tests for a symbol or file.",
|
|
3110
|
+
inputSchema: {
|
|
3111
|
+
target: zod_1.z.string().describe("Symbol name or file path"),
|
|
3112
|
+
depth: zod_1.z
|
|
3113
|
+
.number()
|
|
3114
|
+
.describe("Caller traversal depth 1-3 (default 1)")
|
|
3115
|
+
.optional(),
|
|
3116
|
+
rollup: zod_1.z
|
|
3117
|
+
.boolean()
|
|
3118
|
+
.describe("Return export/package rollup TSV rows")
|
|
3119
|
+
.optional(),
|
|
3120
|
+
top: zod_1.z
|
|
3121
|
+
.number()
|
|
3122
|
+
.describe("Max rows per rollup section (default 10)")
|
|
3123
|
+
.optional(),
|
|
3124
|
+
},
|
|
3125
|
+
}, handleImpactAnalysis);
|
|
3126
|
+
tool("find_similar", {
|
|
3127
|
+
description: "Find semantically similar code using vector similarity.",
|
|
3128
|
+
inputSchema: {
|
|
3129
|
+
target: zod_1.z.string().describe("Symbol name or file path"),
|
|
3130
|
+
limit: zod_1.z.number().describe("Max results (default 5)").optional(),
|
|
3131
|
+
threshold: zod_1.z
|
|
3132
|
+
.number()
|
|
3133
|
+
.describe("Min similarity 0-1 (default 0)")
|
|
3134
|
+
.optional(),
|
|
3135
|
+
},
|
|
3136
|
+
}, handleFindSimilar);
|
|
3137
|
+
tool("build_context", {
|
|
3138
|
+
description: "Token-budgeted topic summary (search + skeleton + extract).",
|
|
3139
|
+
inputSchema: {
|
|
3140
|
+
topic: zod_1.z
|
|
3141
|
+
.string()
|
|
3142
|
+
.describe("Natural language topic or directory path"),
|
|
3143
|
+
budget: zod_1.z.number().describe("Max tokens (default 4000)").optional(),
|
|
3144
|
+
limit: zod_1.z
|
|
3145
|
+
.number()
|
|
3146
|
+
.describe("Search result limit (default 10)")
|
|
3147
|
+
.optional(),
|
|
3148
|
+
},
|
|
3149
|
+
}, handleBuildContext);
|
|
3150
|
+
tool("investigate", {
|
|
3151
|
+
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).",
|
|
3152
|
+
inputSchema: {
|
|
3153
|
+
question: zod_1.z
|
|
3154
|
+
.string()
|
|
3155
|
+
.describe("Natural language question about the codebase"),
|
|
3156
|
+
max_rounds: zod_1.z
|
|
3157
|
+
.number()
|
|
3158
|
+
.describe("Max tool-call rounds (default 10)")
|
|
3159
|
+
.optional(),
|
|
3160
|
+
},
|
|
3161
|
+
}, handleInvestigate);
|
|
3162
|
+
tool("review_commit", {
|
|
3163
|
+
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).",
|
|
3164
|
+
inputSchema: {
|
|
3165
|
+
commit: zod_1.z
|
|
3166
|
+
.string()
|
|
3167
|
+
.describe("Git ref to review (default: HEAD)")
|
|
3168
|
+
.optional(),
|
|
3169
|
+
},
|
|
3170
|
+
}, handleReviewCommit);
|
|
3171
|
+
tool("review_report", {
|
|
3172
|
+
description: "Get the accumulated code review report for the current project. Returns findings from all reviewed commits.",
|
|
3173
|
+
inputSchema: {
|
|
3174
|
+
json: zod_1.z
|
|
3175
|
+
.boolean()
|
|
3176
|
+
.describe("Return raw JSON instead of text (default: false)")
|
|
3177
|
+
.optional(),
|
|
3178
|
+
},
|
|
3179
|
+
}, handleReviewReport);
|
|
3180
|
+
tool("review_risk", {
|
|
3181
|
+
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.",
|
|
3182
|
+
inputSchema: {
|
|
3183
|
+
commit: zod_1.z
|
|
3184
|
+
.string()
|
|
3185
|
+
.describe("Git ref to analyze (default: HEAD)")
|
|
3186
|
+
.optional(),
|
|
3187
|
+
},
|
|
3188
|
+
}, handleReviewRisk);
|
|
3189
|
+
return server;
|
|
2726
3190
|
}
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
.describe("Natural language query (5+ words recommended)"),
|
|
2733
|
-
limit: zod_1.z
|
|
2734
|
-
.number()
|
|
2735
|
-
.describe("Max results (default 3, max 50)")
|
|
2736
|
-
.optional(),
|
|
2737
|
-
root: zod_1.z
|
|
2738
|
-
.string()
|
|
2739
|
-
.describe("Registered project name or exact root")
|
|
2740
|
-
.optional(),
|
|
2741
|
-
path: zod_1.z
|
|
2742
|
-
.string()
|
|
2743
|
-
.describe("Path prefix filter (e.g. 'src/auth/')")
|
|
2744
|
-
.optional(),
|
|
2745
|
-
detail: zod_1.z
|
|
2746
|
-
.string()
|
|
2747
|
-
.describe("'pointer' (default), 'code', or 'full'")
|
|
2748
|
-
.optional(),
|
|
2749
|
-
min_score: zod_1.z
|
|
2750
|
-
.number()
|
|
2751
|
-
.describe("Min score 0-1 (default 0)")
|
|
2752
|
-
.optional(),
|
|
2753
|
-
max_per_file: zod_1.z.number().describe("Max results per file").optional(),
|
|
2754
|
-
file: zod_1.z
|
|
2755
|
-
.string()
|
|
2756
|
-
.describe("Filename filter (e.g. 'syncer.ts')")
|
|
2757
|
-
.optional(),
|
|
2758
|
-
exclude: zod_1.z
|
|
2759
|
-
.string()
|
|
2760
|
-
.describe("Exclude path prefix (e.g. 'tests/')")
|
|
2761
|
-
.optional(),
|
|
2762
|
-
language: zod_1.z
|
|
2763
|
-
.string()
|
|
2764
|
-
.describe("Extension filter (e.g. 'ts', 'py')")
|
|
2765
|
-
.optional(),
|
|
2766
|
-
role: zod_1.z
|
|
2767
|
-
.string()
|
|
2768
|
-
.describe("'ORCHESTRATION', 'DEFINITION', or 'IMPLEMENTATION'")
|
|
2769
|
-
.optional(),
|
|
2770
|
-
context_lines: zod_1.z
|
|
2771
|
-
.number()
|
|
2772
|
-
.describe("Lines before/after chunk (max 20)")
|
|
2773
|
-
.optional(),
|
|
2774
|
-
mode: zod_1.z
|
|
2775
|
-
.string()
|
|
2776
|
-
.describe("'default' or 'symbol' (appends call graph)")
|
|
2777
|
-
.optional(),
|
|
2778
|
-
include_imports: zod_1.z
|
|
2779
|
-
.boolean()
|
|
2780
|
-
.describe("Prepend file imports to results")
|
|
2781
|
-
.optional(),
|
|
2782
|
-
name_pattern: zod_1.z
|
|
2783
|
-
.string()
|
|
2784
|
-
.describe("Regex filter on symbol name")
|
|
2785
|
-
.optional(),
|
|
2786
|
-
scope: zod_1.z
|
|
2787
|
-
.string()
|
|
2788
|
-
.describe("'project' (default) or 'all' (search everything)")
|
|
2789
|
-
.optional(),
|
|
2790
|
-
projects: zod_1.z
|
|
2791
|
-
.string()
|
|
2792
|
-
.describe("Project names to include (comma-separated)")
|
|
2793
|
-
.optional(),
|
|
2794
|
-
exclude_projects: zod_1.z
|
|
2795
|
-
.string()
|
|
2796
|
-
.describe("Project names to exclude (comma-separated)")
|
|
2797
|
-
.optional(),
|
|
2798
|
-
seed_files: zod_1.z
|
|
2799
|
-
.string()
|
|
2800
|
-
.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.")
|
|
2801
|
-
.optional(),
|
|
2802
|
-
seed_symbols: zod_1.z
|
|
2803
|
-
.string()
|
|
2804
|
-
.describe("Bias results toward identifiers you're working with: comma-separated symbol names. Chunks defining a seeded symbol are preferred over mere callers.")
|
|
2805
|
-
.optional(),
|
|
2806
|
-
},
|
|
2807
|
-
}, handleSemanticSearch);
|
|
2808
|
-
tool("code_skeleton", {
|
|
2809
|
-
description: "File structure with bodies collapsed (~4x fewer tokens). Accepts file, directory, or comma-separated paths.",
|
|
2810
|
-
inputSchema: {
|
|
2811
|
-
target: zod_1.z
|
|
2812
|
-
.string()
|
|
2813
|
-
.describe("File, directory, or comma-separated paths"),
|
|
2814
|
-
limit: zod_1.z
|
|
2815
|
-
.number()
|
|
2816
|
-
.describe("Max files for directory mode (default 10)")
|
|
2817
|
-
.optional(),
|
|
2818
|
-
format: zod_1.z.string().describe("'text' (default) or 'json'").optional(),
|
|
2819
|
-
},
|
|
2820
|
-
}, handleCodeSkeleton);
|
|
2821
|
-
tool("trace_calls", {
|
|
2822
|
-
description: "Call graph: importers, callers (multi-hop), callees with file:line.",
|
|
2823
|
-
inputSchema: {
|
|
2824
|
-
symbol: zod_1.z.string().describe("Function/class name to trace"),
|
|
2825
|
-
depth: zod_1.z
|
|
2826
|
-
.number()
|
|
2827
|
-
.describe("Caller depth (default 1, max 3)")
|
|
2828
|
-
.optional(),
|
|
2829
|
-
},
|
|
2830
|
-
}, handleTraceCalls);
|
|
2831
|
-
tool("extract_symbol", {
|
|
2832
|
-
description: "Extract complete function/class body by symbol name.",
|
|
2833
|
-
inputSchema: {
|
|
2834
|
-
symbol: zod_1.z.string().describe("Symbol name to extract"),
|
|
2835
|
-
root: zod_1.z.string().describe("Project root (absolute path)").optional(),
|
|
2836
|
-
include_imports: zod_1.z
|
|
2837
|
-
.boolean()
|
|
2838
|
-
.describe("Prepend file imports")
|
|
2839
|
-
.optional(),
|
|
2840
|
-
},
|
|
2841
|
-
}, handleExtractSymbol);
|
|
2842
|
-
tool("peek_symbol", {
|
|
2843
|
-
description: "Compact symbol overview: signature + callers + callees.",
|
|
2844
|
-
inputSchema: {
|
|
2845
|
-
symbol: zod_1.z.string().describe("Symbol name"),
|
|
2846
|
-
root: zod_1.z.string().describe("Project root (absolute path)").optional(),
|
|
2847
|
-
depth: zod_1.z
|
|
2848
|
-
.number()
|
|
2849
|
-
.describe("Caller depth (default 1, max 3)")
|
|
2850
|
-
.optional(),
|
|
2851
|
-
},
|
|
2852
|
-
}, handlePeekSymbol);
|
|
2853
|
-
tool("dead", {
|
|
2854
|
-
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.",
|
|
2855
|
-
inputSchema: {
|
|
2856
|
-
symbol: zod_1.z.string().describe("Symbol name to check"),
|
|
2857
|
-
root: zod_1.z.string().describe("Project root (absolute path)").optional(),
|
|
2858
|
-
},
|
|
2859
|
-
}, handleDead);
|
|
2860
|
-
tool("audit", {
|
|
2861
|
-
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.",
|
|
2862
|
-
inputSchema: {
|
|
2863
|
-
root: zod_1.z
|
|
2864
|
-
.string()
|
|
2865
|
-
.describe("Project root (default: current)")
|
|
2866
|
-
.optional(),
|
|
2867
|
-
top: zod_1.z
|
|
2868
|
-
.number()
|
|
2869
|
-
.describe("How many of each category to return (default 10)")
|
|
2870
|
-
.optional(),
|
|
2871
|
-
},
|
|
2872
|
-
}, handleAudit);
|
|
2873
|
-
tool("surprising_connections", {
|
|
2874
|
-
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.",
|
|
2875
|
-
inputSchema: {
|
|
2876
|
-
experimental: zod_1.z
|
|
2877
|
-
.boolean()
|
|
2878
|
-
.describe("Must be true to acknowledge experimental signal quality"),
|
|
2879
|
-
root: zod_1.z
|
|
2880
|
-
.string()
|
|
2881
|
-
.describe("Project root (default: current)")
|
|
2882
|
-
.optional(),
|
|
2883
|
-
sample: zod_1.z
|
|
2884
|
-
.number()
|
|
2885
|
-
.describe("Indexed code chunks to sample (default 160, max 10000)")
|
|
2886
|
-
.optional(),
|
|
2887
|
-
neighbors: zod_1.z
|
|
2888
|
-
.number()
|
|
2889
|
-
.describe("Nearest neighbors per sampled chunk (default 20, max 200)")
|
|
2890
|
-
.optional(),
|
|
2891
|
-
top: zod_1.z
|
|
2892
|
-
.number()
|
|
2893
|
-
.describe("Grouped findings to return (default 10, max 100)")
|
|
2894
|
-
.optional(),
|
|
2895
|
-
dir_depth: zod_1.z
|
|
2896
|
-
.number()
|
|
2897
|
-
.describe("Directory bucket depth considered unsurprising")
|
|
2898
|
-
.optional(),
|
|
2899
|
-
min_similarity: zod_1.z
|
|
2900
|
-
.number()
|
|
2901
|
-
.describe("Minimum similarity 0-1 (default 0)")
|
|
2902
|
-
.optional(),
|
|
2903
|
-
max_rows: zod_1.z
|
|
2904
|
-
.number()
|
|
2905
|
-
.describe(`Maximum indexed rows to scan (default 50000, max ${surprising_connections_1.MAX_SURPRISE_ROWS})`)
|
|
2906
|
-
.optional(),
|
|
2907
|
-
in: zod_1.z
|
|
2908
|
-
.string()
|
|
2909
|
-
.describe("Restrict to a sub-path of the project")
|
|
2910
|
-
.optional(),
|
|
2911
|
-
exclude: zod_1.z
|
|
2912
|
-
.string()
|
|
2913
|
-
.describe("Exclude a sub-path of the project")
|
|
2914
|
-
.optional(),
|
|
2915
|
-
include_tests: zod_1.z.boolean().describe("Include test files").optional(),
|
|
2916
|
-
include_eval: zod_1.z
|
|
2917
|
-
.boolean()
|
|
2918
|
-
.describe("Include eval/experiment/script files")
|
|
2919
|
-
.optional(),
|
|
2920
|
-
},
|
|
2921
|
-
}, handleSurprisingConnections);
|
|
2922
|
-
tool("get_neighbors", {
|
|
2923
|
-
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`.",
|
|
2924
|
-
inputSchema: {
|
|
2925
|
-
symbol: zod_1.z.string().describe("Starting symbol"),
|
|
2926
|
-
direction: zod_1.z
|
|
2927
|
-
.enum(["callers", "callees"])
|
|
2928
|
-
.describe("Edge direction (default callees)")
|
|
2929
|
-
.optional(),
|
|
2930
|
-
max_hops: zod_1.z
|
|
2931
|
-
.number()
|
|
2932
|
-
.describe("Max hops (default 2, max 5)")
|
|
2933
|
-
.optional(),
|
|
2934
|
-
root: zod_1.z.string().describe("Project root (absolute path)").optional(),
|
|
2935
|
-
},
|
|
2936
|
-
}, handleGetNeighbors);
|
|
2937
|
-
tool("find_paths", {
|
|
2938
|
-
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.",
|
|
2939
|
-
inputSchema: {
|
|
2940
|
-
from: zod_1.z.string().describe("Start symbol"),
|
|
2941
|
-
to: zod_1.z.string().describe("Target symbol"),
|
|
2942
|
-
direction: zod_1.z
|
|
2943
|
-
.enum(["callers", "callees"])
|
|
2944
|
-
.describe("Search direction (default callees)")
|
|
2945
|
-
.optional(),
|
|
2946
|
-
max_hops: zod_1.z
|
|
2947
|
-
.number()
|
|
2948
|
-
.describe("Max hops (default 6, max 10)")
|
|
2949
|
-
.optional(),
|
|
2950
|
-
root: zod_1.z.string().describe("Project root (absolute path)").optional(),
|
|
2951
|
-
},
|
|
2952
|
-
}, handleFindPaths);
|
|
2953
|
-
tool("subgraph_for_files", {
|
|
2954
|
-
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.",
|
|
2955
|
-
inputSchema: {
|
|
2956
|
-
files: zod_1.z
|
|
2957
|
-
.array(zod_1.z.string())
|
|
2958
|
-
.describe("File paths (absolute or root-relative)"),
|
|
2959
|
-
root: zod_1.z.string().describe("Project root (absolute path)").optional(),
|
|
2960
|
-
},
|
|
2961
|
-
}, handleSubgraphForFiles);
|
|
2962
|
-
tool("list_symbols", {
|
|
2963
|
-
description: "List indexed symbols with role and export status.",
|
|
2964
|
-
inputSchema: {
|
|
2965
|
-
pattern: zod_1.z
|
|
2966
|
-
.string()
|
|
2967
|
-
.describe("Name filter (case-insensitive)")
|
|
2968
|
-
.optional(),
|
|
2969
|
-
limit: zod_1.z.number().describe("Max results (default 20)").optional(),
|
|
2970
|
-
path: zod_1.z.string().describe("Path prefix filter").optional(),
|
|
2971
|
-
},
|
|
2972
|
-
}, handleListSymbols);
|
|
2973
|
-
tool("index_status", {
|
|
2974
|
-
description: "Index health: chunks, files, projects, watcher status.",
|
|
2975
|
-
inputSchema: {},
|
|
2976
|
-
_meta: { "anthropic/alwaysLoad": true },
|
|
2977
|
-
}, handleIndexStatus);
|
|
2978
|
-
tool("list_projects", {
|
|
2979
|
-
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).",
|
|
2980
|
-
inputSchema: {},
|
|
2981
|
-
_meta: { "anthropic/alwaysLoad": true },
|
|
2982
|
-
}, handleListProjects);
|
|
2983
|
-
tool("summarize_directory", {
|
|
2984
|
-
description: "Generate LLM summaries for indexed chunks.",
|
|
2985
|
-
inputSchema: {
|
|
2986
|
-
path: zod_1.z
|
|
2987
|
-
.string()
|
|
2988
|
-
.describe("Directory to summarize (default: project root)")
|
|
2989
|
-
.optional(),
|
|
2990
|
-
limit: zod_1.z
|
|
2991
|
-
.number()
|
|
2992
|
-
.describe("Max chunks (default 200, max 5000)")
|
|
2993
|
-
.optional(),
|
|
2994
|
-
},
|
|
2995
|
-
}, handleSummarizeDirectory);
|
|
2996
|
-
tool("summarize_project", {
|
|
2997
|
-
description: "Project overview: languages, structure, roles, key symbols, entry points.",
|
|
2998
|
-
inputSchema: {
|
|
2999
|
-
root: zod_1.z
|
|
3000
|
-
.string()
|
|
3001
|
-
.describe("Project root (default: current)")
|
|
3002
|
-
.optional(),
|
|
3003
|
-
},
|
|
3004
|
-
}, handleSummarizeProject);
|
|
3005
|
-
tool("related_files", {
|
|
3006
|
-
description: "Find dependencies and dependents of a file by shared symbols.",
|
|
3007
|
-
inputSchema: {
|
|
3008
|
-
file: zod_1.z.string().describe("File path relative to project root"),
|
|
3009
|
-
limit: zod_1.z
|
|
3010
|
-
.number()
|
|
3011
|
-
.describe("Max results per direction (default 10)")
|
|
3012
|
-
.optional(),
|
|
3013
|
-
},
|
|
3014
|
-
}, handleRelatedFiles);
|
|
3015
|
-
tool("recent_changes", {
|
|
3016
|
-
description: "Recently modified indexed files with timestamps.",
|
|
3017
|
-
inputSchema: {
|
|
3018
|
-
limit: zod_1.z
|
|
3019
|
-
.number()
|
|
3020
|
-
.describe("Max files to return (default 20)")
|
|
3021
|
-
.optional(),
|
|
3022
|
-
root: zod_1.z
|
|
3023
|
-
.string()
|
|
3024
|
-
.describe("Project root (defaults to current project)")
|
|
3025
|
-
.optional(),
|
|
3026
|
-
},
|
|
3027
|
-
}, handleRecentChanges);
|
|
3028
|
-
tool("diff_changes", {
|
|
3029
|
-
description: "Search code scoped to git changes. Omit ref for uncommitted changes.",
|
|
3030
|
-
inputSchema: {
|
|
3031
|
-
ref: zod_1.z
|
|
3032
|
-
.string()
|
|
3033
|
-
.describe("Git ref to diff against (e.g. main, HEAD~5)")
|
|
3034
|
-
.optional(),
|
|
3035
|
-
query: zod_1.z
|
|
3036
|
-
.string()
|
|
3037
|
-
.describe("Semantic search within changed files")
|
|
3038
|
-
.optional(),
|
|
3039
|
-
limit: zod_1.z.number().describe("Max results (default 10)").optional(),
|
|
3040
|
-
role: zod_1.z
|
|
3041
|
-
.string()
|
|
3042
|
-
.describe("Filter by role: ORCHESTRATION, DEFINITION, IMPLEMENTATION")
|
|
3043
|
-
.optional(),
|
|
3044
|
-
},
|
|
3045
|
-
}, handleDiffChanges);
|
|
3046
|
-
tool("find_tests", {
|
|
3047
|
-
description: "Find tests that exercise a symbol or file via reverse call graph.",
|
|
3048
|
-
inputSchema: {
|
|
3049
|
-
target: zod_1.z.string().describe("Symbol name or file path"),
|
|
3050
|
-
depth: zod_1.z
|
|
3051
|
-
.number()
|
|
3052
|
-
.describe("Caller traversal depth 1-3 (default 1)")
|
|
3053
|
-
.optional(),
|
|
3054
|
-
},
|
|
3055
|
-
}, handleFindTests);
|
|
3056
|
-
tool("impact_analysis", {
|
|
3057
|
-
description: "Change impact: dependents and affected tests for a symbol or file.",
|
|
3058
|
-
inputSchema: {
|
|
3059
|
-
target: zod_1.z.string().describe("Symbol name or file path"),
|
|
3060
|
-
depth: zod_1.z
|
|
3061
|
-
.number()
|
|
3062
|
-
.describe("Caller traversal depth 1-3 (default 1)")
|
|
3063
|
-
.optional(),
|
|
3064
|
-
rollup: zod_1.z
|
|
3065
|
-
.boolean()
|
|
3066
|
-
.describe("Return export/package rollup TSV rows")
|
|
3067
|
-
.optional(),
|
|
3068
|
-
top: zod_1.z
|
|
3069
|
-
.number()
|
|
3070
|
-
.describe("Max rows per rollup section (default 10)")
|
|
3071
|
-
.optional(),
|
|
3072
|
-
},
|
|
3073
|
-
}, handleImpactAnalysis);
|
|
3074
|
-
tool("find_similar", {
|
|
3075
|
-
description: "Find semantically similar code using vector similarity.",
|
|
3076
|
-
inputSchema: {
|
|
3077
|
-
target: zod_1.z.string().describe("Symbol name or file path"),
|
|
3078
|
-
limit: zod_1.z.number().describe("Max results (default 5)").optional(),
|
|
3079
|
-
threshold: zod_1.z
|
|
3080
|
-
.number()
|
|
3081
|
-
.describe("Min similarity 0-1 (default 0)")
|
|
3082
|
-
.optional(),
|
|
3083
|
-
},
|
|
3084
|
-
}, handleFindSimilar);
|
|
3085
|
-
tool("build_context", {
|
|
3086
|
-
description: "Token-budgeted topic summary (search + skeleton + extract).",
|
|
3087
|
-
inputSchema: {
|
|
3088
|
-
topic: zod_1.z
|
|
3089
|
-
.string()
|
|
3090
|
-
.describe("Natural language topic or directory path"),
|
|
3091
|
-
budget: zod_1.z.number().describe("Max tokens (default 4000)").optional(),
|
|
3092
|
-
limit: zod_1.z
|
|
3093
|
-
.number()
|
|
3094
|
-
.describe("Search result limit (default 10)")
|
|
3095
|
-
.optional(),
|
|
3096
|
-
},
|
|
3097
|
-
}, handleBuildContext);
|
|
3098
|
-
tool("investigate", {
|
|
3099
|
-
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).",
|
|
3100
|
-
inputSchema: {
|
|
3101
|
-
question: zod_1.z
|
|
3102
|
-
.string()
|
|
3103
|
-
.describe("Natural language question about the codebase"),
|
|
3104
|
-
max_rounds: zod_1.z
|
|
3105
|
-
.number()
|
|
3106
|
-
.describe("Max tool-call rounds (default 10)")
|
|
3107
|
-
.optional(),
|
|
3108
|
-
},
|
|
3109
|
-
}, handleInvestigate);
|
|
3110
|
-
tool("review_commit", {
|
|
3111
|
-
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).",
|
|
3112
|
-
inputSchema: {
|
|
3113
|
-
commit: zod_1.z
|
|
3114
|
-
.string()
|
|
3115
|
-
.describe("Git ref to review (default: HEAD)")
|
|
3116
|
-
.optional(),
|
|
3117
|
-
},
|
|
3118
|
-
}, handleReviewCommit);
|
|
3119
|
-
tool("review_report", {
|
|
3120
|
-
description: "Get the accumulated code review report for the current project. Returns findings from all reviewed commits.",
|
|
3121
|
-
inputSchema: {
|
|
3122
|
-
json: zod_1.z
|
|
3123
|
-
.boolean()
|
|
3124
|
-
.describe("Return raw JSON instead of text (default: false)")
|
|
3125
|
-
.optional(),
|
|
3126
|
-
},
|
|
3127
|
-
}, handleReviewReport);
|
|
3128
|
-
tool("review_risk", {
|
|
3129
|
-
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.",
|
|
3130
|
-
inputSchema: {
|
|
3131
|
-
commit: zod_1.z
|
|
3132
|
-
.string()
|
|
3133
|
-
.describe("Git ref to analyze (default: HEAD)")
|
|
3134
|
-
.optional(),
|
|
3135
|
-
},
|
|
3136
|
-
}, handleReviewRisk);
|
|
3137
|
-
yield server.connect(transport);
|
|
3138
|
-
// Start the singleton daemon in the background without delaying MCP startup.
|
|
3139
|
-
void Promise.resolve().then(() => __importStar(require("../lib/utils/daemon-client"))).then(({ ensureDaemonRunning }) => ensureDaemonRunning())
|
|
3140
|
-
.catch((e) => console.error("[MCP] Daemon startup failed:", e));
|
|
3141
|
-
// Kick off index readiness check and watcher in background
|
|
3142
|
-
ensureIndexReady().catch((e) => console.error("[MCP] Index readiness check failed:", e));
|
|
3143
|
-
ensureWatcher();
|
|
3144
|
-
// Keep this session's project leased while the session lives. The daemon
|
|
3145
|
-
// only watches projects some session holds.
|
|
3146
|
-
setInterval(() => void ensureWatcher(), MCP_WATCH_LEASE_RENEW_MS).unref();
|
|
3191
|
+
// Local-only, dual-era MCP: client-owned stdin/stdout pipes only.
|
|
3192
|
+
stdio = (0, stdio_1.serveStdio)(createServer, {
|
|
3193
|
+
legacy: "serve",
|
|
3194
|
+
onerror: (error) => console.error("[MCP] Transport error:", error),
|
|
3195
|
+
});
|
|
3147
3196
|
}));
|