@navigation-agent/mcp-server 0.1.5 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,7 +6,7 @@ import { createInspectTreeService } from "../services/inspectTreeService.js";
6
6
  import { createListEndpointsService } from "../services/listEndpointsService.js";
7
7
  import { createSearchTextService } from "../services/searchTextService.js";
8
8
  import { createTraceCallersService } from "../services/traceCallersService.js";
9
- import { createTraceSymbolService } from "../services/traceSymbolService.js";
9
+ import { createTraceFlowService } from "../services/traceFlowService.js";
10
10
  import { registerCodeTools, } from "../tools/registerCodeTools.js";
11
11
  function toSdkToolResult(result) {
12
12
  return {
@@ -38,7 +38,7 @@ export function createMcpServer(options) {
38
38
  workspaceRoot: options.workspaceRoot,
39
39
  engineClient,
40
40
  });
41
- const traceSymbolService = createTraceSymbolService({
41
+ const traceFlowService = createTraceFlowService({
42
42
  workspaceRoot: options.workspaceRoot,
43
43
  engineClient,
44
44
  });
@@ -52,7 +52,7 @@ export function createMcpServer(options) {
52
52
  listEndpointsHandler: (payload) => listEndpointsService.validateAndExecute(payload),
53
53
  searchTextHandler: (payload) => searchTextService.validateAndExecute(payload),
54
54
  traceCallersHandler: (payload) => traceCallersService.validateAndExecute(payload),
55
- traceSymbolHandler: (payload) => traceSymbolService.validateAndExecute(payload),
55
+ traceFlowHandler: (payload) => traceFlowService.validateAndExecute(payload),
56
56
  });
57
57
  const sdkServer = new SdkMcpServer({
58
58
  name: "navigation-agent-mcp",
@@ -8,7 +8,7 @@ export declare const PUBLIC_SYMBOL_KINDS: readonly ["any", "class", "interface",
8
8
  export type PublicSymbolKind = (typeof PUBLIC_SYMBOL_KINDS)[number];
9
9
  export declare const MATCH_MODES: readonly ["exact", "fuzzy"];
10
10
  export type MatchMode = (typeof MATCH_MODES)[number];
11
- export declare const CODE_TOOL_NAMES: readonly ["code.inspect_tree", "code.list_endpoints", "code.find_symbol", "code.search_text", "code.trace_symbol", "code.trace_callers"];
11
+ export declare const CODE_TOOL_NAMES: readonly ["code.inspect_tree", "code.list_endpoints", "code.find_symbol", "code.search_text", "code.trace_flow", "code.trace_callers"];
12
12
  export type CodeToolName = (typeof CODE_TOOL_NAMES)[number];
13
13
  export interface InspectTreeInput {
14
14
  path?: string | null;
@@ -50,6 +50,7 @@ export interface PublicSymbolDefinition {
50
50
  kind: PublicSymbolKind;
51
51
  path: string;
52
52
  line: number;
53
+ lineEnd: number;
53
54
  language: PublicLanguage | null;
54
55
  }
55
56
  export interface FindSymbolData {
@@ -124,25 +125,38 @@ export interface SearchTextData {
124
125
  totalMatchCount: number;
125
126
  items: SearchTextFileMatch[];
126
127
  }
127
- export interface TraceSymbolInput {
128
+ export interface TraceFlowInput {
128
129
  path: string;
129
130
  symbol: string;
130
131
  language?: PublicLanguage | null;
131
132
  framework?: PublicFramework | null;
132
133
  }
133
- export interface TraceSymbolEntrypoint {
134
+ export interface TraceFlowEntrypoint {
134
135
  path: string;
135
136
  symbol: string;
136
137
  language: PublicLanguage | null;
137
138
  }
138
- export interface TraceSymbolFile {
139
+ export interface TraceFlowFile {
139
140
  path: string;
140
141
  language: PublicLanguage | null;
141
142
  }
142
- export interface TraceSymbolData {
143
- entrypoint: TraceSymbolEntrypoint;
143
+ export interface TraceFlowCallee {
144
+ path: string;
145
+ line: number;
146
+ endLine: number;
147
+ column: number | null;
148
+ callee: string;
149
+ calleeSymbol: string | null;
150
+ relation: string;
151
+ language: PublicLanguage | null;
152
+ snippet: string | null;
153
+ depth: number;
154
+ }
155
+ export interface TraceFlowData {
156
+ entrypoint: TraceFlowEntrypoint;
144
157
  fileCount: number;
145
- items: TraceSymbolFile[];
158
+ items: TraceFlowFile[];
159
+ callees: TraceFlowCallee[];
146
160
  }
147
161
  export interface TraceCallersInput {
148
162
  path: string;
@@ -553,7 +567,7 @@ export declare const searchTextInputSchema: {
553
567
  };
554
568
  };
555
569
  };
556
- export declare const traceSymbolInputSchema: {
570
+ export declare const traceFlowInputSchema: {
557
571
  readonly type: "object";
558
572
  readonly properties: {
559
573
  readonly path: {
@@ -943,7 +957,7 @@ export declare const codeToolSchemas: {
943
957
  };
944
958
  };
945
959
  };
946
- readonly "code.trace_symbol": {
960
+ readonly "code.trace_flow": {
947
961
  readonly type: "object";
948
962
  readonly properties: {
949
963
  readonly path: {
@@ -1090,9 +1104,9 @@ export declare function normalizeSearchTextInput(payload: Record<string, unknown
1090
1104
  ok: false;
1091
1105
  issues: ValidationIssue[];
1092
1106
  };
1093
- export declare function normalizeTraceSymbolInput(payload: Record<string, unknown>): {
1107
+ export declare function normalizeTraceFlowInput(payload: Record<string, unknown>): {
1094
1108
  ok: true;
1095
- value: TraceSymbolInput;
1109
+ value: TraceFlowInput;
1096
1110
  } | {
1097
1111
  ok: false;
1098
1112
  issues: ValidationIssue[];
@@ -18,7 +18,7 @@ export const CODE_TOOL_NAMES = [
18
18
  "code.list_endpoints",
19
19
  "code.find_symbol",
20
20
  "code.search_text",
21
- "code.trace_symbol",
21
+ "code.trace_flow",
22
22
  "code.trace_callers",
23
23
  ];
24
24
  const sharedDefs = {
@@ -196,7 +196,7 @@ export const searchTextInputSchema = {
196
196
  required: ["query"],
197
197
  $defs: sharedDefs,
198
198
  };
199
- export const traceSymbolInputSchema = {
199
+ export const traceFlowInputSchema = {
200
200
  type: "object",
201
201
  properties: {
202
202
  path: { type: "string", minLength: 1 },
@@ -252,7 +252,7 @@ export const codeToolSchemas = {
252
252
  "code.list_endpoints": listEndpointsInputSchema,
253
253
  "code.find_symbol": findSymbolInputSchema,
254
254
  "code.search_text": searchTextInputSchema,
255
- "code.trace_symbol": traceSymbolInputSchema,
255
+ "code.trace_flow": traceFlowInputSchema,
256
256
  "code.trace_callers": traceCallersInputSchema,
257
257
  };
258
258
  export function normalizeInspectTreeInput(payload) {
@@ -351,7 +351,7 @@ export function normalizeSearchTextInput(payload) {
351
351
  },
352
352
  };
353
353
  }
354
- export function normalizeTraceSymbolInput(payload) {
354
+ export function normalizeTraceFlowInput(payload) {
355
355
  const issues = [];
356
356
  const scopedPath = normalizeRequiredTrimmedString(payload.path, "path", issues);
357
357
  const symbol = normalizeRequiredTrimmedString(payload.symbol, "symbol", issues);
@@ -1,5 +1,5 @@
1
1
  import type { MatchMode, PublicFramework, PublicLanguage, PublicEndpointKind, PublicSymbolKind } from "../contracts/public/code.ts";
2
- export declare const ENGINE_CAPABILITIES: readonly ["workspace.inspect_tree", "workspace.find_symbol", "workspace.list_endpoints", "workspace.search_text", "workspace.trace_symbol", "workspace.trace_callers"];
2
+ export declare const ENGINE_CAPABILITIES: readonly ["workspace.inspect_tree", "workspace.find_symbol", "workspace.list_endpoints", "workspace.search_text", "workspace.trace_flow", "workspace.trace_callers"];
3
3
  export type EngineCapability = (typeof ENGINE_CAPABILITIES)[number];
4
4
  export type AnalyzerLanguage = "auto" | "java" | "typescript" | "python" | "rust";
5
5
  export interface InspectTreeEnginePayload {
@@ -57,6 +57,7 @@ export interface FindSymbolEngineItem {
57
57
  kind: PublicSymbolKind;
58
58
  path: string;
59
59
  line: number;
60
+ lineEnd: number;
60
61
  language: PublicLanguage | null;
61
62
  }
62
63
  export interface FindSymbolEngineResult {
@@ -132,19 +133,32 @@ export interface SearchTextEngineResult {
132
133
  totalMatchCount: number;
133
134
  truncated: boolean;
134
135
  }
135
- export interface TraceSymbolEnginePayload {
136
+ export interface TraceFlowEnginePayload {
136
137
  path: string;
137
138
  symbol: string;
138
139
  analyzerLanguage: AnalyzerLanguage;
139
140
  publicLanguageFilter: PublicLanguage | null;
140
141
  }
141
- export interface TraceSymbolEngineItem {
142
+ export interface TraceFlowEngineItem {
142
143
  path: string;
143
144
  language: PublicLanguage | null;
144
145
  }
145
- export interface TraceSymbolEngineResult {
146
+ export interface TraceFlowEngineCallee {
147
+ path: string;
148
+ line: number;
149
+ endLine: number;
150
+ column: number | null;
151
+ callee: string;
152
+ calleeSymbol: string | null;
153
+ relation: string;
154
+ language: PublicLanguage | null;
155
+ snippet: string | null;
156
+ depth: number;
157
+ }
158
+ export interface TraceFlowEngineResult {
146
159
  resolvedPath: string | null;
147
- items: TraceSymbolEngineItem[];
160
+ items: TraceFlowEngineItem[];
161
+ callees: TraceFlowEngineCallee[];
148
162
  totalMatched: number;
149
163
  truncated: boolean;
150
164
  }
@@ -3,7 +3,7 @@ export const ENGINE_CAPABILITIES = [
3
3
  "workspace.find_symbol",
4
4
  "workspace.list_endpoints",
5
5
  "workspace.search_text",
6
- "workspace.trace_symbol",
6
+ "workspace.trace_flow",
7
7
  "workspace.trace_callers",
8
8
  ];
9
9
  let requestSequence = 0;
package/dist/index.d.ts CHANGED
@@ -8,5 +8,5 @@ export * from "./services/inspectTreeService.ts";
8
8
  export * from "./services/listEndpointsService.ts";
9
9
  export * from "./services/searchTextService.ts";
10
10
  export * from "./services/traceCallersService.ts";
11
- export * from "./services/traceSymbolService.ts";
11
+ export * from "./services/traceFlowService.ts";
12
12
  export * from "./tools/registerCodeTools.ts";
package/dist/index.js CHANGED
@@ -8,5 +8,5 @@ export * from "./services/inspectTreeService.js";
8
8
  export * from "./services/listEndpointsService.js";
9
9
  export * from "./services/searchTextService.js";
10
10
  export * from "./services/traceCallersService.js";
11
- export * from "./services/traceSymbolService.js";
11
+ export * from "./services/traceFlowService.js";
12
12
  export * from "./tools/registerCodeTools.js";
@@ -56,6 +56,7 @@ function buildSuccessResponse(input, result) {
56
56
  kind: item.kind,
57
57
  path: item.path,
58
58
  line: item.line,
59
+ lineEnd: item.lineEnd,
59
60
  language: item.language,
60
61
  })),
61
62
  },
@@ -0,0 +1,11 @@
1
+ import { type TraceFlowData, type TraceFlowInput } from "../contracts/public/code.ts";
2
+ import { type ResponseEnvelope } from "../contracts/public/common.ts";
3
+ import type { EngineClient } from "../engine/rustEngineClient.ts";
4
+ export interface TraceFlowService {
5
+ execute(input: TraceFlowInput): Promise<ResponseEnvelope<TraceFlowData>>;
6
+ validateAndExecute(payload: Record<string, unknown>): Promise<ResponseEnvelope<TraceFlowData>>;
7
+ }
8
+ export declare function createTraceFlowService(options: {
9
+ workspaceRoot: string;
10
+ engineClient: EngineClient;
11
+ }): TraceFlowService;
@@ -0,0 +1,245 @@
1
+ import { normalizeTraceFlowInput, } from "../contracts/public/code.js";
2
+ import { createResponseMeta, } from "../contracts/public/common.js";
3
+ import { nextRequestId, } from "../engine/protocol.js";
4
+ const TOOL_NAME = "code.trace_flow";
5
+ export function createTraceFlowService(options) {
6
+ return {
7
+ async execute(input) {
8
+ let response;
9
+ try {
10
+ response = await options.engineClient.request({
11
+ id: nextRequestId(),
12
+ capability: "workspace.trace_flow",
13
+ workspaceRoot: options.workspaceRoot,
14
+ payload: {
15
+ path: input.path,
16
+ symbol: input.symbol,
17
+ analyzerLanguage: resolveAnalyzerLanguage(input.language, input.framework),
18
+ publicLanguageFilter: resolveEffectiveLanguage(input.language, input.framework),
19
+ },
20
+ });
21
+ }
22
+ catch (error) {
23
+ return buildEngineFailureResponse(input, error);
24
+ }
25
+ if (!response.ok) {
26
+ return buildMappedErrorResponse(input, response.error.code, response.error.message, response.error.details, response.error.retryable);
27
+ }
28
+ return buildSuccessResponse(input, response.result);
29
+ },
30
+ async validateAndExecute(payload) {
31
+ const normalized = normalizeTraceFlowInput(payload);
32
+ if (!normalized.ok) {
33
+ return buildValidationErrorResponse(normalized.issues);
34
+ }
35
+ return this.execute(normalized.value);
36
+ },
37
+ };
38
+ }
39
+ function buildSuccessResponse(input, result) {
40
+ const entrypointPath = result.resolvedPath ?? input.path;
41
+ const effectiveLanguage = resolveEffectiveLanguage(input.language, input.framework);
42
+ const fileCount = result.items.length;
43
+ const calleeCount = result.callees?.length ?? 0;
44
+ return {
45
+ tool: TOOL_NAME,
46
+ status: result.truncated ? "partial" : "ok",
47
+ summary: buildSummary(input.symbol, entrypointPath, calleeCount),
48
+ data: {
49
+ entrypoint: {
50
+ path: entrypointPath,
51
+ symbol: input.symbol,
52
+ language: inferLanguageFromPath(entrypointPath),
53
+ },
54
+ fileCount,
55
+ items: result.items.map((item) => ({
56
+ path: item.path,
57
+ language: item.language,
58
+ })),
59
+ callees: (result.callees ?? []).map((callee) => ({
60
+ path: callee.path,
61
+ line: callee.line,
62
+ endLine: callee.endLine,
63
+ column: callee.column,
64
+ callee: callee.callee,
65
+ calleeSymbol: callee.calleeSymbol,
66
+ relation: callee.relation,
67
+ language: callee.language,
68
+ snippet: callee.snippet,
69
+ depth: callee.depth,
70
+ })),
71
+ },
72
+ errors: [],
73
+ meta: createResponseMeta({
74
+ query: { ...input },
75
+ resolvedPath: result.resolvedPath,
76
+ truncated: result.truncated,
77
+ counts: {
78
+ returnedCount: fileCount,
79
+ totalMatched: result.totalMatched,
80
+ },
81
+ detection: {
82
+ effectiveLanguage,
83
+ framework: input.framework ?? null,
84
+ },
85
+ }),
86
+ };
87
+ }
88
+ function buildValidationErrorResponse(issues) {
89
+ return {
90
+ tool: TOOL_NAME,
91
+ status: "error",
92
+ summary: "Request validation failed.",
93
+ data: emptyData(),
94
+ errors: [
95
+ {
96
+ code: "INVALID_INPUT",
97
+ message: "One or more input fields are invalid.",
98
+ retryable: false,
99
+ suggestion: "Correct the invalid fields and try again.",
100
+ details: { issues },
101
+ },
102
+ ],
103
+ meta: createResponseMeta({ query: {} }),
104
+ };
105
+ }
106
+ function buildMappedErrorResponse(input, code, message, details, retryable) {
107
+ const query = { ...input };
108
+ if (code === "FILE_NOT_FOUND") {
109
+ return {
110
+ tool: TOOL_NAME,
111
+ status: "error",
112
+ summary: "Path not found.",
113
+ data: emptyData(input.path, input.symbol),
114
+ errors: [
115
+ {
116
+ code,
117
+ message,
118
+ retryable,
119
+ suggestion: "Provide an existing file path inside the workspace root.",
120
+ details,
121
+ },
122
+ ],
123
+ meta: createResponseMeta({ query }),
124
+ };
125
+ }
126
+ if (code === "PATH_OUTSIDE_WORKSPACE") {
127
+ return {
128
+ tool: TOOL_NAME,
129
+ status: "error",
130
+ summary: "Path validation failed.",
131
+ data: emptyData(input.path, input.symbol),
132
+ errors: [
133
+ {
134
+ code,
135
+ message,
136
+ retryable,
137
+ suggestion: "Use a file path inside the workspace root.",
138
+ details,
139
+ },
140
+ ],
141
+ meta: createResponseMeta({ query }),
142
+ };
143
+ }
144
+ if (code === "UNSUPPORTED_CAPABILITY") {
145
+ return {
146
+ tool: TOOL_NAME,
147
+ status: "error",
148
+ summary: "Flow trace failed.",
149
+ data: emptyData(input.path, input.symbol),
150
+ errors: [
151
+ {
152
+ code: "BACKEND_EXECUTION_FAILED",
153
+ message,
154
+ retryable,
155
+ suggestion: "Verify the engine supports workspace.trace_flow and retry.",
156
+ details,
157
+ },
158
+ ],
159
+ meta: createResponseMeta({ query }),
160
+ };
161
+ }
162
+ return {
163
+ tool: TOOL_NAME,
164
+ status: "error",
165
+ summary: "Flow trace failed.",
166
+ data: emptyData(input.path, input.symbol),
167
+ errors: [
168
+ {
169
+ code,
170
+ message,
171
+ retryable,
172
+ details,
173
+ },
174
+ ],
175
+ meta: createResponseMeta({ query }),
176
+ };
177
+ }
178
+ function buildEngineFailureResponse(input, error) {
179
+ return buildMappedErrorResponse(input, "BACKEND_EXECUTION_FAILED", error instanceof Error ? error.message : String(error), {}, false);
180
+ }
181
+ function emptyData(path = "", symbol = "") {
182
+ return {
183
+ entrypoint: {
184
+ path,
185
+ symbol,
186
+ language: inferLanguageFromPath(path),
187
+ },
188
+ fileCount: 0,
189
+ items: [],
190
+ callees: [],
191
+ };
192
+ }
193
+ function buildSummary(symbol, path, calleeCount) {
194
+ if (calleeCount === 0) {
195
+ return `Trace completed for '${symbol}' from '${path}' with no callees found.`;
196
+ }
197
+ if (calleeCount === 1) {
198
+ return `Traced 1 callee for '${symbol}' from '${path}'.`;
199
+ }
200
+ return `Traced ${calleeCount} callees for '${symbol}' from '${path}'.`;
201
+ }
202
+ function resolveEffectiveLanguage(language, framework) {
203
+ if (language) {
204
+ return language;
205
+ }
206
+ if (framework === "react-router") {
207
+ return "typescript";
208
+ }
209
+ if (framework === "spring") {
210
+ return "java";
211
+ }
212
+ return null;
213
+ }
214
+ function resolveAnalyzerLanguage(language, framework) {
215
+ const effective = resolveEffectiveLanguage(language, framework);
216
+ if (effective === "java") {
217
+ return "java";
218
+ }
219
+ if (effective === "python") {
220
+ return "python";
221
+ }
222
+ if (effective === "rust") {
223
+ return "rust";
224
+ }
225
+ return "typescript";
226
+ }
227
+ function inferLanguageFromPath(path) {
228
+ const normalized = path.toLowerCase();
229
+ if (normalized.endsWith(".ts") || normalized.endsWith(".tsx")) {
230
+ return "typescript";
231
+ }
232
+ if (normalized.endsWith(".js") || normalized.endsWith(".jsx")) {
233
+ return "javascript";
234
+ }
235
+ if (normalized.endsWith(".java")) {
236
+ return "java";
237
+ }
238
+ if (normalized.endsWith(".py")) {
239
+ return "python";
240
+ }
241
+ if (normalized.endsWith(".rs")) {
242
+ return "rust";
243
+ }
244
+ return null;
245
+ }
@@ -15,6 +15,6 @@ export interface RegisterCodeToolsOptions {
15
15
  listEndpointsHandler?: (payload: Record<string, unknown>) => Promise<ResponseEnvelope<unknown>>;
16
16
  searchTextHandler?: (payload: Record<string, unknown>) => Promise<ResponseEnvelope<unknown>>;
17
17
  traceCallersHandler?: (payload: Record<string, unknown>) => Promise<ResponseEnvelope<unknown>>;
18
- traceSymbolHandler?: (payload: Record<string, unknown>) => Promise<ResponseEnvelope<unknown>>;
18
+ traceFlowHandler?: (payload: Record<string, unknown>) => Promise<ResponseEnvelope<unknown>>;
19
19
  }
20
20
  export declare function registerCodeTools(options: RegisterCodeToolsOptions): RegisteredCodeTool[];
@@ -89,10 +89,10 @@ const toolMetadata = [
89
89
  },
90
90
  },
91
91
  {
92
- name: "code.trace_symbol",
93
- title: "Trace symbol forward",
94
- description: "Trace a symbol forward from a starting file to related workspace files. The starting path must exist inside the workspace.",
95
- inputSchema: { ...codeToolSchemas["code.trace_symbol"] },
92
+ name: "code.trace_flow",
93
+ title: "Trace execution flow forward",
94
+ description: "Trace execution flow forward from a starting file and symbol to related workspace files. The starting path must exist inside the workspace.",
95
+ inputSchema: { ...codeToolSchemas["code.trace_flow"] },
96
96
  sdkInputSchema: {
97
97
  path: z.string().trim().min(1),
98
98
  symbol: z.string().trim().min(1),
@@ -161,14 +161,14 @@ export function registerCodeTools(options) {
161
161
  },
162
162
  };
163
163
  }
164
- if (tool.name === "code.trace_symbol") {
164
+ if (tool.name === "code.trace_flow") {
165
165
  return {
166
166
  ...tool,
167
167
  execute: async (payload) => {
168
- if (!options.traceSymbolHandler) {
169
- throw new Error("Trace symbol migrated handler is scaffolded but not yet wired.");
168
+ if (!options.traceFlowHandler) {
169
+ throw new Error("Trace flow migrated handler is scaffolded but not yet wired.");
170
170
  }
171
- return options.traceSymbolHandler(payload);
171
+ return options.traceFlowHandler(payload);
172
172
  },
173
173
  };
174
174
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@navigation-agent/mcp-server",
3
- "version": "0.1.5",
3
+ "version": "0.3.0",
4
4
  "description": "MCP server for AI-assisted code navigation — find symbols, trace callers, list endpoints and more.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -22,11 +22,11 @@
22
22
  "check": "tsc --noEmit"
23
23
  },
24
24
  "optionalDependencies": {
25
- "@navigation-agent/mcp-server-linux-x64": "0.1.5",
26
- "@navigation-agent/mcp-server-linux-arm64": "0.1.5",
27
- "@navigation-agent/mcp-server-darwin-x64": "0.1.5",
28
- "@navigation-agent/mcp-server-darwin-arm64": "0.1.5",
29
- "@navigation-agent/mcp-server-win32-x64": "0.1.5"
25
+ "@navigation-agent/mcp-server-linux-x64": "0.3.0",
26
+ "@navigation-agent/mcp-server-linux-arm64": "0.3.0",
27
+ "@navigation-agent/mcp-server-darwin-x64": "0.3.0",
28
+ "@navigation-agent/mcp-server-darwin-arm64": "0.3.0",
29
+ "@navigation-agent/mcp-server-win32-x64": "0.3.0"
30
30
  },
31
31
  "dependencies": {
32
32
  "@modelcontextprotocol/sdk": "^1.29.0",