MCPcopy Create free account
hub / github.com/colbymchenry/codegraph / handleExplore

Method handleExplore

src/mcp/tools.ts:2619–3900  ·  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

2617 * tax on small projects while earning its keep on large ones.
2618 */
2619 private async handleExplore(args: Record<string, unknown>): Promise<ToolResult> {
2620 const rawQuery = this.validateString(args.query, 'query');
2621 if (typeof rawQuery !== 'string') return rawQuery;
2622 // One normalization point so the flow-builder, relevance search, and
2623 // ranking all see the same canonical spelling (Erlang `mod:fn/arity`).
2624 const query = normalizeQuerySpelling(rawQuery);
2625
2626 const cg = this.getCodeGraph(args.projectPath as string | undefined);
2627 const projectRoot = cg.getProjectRoot();
2628
2629 // Resolve adaptive output budget from project size. Falls back to the
2630 // largest-tier defaults if stats aren't available, which preserves
2631 // pre-#185 behavior for callers that hit the rare stats failure.
2632 let budget: ExploreOutputBudget;
2633 try {
2634 budget = getExploreOutputBudget(cg.getStats().fileCount);
2635 } catch {
2636 budget = getExploreOutputBudget(Infinity);
2637 }
2638 const maxFiles = clamp((args.maxFiles as number) || budget.defaultMaxFiles, 1, 20);
2639
2640 // Step 1: Find relevant context with generous parameters.
2641 // Use a large maxNodes budget — explore has its own 35k char output limit
2642 // that prevents context bloat, so more nodes just means better coverage
2643 // across entry points (especially for large files like Svelte components).
2644 const subgraph = await cg.findRelevantContext(query, {
2645 searchLimit: 8,
2646 traversalDepth: 3,
2647 maxNodes: 200,
2648 minScore: 0.2,
2649 });
2650
2651 if (subgraph.nodes.size === 0) {
2652 return this.textResult(`No relevant code found for "${query}"`);
2653 }
2654
2655 // Graph-aware glue: findRelevantContext builds the subgraph from name/text
2656 // search, so a method that BRIDGES named symbols — e.g. App.tsx's
2657 // triggerRender, which calls the named triggerUpdate — is never a search hit
2658 // and gets missed, forcing the agent to Read the file to trace it. Pull in
2659 // the callers/callees of the entry (root) nodes, but ONLY those that live in
2660 // files the subgraph already surfaces (where the agent reads to fill gaps),
2661 // so we add wiring without dragging in unrelated files. These get an
2662 // importance boost below so they survive the per-file cluster budget.
2663 const glueNodeIds = new Set<string>();
2664 const subgraphFiles = new Set<string>();
2665 for (const n of subgraph.nodes.values()) subgraphFiles.add(n.filePath);
2666 const GLUE_NODE_CAP = 60;
2667 for (const rootId of subgraph.roots) {
2668 if (glueNodeIds.size >= GLUE_NODE_CAP) break;
2669 let neighbors: Node[] = [];
2670 try {
2671 neighbors = [
2672 ...cg.getCallers(rootId).map(c => c.node),
2673 ...cg.getCallees(rootId).map(c => c.node),
2674 ];
2675 } catch {
2676 continue;

Callers 1

dispatchToolMethod · 0.95

Calls 15

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

Tested by

no test coverage detected