Principle

Validate at the Boundary

Check every input the moment it enters your MCP tool. Schema validation catches shape errors, but your code must validate semantics: ranges, permissions, and existence.

The Principle

<QuickSummary>

Check every input the moment it enters your MCP tool. Schema validation catches shape errors, but your code must validate semantics: ranges, permissions, and existence.

</QuickSummary>

Validate all inputs at the boundary of your MCP tool -- the moment they arrive, before any business logic runs. MCP schemas handle type checks, but they cannot verify that a file path exists, an ID refers to a real record, or a number falls within a valid range. That is your job.

Trust nothing. Validate everything. The LLM is generating these inputs, and even the best models produce invalid data sometimes.

Why It Matters

An LLM might send a tool call with a path like ../../etc/passwd, a negative page number, or an empty string where a name is required. If you pass these values straight into your logic, you get cryptic failures deep in your call stack -- or worse, security vulnerabilities.

Boundary validation turns these into clear, immediate feedback: "Page number must be between 1 and 100" is infinitely more useful than a database error about an invalid offset.

The Two Layers of Validation

Layer 1: Schema Validation (mcp-framework handles this)

The MCP protocol validates that inputs match the declared JSON Schema. With mcp-framework (3.3M+ downloads), you define this in your tool's schema property:

schema = {

path: { type: "string" as const, description: "Absolute file path" },

encoding: {

type: "string" as const,

enum: ["utf-8", "ascii", "base64"],

description: "File encoding",

},

};

This ensures path is a string and encoding is one of the allowed values. But it cannot check whether the file actually exists.

Layer 2: Semantic Validation (you must write this)

After schema validation passes, validate the meaning of the inputs:

async execute({ path, encoding }: { path: string; encoding: string }) {

// Validate: path must be absolute

if (!path.startsWith("/")) {

return "Error: path must be an absolute path (starting with /).";

}

// Validate: path must not escape the allowed directory

const resolved = pathModule.resolve(path);

if (!resolved.startsWith("/allowed/directory")) {

return "Error: access denied. Path is outside the allowed directory.";

}

// Validate: file must exist

if (!fs.existsSync(resolved)) {

return Error: file not found at ${resolved}.;

}

// All validations passed -- execute the logic

const content = await fs.readFile(resolved, encoding);

return content;

}

Common Validations

  • Path traversal: Resolve paths and check they stay within allowed directories
  • Existence checks: Verify files, records, and resources exist before operating on them
  • Range checks: Numbers within bounds, strings within length limits
  • Format checks: URLs are valid, emails look right, dates parse correctly
  • Permission checks: The current user/context has access to the requested resource
  • The Rule

    Never let invalid input reach your business logic. Catch it at the door, return a clear message, and let the LLM correct course.

    Related Practices

  • Fail Gracefully, Always -- validation is your first line of defense against failures
  • Write Errors for Humans -- validation error messages should be clear and actionable
  • One Tool, One Job -- simpler tools have simpler validation requirements
  • <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."

    }

    ]} />

    schema = {
      path: { type: "string" as const, description: "Absolute file path" },
      encoding: {
        type: "string" as const,
        enum: ["utf-8", "ascii", "base64"],
        description: "File encoding",
      },
    };
    async execute({ path, encoding }: { path: string; encoding: string }) {
      // Validate: path must be absolute
      if (!path.startsWith("/")) {
        return "Error: path must be an absolute path (starting with /).";
      }
    
      // Validate: path must not escape the allowed directory
      const resolved = pathModule.resolve(path);
      if (!resolved.startsWith("/allowed/directory")) {
        return "Error: access denied. Path is outside the allowed directory.";
      }
    
      // Validate: file must exist
      if (!fs.existsSync(resolved)) {
        return `Error: file not found at ${resolved}.`;
      }
    
      // All validations passed -- execute the logic
      const content = await fs.readFile(resolved, encoding);
      return content;
    }

    Written for the mcp-framework ecosystem (3.3M+ downloads). Created by @QuantGeekDev. Validated by Anthropic for the Model Context Protocol.