fix: extract and serialize JSON Schema from AI SDK v6 tools correctly

THE BREAKTHROUGH: AI SDK v6 tools use jsonSchema() which wraps plain JSON Schema objects, NOT Zod schemas. JSON Schema is fully serializable.

Changes:
1. Railway server: Extract raw JSON Schema from toolModule.inputSchema?.schema
2. Playground loader: Wrap received JSON Schema with { type: 'json_schema', schema: ... }
3. This matches AI SDK v6 format exactly - no Zod serialization needed

How it works:
- Tools define inputSchema: jsonSchema({ type: 'object', properties: {...} })
- AI SDK stores it as { type: 'json_schema', schema: {...} }
- Railway extracts the plain JSON Schema (.schema property)
- Sends it as plain JSON (fully serializable)
- Playground wraps it back in AI SDK format
- OpenAI receives valid JSON Schema for function calling

This fixes both errors:
 No more "def.shape is not a function" (not using Zod)
 No more "Invalid schema type None" (proper JSON Schema provided)

Note: Tools using Zod instead of jsonSchema() will need to migrate.
TPMJS standard: All tools MUST use jsonSchema() with plain JSON Schema.

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ajax Davis 2025-12-04 09:00:00 +10:00
parent 287d9b97c9
commit bb672edf15
2 changed files with 9 additions and 6 deletions

View file

@ -67,10 +67,12 @@ export async function loadToolDynamically(
console.log(`📋 Description: ${data.tool.description}`);
// Create a tool wrapper that executes remotely
// Note: We don't include inputSchema because Zod schemas can't be serialized over HTTP
// The AI SDK will infer parameters from the description
// Railway returns plain JSON Schema - wrap it in the AI SDK jsonSchema format
const tool = {
description: data.tool.description,
inputSchema: data.tool.inputSchema
? { type: 'json_schema' as const, schema: data.tool.inputSchema }
: undefined,
// biome-ignore lint/suspicious/noExplicitAny: Tool params are dynamic
execute: async (params: any) => {
console.log(`🚀 Executing ${packageName}/${exportName} remotely with params:`, params);

View file

@ -76,15 +76,16 @@ async function loadAndDescribe(req: Request): Promise<Response> {
}
// Extract tool definition
// Note: We don't send inputSchema because Zod schemas can't be serialized over JSON
// The playground will use the actual tool's inputSchema when creating the wrapper
// AI SDK v6 tools use jsonSchema() which wraps a plain JSON Schema object
// Extract the raw JSON Schema from toolModule.inputSchema.schema
const rawJsonSchema = toolModule.inputSchema?.schema ?? null;
return Response.json({
success: true,
tool: {
exportName,
description: toolModule.description,
// Store reference that inputSchema exists (for validation)
hasInputSchema: !!toolModule.inputSchema,
inputSchema: rawJsonSchema, // Plain JSON Schema - fully serializable
},
});
} catch (error) {