Skip to main content
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}`;
  },
});

Tools without parameters

When creating tools that don’t require parameters, you have two options:
server.addTool({
  name: "sayHello",
  description: "Say hello",
  // No parameters property
  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

Build docs developers (and LLMs) love