FastMCP uses the Standard Schema specification for defining tool parameters. This allows you to use your preferred schema validation library (Zod, ArkType, or Valibot) as long as it implements the spec.
Supported libraries
FastMCP supports any validation library that implements the Standard Schema specification:
Zod Most popular TypeScript-first schema validation
ArkType TypeScript’s 1:1 validator
Valibot Modular and type-safe schema library
Using Zod
Zod is the most popular choice and comes with excellent TypeScript support:
import { FastMCP } from "fastmcp" ;
import { z } from "zod" ;
const server = new FastMCP ({
name: "My Server" ,
version: "1.0.0" ,
});
server . addTool ({
name: "fetch-content" ,
description: "Fetch the content of a URL" ,
parameters: z . object ({
url: z . string (). url (),
timeout: z . number (). optional (). default ( 5000 ),
headers: z . record ( z . string ()). optional (),
}),
execute : async ( args ) => {
// args is fully typed: { url: string; timeout: number; headers?: Record<string, string> }
return `Fetching ${ args . url } with timeout ${ args . timeout } ms` ;
},
});
Using ArkType
ArkType provides 1:1 TypeScript syntax for runtime validation:
import { FastMCP } from "fastmcp" ;
import { type } from "arktype" ;
const server = new FastMCP ({
name: "My Server" ,
version: "1.0.0" ,
});
server . addTool ({
name: "fetch-content" ,
description: "Fetch the content of a URL" ,
parameters: type ({
url: "string" ,
"timeout?" : "number" ,
"headers?" : "Record<string, string>" ,
}),
execute : async ( args ) => {
return `Fetching ${ args . url } ` ;
},
});
Using Valibot
Valibot is a modular, lightweight alternative with a functional API:
Valibot requires the peer dependency @valibot/to-json-schema.
npm install valibot @valibot/to-json-schema
import { FastMCP } from "fastmcp" ;
import * as v from "valibot" ;
const server = new FastMCP ({
name: "My Server" ,
version: "1.0.0" ,
});
server . addTool ({
name: "fetch-content" ,
description: "Fetch the content of a URL" ,
parameters: v . object ({
url: v . string (),
timeout: v . optional ( v . number ()),
headers: v . optional ( v . record ( v . string (), v . string ())),
}),
execute : async ( args ) => {
return `Fetching ${ args . url } ` ;
},
});
When creating tools that don’t require parameters, you have two options:
Omit parameters
Empty object
server . addTool ({
name: "sayHello" ,
description: "Say hello" ,
// No parameters property
execute : async () => {
return "Hello, world!" ;
},
});
import { z } from "zod" ;
server . addTool ({
name: "sayHello" ,
description: "Say hello" ,
parameters: z . object ({}), // Empty object
execute : async () => {
return "Hello, world!" ;
},
});
Both approaches are fully compatible with all MCP clients, including Cursor. FastMCP automatically generates the proper schema in both cases.
Type inference
All schema libraries provide automatic TypeScript type inference for the execute function:
import { z } from "zod" ;
const UserSchema = z . object ({
name: z . string (),
age: z . number (),
email: z . string (). email (),
});
server . addTool ({
name: "createUser" ,
description: "Create a new user" ,
parameters: UserSchema ,
execute : async ( args ) => {
// args is typed as: { name: string; age: number; email: string }
return `Created user: ${ args . name } ( ${ args . email } ), age ${ args . age } ` ;
},
});
Complex schemas
You can use all features of your chosen schema library:
import { z } from "zod" ;
server . addTool ({
name: "processData" ,
description: "Process complex data" ,
parameters: z . object ({
// Enums
status: z . enum ([ "pending" , "active" , "completed" ]),
// Arrays
tags: z . array ( z . string ()). min ( 1 ). max ( 10 ),
// Nested objects
metadata: z . object ({
createdBy: z . string (),
timestamp: z . number (),
}),
// Union types
value: z . union ([ z . string (), z . number ()]),
// Optional with default
priority: z . number (). min ( 1 ). max ( 5 ). default ( 3 ),
}),
execute : async ( args ) => {
return `Processing with status: ${ args . status } ` ;
},
});
Validation errors
When a client passes invalid parameters, FastMCP automatically handles validation errors and returns them to the client:
import { z } from "zod" ;
server . addTool ({
name: "validateEmail" ,
description: "Validate an email address" ,
parameters: z . object ({
email: z . string (). email ( "Invalid email format" ),
}),
execute : async ( args ) => {
return `Valid email: ${ args . email } ` ;
},
});
// If client passes { email: "not-an-email" }, they receive:
// Error: Invalid email format
Next steps
Tools Learn more about tool definitions
Error Handling Handle errors in your tools