@navigation-agent/mcp-server 0.1.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.
@@ -0,0 +1,290 @@
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"];
3
+ export type EngineCapability = (typeof ENGINE_CAPABILITIES)[number];
4
+ export type AnalyzerLanguage = "auto" | "java" | "typescript" | "python" | "rust";
5
+ export interface InspectTreeEnginePayload {
6
+ path: string | null;
7
+ maxDepth: number;
8
+ extensions: string[];
9
+ filePattern: string | null;
10
+ includeStats: boolean;
11
+ includeHidden: boolean;
12
+ }
13
+ export interface EngineRequest<TPayload = unknown> {
14
+ id: string;
15
+ capability: EngineCapability | string;
16
+ workspaceRoot: string;
17
+ payload: TPayload;
18
+ }
19
+ export interface EngineError {
20
+ code: string;
21
+ message: string;
22
+ retryable: boolean;
23
+ suggestion?: string | null;
24
+ details: Record<string, unknown>;
25
+ }
26
+ export interface InspectTreeEngineItemStats {
27
+ sizeBytes: number;
28
+ modifiedAt: string;
29
+ isSymlink: boolean;
30
+ }
31
+ export interface InspectTreeEngineItem {
32
+ path: string;
33
+ name: string;
34
+ type: "directory" | "file";
35
+ depth: number;
36
+ extension?: string | null;
37
+ stats?: InspectTreeEngineItemStats | null;
38
+ }
39
+ export interface InspectTreeEngineResult {
40
+ root: string;
41
+ items: InspectTreeEngineItem[];
42
+ truncated: boolean;
43
+ maxItems: number;
44
+ ignoredDirectories: string[];
45
+ }
46
+ export interface FindSymbolEnginePayload {
47
+ symbol: string;
48
+ path: string | null;
49
+ analyzerLanguage: AnalyzerLanguage;
50
+ publicLanguageFilter: PublicLanguage | null;
51
+ kind: PublicSymbolKind;
52
+ matchMode: MatchMode;
53
+ limit: number;
54
+ }
55
+ export interface FindSymbolEngineItem {
56
+ symbol: string;
57
+ kind: PublicSymbolKind;
58
+ path: string;
59
+ line: number;
60
+ language: PublicLanguage | null;
61
+ }
62
+ export interface FindSymbolEngineResult {
63
+ resolvedPath: string | null;
64
+ items: FindSymbolEngineItem[];
65
+ totalMatched: number;
66
+ truncated: boolean;
67
+ }
68
+ export interface ListEndpointsEnginePayload {
69
+ path: string | null;
70
+ analyzerLanguage: AnalyzerLanguage;
71
+ publicLanguageFilter: PublicLanguage | null;
72
+ publicFrameworkFilter: PublicFramework | null;
73
+ kind: PublicEndpointKind;
74
+ limit: number;
75
+ }
76
+ export interface ListEndpointsEngineItem {
77
+ name: string;
78
+ kind: string;
79
+ path: string | null;
80
+ file: string;
81
+ line: number;
82
+ language: PublicLanguage | null;
83
+ framework: PublicFramework | null;
84
+ }
85
+ export interface ListEndpointsEngineCounts {
86
+ byKind: Record<string, number>;
87
+ byLanguage: Record<string, number>;
88
+ byFramework: Record<string, number>;
89
+ }
90
+ export interface ListEndpointsEngineResult {
91
+ resolvedPath: string | null;
92
+ items: ListEndpointsEngineItem[];
93
+ totalMatched: number;
94
+ truncated: boolean;
95
+ counts: ListEndpointsEngineCounts;
96
+ }
97
+ export interface SearchTextEnginePayload {
98
+ query: string;
99
+ path: string | null;
100
+ publicLanguageFilter: PublicLanguage | null;
101
+ include: string | null;
102
+ regex: boolean;
103
+ context: number;
104
+ limit: number;
105
+ }
106
+ export interface SearchTextEngineContextLine {
107
+ line: number;
108
+ text: string;
109
+ }
110
+ export interface SearchTextEngineSubmatch {
111
+ start: number;
112
+ end: number;
113
+ text: string;
114
+ }
115
+ export interface SearchTextEngineMatch {
116
+ line: number;
117
+ text: string;
118
+ submatches: SearchTextEngineSubmatch[];
119
+ before: SearchTextEngineContextLine[];
120
+ after: SearchTextEngineContextLine[];
121
+ }
122
+ export interface SearchTextEngineFileMatch {
123
+ path: string;
124
+ language: PublicLanguage | null;
125
+ matchCount: number;
126
+ matches: SearchTextEngineMatch[];
127
+ }
128
+ export interface SearchTextEngineResult {
129
+ resolvedPath: string | null;
130
+ items: SearchTextEngineFileMatch[];
131
+ totalFileCount: number;
132
+ totalMatchCount: number;
133
+ truncated: boolean;
134
+ }
135
+ export interface TraceSymbolEnginePayload {
136
+ path: string;
137
+ symbol: string;
138
+ analyzerLanguage: AnalyzerLanguage;
139
+ publicLanguageFilter: PublicLanguage | null;
140
+ }
141
+ export interface TraceSymbolEngineItem {
142
+ path: string;
143
+ language: PublicLanguage | null;
144
+ }
145
+ export interface TraceSymbolEngineResult {
146
+ resolvedPath: string | null;
147
+ items: TraceSymbolEngineItem[];
148
+ totalMatched: number;
149
+ truncated: boolean;
150
+ }
151
+ export interface TraceCallersEnginePayload {
152
+ path: string;
153
+ symbol: string;
154
+ analyzerLanguage: AnalyzerLanguage;
155
+ publicLanguageFilter: PublicLanguage | null;
156
+ recursive: boolean;
157
+ maxDepth: number | null;
158
+ }
159
+ export interface TraceCallersEngineTarget {
160
+ path: string;
161
+ symbol: string;
162
+ }
163
+ export interface TraceCallersEngineItem {
164
+ path: string;
165
+ line: number;
166
+ column: number | null;
167
+ caller: string;
168
+ callerSymbol: string | null;
169
+ relation: string;
170
+ language: PublicLanguage | null;
171
+ snippet: string | null;
172
+ receiverType: string | null;
173
+ }
174
+ export interface TraceCallersEngineRecursiveVia {
175
+ relation: string | null;
176
+ line: number | null;
177
+ column: number | null;
178
+ snippet: string | null;
179
+ }
180
+ export interface TraceCallersEngineRecursiveNode {
181
+ key: string;
182
+ path: string;
183
+ symbol: string;
184
+ depth: number;
185
+ via: TraceCallersEngineRecursiveVia | null;
186
+ }
187
+ export interface TraceCallersEngineRecursivePathSegment {
188
+ path: string;
189
+ symbol: string;
190
+ depth: number;
191
+ }
192
+ export interface TraceCallersEngineRecursiveCycle {
193
+ fromKey: string;
194
+ toKey: string;
195
+ path: string[];
196
+ }
197
+ export interface TraceCallersEngineRecursiveTruncatedNode {
198
+ key: string;
199
+ path: string;
200
+ symbol: string;
201
+ depth: number;
202
+ }
203
+ export interface TraceCallersEngineProbableEntryPoint {
204
+ key: string | null;
205
+ path: string;
206
+ symbol: string;
207
+ depth: number | null;
208
+ reasons: string[];
209
+ probable: boolean | null;
210
+ pathFromTarget: TraceCallersEngineRecursivePathSegment[];
211
+ }
212
+ export interface TraceCallersEngineClassificationRecord {
213
+ path: string;
214
+ symbol: string;
215
+ caller: string;
216
+ depth: number;
217
+ line: number;
218
+ column: number | null;
219
+ relation: string;
220
+ language: PublicLanguage | null;
221
+ receiverType: string | null;
222
+ snippet: string | null;
223
+ calls: TraceCallersEngineTarget;
224
+ pathFromTarget: TraceCallersEngineRecursivePathSegment[];
225
+ }
226
+ export interface TraceCallersEngineImplementationInterface {
227
+ name: string | null;
228
+ path: string | null;
229
+ symbol: string | null;
230
+ }
231
+ export interface TraceCallersEngineImplementationReference {
232
+ path: string;
233
+ symbol: string | null;
234
+ }
235
+ export interface TraceCallersEngineImplementationInterfaceChain {
236
+ kind: string;
237
+ probable: boolean | null;
238
+ interface: TraceCallersEngineImplementationInterface | null;
239
+ implementation: TraceCallersEngineImplementationReference | null;
240
+ implementations: TraceCallersEngineImplementationReference[];
241
+ callers: TraceCallersEngineClassificationRecord[];
242
+ }
243
+ export interface TraceCallersEngineRecursiveSummary {
244
+ directCallerCount: number;
245
+ indirectCallerCount: number;
246
+ probablePublicEntryPointCount: number;
247
+ implementationInterfaceChainCount: number;
248
+ }
249
+ export interface TraceCallersEngineRecursiveClassifications {
250
+ summary: TraceCallersEngineRecursiveSummary;
251
+ directCallers: TraceCallersEngineClassificationRecord[];
252
+ indirectCallers: TraceCallersEngineClassificationRecord[];
253
+ probablePublicEntryPoints: TraceCallersEngineProbableEntryPoint[];
254
+ implementationInterfaceChain: TraceCallersEngineImplementationInterfaceChain[];
255
+ }
256
+ export interface TraceCallersEngineRecursiveResult {
257
+ enabled: boolean;
258
+ root: TraceCallersEngineRecursiveNode;
259
+ maxDepth: number;
260
+ maxDepthObserved: number;
261
+ nodeCount: number;
262
+ edgeCount: number;
263
+ pathCount: number;
264
+ nodes: TraceCallersEngineRecursiveNode[];
265
+ adjacency: Record<string, string[]>;
266
+ paths: TraceCallersEngineRecursivePathSegment[][];
267
+ cycles: TraceCallersEngineRecursiveCycle[];
268
+ truncated: TraceCallersEngineRecursiveTruncatedNode[];
269
+ probableEntryPoints: TraceCallersEngineProbableEntryPoint[];
270
+ classifications: TraceCallersEngineRecursiveClassifications;
271
+ }
272
+ export interface TraceCallersEngineResult {
273
+ resolvedPath: string | null;
274
+ items: TraceCallersEngineItem[];
275
+ totalMatched: number;
276
+ truncated: boolean;
277
+ recursive: TraceCallersEngineRecursiveResult | null;
278
+ }
279
+ export interface EngineSuccess<TResult = unknown> {
280
+ id: string;
281
+ ok: true;
282
+ result: TResult;
283
+ }
284
+ export interface EngineFailure {
285
+ id: string;
286
+ ok: false;
287
+ error: EngineError;
288
+ }
289
+ export type EngineResponse<TResult = unknown> = EngineSuccess<TResult> | EngineFailure;
290
+ export declare function nextRequestId(prefix?: string): string;
@@ -0,0 +1,13 @@
1
+ export const ENGINE_CAPABILITIES = [
2
+ "workspace.inspect_tree",
3
+ "workspace.find_symbol",
4
+ "workspace.list_endpoints",
5
+ "workspace.search_text",
6
+ "workspace.trace_symbol",
7
+ "workspace.trace_callers",
8
+ ];
9
+ let requestSequence = 0;
10
+ export function nextRequestId(prefix = "req") {
11
+ requestSequence += 1;
12
+ return `${prefix}-${requestSequence}`;
13
+ }
@@ -0,0 +1,16 @@
1
+ import type { EngineRequest, EngineResponse } from "./protocol.ts";
2
+ export interface EngineClient {
3
+ request<TResult = unknown>(request: EngineRequest): Promise<EngineResponse<TResult>>;
4
+ close(): Promise<void>;
5
+ }
6
+ export interface RustEngineClientOptions {
7
+ command?: readonly string[];
8
+ cwd?: string;
9
+ env?: NodeJS.ProcessEnv;
10
+ }
11
+ export declare class RustEngineClient implements EngineClient {
12
+ #private;
13
+ constructor(options?: RustEngineClientOptions);
14
+ request<TResult = unknown>(request: EngineRequest): Promise<EngineResponse<TResult>>;
15
+ close(): Promise<void>;
16
+ }
@@ -0,0 +1,138 @@
1
+ import { spawn } from "node:child_process";
2
+ import { existsSync } from "node:fs";
3
+ import { createRequire } from "node:module";
4
+ import { dirname, join } from "node:path";
5
+ import { once } from "node:events";
6
+ import { EOL } from "node:os";
7
+ const _require = createRequire(import.meta.url);
8
+ export class RustEngineClient {
9
+ #options;
10
+ #child = null;
11
+ #pending = new Map();
12
+ #buffer = "";
13
+ constructor(options = {}) {
14
+ this.#options = options;
15
+ }
16
+ async request(request) {
17
+ const child = this.#ensureChild();
18
+ const responsePromise = new Promise((resolve, reject) => {
19
+ this.#pending.set(request.id, {
20
+ resolve: resolve,
21
+ reject,
22
+ });
23
+ });
24
+ child.stdin.write(`${JSON.stringify(request)}${EOL}`);
25
+ return responsePromise;
26
+ }
27
+ async close() {
28
+ if (!this.#child) {
29
+ return;
30
+ }
31
+ const child = this.#child;
32
+ this.#child = null;
33
+ child.stdin.end();
34
+ child.kill();
35
+ await once(child, "exit").catch(() => undefined);
36
+ }
37
+ #ensureChild() {
38
+ if (this.#child) {
39
+ return this.#child;
40
+ }
41
+ const command = resolveEngineCommand(this.#options.command);
42
+ const [executable, ...args] = command;
43
+ const child = spawn(executable, args, {
44
+ cwd: this.#options.cwd ?? process.cwd(),
45
+ env: { ...process.env, ...this.#options.env },
46
+ stdio: ["pipe", "pipe", "pipe"],
47
+ });
48
+ child.stdout.setEncoding("utf8");
49
+ child.stdout.on("data", (chunk) => {
50
+ this.#buffer += chunk;
51
+ const lines = this.#buffer.split(/\r?\n/);
52
+ this.#buffer = lines.pop() ?? "";
53
+ for (const line of lines) {
54
+ const trimmed = line.trim();
55
+ if (!trimmed) {
56
+ continue;
57
+ }
58
+ let parsed;
59
+ try {
60
+ parsed = JSON.parse(trimmed);
61
+ }
62
+ catch (error) {
63
+ this.#failAll(new Error(`Rust engine returned invalid JSON: ${error.message}`));
64
+ continue;
65
+ }
66
+ const pending = this.#pending.get(parsed.id);
67
+ if (!pending) {
68
+ continue;
69
+ }
70
+ this.#pending.delete(parsed.id);
71
+ pending.resolve(parsed);
72
+ }
73
+ });
74
+ child.stderr.setEncoding("utf8");
75
+ child.stderr.on("data", () => {
76
+ // keep stderr attached for debugging without leaking into public protocol handling
77
+ });
78
+ child.on("error", (error) => {
79
+ this.#failAll(error instanceof Error ? error : new Error(String(error)));
80
+ this.#child = null;
81
+ });
82
+ child.on("exit", (code, signal) => {
83
+ this.#failAll(new Error(`Rust engine exited unexpectedly (code=${code ?? "null"}, signal=${signal ?? "null"}).`));
84
+ this.#child = null;
85
+ });
86
+ this.#child = child;
87
+ return child;
88
+ }
89
+ #failAll(error) {
90
+ for (const pending of this.#pending.values()) {
91
+ pending.reject(error);
92
+ }
93
+ this.#pending.clear();
94
+ }
95
+ }
96
+ function resolveEngineCommand(override) {
97
+ if (override && override.length > 0) {
98
+ return override;
99
+ }
100
+ const configured = process.env.NAVIGATION_MCP_RUST_ENGINE_CMD;
101
+ if (configured) {
102
+ const parsed = JSON.parse(configured);
103
+ if (Array.isArray(parsed) && parsed.every((value) => typeof value === "string")) {
104
+ return parsed;
105
+ }
106
+ throw new Error("NAVIGATION_MCP_RUST_ENGINE_CMD must be a JSON array of strings.");
107
+ }
108
+ // Try to find the pre-compiled binary bundled in the platform-specific optional package.
109
+ const binaryName = process.platform === "win32" ? "navigation-engine.exe" : "navigation-engine";
110
+ const platformPackages = {
111
+ "linux-x64": "@navigation-agent/mcp-server-linux-x64",
112
+ "linux-arm64": "@navigation-agent/mcp-server-linux-arm64",
113
+ "darwin-x64": "@navigation-agent/mcp-server-darwin-x64",
114
+ "darwin-arm64": "@navigation-agent/mcp-server-darwin-arm64",
115
+ "win32-x64": "@navigation-agent/mcp-server-win32-x64",
116
+ };
117
+ const pkgName = platformPackages[`${process.platform}-${process.arch}`];
118
+ if (pkgName) {
119
+ try {
120
+ const pkgJsonPath = _require.resolve(`${pkgName}/package.json`);
121
+ const binaryPath = join(dirname(pkgJsonPath), binaryName);
122
+ if (existsSync(binaryPath)) {
123
+ return [binaryPath];
124
+ }
125
+ }
126
+ catch {
127
+ // Optional package not installed — fall through to dev fallback.
128
+ }
129
+ }
130
+ // Dev fallback: build and run via cargo.
131
+ return [
132
+ "cargo",
133
+ "run",
134
+ "--quiet",
135
+ "--manifest-path",
136
+ "crates/navigation-engine/Cargo.toml",
137
+ ];
138
+ }
@@ -0,0 +1,12 @@
1
+ export * from "./app/createMcpServer.ts";
2
+ export * from "./contracts/public/code.ts";
3
+ export * from "./contracts/public/common.ts";
4
+ export * from "./engine/protocol.ts";
5
+ export * from "./engine/rustEngineClient.ts";
6
+ export * from "./services/findSymbolService.ts";
7
+ export * from "./services/inspectTreeService.ts";
8
+ export * from "./services/listEndpointsService.ts";
9
+ export * from "./services/searchTextService.ts";
10
+ export * from "./services/traceCallersService.ts";
11
+ export * from "./services/traceSymbolService.ts";
12
+ export * from "./tools/registerCodeTools.ts";
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ export * from "./app/createMcpServer.js";
2
+ export * from "./contracts/public/code.js";
3
+ export * from "./contracts/public/common.js";
4
+ export * from "./engine/protocol.js";
5
+ export * from "./engine/rustEngineClient.js";
6
+ export * from "./services/findSymbolService.js";
7
+ export * from "./services/inspectTreeService.js";
8
+ export * from "./services/listEndpointsService.js";
9
+ export * from "./services/searchTextService.js";
10
+ export * from "./services/traceCallersService.js";
11
+ export * from "./services/traceSymbolService.js";
12
+ export * from "./tools/registerCodeTools.js";
@@ -0,0 +1,11 @@
1
+ import { type FindSymbolData, type FindSymbolInput } 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 FindSymbolService {
5
+ execute(input: FindSymbolInput): Promise<ResponseEnvelope<FindSymbolData>>;
6
+ validateAndExecute(payload: Record<string, unknown>): Promise<ResponseEnvelope<FindSymbolData>>;
7
+ }
8
+ export declare function createFindSymbolService(options: {
9
+ workspaceRoot: string;
10
+ engineClient: EngineClient;
11
+ }): FindSymbolService;