MCP Server Input Validation Patterns: Python and Node.js Implementation Guide
MCP servers use Pydantic v2 for Python and Zod for Node/TypeScript to enforce strict input validation, prevent malformed data from reaching tool handlers, and automatically generate OpenAPI-compatible schemas.
The ComposioHQ/awesome-claude-skills repository establishes canonical patterns for validating LLM-driven tool calls in Model Context Protocol (MCP) servers. These validation strategies ensure that only properly formatted data reaches your business logic while providing clear error messages back to the calling LLM.
Why Input Validation Matters for MCP Servers
MCP servers act as bridges between LLMs and external tools, making input validation a critical security and reliability layer. Without strict validation, malformed inputs can crash handlers, leak sensitive data, or trigger unexpected side effects. The reference implementations in mcp-builder/reference/python_mcp_server.md and mcp-builder/reference/node_mcp_server.md demonstrate how to reject unknown fields, enforce type constraints, and sanitize inputs before processing.
Python Validation Pattern with Pydantic
Python MCP servers leverage Pydantic v2 models to define tool inputs. FastMCP automatically derives the inputSchema from these models, creating an OpenAPI-compatible specification that clients use to format requests.
Configuring BaseModel for Strict Validation
According to the Python MCP Server Guide, validation models should use ConfigDict with three critical settings: str_strip_whitespace=True to auto-trim strings, validate_assignment=True to re-validate on field updates, and extra='forbid' to reject unknown parameters.
from pydantic import BaseModel, Field, ConfigDict, field_validator
class ServiceToolInput(BaseModel):
"""Input model for a service-tool operation."""
model_config = ConfigDict(
str_strip_whitespace=True,
validate_assignment=True,
extra='forbid'
)
Field Constraints and Custom Validators
Use Field parameters to enforce length limits, numeric ranges, and collection sizes. For complex validation logic, implement @field_validator methods as shown in mcp-builder/reference/python_mcp_server.md:
param1: str = Field(
...,
description="First parameter (e.g. user ID)",
min_length=1,
max_length=100
)
param2: int | None = Field(
default=None,
description="Optional integer",
ge=0,
le=1000
)
tags: list[str] = Field(
default_factory=list,
description="List of tags",
max_items=10
)
@field_validator('param1')
@classmethod
def non_empty(cls, v: str) -> str:
if not v.strip():
raise ValueError("param1 cannot be empty")
return v
When you type a tool parameter with ServiceToolInput, FastMCP automatically generates the JSON Schema from the Pydantic model definition.
Node/TypeScript Validation Pattern with Zod
Node.js and TypeScript implementations use Zod for runtime type checking. Unlike Pydantic, which generates schemas from types, Zod requires explicit schema construction that doubles as both validator and type definition.
Creating Strict Schemas
The Node MCP Server Guide recommends chaining .strict() to z.object() to forbid unknown properties. Each field uses Zod primitives like z.string(), z.number(), or z.nativeEnum() with constraints attached via .min(), .max(), and .describe():
import { z } from "zod";
const UserSearchInputSchema = z.object({
query: z.string()
.min(2, "Query must be at least 2 characters")
.max(200, "Query must not exceed 200 characters")
.describe("Search string to match against names/emails"),
limit: z.number()
.int()
.min(1)
.max(100)
.default(20)
.describe("Maximum results to return"),
offset: z.number()
.int()
.min(0)
.default(0)
.describe("Number of results to skip for pagination"),
response_format: z.nativeEnum(ResponseFormat)
.default(ResponseFormat.MARKDOWN)
.describe("Output format: markdown or json")
}).strict();
Registering Tools with Input Validation
Pass the Zod schema as the inputSchema property when calling server.registerTool() in mcp-builder/reference/node_mcp_server.md. The framework validates inputs against this schema before invoking your handler:
server.registerTool(
"search_users",
{
inputSchema: UserSearchInputSchema,
// ... other tool config
},
async (params) => {
// params is guaranteed to match UserSearchInputSchema
}
);
Common Validation Principles Across Languages
Both the Python and Node.js patterns share four fundamental principles that ensure robust MCP server implementations:
- Explicit field definitions – Every expected argument must be declared with specific types and constraints.
- Constraint enforcement – Apply minimum/maximum values, regex patterns, and enum restrictions at the schema level.
- Strict mode activation – Use
extra='forbid'in Pydantic or.strict()in Zod to reject unexpected parameters. - Consistent output – Validated inputs guarantee that downstream code receives correctly typed data, eliminating defensive null checks and type coercion.
Summary
- Pydantic v2 provides Python MCP servers with declarative validation through
BaseModel,Fieldconstraints, and@field_validatordecorators, with FastMCP automatically extracting theinputSchema. - Zod enables TypeScript servers to define strict runtime schemas using
z.object().strict()and chainable validation methods, explicitly passing the schema toserver.registerTool(). - Both approaches require
extra='forbid'or.strict()to prevent parameter injection attacks. - Validation occurs before handler execution, ensuring that business logic only processes sanitized, type-safe data.
- The ComposioHQ/awesome-claude-skills reference files provide production-ready templates for implementing these patterns.
Frequently Asked Questions
What happens if an MCP server receives invalid input?
When validation fails, the MCP framework returns a structured error to the calling LLM before executing the tool handler. In Python, Pydantic raises a ValidationError with detailed field-level messages. In Node.js, Zod throws a ZodError containing the specific constraint violations. These errors help the LLM correct its parameters in subsequent calls.
Can MCP servers use alternative validation libraries?
While the ComposioHQ/awesome-claude-skills repository standardizes on Pydantic v2 for Python and Zod for TypeScript, you can technically use alternatives like msgspec or valibot. However, FastMCP's automatic inputSchema generation specifically targets Pydantic models, and the reference architecture in mcp-builder/reference/node_mcp_server.md assumes Zod for TypeScript type inference.
How do validation patterns affect the MCP tool's JSON Schema?
The inputSchema sent to MCP clients directly reflects your validation constraints. Pydantic generates JSON Schema properties from Field parameters like min_length and ge, while Zod's .describe() and constraint methods populate the schema's description, minimum, and maximum fields. This ensures LLMs receive accurate documentation about parameter requirements.
Should MCP servers validate outputs as well as inputs?
Input validation remains the priority for MCP servers since they receive untrusted data from external LLMs. However, output validation using the same libraries (Pydantic or Zod) can ensure your tool responses conform to expected structures before serialization, particularly when returning complex objects to 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 →