debugger-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.
- package/CLAUDE.md +73 -0
- package/README.md +108 -0
- package/dist/bridge/bridgeClient.d.ts +5 -0
- package/dist/bridge/bridgeClient.js +33 -0
- package/dist/bridge/bridgeClient.js.map +1 -0
- package/dist/bridge/bridgeTypes.d.ts +61 -0
- package/dist/bridge/bridgeTypes.js +2 -0
- package/dist/bridge/bridgeTypes.js.map +1 -0
- package/dist/bridge/connectionManager.d.ts +2 -0
- package/dist/bridge/connectionManager.js +67 -0
- package/dist/bridge/connectionManager.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +124 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/analysis/analyzeCode.d.ts +4 -0
- package/dist/tools/analysis/analyzeCode.js +26 -0
- package/dist/tools/analysis/analyzeCode.js.map +1 -0
- package/dist/tools/breakpoints/listBreakpoints.d.ts +4 -0
- package/dist/tools/breakpoints/listBreakpoints.js +16 -0
- package/dist/tools/breakpoints/listBreakpoints.js.map +1 -0
- package/dist/tools/breakpoints/removeBreakpoints.d.ts +4 -0
- package/dist/tools/breakpoints/removeBreakpoints.js +22 -0
- package/dist/tools/breakpoints/removeBreakpoints.js.map +1 -0
- package/dist/tools/breakpoints/setBreakpoints.d.ts +4 -0
- package/dist/tools/breakpoints/setBreakpoints.js +29 -0
- package/dist/tools/breakpoints/setBreakpoints.js.map +1 -0
- package/dist/tools/debugSession/continueExecution.d.ts +4 -0
- package/dist/tools/debugSession/continueExecution.js +24 -0
- package/dist/tools/debugSession/continueExecution.js.map +1 -0
- package/dist/tools/debugSession/evaluateExpression.d.ts +4 -0
- package/dist/tools/debugSession/evaluateExpression.js +28 -0
- package/dist/tools/debugSession/evaluateExpression.js.map +1 -0
- package/dist/tools/debugSession/getCallStack.d.ts +4 -0
- package/dist/tools/debugSession/getCallStack.js +24 -0
- package/dist/tools/debugSession/getCallStack.js.map +1 -0
- package/dist/tools/debugSession/getVariables.d.ts +4 -0
- package/dist/tools/debugSession/getVariables.js +26 -0
- package/dist/tools/debugSession/getVariables.js.map +1 -0
- package/dist/tools/debugSession/startDebugSession.d.ts +4 -0
- package/dist/tools/debugSession/startDebugSession.js +34 -0
- package/dist/tools/debugSession/startDebugSession.js.map +1 -0
- package/dist/tools/debugSession/stepInto.d.ts +4 -0
- package/dist/tools/debugSession/stepInto.js +24 -0
- package/dist/tools/debugSession/stepInto.js.map +1 -0
- package/dist/tools/debugSession/stepOut.d.ts +4 -0
- package/dist/tools/debugSession/stepOut.js +24 -0
- package/dist/tools/debugSession/stepOut.js.map +1 -0
- package/dist/tools/debugSession/stepOver.d.ts +4 -0
- package/dist/tools/debugSession/stepOver.js +24 -0
- package/dist/tools/debugSession/stepOver.js.map +1 -0
- package/dist/tools/debugSession/stopDebugSession.d.ts +4 -0
- package/dist/tools/debugSession/stopDebugSession.js +22 -0
- package/dist/tools/debugSession/stopDebugSession.js.map +1 -0
- package/dist/tools/toolRegistry.d.ts +4 -0
- package/dist/tools/toolRegistry.js +29 -0
- package/dist/tools/toolRegistry.js.map +1 -0
- package/dist/tools/toolTypes.d.ts +6 -0
- package/dist/tools/toolTypes.js +8 -0
- package/dist/tools/toolTypes.js.map +1 -0
- package/package.json +23 -0
- package/src/bridge/bridgeClient.ts +40 -0
- package/src/bridge/bridgeTypes.ts +70 -0
- package/src/bridge/connectionManager.ts +82 -0
- package/src/index.ts +10 -0
- package/src/server.ts +143 -0
- package/src/tools/analysis/analyzeCode.ts +33 -0
- package/src/tools/breakpoints/listBreakpoints.ts +23 -0
- package/src/tools/breakpoints/removeBreakpoints.ts +28 -0
- package/src/tools/breakpoints/setBreakpoints.ts +37 -0
- package/src/tools/debugSession/continueExecution.ts +30 -0
- package/src/tools/debugSession/evaluateExpression.ts +34 -0
- package/src/tools/debugSession/getCallStack.ts +30 -0
- package/src/tools/debugSession/getVariables.ts +32 -0
- package/src/tools/debugSession/startDebugSession.ts +40 -0
- package/src/tools/debugSession/stepInto.ts +30 -0
- package/src/tools/debugSession/stepOut.ts +30 -0
- package/src/tools/debugSession/stepOver.ts +30 -0
- package/src/tools/debugSession/stopDebugSession.ts +28 -0
- package/src/tools/toolRegistry.ts +30 -0
- package/src/tools/toolTypes.ts +8 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { readFileSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { homedir } from "os";
|
|
4
|
+
import type { BridgeHealthResponse } from "./bridgeTypes.js";
|
|
5
|
+
|
|
6
|
+
const PORT_FILE_PATH = join(homedir(), ".agentic-debugger", "bridge-port");
|
|
7
|
+
const HEALTH_TIMEOUT_MS = 5000;
|
|
8
|
+
|
|
9
|
+
type CachedUrl = string | null;
|
|
10
|
+
const cache: { bridgeUrl: CachedUrl } = { bridgeUrl: null };
|
|
11
|
+
|
|
12
|
+
const readPortFile = (): number => {
|
|
13
|
+
try {
|
|
14
|
+
const content = readFileSync(PORT_FILE_PATH, "utf-8").trim();
|
|
15
|
+
const port = parseInt(content, 10);
|
|
16
|
+
if (isNaN(port) || port <= 0 || port > 65535) {
|
|
17
|
+
throw new Error("Invalid port number in bridge port file.");
|
|
18
|
+
}
|
|
19
|
+
return port;
|
|
20
|
+
} catch (error) {
|
|
21
|
+
if (error instanceof Error && error.message.includes("Invalid port")) {
|
|
22
|
+
throw error;
|
|
23
|
+
}
|
|
24
|
+
throw new Error(
|
|
25
|
+
"Bridge port file not found. Make sure VS Code is open with the Agentic Debugger extension installed."
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const checkHealth = async (params: { url: string }): Promise<boolean> => {
|
|
31
|
+
const { url } = params;
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const controller = new AbortController();
|
|
35
|
+
const timeout = setTimeout(() => controller.abort(), HEALTH_TIMEOUT_MS);
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const response = await fetch(`${url}/api/health`, {
|
|
39
|
+
method: "GET",
|
|
40
|
+
signal: controller.signal,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
if (!response.ok) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const data = (await response.json()) as BridgeHealthResponse;
|
|
48
|
+
return data.status === "ok";
|
|
49
|
+
} finally {
|
|
50
|
+
clearTimeout(timeout);
|
|
51
|
+
}
|
|
52
|
+
} catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const getBridgeUrl = async (): Promise<string> => {
|
|
58
|
+
if (cache.bridgeUrl !== null) {
|
|
59
|
+
const isHealthy = await checkHealth({ url: cache.bridgeUrl });
|
|
60
|
+
if (isHealthy) {
|
|
61
|
+
return cache.bridgeUrl;
|
|
62
|
+
}
|
|
63
|
+
cache.bridgeUrl = null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const port = readPortFile();
|
|
67
|
+
const url = `http://127.0.0.1:${port}`;
|
|
68
|
+
const isHealthy = await checkHealth({ url });
|
|
69
|
+
|
|
70
|
+
if (!isHealthy) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
"VS Code extension bridge is not responding. Make sure VS Code is open with the Agentic Debugger extension installed."
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
cache.bridgeUrl = url;
|
|
77
|
+
return url;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export const invalidateCache = (): void => {
|
|
81
|
+
cache.bridgeUrl = null;
|
|
82
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createApp } from "./server.js";
|
|
2
|
+
|
|
3
|
+
const DEFAULT_PORT = 6090;
|
|
4
|
+
const PORT = parseInt(process.env["PORT"] ?? String(DEFAULT_PORT), 10);
|
|
5
|
+
|
|
6
|
+
const app = createApp();
|
|
7
|
+
|
|
8
|
+
app.listen(PORT, () => {
|
|
9
|
+
console.log(`Debugger MCP server running on port ${PORT}.`);
|
|
10
|
+
});
|
package/src/server.ts
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
3
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
4
|
+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import { randomUUID } from "crypto";
|
|
6
|
+
import express from "express";
|
|
7
|
+
import { registerAllTools } from "./tools/toolRegistry.js";
|
|
8
|
+
|
|
9
|
+
const createConnectedServer = (): McpServer => {
|
|
10
|
+
const server = new McpServer({
|
|
11
|
+
name: "debugger-mcp-server",
|
|
12
|
+
version: "0.1.0",
|
|
13
|
+
});
|
|
14
|
+
registerAllTools({ server });
|
|
15
|
+
return server;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const createApp = (): express.Express => {
|
|
19
|
+
const app = express();
|
|
20
|
+
app.use(express.json());
|
|
21
|
+
|
|
22
|
+
const transports = new Map<string, StreamableHTTPServerTransport | SSEServerTransport>();
|
|
23
|
+
|
|
24
|
+
// ==========================================================================
|
|
25
|
+
// Streamable HTTP transport (protocol version 2025-11-25)
|
|
26
|
+
// ==========================================================================
|
|
27
|
+
|
|
28
|
+
app.post("/mcp", async (req, res) => {
|
|
29
|
+
const sessionId = req.headers["mcp-session-id"] as string | undefined;
|
|
30
|
+
|
|
31
|
+
if (sessionId !== undefined && transports.has(sessionId)) {
|
|
32
|
+
const existingTransport = transports.get(sessionId) as NonNullable<ReturnType<typeof transports.get>>;
|
|
33
|
+
if (existingTransport instanceof SSEServerTransport) {
|
|
34
|
+
res.status(400).json({
|
|
35
|
+
jsonrpc: "2.0",
|
|
36
|
+
error: { code: -32000, message: "Session uses a different transport protocol." },
|
|
37
|
+
id: null,
|
|
38
|
+
});
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
await existingTransport.handleRequest(req, res, req.body);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (isInitializeRequest(req.body)) {
|
|
46
|
+
const transport = new StreamableHTTPServerTransport({
|
|
47
|
+
sessionIdGenerator: () => randomUUID(),
|
|
48
|
+
onsessioninitialized: (newSessionId) => {
|
|
49
|
+
transports.set(newSessionId, transport);
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
transport.onclose = () => {
|
|
54
|
+
const transportSessionId = transport.sessionId;
|
|
55
|
+
if (transportSessionId !== undefined) {
|
|
56
|
+
transports.delete(transportSessionId);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const server = createConnectedServer();
|
|
61
|
+
await server.connect(transport);
|
|
62
|
+
await transport.handleRequest(req, res, req.body);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
res.status(400).json({ error: "Invalid request. Missing session ID or not an initialize request." });
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
app.get("/mcp", async (req, res) => {
|
|
70
|
+
const sessionId = req.headers["mcp-session-id"] as string | undefined;
|
|
71
|
+
if (sessionId !== undefined && transports.has(sessionId)) {
|
|
72
|
+
const existingTransport = transports.get(sessionId) as NonNullable<ReturnType<typeof transports.get>>;
|
|
73
|
+
if (existingTransport instanceof SSEServerTransport) {
|
|
74
|
+
res.status(400).json({
|
|
75
|
+
jsonrpc: "2.0",
|
|
76
|
+
error: { code: -32000, message: "Session uses a different transport protocol." },
|
|
77
|
+
id: null,
|
|
78
|
+
});
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
await existingTransport.handleRequest(req, res);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
res.status(400).json({ error: "Invalid session." });
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
app.delete("/mcp", async (req, res) => {
|
|
88
|
+
const sessionId = req.headers["mcp-session-id"] as string | undefined;
|
|
89
|
+
if (sessionId !== undefined && transports.has(sessionId)) {
|
|
90
|
+
const existingTransport = transports.get(sessionId) as NonNullable<ReturnType<typeof transports.get>>;
|
|
91
|
+
if (existingTransport instanceof SSEServerTransport) {
|
|
92
|
+
res.status(400).json({
|
|
93
|
+
jsonrpc: "2.0",
|
|
94
|
+
error: { code: -32000, message: "Session uses a different transport protocol." },
|
|
95
|
+
id: null,
|
|
96
|
+
});
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
await existingTransport.handleRequest(req, res);
|
|
100
|
+
transports.delete(sessionId);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
res.status(400).json({ error: "Invalid session." });
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// ==========================================================================
|
|
107
|
+
// Legacy SSE transport (protocol version 2024-11-05)
|
|
108
|
+
// ==========================================================================
|
|
109
|
+
|
|
110
|
+
app.get("/sse", async (_req, res) => {
|
|
111
|
+
const transport = new SSEServerTransport("/messages", res);
|
|
112
|
+
transports.set(transport.sessionId, transport);
|
|
113
|
+
|
|
114
|
+
res.on("close", () => {
|
|
115
|
+
transports.delete(transport.sessionId);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const server = createConnectedServer();
|
|
119
|
+
await server.connect(transport);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
app.post("/messages", async (req, res) => {
|
|
123
|
+
const sessionId = req.query.sessionId as string | undefined;
|
|
124
|
+
if (sessionId === undefined) {
|
|
125
|
+
res.status(400).json({ error: "Missing sessionId query parameter." });
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const existingTransport = transports.get(sessionId);
|
|
130
|
+
if (!(existingTransport instanceof SSEServerTransport)) {
|
|
131
|
+
res.status(400).json({
|
|
132
|
+
jsonrpc: "2.0",
|
|
133
|
+
error: { code: -32000, message: "No SSE transport found for this session." },
|
|
134
|
+
id: null,
|
|
135
|
+
});
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
await existingTransport.handlePostMessage(req, res, req.body);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
return app;
|
|
143
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { sendBridgeCommand } from "../../bridge/bridgeClient.js";
|
|
4
|
+
import { formatToolResult, formatErrorResult } from "../toolTypes.js";
|
|
5
|
+
import type { AnalyzeCodeResponse } from "../../bridge/bridgeTypes.js";
|
|
6
|
+
|
|
7
|
+
const ANALYZE_CODE_TIMEOUT_MS = 120000;
|
|
8
|
+
|
|
9
|
+
export const registerAnalyzeCodeTool = (params: { server: McpServer }): void => {
|
|
10
|
+
params.server.tool(
|
|
11
|
+
"analyze_code",
|
|
12
|
+
"Analyze codebase to find relevant code locations for debugging a feature. Uses AI to explore and identify entry points, core logic, and key functions.",
|
|
13
|
+
{
|
|
14
|
+
featureDescription: z.string().describe("Description of the feature or code area to analyze"),
|
|
15
|
+
workspacePath: z.string().describe("Absolute path to the workspace root"),
|
|
16
|
+
},
|
|
17
|
+
async (args) => {
|
|
18
|
+
try {
|
|
19
|
+
const result = await sendBridgeCommand<AnalyzeCodeResponse>({
|
|
20
|
+
command: "analyzeCode",
|
|
21
|
+
args: {
|
|
22
|
+
featureDescription: args.featureDescription,
|
|
23
|
+
workspacePath: args.workspacePath,
|
|
24
|
+
},
|
|
25
|
+
timeoutMs: ANALYZE_CODE_TIMEOUT_MS,
|
|
26
|
+
});
|
|
27
|
+
return { content: [{ type: "text" as const, text: formatToolResult({ data: result }) }] };
|
|
28
|
+
} catch (error) {
|
|
29
|
+
return { content: [{ type: "text" as const, text: formatErrorResult({ error }) }], isError: true };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { sendBridgeCommand } from "../../bridge/bridgeClient.js";
|
|
4
|
+
import { formatToolResult, formatErrorResult } from "../toolTypes.js";
|
|
5
|
+
import type { ListBreakpointsResponse } from "../../bridge/bridgeTypes.js";
|
|
6
|
+
|
|
7
|
+
export const registerListBreakpointsTool = (params: { server: McpServer }): void => {
|
|
8
|
+
params.server.tool(
|
|
9
|
+
"list_breakpoints",
|
|
10
|
+
"List all currently set breakpoints in VS Code with their locations and conditions.",
|
|
11
|
+
{},
|
|
12
|
+
async () => {
|
|
13
|
+
try {
|
|
14
|
+
const result = await sendBridgeCommand<ListBreakpointsResponse>({
|
|
15
|
+
command: "listBreakpoints",
|
|
16
|
+
});
|
|
17
|
+
return { content: [{ type: "text" as const, text: formatToolResult({ data: result }) }] };
|
|
18
|
+
} catch (error) {
|
|
19
|
+
return { content: [{ type: "text" as const, text: formatErrorResult({ error }) }], isError: true };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { sendBridgeCommand } from "../../bridge/bridgeClient.js";
|
|
4
|
+
import { formatToolResult, formatErrorResult } from "../toolTypes.js";
|
|
5
|
+
import type { RemoveBreakpointsResponse } from "../../bridge/bridgeTypes.js";
|
|
6
|
+
|
|
7
|
+
export const registerRemoveBreakpointsTool = (params: { server: McpServer }): void => {
|
|
8
|
+
params.server.tool(
|
|
9
|
+
"remove_breakpoints",
|
|
10
|
+
"Remove breakpoints by their IDs. If no IDs provided, removes all breakpoints.",
|
|
11
|
+
{
|
|
12
|
+
breakpointIds: z.array(z.string()).optional().describe("Specific breakpoint IDs to remove. Omit to remove all."),
|
|
13
|
+
},
|
|
14
|
+
async (args) => {
|
|
15
|
+
try {
|
|
16
|
+
const result = await sendBridgeCommand<RemoveBreakpointsResponse>({
|
|
17
|
+
command: "removeBreakpoints",
|
|
18
|
+
args: {
|
|
19
|
+
breakpointIds: args.breakpointIds,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
return { content: [{ type: "text" as const, text: formatToolResult({ data: result }) }] };
|
|
23
|
+
} catch (error) {
|
|
24
|
+
return { content: [{ type: "text" as const, text: formatErrorResult({ error }) }], isError: true };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { sendBridgeCommand } from "../../bridge/bridgeClient.js";
|
|
4
|
+
import { formatToolResult, formatErrorResult } from "../toolTypes.js";
|
|
5
|
+
import type { SetBreakpointsResponse } from "../../bridge/bridgeTypes.js";
|
|
6
|
+
|
|
7
|
+
export const registerSetBreakpointsTool = (params: { server: McpServer }): void => {
|
|
8
|
+
params.server.tool(
|
|
9
|
+
"set_breakpoints",
|
|
10
|
+
"Set breakpoints at specified file locations. Optionally clear existing breakpoints first.",
|
|
11
|
+
{
|
|
12
|
+
locations: z.array(
|
|
13
|
+
z.object({
|
|
14
|
+
filePath: z.string().describe("Absolute or workspace-relative file path"),
|
|
15
|
+
lineNumber: z.number().describe("Line number to set breakpoint on"),
|
|
16
|
+
condition: z.string().optional().describe("Conditional expression for the breakpoint"),
|
|
17
|
+
logMessage: z.string().optional().describe("Log message (logpoint) instead of breaking"),
|
|
18
|
+
})
|
|
19
|
+
).describe("Array of breakpoint locations to set"),
|
|
20
|
+
shouldClearExisting: z.boolean().default(false).describe("Clear all existing breakpoints before setting new ones"),
|
|
21
|
+
},
|
|
22
|
+
async (args) => {
|
|
23
|
+
try {
|
|
24
|
+
const result = await sendBridgeCommand<SetBreakpointsResponse>({
|
|
25
|
+
command: "setBreakpoints",
|
|
26
|
+
args: {
|
|
27
|
+
locations: args.locations,
|
|
28
|
+
shouldClearExisting: args.shouldClearExisting,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
return { content: [{ type: "text" as const, text: formatToolResult({ data: result }) }] };
|
|
32
|
+
} catch (error) {
|
|
33
|
+
return { content: [{ type: "text" as const, text: formatErrorResult({ error }) }], isError: true };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { sendBridgeCommand } from "../../bridge/bridgeClient.js";
|
|
4
|
+
import { formatToolResult, formatErrorResult } from "../toolTypes.js";
|
|
5
|
+
import type { StepResponse } from "../../bridge/bridgeTypes.js";
|
|
6
|
+
|
|
7
|
+
export const registerContinueExecutionTool = (params: { server: McpServer }): void => {
|
|
8
|
+
params.server.tool(
|
|
9
|
+
"continue_execution",
|
|
10
|
+
"Continue execution until the next breakpoint or program termination.",
|
|
11
|
+
{
|
|
12
|
+
sessionId: z.string().optional().describe("Debug session ID. Defaults to the active session."),
|
|
13
|
+
threadId: z.number().optional().describe("Thread ID to continue. Defaults to the first available thread."),
|
|
14
|
+
},
|
|
15
|
+
async (args) => {
|
|
16
|
+
try {
|
|
17
|
+
const result = await sendBridgeCommand<StepResponse>({
|
|
18
|
+
command: "continueExecution",
|
|
19
|
+
args: {
|
|
20
|
+
sessionId: args.sessionId,
|
|
21
|
+
threadId: args.threadId,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
return { content: [{ type: "text" as const, text: formatToolResult({ data: result }) }] };
|
|
25
|
+
} catch (error) {
|
|
26
|
+
return { content: [{ type: "text" as const, text: formatErrorResult({ error }) }], isError: true };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { sendBridgeCommand } from "../../bridge/bridgeClient.js";
|
|
4
|
+
import { formatToolResult, formatErrorResult } from "../toolTypes.js";
|
|
5
|
+
import type { EvaluateExpressionResponse } from "../../bridge/bridgeTypes.js";
|
|
6
|
+
|
|
7
|
+
export const registerEvaluateExpressionTool = (params: { server: McpServer }): void => {
|
|
8
|
+
params.server.tool(
|
|
9
|
+
"evaluate_expression",
|
|
10
|
+
"Evaluate an expression in the context of the current debug session. Useful for inspecting values, calling functions, or testing conditions.",
|
|
11
|
+
{
|
|
12
|
+
expression: z.string().describe("The expression to evaluate"),
|
|
13
|
+
sessionId: z.string().optional().describe("Debug session ID. Defaults to the active session."),
|
|
14
|
+
frameId: z.number().optional().describe("Stack frame ID for evaluation context. Defaults to the top frame."),
|
|
15
|
+
context: z.enum(["watch", "repl", "hover"]).optional().describe("Evaluation context type. Defaults to 'repl'."),
|
|
16
|
+
},
|
|
17
|
+
async (args) => {
|
|
18
|
+
try {
|
|
19
|
+
const result = await sendBridgeCommand<EvaluateExpressionResponse>({
|
|
20
|
+
command: "evaluateExpression",
|
|
21
|
+
args: {
|
|
22
|
+
expression: args.expression,
|
|
23
|
+
sessionId: args.sessionId,
|
|
24
|
+
frameId: args.frameId,
|
|
25
|
+
context: args.context,
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
return { content: [{ type: "text" as const, text: formatToolResult({ data: result }) }] };
|
|
29
|
+
} catch (error) {
|
|
30
|
+
return { content: [{ type: "text" as const, text: formatErrorResult({ error }) }], isError: true };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { sendBridgeCommand } from "../../bridge/bridgeClient.js";
|
|
4
|
+
import { formatToolResult, formatErrorResult } from "../toolTypes.js";
|
|
5
|
+
import type { GetCallStackResponse } from "../../bridge/bridgeTypes.js";
|
|
6
|
+
|
|
7
|
+
export const registerGetCallStackTool = (params: { server: McpServer }): void => {
|
|
8
|
+
params.server.tool(
|
|
9
|
+
"get_call_stack",
|
|
10
|
+
"Get the current call stack (stack trace) showing the chain of function calls.",
|
|
11
|
+
{
|
|
12
|
+
sessionId: z.string().optional().describe("Debug session ID. Defaults to the active session."),
|
|
13
|
+
threadId: z.number().optional().describe("Thread ID to get call stack for. Defaults to the first available thread."),
|
|
14
|
+
},
|
|
15
|
+
async (args) => {
|
|
16
|
+
try {
|
|
17
|
+
const result = await sendBridgeCommand<GetCallStackResponse>({
|
|
18
|
+
command: "getCallStack",
|
|
19
|
+
args: {
|
|
20
|
+
sessionId: args.sessionId,
|
|
21
|
+
threadId: args.threadId,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
return { content: [{ type: "text" as const, text: formatToolResult({ data: result }) }] };
|
|
25
|
+
} catch (error) {
|
|
26
|
+
return { content: [{ type: "text" as const, text: formatErrorResult({ error }) }], isError: true };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { sendBridgeCommand } from "../../bridge/bridgeClient.js";
|
|
4
|
+
import { formatToolResult, formatErrorResult } from "../toolTypes.js";
|
|
5
|
+
import type { GetVariablesResponse } from "../../bridge/bridgeTypes.js";
|
|
6
|
+
|
|
7
|
+
export const registerGetVariablesTool = (params: { server: McpServer }): void => {
|
|
8
|
+
params.server.tool(
|
|
9
|
+
"get_variables",
|
|
10
|
+
"Get variables visible at the current debug position. Returns all scopes (local, closure, global) by default.",
|
|
11
|
+
{
|
|
12
|
+
sessionId: z.string().optional().describe("Debug session ID. Defaults to the active session."),
|
|
13
|
+
frameId: z.number().optional().describe("Stack frame ID to get variables for. Defaults to the top frame."),
|
|
14
|
+
variablesReference: z.number().optional().describe("Specific variables reference to expand (for nested objects)."),
|
|
15
|
+
},
|
|
16
|
+
async (args) => {
|
|
17
|
+
try {
|
|
18
|
+
const result = await sendBridgeCommand<GetVariablesResponse>({
|
|
19
|
+
command: "getVariables",
|
|
20
|
+
args: {
|
|
21
|
+
sessionId: args.sessionId,
|
|
22
|
+
frameId: args.frameId,
|
|
23
|
+
variablesReference: args.variablesReference,
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
return { content: [{ type: "text" as const, text: formatToolResult({ data: result }) }] };
|
|
27
|
+
} catch (error) {
|
|
28
|
+
return { content: [{ type: "text" as const, text: formatErrorResult({ error }) }], isError: true };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
);
|
|
32
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { sendBridgeCommand } from "../../bridge/bridgeClient.js";
|
|
4
|
+
import { formatToolResult, formatErrorResult } from "../toolTypes.js";
|
|
5
|
+
import type { StartDebugSessionResponse } from "../../bridge/bridgeTypes.js";
|
|
6
|
+
|
|
7
|
+
export const registerStartDebugSessionTool = (params: { server: McpServer }): void => {
|
|
8
|
+
params.server.tool(
|
|
9
|
+
"start_debug_session",
|
|
10
|
+
"Start a new debug session in VS Code. Supports launching programs or attaching to running processes.",
|
|
11
|
+
{
|
|
12
|
+
type: z.string().describe("Debug adapter type (e.g., 'node', 'python', 'cppdbg')"),
|
|
13
|
+
name: z.string().describe("Human-readable name for the debug session"),
|
|
14
|
+
request: z.enum(["launch", "attach"]).describe("Whether to launch a new process or attach to existing"),
|
|
15
|
+
program: z.string().optional().describe("Path to the program to debug"),
|
|
16
|
+
args: z.array(z.string()).optional().describe("Command line arguments for the program"),
|
|
17
|
+
cwd: z.string().optional().describe("Working directory for the program"),
|
|
18
|
+
additionalConfig: z.record(z.unknown()).optional().describe("Additional debug configuration properties passed to the debug adapter"),
|
|
19
|
+
},
|
|
20
|
+
async (args) => {
|
|
21
|
+
try {
|
|
22
|
+
const result = await sendBridgeCommand<StartDebugSessionResponse>({
|
|
23
|
+
command: "startDebugSession",
|
|
24
|
+
args: {
|
|
25
|
+
type: args.type,
|
|
26
|
+
name: args.name,
|
|
27
|
+
request: args.request,
|
|
28
|
+
program: args.program,
|
|
29
|
+
args: args.args,
|
|
30
|
+
cwd: args.cwd,
|
|
31
|
+
additionalConfig: args.additionalConfig,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
return { content: [{ type: "text" as const, text: formatToolResult({ data: result }) }] };
|
|
35
|
+
} catch (error) {
|
|
36
|
+
return { content: [{ type: "text" as const, text: formatErrorResult({ error }) }], isError: true };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { sendBridgeCommand } from "../../bridge/bridgeClient.js";
|
|
4
|
+
import { formatToolResult, formatErrorResult } from "../toolTypes.js";
|
|
5
|
+
import type { StepResponse } from "../../bridge/bridgeTypes.js";
|
|
6
|
+
|
|
7
|
+
export const registerStepIntoTool = (params: { server: McpServer }): void => {
|
|
8
|
+
params.server.tool(
|
|
9
|
+
"step_into",
|
|
10
|
+
"Step into the function call at the current line in the debugger.",
|
|
11
|
+
{
|
|
12
|
+
sessionId: z.string().optional().describe("Debug session ID. Defaults to the active session."),
|
|
13
|
+
threadId: z.number().optional().describe("Thread ID to step. Defaults to the first available thread."),
|
|
14
|
+
},
|
|
15
|
+
async (args) => {
|
|
16
|
+
try {
|
|
17
|
+
const result = await sendBridgeCommand<StepResponse>({
|
|
18
|
+
command: "stepInto",
|
|
19
|
+
args: {
|
|
20
|
+
sessionId: args.sessionId,
|
|
21
|
+
threadId: args.threadId,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
return { content: [{ type: "text" as const, text: formatToolResult({ data: result }) }] };
|
|
25
|
+
} catch (error) {
|
|
26
|
+
return { content: [{ type: "text" as const, text: formatErrorResult({ error }) }], isError: true };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { sendBridgeCommand } from "../../bridge/bridgeClient.js";
|
|
4
|
+
import { formatToolResult, formatErrorResult } from "../toolTypes.js";
|
|
5
|
+
import type { StepResponse } from "../../bridge/bridgeTypes.js";
|
|
6
|
+
|
|
7
|
+
export const registerStepOutTool = (params: { server: McpServer }): void => {
|
|
8
|
+
params.server.tool(
|
|
9
|
+
"step_out",
|
|
10
|
+
"Step out of the current function, returning to the caller.",
|
|
11
|
+
{
|
|
12
|
+
sessionId: z.string().optional().describe("Debug session ID. Defaults to the active session."),
|
|
13
|
+
threadId: z.number().optional().describe("Thread ID to step. Defaults to the first available thread."),
|
|
14
|
+
},
|
|
15
|
+
async (args) => {
|
|
16
|
+
try {
|
|
17
|
+
const result = await sendBridgeCommand<StepResponse>({
|
|
18
|
+
command: "stepOut",
|
|
19
|
+
args: {
|
|
20
|
+
sessionId: args.sessionId,
|
|
21
|
+
threadId: args.threadId,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
return { content: [{ type: "text" as const, text: formatToolResult({ data: result }) }] };
|
|
25
|
+
} catch (error) {
|
|
26
|
+
return { content: [{ type: "text" as const, text: formatErrorResult({ error }) }], isError: true };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { sendBridgeCommand } from "../../bridge/bridgeClient.js";
|
|
4
|
+
import { formatToolResult, formatErrorResult } from "../toolTypes.js";
|
|
5
|
+
import type { StepResponse } from "../../bridge/bridgeTypes.js";
|
|
6
|
+
|
|
7
|
+
export const registerStepOverTool = (params: { server: McpServer }): void => {
|
|
8
|
+
params.server.tool(
|
|
9
|
+
"step_over",
|
|
10
|
+
"Step over the current line in the debugger, executing it without stepping into function calls.",
|
|
11
|
+
{
|
|
12
|
+
sessionId: z.string().optional().describe("Debug session ID. Defaults to the active session."),
|
|
13
|
+
threadId: z.number().optional().describe("Thread ID to step. Defaults to the first available thread."),
|
|
14
|
+
},
|
|
15
|
+
async (args) => {
|
|
16
|
+
try {
|
|
17
|
+
const result = await sendBridgeCommand<StepResponse>({
|
|
18
|
+
command: "stepOver",
|
|
19
|
+
args: {
|
|
20
|
+
sessionId: args.sessionId,
|
|
21
|
+
threadId: args.threadId,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
return { content: [{ type: "text" as const, text: formatToolResult({ data: result }) }] };
|
|
25
|
+
} catch (error) {
|
|
26
|
+
return { content: [{ type: "text" as const, text: formatErrorResult({ error }) }], isError: true };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { sendBridgeCommand } from "../../bridge/bridgeClient.js";
|
|
4
|
+
import { formatToolResult, formatErrorResult } from "../toolTypes.js";
|
|
5
|
+
import type { StopDebugSessionResponse } from "../../bridge/bridgeTypes.js";
|
|
6
|
+
|
|
7
|
+
export const registerStopDebugSessionTool = (params: { server: McpServer }): void => {
|
|
8
|
+
params.server.tool(
|
|
9
|
+
"stop_debug_session",
|
|
10
|
+
"Stop an active debug session. If no session ID is provided, stops the currently active session.",
|
|
11
|
+
{
|
|
12
|
+
sessionId: z.string().optional().describe("ID of the debug session to stop. Omit for the active session."),
|
|
13
|
+
},
|
|
14
|
+
async (args) => {
|
|
15
|
+
try {
|
|
16
|
+
const result = await sendBridgeCommand<StopDebugSessionResponse>({
|
|
17
|
+
command: "stopDebugSession",
|
|
18
|
+
args: {
|
|
19
|
+
sessionId: args.sessionId,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
return { content: [{ type: "text" as const, text: formatToolResult({ data: result }) }] };
|
|
23
|
+
} catch (error) {
|
|
24
|
+
return { content: [{ type: "text" as const, text: formatErrorResult({ error }) }], isError: true };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
};
|