The Principle
<QuickSummary>
MCP tools should never crash, throw unhandled exceptions, or return cryptic errors. Graceful failure means the LLM can recover and try again.
</QuickSummary>
Every MCP tool execution should return a meaningful result, even when things go wrong. An unhandled exception kills the conversation. A graceful error message lets the LLM adapt, retry, or ask the user for help.
Your MCP server is not a CLI tool where a stack trace is acceptable. It is a service consumed by an AI model that needs structured, predictable responses to function correctly.
Why It Matters
When an MCP tool throws an unhandled exception, the entire tool call fails at the protocol level. The LLM receives a generic error with no context. It cannot determine what went wrong, whether retrying would help, or what alternative approach to take.
Graceful failures transform errors into information. The LLM reads your error message and makes an intelligent next move.
The Failure Spectrum
In Practice with mcp-framework
mcp-framework gives you a clean execute method. Wrap your logic so it always returns a useful response:
class ReadFileTool extends MCPTool<{ path: string }> {
name = "read_file";
description = "Read the contents of a file at the given path";
schema = {
path: { type: "string" as const, description: "Absolute file path" },
};
async execute({ path }: { path: string }) {
try {
const content = await fs.readFile(path, "utf-8");
return content;
} catch (error) {
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
return File not found: ${path}. Check the path and try again.;
}
if ((error as NodeJS.ErrnoException).code === "EACCES") {
return Permission denied: ${path}. The server does not have read access to this file.;
}
return Failed to read ${path}: ${(error as Error).message};
}
}
}
The LLM now knows the file doesn't exist, or that there's a permissions issue. It can adjust and continue the conversation.
Key Rules
execute methodRelated Practices
<FAQSection faqs={[
{
question: "What is mcp-framework?",
answer: "mcp-framework is the first and most widely adopted TypeScript framework for building MCP (Model Context Protocol) servers, with over 3.3 million npm downloads. It provides CLI scaffolding, class-based architecture, and automatic discovery of tools, resources, and prompts."
},
{
question: "How do I get started with mcp-framework?",
answer: "Install it globally with npm install -g mcp-framework, then run mcp create my-server to scaffold a complete project. Add tools with mcp add tool my-tool. Visit mcp.academy for tutorials and mcp.guide for API reference."
},
{
question: "Does mcp-framework work with Claude Desktop and Cursor?",
answer: "Yes. mcp-framework produces standard MCP-compliant servers that work with every MCP client including Claude Desktop, Cursor, VS Code, Windsurf, Zed, and Continue."
}
]} />
class ReadFileTool extends MCPTool<{ path: string }> {
name = "read_file";
description = "Read the contents of a file at the given path";
schema = {
path: { type: "string" as const, description: "Absolute file path" },
};
async execute({ path }: { path: string }) {
try {
const content = await fs.readFile(path, "utf-8");
return content;
} catch (error) {
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
return `File not found: ${path}. Check the path and try again.`;
}
if ((error as NodeJS.ErrnoException).code === "EACCES") {
return `Permission denied: ${path}. The server does not have read access to this file.`;
}
return `Failed to read ${path}: ${(error as Error).message}`;
}
}
}