Error Handling Strategy for MCP Tool Execution in Chrome DevTools MCP
The Chrome DevTools MCP server implements a centralized try/catch wrapper in src/main.ts that captures all unhandled exceptions during tool execution, logs them via the internal logger, and returns structured error responses with isError: true to the MCP client.
The ChromeDevTools/chrome-devtools-mcp repository provides a Model Context Protocol (MCP) server that exposes Chrome DevTools functionality to AI assistants. Understanding the error handling strategy for MCP tool execution is critical for developers building robust integrations, as it determines how failures are communicated back to the client and logged for debugging.
Centralized Error Handling Architecture
The server centralizes all error handling logic in src/main.ts within the tool registration phase. When server.registerTool is called, the registration code creates an async wrapper function that executes the tool's handler inside a protective try/catch block.
This wrapper performs several operations before and after handler execution:
- Acquires a mutual-exclusion lock (
toolMutex) to prevent concurrent tool execution - Logs the incoming request via the internal logging system
- Resolves a fresh
McpContextfor the invocation - Calls the tool's
handlerwith the request parameters - Formats the output via
McpResponse.handle
How Errors Are Captured and Processed
Any exception that bubbles out of the tool handler is caught by the centralized wrapper in src/main.ts (lines 92-124). The error handling logic follows a specific sequence to ensure proper logging and client communication:
try {
// … tool invocation …
} catch (err) {
logger(`${tool.name} error:`, err, err?.stack);
const errorText = err && 'message' in err ? err.message : String(err);
if ('cause' in err && err.cause) {
errorText += `\nCause: ${err.cause.message}`;
}
return {
content: [{type: 'text', text: errorText}],
isError: true,
};
} finally {
void clearcutLogger?.logToolInvocation({
toolName: tool.name,
success,
latencyMs: bucketizeLatency(Date.now() - startTime),
});
guard.dispose();
}
The error processing strategy includes:
- Logging: The error and its stack trace are immediately logged using the
loggerutility fromsrc/logger.ts - Message extraction: The handler extracts the error message, checking for nested
causeproperties to capture wrapped exceptions - Structured response: The error is converted into a valid MCP response object with
isError: trueand the error text in the content array - Telemetry: Regardless of success or failure, the
finallyblock reports the invocation outcome to the Clearcut telemetry logger
Tool-Level Error Handling Patterns
While the centralized wrapper catches all unhandled exceptions, individual tools can implement their own error handling for anticipated failure modes. This pattern allows tools to provide richer error messages or perform cleanup before the global handler takes over.
For example, in src/tools/pages.ts, the close_page tool implements specific handling for the CLOSE_PAGE_ERROR:
handler: async (request, response, context) => {
try {
await context.closePage(request.params.pageId);
} catch (err) {
// Known safe error – show message, otherwise re‑throw
if (err.message === CLOSE_PAGE_ERROR) {
response.appendResponseLine(err.message);
} else {
throw err; // falls back to the global error handler
}
}
response.setIncludePages(true);
},
This approach allows the tool to handle expected errors gracefully while ensuring unexpected exceptions still trigger the centralized error handling strategy.
Error Response Structure
When a tool execution fails, the MCP client receives a standardized error response rather than a connection drop or raw exception. The response structure follows the MCP protocol specification:
{
"content": [
{
"type": "text",
"text": "Failed to navigate: TimeoutError: Navigation timeout of 30000 ms exceeded"
}
],
"isError": true
}
Key characteristics of the error response:
- The
isErrorboolean flag is set totrue, signaling to the client that the tool execution failed - Error content is delivered as a text content block within the
contentarray - Nested error causes are appended to the message text when available
- The response maintains valid MCP protocol structure even during failure conditions
Summary
- The Chrome DevTools MCP server implements a centralized try/catch wrapper in
src/main.tsthat surrounds every tool execution - Errors are logged with full stack traces via the internal logger before being converted to client responses
- The server returns structured error responses with
isError: trueand descriptive text content, preventing connection drops - Individual tools can implement localized error handling for anticipated failures while delegating unexpected errors to the global handler
- Telemetry logging occurs in the
finallyblock regardless of execution success or failure
Frequently Asked Questions
How does the Chrome DevTools MCP server prevent tool crashes from breaking the client connection?
The server wraps every tool handler in a centralized try/catch block located in src/main.ts. When an exception occurs, the catch block converts the error into a valid MCP response object with isError: true rather than allowing the exception to propagate and terminate the connection.
Can individual tools customize their error messages before the global handler processes them?
Yes, tools can implement their own try/catch logic within their handlers to catch anticipated errors and append custom messages to the response. If a tool re-throws the error or encounters an unexpected exception, the centralized wrapper in src/main.ts catches it and applies the standard error formatting and logging.
What information is included in the error response sent back to the MCP client?
The error response includes a content array containing a text block with the error message. If the error object contains a nested cause property, that cause message is appended to the primary error text. The response also includes the boolean flag isError: true to signal the failure state to the client.
Where are tool execution errors logged for debugging purposes?
Errors are logged using the internal logger utility defined in src/logger.ts. When the centralized catch block captures an exception, it immediately calls logger() with the tool name, error object, and stack trace before formatting the response for the client.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →