Skip to main content
The schema generator converts TypeScript classes to JSON Schema with support for constraints, validation, and type inference.

Functions

classToJsonSchemaWithConstraints()

Converts a TypeScript class to JSON Schema with full constraint support.
function classToJsonSchemaWithConstraints(
  classConstructor: new () => any,
  sourceFilePath?: string
): object
classConstructor
new () => any
required
Class constructor to convert to JSON Schema
sourceFilePath
string
Optional path to source file for type parsing (used in dev mode)
schema
object
JSON Schema object with properties, required fields, and constraints
{
  type: 'object',
  properties: { ... },
  required: [...]
}

Example

import { classToJsonSchemaWithConstraints, SchemaConstraint, Optional } from '@leanmcp/core';

class UserInput {
  @SchemaConstraint({
    description: 'User email',
    pattern: '^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,}$',
  })
  email!: string;

  @SchemaConstraint({
    description: 'User age',
    minimum: 0,
    maximum: 120,
  })
  age!: number;

  @Optional()
  @SchemaConstraint({ description: 'Optional bio' })
  bio?: string;
}

const schema = classToJsonSchemaWithConstraints(UserInput);
console.log(JSON.stringify(schema, null, 2));
Output:
{
  "type": "object",
  "properties": {
    "email": {
      "type": "string",
      "description": "User email",
      "pattern": "^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,}$"
    },
    "age": {
      "type": "number",
      "description": "User age",
      "minimum": 0,
      "maximum": 120
    },
    "bio": {
      "type": "string",
      "description": "Optional bio"
    }
  },
  "required": ["email", "age"]
}

classToJsonSchema()

Basic JSON Schema generation without constraints (legacy).
function classToJsonSchema(classConstructor: new () => any): object
classConstructor
new () => any
required
Class constructor to convert to JSON Schema
schema
object
Basic JSON Schema object without constraints

Example

import { classToJsonSchema } from '@leanmcp/core';

class SimpleInput {
  name!: string;
  age!: number;
}

const schema = classToJsonSchema(SimpleInput);
// { type: 'object', properties: { name: { type: 'string' }, age: { type: 'number' } } }

Type Inference

The schema generator uses two strategies for type inference:

1. Pre-computed Metadata (Production)

In production builds (leanmcp build), schemas are pre-computed and stored in dist/schema-metadata.json:
{
  "UserInput": {
    "email": { "type": "string" },
    "age": { "type": "number" },
    "tags": {
      "type": "array",
      "items": { "type": "string" }
    }
  }
}
This provides fast startup with no runtime parsing.

2. Runtime Parsing (Development)

In development mode, the schema generator uses ts-morph to parse TypeScript source files and extract exact type information:
import { parseClassTypesSync, registerClassSource } from '@leanmcp/core';

// Register source file for a class
registerClassSource('UserInput', './src/types.ts');

// Parse types at runtime
const types = parseClassTypesSync(UserInput);
This provides accurate array item types and complex type support during development.

Supported Types

The schema generator supports the following TypeScript types:
TypeScript TypeJSON Schema TypeNotes
string"string"-
number"number"-
boolean"boolean"-
Array<T>{ "type": "array", "items": ... }Item type inferred
T[]{ "type": "array", "items": ... }Item type inferred
object"object"-
Custom class"object"Recursive schema generation

Array Type Inference

import { SchemaConstraint } from '@leanmcp/core';

class ArrayExample {
  @SchemaConstraint({ description: 'List of tags' })
  tags!: string[]; // Inferred as array of strings

  @SchemaConstraint({ description: 'List of numbers' })
  scores!: number[]; // Inferred as array of numbers

  @SchemaConstraint({ description: 'Generic array (fallback)' })
  items!: any[]; // Falls back to array of strings
}

Constraint Types

Supported JSON Schema constraints:

String Constraints

class StringConstraints {
  @SchemaConstraint({
    description: 'Username',
    minLength: 3,
    maxLength: 20,
    pattern: '^[a-zA-Z0-9_]+$',
  })
  username!: string;

  @SchemaConstraint({
    description: 'Status',
    enum: ['active', 'inactive', 'pending'],
  })
  status!: string;
}

Number Constraints

class NumberConstraints {
  @SchemaConstraint({
    description: 'Rating',
    minimum: 0,
    maximum: 5,
  })
  rating!: number;

  @SchemaConstraint({
    description: 'Priority',
    enum: [1, 2, 3],
  })
  priority!: number;
}

Array Constraints

class ArrayConstraints {
  @SchemaConstraint({
    description: 'Tags',
    type: 'array',
    items: { type: 'string' },
  })
  tags!: string[];
}

Default Values

class DefaultValues {
  @SchemaConstraint({
    description: 'Enabled flag',
    default: true,
  })
  enabled?: boolean;

  @SchemaConstraint({
    description: 'Limit',
    default: 10,
  })
  limit?: number;
}

Decorators

@Optional

Marks a property as optional in the JSON Schema.
import { Optional } from '@leanmcp/core';

class OptionalExample {
  required!: string; // Required by default

  @Optional()
  optional?: string; // Marked as optional
}

@SchemaConstraint

Adds constraints and metadata to a property.
import { SchemaConstraint } from '@leanmcp/core';

class ConstraintExample {
  @SchemaConstraint({
    description: 'User email',
    pattern: '^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,}$',
  })
  email!: string;
}
See Decorators for full constraint options.

Advanced Usage

Nested Classes

import { SchemaConstraint } from '@leanmcp/core';

class Address {
  @SchemaConstraint({ description: 'Street' })
  street!: string;

  @SchemaConstraint({ description: 'City' })
  city!: string;
}

class User {
  @SchemaConstraint({ description: 'User name' })
  name!: string;

  @SchemaConstraint({ description: 'User address' })
  address!: Address; // Nested class - generates nested object schema
}

Type Inference from Constraints

If design:type metadata is unavailable, the generator infers types from constraints:
class InferredTypes {
  @SchemaConstraint({
    minLength: 5, // Inferred as string
  })
  field1!: string;

  @SchemaConstraint({
    minimum: 0, // Inferred as number
  })
  field2!: number;

  @SchemaConstraint({
    enum: [true, false], // Inferred as boolean
  })
  field3!: boolean;
}

Source File Registration

import { registerClassSource, clearTypeCache } from '@leanmcp/core';

// Register source file for a class (for runtime parsing)
registerClassSource('UserInput', './src/inputs/user.ts');

// Clear type cache (useful for hot-reload)
clearTypeCache();

Performance

Production Mode

  • Uses pre-computed schema metadata from dist/schema-metadata.json
  • Zero runtime parsing - schemas loaded from JSON
  • Fast server startup (~10ms for schema loading)

Development Mode

  • Falls back to runtime parsing with ts-morph
  • Slower startup (~100-500ms for parsing)
  • Accurate type inference for complex types

Build docs developers (and LLMs) love