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

Method handleExplore

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

2500 * tax on small projects while earning its keep on large ones.
2501 */
2502 private async handleExplore(args: Record<string, unknown>): Promise<ToolResult> {
2503 const rawQuery = this.validateString(args.query, 'query');
2504 if (typeof rawQuery !== 'string') return rawQuery;
2505 // One normalization point so the flow-builder, relevance search, and
2506 // ranking all see the same canonical spelling (Erlang `mod:fn/arity`).
2507 const query = normalizeQuerySpelling(rawQuery);
2508
2509 const cg = this.getCodeGraph(args.projectPath as string | undefined);
2510 const projectRoot = cg.getProjectRoot();
2511
2512 // Resolve adaptive output budget from project size. Falls back to the
2513 // largest-tier defaults if stats aren't available, which preserves
2514 // pre-#185 behavior for callers that hit the rare stats failure.
2515 let budget: ExploreOutputBudget;
2516 try {
2517 budget = getExploreOutputBudget(cg.getStats().fileCount);
2518 } catch {
2519 budget = getExploreOutputBudget(Infinity);
2520 }
2521 const maxFiles = clamp((args.maxFiles as number) || budget.defaultMaxFiles, 1, 20);
2522
2523 // Step 1: Find relevant context with generous parameters.
2524 // Use a large maxNodes budget — explore has its own 35k char output limit
2525 // that prevents context bloat, so more nodes just means better coverage
2526 // across entry points (especially for large files like Svelte components).
2527 const subgraph = await cg.findRelevantContext(query, {
2528 searchLimit: 8,
2529 traversalDepth: 3,
2530 maxNodes: 200,
2531 minScore: 0.2,
2532 });
2533
2534 if (subgraph.nodes.size === 0) {
2535 return this.textResult(`No relevant code found for "${query}"`);
2536 }
2537
2538 // Graph-aware glue: findRelevantContext builds the subgraph from name/text
2539 // search, so a method that BRIDGES named symbols — e.g. App.tsx's
2540 // triggerRender, which calls the named triggerUpdate — is never a search hit
2541 // and gets missed, forcing the agent to Read the file to trace it. Pull in
2542 // the callers/callees of the entry (root) nodes, but ONLY those that live in
2543 // files the subgraph already surfaces (where the agent reads to fill gaps),
2544 // so we add wiring without dragging in unrelated files. These get an
2545 // importance boost below so they survive the per-file cluster budget.
2546 const glueNodeIds = new Set<string>();
2547 const subgraphFiles = new Set<string>();
2548 for (const n of subgraph.nodes.values()) subgraphFiles.add(n.filePath);
2549 const GLUE_NODE_CAP = 60;
2550 for (const rootId of subgraph.roots) {
2551 if (glueNodeIds.size >= GLUE_NODE_CAP) break;
2552 let neighbors: Node[] = [];
2553 try {
2554 neighbors = [
2555 ...cg.getCallers(rootId).map(c => c.node),
2556 ...cg.getCallees(rootId).map(c => c.node),
2557 ];
2558 } catch {
2559 continue;

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