MCPcopy
hub / github.com/colbymchenry/codegraph / handleExplore

Method handleExplore

src/mcp/tools.ts:2460–3623  ·  view source on GitHub ↗

* Handle codegraph_explore — deep exploration in a single call * * Strategy: find relevant symbols via graph traversal, group by file, * then read contiguous file sections covering all symbols per file. * This replaces multiple codegraph_node + Read calls. * * Output size is adapti

(args: Record<string, unknown>)

Source from the content-addressed store, hash-verified

2458 * tax on small projects while earning its keep on large ones.
2459 */
2460 private async handleExplore(args: Record<string, unknown>): Promise<ToolResult> {
2461 const query = this.validateString(args.query, 'query');
2462 if (typeof query !== 'string') return query;
2463
2464 const cg = this.getCodeGraph(args.projectPath as string | undefined);
2465 const projectRoot = cg.getProjectRoot();
2466
2467 // Resolve adaptive output budget from project size. Falls back to the
2468 // largest-tier defaults if stats aren't available, which preserves
2469 // pre-#185 behavior for callers that hit the rare stats failure.
2470 let budget: ExploreOutputBudget;
2471 try {
2472 budget = getExploreOutputBudget(cg.getStats().fileCount);
2473 } catch {
2474 budget = getExploreOutputBudget(Infinity);
2475 }
2476 const maxFiles = clamp((args.maxFiles as number) || budget.defaultMaxFiles, 1, 20);
2477
2478 // Step 1: Find relevant context with generous parameters.
2479 // Use a large maxNodes budget — explore has its own 35k char output limit
2480 // that prevents context bloat, so more nodes just means better coverage
2481 // across entry points (especially for large files like Svelte components).
2482 const subgraph = await cg.findRelevantContext(query, {
2483 searchLimit: 8,
2484 traversalDepth: 3,
2485 maxNodes: 200,
2486 minScore: 0.2,
2487 });
2488
2489 if (subgraph.nodes.size === 0) {
2490 return this.textResult(`No relevant code found for "${query}"`);
2491 }
2492
2493 // Graph-aware glue: findRelevantContext builds the subgraph from name/text
2494 // search, so a method that BRIDGES named symbols — e.g. App.tsx's
2495 // triggerRender, which calls the named triggerUpdate — is never a search hit
2496 // and gets missed, forcing the agent to Read the file to trace it. Pull in
2497 // the callers/callees of the entry (root) nodes, but ONLY those that live in
2498 // files the subgraph already surfaces (where the agent reads to fill gaps),
2499 // so we add wiring without dragging in unrelated files. These get an
2500 // importance boost below so they survive the per-file cluster budget.
2501 const glueNodeIds = new Set<string>();
2502 const subgraphFiles = new Set<string>();
2503 for (const n of subgraph.nodes.values()) subgraphFiles.add(n.filePath);
2504 const GLUE_NODE_CAP = 60;
2505 for (const rootId of subgraph.roots) {
2506 if (glueNodeIds.size >= GLUE_NODE_CAP) break;
2507 let neighbors: Node[] = [];
2508 try {
2509 neighbors = [
2510 ...cg.getCallers(rootId).map(c => c.node),
2511 ...cg.getCallees(rootId).map(c => c.node),
2512 ];
2513 } catch {
2514 continue;
2515 }
2516 for (const nb of neighbors) {
2517 if (glueNodeIds.size >= GLUE_NODE_CAP) break;

Callers 1

dispatchToolMethod · 0.95

Calls 15

validateStringMethod · 0.95
getCodeGraphMethod · 0.95
textResultMethod · 0.95
findAllSymbolsMethod · 0.95
computeGraphRelevanceMethod · 0.95
clampFunction · 0.90
normalizeNameTokenFunction · 0.90
isConfigLeafNodeFunction · 0.90
isGeneratedFileFunction · 0.90
validatePathWithinRootFunction · 0.90

Tested by

no test coverage detected