import type {
StandardJSONSchemaV1,
StandardSchemaV1,
StandardTypedV1,
} from "@standard-schema/spec";
export type { StandardJSONSchemaV1, StandardSchemaV1, StandardTypedV1 };
// TODO(sdk-v2): once @modelcontextprotocol/core v2 is stable, import
// StandardSchemaWithJSON / standardSchemaToJsonSchema / validateStandardSchema
// from there and delete this file. At that point decide whether to tighten
// App.registerTool to StandardSchemaWithJSON (drops zod 3 from the peer range
// and the lazy z.toJSONSchema fallback below).
/**
* A schema that implements both Standard Schema (validation) and Standard JSON
* Schema (serialization). Zod v4, ArkType, and Valibot (via
* `@valibot/to-json-schema`) all satisfy this.
*
* Mirrors the type of the same name in `@modelcontextprotocol/core` v2 so that
* bumping to that package later is a drop-in import swap.
*
* @see https://standardschema.dev/
* @see https://github.com/modelcontextprotocol/typescript-sdk/pull/1689
*/
export interface StandardSchemaWithJSON {
readonly "~standard": StandardSchemaV1.Props &
StandardJSONSchemaV1.Props;
}
export namespace StandardSchemaWithJSON {
export type InferInput =
StandardTypedV1.InferInput;
export type InferOutput =
StandardTypedV1.InferOutput;
}
/** JSON-Schema target draft used for tool input/output schemas (matches core MCP). */
const TARGET = { target: "draft-2020-12" } as const;
/**
* Serialize a Standard Schema to JSON Schema for the given direction.
*
* Uses `~standard.jsonSchema` when present (zod v4, ArkType, Valibot, …).
* Falls back to a lazy `zod/v4` import for zod v3.25.x — which implements
* `~standard.validate` but not yet `~standard.jsonSchema` — so the existing
* `^3.25.0 || ^4.0.0` peer range keeps working. Non-zod schemas without
* `jsonSchema` throw.
*/
export async function standardSchemaToJsonSchema(
schema: StandardSchemaV1,
io: "input" | "output",
): Promise> {
const std = schema["~standard"] as Partial<
StandardSchemaWithJSON["~standard"]
>;
if (std.jsonSchema) {
return std.jsonSchema[io](TARGET);
}
if (std.vendor === "zod") {
const { z } = await import("zod/v4");
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- bridging StandardSchemaV1 → zod's $ZodType for the v3.25 fallback
return z.toJSONSchema(schema as any, { io });
}
throw new Error(
`Schema (vendor: ${std.vendor}) does not implement Standard JSON Schema (~standard.jsonSchema). ` +
`Use a library that does (zod v4, ArkType, Valibot) or wrap your schema accordingly.`,
);
}
/**
* Validate a value against a Standard Schema. Returns the parsed value on
* success or throws with a formatted issue list (optionally prefixed).
*/
export async function validateStandardSchema(
schema: S,
value: unknown,
errorPrefix = "",
): Promise> {
const result = await schema["~standard"].validate(value);
if (result.issues) {
const msg = result.issues
.map((i) => {
const path = i.path
?.map((p) => (typeof p === "object" ? p.key : p))
.join(".");
return path ? `${path}: ${i.message}` : i.message;
})
.join("; ");
throw new Error(errorPrefix + msg);
}
return result.value;
}