feat(codemode): support AI SDK jsonSchema wrapper in type generation#960
feat(codemode): support AI SDK jsonSchema wrapper in type generation#960mattzcarey wants to merge 15 commits intomainfrom
Conversation
MCP tools returned by getAITools() now have the _zod property on their inputSchema, which is required for codemode type generation. Previously, getAITools() used the AI SDK's jsonSchema() function to wrap MCP tool JSON schemas, which created Schema objects without the _zod property. This change uses Zod v4's fromJSONSchema() function instead, which converts JSON schemas to actual Zod schemas that include the _zod property. - Add static import of fromJSONSchema from zod - Replace this.jsonSchema() calls with fromJSONSchema() in getAITools() - Remove the jsonSchema initialization check (no longer needed) - Update tests to verify _zod property is present on tool inputSchema
|
commit: |
46ca09f to
9f2be18
Compare
- Remove unused json-schema-to-typescript dependency from codemode - Add comprehensive test for MCP tools with input AND output schemas - Add comprehensive test for AI SDK tool() with input AND output schemas - Remove redundant mixed tools test (covered by the two focused tests) - Both tests verify rich output types, not just 'unknown'
- Fix JSONSchema7 type incompatibility with fromJSONSchema parameter type - Remove unused JSONSchema7 import - Simplify test to use ToolDescriptors directly instead of AI SDK tool() - Fix Text component className prop error in codemode example
- Keep jsonSchema property on MCPClientManager (deprecated but functional) - ensureJsonSchema() still lazy-loads jsonSchema from AI SDK for compat - No breaking changes - existing code using manager.jsonSchema still works
Performance Analysis:
|
| Metric | fromJSONSchema() |
jsonSchema() |
Ratio |
|---|---|---|---|
| Simple schema (2 props) | 25 µs | 0.25 µs | ~100x slower |
| Complex MCP schema | 151 µs | 0.22 µs | ~685x slower |
| Deeply nested (5 levels) | 89 µs | 0.26 µs | ~342x slower |
| Memory per schema | ~559 KB | ~1 KB | ~559x more |
What this means in practice
For a typical MCP server with 10 tools:
fromJSONSchema(): ~1.5ms startup + ~5.5MB memoryjsonSchema(): ~0.002ms startup + ~10KB memory
However, this cost is paid once when getAITools() is called, not per-request. After initialization:
- Validation is fast: ~1 µs per call
- Schemas are cached in the returned tool definitions
Why we need fromJSONSchema()
jsonSchema()creates a thin wrapper without_zodpropertyfromJSONSchema()creates a real Zod schema with_zodproperty- The
_zodproperty is required forzod-to-tswhich codemode uses for type generation
Recommendation
keep jsonSchema and build a parser to support it.
…andling - Add safeZodToTs() wrapper that returns "unknown" for schemas that can't be represented in TypeScript (e.g., transforms) instead of throwing - Add schema-conversion.test.ts with 165 tests covering all Zod v4 types: primitives, literals, enums, objects, arrays, tuples, unions, intersections, records, maps, sets, modifiers, readonly, coerce, pipe, transform, template literals, lazy/recursive, functions, promises, branded types, effects/refinements, string/number validators, fromJSONSchema, and sanitizeToolName edge cases - Test coverage exceeds zod-to-ts package (~25 tests vs 165 tests)
These were used to validate fromJSONSchema() vs jsonSchema() performance: - fromJSONSchema: ~168µs (creates real Zod schema with _zod property) - jsonSchema: ~0.25µs (thin wrapper, no _zod property) The ~680x slower performance is acceptable since schema creation happens once at startup, not per-request. fromJSONSchema is required for codemode type generation which needs the _zod property.
Reverts MCP client to use fast jsonSchema wrapper (~0.25µs) instead of slower fromJSONSchema (~168µs). Type conversion is now handled in the codemode package's generateTypes function. Changes to codemode/types.ts: - Add schema detection: isZodSchema, isJsonSchemaWrapper, isRawJsonSchema - Add normalizeToZodSchema to convert any schema type to Zod - Extract JSON schema from AI SDK jsonSchema wrapper via symbol properties - Use fromJSONSchema internally only during type generation (one-time cost) - Support: Zod schemas, AI SDK jsonSchema wrapper, raw JSON schemas Performance: - MCP getAITools(): ~0.25µs per schema (fast jsonSchema wrapper) - generateTypes(): ~210µs for complex schemas (acceptable startup cost) Tests added: - AI SDK jsonSchema wrapper handling - Raw JSON Schema object handling
Replace fromJSONSchema-based conversion with direct JSON Schema to TypeScript string conversion. This avoids creating Zod validators when we only need type information. - Add jsonSchemaToTypeString() for direct conversion - Remove fromJSONSchema import (no longer needed) - Remove raw JSON Schema handling (only Zod + jsonSchema wrapper) - Support: objects, arrays, enums, unions, intersections, etc. Performance: - jsonSchema wrapper → TypeScript: ~6µs (was ~210µs, 35x faster) - Zod schema → TypeScript: ~32µs (uses zod-to-ts)
Remove 144 tests that were testing zod-to-ts library behavior. Keep 21 focused tests for our own code: - sanitizeToolName (10 tests) - generateTypes with jsonSchema wrapper (9 tests) - generateTypes with Zod schema (2 tests - integration only)
Summary
jsonSchemawrapper directly (~6µs)jsonSchemawrapper (~0.25µs per schema)Problem
The codemode
generateTypesfunction only worked with Zod schemas (requiring_zodproperty). MCP tools use the AI SDKjsonSchemawrapper which doesn't have this property.Solution
Update codemode converter to detect and handle both schema types:
zodToTs(~32µs)jsonSchemawrapper - direct JSON Schema → TypeScript (~6µs)Performance
getAITools()generateTypes()with jsonSchemagenerateTypes()with ZodChanges
packages/codemode/src/types.tsisJsonSchemaWrapper()- detect AI SDK wrapperextractJsonSchema()- extract JSON schema from wrapperjsonSchemaToTypeString()- direct JSON Schema → TypeScriptpackages/codemode/src/tests/schema-conversion.test.tsTest plan