Skip to content

Schema inference

Opt in to build-time tool schema inference while preserving runtime input validation.

For the complete documentation index, see llms.txt. Markdown variants of every page are available by appending .md to the URL.

Enable experimental schema inference to expose existing typed functions without declaring their input types again in Zod. This feature is disabled by default.

xmcp.config.ts

Enable strict or strictNullChecks in your tsconfig.json so nullable inputs retain their meaning.

src/tools/greet.ts

The compiler generates a schema for the first handler parameter. MCP clients see a required string named name, with its property description. Runtime validation rejects missing or non-string names before calling the handler. The second parameter remains the request context and is not part of the input schema.

Reuse existing functions

Imported types, interfaces, aliases, and re-exported handlers are resolved using your project's TypeScript configuration, including path aliases.

src/lib/greet.ts
src/tools/greet.ts

The function's JSDoc becomes the tool description. An explicit metadata.description takes precedence. Property JSDoc becomes each field's description. Supported JSDoc constraint tags also add runtime validation.

JSDoc validation constraints

Add these tags to the JSDoc immediately above an input property. The compiler emits Zod validation, and MCP clients see the corresponding JSON Schema constraints:

TagProperty typeGenerated validation
@minimum / @maximumnumber.min() / .max() (inclusive)
@minLength / @maxLengthstring.min() / .max()
@patternstring.regex(new RegExp(...))
@format emailstring.email()
@format uristring.url()
@format uuidstring.uuid()

A day of 99 or an invalid email is rejected before the handler runs. Bounds do not imply integer validation. Tags also work on imported interfaces, nested properties, and optional or nullable strings/numbers; null and omitted optional fields retain their normal behavior.

Numeric bounds must be finite. Lengths must be nonnegative safe integers. Patterns use raw JavaScript regular expression source, without /.../ delimiters or flags. Duplicate tags, malformed values, reversed bounds, unsupported formats, and tags on incompatible types fail the build with the property's source location. Constraint tags belong on input properties, not handlers. Literal types, arrays, and mixed unions need explicit schemas for these constraints. Unrecognized JSDoc tags are ignored.

Supported inputs

The first parameter must resolve to an object with named properties. Supported property types include:

  • Strings, numbers, booleans, and literal values.
  • Optional properties, nullable properties, and unions of supported types.
  • Arrays and readonly arrays.
  • Nested objects and imported interfaces/type aliases.
  • Concrete utility types, such as Pick, when they resolve to supported properties.

String literal unions generate z.enum, so clients see the allowed values and validation errors identify valid options. Booleans generate z.boolean() rather than a union of true and false.

Functions with no parameters retain an empty input schema. A handler accepting request context but no inputs should explicitly export schema = {}.

Unbound generics, overloads, any, unknown, functions, classes, Date, bigint, symbols, tuples, index signatures, intersections, recursive types, and unsupported top-level inputs fail the build. The error identifies the tool, property, and source location. Use an explicit schema for these tools. A required property containing undefined is unsupported; use an optional property (value?: string) when omission is intended.

Explicit schemas take precedence

Any explicit runtime schema export disables input inference for that tool, including re-exports and an empty schema. Metadata and output schemas keep their existing behavior. Tools with unsupported input types can therefore coexist with inferred tools.

src/tools/validate-email.ts

JSDoc tags cover the constraints above. Keep explicit schemas for custom refinements, coercion, transforms, and schema defaults. Function defaults still execute normally; inference does not evaluate them or advertise them as schema defaults. Unknown object properties follow the existing Zod stripping behavior.

Builds and development

Inference runs in the compiler and generates Zod schemas using the application's installed Zod version. Keep Zod installed; neither the TypeScript compiler nor source files are needed by the deployed server.

Development builds reuse the TypeScript program and track imported type files and configuration dependencies. Editing those files regenerates the schemas. An unsupported edit reports an error until corrected. Inference still runs with typescript.skipTypeCheck: true, because extracting schemas requires type information even when general diagnostics are skipped.

This feature covers tool inputs only. It does not infer output schemas, prompts, or resources. Without the opt-in, omitting schema continues to mean an empty tool input schema.

See the runnable inferred tool schemas example.

On this page

One framework to rule them all