This document provides comprehensive documentation of error handling features and patterns in the Model Context Protocol (MCP) TypeScript SDK. The SDK implements a robust error handling system that covers protocol-level errors, OAuth authentication errors, transport errors, and application-level exceptions.
Table of Contents
- Core Error Classes
- JSON-RPC Error Codes
- OAuth Error Handling
- Protocol Error Patterns
- Transport Error Handling
- Client Error Handling
- Server Error Handling
- Request Lifecycle Error Management
- Timeout and Cancellation
- Error Propagation and Recovery
- Best Practices
- Examples
Core Error Classes
McpError
The McpError
class is the primary error type for MCP protocol-level errors. It extends the standard JavaScript Error
class with MCP-specific information.
Location: src/types.ts:1461-1470
export class McpError extends Error {
constructor(
public readonly code: number,
message: string,
public readonly data?: unknown,
) {
super(`MCP error ${code}: ${message}`);
this.name = "McpError";
}
}
Properties:
-
code
: Numeric error code (fromErrorCode
enum) -
message
: Human-readable error description -
data
: Optional additional error information -
name
: Always set to "McpError"
Usage:
import { McpError, ErrorCode } from "@modelcontextprotocol/sdk";
throw new McpError(
ErrorCode.InvalidRequest,
"Missing required parameter",
{ parameter: "name" }
);
OAuthError Hierarchy
A comprehensive set of OAuth-specific error classes that implement RFC 6749 OAuth 2.0 error responses.
Location: src/server/auth/errors.ts
Base Class: OAuthError
export class OAuthError extends Error {
static errorCode: string;
constructor(
message: string,
public readonly errorUri?: string
) {
super(message);
this.name = this.constructor.name;
}
toResponseObject(): OAuthErrorResponse {
const response: OAuthErrorResponse = {
error: this.errorCode,
error_description: this.message
};
if (this.errorUri) {
response.error_uri = this.errorUri;
}
return response;
}
get errorCode(): string {
return (this.constructor as typeof OAuthError).errorCode
}
}
Standard OAuth Error Types
-
InvalidRequestError (
invalid_request
)- Malformed request, missing parameters, or invalid parameter values
-
InvalidClientError (
invalid_client
)- Client authentication failed
-
InvalidGrantError (
invalid_grant
)- Authorization grant is invalid, expired, or revoked
-
UnauthorizedClientError (
unauthorized_client
)- Client not authorized for this grant type
-
UnsupportedGrantTypeError (
unsupported_grant_type
)- Grant type not supported by authorization server
-
InvalidScopeError (
invalid_scope
)- Requested scope is invalid or exceeds granted scope
-
AccessDeniedError (
access_denied
)- Resource owner or authorization server denied the request
-
ServerError (
server_error
)- Unexpected server condition
-
TemporarilyUnavailableError (
temporarily_unavailable
)- Server temporarily overloaded or under maintenance
-
UnsupportedResponseTypeError (
unsupported_response_type
)- Authorization server doesn't support this response type
-
UnsupportedTokenTypeError (
unsupported_token_type
)- Token type not supported
-
InvalidTokenError (
invalid_token
)- Access token expired, revoked, or malformed
-
MethodNotAllowedError (
method_not_allowed
)- HTTP method not allowed (custom extension)
-
TooManyRequestsError (
too_many_requests
)- Rate limit exceeded (RFC 6585)
-
InvalidClientMetadataError (
invalid_client_metadata
)- Invalid client metadata (RFC 7591)
-
InsufficientScopeError (
insufficient_scope
)- Request requires higher privileges
CustomOAuthError
For defining custom error codes:
export class CustomOAuthError extends OAuthError {
constructor(
private readonly customErrorCode: string,
message: string,
errorUri?: string
) {
super(message, errorUri);
}
get errorCode(): string {
return this.customErrorCode;
}
}
Error Registry
All OAuth errors are registered in the OAUTH_ERRORS
constant for runtime error parsing:
export const OAUTH_ERRORS = {
[InvalidRequestError.errorCode]: InvalidRequestError,
[InvalidClientError.errorCode]: InvalidClientError,
// ... all other error types
} as const;
JSON-RPC Error Codes
The SDK defines standard JSON-RPC error codes in the ErrorCode
enum:
Location: src/types.ts:122-133
export enum ErrorCode {
// SDK error codes
ConnectionClosed = -32000,
RequestTimeout = -32001,
// Standard JSON-RPC error codes
ParseError = -32700,
InvalidRequest = -32600,
MethodNotFound = -32601,
InvalidParams = -32602,
InternalError = -32603,
}
Error Code Categories
-
SDK-Specific Errors (-32000 to -32099)
-
ConnectionClosed (-32000)
: Transport connection was closed -
RequestTimeout (-32001)
: Request exceeded timeout duration
-
-
Standard JSON-RPC Errors (-32600 to -32699)
-
ParseError (-32700)
: Invalid JSON received -
InvalidRequest (-32600)
: JSON-RPC request is invalid -
MethodNotFound (-32601)
: Requested method doesn't exist -
InvalidParams (-32602)
: Invalid method parameters -
InternalError (-32603)
: Internal server error
-
OAuth Error Handling
Client-Side OAuth Errors
Location: src/client/auth.ts
The SDK provides utilities for handling OAuth errors in client applications:
import {
OAuthError,
InvalidClientError,
InvalidGrantError
} from "@modelcontextprotocol/sdk";
// Error handling in OAuth flow
try {
const token = await exchangeAuthorization(authCode, codeVerifier, metadata);
} catch (error) {
if (error instanceof InvalidGrantError) {
// Handle invalid authorization code
console.error("Authorization code is invalid or expired");
} else if (error instanceof InvalidClientError) {
// Handle client authentication failure
console.error("Client credentials are invalid");
} else if (error instanceof OAuthError) {
// Handle other OAuth errors
console.error(`OAuth error: ${error.errorCode} - ${error.message}`);
}
}
Server-Side OAuth Error Responses
OAuth errors are automatically converted to proper HTTP responses:
try {
// OAuth operation
} catch (error) {
if (error instanceof OAuthError) {
const errorResponse = error.toResponseObject();
return new Response(JSON.stringify(errorResponse), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}
}
UnauthorizedError
A special client-side error for handling unauthorized access:
export class UnauthorizedError extends Error {
constructor(message: string = "Unauthorized") {
super(message);
this.name = "UnauthorizedError";
}
}
Protocol Error Patterns
Request Handler Error Handling
Location: src/shared/protocol.ts:411-449
The Protocol class implements comprehensive error handling for request processing:
Promise.resolve()
.then(() => handler(request, fullExtra))
.then(
(result) => {
if (abortController.signal.aborted) {
return;
}
return capturedTransport?.send({
result,
jsonrpc: "2.0",
id: request.id,
});
},
(error) => {
if (abortController.signal.aborted) {
return;
}
return capturedTransport?.send({
jsonrpc: "2.0",
id: request.id,
error: {
code: Number.isSafeInteger(error["code"])
? error["code"]
: ErrorCode.InternalError,
message: error.message ?? "Internal error",
},
});
},
)
.catch((error) =>
this._onerror(new Error(`Failed to send response: ${error}`)),
);
Key Features:
- Automatic error code detection and fallback
- Abort signal handling
- Transport error isolation
- Structured error responses
Notification Handler Error Handling
Location: src/shared/protocol.ts:359-367
Promise.resolve()
.then(() => handler(notification))
.catch((error) =>
this._onerror(
new Error(`Uncaught error in notification handler: ${error}`),
),
);
Features:
- Async error containment
- Error reporting through
onerror
callback - Non-blocking error handling
Transport Error Handling
Connection Management
Location: src/shared/protocol.ts:331-343
private _onclose(): void {
const responseHandlers = this._responseHandlers;
this._responseHandlers = new Map();
this._progressHandlers.clear();
this._pendingDebouncedNotifications.clear();
this._transport = undefined;
this.onclose?.();
const error = new McpError(ErrorCode.ConnectionClosed, "Connection closed");
for (const handler of responseHandlers.values()) {
handler(error);
}
}
Connection Closure Handling:
- All pending requests receive
ConnectionClosed
error - Clean resource cleanup
- Handler deregistration
- Optional close callback invocation
WebSocket Error Handling
Location: src/client/websocket.ts
this._ws.onerror = (event) => {
const error = event.error
? (event.error as Error)
: new Error(`WebSocket error: ${JSON.stringify(event)}`);
this.onerror?.(error);
};
this._ws.onmessage = (event) => {
try {
const message = JSON.parse(event.data);
this.onmessage?.(message);
} catch (error) {
this.onerror?.(error as Error);
}
};
Features:
- WebSocket event error transformation
- JSON parsing error handling
- Error callback propagation
Client Error Handling
Initialization Error Handling
Location: src/client/index.ts:135-183
try {
const result = await this.request(
{
method: "initialize",
params: {
protocolVersion: LATEST_PROTOCOL_VERSION,
capabilities: this._capabilities,
clientInfo: this._clientInfo,
},
},
InitializeResultSchema,
options
);
if (result === undefined) {
throw new Error(`Server sent invalid initialize result: ${result}`);
}
if (!SUPPORTED_PROTOCOL_VERSIONS.includes(result.protocolVersion)) {
throw new Error(
`Server's protocol version is not supported: ${result.protocolVersion}`,
);
}
// ... initialization logic
} catch (error) {
// Disconnect if initialization fails
void this.close();
throw error;
}
Initialization Error Patterns:
- Automatic disconnection on failure
- Protocol version validation
- Result validation
- Error propagation with cleanup
Capability Validation
protected assertCapability(
capability: keyof ServerCapabilities,
method: string,
): void {
if (!this._serverCapabilities?.[capability]) {
throw new Error(
`Server does not support ${capability} (required for ${method})`,
);
}
}
Tool Call Validation
Location: src/client/index.ts:429-478
async callTool(params: CallToolRequest["params"]) {
const result = await this.request(
{ method: "tools/call", params },
resultSchema,
options,
);
// Validate tool output schema
const validator = this.getToolOutputValidator(params.name);
if (validator) {
if (!result.structuredContent && !result.isError) {
throw new McpError(
ErrorCode.InvalidRequest,
`Tool ${params.name} has an output schema but did not return structured content`
);
}
if (result.structuredContent) {
try {
const isValid = validator(result.structuredContent);
if (!isValid) {
throw new McpError(
ErrorCode.InvalidParams,
`Structured content does not match the tool's output schema: ${this._ajv.errorsText(validator.errors)}`
);
}
} catch (error) {
if (error instanceof McpError) {
throw error;
}
throw new McpError(
ErrorCode.InvalidParams,
`Failed to validate structured content: ${error instanceof Error ? error.message : String(error)}`
);
}
}
}
return result;
}
Tool Validation Features:
- Output schema validation
- Structured content requirements
- Error classification and re-throwing
- AJV integration for JSON Schema validation
Server Error Handling
Elicitation Input Validation
Location: src/server/index.ts:313-349
async elicitInput(
params: ElicitRequest["params"],
options?: RequestOptions,
): Promise<ElicitResult> {
const result = await this.request(
{ method: "elicitation/create", params },
ElicitResultSchema,
options,
);
if (result.action === "accept" && result.content) {
try {
const ajv = new Ajv();
const validate = ajv.compile(params.requestedSchema);
const isValid = validate(result.content);
if (!isValid) {
throw new McpError(
ErrorCode.InvalidParams,
`Elicitation response content does not match requested schema: ${ajv.errorsText(validate.errors)}`,
);
}
} catch (error) {
if (error instanceof McpError) {
throw error;
}
throw new McpError(
ErrorCode.InternalError,
`Error validating elicitation response: ${error}`,
);
}
}
return result;
}
Validation Features:
- Schema-based content validation
- Error classification and wrapping
- JSON Schema integration
- Detailed error messages
Request Lifecycle Error Management
Request Timeout Management
Location: src/shared/protocol.ts:249-292
private _setupTimeout(
messageId: number,
timeout: number,
maxTotalTimeout: number | undefined,
onTimeout: () => void,
resetTimeoutOnProgress: boolean = false
) {
this._timeoutInfo.set(messageId, {
timeoutId: setTimeout(onTimeout, timeout),
startTime: Date.now(),
timeout,
maxTotalTimeout,
resetTimeoutOnProgress,
onTimeout
});
}
private _resetTimeout(messageId: number): boolean {
const info = this._timeoutInfo.get(messageId);
if (!info) return false;
const totalElapsed = Date.now() - info.startTime;
if (info.maxTotalTimeout && totalElapsed >= info.maxTotalTimeout) {
this._timeoutInfo.delete(messageId);
throw new McpError(
ErrorCode.RequestTimeout,
"Maximum total timeout exceeded",
{ maxTotalTimeout: info.maxTotalTimeout, totalElapsed }
);
}
clearTimeout(info.timeoutId);
info.timeoutId = setTimeout(info.onTimeout, info.timeout);
return true;
}
Timeout Features:
- Per-request timeout tracking
- Progress-based timeout reset
- Maximum total timeout enforcement
- Automatic cleanup on completion/error
Request Cancellation
Location: src/shared/protocol.ts:582-601
const cancel = (reason: unknown) => {
this._responseHandlers.delete(messageId);
this._progressHandlers.delete(messageId);
this._cleanupTimeout(messageId);
this._transport
?.send({
jsonrpc: "2.0",
method: "notifications/cancelled",
params: {
requestId: messageId,
reason: String(reason),
},
})
.catch((error) =>
this._onerror(new Error(`Failed to send cancellation: ${error}`)),
);
reject(reason);
};
Cancellation Features:
- Clean handler removal
- Timeout cleanup
- Cancellation notification to remote peer
- Promise rejection with reason
AbortSignal Integration
options?.signal?.addEventListener("abort", () => {
cancel(options?.signal?.reason);
});
Timeout and Cancellation
RequestOptions Timeout Configuration
export type RequestOptions = {
timeout?: number; // Individual request timeout
maxTotalTimeout?: number; // Maximum total time regardless of progress
resetTimeoutOnProgress?: boolean; // Reset timeout on progress notifications
signal?: AbortSignal; // External cancellation signal
onprogress?: ProgressCallback; // Progress notification handler
};
Progress-Based Timeout Reset
Location: src/shared/protocol.ts:451-474
private _onprogress(notification: ProgressNotification): void {
const { progressToken, ...params } = notification.params;
const messageId = Number(progressToken);
const handler = this._progressHandlers.get(messageId);
if (!handler) {
this._onerror(new Error(`Received a progress notification for an unknown token: ${JSON.stringify(notification)}`));
return;
}
const responseHandler = this._responseHandlers.get(messageId);
const timeoutInfo = this._timeoutInfo.get(messageId);
if (timeoutInfo && responseHandler && timeoutInfo.resetTimeoutOnProgress) {
try {
this._resetTimeout(messageId);
} catch (error) {
responseHandler(error as Error);
return;
}
}
handler(params);
}
Error Propagation and Recovery
Response Handler Error Processing
Location: src/shared/protocol.ts:476-502
private _onresponse(response: JSONRPCResponse | JSONRPCError): void {
const messageId = Number(response.id);
const handler = this._responseHandlers.get(messageId);
if (handler === undefined) {
this._onerror(
new Error(
`Received a response for an unknown message ID: ${JSON.stringify(response)}`,
),
);
return;
}
this._responseHandlers.delete(messageId);
this._progressHandlers.delete(messageId);
this._cleanupTimeout(messageId);
if (isJSONRPCResponse(response)) {
handler(response);
} else {
const error = new McpError(
response.error.code,
response.error.message,
response.error.data,
);
handler(error);
}
}
Error Processing Features:
- Unknown message ID handling
- Automatic resource cleanup
- McpError construction from JSON-RPC errors
- Handler deregistration
Fallback Error Handlers
/**
* A handler to invoke for any request types that do not have their own handler installed.
*/
fallbackRequestHandler?: (
request: JSONRPCRequest,
extra: RequestHandlerExtra<SendRequestT, SendNotificationT>
) => Promise<SendResultT>;
/**
* A handler to invoke for any notification types that do not have their own handler installed.
*/
fallbackNotificationHandler?: (notification: Notification) => Promise<void>;
Best Practices
1. Use Specific Error Types
// Good: Specific error types
throw new McpError(ErrorCode.InvalidParams, "Missing required field: name");
throw new InvalidClientError("Client authentication failed");
// Avoid: Generic errors
throw new Error("Something went wrong");
2. Provide Meaningful Error Messages
// Good: Descriptive messages with context
throw new McpError(
ErrorCode.InvalidRequest,
`Tool ${toolName} requires parameter '${paramName}' of type ${expectedType}`,
{ toolName, paramName, expectedType, receivedValue }
);
// Avoid: Vague messages
throw new McpError(ErrorCode.InvalidRequest, "Bad request");
3. Handle Errors at Appropriate Levels
// Application level - handle business logic errors
try {
const result = await client.callTool({ name: "calculator", arguments: { a: 1, b: 2 } });
} catch (error) {
if (error instanceof McpError && error.code === ErrorCode.MethodNotFound) {
console.log("Calculator tool not available, using fallback");
return fallbackCalculation(1, 2);
}
throw error; // Re-throw unexpected errors
}
// Transport level - handle connection errors
client.onerror = (error) => {
console.error("Transport error:", error);
// Implement reconnection logic
};
client.onclose = () => {
console.log("Connection closed, attempting reconnect...");
// Implement reconnection strategy
};
4. Implement Proper Cleanup
class MyClient {
private client: Client;
async connect() {
try {
await this.client.connect(transport);
} catch (error) {
// Cleanup on connection failure
await this.client.close();
throw error;
}
}
async shutdown() {
// Always cleanup resources
try {
await this.client.close();
} catch (error) {
console.error("Error during shutdown:", error);
}
}
}
5. Use AbortSignal for Cancellation
const controller = new AbortController();
// Set timeout for user cancellation
setTimeout(() => controller.abort("User cancelled"), 30000);
try {
const result = await client.callTool(
{ name: "longRunningTool", arguments: {} },
CallToolResultSchema,
{ signal: controller.signal }
);
} catch (error) {
if (error.name === 'AbortError') {
console.log("Operation was cancelled");
} else {
console.error("Operation failed:", error);
}
}
Examples
Complete Error Handling in Client Application
import {
Client,
McpError,
ErrorCode,
InvalidClientError,
UnauthorizedError
} from "@modelcontextprotocol/sdk";
class MCPClientWrapper {
private client: Client;
private reconnectAttempts = 0;
private maxReconnectAttempts = 3;
constructor() {
this.client = new Client({ name: "MyApp", version: "1.0.0" });
this.setupErrorHandlers();
}
private setupErrorHandlers() {
this.client.onerror = (error) => {
console.error("MCP Client Error:", error);
if (error instanceof UnauthorizedError) {
this.handleAuthenticationError();
} else if (error instanceof McpError) {
this.handleMCPError(error);
} else {
this.handleGenericError(error);
}
};
this.client.onclose = () => {
console.log("Connection closed");
this.attemptReconnect();
};
}
private handleMCPError(error: McpError) {
switch (error.code) {
case ErrorCode.ConnectionClosed:
console.log("Connection lost, will attempt to reconnect");
break;
case ErrorCode.RequestTimeout:
console.log("Request timed out, consider retrying");
break;
case ErrorCode.MethodNotFound:
console.log("Method not supported by server");
break;
default:
console.error(`MCP Error ${error.code}: ${error.message}`);
}
}
private async handleAuthenticationError() {
try {
await this.refreshAuthentication();
} catch (error) {
console.error("Failed to refresh authentication:", error);
// Redirect to login or request new credentials
}
}
private handleGenericError(error: Error) {
console.error("Generic error:", error.message);
// Log to error reporting service
}
private async attemptReconnect() {
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
console.error("Max reconnection attempts reached");
return;
}
this.reconnectAttempts++;
const delay = Math.pow(2, this.reconnectAttempts) * 1000; // Exponential backoff
console.log(`Attempting reconnection ${this.reconnectAttempts}/${this.maxReconnectAttempts} in ${delay}ms`);
setTimeout(async () => {
try {
await this.connect();
this.reconnectAttempts = 0; // Reset on successful connection
} catch (error) {
console.error("Reconnection failed:", error);
this.attemptReconnect();
}
}, delay);
}
async callToolSafely(toolName: string, args: any) {
const maxRetries = 3;
let attempt = 0;
while (attempt < maxRetries) {
try {
const result = await this.client.callTool(
{ name: toolName, arguments: args },
CallToolResultSchema,
{
timeout: 30000,
resetTimeoutOnProgress: true
}
);
if (result.isError) {
throw new Error(`Tool error: ${result.content?.[0]?.text || 'Unknown error'}`);
}
return result;
} catch (error) {
attempt++;
if (error instanceof McpError) {
if (error.code === ErrorCode.RequestTimeout && attempt < maxRetries) {
console.log(`Request timed out, retrying (${attempt}/${maxRetries})`);
continue;
} else if (error.code === ErrorCode.InvalidParams) {
// Don't retry parameter errors
throw error;
}
}
if (attempt >= maxRetries) {
throw error;
}
// Wait before retry
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
}
}
}
Server Error Handling with OAuth
import {
Server,
McpError,
ErrorCode,
OAuthError,
InvalidTokenError,
InsufficientScopeError
} from "@modelcontextprotocol/sdk";
class MCPServerWrapper {
private server: Server;
constructor() {
this.server = new Server(
{ name: "MyServer", version: "1.0.0" },
{
capabilities: { tools: {}, resources: {} },
instructions: "A secure MCP server with OAuth"
}
);
this.setupRequestHandlers();
}
private setupRequestHandlers() {
// Tool call handler with authentication
this.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
try {
// Verify authentication
if (!extra.authInfo?.valid) {
throw new InvalidTokenError("Invalid or expired access token");
}
// Check authorization scope
const requiredScope = this.getRequiredScopeForTool(request.params.name);
if (!this.hasScope(extra.authInfo.scopes, requiredScope)) {
throw new InsufficientScopeError(
`Tool '${request.params.name}' requires scope '${requiredScope}'`
);
}
// Execute tool
const result = await this.executeTool(request.params.name, request.params.arguments);
return {
content: [{ type: "text", text: JSON.stringify(result) }],
isError: false
};
} catch (error) {
// Convert OAuth errors to tool results
if (error instanceof OAuthError) {
return {
content: [{
type: "text",
text: `Authentication error: ${error.message}`
}],
isError: true
};
}
// Handle other errors
if (error instanceof McpError) {
throw error; // Re-throw MCP errors
}
// Wrap unknown errors
throw new McpError(
ErrorCode.InternalError,
`Tool execution failed: ${error.message}`,
{ toolName: request.params.name, originalError: error.message }
);
}
});
// Resource handler with error handling
this.server.setRequestHandler(ReadResourceRequestSchema, async (request, extra) => {
try {
const resource = await this.loadResource(request.params.uri);
if (!resource) {
throw new McpError(
ErrorCode.InvalidParams,
`Resource not found: ${request.params.uri}`,
{ uri: request.params.uri }
);
}
return {
contents: [{
uri: request.params.uri,
mimeType: resource.mimeType,
text: resource.content
}]
};
} catch (error) {
if (error instanceof McpError) {
throw error;
}
throw new McpError(
ErrorCode.InternalError,
`Failed to read resource: ${error.message}`,
{ uri: request.params.uri }
);
}
});
}
private getRequiredScopeForTool(toolName: string): string {
const scopeMap = {
'read_files': 'files:read',
'write_files': 'files:write',
'execute_command': 'system:execute'
};
return scopeMap[toolName] || 'basic';
}
private hasScope(userScopes: string[], requiredScope: string): boolean {
return userScopes.includes(requiredScope) || userScopes.includes('admin');
}
private async executeTool(name: string, args: any) {
// Tool implementation with proper error handling
switch (name) {
case 'calculator':
if (typeof args.a !== 'number' || typeof args.b !== 'number') {
throw new McpError(
ErrorCode.InvalidParams,
"Calculator requires numeric parameters 'a' and 'b'",
{ providedArgs: args }
);
}
return { result: args.a + args.b };
default:
throw new McpError(
ErrorCode.MethodNotFound,
`Tool '${name}' not found`,
{ availableTools: ['calculator'] }
);
}
}
private async loadResource(uri: string) {
// Resource loading with error handling
try {
// Implementation would load actual resource
return {
content: "Resource content",
mimeType: "text/plain"
};
} catch (error) {
// Re-throw as MCP error with context
throw new McpError(
ErrorCode.InternalError,
`Failed to load resource from ${uri}: ${error.message}`,
{ uri, error: error.message }
);
}
}
}
This comprehensive error handling documentation covers all major error scenarios in the MCP TypeScript SDK, from low-level transport errors to high-level application errors, providing developers with the knowledge and patterns needed to build robust MCP applications.
Top comments (0)