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

Method handleNode

src/mcp/tools.ts:5996–6110  ·  view source on GitHub ↗

* Handle codegraph_node

(args: Record<string, unknown>)

Source from the content-addressed store, hash-verified

5994 * Handle codegraph_node
5995 */
5996 private async handleNode(args: Record<string, unknown>): Promise<ToolResult> {
5997 const cg = this.getCodeGraph(args.projectPath as string | undefined);
5998 // Default to false to minimize context usage
5999 const includeCode = args.includeCode === true;
6000 const fileHint = typeof args.file === 'string' && args.file.trim() ? args.file.trim() : undefined;
6001 const lineHint = typeof args.line === 'number' && args.line > 0 ? args.line : undefined;
6002 const offset = typeof args.offset === 'number' && args.offset > 0 ? Math.floor(args.offset) : undefined;
6003 const limit = typeof args.limit === 'number' && args.limit > 0 ? Math.floor(args.limit) : undefined;
6004 const symbolsOnly = args.symbolsOnly === true;
6005 const symbolRaw = typeof args.symbol === 'string' ? args.symbol.trim() : '';
6006
6007 // FILE READ MODE: a `file` with no `symbol` reads that file like the Read
6008 // tool — its current on-disk source with line numbers, narrowable with
6009 // `offset`/`limit` exactly as Read does — PLUS a one-line blast-radius
6010 // header (which files depend on it). `symbolsOnly` returns just the
6011 // structural map instead. Backed by the index: same bytes Read gives you.
6012 if (!symbolRaw && fileHint) {
6013 return this.handleFileView(cg, fileHint, { offset, limit, symbolsOnly });
6014 }
6015
6016 const symbol = this.validateString(args.symbol, 'symbol');
6017 if (typeof symbol !== 'string') return symbol;
6018
6019 let matches = this.findSymbolMatches(cg, symbol);
6020 if (matches.length === 0) {
6021 return this.textResult(`Symbol "${symbol}" not found in the codebase`);
6022 }
6023
6024 // Disambiguate a heavily-overloaded name to a specific definition the caller
6025 // pinned by file/line (the `file:line` a trail or another tool showed it) —
6026 // so it can fetch e.g. `Harness::poll` at harness.rs:153 out of 50+ `poll`s
6027 // instead of Reading. file matches by path suffix/substring; line prefers the
6028 // def whose body contains it, else the nearest start. Only narrows (never
6029 // empties — if a hint matches nothing it's ignored).
6030 if (matches.length > 1 && (fileHint || lineHint !== undefined)) {
6031 const norm = (p: string) => p.replace(/\\/g, '/').toLowerCase();
6032 let narrowed = matches;
6033 if (fileHint) {
6034 const fh = norm(fileHint);
6035 const byFile = narrowed.filter((n) => norm(n.filePath).endsWith(fh) || norm(n.filePath).includes(fh));
6036 if (byFile.length > 0) narrowed = byFile;
6037 }
6038 if (lineHint !== undefined && narrowed.length > 1) {
6039 const containing = narrowed.filter((n) => n.startLine <= lineHint && (n.endLine ?? n.startLine) >= lineHint);
6040 narrowed = containing.length > 0
6041 ? containing
6042 : [...narrowed].sort((a, b) => Math.abs(a.startLine - lineHint) - Math.abs(b.startLine - lineHint)).slice(0, 1);
6043 }
6044 if (narrowed.length > 0) matches = narrowed;
6045 }
6046
6047 // Single definition — the common case.
6048 if (matches.length === 1) {
6049 return this.textResult(this.truncateOutput(await this.renderNodeSection(cg, matches[0]!, includeCode)));
6050 }
6051
6052 // Multiple definitions share this name — overloads, or same-named methods on
6053 // 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
normFunction · 0.50
joinMethod · 0.45

Tested by

no test coverage detected