npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mcp-schema-validator

v0.1.0

Published

Runtime schema validation middleware for MCP tool inputs and outputs. Compatible with Zod, Valibot, ArkType.

Readme

mcp-schema-validator

Runtime schema validation middleware for MCP tool inputs and outputs.

npm version License: MIT Standard Schema

MCP tool schemas are declared but rarely validated at runtime. Malformed LLM inputs cause cryptic errors deep in your handlers. This library wraps any MCP server with a composable validation middleware in one line.


Features

  • Runtime validation for all tool inputs and outputs (Zod v3/v4, Valibot, ArkType)
  • LLM-friendly errors - structured messages that tell the model exactly what went wrong
  • Type-safe schema registry - write once, validate everywhere, infer TypeScript types for free
  • Drop-in wrapper - one function call wraps your existing handler
  • Composable middleware - chain with rate-limiter, audit-log, and your own middleware
  • Strict mode - reject any tool call where schema is undeclared
  • Soft mode - log violations without blocking (safe for gradual rollout)

Installation

npm install mcp-schema-validator
# peer deps
npm install @standard-schema/spec
# pick your schema library
npm install zod          # or valibot, or arktype

Quick Start

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
import { createRegistry, withValidation } from "mcp-schema-validator";

// 1. Declare schemas once - TypeScript types inferred automatically
const registry = createRegistry()
  .register("search", {
    input: z.object({
      query: z.string().min(1),
      limit: z.number().int().min(1).max(100).default(10),
    }),
    output: z.object({
      results: z.array(z.string()),
      total: z.number(),
    }),
  })
  .build();

// 2. Your handler stays clean - no validation boilerplate
async function handler(req) {
  // ...fetch results...
  return { content: [{ type: "text", text: JSON.stringify({ results: [], total: 0 }) }] };
}

// 3. Wrap in one line
server.setRequestHandler(
  CallToolRequestSchema,
  withValidation(registry, handler, { mode: "strict" })
);

Schema Libraries

This library uses the Standard Schema interface. Any compatible library works identically:

// Zod
import { z } from "zod";
const schema = z.object({ query: z.string() });

// Valibot
import * as v from "valibot";
const schema = v.object({ query: v.string() });

// ArkType
import { type } from "arktype";
const schema = type({ query: "string" });

Middleware Composition

import { compose, withLogger, withRateLimit, withAuditLog } from "mcp-schema-validator";

const pipeline = compose(
  withLogger(),
  withRateLimit({ maxRpm: 60 }),
  withAuditLog({ onRecord: (r) => db.insert(r) })
);

const validatedHandler = withValidation(registry, pipeline(rawHandler), { mode: "strict" });

Strict vs. Soft Mode

| Mode | Unregistered tool | Invalid input | Invalid output | |------|-------------------|---------------|----------------| | strict (default) | ❌ Blocked, error returned | ❌ Blocked | ❌ Blocked | | soft | ⚠️ Logged, continues | ⚠️ Logged, continues | ⚠️ Logged, continues |

Soft mode is ideal for gradual rollout - instrument first, enforce later.

withValidation(registry, handler, {
  mode: "soft",
  onSoftViolation: (tool, phase, errors) => {
    metrics.increment("mcp.violation", { tool, phase });
  },
})

Error Format

When validation fails in strict mode, the LLM receives a structured, actionable message:

Tool "search" failed input validation.

The following fields are invalid:
  • [limit]: Number must be less than or equal to 100
  • [query]: String must contain at least 1 character(s)

Please correct your arguments and retry.

API Reference

withValidation(registry, handler, options?)

Wraps a tool handler with input + output validation.

| Parameter | Type | Description | |-----------|------|-------------| | registry | SchemaRegistry | Map of tool name → { input?, output? } schemas | | handler | ToolHandler | Your existing MCP CallToolRequest handler | | options.mode | "strict" \| "soft" | Default: "strict" | | options.onValidationError | fn | Called when validation fails | | options.onSoftViolation | fn | Called for soft-mode violations | | options.logger | Console | Custom logger |

createRegistry()

Fluent builder for type-safe schema registration. Returns a SchemaRegistryBuilder.

const registry = createRegistry()
  .register("tool_name", { input: schema, output: schema })
  .merge(otherBuilder)
  .build();

compose(...middlewares)

Left-to-right middleware composition. First middleware runs first (outermost).

withLogger(options?) · withRateLimit(options) · withAuditLog(options)

Built-in middleware. See TypeScript types for full option shapes.


License

MIT © ConfigKits