@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,18 @@
1
+ import { type EngineClient } from "../engine/rustEngineClient.ts";
2
+ import { type RegisteredCodeTool } from "../tools/registerCodeTools.ts";
3
+ export interface CreateMcpServerOptions {
4
+ workspaceRoot: string;
5
+ engineClient?: EngineClient;
6
+ }
7
+ export interface McpServerPlan {
8
+ name: "navigation-agent-mcp";
9
+ version: "0.1.0";
10
+ workspaceRoot: string;
11
+ tools: RegisteredCodeTool[];
12
+ listTools(): RegisteredCodeTool[];
13
+ callTool(name: string, payload: Record<string, unknown>): Promise<unknown>;
14
+ serveStdio(): Promise<void>;
15
+ serveStdioLegacy(): Promise<void>;
16
+ close(): Promise<void>;
17
+ }
18
+ export declare function createMcpServer(options: CreateMcpServerOptions): McpServerPlan;
@@ -0,0 +1,152 @@
1
+ import { McpServer as SdkMcpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { RustEngineClient } from "../engine/rustEngineClient.js";
4
+ import { createFindSymbolService } from "../services/findSymbolService.js";
5
+ import { createInspectTreeService } from "../services/inspectTreeService.js";
6
+ import { createListEndpointsService } from "../services/listEndpointsService.js";
7
+ import { createSearchTextService } from "../services/searchTextService.js";
8
+ import { createTraceCallersService } from "../services/traceCallersService.js";
9
+ import { createTraceSymbolService } from "../services/traceSymbolService.js";
10
+ import { registerCodeTools, } from "../tools/registerCodeTools.js";
11
+ function toSdkToolResult(result) {
12
+ return {
13
+ content: [
14
+ {
15
+ type: "text",
16
+ text: JSON.stringify(result, null, 2),
17
+ },
18
+ ],
19
+ structuredContent: result,
20
+ isError: result.status === "error",
21
+ };
22
+ }
23
+ export function createMcpServer(options) {
24
+ const engineClient = options.engineClient ?? new RustEngineClient();
25
+ const inspectTreeService = createInspectTreeService({
26
+ workspaceRoot: options.workspaceRoot,
27
+ engineClient,
28
+ });
29
+ const findSymbolService = createFindSymbolService({
30
+ workspaceRoot: options.workspaceRoot,
31
+ engineClient,
32
+ });
33
+ const listEndpointsService = createListEndpointsService({
34
+ workspaceRoot: options.workspaceRoot,
35
+ engineClient,
36
+ });
37
+ const searchTextService = createSearchTextService({
38
+ workspaceRoot: options.workspaceRoot,
39
+ engineClient,
40
+ });
41
+ const traceSymbolService = createTraceSymbolService({
42
+ workspaceRoot: options.workspaceRoot,
43
+ engineClient,
44
+ });
45
+ const traceCallersService = createTraceCallersService({
46
+ workspaceRoot: options.workspaceRoot,
47
+ engineClient,
48
+ });
49
+ const tools = registerCodeTools({
50
+ inspectTreeHandler: (payload) => inspectTreeService.validateAndExecute(payload),
51
+ findSymbolHandler: (payload) => findSymbolService.validateAndExecute(payload),
52
+ listEndpointsHandler: (payload) => listEndpointsService.validateAndExecute(payload),
53
+ searchTextHandler: (payload) => searchTextService.validateAndExecute(payload),
54
+ traceCallersHandler: (payload) => traceCallersService.validateAndExecute(payload),
55
+ traceSymbolHandler: (payload) => traceSymbolService.validateAndExecute(payload),
56
+ });
57
+ const sdkServer = new SdkMcpServer({
58
+ name: "navigation-agent-mcp",
59
+ version: "0.1.0",
60
+ });
61
+ for (const tool of tools) {
62
+ sdkServer.registerTool(tool.name, {
63
+ title: tool.title,
64
+ description: tool.description,
65
+ inputSchema: tool.sdkInputSchema,
66
+ }, async (payload) => toSdkToolResult(await tool.execute(payload)));
67
+ }
68
+ return {
69
+ name: "navigation-agent-mcp",
70
+ version: "0.1.0",
71
+ workspaceRoot: options.workspaceRoot,
72
+ tools,
73
+ listTools() {
74
+ return tools;
75
+ },
76
+ async callTool(name, payload) {
77
+ const tool = tools.find((candidate) => candidate.name === name);
78
+ if (!tool) {
79
+ throw new Error(`Unknown tool '${name}'.`);
80
+ }
81
+ return tool.execute(payload);
82
+ },
83
+ async serveStdio() {
84
+ await sdkServer.connect(new StdioServerTransport());
85
+ },
86
+ async serveStdioLegacy() {
87
+ process.stdin.setEncoding("utf8");
88
+ let buffer = "";
89
+ for await (const chunk of process.stdin) {
90
+ buffer += chunk;
91
+ const lines = buffer.split(/\r?\n/);
92
+ buffer = lines.pop() ?? "";
93
+ for (const line of lines) {
94
+ const trimmed = line.trim();
95
+ if (!trimmed) {
96
+ continue;
97
+ }
98
+ let request;
99
+ try {
100
+ request = JSON.parse(trimmed);
101
+ }
102
+ catch (error) {
103
+ process.stdout.write(`${JSON.stringify({
104
+ id: null,
105
+ ok: false,
106
+ error: {
107
+ code: "INVALID_REQUEST",
108
+ message: error instanceof Error ? error.message : String(error),
109
+ },
110
+ })}\n`);
111
+ continue;
112
+ }
113
+ const id = request.id ?? null;
114
+ if (request.method === "list_tools") {
115
+ process.stdout.write(`${JSON.stringify({ id, ok: true, result: tools.map(({ execute, sdkInputSchema, ...tool }) => tool) })}\n`);
116
+ continue;
117
+ }
118
+ if (request.method === "call_tool") {
119
+ const toolName = typeof request.params?.name === "string" ? request.params.name : null;
120
+ const argumentsPayload = request.params && typeof request.params.arguments === "object" && request.params.arguments
121
+ ? request.params.arguments
122
+ : {};
123
+ if (!toolName) {
124
+ process.stdout.write(`${JSON.stringify({ id, ok: false, error: { code: "INVALID_REQUEST", message: "call_tool requires params.name" } })}\n`);
125
+ continue;
126
+ }
127
+ try {
128
+ const result = await this.callTool(toolName, argumentsPayload);
129
+ process.stdout.write(`${JSON.stringify({ id, ok: true, result })}\n`);
130
+ }
131
+ catch (error) {
132
+ process.stdout.write(`${JSON.stringify({
133
+ id,
134
+ ok: false,
135
+ error: {
136
+ code: "CALL_FAILED",
137
+ message: error instanceof Error ? error.message : String(error),
138
+ },
139
+ })}\n`);
140
+ }
141
+ continue;
142
+ }
143
+ process.stdout.write(`${JSON.stringify({ id, ok: false, error: { code: "INVALID_REQUEST", message: `Unsupported method '${request.method ?? "unknown"}'.` } })}\n`);
144
+ }
145
+ }
146
+ },
147
+ async close() {
148
+ await sdkServer.close();
149
+ await engineClient.close();
150
+ },
151
+ };
152
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env -S node --experimental-strip-types
2
+ export {};
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ #!/usr/bin/env -S node --experimental-strip-types
3
+ import { createMcpServer } from "../app/createMcpServer.js";
4
+ function getFlagValue(argv, flag) {
5
+ const flagIndex = argv.findIndex((value) => value === flag);
6
+ if (flagIndex === -1) {
7
+ return null;
8
+ }
9
+ const value = argv[flagIndex + 1];
10
+ return value && !value.startsWith("--") ? value : null;
11
+ }
12
+ const argv = process.argv.slice(2);
13
+ const server = createMcpServer({
14
+ workspaceRoot: getFlagValue(argv, "--workspace-root") ?? process.cwd(),
15
+ });
16
+ if (argv.includes("--describe-tools")) {
17
+ process.stdout.write(`${JSON.stringify({
18
+ name: server.name,
19
+ version: server.version,
20
+ workspaceRoot: server.workspaceRoot,
21
+ toolCount: server.tools.length,
22
+ tools: server.listTools().map(({ execute, sdkInputSchema, ...tool }) => tool),
23
+ runtime: "navigation-sdk-stdio",
24
+ transports: ["stdio", "stdio-legacy"],
25
+ }, null, 2)}\n`);
26
+ }
27
+ else if ((getFlagValue(argv, "--transport") ?? "stdio") === "stdio") {
28
+ await server.serveStdio();
29
+ }
30
+ else if (getFlagValue(argv, "--transport") === "stdio-legacy") {
31
+ await server.serveStdioLegacy();
32
+ }
33
+ else {
34
+ process.stderr.write("Supported transports: stdio, stdio-legacy.\n");
35
+ process.exitCode = 1;
36
+ }