MCPcopy Index your code
hub / github.com/colbymchenry/codegraph / handleNode

Method handleNode

src/mcp/tools.ts:3628–3742  ·  view source on GitHub ↗

* Handle codegraph_node

(args: Record<string, unknown>)

Source from the content-addressed store, hash-verified

3626 * Handle codegraph_node
3627 */
3628 private async handleNode(args: Record<string, unknown>): Promise<ToolResult> {
3629 const cg = this.getCodeGraph(args.projectPath as string | undefined);
3630 // Default to false to minimize context usage
3631 const includeCode = args.includeCode === true;
3632 const fileHint = typeof args.file === 'string' && args.file.trim() ? args.file.trim() : undefined;
3633 const lineHint = typeof args.line === 'number' && args.line > 0 ? args.line : undefined;
3634 const offset = typeof args.offset === 'number' && args.offset > 0 ? Math.floor(args.offset) : undefined;
3635 const limit = typeof args.limit === 'number' && args.limit > 0 ? Math.floor(args.limit) : undefined;
3636 const symbolsOnly = args.symbolsOnly === true;
3637 const symbolRaw = typeof args.symbol === 'string' ? args.symbol.trim() : '';
3638
3639 // FILE READ MODE: a `file` with no `symbol` reads that file like the Read
3640 // tool — its current on-disk source with line numbers, narrowable with
3641 // `offset`/`limit` exactly as Read does — PLUS a one-line blast-radius
3642 // header (which files depend on it). `symbolsOnly` returns just the
3643 // structural map instead. Backed by the index: same bytes Read gives you.
3644 if (!symbolRaw && fileHint) {
3645 return this.handleFileView(cg, fileHint, { offset, limit, symbolsOnly });
3646 }
3647
3648 const symbol = this.validateString(args.symbol, 'symbol');
3649 if (typeof symbol !== 'string') return symbol;
3650
3651 let matches = this.findSymbolMatches(cg, symbol);
3652 if (matches.length === 0) {
3653 return this.textResult(`Symbol "${symbol}" not found in the codebase`);
3654 }
3655
3656 // Disambiguate a heavily-overloaded name to a specific definition the caller
3657 // pinned by file/line (the `file:line` a trail or another tool showed it) —
3658 // so it can fetch e.g. `Harness::poll` at harness.rs:153 out of 50+ `poll`s
3659 // instead of Reading. file matches by path suffix/substring; line prefers the
3660 // def whose body contains it, else the nearest start. Only narrows (never
3661 // empties — if a hint matches nothing it's ignored).
3662 if (matches.length > 1 && (fileHint || lineHint !== undefined)) {
3663 const norm = (p: string) => p.replace(/\\/g, '/').toLowerCase();
3664 let narrowed = matches;
3665 if (fileHint) {
3666 const fh = norm(fileHint);
3667 const byFile = narrowed.filter((n) => norm(n.filePath).endsWith(fh) || norm(n.filePath).includes(fh));
3668 if (byFile.length > 0) narrowed = byFile;
3669 }
3670 if (lineHint !== undefined && narrowed.length > 1) {
3671 const containing = narrowed.filter((n) => n.startLine <= lineHint && (n.endLine ?? n.startLine) >= lineHint);
3672 narrowed = containing.length > 0
3673 ? containing
3674 : [...narrowed].sort((a, b) => Math.abs(a.startLine - lineHint) - Math.abs(b.startLine - lineHint)).slice(0, 1);
3675 }
3676 if (narrowed.length > 0) matches = narrowed;
3677 }
3678
3679 // Single definition — the common case.
3680 if (matches.length === 1) {
3681 return this.textResult(this.truncateOutput(await this.renderNodeSection(cg, matches[0]!, includeCode)));
3682 }
3683
3684 // Multiple definitions share this name — overloads, or same-named methods on
3685 // different types (Alamofire `didCompleteTask`/`task`/`validate`, gin

Callers 1

dispatchToolMethod · 0.95

Calls 9

getCodeGraphMethod · 0.95
handleFileViewMethod · 0.95
validateStringMethod · 0.95
findSymbolMatchesMethod · 0.95
textResultMethod · 0.95
truncateOutputMethod · 0.95
renderNodeSectionMethod · 0.95
joinMethod · 0.80
normFunction · 0.50

Tested by

no test coverage detected