#
tokens: 16449/50000 7/7 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── examples
│   ├── crash.c
│   └── USAGE.md
├── install.sh
├── package.json
├── README.md
├── src
│   └── index.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # MCP GDB Server
  2 | 
  3 | A Model Context Protocol (MCP) server that provides GDB debugging functionality for use with Claude or other AI assistants.
  4 | 
  5 | ## Features
  6 | 
  7 | - Start and manage GDB debugging sessions
  8 | - Load programs and core dumps for analysis
  9 | - Set breakpoints, step through code, and examine memory
 10 | - View call stacks, variables, and registers
 11 | - Execute arbitrary GDB commands
 12 | 
 13 | ## Installation
 14 | 
 15 | ```bash
 16 | # Clone the repository
 17 | git clone https://github.com/signal-slot/mcp-gdb.git
 18 | cd mcp-gdb
 19 | 
 20 | # Install dependencies
 21 | npm install
 22 | 
 23 | # Build the project
 24 | npm run build
 25 | ```
 26 | 
 27 | ## Usage
 28 | 
 29 | ### Using with Claude or other MCP-enabled assistants
 30 | 
 31 | 1. Configure the MCP settings in the Claude desktop app or browser extension to include this server:
 32 | 
 33 | ```json
 34 | {
 35 |   "mcpServers": {
 36 |     "gdb": {
 37 |       "command": "node",
 38 |       "args": ["/path/to/mcp-gdb/build/index.js"],
 39 |       "disabled": false
 40 |     }
 41 |   }
 42 | }
 43 | ```
 44 | 
 45 | 2. Restart Claude or refresh the page.
 46 | 
 47 | 3. Now you can use the GDB tools in your conversations with Claude.
 48 | 
 49 | ### Example Commands
 50 | 
 51 | Here are some examples of using the GDB MCP server through Claude:
 52 | 
 53 | #### Starting a GDB session
 54 | ```
 55 | Use gdb_start to start a new debugging session
 56 | ```
 57 | 
 58 | #### Loading a program
 59 | ```
 60 | Use gdb_load to load /path/to/my/program with the sessionId that was returned from gdb_start
 61 | ```
 62 | 
 63 | #### Setting a breakpoint
 64 | ```
 65 | Use gdb_set_breakpoint to set a breakpoint at main in the active GDB session
 66 | ```
 67 | 
 68 | #### Running the program
 69 | ```
 70 | Use gdb_continue to start execution
 71 | ```
 72 | 
 73 | #### Examining variables
 74 | ```
 75 | Use gdb_print to evaluate the expression "my_variable" in the current context
 76 | ```
 77 | 
 78 | #### Getting a backtrace
 79 | ```
 80 | Use gdb_backtrace to see the current call stack
 81 | ```
 82 | 
 83 | #### Terminating the session
 84 | ```
 85 | Use gdb_terminate to end the debugging session
 86 | ```
 87 | 
 88 | ## Supported GDB Commands
 89 | 
 90 | - `gdb_start`: Start a new GDB session
 91 | - `gdb_load`: Load a program into GDB
 92 | - `gdb_command`: Execute an arbitrary GDB command
 93 | - `gdb_terminate`: Terminate a GDB session
 94 | - `gdb_list_sessions`: List all active GDB sessions
 95 | - `gdb_attach`: Attach to a running process
 96 | - `gdb_load_core`: Load a core dump file
 97 | - `gdb_set_breakpoint`: Set a breakpoint
 98 | - `gdb_continue`: Continue program execution
 99 | - `gdb_step`: Step program execution
100 | - `gdb_next`: Step over function calls
101 | - `gdb_finish`: Execute until the current function returns
102 | - `gdb_backtrace`: Show call stack
103 | - `gdb_print`: Print value of expression
104 | - `gdb_examine`: Examine memory
105 | - `gdb_info_registers`: Display registers
106 | 
107 | ## License
108 | 
109 | MIT
110 | 
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2020",
 4 |     "module": "NodeNext",
 5 |     "moduleResolution": "NodeNext",
 6 |     "esModuleInterop": true,
 7 |     "strict": true,
 8 |     "skipLibCheck": true,
 9 |     "forceConsistentCasingInFileNames": true,
10 |     "outDir": "build",
11 |     "declaration": true,
12 |     "sourceMap": true,
13 |     "resolveJsonModule": true,
14 |     "noImplicitAny": false,
15 |     "allowSyntheticDefaultImports": true
16 |   },
17 |   "include": ["src/**/*"],
18 |   "exclude": ["node_modules", "build"]
19 | }
20 | 
```

--------------------------------------------------------------------------------
/examples/crash.c:
--------------------------------------------------------------------------------

```cpp
 1 | #include <stdio.h>
 2 | #include <stdlib.h>
 3 | 
 4 | void function_that_crashes() {
 5 |     // Access a null pointer to cause a segmentation fault
 6 |     int *ptr = NULL;
 7 |     *ptr = 42;  // This will cause a crash
 8 | }
 9 | 
10 | void function_with_args(int a, int b) {
11 |     printf("Arguments: a=%d, b=%d\n", a, b);
12 |     if (a > 10) {
13 |         function_that_crashes();
14 |     }
15 | }
16 | 
17 | int main(int argc, char **argv) {
18 |     printf("Starting the program...\n");
19 |     
20 |     // Print command line arguments
21 |     printf("Got %d arguments\n", argc);
22 |     for (int i = 0; i < argc; i++) {
23 |         printf("Argument %d: %s\n", i, argv[i]);
24 |     }
25 |     
26 |     int number = 5;
27 |     
28 |     if (argc > 1) {
29 |         number = atoi(argv[1]);
30 |     }
31 |     
32 |     printf("Working with number: %d\n", number);
33 |     
34 |     function_with_args(number, number * 2);
35 |     
36 |     printf("Program completed successfully\n");
37 |     return 0;
38 | }
39 | 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "mcp-gdb",
 3 |   "version": "0.1.1",
 4 |   "description": "MCP server for GDB debugging functionality",
 5 |   "type": "module",
 6 |   "main": "build/index.js",
 7 |   "scripts": {
 8 |     "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
 9 |     "start": "node build/index.js",
10 |     "dev": "tsc-watch --onSuccess \"node build/index.js\"",
11 |     "lint": "eslint src/**/*.ts",
12 |     "test": "echo \"Error: no test specified\" && exit 1"
13 |   },
14 |   "keywords": [
15 |     "gdb",
16 |     "debug",
17 |     "mcp"
18 |   ],
19 |   "author": "Tasuku Suzuki",
20 |   "license": "MIT",
21 |   "devDependencies": {
22 |     "@types/node": "^20.10.5",
23 |     "@typescript-eslint/eslint-plugin": "^6.15.0",
24 |     "@typescript-eslint/parser": "^6.15.0",
25 |     "eslint": "^8.56.0",
26 |     "tsc-watch": "^6.0.4",
27 |     "typescript": "^5.3.3"
28 |   },
29 |   "dependencies": {
30 |     "@modelcontextprotocol/sdk": "^1.7.0"
31 |   },
32 |   "directories": {
33 |     "example": "examples"
34 |   },
35 |   "repository": {
36 |     "type": "git",
37 |     "url": "git+https://github.com/signal-slot/mcp-gdb.git"
38 |   },
39 |   "types": "./build/index.d.ts",
40 |   "bugs": {
41 |     "url": "https://github.com/signal-slot/mcp-gdb/issues"
42 |   },
43 |   "homepage": "https://github.com/signal-slot/mcp-gdb#readme"
44 | }
45 | 
```

--------------------------------------------------------------------------------
/install.sh:
--------------------------------------------------------------------------------

```bash
 1 | #!/bin/bash
 2 | 
 3 | # MCP GDB Server Installer Script
 4 | 
 5 | set -e
 6 | 
 7 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
 8 | BUILD_DIR="$SCRIPT_DIR/build"
 9 | GDB_PATH=$(which gdb 2>/dev/null || echo "")
10 | 
11 | echo "MCP GDB Server Installer"
12 | echo "========================"
13 | echo
14 | 
15 | # Check if GDB is installed
16 | if [ -z "$GDB_PATH" ]; then
17 |     echo "⚠️ Warning: GDB is not found in your PATH. You need to install GDB before using this MCP server."
18 |     echo "On Ubuntu/Debian: sudo apt-get install gdb"
19 |     echo "On Fedora/RHEL: sudo dnf install gdb"
20 |     echo "On Arch Linux: sudo pacman -S gdb"
21 |     echo "On macOS: brew install gdb"
22 |     echo
23 |     read -p "Do you want to continue with the installation? (y/n) " -n 1 -r
24 |     echo
25 |     if [[ ! $REPLY =~ ^[Yy]$ ]]; then
26 |         exit 1
27 |     fi
28 | fi
29 | 
30 | # Install dependencies
31 | echo "Installing dependencies..."
32 | npm install
33 | 
34 | # Build the project
35 | echo "Building the MCP GDB server..."
36 | npm run build
37 | 
38 | # Get the absolute path to the build directory
39 | ABSOLUTE_PATH=$(cd "$BUILD_DIR" && pwd)
40 | 
41 | # Generate MCP settings configuration snippet
42 | echo
43 | echo "MCP GDB Server has been built successfully!"
44 | echo
45 | echo "To use it with Claude or other MCP-enabled assistants, add the following to your MCP settings configuration:"
46 | echo
47 | echo "{" 
48 | echo "  \"mcpServers\": {"
49 | echo "    \"gdb\": {"
50 | echo "      \"command\": \"node\","
51 | echo "      \"args\": [\"$ABSOLUTE_PATH/index.js\"],"
52 | echo "      \"disabled\": false"
53 | echo "    }"
54 | echo "  }"
55 | echo "}"
56 | echo
57 | echo "For Claude Desktop, this file is typically located at:"
58 | echo "- Linux: ~/.config/Claude/claude_desktop_config.json"
59 | echo "- macOS: ~/Library/Application Support/Claude/claude_desktop_config.json"
60 | echo "- Windows: %APPDATA%\\Claude\\claude_desktop_config.json"
61 | echo
62 | echo "For Claude in VSCode, this file is typically located at:"
63 | echo "- Linux: ~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json"
64 | echo "- macOS: ~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json"
65 | echo "- Windows: %APPDATA%\\Code\\User\\globalStorage\\saoudrizwan.claude-dev\\settings\\cline_mcp_settings.json"
66 | echo
67 | echo "After adding the configuration, restart Claude or reload the extension for the changes to take effect."
68 | echo
69 | echo "To test the MCP GDB server, compile and debug the example program:"
70 | echo "  cd examples"
71 | echo "  gcc -g crash.c -o crash"
72 | echo
73 | echo "Then follow the instructions in examples/USAGE.md to debug the program using Claude."
74 | echo
75 | 
76 | # Create example directory and compile the example file
77 | if [ -d "$SCRIPT_DIR/examples" ]; then
78 |     echo "Would you like to compile the example program now? (y/n) "
79 |     read -n 1 -r
80 |     echo
81 |     if [[ $REPLY =~ ^[Yy]$ ]]; then
82 |         cd "$SCRIPT_DIR/examples"
83 |         echo "Compiling example program..."
84 |         gcc -g crash.c -o crash
85 |         echo "Example program compiled successfully!"
86 |     fi
87 | fi
88 | 
89 | echo "Installation complete!"
90 | 
```

--------------------------------------------------------------------------------
/examples/USAGE.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Using the MCP GDB Server: Example Usage
  2 | 
  3 | This guide demonstrates how to use the MCP GDB server to debug the included `crash.c` example.
  4 | 
  5 | ## Compiling the Example
  6 | 
  7 | First, compile the example with debug symbols:
  8 | 
  9 | ```bash
 10 | cd examples
 11 | gcc -g crash.c -o crash
 12 | ```
 13 | 
 14 | ## Debugging Session Example
 15 | 
 16 | Here's a step-by-step guide showing how to debug the crash.c program using the MCP GDB server through Claude.
 17 | 
 18 | ### 1. Start a GDB Session
 19 | 
 20 | Ask Claude to start a new GDB session:
 21 | 
 22 | ```
 23 | I need to debug a program that's crashing. Can you start a new GDB session for me?
 24 | ```
 25 | 
 26 | Claude will use the `gdb_start` tool:
 27 | 
 28 | ```
 29 | <use_mcp_tool>
 30 | <server_name>gdb</server_name>
 31 | <tool_name>gdb_start</tool_name>
 32 | <arguments>
 33 | {}
 34 | </arguments>
 35 | </use_mcp_tool>
 36 | ```
 37 | 
 38 | Claude will receive a response with a session ID (e.g., "1616161616161").
 39 | 
 40 | ### 2. Load the Program
 41 | 
 42 | Now, ask Claude to load the program:
 43 | 
 44 | ```
 45 | Can you load the crash executable I compiled in the examples directory?
 46 | ```
 47 | 
 48 | Claude will use the `gdb_load` tool:
 49 | 
 50 | ```
 51 | <use_mcp_tool>
 52 | <server_name>gdb</server_name>
 53 | <tool_name>gdb_load</tool_name>
 54 | <arguments>
 55 | {
 56 |   "sessionId": "1616161616161",
 57 |   "program": "/path/to/mcp-gdb/examples/crash"
 58 | }
 59 | </arguments>
 60 | </use_mcp_tool>
 61 | ```
 62 | 
 63 | ### 3. Set Breakpoints
 64 | 
 65 | Set a breakpoint at the main function:
 66 | 
 67 | ```
 68 | Please set a breakpoint at the main function.
 69 | ```
 70 | 
 71 | Claude will use `gdb_set_breakpoint`:
 72 | 
 73 | ```
 74 | <use_mcp_tool>
 75 | <server_name>gdb</server_name>
 76 | <tool_name>gdb_set_breakpoint</tool_name>
 77 | <arguments>
 78 | {
 79 |   "sessionId": "1616161616161",
 80 |   "location": "main"
 81 | }
 82 | </arguments>
 83 | </use_mcp_tool>
 84 | ```
 85 | 
 86 | Let's also set a breakpoint at the function that might crash:
 87 | 
 88 | ```
 89 | Can you also set a breakpoint at function_with_args?
 90 | ```
 91 | 
 92 | Claude will set another breakpoint:
 93 | 
 94 | ```
 95 | <use_mcp_tool>
 96 | <server_name>gdb</server_name>
 97 | <tool_name>gdb_set_breakpoint</tool_name>
 98 | <arguments>
 99 | {
100 |   "sessionId": "1616161616161",
101 |   "location": "function_with_args"
102 | }
103 | </arguments>
104 | </use_mcp_tool>
105 | ```
106 | 
107 | ### 4. Start the Program
108 | 
109 | Start the program with an argument that will cause a crash (any number > 10):
110 | 
111 | ```
112 | Please run the program with an argument of 15, which should trigger the crash.
113 | ```
114 | 
115 | Claude will use `gdb_command` to set the arguments and then run the program:
116 | 
117 | ```
118 | <use_mcp_tool>
119 | <server_name>gdb</server_name>
120 | <tool_name>gdb_command</tool_name>
121 | <arguments>
122 | {
123 |   "sessionId": "1616161616161",
124 |   "command": "set args 15"
125 | }
126 | </arguments>
127 | </use_mcp_tool>
128 | ```
129 | 
130 | Then Claude will start the program:
131 | 
132 | ```
133 | <use_mcp_tool>
134 | <server_name>gdb</server_name>
135 | <tool_name>gdb_continue</tool_name>
136 | <arguments>
137 | {
138 |   "sessionId": "1616161616161"
139 | }
140 | </arguments>
141 | </use_mcp_tool>
142 | ```
143 | 
144 | The program will hit the first breakpoint at `main`.
145 | 
146 | ### 5. Step Through the Program
147 | 
148 | Ask Claude to step through the execution:
149 | 
150 | ```
151 | Please step through the program execution so we can see what's happening.
152 | ```
153 | 
154 | Claude will use `gdb_next` to step through the code:
155 | 
156 | ```
157 | <use_mcp_tool>
158 | <server_name>gdb</server_name>
159 | <tool_name>gdb_next</tool_name>
160 | <arguments>
161 | {
162 |   "sessionId": "1616161616161"
163 | }
164 | </arguments>
165 | </use_mcp_tool>
166 | ```
167 | 
168 | Repeat this command to step through the program.
169 | 
170 | ### 6. Examine Variables
171 | 
172 | At any point, you can ask Claude to print the value of variables:
173 | 
174 | ```
175 | What is the value of the 'number' variable?
176 | ```
177 | 
178 | Claude will use `gdb_print`:
179 | 
180 | ```
181 | <use_mcp_tool>
182 | <server_name>gdb</server_name>
183 | <tool_name>gdb_print</tool_name>
184 | <arguments>
185 | {
186 |   "sessionId": "1616161616161",
187 |   "expression": "number"
188 | }
189 | </arguments>
190 | </use_mcp_tool>
191 | ```
192 | 
193 | ### 7. Continue to Crash
194 | 
195 | Continue execution until the program crashes:
196 | 
197 | ```
198 | Please continue execution until we hit the crash.
199 | ```
200 | 
201 | Claude will use `gdb_continue`:
202 | 
203 | ```
204 | <use_mcp_tool>
205 | <server_name>gdb</server_name>
206 | <tool_name>gdb_continue</tool_name>
207 | <arguments>
208 | {
209 |   "sessionId": "1616161616161"
210 | }
211 | </arguments>
212 | </use_mcp_tool>
213 | ```
214 | 
215 | The program will hit the second breakpoint and then crash when trying to dereference a NULL pointer.
216 | 
217 | ### 8. Analyze the Crash
218 | 
219 | When the program crashes, examine the backtrace to see where it occurred:
220 | 
221 | ```
222 | Can you show me the backtrace of the crash?
223 | ```
224 | 
225 | Claude will use `gdb_backtrace`:
226 | 
227 | ```
228 | <use_mcp_tool>
229 | <server_name>gdb</server_name>
230 | <tool_name>gdb_backtrace</tool_name>
231 | <arguments>
232 | {
233 |   "sessionId": "1616161616161"
234 | }
235 | </arguments>
236 | </use_mcp_tool>
237 | ```
238 | 
239 | The backtrace will show that the crash occurred in `function_that_crashes` when trying to dereference a NULL pointer.
240 | 
241 | ### 9. Terminate the Session
242 | 
243 | When you're done debugging, terminate the GDB session:
244 | 
245 | ```
246 | Please terminate the GDB session.
247 | ```
248 | 
249 | Claude will use `gdb_terminate`:
250 | 
251 | ```
252 | <use_mcp_tool>
253 | <server_name>gdb</server_name>
254 | <tool_name>gdb_terminate</tool_name>
255 | <arguments>
256 | {
257 |   "sessionId": "1616161616161"
258 | }
259 | </arguments>
260 | </use_mcp_tool>
261 | ```
262 | 
263 | ## Using with Core Dumps
264 | 
265 | If the program has already crashed and generated a core dump, you can load it for analysis:
266 | 
267 | ```
268 | <use_mcp_tool>
269 | <server_name>gdb</server_name>
270 | <tool_name>gdb_start</tool_name>
271 | <arguments>
272 | {}
273 | </arguments>
274 | </use_mcp_tool>
275 | ```
276 | 
277 | Then load the program and core dump:
278 | 
279 | ```
280 | <use_mcp_tool>
281 | <server_name>gdb</server_name>
282 | <tool_name>gdb_load_core</tool_name>
283 | <arguments>
284 | {
285 |   "sessionId": "1616161616161",
286 |   "program": "/path/to/mcp-gdb/examples/crash",
287 |   "corePath": "/path/to/core.dump"
288 | }
289 | </arguments>
290 | </use_mcp_tool>
291 | ```
292 | 
293 | Then you can analyze the crash just as if you had caught it in real-time.
294 | 
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
   1 | #!/usr/bin/env node
   2 | import { Server } from '@modelcontextprotocol/sdk/server/index.js';
   3 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
   4 | import {
   5 |   CallToolRequestSchema,
   6 |   ErrorCode,
   7 |   ListToolsRequestSchema,
   8 |   McpError
   9 | } from '@modelcontextprotocol/sdk/types.js';
  10 | import { spawn, ChildProcess } from 'child_process';
  11 | import * as readline from 'readline';
  12 | import * as fs from 'fs';
  13 | import * as path from 'path';
  14 | 
  15 | // Interface for GDB session
  16 | interface GdbSession {
  17 |   process: ChildProcess;
  18 |   rl: readline.Interface;
  19 |   ready: boolean;
  20 |   id: string;
  21 |   target?: string;
  22 |   workingDir?: string;
  23 | }
  24 | 
  25 | // Map to store active GDB sessions
  26 | const activeSessions = new Map<string, GdbSession>();
  27 | 
  28 | class GdbServer {
  29 |   private server: Server;
  30 | 
  31 |   constructor() {
  32 |     this.server = new Server(
  33 |       {
  34 |         name: 'mcp-gdb-server',
  35 |         version: '0.1.0',
  36 |       },
  37 |       {
  38 |         capabilities: {
  39 |           tools: {},
  40 |         },
  41 |       }
  42 |     );
  43 | 
  44 |     this.setupToolHandlers();
  45 |     
  46 |     // Error handling
  47 |     this.server.onerror = (error) => console.error('[MCP Error]', error);
  48 |     process.on('SIGINT', async () => {
  49 |       // Clean up all active GDB sessions
  50 |       for (const [id, session] of activeSessions.entries()) {
  51 |         await this.terminateGdbSession(id);
  52 |       }
  53 |       await this.server.close();
  54 |       process.exit(0);
  55 |     });
  56 |   }
  57 | 
  58 |   private setupToolHandlers() {
  59 |     this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
  60 |       tools: [
  61 |         {
  62 |           name: 'gdb_start',
  63 |           description: 'Start a new GDB session',
  64 |           inputSchema: {
  65 |             type: 'object',
  66 |             properties: {
  67 |               gdbPath: {
  68 |                 type: 'string',
  69 |                 description: 'Path to the GDB executable (optional, defaults to "gdb")'
  70 |               },
  71 |               workingDir: {
  72 |                 type: 'string',
  73 |                 description: 'Working directory for GDB (optional)'
  74 |               }
  75 |             }
  76 |           }
  77 |         },
  78 |         {
  79 |           name: 'gdb_load',
  80 |           description: 'Load a program into GDB',
  81 |           inputSchema: {
  82 |             type: 'object',
  83 |             properties: {
  84 |               sessionId: {
  85 |                 type: 'string',
  86 |                 description: 'GDB session ID'
  87 |               },
  88 |               program: {
  89 |                 type: 'string',
  90 |                 description: 'Path to the program to debug'
  91 |               },
  92 |               arguments: {
  93 |                 type: 'array',
  94 |                 items: {
  95 |                   type: 'string'
  96 |                 },
  97 |                 description: 'Command-line arguments for the program (optional)'
  98 |               }
  99 |             },
 100 |             required: ['sessionId', 'program']
 101 |           }
 102 |         },
 103 |         {
 104 |           name: 'gdb_command',
 105 |           description: 'Execute a GDB command',
 106 |           inputSchema: {
 107 |             type: 'object',
 108 |             properties: {
 109 |               sessionId: {
 110 |                 type: 'string',
 111 |                 description: 'GDB session ID'
 112 |               },
 113 |               command: {
 114 |                 type: 'string',
 115 |                 description: 'GDB command to execute'
 116 |               }
 117 |             },
 118 |             required: ['sessionId', 'command']
 119 |           }
 120 |         },
 121 |         {
 122 |           name: 'gdb_terminate',
 123 |           description: 'Terminate a GDB session',
 124 |           inputSchema: {
 125 |             type: 'object',
 126 |             properties: {
 127 |               sessionId: {
 128 |                 type: 'string',
 129 |                 description: 'GDB session ID'
 130 |               }
 131 |             },
 132 |             required: ['sessionId']
 133 |           }
 134 |         },
 135 |         {
 136 |           name: 'gdb_list_sessions',
 137 |           description: 'List all active GDB sessions',
 138 |           inputSchema: {
 139 |             type: 'object',
 140 |             properties: {}
 141 |           }
 142 |         },
 143 |         {
 144 |           name: 'gdb_attach',
 145 |           description: 'Attach to a running process',
 146 |           inputSchema: {
 147 |             type: 'object',
 148 |             properties: {
 149 |               sessionId: {
 150 |                 type: 'string',
 151 |                 description: 'GDB session ID'
 152 |               },
 153 |               pid: {
 154 |                 type: 'number',
 155 |                 description: 'Process ID to attach to'
 156 |               }
 157 |             },
 158 |             required: ['sessionId', 'pid']
 159 |           }
 160 |         },
 161 |         {
 162 |           name: 'gdb_load_core',
 163 |           description: 'Load a core dump file',
 164 |           inputSchema: {
 165 |             type: 'object',
 166 |             properties: {
 167 |               sessionId: {
 168 |                 type: 'string',
 169 |                 description: 'GDB session ID'
 170 |               },
 171 |               program: {
 172 |                 type: 'string',
 173 |                 description: 'Path to the program executable'
 174 |               },
 175 |               corePath: {
 176 |                 type: 'string',
 177 |                 description: 'Path to the core dump file'
 178 |               }
 179 |             },
 180 |             required: ['sessionId', 'program', 'corePath']
 181 |           }
 182 |         },
 183 |         {
 184 |           name: 'gdb_set_breakpoint',
 185 |           description: 'Set a breakpoint',
 186 |           inputSchema: {
 187 |             type: 'object',
 188 |             properties: {
 189 |               sessionId: {
 190 |                 type: 'string',
 191 |                 description: 'GDB session ID'
 192 |               },
 193 |               location: {
 194 |                 type: 'string',
 195 |                 description: 'Breakpoint location (e.g., function name, file:line)'
 196 |               },
 197 |               condition: {
 198 |                 type: 'string',
 199 |                 description: 'Breakpoint condition (optional)'
 200 |               }
 201 |             },
 202 |             required: ['sessionId', 'location']
 203 |           }
 204 |         },
 205 |         {
 206 |           name: 'gdb_continue',
 207 |           description: 'Continue program execution',
 208 |           inputSchema: {
 209 |             type: 'object',
 210 |             properties: {
 211 |               sessionId: {
 212 |                 type: 'string',
 213 |                 description: 'GDB session ID'
 214 |               }
 215 |             },
 216 |             required: ['sessionId']
 217 |           }
 218 |         },
 219 |         {
 220 |           name: 'gdb_step',
 221 |           description: 'Step program execution',
 222 |           inputSchema: {
 223 |             type: 'object',
 224 |             properties: {
 225 |               sessionId: {
 226 |                 type: 'string',
 227 |                 description: 'GDB session ID'
 228 |               },
 229 |               instructions: {
 230 |                 type: 'boolean',
 231 |                 description: 'Step by instructions instead of source lines (optional)'
 232 |               }
 233 |             },
 234 |             required: ['sessionId']
 235 |           }
 236 |         },
 237 |         {
 238 |           name: 'gdb_next',
 239 |           description: 'Step over function calls',
 240 |           inputSchema: {
 241 |             type: 'object',
 242 |             properties: {
 243 |               sessionId: {
 244 |                 type: 'string',
 245 |                 description: 'GDB session ID'
 246 |               },
 247 |               instructions: {
 248 |                 type: 'boolean',
 249 |                 description: 'Step by instructions instead of source lines (optional)'
 250 |               }
 251 |             },
 252 |             required: ['sessionId']
 253 |           }
 254 |         },
 255 |         {
 256 |           name: 'gdb_finish',
 257 |           description: 'Execute until the current function returns',
 258 |           inputSchema: {
 259 |             type: 'object',
 260 |             properties: {
 261 |               sessionId: {
 262 |                 type: 'string',
 263 |                 description: 'GDB session ID'
 264 |               }
 265 |             },
 266 |             required: ['sessionId']
 267 |           }
 268 |         },
 269 |         {
 270 |           name: 'gdb_backtrace',
 271 |           description: 'Show call stack',
 272 |           inputSchema: {
 273 |             type: 'object',
 274 |             properties: {
 275 |               sessionId: {
 276 |                 type: 'string',
 277 |                 description: 'GDB session ID'
 278 |               },
 279 |               full: {
 280 |                 type: 'boolean',
 281 |                 description: 'Show variables in each frame (optional)'
 282 |               },
 283 |               limit: {
 284 |                 type: 'number',
 285 |                 description: 'Maximum number of frames to show (optional)'
 286 |               }
 287 |             },
 288 |             required: ['sessionId']
 289 |           }
 290 |         },
 291 |         {
 292 |           name: 'gdb_print',
 293 |           description: 'Print value of expression',
 294 |           inputSchema: {
 295 |             type: 'object',
 296 |             properties: {
 297 |               sessionId: {
 298 |                 type: 'string',
 299 |                 description: 'GDB session ID'
 300 |               },
 301 |               expression: {
 302 |                 type: 'string',
 303 |                 description: 'Expression to evaluate'
 304 |               }
 305 |             },
 306 |             required: ['sessionId', 'expression']
 307 |           }
 308 |         },
 309 |         {
 310 |           name: 'gdb_examine',
 311 |           description: 'Examine memory',
 312 |           inputSchema: {
 313 |             type: 'object',
 314 |             properties: {
 315 |               sessionId: {
 316 |                 type: 'string',
 317 |                 description: 'GDB session ID'
 318 |               },
 319 |               expression: {
 320 |                 type: 'string',
 321 |                 description: 'Memory address or expression'
 322 |               },
 323 |               format: {
 324 |                 type: 'string',
 325 |                 description: 'Display format (e.g., "x" for hex, "i" for instruction)'
 326 |               },
 327 |               count: {
 328 |                 type: 'number',
 329 |                 description: 'Number of units to display'
 330 |               }
 331 |             },
 332 |             required: ['sessionId', 'expression']
 333 |           }
 334 |         },
 335 |         {
 336 |           name: 'gdb_info_registers',
 337 |           description: 'Display registers',
 338 |           inputSchema: {
 339 |             type: 'object',
 340 |             properties: {
 341 |               sessionId: {
 342 |                 type: 'string',
 343 |                 description: 'GDB session ID'
 344 |               },
 345 |               register: {
 346 |                 type: 'string',
 347 |                 description: 'Specific register to display (optional)'
 348 |               }
 349 |             },
 350 |             required: ['sessionId']
 351 |           }
 352 |         }
 353 |       ],
 354 |     }));
 355 | 
 356 |     this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
 357 |       // Route the tool call to the appropriate handler based on the tool name
 358 |       switch (request.params.name) {
 359 |         case 'gdb_start':
 360 |           return await this.handleGdbStart(request.params.arguments);
 361 |         case 'gdb_load':
 362 |           return await this.handleGdbLoad(request.params.arguments);
 363 |         case 'gdb_command':
 364 |           return await this.handleGdbCommand(request.params.arguments);
 365 |         case 'gdb_terminate':
 366 |           return await this.handleGdbTerminate(request.params.arguments);
 367 |         case 'gdb_list_sessions':
 368 |           return await this.handleGdbListSessions();
 369 |         case 'gdb_attach':
 370 |           return await this.handleGdbAttach(request.params.arguments);
 371 |         case 'gdb_load_core':
 372 |           return await this.handleGdbLoadCore(request.params.arguments);
 373 |         case 'gdb_set_breakpoint':
 374 |           return await this.handleGdbSetBreakpoint(request.params.arguments);
 375 |         case 'gdb_continue':
 376 |           return await this.handleGdbContinue(request.params.arguments);
 377 |         case 'gdb_step':
 378 |           return await this.handleGdbStep(request.params.arguments);
 379 |         case 'gdb_next':
 380 |           return await this.handleGdbNext(request.params.arguments);
 381 |         case 'gdb_finish':
 382 |           return await this.handleGdbFinish(request.params.arguments);
 383 |         case 'gdb_backtrace':
 384 |           return await this.handleGdbBacktrace(request.params.arguments);
 385 |         case 'gdb_print':
 386 |           return await this.handleGdbPrint(request.params.arguments);
 387 |         case 'gdb_examine':
 388 |           return await this.handleGdbExamine(request.params.arguments);
 389 |         case 'gdb_info_registers':
 390 |           return await this.handleGdbInfoRegisters(request.params.arguments);
 391 |         default:
 392 |           throw new McpError(
 393 |             ErrorCode.MethodNotFound,
 394 |             `Unknown tool: ${request.params.name}`
 395 |           );
 396 |       }
 397 |     });
 398 |   }
 399 | 
 400 |   private async handleGdbStart(args: any) {
 401 |     const gdbPath = args.gdbPath || 'gdb';
 402 |     const workingDir = args.workingDir || process.cwd();
 403 |     
 404 |     // Create a unique session ID
 405 |     const sessionId = Date.now().toString();
 406 |     
 407 |     try {
 408 |       // Start GDB process with MI mode enabled for machine interface
 409 |       const gdbProcess = spawn(gdbPath, ['--interpreter=mi'], {
 410 |         cwd: workingDir,
 411 |         env: process.env,
 412 |         stdio: ['pipe', 'pipe', 'pipe']
 413 |       });
 414 |       
 415 |       // Create readline interface for reading GDB output
 416 |       const rl = readline.createInterface({
 417 |         input: gdbProcess.stdout,
 418 |         terminal: false
 419 |       });
 420 |       
 421 |       // Create new GDB session
 422 |       const session: GdbSession = {
 423 |         process: gdbProcess,
 424 |         rl,
 425 |         ready: false,
 426 |         id: sessionId,
 427 |         workingDir
 428 |       };
 429 |       
 430 |       // Store session in active sessions map
 431 |       activeSessions.set(sessionId, session);
 432 |       
 433 |       // Collect GDB output until ready
 434 |       let outputBuffer = '';
 435 |       
 436 |       // Wait for GDB to be ready (when it outputs the initial prompt)
 437 |       await new Promise<void>((resolve, reject) => {
 438 |         const timeout = setTimeout(() => {
 439 |           reject(new Error('GDB start timeout'));
 440 |         }, 10000); // 10 second timeout
 441 |         
 442 |         rl.on('line', (line) => {
 443 |           // Append line to output buffer
 444 |           outputBuffer += line + '\n';
 445 |           
 446 |           // Check if GDB is ready (outputs prompt)
 447 |           if (line.includes('(gdb)') || line.includes('^done')) {
 448 |             clearTimeout(timeout);
 449 |             session.ready = true;
 450 |             resolve();
 451 |           }
 452 |         });
 453 |         
 454 |         gdbProcess.stderr.on('data', (data) => {
 455 |           outputBuffer += `[stderr] ${data.toString()}\n`;
 456 |         });
 457 |         
 458 |         gdbProcess.on('error', (err) => {
 459 |           clearTimeout(timeout);
 460 |           reject(err);
 461 |         });
 462 |         
 463 |         gdbProcess.on('exit', (code) => {
 464 |           clearTimeout(timeout);
 465 |           if (!session.ready) {
 466 |             reject(new Error(`GDB process exited with code ${code}`));
 467 |           }
 468 |         });
 469 |       });
 470 |       
 471 |       return {
 472 |         content: [
 473 |           {
 474 |             type: 'text',
 475 |             text: `GDB session started with ID: ${sessionId}\n\nOutput:\n${outputBuffer}`
 476 |           }
 477 |         ]
 478 |       };
 479 |     } catch (error) {
 480 |       // Clean up if an error occurs
 481 |       if (activeSessions.has(sessionId)) {
 482 |         const session = activeSessions.get(sessionId)!;
 483 |         session.process.kill();
 484 |         session.rl.close();
 485 |         activeSessions.delete(sessionId);
 486 |       }
 487 |       
 488 |       const errorMessage = error instanceof Error ? error.message : String(error);
 489 |       return {
 490 |         content: [
 491 |           {
 492 |             type: 'text',
 493 |             text: `Failed to start GDB: ${errorMessage}`
 494 |           }
 495 |         ],
 496 |         isError: true
 497 |       };
 498 |     }
 499 |   }
 500 | 
 501 |   private async handleGdbLoad(args: any) {
 502 |     const { sessionId, program, arguments: programArgs = [] } = args;
 503 |     
 504 |     if (!activeSessions.has(sessionId)) {
 505 |       return {
 506 |         content: [
 507 |           {
 508 |             type: 'text',
 509 |             text: `No active GDB session with ID: ${sessionId}`
 510 |           }
 511 |         ],
 512 |         isError: true
 513 |       };
 514 |     }
 515 |     
 516 |     const session = activeSessions.get(sessionId)!;
 517 |     
 518 |     try {
 519 |       // Normalize path if working directory is set
 520 |       const normalizedPath = session.workingDir && !path.isAbsolute(program) 
 521 |         ? path.resolve(session.workingDir, program)
 522 |         : program;
 523 |       
 524 |       // Update session target
 525 |       session.target = normalizedPath;
 526 |       
 527 |       // Execute file command to load program
 528 |       const loadCommand = `file "${normalizedPath}"`;
 529 |       const loadOutput = await this.executeGdbCommand(session, loadCommand);
 530 |       
 531 |       // Set program arguments if provided
 532 |       let argsOutput = '';
 533 |       if (programArgs.length > 0) {
 534 |         const argsCommand = `set args ${programArgs.join(' ')}`;
 535 |         argsOutput = await this.executeGdbCommand(session, argsCommand);
 536 |       }
 537 |       
 538 |       return {
 539 |         content: [
 540 |           {
 541 |             type: 'text',
 542 |             text: `Program loaded: ${normalizedPath}\n\nOutput:\n${loadOutput}${argsOutput ? '\n' + argsOutput : ''}`
 543 |           }
 544 |         ]
 545 |       };
 546 |     } catch (error) {
 547 |       const errorMessage = error instanceof Error ? error.message : String(error);
 548 |       return {
 549 |         content: [
 550 |           {
 551 |             type: 'text',
 552 |             text: `Failed to load program: ${errorMessage}`
 553 |           }
 554 |         ],
 555 |         isError: true
 556 |       };
 557 |     }
 558 |   }
 559 | 
 560 |   private async handleGdbCommand(args: any) {
 561 |     const { sessionId, command } = args;
 562 |     
 563 |     if (!activeSessions.has(sessionId)) {
 564 |       return {
 565 |         content: [
 566 |           {
 567 |             type: 'text',
 568 |             text: `No active GDB session with ID: ${sessionId}`
 569 |           }
 570 |         ],
 571 |         isError: true
 572 |       };
 573 |     }
 574 |     
 575 |     const session = activeSessions.get(sessionId)!;
 576 |     
 577 |     try {
 578 |       const output = await this.executeGdbCommand(session, command);
 579 |       
 580 |       return {
 581 |         content: [
 582 |           {
 583 |             type: 'text',
 584 |             text: `Command: ${command}\n\nOutput:\n${output}`
 585 |           }
 586 |         ]
 587 |       };
 588 |     } catch (error) {
 589 |       const errorMessage = error instanceof Error ? error.message : String(error);
 590 |       return {
 591 |         content: [
 592 |           {
 593 |             type: 'text',
 594 |             text: `Failed to execute command: ${errorMessage}`
 595 |           }
 596 |         ],
 597 |         isError: true
 598 |       };
 599 |     }
 600 |   }
 601 | 
 602 |   private async handleGdbTerminate(args: any) {
 603 |     const { sessionId } = args;
 604 |     
 605 |     if (!activeSessions.has(sessionId)) {
 606 |       return {
 607 |         content: [
 608 |           {
 609 |             type: 'text',
 610 |             text: `No active GDB session with ID: ${sessionId}`
 611 |           }
 612 |         ],
 613 |         isError: true
 614 |       };
 615 |     }
 616 |     
 617 |     try {
 618 |       await this.terminateGdbSession(sessionId);
 619 |       
 620 |       return {
 621 |         content: [
 622 |           {
 623 |             type: 'text',
 624 |             text: `GDB session terminated: ${sessionId}`
 625 |           }
 626 |         ]
 627 |       };
 628 |     } catch (error) {
 629 |       const errorMessage = error instanceof Error ? error.message : String(error);
 630 |       return {
 631 |         content: [
 632 |           {
 633 |             type: 'text',
 634 |             text: `Failed to terminate GDB session: ${errorMessage}`
 635 |           }
 636 |         ],
 637 |         isError: true
 638 |       };
 639 |     }
 640 |   }
 641 | 
 642 |   private async handleGdbListSessions() {
 643 |     const sessions = Array.from(activeSessions.entries()).map(([id, session]) => ({
 644 |       id,
 645 |       target: session.target || 'No program loaded',
 646 |       workingDir: session.workingDir || process.cwd()
 647 |     }));
 648 |     
 649 |     return {
 650 |       content: [
 651 |         {
 652 |           type: 'text',
 653 |           text: `Active GDB Sessions (${sessions.length}):\n\n${JSON.stringify(sessions, null, 2)}`
 654 |         }
 655 |       ]
 656 |     };
 657 |   }
 658 | 
 659 |   private async handleGdbAttach(args: any) {
 660 |     const { sessionId, pid } = args;
 661 |     
 662 |     if (!activeSessions.has(sessionId)) {
 663 |       return {
 664 |         content: [
 665 |           {
 666 |             type: 'text',
 667 |             text: `No active GDB session with ID: ${sessionId}`
 668 |           }
 669 |         ],
 670 |         isError: true
 671 |       };
 672 |     }
 673 |     
 674 |     const session = activeSessions.get(sessionId)!;
 675 |     
 676 |     try {
 677 |       const output = await this.executeGdbCommand(session, `attach ${pid}`);
 678 |       
 679 |       return {
 680 |         content: [
 681 |           {
 682 |             type: 'text',
 683 |             text: `Attached to process ${pid}\n\nOutput:\n${output}`
 684 |           }
 685 |         ]
 686 |       };
 687 |     } catch (error) {
 688 |       const errorMessage = error instanceof Error ? error.message : String(error);
 689 |       return {
 690 |         content: [
 691 |           {
 692 |             type: 'text',
 693 |             text: `Failed to attach to process: ${errorMessage}`
 694 |           }
 695 |         ],
 696 |         isError: true
 697 |       };
 698 |     }
 699 |   }
 700 | 
 701 |   private async handleGdbLoadCore(args: any) {
 702 |     const { sessionId, program, corePath } = args;
 703 |     
 704 |     if (!activeSessions.has(sessionId)) {
 705 |       return {
 706 |         content: [
 707 |           {
 708 |             type: 'text',
 709 |             text: `No active GDB session with ID: ${sessionId}`
 710 |           }
 711 |         ],
 712 |         isError: true
 713 |       };
 714 |     }
 715 |     
 716 |     const session = activeSessions.get(sessionId)!;
 717 |     
 718 |     try {
 719 |       // First load the program
 720 |       const fileOutput = await this.executeGdbCommand(session, `file "${program}"`);
 721 |       
 722 |       // Then load the core file
 723 |       const coreOutput = await this.executeGdbCommand(session, `core-file "${corePath}"`);
 724 |       
 725 |       // Get backtrace to show initial state
 726 |       const backtraceOutput = await this.executeGdbCommand(session, "backtrace");
 727 |       
 728 |       return {
 729 |         content: [
 730 |           {
 731 |             type: 'text',
 732 |             text: `Core file loaded: ${corePath}\n\nOutput:\n${fileOutput}\n${coreOutput}\n\nBacktrace:\n${backtraceOutput}`
 733 |           }
 734 |         ]
 735 |       };
 736 |     } catch (error) {
 737 |       const errorMessage = error instanceof Error ? error.message : String(error);
 738 |       return {
 739 |         content: [
 740 |           {
 741 |             type: 'text',
 742 |             text: `Failed to load core file: ${errorMessage}`
 743 |           }
 744 |         ],
 745 |         isError: true
 746 |       };
 747 |     }
 748 |   }
 749 | 
 750 |   private async handleGdbSetBreakpoint(args: any) {
 751 |     const { sessionId, location, condition } = args;
 752 |     
 753 |     if (!activeSessions.has(sessionId)) {
 754 |       return {
 755 |         content: [
 756 |           {
 757 |             type: 'text',
 758 |             text: `No active GDB session with ID: ${sessionId}`
 759 |           }
 760 |         ],
 761 |         isError: true
 762 |       };
 763 |     }
 764 |     
 765 |     const session = activeSessions.get(sessionId)!;
 766 |     
 767 |     try {
 768 |       // Set breakpoint
 769 |       let command = `break ${location}`;
 770 |       const output = await this.executeGdbCommand(session, command);
 771 |       
 772 |       // Set condition if provided
 773 |       let conditionOutput = '';
 774 |       if (condition) {
 775 |         // Extract breakpoint number from output (assumes format like "Breakpoint 1 at...")
 776 |         const match = output.match(/Breakpoint (\d+)/);
 777 |         if (match && match[1]) {
 778 |           const bpNum = match[1];
 779 |           const conditionCommand = `condition ${bpNum} ${condition}`;
 780 |           conditionOutput = await this.executeGdbCommand(session, conditionCommand);
 781 |         }
 782 |       }
 783 |       
 784 |       return {
 785 |         content: [
 786 |           {
 787 |             type: 'text',
 788 |             text: `Breakpoint set at: ${location}${condition ? ` with condition: ${condition}` : ''}\n\nOutput:\n${output}${conditionOutput ? '\n' + conditionOutput : ''}`
 789 |           }
 790 |         ]
 791 |       };
 792 |     } catch (error) {
 793 |       const errorMessage = error instanceof Error ? error.message : String(error);
 794 |       return {
 795 |         content: [
 796 |           {
 797 |             type: 'text',
 798 |             text: `Failed to set breakpoint: ${errorMessage}`
 799 |           }
 800 |         ],
 801 |         isError: true
 802 |       };
 803 |     }
 804 |   }
 805 | 
 806 |   private async handleGdbContinue(args: any) {
 807 |     const { sessionId } = args;
 808 |     
 809 |     if (!activeSessions.has(sessionId)) {
 810 |       return {
 811 |         content: [
 812 |           {
 813 |             type: 'text',
 814 |             text: `No active GDB session with ID: ${sessionId}`
 815 |           }
 816 |         ],
 817 |         isError: true
 818 |       };
 819 |     }
 820 |     
 821 |     const session = activeSessions.get(sessionId)!;
 822 |     
 823 |     try {
 824 |       const output = await this.executeGdbCommand(session, "continue");
 825 |       
 826 |       return {
 827 |         content: [
 828 |           {
 829 |             type: 'text',
 830 |             text: `Continued execution\n\nOutput:\n${output}`
 831 |           }
 832 |         ]
 833 |       };
 834 |     } catch (error) {
 835 |       const errorMessage = error instanceof Error ? error.message : String(error);
 836 |       return {
 837 |         content: [
 838 |           {
 839 |             type: 'text',
 840 |             text: `Failed to continue execution: ${errorMessage}`
 841 |           }
 842 |         ],
 843 |         isError: true
 844 |       };
 845 |     }
 846 |   }
 847 | 
 848 |   private async handleGdbStep(args: any) {
 849 |     const { sessionId, instructions = false } = args;
 850 |     
 851 |     if (!activeSessions.has(sessionId)) {
 852 |       return {
 853 |         content: [
 854 |           {
 855 |             type: 'text',
 856 |             text: `No active GDB session with ID: ${sessionId}`
 857 |           }
 858 |         ],
 859 |         isError: true
 860 |       };
 861 |     }
 862 |     
 863 |     const session = activeSessions.get(sessionId)!;
 864 |     
 865 |     try {
 866 |       // Use stepi for instruction-level stepping, otherwise step
 867 |       const command = instructions ? "stepi" : "step";
 868 |       const output = await this.executeGdbCommand(session, command);
 869 |       
 870 |       return {
 871 |         content: [
 872 |           {
 873 |             type: 'text',
 874 |             text: `Stepped ${instructions ? 'instruction' : 'line'}\n\nOutput:\n${output}`
 875 |           }
 876 |         ]
 877 |       };
 878 |     } catch (error) {
 879 |       const errorMessage = error instanceof Error ? error.message : String(error);
 880 |       return {
 881 |         content: [
 882 |           {
 883 |             type: 'text',
 884 |             text: `Failed to step: ${errorMessage}`
 885 |           }
 886 |         ],
 887 |         isError: true
 888 |       };
 889 |     }
 890 |   }
 891 | 
 892 |   private async handleGdbNext(args: any) {
 893 |     const { sessionId, instructions = false } = args;
 894 |     
 895 |     if (!activeSessions.has(sessionId)) {
 896 |       return {
 897 |         content: [
 898 |           {
 899 |             type: 'text',
 900 |             text: `No active GDB session with ID: ${sessionId}`
 901 |           }
 902 |         ],
 903 |         isError: true
 904 |       };
 905 |     }
 906 |     
 907 |     const session = activeSessions.get(sessionId)!;
 908 |     
 909 |     try {
 910 |       // Use nexti for instruction-level stepping, otherwise next
 911 |       const command = instructions ? "nexti" : "next";
 912 |       const output = await this.executeGdbCommand(session, command);
 913 |       
 914 |       return {
 915 |         content: [
 916 |           {
 917 |             type: 'text',
 918 |             text: `Stepped over ${instructions ? 'instruction' : 'function call'}\n\nOutput:\n${output}`
 919 |           }
 920 |         ]
 921 |       };
 922 |     } catch (error) {
 923 |       const errorMessage = error instanceof Error ? error.message : String(error);
 924 |       return {
 925 |         content: [
 926 |           {
 927 |             type: 'text',
 928 |             text: `Failed to step over: ${errorMessage}`
 929 |           }
 930 |         ],
 931 |         isError: true
 932 |       };
 933 |     }
 934 |   }
 935 | 
 936 |   private async handleGdbFinish(args: any) {
 937 |     const { sessionId } = args;
 938 |     
 939 |     if (!activeSessions.has(sessionId)) {
 940 |       return {
 941 |         content: [
 942 |           {
 943 |             type: 'text',
 944 |             text: `No active GDB session with ID: ${sessionId}`
 945 |           }
 946 |         ],
 947 |         isError: true
 948 |       };
 949 |     }
 950 |     
 951 |     const session = activeSessions.get(sessionId)!;
 952 |     
 953 |     try {
 954 |       const output = await this.executeGdbCommand(session, "finish");
 955 |       
 956 |       return {
 957 |         content: [
 958 |           {
 959 |             type: 'text',
 960 |             text: `Finished current function\n\nOutput:\n${output}`
 961 |           }
 962 |         ]
 963 |       };
 964 |     } catch (error) {
 965 |       const errorMessage = error instanceof Error ? error.message : String(error);
 966 |       return {
 967 |         content: [
 968 |           {
 969 |             type: 'text',
 970 |             text: `Failed to finish function: ${errorMessage}`
 971 |           }
 972 |         ],
 973 |         isError: true
 974 |       };
 975 |     }
 976 |   }
 977 | 
 978 |   private async handleGdbBacktrace(args: any) {
 979 |     const { sessionId, full = false, limit } = args;
 980 |     
 981 |     if (!activeSessions.has(sessionId)) {
 982 |       return {
 983 |         content: [
 984 |           {
 985 |             type: 'text',
 986 |             text: `No active GDB session with ID: ${sessionId}`
 987 |           }
 988 |         ],
 989 |         isError: true
 990 |       };
 991 |     }
 992 |     
 993 |     const session = activeSessions.get(sessionId)!;
 994 |     
 995 |     try {
 996 |       // Build backtrace command with options
 997 |       let command = full ? "backtrace full" : "backtrace";
 998 |       if (typeof limit === 'number') {
 999 |         command += ` ${limit}`;
1000 |       }
1001 |       
1002 |       const output = await this.executeGdbCommand(session, command);
1003 |       
1004 |       return {
1005 |         content: [
1006 |           {
1007 |             type: 'text',
1008 |             text: `Backtrace${full ? ' (full)' : ''}${limit ? ` (limit: ${limit})` : ''}:\n\n${output}`
1009 |           }
1010 |         ]
1011 |       };
1012 |     } catch (error) {
1013 |       const errorMessage = error instanceof Error ? error.message : String(error);
1014 |       return {
1015 |         content: [
1016 |           {
1017 |             type: 'text',
1018 |             text: `Failed to get backtrace: ${errorMessage}`
1019 |           }
1020 |         ],
1021 |         isError: true
1022 |       };
1023 |     }
1024 |   }
1025 | 
1026 |   private async handleGdbPrint(args: any) {
1027 |     const { sessionId, expression } = args;
1028 |     
1029 |     if (!activeSessions.has(sessionId)) {
1030 |       return {
1031 |         content: [
1032 |           {
1033 |             type: 'text',
1034 |             text: `No active GDB session with ID: ${sessionId}`
1035 |           }
1036 |         ],
1037 |         isError: true
1038 |       };
1039 |     }
1040 |     
1041 |     const session = activeSessions.get(sessionId)!;
1042 |     
1043 |     try {
1044 |       const output = await this.executeGdbCommand(session, `print ${expression}`);
1045 |       
1046 |       return {
1047 |         content: [
1048 |           {
1049 |             type: 'text',
1050 |             text: `Print ${expression}:\n\n${output}`
1051 |           }
1052 |         ]
1053 |       };
1054 |     } catch (error) {
1055 |       const errorMessage = error instanceof Error ? error.message : String(error);
1056 |       return {
1057 |         content: [
1058 |           {
1059 |             type: 'text',
1060 |             text: `Failed to print expression: ${errorMessage}`
1061 |           }
1062 |         ],
1063 |         isError: true
1064 |       };
1065 |     }
1066 |   }
1067 | 
1068 |   private async handleGdbExamine(args: any) {
1069 |     const { sessionId, expression, format = 'x', count = 1 } = args;
1070 |     
1071 |     if (!activeSessions.has(sessionId)) {
1072 |       return {
1073 |         content: [
1074 |           {
1075 |             type: 'text',
1076 |             text: `No active GDB session with ID: ${sessionId}`
1077 |           }
1078 |         ],
1079 |         isError: true
1080 |       };
1081 |     }
1082 |     
1083 |     const session = activeSessions.get(sessionId)!;
1084 |     
1085 |     try {
1086 |       // Format examine command: x/[count][format] [expression]
1087 |       const command = `x/${count}${format} ${expression}`;
1088 |       const output = await this.executeGdbCommand(session, command);
1089 |       
1090 |       return {
1091 |         content: [
1092 |           {
1093 |             type: 'text',
1094 |             text: `Examine ${expression} (format: ${format}, count: ${count}):\n\n${output}`
1095 |           }
1096 |         ]
1097 |       };
1098 |     } catch (error) {
1099 |       const errorMessage = error instanceof Error ? error.message : String(error);
1100 |       return {
1101 |         content: [
1102 |           {
1103 |             type: 'text',
1104 |             text: `Failed to examine memory: ${errorMessage}`
1105 |           }
1106 |         ],
1107 |         isError: true
1108 |       };
1109 |     }
1110 |   }
1111 | 
1112 |   private async handleGdbInfoRegisters(args: any) {
1113 |     const { sessionId, register } = args;
1114 |     
1115 |     if (!activeSessions.has(sessionId)) {
1116 |       return {
1117 |         content: [
1118 |           {
1119 |             type: 'text',
1120 |             text: `No active GDB session with ID: ${sessionId}`
1121 |           }
1122 |         ],
1123 |         isError: true
1124 |       };
1125 |     }
1126 |     
1127 |     const session = activeSessions.get(sessionId)!;
1128 |     
1129 |     try {
1130 |       // Build info registers command, optionally with specific register
1131 |       const command = register ? `info registers ${register}` : `info registers`;
1132 |       const output = await this.executeGdbCommand(session, command);
1133 |       
1134 |       return {
1135 |         content: [
1136 |           {
1137 |             type: 'text',
1138 |             text: `Register info${register ? ` for ${register}` : ''}:\n\n${output}`
1139 |           }
1140 |         ]
1141 |       };
1142 |     } catch (error) {
1143 |       const errorMessage = error instanceof Error ? error.message : String(error);
1144 |       return {
1145 |         content: [
1146 |           {
1147 |             type: 'text',
1148 |             text: `Failed to get register info: ${errorMessage}`
1149 |           }
1150 |         ],
1151 |         isError: true
1152 |       };
1153 |     }
1154 |   }
1155 | 
1156 |   /**
1157 |    * Execute a GDB command and wait for the response
1158 |    */
1159 |   private executeGdbCommand(session: GdbSession, command: string): Promise<string> {
1160 |     return new Promise<string>((resolve, reject) => {
1161 |       if (!session.ready) {
1162 |         reject(new Error('GDB session is not ready'));
1163 |         return;
1164 |       }
1165 |       
1166 |       // Write command to GDB's stdin
1167 |       if (session.process.stdin) {
1168 |         session.process.stdin.write(command + '\n');
1169 |       } else {
1170 |         reject(new Error('GDB stdin is not available'));
1171 |         return;
1172 |       }
1173 |       
1174 |       let output = '';
1175 |       let responseComplete = false;
1176 |       
1177 |       // Create a one-time event handler for GDB output
1178 |       const onLine = (line: string) => {
1179 |         output += line + '\n';
1180 |         
1181 |         // Check if this line indicates the end of the GDB response
1182 |         if (line.includes('(gdb)') || line.includes('^done') || line.includes('^error')) {
1183 |           responseComplete = true;
1184 |           
1185 |           // If we've received the complete response, resolve the promise
1186 |           if (responseComplete) {
1187 |             // Remove the listener to avoid memory leaks
1188 |             session.rl.removeListener('line', onLine);
1189 |             resolve(output);
1190 |           }
1191 |         }
1192 |       };
1193 |       
1194 |       // Add the line handler to the readline interface
1195 |       session.rl.on('line', onLine);
1196 |       
1197 |       // Set a timeout to prevent hanging
1198 |       const timeout = setTimeout(() => {
1199 |         session.rl.removeListener('line', onLine);
1200 |         reject(new Error('GDB command timed out'));
1201 |       }, 10000); // 10 second timeout
1202 |       
1203 |       // Handle GDB errors
1204 |       const errorHandler = (data: Buffer) => {
1205 |         const errorText = data.toString();
1206 |         output += `[stderr] ${errorText}\n`;
1207 |       };
1208 |       
1209 |       // Add error handler
1210 |       if (session.process.stderr) {
1211 |         session.process.stderr.once('data', errorHandler);
1212 |       }
1213 |       
1214 |       // Clean up event handlers when the timeout expires
1215 |       timeout.unref();
1216 |     });
1217 |   }
1218 | 
1219 |   /**
1220 |    * Terminate a GDB session
1221 |    */
1222 |   private async terminateGdbSession(sessionId: string): Promise<void> {
1223 |     if (!activeSessions.has(sessionId)) {
1224 |       throw new Error(`No active GDB session with ID: ${sessionId}`);
1225 |     }
1226 |     
1227 |     const session = activeSessions.get(sessionId)!;
1228 |     
1229 |     // Send quit command to GDB
1230 |     try {
1231 |       await this.executeGdbCommand(session, 'quit');
1232 |     } catch (error) {
1233 |       // Ignore errors from quit command, we'll force kill if needed
1234 |     }
1235 |     
1236 |     // Force kill the process if it's still running
1237 |     if (!session.process.killed) {
1238 |       session.process.kill();
1239 |     }
1240 |     
1241 |     // Close the readline interface
1242 |     session.rl.close();
1243 |     
1244 |     // Remove from active sessions
1245 |     activeSessions.delete(sessionId);
1246 |   }
1247 | 
1248 |   async run() {
1249 |     const transport = new StdioServerTransport();
1250 |     await this.server.connect(transport);
1251 |     console.error('GDB MCP server running on stdio');
1252 |   }
1253 | }
1254 | 
1255 | // Create and run the server
1256 | const server = new GdbServer();
1257 | server.run().catch((error) => {
1258 |   console.error('Failed to start GDB MCP server:', error);
1259 |   process.exit(1);
1260 | });
1261 | 
```