Summary
The TypeScript SDK generator produces z.custom<Decimal>() branches in src/types/decimal.ts. In Zod v4, toJSONSchema() throws "Custom types cannot be represented in JSON Schema" when it encounters a ZodCustom type, which causes the MCP server's tools/list to fail entirely for any SDK that has Decimal fields in its tool schemas.
Generator version
Speakeasy-generated TypeScript SDK (using Zod v4 mini, as shipped in the recent Zod v4 release).
Generated code (current)
src/types/decimal.ts — decimal() function (and all variants):
export function decimal() {
return z.union([
z.custom<Decimal>((val) => val instanceof Decimal), // ← breaks toJSONSchema()
z.number().transform((v) => new Decimal(v)),
z.string().transform((v, ctx) => { ... }),
]);
}
Same pattern appears in decimalStr, decimalOptional, decimalNullable, decimalStrOptional, decimalStrNullable, decimalConst, decimalStrConst.
Error
When the MCP SDK calls z4mini.toJSONSchema(toolInputSchema, { unrepresentable: "throw" }) during tools/list:
Custom types cannot be represented in JSON Schema
This fails the entire tools/list response, not just the affected tool — so none of the MCP tools are accessible.
Expected / suggested fix
Replace z.custom<Decimal>(...) branches with JSON Schema-representable types. For MCP tool input schemas, z.number() and z.string() are sufficient — LLMs always send primitive values, never Decimal instances.
Suggested output for decimal():
export function decimal() {
return z.union([
z.number().transform((v) => new Decimal(v)),
z.string().transform((v, ctx) => { ... }),
]);
}
The z.custom<Decimal>((val) => val instanceof Decimal) branch is only useful at runtime when calling the SDK programmatically with Decimal instances directly. It is never exercised via MCP tool calls. Removing it from the union keeps runtime SDK behavior intact while making the schema JSON Schema-serializable.
Similarly, z.undefined() branches in the optional/nullable variants should be removed or replaced, as undefined is also not representable in JSON Schema.
Impact
- Affected: any Speakeasy-generated MCP server for an API with Decimal/currency fields (e.g. shipping rates, financial amounts)
- Workaround: post-codegen script that strips
z.custom<Decimal>() lines from decimal.ts before building
Steps to reproduce
- Generate a TypeScript SDK with Speakeasy for an API that has Decimal fields
- Build the MCP server (
npm run build)
- Send
tools/list via the MCP protocol
- Observe: server returns error
-32603 with message "Custom types cannot be represented in JSON Schema"
Summary
The TypeScript SDK generator produces
z.custom<Decimal>()branches insrc/types/decimal.ts. In Zod v4,toJSONSchema()throws"Custom types cannot be represented in JSON Schema"when it encounters aZodCustomtype, which causes the MCP server'stools/listto fail entirely for any SDK that has Decimal fields in its tool schemas.Generator version
Speakeasy-generated TypeScript SDK (using Zod v4 mini, as shipped in the recent Zod v4 release).
Generated code (current)
src/types/decimal.ts—decimal()function (and all variants):Same pattern appears in
decimalStr,decimalOptional,decimalNullable,decimalStrOptional,decimalStrNullable,decimalConst,decimalStrConst.Error
When the MCP SDK calls
z4mini.toJSONSchema(toolInputSchema, { unrepresentable: "throw" })duringtools/list:This fails the entire
tools/listresponse, not just the affected tool — so none of the MCP tools are accessible.Expected / suggested fix
Replace
z.custom<Decimal>(...)branches with JSON Schema-representable types. For MCP tool input schemas,z.number()andz.string()are sufficient — LLMs always send primitive values, neverDecimalinstances.Suggested output for
decimal():The
z.custom<Decimal>((val) => val instanceof Decimal)branch is only useful at runtime when calling the SDK programmatically withDecimalinstances directly. It is never exercised via MCP tool calls. Removing it from the union keeps runtime SDK behavior intact while making the schema JSON Schema-serializable.Similarly,
z.undefined()branches in the optional/nullable variants should be removed or replaced, asundefinedis also not representable in JSON Schema.Impact
z.custom<Decimal>()lines fromdecimal.tsbefore buildingSteps to reproduce
npm run build)tools/listvia the MCP protocol-32603with message"Custom types cannot be represented in JSON Schema"