From outputai
Fixes missing inputSchema and outputSchema definitions in Output SDK steps to resolve type errors, undefined properties at boundaries, validation failures, and poor typing.
npx claudepluginhub growthxai/output --plugin outputaiThis skill is limited to using the following tools:
This skill helps diagnose and fix issues caused by steps that lack explicit `inputSchema` or `outputSchema` definitions. Schemas are essential for type safety, validation, and proper data serialization between steps.
Fixes Zod schema import issues in Output SDK workflows by replacing 'zod' imports with '@outputai/core'. Use for incompatible schema errors, type errors at step boundaries, and validation failures.
Mandates invoking relevant skills via tools before any response in coding sessions. Covers access, priorities, and adaptations for Claude Code, Copilot CLI, Gemini CLI.
Share bugs, ideas, or general feedback.
This skill helps diagnose and fix issues caused by steps that lack explicit inputSchema or outputSchema definitions. Schemas are essential for type safety, validation, and proper data serialization between steps.
You're seeing:
Steps without explicit schemas:
// WRONG: No input validation
export const processData = step({
name: 'processData',
// inputSchema: missing!
outputSchema: z.object({ result: z.string() }),
fn: async (input) => {
return { result: input.value }; // input.value might be undefined!
}
});
// WRONG: No output validation
export const fetchData = step({
name: 'fetchData',
inputSchema: z.object({ id: z.string() }),
// outputSchema: missing!
fn: async (input) => {
return { data: await getFromApi(input.id) }; // Output shape not validated
}
});
// WRONG: No validation at all
export const transformData = step({
name: 'transformData',
// No schemas!
fn: async (input) => {
return transform(input);
}
});
Always define both inputSchema and outputSchema for every step:
import { z, step } from '@outputai/core';
export const processData = step({
name: 'processData',
inputSchema: z.object({
id: z.string(),
value: z.number(),
optional: z.string().optional(),
}),
outputSchema: z.object({
result: z.string(),
processedAt: z.number(),
}),
fn: async (input) => {
// input is fully typed: { id: string, value: number, optional?: string }
return {
result: `Processed ${input.id}`,
processedAt: Date.now(),
};
// output is validated against outputSchema
},
});
// Good: Clear, descriptive schema
inputSchema: z.object({
userId: z.string().uuid(),
email: z.string().email(),
age: z.number().int().positive(),
})
inputSchema: z.object({
required: z.string(),
optional: z.string().optional(),
withDefault: z.string().default('fallback'),
})
// Define reusable schemas
const userSchema = z.object({
id: z.string(),
name: z.string(),
});
const addressSchema = z.object({
street: z.string(),
city: z.string(),
});
// Compose in step
inputSchema: z.object({
user: userSchema,
address: addressSchema,
})
inputSchema: z.object({
items: z.array(z.object({
id: z.string(),
quantity: z.number(),
})),
metadata: z.record(z.string()),
})
Search your codebase:
# Find step definitions
grep -rn "step({" src/workflows/
# Look for steps without inputSchema
grep -A5 "step({" src/workflows/ | grep -B2 "fn:"
# Check if schemas are present
grep -rn "inputSchema:" src/workflows/
grep -rn "outputSchema:" src/workflows/
Review each step definition to ensure both schemas are present.
export const fetchUser = step({
name: 'fetchUser',
inputSchema: z.object({
userId: z.string(),
}),
outputSchema: z.object({
user: z.object({
id: z.string(),
name: z.string(),
email: z.string(),
}).nullable(), // Handle not found
found: z.boolean(),
}),
fn: async (input) => {
const user = await api.getUser(input.userId);
return { user, found: user !== null };
},
});
export const transformData = step({
name: 'transformData',
inputSchema: z.object({
raw: z.array(z.unknown()),
}),
outputSchema: z.object({
processed: z.array(z.object({
id: z.string(),
value: z.number(),
})),
count: z.number(),
}),
fn: async (input) => {
const processed = input.raw.map(transformItem);
return { processed, count: processed.length };
},
});
For steps that don't return meaningful data:
export const logEvent = step({
name: 'logEvent',
inputSchema: z.object({
event: z.string(),
data: z.record(z.unknown()),
}),
outputSchema: z.object({
logged: z.literal(true),
}),
fn: async (input) => {
await logger.log(input.event, input.data);
return { logged: true };
},
});
After adding schemas:
npm run output:worker:build should pass without type errorsnpx output workflow run <name> '<input>' should validate correctlyoutput-error-zod-import