Zod v4 schema patterns. For projects using Zod validation library.
Builds type-safe Zod v4 validation schemas with `.meta()` for OpenAPI, exports inferred types, and splits base/refined schemas for composition. Claude uses this when creating or updating validation schemas for projects using Zod.
/plugin marketplace add settlemint/agent-marketplace/plugin install devtools@settlemintThis skill inherits all available tools. When active, it can use any tool Claude has access to.
<mcp_first> CRITICAL: Zod v4 has breaking changes from v3. Always fetch current docs.
MCPSearch({ query: "select:mcp__plugin_devtools_context7__query-docs" })
// Zod v4 schema patterns
mcp__context7__query_docs({
libraryId: "/colinhacks/zod",
query: "How do I use z.object, z.string, and z.number in Zod v4?",
});
// .meta() usage (v4 specific)
mcp__context7__query_docs({
libraryId: "/colinhacks/zod",
query: "How do I use .meta() for description and examples in Zod v4?",
});
// Transforms and refinements
mcp__context7__query_docs({
libraryId: "/colinhacks/zod",
query: "How do I use transform, refine, and superRefine?",
});
Note: Context7 v2 uses server-side filtering. Use descriptive natural language queries. </mcp_first>
<quick_start>
Use .meta() NOT .describe():
// ✅ CORRECT - Zod v4
z.string().meta({
description: "The recipient wallet address",
examples: ["0x1234567890123456789012345678901234567890"],
});
// ❌ WRONG - Zod v3 legacy
z.string().describe("The recipient wallet address");
Standard schema pattern:
import { z } from "zod";
export const mySchema = z
.string()
.min(1, "Must not be empty")
.max(100, "Must be at most 100 characters")
.transform((value) => value.toLowerCase())
.meta({
description: "Human-readable description for OpenAPI",
examples: ["valid-input"],
});
// Export inferred types
export type MySchema = z.infer<typeof mySchema>;
export type MySchemaInput = z.input<typeof mySchema>;
</quick_start>
<patterns> **Refined schema composition (v4.3+):**.pick(), .omit(), .extend() throw on refined schemas. Split base and refined:
// ✅ Correct - Split base and refined schemas
export const UserBaseSchema = z.object({
email: z.string().email(),
password: z.string(),
confirmPassword: z.string(),
});
export const UserSchema = UserBaseSchema.refine(
(data) => data.password === data.confirmPassword,
{ message: "Passwords must match", path: ["confirmPassword"] },
);
// Use base schema for composition
export const UserCreateSchema = UserBaseSchema.omit({ confirmPassword: true });
// ❌ Wrong - Will throw at runtime
// UserSchema.omit({ confirmPassword: true }); // Error!
Exclusive unions with z.xor() (v4.3+):
// z.xor() - fails if zero or more than one match
const PaymentMethod = z.xor([
z.object({ type: z.literal("card"), cardNumber: z.string() }),
z.object({ type: z.literal("bank"), accountNumber: z.string() }),
]);
Exact optional (v4.3+):
const Schema = z.object({
name: z.string(),
nickname: z.string().optional(), // accepts undefined
middleName: z.string().exactOptional(), // rejects undefined, allows omission
});
Schema.parse({ name: "Alice" }); // ✅
Schema.parse({ name: "Alice", nickname: undefined }); // ✅
Schema.parse({ name: "Alice", middleName: undefined }); // ❌
Composable transforms with .apply() (v4.3+):
const withPositiveConstraint = <T extends z.ZodNumber>(schema: T) => {
return schema.min(0).max(1_000_000);
};
const AmountSchema = z.number().apply(withPositiveConstraint).nullable();
const withTrimAndLowercase = <T extends z.ZodString>(schema: T) => {
return schema.trim().toLowerCase();
};
const EmailSchema = z.string().email().apply(withTrimAndLowercase);
Type predicates in refinements (v4.3+):
const AdminUser = z
.object({
role: z.string(),
permissions: z.array(z.string()),
})
.refine(
(user): user is { role: "admin"; permissions: string[] } =>
user.role === "admin",
);
type AdminOutput = z.output<typeof AdminUser>; // { role: "admin"; permissions: string[] }
Loose records (v4.3+):
// Only validates keys matching the pattern, passes through others
const EnvVars = z.looseRecord(z.string().regex(/^APP_/), z.string());
EnvVars.parse({ APP_NAME: "myapp", OTHER: 123 });
// ✅ { APP_NAME: "myapp", OTHER: 123 }
Enum with values:
export const AssetTypeValues = ["deposit", "bond", "equity"] as const;
export const AssetTypeEnum = z.enum(AssetTypeValues).meta({
description: "Type of tokenized asset",
examples: ["bond", "equity"],
});
export type AssetType = z.infer<typeof AssetTypeEnum>;
</patterns>
<constraints>
**Banned:**
- `.describe()` (use `.meta()` - Zod v4 pattern)
- `.pick()`, `.omit()`, `.extend()` on refined schemas (split base first)
- `any` without justification
- Duplicate schemas (import from shared location)
- Re-exports via barrel files (import from canonical source)
Required:
.meta({ description, examples }) on all schemasz.infer<>z.input<>, z.output<>.pick(), .omit(), .extend()z.xor() for exclusive unions (exactly one match required).exactOptional() for properties that can be omitted but not undefinedNaming: Schema files=lowercase-with-hyphens.ts
</constraints>
<success_criteria>
.meta({ description, examples }) (not .describe())z.infer<>z.xor(), .exactOptional(), .apply()This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.