How to Implement Global Error Handling in the Instagit MCP API
You can centralize all API errors by assigning a handler to transport.onerror in StreamableHttpServerTransport, which captures network failures, server validation errors, and downstream API exceptions in a single callback.
The castrozan/tcc repository implements a layered error-handling strategy for its Instagit MCP server that ensures no failure goes unobserved. By understanding how errors propagate from the HTTP transport through the MCP server to the downstream API client, you can implement robust global error handling that centralizes logging, monitoring, and recovery logic.
Understanding the Layered Error Handling Architecture
The Instagit MCP server divides error handling into four distinct layers, each responsible for capturing specific failure modes before bubbling them to a single global handler.
Transport Layer (HTTP ↔ MCP)
The StreamableHttpServerTransport class in src/transport/StreamableHttpServerTransport.ts manages the HTTP server lifecycle and JSON-RPC message parsing. It captures network-level errors including socket failures, write errors to streaming responses, and malformed incoming messages. The transport exposes an onerror callback (defined at line 61) that receives all transport-level exceptions.
Server Request Handling Layer
The MCP Server instance created in src/server.ts validates incoming tool calls and session state. When validation fails—such as when a tool ID is missing—the server throws standard Error objects (lines 55-58). These errors bubble up to the transport layer, which then forwards them to the onerror handler if one is registered.
API Client Layer
The ApiClient class in src/api-client.ts handles downstream HTTP requests to the backend API. It catches AxiosError exceptions and re-throws normalized Error objects with concise messages (lines 44-48). This normalization ensures that upstream layers remain agnostic of the HTTP client implementation while preserving essential error context like status codes and response bodies.
Implementing the Global Error Handler
To establish global error handling across the entire API, assign a function to the onerror property of your transport instance immediately after creation:
import { StreamableHttpServerTransport } from "./src/transport/StreamableHttpServerTransport.js";
import { OpenAPIServer } from "./src/server.js";
const transport = new StreamableHttpServerTransport(8080);
// Global error handling entry point
transport.onerror = (err: Error) => {
console.error("[GLOBAL ERROR]", err);
// Integrate with external monitoring (Sentry, Datadog, etc.)
// monitoringClient.captureException(err);
// Implement custom recovery or alerting logic
if (err.message.includes("ECONNREFUSED")) {
alertOpsTeam("Backend API unreachable");
}
};
const server = new OpenAPIServer({
name: "instagit-mcp",
version: "1.0.0",
apiBaseUrl: "https://api.example.com"
});
await server.start(transport);
This single callback receives every error from network failures, validation exceptions, and downstream API timeouts, creating a unified observability surface.
How Errors Propagate Through the System
Understanding the propagation path helps diagnose where to add specific handling logic.
Transport-Level Network Errors
When the HTTP server fails to start or encounters socket errors during operation, the transport invokes onerror directly (lines 87-88). Similarly, write failures to the streaming HTTP response—common when clients disconnect unexpectedly—are caught and forwarded (lines 232-233).
Server Validation Errors
In src/server.ts, the tool execution handler validates that an idOrName parameter exists:
if (!idOrName) {
throw new Error("Tool ID or name is required");
}
This error propagates up the call stack to the transport layer. Since the transport wraps request handling in try-catch blocks, it captures the exception and passes it to your onerror handler.
Downstream API Failures
When ApiClient.executeApiCall encounters an HTTP error from the backend, it normalizes the exception:
catch (error) {
if (axios.isAxiosError(error)) {
throw new Error(
`API request failed: ${error.message}` +
(error.response ? ` (${error.response.status}: ${JSON.stringify(error.response.data)})` : "")
);
}
throw error;
}
The resulting Error bubbles through the server layer to the transport, ultimately reaching your global onerror callback with a clean, implementation-agnostic message.
Key Files for Error Handling
| File | Purpose | Direct Link |
|---|---|---|
src/transport/StreamableHttpServerTransport.ts |
Implements HTTP transport, defines onerror callback, forwards parsing/write/network errors |
View on GitHub |
src/server.ts |
MCP server setup, validates tool IDs, throws errors that propagate to transport | View on GitHub |
src/api-client.ts |
Executes downstream API calls, catches AxiosError, re-throws normalized Error |
View on GitHub |
Summary
- Assign a handler to
transport.onerrorto create a single global error handling entry point for the entire Instagit MCP API. - The transport layer captures network failures, JSON-RPC parsing errors, and streaming write errors (lines 61, 87-88, 232-233, 505-506).
- Server validation errors in
src/server.ts(lines 55-58) bubble up to the transport automatically. - API client errors are normalized in
src/api-client.ts(lines 44-48) to provide clean, implementation-agnostic error messages upstream. - Never swallow errors—always either log and handle or re-throw so the global handler receives them.
Frequently Asked Questions
How do I capture network errors when the HTTP server fails to start?
Network errors during server startup, such as port conflicts or permission issues, are captured by the StreamableHttpServerTransport and delivered to your onerror callback (lines 87-88). Ensure you assign the onerror handler immediately after instantiating the transport, before calling server.start(transport).
What happens to validation errors thrown by the MCP server?
When the server detects invalid requests—such as missing tool IDs in src/server.ts (lines 55-58)—it throws standard JavaScript Error objects. These exceptions propagate through the transport layer's request handling logic and are automatically forwarded to your global onerror handler, allowing you to log or alert on validation failures consistently.
How are downstream API errors normalized before reaching the global handler?
The ApiClient class in src/api-client.ts wraps all HTTP requests in try-catch blocks (lines 44-48). When it catches an AxiosError, it extracts the status code and response body, then re-throws a plain Error with a descriptive message. This normalization ensures your global error handler receives clean, implementation-agnostic errors without requiring Axios-specific logic in your monitoring code.
Can I implement different error handling strategies for different error types?
Yes. Since all errors flow through the single transport.onerror callback, you can implement conditional logic based on error properties. Inspect err.message for specific patterns (such as "ECONNREFUSED" for connection failures or "Tool ID is required" for validation errors) and route them to appropriate handlers—whether that's retry logic, alerting specific teams, or returning different status codes to clients.
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 →