Skip to content

Commit 3c10d72

Browse files
prosdevclaude
andcommitted
feat(mcp): add path tracing to dev_refs
New traceTo parameter on dev_refs: traces the dependency chain from a function's file to a target file through the call graph. Example: dev_refs { name: "authenticate", traceTo: "src/database.ts" } → "src/auth.ts → src/user-service.ts → src/repository.ts → src/database.ts (3 hops)" Uses shortestPath BFS from graph.ts. Requires indexer for graph building (optional — traceTo is ignored without it). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e77890d commit 3c10d72

3 files changed

Lines changed: 50 additions & 4 deletions

File tree

packages/mcp-server/bin/dev-agent-mcp.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ async function main() {
277277

278278
const refsAdapter = new RefsAdapter({
279279
searchService,
280+
indexer,
280281
defaultLimit: 20,
281282
});
282283

packages/mcp-server/src/adapters/built-in/refs-adapter.ts

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33
* Provides call graph queries via the dev_refs tool
44
*/
55

6-
import type { CalleeInfo, SearchResult, SearchService } from '@prosdevlab/dev-agent-core';
6+
import type {
7+
CalleeInfo,
8+
RepositoryIndexer,
9+
SearchResult,
10+
SearchService,
11+
} from '@prosdevlab/dev-agent-core';
12+
import { buildDependencyGraph, shortestPath } from '@prosdevlab/dev-agent-core';
713
import { estimateTokensForText, startTimer } from '../../formatters/utils';
814
import { RefsArgsSchema } from '../../schemas/index.js';
915
import { ToolAdapter } from '../tool-adapter';
@@ -24,6 +30,11 @@ export interface RefsAdapterConfig {
2430
*/
2531
searchService: SearchService;
2632

33+
/**
34+
* Repository indexer — needed for path tracing (optional)
35+
*/
36+
indexer?: RepositoryIndexer;
37+
2738
/**
2839
* Default result limit
2940
*/
@@ -54,13 +65,17 @@ export class RefsAdapter extends ToolAdapter {
5465
};
5566

5667
private searchService: SearchService;
57-
private config: Required<Omit<RefsAdapterConfig, 'searchService'>> & {
68+
private config: {
5869
searchService: SearchService;
70+
defaultLimit: number;
5971
};
6072

73+
private indexer?: RepositoryIndexer;
74+
6175
constructor(config: RefsAdapterConfig) {
6276
super();
6377
this.searchService = config.searchService;
78+
this.indexer = config.indexer;
6479
this.config = {
6580
searchService: config.searchService,
6681
defaultLimit: config.defaultLimit ?? 20,
@@ -101,6 +116,12 @@ export class RefsAdapter extends ToolAdapter {
101116
maximum: 50,
102117
default: this.config.defaultLimit,
103118
},
119+
traceTo: {
120+
type: 'string',
121+
description:
122+
"Trace the dependency chain from this function's file to a target file " +
123+
'(e.g., "src/database.ts"). Shows the shortest path through the call graph.',
124+
},
104125
},
105126
required: ['name'],
106127
},
@@ -114,11 +135,11 @@ export class RefsAdapter extends ToolAdapter {
114135
return validation.error;
115136
}
116137

117-
const { name, direction, limit } = validation.data;
138+
const { name, direction, limit, traceTo } = validation.data;
118139

119140
try {
120141
const timer = startTimer();
121-
context.logger.debug('Executing refs query', { name, direction, limit });
142+
context.logger.debug('Executing refs query', { name, direction, limit, traceTo });
122143

123144
// First, find the target component
124145
const searchResults = await this.searchService.search(name, { limit: 10 });
@@ -136,6 +157,29 @@ export class RefsAdapter extends ToolAdapter {
136157
};
137158
}
138159

160+
// Handle traceTo — find shortest dependency path
161+
if (traceTo && this.indexer) {
162+
const sourceFile = (target.metadata.path as string) || '';
163+
const allDocs = await this.indexer.getAll({ limit: 10000 });
164+
const graph = buildDependencyGraph(allDocs);
165+
const path = shortestPath(graph, sourceFile, traceTo);
166+
167+
const content = path
168+
? `## Dependency Path: ${sourceFile}${traceTo}\n\n${path.join(' → ')}\n\n**${path.length - 1} hop${path.length - 1 === 1 ? '' : 's'}**`
169+
: `## No Path Found\n\nNo dependency chain from \`${sourceFile}\` to \`${traceTo}\`.\nThese files may be in separate subsystems.`;
170+
171+
return {
172+
success: true,
173+
data: content,
174+
metadata: {
175+
tokens: estimateTokensForText(content),
176+
duration_ms: timer.elapsed(),
177+
timestamp: new Date().toISOString(),
178+
cached: false,
179+
},
180+
};
181+
}
182+
139183
const result: {
140184
target: {
141185
name: string;

packages/mcp-server/src/schemas/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export const RefsArgsSchema = z
6363
name: z.string().min(1, 'Name must be a non-empty string'),
6464
direction: z.enum(['callees', 'callers', 'both']).default('both'),
6565
limit: z.number().int().min(1).max(50).default(20),
66+
traceTo: z.string().optional(),
6667
})
6768
.strict();
6869

0 commit comments

Comments
 (0)