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

Method handleCallers

src/mcp/tools.ts:3080–3158  ·  view source on GitHub ↗

* Handle codegraph_callers

(args: Record<string, unknown>)

Source from the content-addressed store, hash-verified

3078 * Handle codegraph_callers
3079 */
3080 private async handleCallers(args: Record<string, unknown>): Promise<ToolResult> {
3081 const symbol = this.validateString(args.symbol, 'symbol');
3082 if (typeof symbol !== 'string') return symbol;
3083
3084 const cg = this.getCodeGraph(args.projectPath as string | undefined);
3085 const limit = clamp((args.limit as number) || 20, 1, 100);
3086 const fileFilter = typeof args.file === 'string' ? args.file : undefined;
3087
3088 const allMatches = this.findAllSymbols(cg, symbol);
3089 if (allMatches.nodes.length === 0) {
3090 return this.textResult(`Symbol "${symbol}" not found in the codebase${allMatches.note}`);
3091 }
3092
3093 const { groups, filteredOut } = this.groupDefinitions(allMatches.nodes, fileFilter);
3094 const answerPaths = new Set(groups.flat().map(node => node.filePath));
3095 const filterNote = filteredOut
3096 ? `\n\n> **Note:** no definition of "${symbol}" matches file "${fileFilter}" — showing all definitions instead.`
3097 : '';
3098
3099 const collect = (defNodes: Node[]) => {
3100 const seen = new Set<string>();
3101 const callers: Node[] = [];
3102 const labels = new Map<string, string>();
3103 for (const node of defNodes) {
3104 for (const c of cg.getCallers(node.id)) {
3105 if (!seen.has(c.node.id)) {
3106 seen.add(c.node.id);
3107 callers.push(c.node);
3108 answerPaths.add(c.node.filePath);
3109 const label = this.edgeLabel(c.edge);
3110 if (label) labels.set(c.node.id, label);
3111 }
3112 }
3113 }
3114 return { callers, labels };
3115 };
3116
3117 // Single definition (or same-file overloads): the familiar flat list.
3118 if (groups.length === 1) {
3119 const { callers, labels } = collect(groups[0]!);
3120 if (callers.length === 0) {
3121 return this.answerResult(cg, `No callers found for "${symbol}"${allMatches.note}${filterNote}`, answerPaths);
3122 }
3123 // A successful `file` narrowing makes the multi-symbol aggregation note
3124 // stale — suppress it.
3125 const note = fileFilter && !filteredOut ? '' : allMatches.note;
3126 // Say when the cap cut the list (#1639, #1674): a truncated answer with
3127 // no marker reads as the complete set, and an agent under-counts from it.
3128 const cut = callers.length > limit
3129 ? `\n\n> Showing ${limit} of ${callers.length} callers; pass \`limit\` (up to 100) to widen.`
3130 : '';
3131 const formatted = this.formatNodeList(callers.slice(0, limit), `Callers of ${symbol}`, labels) + cut + note + filterNote;
3132 return this.answerResult(cg, this.truncateOutput(formatted), answerPaths);
3133 }
3134
3135 // Multiple DISTINCT definitions (#764): one section per definition so an
3136 // agent never mistakes one app's callers for another's. Narrow with
3137 // `file` to focus a single definition.

Callers 1

dispatchToolMethod · 0.95

Calls 15

validateStringMethod · 0.95
getCodeGraphMethod · 0.95
findAllSymbolsMethod · 0.95
textResultMethod · 0.95
groupDefinitionsMethod · 0.95
answerResultMethod · 0.95
formatNodeListMethod · 0.95
truncateOutputMethod · 0.95
definitionHeadingMethod · 0.95
clampFunction · 0.90
mapMethod · 0.80
pushMethod · 0.80

Tested by

no test coverage detected