@ai-capabilities-suite/mcp-debugger-server
Version:
Enterprise-grade MCP server providing advanced debugging capabilities for Node.js and TypeScript applications. Features 25+ debugging tools including breakpoints, variable inspection, execution control, CPU/memory profiling, hang detection, source map sup
1,239 lines (1,238 loc) • 91.1 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.McpDebuggerServer = void 0;
exports.startMcpDebuggerServer = startMcpDebuggerServer;
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
const zod_1 = require("zod");
const mcp_debugger_core_1 = require("@ai-capabilities-suite/mcp-debugger-core");
/**
* MCP Debugger Server
* Provides debugging capabilities for Node.js and TypeScript applications
* through the Model Context Protocol
*/
class McpDebuggerServer {
constructor() {
this.server = new mcp_js_1.McpServer({
name: "debugger-server",
version: "1.1.6",
}, {
capabilities: {
tools: {},
},
});
this.sessionManager = new mcp_debugger_core_1.SessionManager();
this.hangDetector = new mcp_debugger_core_1.HangDetector();
this.shutdownHandler = new mcp_debugger_core_1.GracefulShutdownHandler(30000);
this.registerTools();
this.setupShutdownHandlers();
}
/**
* Setup graceful shutdown handlers
*/
setupShutdownHandlers() {
// Register cleanup for session manager
this.shutdownHandler.registerCleanup("sessions", async () => {
console.log("Cleaning up all debug sessions...");
await this.sessionManager.cleanupAll();
});
// Register cleanup for MCP server
this.shutdownHandler.registerCleanup("mcp-server", async () => {
console.log("Closing MCP server...");
await this.server.close();
});
// Initialize signal handlers
this.shutdownHandler.initialize();
}
/**
* Register all MCP tools
*/
registerTools() {
this.registerDebuggerStart();
this.registerDebuggerSetBreakpoint();
this.registerDebuggerContinue();
this.registerDebuggerStepOver();
this.registerDebuggerStepInto();
this.registerDebuggerStepOut();
this.registerDebuggerPause();
this.registerDebuggerRemoveBreakpoint();
this.registerDebuggerToggleBreakpoint();
this.registerDebuggerListBreakpoints();
this.registerDebuggerGetLocalVariables();
this.registerDebuggerGetGlobalVariables();
this.registerDebuggerInspectObject();
this.registerDebuggerAddWatch();
this.registerDebuggerRemoveWatch();
this.registerDebuggerGetWatches();
this.registerDebuggerSwitchStackFrame();
this.registerDebuggerStopSession();
this.registerDebuggerInspect();
this.registerDebuggerGetStack();
this.registerDebuggerDetectHang();
// Advanced breakpoint types
this.registerDebuggerSetLogpoint();
this.registerDebuggerSetExceptionBreakpoint();
this.registerDebuggerSetFunctionBreakpoint();
this.registerDebuggerSetHitCountCondition();
// Performance profiling tools
this.registerDebuggerStartCPUProfile();
this.registerDebuggerStopCPUProfile();
this.registerDebuggerTakeHeapSnapshot();
this.registerDebuggerGetPerformanceMetrics();
}
/**
* Tool: debugger_start
* Start a new debug session
* Requirements: 2.1, 9.1
*/
registerDebuggerStart() {
this.server.registerTool("debugger_start", {
description: "Start a new debug session with a Node.js process. The process will be paused at the start.",
inputSchema: {
command: zod_1.z
.string()
.describe('The command to execute (e.g., "node", "npm")'),
args: zod_1.z
.array(zod_1.z.string())
.optional()
.describe('Command arguments (e.g., ["test.js"])'),
cwd: zod_1.z
.string()
.optional()
.describe("Working directory for the process"),
timeout: zod_1.z
.number()
.optional()
.describe("Timeout in milliseconds (default: 30000)"),
},
}, async (args) => {
try {
const config = {
command: args.command,
args: args.args,
cwd: args.cwd,
timeout: args.timeout || 30000,
};
const session = await this.sessionManager.createSession(config);
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "success",
sessionId: session.id,
state: session.getState(),
pid: session.getProcess()?.pid,
}, null, 2),
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "SESSION_START_FAILED",
message: error instanceof Error ? error.message : String(error),
}, null, 2),
},
],
isError: true,
};
}
});
}
/**
* Tool: debugger_set_breakpoint
* Set a breakpoint in the debug session
* Requirements: 1.1, 1.2, 9.1
*/
registerDebuggerSetBreakpoint() {
this.server.registerTool("debugger_set_breakpoint", {
description: "Set a breakpoint at a specific file and line number. Optionally provide a condition.",
inputSchema: {
sessionId: zod_1.z.string().describe("The debug session ID"),
file: zod_1.z.string().describe("The file path (absolute or relative)"),
line: zod_1.z.number().describe("The line number (1-indexed)"),
condition: zod_1.z
.string()
.optional()
.describe('Optional condition expression (e.g., "x > 10")'),
},
}, async (args) => {
try {
const session = this.sessionManager.getSession(args.sessionId);
if (!session) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "SESSION_NOT_FOUND",
message: `Session ${args.sessionId} not found`,
}, null, 2),
},
],
isError: true,
};
}
const breakpoint = await session.setBreakpoint(args.file, args.line, args.condition);
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "success",
breakpointId: breakpoint.id,
file: breakpoint.file,
line: breakpoint.line,
condition: breakpoint.condition,
enabled: breakpoint.enabled,
verified: !!breakpoint.cdpBreakpointId,
}, null, 2),
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "BREAKPOINT_SET_FAILED",
message: error instanceof Error ? error.message : String(error),
}, null, 2),
},
],
isError: true,
};
}
});
}
/**
* Tool: debugger_continue
* Resume execution of a paused debug session
* Requirements: 2.2, 9.1
*/
registerDebuggerContinue() {
this.server.registerTool("debugger_continue", {
description: "Resume execution of a paused debug session until the next breakpoint or program termination.",
inputSchema: {
sessionId: zod_1.z.string().describe("The debug session ID"),
},
}, async (args) => {
try {
const session = this.sessionManager.getSession(args.sessionId);
if (!session) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "SESSION_NOT_FOUND",
message: `Session ${args.sessionId} not found`,
}, null, 2),
},
],
isError: true,
};
}
await session.resume();
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "success",
state: session.getState(),
}, null, 2),
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "CONTINUE_FAILED",
message: error instanceof Error ? error.message : String(error),
}, null, 2),
},
],
isError: true,
};
}
});
}
/**
* Tool: debugger_step_over
* Step over the current line
* Requirements: 2.3, 9.1
*/
registerDebuggerStepOver() {
this.server.registerTool("debugger_step_over", {
description: "Execute the current line and pause at the next line in the same scope.",
inputSchema: {
sessionId: zod_1.z.string().describe("The debug session ID"),
},
}, async (args) => {
try {
const session = this.sessionManager.getSession(args.sessionId);
if (!session) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "SESSION_NOT_FOUND",
message: `Session ${args.sessionId} not found`,
}, null, 2),
},
],
isError: true,
};
}
await session.stepOver();
// Wait a bit for the paused event to populate call frames
await new Promise((resolve) => setTimeout(resolve, 100));
const stack = await session.getCallStack();
const location = stack.length > 0
? { file: stack[0].file, line: stack[0].line }
: null;
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "success",
state: session.getState(),
location,
}, null, 2),
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "STEP_OVER_FAILED",
message: error instanceof Error ? error.message : String(error),
}, null, 2),
},
],
isError: true,
};
}
});
}
/**
* Tool: debugger_inspect
* Evaluate an expression in the current execution context
* Requirements: 3.4, 9.1, 9.3
*/
registerDebuggerInspect() {
this.server.registerTool("debugger_inspect", {
description: "Evaluate a JavaScript expression in the current execution context and return the result with type information.",
inputSchema: {
sessionId: zod_1.z.string().describe("The debug session ID"),
expression: zod_1.z
.string()
.describe('The JavaScript expression to evaluate (e.g., "x + 1")'),
},
}, async (args) => {
try {
const session = this.sessionManager.getSession(args.sessionId);
if (!session) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "SESSION_NOT_FOUND",
message: `Session ${args.sessionId} not found`,
}, null, 2),
},
],
isError: true,
};
}
const result = await session.evaluateExpression(args.expression);
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "success",
expression: args.expression,
value: result.value,
type: result.type,
objectId: result.objectId,
}, null, 2),
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "INSPECT_FAILED",
message: error instanceof Error ? error.message : String(error),
}, null, 2),
},
],
isError: true,
};
}
});
}
/**
* Tool: debugger_get_stack
* Get the current call stack
* Requirements: 4.1, 9.1, 9.4
*/
registerDebuggerGetStack() {
this.server.registerTool("debugger_get_stack", {
description: "Get the current call stack with function names, file locations (absolute paths), and line numbers.",
inputSchema: {
sessionId: zod_1.z.string().describe("The debug session ID"),
},
}, async (args) => {
try {
const session = this.sessionManager.getSession(args.sessionId);
if (!session) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "SESSION_NOT_FOUND",
message: `Session ${args.sessionId} not found`,
}, null, 2),
},
],
isError: true,
};
}
const stack = await session.getCallStack();
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "success",
stack: stack.map((frame) => ({
function: frame.functionName,
file: frame.file,
line: frame.line,
column: frame.column,
})),
}, null, 2),
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "GET_STACK_FAILED",
message: error instanceof Error ? error.message : String(error),
}, null, 2),
},
],
isError: true,
};
}
});
}
/**
* Tool: debugger_detect_hang
* Detect if a process hangs or enters an infinite loop
* Requirements: 5.1, 5.2, 5.3, 5.4, 9.1
*/
registerDebuggerDetectHang() {
this.server.registerTool("debugger_detect_hang", {
description: "Run a command and detect if it hangs or enters an infinite loop. Returns hang status, location, and stack trace if hung.",
inputSchema: {
command: zod_1.z
.string()
.describe('The command to execute (e.g., "node", "npm")'),
args: zod_1.z
.array(zod_1.z.string())
.optional()
.describe('Command arguments (e.g., ["test.js"])'),
cwd: zod_1.z
.string()
.optional()
.describe("Working directory for the process"),
timeout: zod_1.z.number().describe("Timeout in milliseconds (e.g., 5000)"),
sampleInterval: zod_1.z
.number()
.optional()
.describe("Sample interval in milliseconds for infinite loop detection (e.g., 100)"),
},
}, async (args) => {
try {
console.log("[MCP Server] debugger_detect_hang called with:", args);
const result = await this.hangDetector.detectHang({
command: args.command,
args: args.args,
cwd: args.cwd,
timeout: args.timeout,
sampleInterval: args.sampleInterval,
});
console.log("[MCP Server] detectHang completed:", result);
if (result.hung) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "success",
hung: true,
location: result.location,
stack: result.stack,
message: result.message,
duration: result.duration,
}, null, 2),
},
],
};
}
else {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "success",
hung: false,
completed: result.completed,
exitCode: result.exitCode,
duration: result.duration,
}, null, 2),
},
],
};
}
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "HANG_DETECTION_FAILED",
message: error instanceof Error ? error.message : String(error),
}, null, 2),
},
],
isError: true,
};
}
});
}
/**
* Tool: debugger_step_into
* Step into the current line
* Requirements: 2.4, 9.1
*/
registerDebuggerStepInto() {
this.server.registerTool("debugger_step_into", {
description: "Execute the current line and pause at the first line inside any called function.",
inputSchema: {
sessionId: zod_1.z.string().describe("The debug session ID"),
},
}, async (args) => {
try {
const session = this.sessionManager.getSession(args.sessionId);
if (!session) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "SESSION_NOT_FOUND",
message: `Session ${args.sessionId} not found`,
}, null, 2),
},
],
isError: true,
};
}
await session.stepInto();
// Wait a bit for the paused event to populate call frames
await new Promise((resolve) => setTimeout(resolve, 100));
const stack = await session.getCallStack();
const location = stack.length > 0
? { file: stack[0].file, line: stack[0].line }
: null;
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "success",
state: session.getState(),
location,
}, null, 2),
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "STEP_INTO_FAILED",
message: error instanceof Error ? error.message : String(error),
}, null, 2),
},
],
isError: true,
};
}
});
}
/**
* Tool: debugger_step_out
* Step out of the current function
* Requirements: 2.5, 9.1
*/
registerDebuggerStepOut() {
this.server.registerTool("debugger_step_out", {
description: "Execute until the current function returns and pause at the calling location.",
inputSchema: {
sessionId: zod_1.z.string().describe("The debug session ID"),
},
}, async (args) => {
try {
const session = this.sessionManager.getSession(args.sessionId);
if (!session) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "SESSION_NOT_FOUND",
message: `Session ${args.sessionId} not found`,
}, null, 2),
},
],
isError: true,
};
}
await session.stepOut();
// Wait a bit for the paused event to populate call frames
await new Promise((resolve) => setTimeout(resolve, 100));
const stack = await session.getCallStack();
const location = stack.length > 0
? { file: stack[0].file, line: stack[0].line }
: null;
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "success",
state: session.getState(),
location,
}, null, 2),
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "STEP_OUT_FAILED",
message: error instanceof Error ? error.message : String(error),
}, null, 2),
},
],
isError: true,
};
}
});
}
/**
* Tool: debugger_pause
* Pause running execution
* Requirements: 2.6, 9.1
*/
registerDebuggerPause() {
this.server.registerTool("debugger_pause", {
description: "Pause a running debug session and return the current execution location.",
inputSchema: {
sessionId: zod_1.z.string().describe("The debug session ID"),
},
}, async (args) => {
try {
const session = this.sessionManager.getSession(args.sessionId);
if (!session) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "SESSION_NOT_FOUND",
message: `Session ${args.sessionId} not found`,
}, null, 2),
},
],
isError: true,
};
}
await session.pause();
const stack = await session.getCallStack();
const location = stack.length > 0
? { file: stack[0].file, line: stack[0].line }
: null;
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "success",
state: session.getState(),
location,
}, null, 2),
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "PAUSE_FAILED",
message: error instanceof Error ? error.message : String(error),
}, null, 2),
},
],
isError: true,
};
}
});
}
/**
* Tool: debugger_remove_breakpoint
* Remove a breakpoint from the session
* Requirements: 1.4, 9.1
*/
registerDebuggerRemoveBreakpoint() {
this.server.registerTool("debugger_remove_breakpoint", {
description: "Remove a breakpoint from the debug session.",
inputSchema: {
sessionId: zod_1.z.string().describe("The debug session ID"),
breakpointId: zod_1.z.string().describe("The breakpoint ID to remove"),
},
}, async (args) => {
try {
const session = this.sessionManager.getSession(args.sessionId);
if (!session) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "SESSION_NOT_FOUND",
message: `Session ${args.sessionId} not found`,
}, null, 2),
},
],
isError: true,
};
}
const removed = await session.removeBreakpoint(args.breakpointId);
if (!removed) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "BREAKPOINT_NOT_FOUND",
message: `Breakpoint ${args.breakpointId} not found`,
}, null, 2),
},
],
isError: true,
};
}
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "success",
breakpointId: args.breakpointId,
removed: true,
}, null, 2),
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "REMOVE_BREAKPOINT_FAILED",
message: error instanceof Error ? error.message : String(error),
}, null, 2),
},
],
isError: true,
};
}
});
}
/**
* Tool: debugger_toggle_breakpoint
* Toggle a breakpoint's enabled state
* Requirements: 1.5, 9.1
*/
registerDebuggerToggleBreakpoint() {
this.server.registerTool("debugger_toggle_breakpoint", {
description: "Toggle a breakpoint between enabled and disabled states.",
inputSchema: {
sessionId: zod_1.z.string().describe("The debug session ID"),
breakpointId: zod_1.z.string().describe("The breakpoint ID to toggle"),
},
}, async (args) => {
try {
const session = this.sessionManager.getSession(args.sessionId);
if (!session) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "SESSION_NOT_FOUND",
message: `Session ${args.sessionId} not found`,
}, null, 2),
},
],
isError: true,
};
}
const breakpoint = await session.toggleBreakpoint(args.breakpointId);
if (!breakpoint) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "BREAKPOINT_NOT_FOUND",
message: `Breakpoint ${args.breakpointId} not found`,
}, null, 2),
},
],
isError: true,
};
}
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "success",
breakpointId: breakpoint.id,
file: breakpoint.file,
line: breakpoint.line,
condition: breakpoint.condition,
enabled: breakpoint.enabled,
}, null, 2),
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "TOGGLE_BREAKPOINT_FAILED",
message: error instanceof Error ? error.message : String(error),
}, null, 2),
},
],
isError: true,
};
}
});
}
/**
* Tool: debugger_list_breakpoints
* List all breakpoints in the session
* Requirements: 1.3, 9.1
*/
registerDebuggerListBreakpoints() {
this.server.registerTool("debugger_list_breakpoints", {
description: "Get all breakpoints for a debug session with their file, line, condition, and enabled state.",
inputSchema: {
sessionId: zod_1.z.string().describe("The debug session ID"),
},
}, async (args) => {
try {
const session = this.sessionManager.getSession(args.sessionId);
if (!session) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "SESSION_NOT_FOUND",
message: `Session ${args.sessionId} not found`,
}, null, 2),
},
],
isError: true,
};
}
const breakpoints = session.getAllBreakpoints();
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "success",
breakpoints: breakpoints.map((bp) => ({
id: bp.id,
file: bp.file,
line: bp.line,
condition: bp.condition,
enabled: bp.enabled,
verified: !!bp.cdpBreakpointId,
})),
}, null, 2),
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "LIST_BREAKPOINTS_FAILED",
message: error instanceof Error ? error.message : String(error),
}, null, 2),
},
],
isError: true,
};
}
});
}
/**
* Tool: debugger_get_local_variables
* Get all local variables in the current scope
* Requirements: 3.1, 9.1, 9.3
*/
registerDebuggerGetLocalVariables() {
this.server.registerTool("debugger_get_local_variables", {
description: "Get all local variables in the current scope with their names, values, and types.",
inputSchema: {
sessionId: zod_1.z.string().describe("The debug session ID"),
},
}, async (args) => {
try {
const session = this.sessionManager.getSession(args.sessionId);
if (!session) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "SESSION_NOT_FOUND",
message: `Session ${args.sessionId} not found`,
}, null, 2),
},
],
isError: true,
};
}
if (!session.isPaused()) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "NOT_PAUSED",
message: "Process must be paused to get local variables",
}, null, 2),
},
],
isError: true,
};
}
const callFrames = session.getCurrentCallFrames();
if (!callFrames || callFrames.length === 0) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "success",
variables: [],
}, null, 2),
},
],
};
}
const currentFrame = callFrames[session.getCurrentFrameIndex()];
const scopeChain = currentFrame.scopeChain || [];
// Get local scope (first scope in chain is usually local)
const localScope = scopeChain.find((scope) => scope.type === "local");
if (!localScope || !localScope.object?.objectId) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "success",
variables: [],
}, null, 2),
},
],
};
}
const properties = await session.getObjectProperties(localScope.object.objectId);
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "success",
variables: properties.map((prop) => ({
name: prop.name,
value: prop.value,
type: prop.type,
})),
}, null, 2),
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "GET_LOCAL_VARIABLES_FAILED",
message: error instanceof Error ? error.message : String(error),
}, null, 2),
},
],
isError: true,
};
}
});
}
/**
* Tool: debugger_get_global_variables
* Get global variables accessible from the current scope
* Requirements: 3.2, 9.1, 9.3
*/
registerDebuggerGetGlobalVariables() {
this.server.registerTool("debugger_get_global_variables", {
description: "Get global variables accessible from the current scope with their names, values, and types.",
inputSchema: {
sessionId: zod_1.z.string().describe("The debug session ID"),
},
}, async (args) => {
try {
const session = this.sessionManager.getSession(args.sessionId);
if (!session) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "SESSION_NOT_FOUND",
message: `Session ${args.sessionId} not found`,
}, null, 2),
},
],
isError: true,
};
}
if (!session.isPaused()) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "NOT_PAUSED",
message: "Process must be paused to get global variables",
}, null, 2),
},
],
isError: true,
};
}
const callFrames = session.getCurrentCallFrames();
if (!callFrames || callFrames.length === 0) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "success",
variables: [],
}, null, 2),
},
],
};
}
const currentFrame = callFrames[session.getCurrentFrameIndex()];
const scopeChain = currentFrame.scopeChain || [];
// Get global scope (last scope in chain is usually global)
const globalScope = scopeChain.find((scope) => scope.type === "global");
if (!globalScope || !globalScope.object?.objectId) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "success",
variables: [],
}, null, 2),
},
],
};
}
const properties = await session.getObjectProperties(globalScope.object.objectId);
// Filter out built-in globals to reduce noise
const userGlobals = properties.filter((prop) => !["console", "process", "Buffer", "global", "require"].includes(prop.name));
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "success",
variables: userGlobals.map((prop) => ({
name: prop.name,
value: prop.value,
type: prop.type,
})),
}, null, 2),
},
],
};
}
catch (error) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "GET_GLOBAL_VARIABLES_FAILED",
message: error instanceof Error ? error.message : String(error),
}, null, 2),
},
],
isError: true,
};
}
});
}
/**
* Tool: debugger_inspect_object
* Inspect an object's properties with nested resolution
* Requirements: 3.3, 9.1, 9.3
*/
registerDebuggerInspectObject() {
this.server.registerTool("debugger_inspect_object", {
description: "Inspect an object by its object reference, returning properties with values. Handles nested objects and arrays up to a specified depth.",
inputSchema: {
sessionId: zod_1.z.string().describe("The debug session ID"),
objectId: zod_1.z
.string()
.describe("The object ID (from a previous inspection or evaluation)"),
maxDepth: zod_1.z
.number()
.optional()
.describe("Maximum depth to traverse (default: 2)"),
},
}, async (args) => {
try {
const session = this.sessionManager.getSession(args.sessionId);
if (!session) {
return {
content: [
{
type: "text",
text: JSON.stringify({
status: "error",
code: "SESSION_NOT_FOUND",
message: `Session ${args.sessionId} not found`,
}, null, 2),
},
],
isError: true,
};
}
if (!session.isPaused()) {
return {
content: [
{
type: "text",
text: JSON.stringify({