MCPcopy
hub / github.com/colbymchenry/codegraph / handleCallers

Method handleCallers

src/mcp/tools.ts:1537–1605  ·  view source on GitHub ↗

* Handle codegraph_callers

(args: Record<string, unknown>)

Source from the content-addressed store, hash-verified

1535 * Handle codegraph_callers
1536 */
1537 private async handleCallers(args: Record<string, unknown>): Promise<ToolResult> {
1538 const symbol = this.validateString(args.symbol, 'symbol');
1539 if (typeof symbol !== 'string') return symbol;
1540
1541 const cg = this.getCodeGraph(args.projectPath as string | undefined);
1542 const limit = clamp((args.limit as number) || 20, 1, 100);
1543 const fileFilter = typeof args.file === 'string' ? args.file : undefined;
1544
1545 const allMatches = this.findAllSymbols(cg, symbol);
1546 if (allMatches.nodes.length === 0) {
1547 return this.textResult(`Symbol "${symbol}" not found in the codebase`);
1548 }
1549
1550 const { groups, filteredOut } = this.groupDefinitions(allMatches.nodes, fileFilter);
1551 const filterNote = filteredOut
1552 ? `\n\n> **Note:** no definition of "${symbol}" matches file "${fileFilter}" — showing all definitions instead.`
1553 : '';
1554
1555 const collect = (defNodes: Node[]) => {
1556 const seen = new Set<string>();
1557 const callers: Node[] = [];
1558 const labels = new Map<string, string>();
1559 for (const node of defNodes) {
1560 for (const c of cg.getCallers(node.id)) {
1561 if (!seen.has(c.node.id)) {
1562 seen.add(c.node.id);
1563 callers.push(c.node);
1564 const label = this.edgeLabel(c.edge);
1565 if (label) labels.set(c.node.id, label);
1566 }
1567 }
1568 }
1569 return { callers, labels };
1570 };
1571
1572 // Single definition (or same-file overloads): the familiar flat list.
1573 if (groups.length === 1) {
1574 const { callers, labels } = collect(groups[0]!);
1575 if (callers.length === 0) {
1576 return this.textResult(`No callers found for "${symbol}"${allMatches.note}${filterNote}`);
1577 }
1578 // A successful `file` narrowing makes the multi-symbol aggregation note
1579 // stale — suppress it.
1580 const note = fileFilter && !filteredOut ? '' : allMatches.note;
1581 const formatted = this.formatNodeList(callers.slice(0, limit), `Callers of ${symbol}`, labels) + note + filterNote;
1582 return this.textResult(this.truncateOutput(formatted));
1583 }
1584
1585 // Multiple DISTINCT definitions (#764): one section per definition so an
1586 // agent never mistakes one app's callers for another's. Narrow with
1587 // `file` to focus a single definition.
1588 const lines: string[] = [
1589 `**Callers of ${symbol} — ${groups.length} distinct definitions (narrow with \`file\`)**`,
1590 ];
1591 for (const group of groups) {
1592 const { callers, labels } = collect(group);
1593 lines.push('', this.definitionHeading(group));
1594 if (callers.length === 0) {

Callers 1

dispatchToolMethod · 0.95

Calls 11

validateStringMethod · 0.95
getCodeGraphMethod · 0.95
findAllSymbolsMethod · 0.95
textResultMethod · 0.95
groupDefinitionsMethod · 0.95
formatNodeListMethod · 0.95
truncateOutputMethod · 0.95
definitionHeadingMethod · 0.95
clampFunction · 0.90
joinMethod · 0.80
getMethod · 0.65

Tested by

no test coverage detected