This document provides detailed API documentation for the MCP ACS Debugger Server, including JSDoc-style documentation for all public APIs, CDP protocol interactions, and error codes.
The main server class that implements the MCP protocol and exposes debugging tools.
/**
* MCP ACS Debugger Server
*
* Provides debugging capabilities for Node.js and TypeScript applications
* through the Model Context Protocol.
*
* @class McpDebuggerServer
* @example
* ```typescript
* const server = new McpDebuggerServer();
* await server.start();
* ```
*/
class McpDebuggerServer {
/**
* Creates a new MCP ACS Debugger Server instance
*
* Initializes the MCP server with debugging capabilities and registers
* all available debugging tools.
*
* @constructor
*/
constructor();
/**
* Start the MCP server
*
* Connects the server to stdio transport and begins listening for
* MCP protocol messages.
*
* @async
* @returns {Promise<void>}
* @throws {Error} If the server fails to start
*/
async start(): Promise<void>;
/**
* Stop the MCP server and cleanup all sessions
*
* Terminates all active debug sessions, disconnects from all processes,
* and closes the MCP server connection.
*
* @async
* @returns {Promise<void>}
*/
async stop(): Promise<void>;
}Manages multiple concurrent debug sessions.
/**
* Session Manager
*
* Manages the lifecycle of debug sessions, including creation, retrieval,
* and cleanup. Ensures session isolation and resource management.
*
* @class SessionManager
*/
class SessionManager {
/**
* Create a new debug session
*
* Spawns a Node.js process with the Inspector Protocol attached and
* creates a new DebugSession instance to manage it.
*
* @async
* @param {DebugSessionConfig} config - Session configuration
* @returns {Promise<DebugSession>} The created debug session
* @throws {Error} If the process fails to start or inspector fails to attach
*
* @example
* ```typescript
* const session = await sessionManager.createSession({
* command: 'node',
* args: ['app.js'],
* timeout: 30000
* });
* ```
*/
async createSession(config: DebugSessionConfig): Promise<DebugSession>;
/**
* Get an existing debug session by ID
*
* @param {string} sessionId - The session ID
* @returns {DebugSession | null} The session or null if not found
*/
getSession(sessionId: string): DebugSession | null;
/**
* Remove a debug session and cleanup resources
*
* Terminates the process, disconnects the inspector, and removes
* the session from the manager.
*
* @async
* @param {string} sessionId - The session ID to remove
* @returns {Promise<void>}
*/
async removeSession(sessionId: string): Promise<void>;
/**
* Cleanup all active sessions
*
* Called during server shutdown to ensure all resources are released.
*
* @async
* @returns {Promise<void>}
*/
async cleanupAll(): Promise<void>;
}Represents a single debug session with a Node.js process.
/**
* Debug Session
*
* Manages a single debugging session with a Node.js process, including
* breakpoints, execution control, and variable inspection.
*
* @class DebugSession
*/
class DebugSession {
/**
* Session unique identifier
* @type {string}
*/
readonly id: string;
/**
* Set a breakpoint at a specific location
*
* Uses the Chrome DevTools Protocol to set a breakpoint. If a condition
* is provided, the breakpoint will only pause when the condition evaluates
* to true.
*
* @async
* @param {string} file - Absolute or relative file path
* @param {number} line - Line number (1-indexed)
* @param {string} [condition] - Optional condition expression
* @returns {Promise<Breakpoint>} The created breakpoint
* @throws {Error} If the breakpoint cannot be set
*
* @example
* ```typescript
* const bp = await session.setBreakpoint('/path/to/file.js', 42, 'x > 10');
* ```
*/
async setBreakpoint(
file: string,
line: number,
condition?: string
): Promise<Breakpoint>;
/**
* Remove a breakpoint by ID
*
* @async
* @param {string} breakpointId - The breakpoint ID
* @returns {Promise<boolean>} True if removed, false if not found
*/
async removeBreakpoint(breakpointId: string): Promise<boolean>;
/**
* Toggle a breakpoint's enabled state
*
* @async
* @param {string} breakpointId - The breakpoint ID
* @returns {Promise<Breakpoint | null>} The updated breakpoint or null
*/
async toggleBreakpoint(breakpointId: string): Promise<Breakpoint | null>;
/**
* Get all breakpoints in this session
*
* @returns {Breakpoint[]} Array of all breakpoints
*/
getAllBreakpoints(): Breakpoint[];
/**
* Resume execution until the next breakpoint or program termination
*
* Sends the Debugger.resume CDP command to continue execution.
*
* @async
* @returns {Promise<void>}
* @throws {Error} If the session is not paused
*/
async resume(): Promise<void>;
/**
* Step over the current line
*
* Executes the current line and pauses at the next line in the same scope.
* Uses the Debugger.stepOver CDP command.
*
* @async
* @returns {Promise<void>}
* @throws {Error} If the session is not paused
*/
async stepOver(): Promise<void>;
/**
* Step into the current line
*
* Executes the current line and pauses at the first line inside any
* called function. Uses the Debugger.stepInto CDP command.
*
* @async
* @returns {Promise<void>}
* @throws {Error} If the session is not paused
*/
async stepInto(): Promise<void>;
/**
* Step out of the current function
*
* Executes until the current function returns and pauses at the calling
* location. Uses the Debugger.stepOut CDP command.
*
* @async
* @returns {Promise<void>}
* @throws {Error} If the session is not paused
*/
async stepOut(): Promise<void>;
/**
* Pause execution
*
* Interrupts the running process and pauses at the current execution point.
* Uses the Debugger.pause CDP command.
*
* @async
* @returns {Promise<void>}
* @throws {Error} If the session is already paused
*/
async pause(): Promise<void>;
/**
* Evaluate an expression in the current execution context
*
* Uses Debugger.evaluateOnCallFrame to evaluate the expression in the
* context of the current call frame.
*
* @async
* @param {string} expression - JavaScript expression to evaluate
* @returns {Promise<EvaluationResult>} The evaluation result
* @throws {Error} If evaluation fails or session is not paused
*
* @example
* ```typescript
* const result = await session.evaluateExpression('user.name + " " + user.age');
* console.log(result.value); // "John 30"
* ```
*/
async evaluateExpression(expression: string): Promise<EvaluationResult>;
/**
* Get the current call stack
*
* Returns the complete call stack with function names, file locations
* (absolute paths), and line numbers.
*
* @async
* @returns {Promise<StackFrame[]>} Array of stack frames
* @throws {Error} If the session is not paused
*/
async getCallStack(): Promise<StackFrame[]>;
/**
* Switch to a different stack frame
*
* Changes the context for variable inspection to the specified frame.
* Frame 0 is the top frame (current location).
*
* @param {number} frameIndex - The frame index (0-based)
* @throws {Error} If the frame index is invalid
*/
switchToFrame(frameIndex: number): void;
/**
* Get object properties
*
* Uses Runtime.getProperties CDP command to retrieve all properties
* of an object by its object ID.
*
* @async
* @param {string} objectId - The object ID from a previous inspection
* @returns {Promise<Property[]>} Array of object properties
*/
async getObjectProperties(objectId: string): Promise<Property[]>;
/**
* Inspect an object with nested resolution
*
* Recursively inspects an object up to the specified depth, resolving
* nested objects and arrays.
*
* @async
* @param {string} objectId - The object ID
* @param {number} maxDepth - Maximum depth to traverse (default: 2)
* @returns {Promise<any>} The inspected object data
*/
async inspectObject(objectId: string, maxDepth: number): Promise<any>;
/**
* Add a watched variable
*
* Adds an expression to the watch list. The expression will be evaluated
* each time the process pauses.
*
* @param {WatchedVariable} watch - The watch configuration
*/
addWatchedVariable(watch: WatchedVariable): void;
/**
* Remove a watched variable
*
* @param {string} watchId - The watch ID (expression)
* @returns {boolean} True if removed, false if not found
*/
removeWatchedVariable(watchId: string): boolean;
/**
* Get all watched variables
*
* @returns {WatchedVariable[]} Array of watched variables
*/
getAllWatchedVariables(): WatchedVariable[];
/**
* Get watched variable changes since last pause
*
* @returns {Map<string, VariableChange>} Map of variable changes
*/
getWatchedVariableChanges(): Map<string, VariableChange>;
/**
* Get the current session state
*
* @returns {'paused' | 'running' | 'terminated'} The session state
*/
getState(): 'paused' | 'running' | 'terminated';
/**
* Check if the session is paused
*
* @returns {boolean} True if paused
*/
isPaused(): boolean;
/**
* Get the Node.js process
*
* @returns {ChildProcess | null} The process or null if terminated
*/
getProcess(): ChildProcess | null;
}Detects hanging processes and infinite loops.
/**
* Hang Detector
*
* Monitors process execution to detect hangs and infinite loops using
* timeout-based detection and periodic call stack sampling.
*
* @class HangDetector
*/
class HangDetector {
/**
* Detect if a process hangs or enters an infinite loop
*
* Starts a debug session and monitors execution. If the process doesn't
* complete within the timeout, or if the execution location remains
* unchanged across multiple samples, a hang is detected.
*
* @async
* @param {HangDetectionConfig} config - Detection configuration
* @returns {Promise<HangDetectionResult>} The detection result
*
* @example
* ```typescript
* const result = await hangDetector.detectHang({
* command: 'node',
* args: ['script.js'],
* timeout: 5000,
* sampleInterval: 100
* });
*
* if (result.hung) {
* console.log('Hung at:', result.location);
* console.log('Stack:', result.stack);
* }
* ```
*/
async detectHang(config: HangDetectionConfig): Promise<HangDetectionResult>;
}The MCP ACS Debugger Server uses the Chrome DevTools Protocol (CDP) to communicate with the Node.js Inspector. Below are the key CDP domains and commands used.
The Debugger domain provides debugging functionality.
Enables the debugger for the target.
CDP Command:
{
"method": "Debugger.enable"
}Usage: Called during session initialization to enable debugging capabilities.
Sets a breakpoint at a specific URL and line number.
CDP Command:
{
"method": "Debugger.setBreakpointByUrl",
"params": {
"lineNumber": 41,
"url": "file:///path/to/file.js",
"columnNumber": 0,
"condition": "x > 10"
}
}Response:
{
"breakpointId": "1:41:0:file:///path/to/file.js",
"locations": [
{
"scriptId": "123",
"lineNumber": 41,
"columnNumber": 0
}
]
}Usage:
Called by setBreakpoint() to create breakpoints.
Removes a breakpoint by ID.
CDP Command:
{
"method": "Debugger.removeBreakpoint",
"params": {
"breakpointId": "1:41:0:file:///path/to/file.js"
}
}Usage:
Called by removeBreakpoint() to delete breakpoints.
Resumes execution.
CDP Command:
{
"method": "Debugger.resume"
}Usage:
Called by resume() to continue execution.
Steps over the current line.
CDP Command:
{
"method": "Debugger.stepOver"
}Usage:
Called by stepOver() to execute the current line and pause at the next.
Steps into the current line.
CDP Command:
{
"method": "Debugger.stepInto"
}Usage:
Called by stepInto() to step into function calls.
Steps out of the current function.
CDP Command:
{
"method": "Debugger.stepOut"
}Usage:
Called by stepOut() to return from the current function.
Pauses execution.
CDP Command:
{
"method": "Debugger.pause"
}Usage:
Called by pause() to interrupt running execution.
Evaluates an expression in the context of a call frame.
CDP Command:
{
"method": "Debugger.evaluateOnCallFrame",
"params": {
"callFrameId": "frame-id",
"expression": "user.name",
"returnByValue": true
}
}Response:
{
"result": {
"type": "string",
"value": "John"
}
}Usage:
Called by evaluateExpression() to evaluate expressions.
Fired when execution pauses.
CDP Event:
{
"method": "Debugger.paused",
"params": {
"reason": "breakpoint",
"callFrames": [
{
"callFrameId": "frame-id",
"functionName": "myFunction",
"location": {
"scriptId": "123",
"lineNumber": 41,
"columnNumber": 0
},
"scopeChain": [...]
}
]
}
}Usage: Listened to by the session to update state and call frames.
Fired when execution resumes.
CDP Event:
{
"method": "Debugger.resumed"
}Usage: Listened to by the session to update state.
The Runtime domain provides runtime information and object inspection.
Enables the runtime for the target.
CDP Command:
{
"method": "Runtime.enable"
}Usage: Called during session initialization.
Gets properties of an object.
CDP Command:
{
"method": "Runtime.getProperties",
"params": {
"objectId": "object-id",
"ownProperties": true
}
}Response:
{
"result": [
{
"name": "propertyName",
"value": {
"type": "string",
"value": "propertyValue"
}
}
]
}Usage:
Called by getObjectProperties() and inspectObject().
All error responses follow this structure:
{
"status": "error",
"code": "ERROR_CODE",
"message": "Human-readable error message"
}Description: The specified session ID doesn't exist.
Causes:
- Invalid session ID provided
- Session was already terminated
- Session expired
Resolution:
- Verify the session ID is correct
- Start a new session with
debugger_start
Description: Failed to start the debug session.
Causes:
- Invalid command or arguments
- Process failed to spawn
- Inspector failed to attach
- Timeout waiting for inspector
Resolution:
- Verify the command and arguments are correct
- Check that Node.js is installed and accessible
- Increase the timeout parameter
- Check process stderr for error messages
Description: Failed to set the breakpoint.
Causes:
- Invalid file path
- Line number out of range
- Script not loaded yet
- Invalid condition expression
Resolution:
- Use absolute file paths
- Verify the line number is valid
- Wait for the script to load before setting breakpoints
- Check the condition expression syntax
Description: The specified breakpoint doesn't exist.
Causes:
- Invalid breakpoint ID
- Breakpoint was already removed
Resolution:
- Use
debugger_list_breakpointsto get valid breakpoint IDs - Verify the breakpoint wasn't already removed
Description: Failed to resume execution.
Causes:
- Session is not paused
- Process has terminated
- CDP command failed
Resolution:
- Ensure the session is paused before calling continue
- Check if the process is still running
Description: Failed to step over.
Causes:
- Session is not paused
- Process has terminated
- At the end of execution
Resolution:
- Ensure the session is paused
- Check if there are more lines to execute
Description: Failed to step into.
Causes:
- Session is not paused
- No function call on current line
- Process has terminated
Resolution:
- Ensure the session is paused
- Verify there's a function call to step into
Description: Failed to step out.
Causes:
- Session is not paused
- Already at the top level
- Process has terminated
Resolution:
- Ensure the session is paused
- Verify you're inside a function
Description: Failed to pause execution.
Causes:
- Session is already paused
- Process has terminated
- CDP command failed
Resolution:
- Check the session state before pausing
- Verify the process is still running
Description: Failed to evaluate expression.
Causes:
- Session is not paused
- Invalid expression syntax
- Variable not in scope
- Expression threw an error
Resolution:
- Ensure the session is paused
- Check the expression syntax
- Verify variables are in scope
- Handle expression errors gracefully
Description: Failed to get call stack.
Causes:
- Session is not paused
- No call frames available
- CDP command failed
Resolution:
- Ensure the session is paused
- Verify execution has started
Description: Operation requires the process to be paused.
Causes:
- Trying to inspect variables while running
- Trying to step while running
Resolution:
- Set a breakpoint and continue to it
- Use
debugger_pauseto pause execution
Description: Failed to get local variables.
Causes:
- Session is not paused
- No local scope available
- CDP command failed
Resolution:
- Ensure the session is paused
- Verify you're inside a function
Description: Failed to get global variables.
Causes:
- Session is not paused
- No global scope available
- CDP command failed
Resolution:
- Ensure the session is paused
Description: Failed to inspect object.
Causes:
- Invalid object ID
- Object no longer exists
- CDP command failed
Resolution:
- Verify the object ID is valid
- Re-evaluate the expression to get a fresh object ID
Description: Failed to add watched variable.
Causes:
- Invalid expression
- Internal error
Resolution:
- Check the expression syntax
Description: Failed to remove watched variable.
Causes:
- Internal error
Resolution:
- Retry the operation
Description: The specified watch doesn't exist.
Causes:
- Invalid watch ID
- Watch was already removed
Resolution:
- Use
debugger_get_watchesto get valid watch IDs
Description: Failed to get watched variables.
Causes:
- Internal error
Resolution:
- Retry the operation
Description: Failed to switch stack frame.
Causes:
- Invalid frame index
- Session is not paused
- No call frames available
Resolution:
- Use
debugger_get_stackto get valid frame indices - Ensure the session is paused
Description: Failed to detect hang.
Causes:
- Failed to start process
- Inspector failed to attach
- Internal error
Resolution:
- Verify the command and arguments
- Check process stderr for errors
- Retry with different parameters
Description: Failed to stop the session.
Causes:
- Session already stopped
- Internal error during cleanup
Resolution:
- The session may already be terminated
- Check if resources were released
Description: Failed to remove breakpoint.
Causes:
- CDP command failed
- Breakpoint already removed
Resolution:
- Verify the breakpoint exists
- Retry the operation
Description: Failed to toggle breakpoint.
Causes:
- CDP command failed
- Breakpoint doesn't exist
Resolution:
- Verify the breakpoint exists
- Use
debugger_list_breakpointsto check status
Description: Failed to list breakpoints.
Causes:
- Internal error
Resolution:
- Retry the operation
Configuration for creating a debug session.
interface DebugSessionConfig {
/** The command to execute (e.g., "node", "npm") */
command: string;
/** Command arguments (e.g., ["test.js"]) */
args?: string[];
/** Working directory for the process */
cwd?: string;
/** Timeout in milliseconds (default: 30000) */
timeout?: number;
}Represents a breakpoint in a debug session.
interface Breakpoint {
/** Unique breakpoint identifier */
id: string;
/** Absolute file path */
file: string;
/** Line number (1-indexed) */
line: number;
/** Optional condition expression */
condition?: string;
/** Whether the breakpoint is enabled */
enabled: boolean;
/** CDP breakpoint ID (present if verified) */
cdpBreakpointId?: string;
}Represents a call stack frame.
interface StackFrame {
/** Function name (or "<anonymous>") */
functionName: string;
/** Absolute file path */
file: string;
/** Line number (1-indexed) */
line: number;
/** Column number (0-indexed) */
column: number;
/** CDP call frame ID */
callFrameId?: string;
}Result of evaluating an expression.
interface EvaluationResult {
/** The evaluated value */
value: any;
/** The value type (e.g., "string", "number", "object") */
type: string;
/** Object ID if the value is an object */
objectId?: string;
}Represents an object property.
interface Property {
/** Property name */
name: string;
/** Property value */
value: any;
/** Property type */
type: string;
/** Object ID if the value is an object */
objectId?: string;
}Represents a watched variable.
interface WatchedVariable {
/** Watch identifier (expression) */
name: string;
/** Expression to evaluate */
expression: string;
/** Last evaluated value */
lastValue?: any;
}Represents a change in a watched variable.
interface VariableChange {
/** Old value */
oldValue: any;
/** New value */
newValue: any;
}Configuration for hang detection.
interface HangDetectionConfig {
/** The command to execute */
command: string;
/** Command arguments */
args?: string[];
/** Working directory */
cwd?: string;
/** Timeout in milliseconds */
timeout: number;
/** Sample interval for loop detection (default: 100ms) */
sampleInterval?: number;
}Result of hang detection.
interface HangDetectionResult {
/** Whether the process hung */
hung: boolean;
/** Location where the process hung (if hung) */
location?: string;
/** Call stack at hang location (if hung) */
stack?: StackFrame[];
/** Hang message (if hung) */
message?: string;
/** Whether the process completed normally (if not hung) */
completed?: boolean;
/** Exit code (if completed) */
exitCode?: number;
/** Execution duration in milliseconds */
duration: number;
}Always handle errors gracefully:
try {
const result = await session.evaluateExpression('user.name');
console.log(result.value);
} catch (error) {
if (error.code === 'NOT_PAUSED') {
// Pause the session first
await session.pause();
// Retry
const result = await session.evaluateExpression('user.name');
} else {
console.error('Evaluation failed:', error.message);
}
}Always cleanup sessions when done:
const session = await sessionManager.createSession(config);
try {
// Use the session
await session.setBreakpoint('file.js', 42);
await session.resume();
} finally {
// Always cleanup
await sessionManager.removeSession(session.id);
}Check if breakpoints are verified before relying on them:
const bp = await session.setBreakpoint('file.js', 42);
if (!bp.cdpBreakpointId) {
console.warn('Breakpoint not verified - may not hit');
}Use appropriate timeouts for different scenarios:
// Short timeout for unit tests
const testSession = await sessionManager.createSession({
command: 'node',
args: ['test.js'],
timeout: 10000 // 10 seconds
});
// Longer timeout for integration tests
const integrationSession = await sessionManager.createSession({
command: 'node',
args: ['integration-test.js'],
timeout: 60000 // 60 seconds
});- README.md - User documentation and usage examples
- TESTING.md - Testing documentation
- Chrome DevTools Protocol - CDP specification
- Node.js Inspector API - Node.js debugging API