Skip to content

Conversation

@lachieh
Copy link
Contributor

@lachieh lachieh commented Dec 2, 2025

Summary

  • Migrate from zod-to-json-schema to @standard-community/standard-json for vendor-agnostic Standard Schema to JSON Schema conversion
  • Add support for any Standard Schema compliant validator (Zod 3, Zod 4, Valibot, ArkType, etc.)
  • Maintain backward compatibility with Zod 3 via optional peer dependency zod-to-json-schema
  • Handle Zod function schemas by extracting args schema before JSON Schema conversion

Key Changes

  • New schema utilities (src/util/schema.ts): Unified interface for Standard Schema and JSON Schema handling
  • Function schema support: Added helpers to detect and extract args from Zod function schemas (v3 and v4)
  • Validation utilities: Renamed validate-zod-schema.ts to validate-schema.ts with Standard Schema support
  • Comprehensive tests: Added schema compatibility tests for Zod 3, Zod 4, and JSON Schema

Test plan

  • All 328 existing tests pass
  • New compatibility tests for Standard Schema conversion
  • Lint and type checks pass
  • Manual testing in showcase app

🤖 Generated with Claude Code

@charliecreates charliecreates bot requested a review from CharlieHelps December 2, 2025 21:46
@vercel
Copy link

vercel bot commented Dec 2, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
cloud Error Error Dec 5, 2025 3:53pm
showcase Error Error Dec 5, 2025 3:53pm
tambo-docs Error Error Dec 5, 2025 3:53pm

Copy link
Contributor

@charliecreates charliecreates bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • The migration to Standard Schema introduces a few behavioral regressions and loosened type guarantees, notably around TamboTool typing and component prop validation in renderComponentIntoMessage.
  • Schema validation has been generalized from Zod AST inspection to JSON Schema pattern detection, which is more flexible but depends on converter behavior and could silently miss some dynamic-key map patterns.
  • Several central helpers (schemaToJsonSchema, safeSchemaToJsonSchema, getParametersFromToolSchema) currently handle errors and unknown schema types leniently, which may mask real integration problems.
  • Tests and utilities have largely been updated correctly for Zod 4, but there are a few brittle Zod-internal assumptions and minor API misuses that should be tightened to avoid future compatibility issues.
Additional notes (8)
  • Maintainability | react-sdk/src/model/component-metadata.ts:78-78
    TamboTool.tool and transformToContent were previously strongly typed with Zod-based generics (Args, Returns). They've been relaxed to (...args: any[]) => any and result: any, which throws away compile-time safety for tool implementations.

Given that you're now vendor-agnostic at the schema level, you can still preserve type safety by keeping the generic interface and parameterizing it by the value-level types rather than the validator implementation itself. For example:

  • Keep TamboTool<Args extends unknown[] = unknown[], Result = unknown> with tool: (...args: Args) => Result and transformToContent?: (result: Result) => ....
  • Let toolSchema remain StandardSchemaV1 | JSONSchemaLite without tying it to those generics (document that they must match, but don’t try to encode that in the type system).

Right now this change makes it much easier to write tools whose runtime behavior doesn’t actually match the described schema, and TypeScript will no longer help catch that mismatch at call sites.

  • Maintainability | react-sdk/src/util/generate-component.ts:27-39
    renderComponentIntoMessage now determines whether to validate props by checking isStandardSchema(registeredComponent.props). However, registeredComponent.props appears to be the JSON Schema representation produced by getSerializedProps/convertPropsToJsonSchema, not the original validator.

This means that in the usual registry flow you're likely never hitting the parse branch anymore:

  • In the original design, registeredComponent.props held a Zod schema, so instanceof ZodType worked.
  • After migration, props is serialized to JSON Schema in the registry, but this function still assumes it can call .parse().

As a result, components that previously had their props validated at render-time may now be receiving unvalidated parsedProps if only JSON Schema is stored. That’s a subtle behavior regression and will be hard to spot in normal testing.

  • Maintainability | react-sdk/src/util/validate-schema.ts:175-181
    You’ve reintroduced a local isJSONSchema implementation here after moving a more general one into schema.ts. Maintaining two similar-but-not-identical JSON Schema detectors in different modules increases the risk of divergent behavior over time.

assertNoRecordSchema already depends on utilities from schema.ts; re-using that module’s isJSONSchema would reduce subtle inconsistencies around what the code considers a JSON Schema.

  • Readability | react-sdk/src/providers/tambo-registry-provider.test.tsx:189-189
    The new registry tests intentionally reject z.record() by asserting on the message Record types (objects with dynamic keys) are not supported .... That’s good. However, in a couple of cases you’ve updated the test schemas from z.record(z.string()) to z.record(z.string(), z.string()) to fit the Zod 4 signature.

These tests currently pass only because your new validator operates on the JSON Schema output, not the Zod AST, so it doesn’t care about the exact Zod call shape. That’s fine for behavior, but the tests are now slightly misleading—they look like they’re verifying Zod behavior when in reality they’re verifying JSON Schema patterns.

To keep tests maintainable, it would help to make that distinction explicit in comments, especially where the same test file is mixing Zod 3 and Zod 4 usage via z3/z4.

  • Maintainability | react-sdk/src/testing/tools.ts:10-10
    serializeRegistry now wraps all tool parameters into a single args parameter whose schema is either:
  • The JSON Schema derived from a Standard Schema validator (for Zod/Valibot/etc.), or
  • The JSON Schema passed in directly.

This is a significant behavioral shift from the previous implementation, which:

  • Inspected Zod function schemas and emitted one parameter per argument (param1, param2, etc.).

If any downstream consumers (showcase app, backend, or other tools) expect named positional parameters rather than a single args object, this will be a breaking change in how tools are described, even if the underlying schema content is equivalent. It’s not obvious from the PR description whether this change of shape is intentional and acceptable for all consumers.

  • Maintainability | react-sdk/src/util/registry.ts:138-138
    getParametersFromToolSchema now returns an empty parameter list and logs a warning when the schema is neither JSON Schema nor Standard Schema. This makes the system quite lenient in the face of misconfigured tool schemas: a tool with an unsupported or malformed toolSchema will be exposed to the AI with no parameters, which can be very confusing to debug.

Previously, misconfigured Zod schemas would either fail conversion or validation more loudly. Swallowing these into an empty parameter list could mask real integration bugs.

  • Maintainability | react-sdk/package.json:61-61
    The new peerDependencies entry for zod-to-json-schema on the React SDK is marked optional, which aligns with the intent: only Zod 3 users should need it. However, the code paths that actually require it (schema.ts’s Zod vendor registration) throw an error only when the converter is invoked for a Zod 3 schema.

This laziness is good, but there’s currently no high-level documentation or runtime guidance for SDK consumers who:

  • Pass Zod 3 schemas,
  • Haven’t installed zod-to-json-schema, and
  • Encounter a thrown error during registry setup.

Given that this is a new requirement, you may want to make the failure mode more discoverable for users.

  • Maintainability | react-sdk/src/providers/tambo-registry-provider.tsx:29-32
    deriveServerKey is unchanged, but now that the registry is more schema-flexible, the validation/logging paths around MCP server infos become even more important. Currently, if a malformed URL is passed, you catch the error and fall back to url.toLowerCase(), which may still be a completely bogus key.

While this isn’t new in this diff, the broader refactor is a good opportunity to consider whether silently accepting invalid URLs is still the right tradeoff, versus surfacing a clearer error to the caller.

Summary of changes

Overview

  • Migrated the React SDK from directly using Zod 3 + zod-to-json-schema to a Standard Schema-based abstraction using @standard-community/standard-json.
  • Introduced a new schema utility module to detect Standard Schema vs JSON Schema and convert to JSON Schema, with vendor-specific handling for Zod 3 and Zod 4.
  • Replaced validate-zod-schema with a more general validate-schema that validates both Standard Schema and JSON Schema for unsupported record/map patterns.
  • Updated TamboTool, TamboComponent, registry, interactable provider, tests, and tooling to:
    • Support both Zod 3 and Zod 4 (and other Standard Schema vendors) for component props and tool schemas.
    • Treat toolSchema/propsSchema as Standard Schema or JSON Schema rather than Zod-only.
    • Handle Zod function schemas (v3 & v4) by extracting argument schemas before JSON Schema conversion.
  • Adjusted package dependencies and peer dependencies to:
    • Add @standard-community/standard-json, @standard-schema/spec, zod@4 (dev), zod3 alias, and updated zod-to-json-schema.
    • Make zod-to-json-schema an optional peer of the React SDK for Zod 3 users.
  • Added extensive compatibility tests for Zod 3, Zod 4, and JSON Schema usage in the registry and validation utilities.
  • Updated all test imports and code paths to use zod/v4 where appropriate and to work with the new schema abstraction.

Copy link
Contributor Author

@lachieh lachieh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@CharlieHelps make these changes too

@charliecreates
Copy link
Contributor

charliecreates bot commented Dec 2, 2025

Expand this to see my work.
  • Previously scanned react-sdk/src/util/registry.ts around isZod3FunctionSchema, isZod4FunctionSchema, and getZodFunctionArgs to remove as assertions in favor of versioned type-guard helpers, and confirmed isZod3Schema/isZod4Schema from react-sdk/src/util/schema.ts were available and imported.
  • Previously refined Zod3FunctionSchema/Zod4FunctionSchema typings and updated getZodFunctionArgs to branch on isZod4FunctionSchema(schema) and isZod3FunctionSchema(schema), adjusting its return type to lean on the narrowed schema type instead of explicit casts.
  • Previously ran npm run lint -- --filter=@tambo-ai/react and addressed a jsdoc/check-alignment warning in react-sdk/src/util/schema.ts, then committed the changes as fix(react-sdk): use versioned Zod function schema guards (08e1690).
  • Previously inspected the upstream react-sdk/src/util/schema.ts on origin/lachieh/move-to-standard-schema to capture the original isZod3Schema/isZod4Schema implementations and JSDoc formatting for the Zod helpers.
  • Previously reset react-sdk/src/util/schema.ts back to the upstream version from origin/lachieh/move-to-standard-schema and re-applied a minimal patch that simply adds export to both isZod3Schema and isZod4Schema while preserving the original JSDoc and structure.
  • Previously re-ran npm run lint -- --filter=@tambo-ai/react to validate that the updated schema.ts (with exported isZod3Schema/isZod4Schema) and the earlier registry.ts changes are clean under the React SDK lint configuration, then addressed jsdoc/check-alignment warnings around the isZod3Schema/isZod4Schema blocks.
  • Previously re-ran npm run check-types && npm test from the repo root to confirm all packages (including @tambo-ai/react) type-check and tests pass after the schema helper exports, with no new failures reported in the shared pipeline.
  • Previously inspected git status -sb and a focused git diff for react-sdk/src/util/schema.ts to verify that the only outstanding changes are the export function isZod3Schema/export function isZod4Schema adjustments and aligned JSDoc blocks around those helpers.
  • Previously checked staged vs unstaged diffs to drop an earlier commit and leave a minimal react-sdk/src/util/registry.ts delta for PR #1446, focused on versioned function-schema guards.
  • Confirmed react-sdk/src/util/registry.ts is now using the versioned isZod3FunctionSchema/isZod4FunctionSchema type guards without as assertions and that a final npm run check-types && npm test across the monorepo reports all tasks as successful with @tambo-ai/react clean.

Copy link
Contributor

@alecf alecf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good - seems like charlie caught the good stuff :) a few minor comments

* };
* ```
*/
export interface TamboTool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sad to see that there's no easy way to keep Args and Returns - I assume this just gets incredibly hard when you're dealing with a combination of zod and StandardSchema?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's definitely possible, and I think we should extend their usage to the functions that take them as args so that we get type errors inline when registering tools/components/interactables (unless I'm missing something and that already works?)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well what already worked was you could assign the tool, and then pass in schema, and the type system would complain if things didn't line up:

{
  tool: (message: string) => { console.log("I got a message: ", message); return "Hello!" },
  toolSchema: z.function().args(z.number()).returns(z.number())
 }

and you'd get a type error because the args / return values do not match

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, yeah I wasn't getting those because it wasn't specifying the type args. I'm going to try something to fix this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, this is where some chunky changes happen. See the changes in react-sdk/src/model/component-metadata.ts and the new RegisterToolFn interface in react-sdk/src/providers/tambo-registry-provider.tsx. This should add some stronger type checking! With that, there are some opinions though. We should probably give it some use to see how useful or aggressive it is.

Copy link
Contributor

@charliecreates charliecreates bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The diff successfully introduces a Standard Schema abstraction and JSON Schema conversion pipeline, but it also relaxes type safety around tools and props, particularly by dropping TamboTool generics and relying on any in several places. Record-schema detection and error handling in assertNoRecordSchema/getSerializedProps could be stricter to avoid silently skipping problematic schemas or masking conversion failures. The new registry and testing utilities centralize schema handling but change the shape of serialized tool parameters and treat unknown toolSchema types very leniently, which may lead to confusing behavior when integrations are misconfigured. Tightening these areas will make the new vendor-agnostic design more robust and easier to debug for SDK consumers.

Additional notes (9)
  • Maintainability | react-sdk/src/model/component-metadata.ts:55-55
    TamboTool was previously generic over argument and return types, which allowed strong typing of tool and transformToContent at compile time. The new non-generic interface with tool: (...args: any[]) => any and result: any discards those guarantees and makes it much easier to drift out of sync with the declared toolSchema.

Given that you’re now schema-vendor-agnostic, you can still keep value-level generics without coupling them to Zod:

  • Keep TamboTool<Args extends unknown[] = unknown[], Result = unknown> and type tool: (...args: Args) => Result and transformToContent?: (result: Result) => ....
  • Leave toolSchema as StandardSchemaV1 | JSONSchemaLite and document that it must describe Args/Result without trying to encode that relationship in the type system.

Right now this change represents a regression in type safety for tool authors and callers, and it violates the “avoid unnecessary any” guidance implied by typical TypeScript best practices.

  • Maintainability | react-sdk/src/util/generate-component.ts:27-39
    renderComponentIntoMessage used to check instanceof ZodType against the stored props and would only call .parse when the registry still held a Zod schema. After the migration, registeredComponent.props appears to be the JSON Schema output from getSerializedProps in most flows, but this function still attempts props.parse(...) whenever isStandardSchema returns true.

If props is already a JSON Schema object (which is the normal case once registered), isStandardSchema will be false and no validation occurs. However, when props is a non-Zod Standard Schema vendor implementation that doesn’t expose parse with the same runtime shape, this cast to { parse: (data: unknown) => unknown } risks a late runtime error.

More importantly, the branch no longer clearly distinguishes between validator instances and serialized schemas, which makes it easy to accidentally skip validation entirely and diverge from the design intent of validating props before rendering.

  • Maintainability | react-sdk/src/testing/tools.ts:1-1
    The new serializeRegistry implementation unconditionally wraps all tool parameters into a single args object and no longer inspects Zod function schemas to emit one parameter per argument. This is a significant behavioral change from the previous version, which produced param1, param2, etc. from Zod function .parameters().

If any downstream consumers (tests, tooling, backend expectations) rely on the old positional parameter shape, this will be an observable breaking change even though the overall JSON content is equivalent. The tests in this file only use serializeRegistry within Jest expectations, so they won’t catch external consumer regressions.

  • Maintainability | react-sdk/src/testing/tools.ts:5-5
    serializeRegistry now uses safeSchemaToJsonSchema and always wraps tool parameters into a single args object, mirroring the new runtime behavior. That’s consistent with getParametersFromToolSchema, but unlike schemaToJsonSchema this helper drops any error information: the tests don’t exercise the undefined path, and callers ignore the possibility that conversion failed.

If safeSchemaToJsonSchema returns undefined for a Standard Schema (e.g. missing zod-to-json-schema for Zod 3), you’ll emit a parameter whose schema is undefined in the serialized registry. That’s a confusing artifact for tests that are supposed to describe what the AI sees and may mask issues in snapshot-style assertions.

  • Maintainability | react-sdk/src/util/registry.ts:129-177
    The new Zod function helpers (isZod4FunctionSchema, isZod3FunctionSchema, getZodFunctionArgs) correctly delegate to the shared isZodSchema guard before touching internals, which is good. However, the return type of isZod4FunctionSchema/isZod3FunctionSchema is just boolean, so call sites don’t benefit from type narrowing and still rely on manual casting when accessing def/_def.

Given that these helpers exist specifically to identify function-shaped Zod schemas, you can make them proper type guards that refine to the appropriate Zod type and eliminate the remaining as assertions in getParametersFromToolSchema. This reduces the surface area of "brittle internal" usage further.

  • Maintainability | react-sdk/src/util/registry.ts:117-117
    mapTamboToolToContextTool used to derive parameter metadata by inspecting Zod function schemas and emitting one parameter per argument; it now calls getParametersFromToolSchema, which always collapses the tool parameters to a single args object based on the converted JSON Schema.

This is a semantic change to how tools are exposed: downstream consumers that previously saw param1, param2, etc. will now only see { name: "args", type: "object", schema: ... }. While the underlying JSON Schema still captures the structure, any consumers that relied on positional param names or descriptions per-arg will break or behave differently.

The new behavior might be acceptable, but it should be clearly intentional and, ideally, guarded by tests that assert the new shape so regressions between releases are explicit rather than accidental.

  • Readability | react-sdk/src/util/schema.ts:67-134
    looksLikeJSONSchema correctly avoids classifying Standard Schema values as JSON Schema, but the Zod-based validator is still quite broad and will accept any object that happens to match its shape, even if it isn’t actually a JSON Schema used in this system. Given that schemaToJsonSchema already handles Standard Schema conversion centrally, you don’t strictly need this helper to be a type guard that returns JSONSchema7—a looser structural check might be sufficient.

The main risk is that callers may treat arbitrary objects that happen to satisfy jsonSchemaTopLevel as trusted schemas and skip validation or conversion paths they should go through.

  • Maintainability | react-sdk/src/providers/tambo-registry-provider.tsx:463-494
    getSerializedProps previously had a simple and predictable behavior: Zod schemas were converted with zod-to-json-schema, and other shapes were treated as raw JSON Schema. With the new Standard Schema abstraction, the error path now throws a generic "Failed to convert props schema for ${name}" after logging, but there’s no context about why conversion failed (e.g. missing Zod 3 converter, unsupported vendor, function schemas, etc.).

Given that this function is on the hot path for component registration, opaque errors here can be very frustrating to debug for consumers integrating new schema vendors or migrating from Zod 3 to Zod 4.

  • Maintainability | react-sdk/src/providers/hoc/with-tambo-interactable.tsx:7-13
    InteractableConfig.propsSchema has been widened from z.ZodTypeAny to Schema (Standard Schema or JSON Schema), which is consistent with the rest of the migration. However, the HOC does not appear to perform any conversion or validation against this schema—it just passes the config into useTamboInteractable.

That means callers can now pass arbitrary JSON Schema objects and the type system won’t help them keep propsSchema in sync with the wrapped component’s actual props. This was already somewhat true with ZodTypeAny, but broadening the accepted shape while continuing to treat it as an opaque value increases the risk that malformed or mismatched schemas get attached to interactables without any early feedback.

Given that InteractableConfig is a public surface for users, it’s worth clarifying (in code comments or types) whether JSON Schema is really supported here, or if this should be limited to Standard Schema validators only (and then converted downstream).

Summary of changes

Overview

  • Migrated multiple packages from zod default import to versioned imports (zod/v3 in backend, zod/v4 in React SDK and docs) to support side‑by‑side Zod 3 & Zod 4.
  • Introduced a new Standard Schema abstraction in the React SDK (src/util/schema.ts) and a JSON Schema validation helper (src/util/validate-schema.ts), replacing Zod‑specific logic.
  • Updated the Tambo React component/tool model (component-metadata.ts) and registry utilities (registry.ts, generate-component.ts, testing/tools.ts) to work with StandardSchemaV1 | JSONSchema7 instead of Zod-only schemas.
  • Added comprehensive schema compatibility tests (tambo-registry-schema-compat.test.tsx, schema.test.ts, validate-schema.test.ts) and removed the old Zod‑specific validation tests.
  • Updated package metadata (react-sdk/package.json, packages/backend/package.json, packages/core/package.json, root package-lock.json) to depend on @standard-community/standard-json, @standard-schema/spec, and newer zod-to-json-schema, while making Zod/Z2JS optional peers in the React SDK.
  • Adjusted various providers and tests to use the new Zod 4 function API and Standard Schema‑based validation rather than direct Zod AST inspection.

@charliecreates charliecreates bot removed the request for review from CharlieHelps December 2, 2025 23:58
@charliecreates
Copy link
Contributor

charliecreates bot commented Dec 3, 2025

#1446 (comment)
#1446 (comment)

Updated the Zod function helpers in registry.ts to use the versioned guards and expose proper type predicates.

Changes
  • In react-sdk/src/util/schema.ts:

    • Made the existing Zod version guards exportable:
      • isZod3Schema(schema: unknown): schema is z.infer<typeof zod3SchemaType>
      • isZod4Schema(schema: unknown): schema is $ZodType
    • Left their runtime behavior and JSDoc comments otherwise unchanged so they still align with the current Standard Schema wiring.
  • In react-sdk/src/util/registry.ts:

    • Introduced minimal internal interfaces for the Zod function internals we actually use:
      • Zod4FunctionSchema with:
        • def?: { type?: string } (for def.type === "function")
        • _def?: { input?: unknown } (for v4 function args)
      • Zod3FunctionSchema with:
        • _def?: { typeName?: string; args?: unknown }
    • Updated the Zod function helpers to be proper type guards and to use the versioned schema guards:
      • isZod4FunctionSchema now:
        • Calls isZod4Schema(schema) instead of the broader isZodSchema(schema).
        • Is typed as schema is Zod4FunctionSchema.
        • Only inspects candidate.def?.type === "function" after the versioned guard passes.
      • isZod3FunctionSchema now:
        • Calls isZod3Schema(schema) instead of isZodSchema(schema).
        • Is typed as schema is Zod3FunctionSchema.
        • Uses _def?.typeName === "ZodFunction" on the narrowed value.
    • Refactored getZodFunctionArgs to use the versioned function guards instead of a single isZodSchema branch:
      • For Zod 4 function schemas (isZod4FunctionSchema(schema)), returns schema._def?.input.
      • For Zod 3 function schemas (isZod3FunctionSchema(schema)), returns schema._def?.args.
      • For anything else, returns undefined without touching internals.
    • Left getParametersFromToolSchema behavior the same at call sites, but it now benefits from:
      • Versioned Zod detection via isZod4FunctionSchema / isZod3FunctionSchema.
      • Narrower types for the schema variable when those guards succeed.

Net effect: we only poke at def/_def on values that have passed a version-specific Zod schema guard, and the helpers now act as type guards rather than plain booleans.

Verification
# Lint (monorepo via Turbo)
$ npm run lint
# ✅ Passed (no errors). Existing warnings remain, including:
#   - jsdoc/check-alignment and jsdoc/require-jsdoc in schema/validate-schema
#     that were already present on this branch.

# Type-check (monorepo)
$ npm run check-types
# ✅ Passed across all packages, including @tambo-ai/react.

# Tests (monorepo)
$ npm test
# ✅ All Jest and Node test suites passed (no new failures in react-sdk).

Self-review items not applied (kept as follow-ups to avoid widening this change):

  • react-sdk/src/util/schema.ts:92-118 (looksLikeJSONSchema heuristic): kept the existing top-level zod-based validator as-is. Tightening which key combinations count as “JSON Schema-like” is a broader behavioral change for the registry and would be better handled in a dedicated pass with explicit product sign-off.
  • react-sdk/src/providers/tambo-registry-provider.tsx:35-52 (extra tests for borderline propsSchema inputs): did not add new tests in this patch to keep the scope focused on the Zod function helpers. The current registry/compat tests already exercise Standard Schema and JSON Schema flows; adding more cases around looksLikeJSONSchema can be done in a subsequent testing-focused change.
  • react-sdk/src/util/registry.ts:15-32 (rename Zod function interfaces): left the names as Zod4FunctionSchema / Zod3FunctionSchema with minimal fields, since they are file-local implementation details and only describe the subset of internals we actually touch. A rename to *SchemaLike would be purely cosmetic and felt out-of-scope for this targeted fix.
  • react-sdk/src/util/registry.ts:44-61 and 73-90 (extra runtime validation of _def and args/input): kept the guards focused on the fields we need (def.type, _def.typeName) and let getZodFunctionArgs return undefined when _def.input/_def.args is missing or unexpected. Adding stricter shape validation and corresponding error behavior would be a behavior change for function-typed tools and deserves its own small PR.
  • react-sdk/src/util/registry.ts:120-171 (stricter handling when a Zod function’s args schema isn’t Standard Schema): kept the existing behavior of returning [] and logging when the schema isn’t Standard/JSON Schema. Surfacing a hard error for those cases is a larger UX decision for tool authors and wasn’t requested in this follow-up.
  • react-sdk/src/util/validate-schema.ts:1-24 (documenting the stricter record detection): the isRecordJsonSchema behavior comes from earlier commits and is already covered by tests. Updating higher-level docs or comments about partially-open objects feels like a doc/product change and wasn’t necessary to land the Zod function guard tweaks here.

Copy link
Contributor

@charliecreates charliecreates bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The migration largely implements the Standard Schema abstraction correctly, but a few areas introduce risk: props validation in renderComponentIntoMessage now assumes .parse on any Standard Schema and can crash for non-Zod vendors; TamboTool’s public type has lost its generic coupling between tool and toolSchema, weakening compile-time safety. Tool and component schema serialization is stricter but still conflates different failure modes and sometimes swallows conversion errors, and registry parameter extraction now unconditionally collapses all schemas into a single args object, which may be a breaking surface change. Several helper and stub implementations (tool-caller, TamboStubRegistryProvider) add unnecessary indirection or tight coupling that could be simplified for better maintainability.

Additional notes (7)
  • Maintainability | react-sdk/src/mcp/tambo-mcp-provider.tsx:364-371
    TamboMcpProvider now falls back to ({} as JSONSchemaLite) when tool.inputSchema is falsy. That silently converts a missing MCP tool schema into an empty object schema, which will:

  • Mislead downstream consumers about the true shape of the tool parameters.

  • Potentially hide integration bugs where MCP tools failed to advertise an input schema.

Given that this is an error path, masking it with {} (and a TODO comment) is not ideal. Callers will have no indication that the MCP server did not provide a usable schema.

  • Maintainability | react-sdk/src/model/component-metadata.ts:71-71
    TamboToolBase has been split into several variants (TamboToolZod3Function, TamboToolZod4Function, TamboToolStandardSchema, TamboToolJSONSchema, etc.) and defineTamboTool overloads are introduced to recover type inference. However, the exported TamboTool type is now a non-generic base with tool: (...args: any[]) => any and toolSchema: unknown, which:

  • Throws away compile-time coupling between tool value-level types and toolSchema descriptions.

  • Forces downstream consumers (including mapTamboToolToContextTool) to treat everything as unknown and rely on runtime guards.

This is a regression from the previous generic TamboTool<Args, Returns> design, which statically enforced that the function signature matched the Zod schema. You’ve reintroduced some inference for defineTamboTool, but the public TamboTool remains untyped.

  • Maintainability | react-sdk/src/providers/tambo-registry-provider.tsx:125-152
    RegisterToolFn overloads correctly distinguish between Zod 4, Zod 3, Standard Schema, JSON Schema, and the fallback TamboTool type. However, the implementation signature is still const registerTool: RegisterToolFn = useCallback((tool: TamboTool, warnOnOverwrite = true) => { ... }), which completely erases the generics and specialised tool shapes at runtime.

This has two consequences:

  • Inside registerTool, tool is typed as the wide TamboTool union, so you can’t leverage the richer schema typing from defineTamboTool without additional narrowing.
  • At call sites that import RegisterToolFn, the overloads help inference, but downstream functions like mapTamboToolToContextTool are still operating on the erased shape and rely on runtime detection (isStandardSchema, looksLikeJSONSchema).

Given the complexity of the overloads, this erasure undermines some of the value you’re trying to gain from them.

  • Maintainability | react-sdk/src/testing/tools.ts:11-11
    serializeRegistry now uses safeSchemaToJsonSchema for Standard Schema props and tools, but it ignores the possibility that conversion failed and just returns whatever safeSchemaToJsonSchema produced. Since safeSchemaToJsonSchema currently returns a plain JSONSchema7 | undefined in this file, tools or components whose schemas fail conversion will be serialized with schema: undefined, which is a confusing artifact in snapshot-based tests and hides the underlying issue.

  • Maintainability | react-sdk/src/testing/tools.ts:1-1
    serializeRegistry has been significantly changed: instead of introspecting Zod function schemas and emitting one parameter per argument, it now wraps everything into a single args parameter whose schema is either the Standard Schema–derived JSON Schema or the JSON Schema passed through.

This aligns with the new runtime behaviour of getParametersFromToolSchema, but it is a breaking change for any consumers (tests, tooling, or visualization code) that expected positional parameter names like param1, param2, etc. The existing tests in this file don’t exercise or assert on that structural difference, so an inadvertent future regression here would be easy to miss.

  • Performance | react-sdk/src/util/tool-caller.ts:78-81
    findTool now wraps registryTool.tool in an async arrow function that simply awaits the original call:
getComponentContext: async (...args) => await registryTool.tool(...args)

Since tool can already return either a value or a promise (MaybeAsync), the previous direct reference (getComponentContext: registryTool.tool) would have worked fine; wrapping it adds an extra microtask and obscures the original type shape without a clear benefit. It could also interfere with this binding if any tools rely on it (unlikely but possible).

  • Maintainability | react-sdk/src/providers/tambo-stubs.tsx:100-104
    TamboStubRegistryProvider’s props type has been replaced with PropsWithChildren<TamboRegistryContext>. However, TamboRegistryContext includes runtime-only fields like registerMcpServer, registerMcpServers, and onCallUnregisteredTool that the stub previously didn’t accept. In the JSX usage below you’ve added dummy mcpServerInfos, registerMcpServer, and registerMcpServers props, but onCallUnregisteredTool is still missing.

More importantly, coupling the stub’s props directly to the full context type makes tests brittle: any future addition to TamboRegistryContext will break all TamboStubProvider call sites. The original explicit prop list was more stable and focused on what tests actually need.

Summary of changes

Overview

This diff migrates multiple parts of the backend and React SDK from direct Zod 3 usage and zod-to-json-schema to a Standard Schema–centric design with JSON Schema conversion.

Key changes include:

  • Backend: switches imports from zod to zod/v3 and bumps zod-to-json-schema to ^3.25.0 in packages/backend and packages/core.
  • React SDK:
    • Adds @standard-community/standard-json and @standard-schema/spec dependencies, plus versioned zod peer/dev dependencies with optional zod-to-json-schema for Zod 3.
    • Introduces src/util/schema.ts with Standard Schema detection, Zod 3/4 guards, JSON Schema heuristics (looksLikeJSONSchema), and conversion helpers (schemaToJsonSchema, safeSchemaToJsonSchema).
    • Replaces validate-zod-schema with validate-schema, operating on JSON Schema derived via Standard Schema, and forbids record-like types using additionalProperties detection.
    • Refactors TamboTool/TamboComponent model to support multiple schema forms, adds defineTamboTool and inference helpers, and introduces several specialized TamboTool* variants.
    • Updates tambo-registry-provider and registry.ts to use the new schema utilities, register tools/components with Standard Schema or JSON Schema, and extract function args for Zod 3/4.
    • Updates tests and docs to use zod/v4, adds compatibility suites (tambo-registry-schema-compat.test.tsx, schema.test.ts, validate-schema.test.ts), and adjusts testing helpers (serializeRegistry) to work via Standard Schema JSON conversion.
    • Minor behavioral tweaks: tool calling wraps tool in an async function; MCP provider now falls back to {} JSON schema when inputSchema is missing; stubs and HOCs updated to use new types.
  • Root package-lock.json is regenerated to reflect new packages, peer flags, and updated zod-to-json-schema/zod topology.

Comment on lines 138 to 281
schema: TamboTool["toolSchema"],
): ParameterSpec[] => {
if (isJsonSchema(schema)) {
// Handle JSON Schema - wrap as a single "args" parameter
if (looksLikeJSONSchema(schema)) {
const jsonSchema = schema;
return [
{
name: "args",
type: "object",
description: schema.description ?? "",
description: jsonSchema.description ?? "",
isRequired: true,
schema: schema,
schema: jsonSchema,
},
];
}

const parameters: z.ZodTuple = schema.parameters();
return parameters.items.map((param, index): ParameterSpec => {
const name = `param${index + 1}`;
const type = getZodBaseType(param);
const description = param.description ?? "";
const isRequired = !param.isOptional();
const schema = zodToJsonSchema(param);

return {
name,
type,
description,
isRequired,
schema,
};
});
};

const getZodBaseType = (schema: z.ZodTypeAny): string => {
const typeName = schema._def.typeName;
switch (typeName) {
case "ZodString":
return "string";
case "ZodNumber":
return "number";
case "ZodBoolean":
return "boolean";
case "ZodArray":
return "array";
case "ZodEnum":
return "enum";
case "ZodDate":
return "date";
case "ZodObject":
return "object";
default:
console.warn("falling back to string for", typeName);
return "string";
// Handle Standard Schema validators
if (isStandardSchema(schema)) {
// Check for Zod function schemas - extract args before converting
if (isZod4FunctionSchema(schema) || isZod3FunctionSchema(schema)) {
const argsSchema = getZodFunctionArgs(schema);
if (argsSchema && isStandardSchema(argsSchema)) {
const jsonSchema = schemaToJsonSchema(argsSchema);
return [
{
name: "args",
type: "object",
description: jsonSchema.description ?? "",
isRequired: true,
schema: jsonSchema,
},
];
}
}

// Non-function Standard Schema - convert directly
const jsonSchema = schemaToJsonSchema(schema);
return [
{
name: "args",
type: "object",
description: jsonSchema.description ?? "",
isRequired: true,
schema: jsonSchema,
},
];
}

// Fallback for unknown schema types
console.warn("Unknown tool schema type, returning empty parameters");
return [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getParametersFromToolSchema now collapses all tool schemas (Standard Schema, Zod function, and JSON Schema) into a single "args" parameter containing the converted JSON Schema. This is consistent internally, but it is a semantic change from the previous behavior, where Zod function schemas produced one parameter per positional argument (param1, param2, etc.).

If any existing consumers (UI, backend, or tests) depended on the old per-argument structure, this is a breaking change in the tool surface, not just an implementation detail. The tests in this diff only assert that something is produced; they don’t assert the shape compared to the previous behavior.

Suggestion

If the new single-args shape is the intended long-term contract, it should be explicitly documented and tested as such, and any downstream consumers updated accordingly. At a minimum, add tests that:

  • Verify Zod 3 function schemas with multiple args now produce a single args parameter whose schema is an object/tuple capturing all inputs.
  • Confirm JSON Schema-based tools are treated the same way.

If backward compatibility is desired, an alternative is to preserve per-argument parameter expansion for Zod function schemas, and only use the single-args pattern for non-function Standard Schema/JSON Schema. Reply with "@CharlieHelps yes please" if you’d like me to add tests clarifying and locking in the new args-only contract, or sketch a backward-compatible expansion strategy.

Comment on lines +4 to +10
import { Schema } from "../../util/schema";
import { useTamboInteractable } from "../tambo-interactable-provider";

export interface InteractableConfig {
componentName: string;
description: string;
propsSchema?: z.ZodTypeAny;
propsSchema?: Schema;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with-tambo-interactable used to constrain propsSchema to z.ZodTypeAny, which at least encoded that this value was a validator rather than arbitrary data. Widening it to Schema (Standard Schema or JSON Schema) means callers can now pass raw JSON Schema here, but this HOC does not perform any conversion/validation and simply forwards the config into useTamboInteractable.

That combination is risky: the type suggests JSON Schema is acceptable, but there’s no clear contract around whether downstream code expects a validator vs. a schema object. It also deviates from the rest of the public surface, where TamboComponent.propsSchema is clearly documented as “schema describing the component’s props” and then converted centrally.

If this HOC is intended to mirror TamboComponent, it should either:

  • Be restricted to StandardSchemaV1-style validators only (and converted later), or
  • Clearly document that JSON Schema is accepted but never executed, so users don’t assume runtime validation like zod.parse() will occur.
Suggestion

Consider tightening InteractableConfig.propsSchema back to a validator-only type and documenting that JSON Schema should go through the registry/component path instead. For example:

// Prefer a validator-only type here
import type { StandardSchemaV1 } from "@standard-schema/spec";

export interface InteractableConfig {
  componentName: string;
  description: string;
  propsSchema?: StandardSchemaV1; // or a narrower alias re-exported from util/schema
}

Then, if there’s a strong use case for JSON Schema on interactables, add an explicit propsJsonSchema?: JSONSchema7 field so the two code paths can be handled distinctly downstream.

Reply with "@CharlieHelps yes please" if you’d like me to draft that refactor and thread it through useTamboInteractable and callers.

@charliecreates charliecreates bot removed the request for review from CharlieHelps December 4, 2025 06:13
Copy link
Contributor

@charliecreates charliecreates bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The migration to Standard Schema and JSON Schema conversion is broadly on the right track, but several core paths now rely on loose heuristics, brittle Zod internals, and silent fallbacks that can hide schema misconfigurations. In particular, renderComponentIntoMessage, TamboTool typing, serializeRegistry, and getParametersFromToolSchema all deserve tightening to avoid lost validation and confusing behavior. Consolidating JSON Schema detection, hardening Zod function helpers, and making unknown schema shapes fail loudly will make the new abstraction safer and easier to debug.

Additional notes (6)
  • Maintainability | react-sdk/src/util/generate-component.ts:27-37
    Switching this check from instanceof ZodType to isStandardSchema(registeredComponent.props) changes the semantics pretty significantly:

  • registeredComponent.props is now set by getSerializedProps, which converts Standard Schema validators to JSON Schema objects. After registration, props is no longer the original validator but the serialized schema.

  • isStandardSchema(registeredComponent.props) will therefore be false for the normal registry flow, because the Standard Schema wrapper (~standard) has been stripped. That means this branch will almost never run in practice.

  • When it does run (e.g. if someone bypasses the registry and sticks a raw validator onto componentList), you cast to { parse: (data: unknown) => unknown } and call .parse without checking the vendor or method name. Non‑Zod Standard Schema implementations are not required to expose parse, so this can blow up at runtime.

Net effect: most components have silently lost props validation at render time, and the remaining code path is brittle.

To restore predictable behavior and avoid unsafe casts, you should either:

  • Treat registeredComponent.props as always JSON Schema at this point and skip runtime parsing here (validation happens earlier), or
  • Thread both the validator and the serialized schema through the registry and add a dedicated validateProps helper that only calls .parse on known Zod schemas (using isZodSchema) and leaves non‑Zod Standard Schema/JSON Schema alone.

Right now this logic is confusing and easy to misuse because props is sometimes a validator and sometimes a plain schema object.

  • Maintainability | react-sdk/src/model/component-metadata.ts:71-71
    RegisterToolFn overloads are a good step toward recovering type safety, but the concrete TamboTool type has been reduced to tool: (...args: any[]) => any and toolSchema: unknown. This discards the relationship between the overload variants and the public TamboTool you actually store and pass around.

As written:

  • A user can construct a TamboTool manually (not via defineTamboTool) with completely mismatched tool and toolSchema types and pass it into registerTool without compiler complaints.
  • Downstream utilities like mapTamboToolToContextTool and tool-caller can no longer rely on value-level type information from TamboTool, so you fall back to any arguments and return values.

Given your goal of vendor‑agnostic schemas but strong typing for value‑level behavior, it would be more robust to make TamboTool itself generic over Args and Result with defaults, and have the schema field be structurally related but not strictly tied in the type system. defineTamboTool can then enforce alignment for the common cases while TamboTool remains a safer container type.

  • Maintainability | react-sdk/src/testing/tools.ts:1-1
    The new serializeRegistry implementation centralizes schema conversion for tools and components, which is good, but there are a couple of behavior changes and edge cases worth tightening:

  • All tools now serialize to a single args parameter instead of one parameter per argument. This is a breaking change from the previous Zod‑function-based behavior and may invalidate assumptions in any consumers that expect param1, param2, etc.

  • safeSchemaToJsonSchema can return undefined on conversion failure, but you propagate that directly into schema without surfacing the error. In snapshots or debugging output, an args parameter with schema: undefined is very hard to interpret.

  • For non‑Standard Schema inputs, you unconditionally treat toolSchema as JSON Schema without validating it via looksLikeJSONSchema, which is more permissive than the runtime registry code.

These differences between test serialization and runtime behavior can mask bugs or make tests pass when production would fail.

  • Maintainability | react-sdk/src/util/validate-schema.ts:147-185
    You’ve reintroduced a local isStandardSchema/JSON Schema detection in validate-schema.ts by checking for typeof schema === "object" && "type" in schema, even though you already have a richer looksLikeJSONSchema helper in schema.ts. Keeping detection logic duplicated in different modules is brittle and increases the risk of divergent behavior.

For example, if you later tighten looksLikeJSONSchema but forget to update this check, assertNoRecordSchema may treat some objects as JSON Schema that the rest of the codebase does not, or vice versa.

  • Maintainability | react-sdk/src/util/schema.ts:141-193
    isZod3FunctionSchema, isZod4FunctionSchema, and getZodFunctionArgs in schema.ts are a central part of your Zod function handling now, but they still rely on undocumented internals like _zod.def.input and _def.args. You’ve added some structure around them, which helps, but they are not fully type-safe and there’s no fallback when the expected properties are missing.

Given how much of the tool parameter logic now hangs off of these helpers, a small change in Zod’s internal representation could silently break argument extraction for function‑typed tools without any obvious error.

  • Maintainability | react-sdk/src/util/tool-caller.ts:78-81
    Wrapping registryTool.tool in an async arrow here changes the call signature from “possibly sync or async” to “always returns a Promise”, even if the underlying implementation is synchronous. That’s generally fine, but it means the type-level relationship between TamboTool.tool and ComponentContextTool.getComponentContext is now hidden behind any and a coercing wrapper.

Given that you’re already using MaybeAsync in component-metadata.ts, it would be more consistent to type getComponentContext as returning a Promise and have the compiler enforce this contract, rather than forcing the async boundary in this helper.

Summary of changes

Summary of Changes

  • Migrated backend and React SDK code to explicitly use zod/v3 and zod/v4 entry points instead of unversioned zod imports.
  • Introduced a new schema utility module (react-sdk/src/util/schema.ts) and tests (schema.test.ts) to support Standard Schema v1, vendor detection (Zod 3 & 4), JSON Schema heuristics, and conversion via @standard-community/standard-json.
  • Replaced Zod‑specific record validation (assertNoZodRecord) with generalized JSON Schema–based validation (assertNoRecordSchema) and corresponding tests.
  • Refactored the Tambo tool and component model (component-metadata.ts) to:
    • Support StandardSchemaV1 | JSONSchema7 (TamboSchema),
    • Add richer TamboTool* variants (Zod3/Zod4/StandardSchema/JSONSchema),
    • Introduce defineTamboTool helper and inference utilities.
  • Updated the registry, interactable providers, MCP provider, and testing utilities to:
    • Use the new schema utilities and type guards,
    • Treat tool and props schemas as Standard Schema or JSON Schema,
    • Serialize tools/components via JSON Schema, including Zod function arg extraction.
  • Tightened dependency metadata (especially around zod, zod-to-json-schema, and @standard-community/standard-json) in package-lock.json, packages/backend/package.json, packages/core/package.json, and react-sdk/package.json.
  • Updated README examples and tests to use zod/v4 and the new function schema API.
  • Removed legacy Zod‑specific validation files (validate-zod-schema.ts and its tests`) and replaced them with Standard Schema–aware equivalents.

Comment on lines 125 to 141
export interface RegisterToolFn {
<Args extends $ZodFunctionIn, Returns extends $ZodType>(
tool: TamboToolZod4Function<Args, Returns>,
warnOnOverwrite?: boolean,
): void;
<Args extends ZodTuple<any, any>, Returns extends ZodType>(
tool: TamboToolZod3Function<Args, Returns>,
warnOnOverwrite?: boolean,
): void;
<Args extends StandardSchemaV1<any[]>, Returns extends StandardSchemaV1>(
tool: TamboToolStandardSchema<Args, Returns>,
warnOnOverwrite?: boolean,
): void;
<Args extends unknown[], Returns = unknown>(
tool: TamboToolJSONSchema<Args, Returns>,
warnOnOverwrite?: boolean,
): void;
// Fallback for pre-defined tools typed as TamboTool union
(tool: TamboTool, warnOnOverwrite?: boolean): void;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

registerTool’s overloads and new TamboTool* variants are a good direction, but the implementation still treats tool.toolSchema as an opaque unknown and does not actually leverage the stronger generics to keep tool and toolSchema in sync.

Concretely:

  • A caller can still supply a tool whose runtime signature doesn’t match the schema (e.g. wrong arg count or result type) and TypeScript will not complain unless they go through defineTamboTool correctly.
  • Even with defineTamboTool, the registry happily stores tools that don’t conform to any of the variant-specific shapes, because the fallback overload (tool: TamboTool) accepts any TamboToolBase.

This undermines part of the type-safety you’re trying to win back after relaxing TamboTool earlier, and makes it easier for mis-typed tools to slip into the registry unnoticed.

Suggestion

To better enforce the relationship between tool, its arguments, and toolSchema, restrict the public registration API to the typed variants and route all call sites through them.

For example:

  • Export RegisterToolFn and defineTamboTool as the only supported ways to construct/register tools.
  • Make the fallback (tool: TamboTool) overload internal (or remove it) so external callers cannot bypass the schema/arg coupling.
  • In your own code, update remaining TamboTool literals to go through defineTamboTool so type-checking kicks in at definition time.

If you’d like, I can go through the React SDK and convert the remaining bare TamboTool literals to defineTamboTool usages and tighten the overload set accordingly. Reply with @CharlieHelps yes please if you want that commit.

feat(react): add standard schema support

chore(react): update zod deps according to recommended service

fix(react): harden Zod function helpers with shared schema guard

Use the shared `isZodSchema` type guard from `schema.ts` in the Zod
function helpers to avoid blindly poking at internal properties on
unknown values.

- Import and use `isZodSchema` in `isZod4FunctionSchema`,
  `isZod3FunctionSchema`, and `getZodFunctionArgs`
- Return early when the input does not look like a Zod schema
- Narrow internal access to `def` / `_def` only after the guard passes

This reduces the risk of runtime errors when tool schemas are not Zod
instances, which is increasingly important now that we support Standard
Schema and multiple schema shapes in the registry.

fix(react): tighten JSON schema detection

fix(react-sdk): treat any object with additionalProperties as record

Previously, `isRecordJsonSchema` only treated `object` schemas as records when they had `additionalProperties` and no explicit `properties`. This allowed mixed schemas (with both `properties` and `additionalProperties`) to slip through, even though they still represent dynamic-key records and are not supported by the Tambo backend.

This change:

- Updates `isRecordJsonSchema` to treat any `object` schema with `additionalProperties` defined as a schema object (not `true`/`false`) as a record, regardless of whether it also has `properties`.
- Adds a test to ensure schemas with both `properties` and `additionalProperties` are rejected with the existing record-type error message.

feat(react-sdk): add type safety/inference to registerTool function with TamboTool

fix(react-sdk): use versioned Zod function schema guards

apply changes from Charlie

remove complex type for TamboTool

remove type cast in favor of standard schema usage

feat(react-sdk): make tool schema extraction library-agnostic

- Add library-agnostic tuple detection via JSON Schema conversion
  (supports Zod 3/4, Valibot, ArkType, and any Standard Schema library)
- Deprecate z.function() format with runtime warning
- Recommend {args: z.tuple([...]), returns: z.type()} format instead
- Remove TamboToolZod4Function and TamboToolStandardSchema types
- Add comprehensive tests for Zod 3, Zod 4, Valibot, and JSON Schema
- Update all production code and test fixtures to new format

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@lachieh lachieh force-pushed the lachieh/move-to-standard-schema branch from 4b70bd3 to 4cd8c4b Compare December 5, 2025 15:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants