import { z } from "zod";
// Build tool schema
export const BuildArgsSchema = z.object({
configuration: z.string().optional().describe("Device configuration name"),
configPath: z.string().optional().describe("Path to Detox config file"),
ifMissing: z.boolean().optional().default(false).describe("Only build if app binary is missing"),
silent: z.boolean().optional().default(false).describe("Don't fail if no build command exists"),
cwd: z.string().optional().describe("Working directory"),
});
export type BuildArgs = z.infer<typeof BuildArgsSchema>;
// Test tool schema
export const TestArgsSchema = z.object({
configuration: z.string().optional().describe("Device configuration name"),
testFilePaths: z.array(z.string()).optional().describe("Specific test files to run"),
testNamePattern: z.string().optional().describe("Regex pattern to filter tests"),
deviceName: z.string().optional().describe("Override device name"),
loglevel: z
.enum(["fatal", "error", "warn", "info", "verbose", "trace"])
.optional()
.default("info"),
retries: z.number().optional().describe("Number of times to retry failing tests"),
reuse: z.boolean().optional().describe("Reuse existing app installation"),
headless: z.boolean().optional().describe("Run in headless mode"),
recordLogs: z.enum(["failing", "all", "none"]).optional().default("failing"),
takeScreenshots: z.enum(["manual", "failing", "all", "none"]).optional().default("failing"),
recordVideos: z.enum(["failing", "all", "none"]).optional().default("none"),
artifactsLocation: z.string().optional().describe("Custom path for artifacts"),
cleanup: z.boolean().optional().describe("Shutdown simulator after tests"),
cwd: z.string().optional().describe("Working directory"),
});
export type TestArgs = z.infer<typeof TestArgsSchema>;
// Init tool schema
export const InitArgsSchema = z.object({
projectPath: z.string().describe("Path to React Native project root"),
testRunner: z.enum(["jest", "mocha"]).optional().default("jest"),
});
export type InitArgs = z.infer<typeof InitArgsSchema>;
// Config read schema
export const ReadConfigArgsSchema = z.object({
projectPath: z.string().optional().describe("Path to project root"),
configPath: z.string().optional().describe("Explicit path to config file"),
});
export type ReadConfigArgs = z.infer<typeof ReadConfigArgsSchema>;
// List configurations schema
export const ListConfigurationsArgsSchema = z.object({
projectPath: z.string().optional().describe("Path to project root"),
});
export type ListConfigurationsArgs = z.infer<typeof ListConfigurationsArgsSchema>;
// Validate config schema
export const ValidateConfigArgsSchema = z.object({
projectPath: z.string().optional().describe("Path to project root"),
configuration: z.string().optional().describe("Specific configuration to validate"),
});
export type ValidateConfigArgs = z.infer<typeof ValidateConfigArgsSchema>;
// Create config schema
export const CreateConfigArgsSchema = z.object({
projectPath: z.string().describe("Path to project root"),
platforms: z.array(z.enum(["ios", "android"])).describe("Platforms to configure"),
appName: z.string().optional().describe("Application name"),
bundleId: z.string().optional().describe("iOS bundle identifier"),
packageName: z.string().optional().describe("Android package name"),
});
export type CreateConfigArgs = z.infer<typeof CreateConfigArgsSchema>;
// Device list schema
export const ListDevicesArgsSchema = z.object({
platform: z.enum(["ios", "android", "all"]).optional().default("all"),
availableOnly: z.boolean().optional().default(false),
});
export type ListDevicesArgs = z.infer<typeof ListDevicesArgsSchema>;
// Generate test schema
export const GenerateTestArgsSchema = z.object({
description: z.string().describe("Natural language description of the test scenario"),
testName: z.string().describe("Name for the test/describe block"),
outputPath: z.string().optional().describe("Where to write the generated test file"),
includeSetup: z.boolean().optional().default(true),
platform: z.enum(["ios", "android", "cross-platform"]).optional().default("cross-platform"),
});
export type GenerateTestArgs = z.infer<typeof GenerateTestArgsSchema>;
// Generate matcher schema
export const GenerateMatcherArgsSchema = z.object({
elementDescription: z.string().describe("Description of the element to match"),
matcherType: z.enum(["id", "text", "label", "type", "traits"]).optional(),
withAncestor: z.string().optional().describe("Ancestor matcher"),
withDescendant: z.string().optional().describe("Descendant matcher"),
atIndex: z.number().optional().describe("Index for multiple matches"),
});
export type GenerateMatcherArgs = z.infer<typeof GenerateMatcherArgsSchema>;
// Generate action schema
export const GenerateActionArgsSchema = z.object({
actionType: z
.enum([
"tap",
"multiTap",
"longPress",
"typeText",
"replaceText",
"clearText",
"scroll",
"scrollTo",
"swipe",
"pinch",
])
.describe("Type of action"),
elementMatcher: z.string().describe("Matcher code or description"),
actionParams: z
.object({
text: z.string().optional(),
times: z.number().optional(),
duration: z.number().optional(),
direction: z.enum(["up", "down", "left", "right"]).optional(),
speed: z.enum(["fast", "slow"]).optional(),
offset: z.number().optional(),
edge: z.enum(["top", "bottom", "left", "right"]).optional(),
})
.optional(),
});
export type GenerateActionArgs = z.infer<typeof GenerateActionArgsSchema>;
// Generate expectation schema
export const GenerateExpectationArgsSchema = z.object({
expectationType: z
.enum([
"toBeVisible",
"toExist",
"toBeFocused",
"toHaveText",
"toHaveLabel",
"toHaveId",
"toHaveValue",
"toHaveSliderPosition",
"toHaveToggleValue",
])
.describe("Type of expectation"),
elementMatcher: z.string().describe("Matcher code or description"),
expectedValue: z.string().optional().describe("Expected value"),
negated: z.boolean().optional().default(false),
timeout: z.number().optional().describe("Custom timeout in ms"),
visibilityThreshold: z.number().min(1).max(100).optional(),
});
export type GenerateExpectationArgs = z.infer<typeof GenerateExpectationArgsSchema>;
// Screenshot schema
export const TakeScreenshotArgsSchema = z.object({
name: z.string().optional().describe("Name for screenshot file"),
configuration: z.string().describe("Detox configuration to use"),
outputPath: z.string().optional().describe("Custom output directory"),
});
export type TakeScreenshotArgs = z.infer<typeof TakeScreenshotArgsSchema>;
// List artifacts schema
export const ListArtifactsArgsSchema = z.object({
artifactsPath: z.string().optional().describe("Path to artifacts directory"),
type: z.enum(["screenshots", "videos", "logs", "hierarchy", "all"]).optional().default("all"),
testName: z.string().optional().describe("Filter by test name"),
});
export type ListArtifactsArgs = z.infer<typeof ListArtifactsArgsSchema>;
// Helper to convert Zod schema to JSON Schema for MCP
export function zodToJsonSchema(schema: z.ZodType<any>): object {
// Use zod's built-in JSON schema generation capabilities
// For now, we'll return a simplified version
const shape = (schema as any)._def?.shape?.();
if (!shape) {
return { type: "object", properties: {} };
}
const properties: Record<string, any> = {};
const required: string[] = [];
for (const [key, value] of Object.entries(shape)) {
const zodType = value as z.ZodType<any>;
const def = (zodType as any)._def;
let prop: any = {};
if (def.typeName === "ZodString") {
prop.type = "string";
} else if (def.typeName === "ZodNumber") {
prop.type = "number";
} else if (def.typeName === "ZodBoolean") {
prop.type = "boolean";
} else if (def.typeName === "ZodArray") {
prop.type = "array";
prop.items = { type: "string" };
} else if (def.typeName === "ZodEnum") {
prop.type = "string";
prop.enum = def.values;
} else if (def.typeName === "ZodOptional") {
const innerDef = def.innerType._def;
if (innerDef.typeName === "ZodString") {
prop.type = "string";
} else if (innerDef.typeName === "ZodNumber") {
prop.type = "number";
} else if (innerDef.typeName === "ZodBoolean") {
prop.type = "boolean";
} else if (innerDef.typeName === "ZodEnum") {
prop.type = "string";
prop.enum = innerDef.values;
} else if (innerDef.typeName === "ZodDefault") {
const defaultInner = innerDef.innerType._def;
if (defaultInner.typeName === "ZodString") {
prop.type = "string";
} else if (defaultInner.typeName === "ZodBoolean") {
prop.type = "boolean";
} else if (defaultInner.typeName === "ZodEnum") {
prop.type = "string";
prop.enum = defaultInner.values;
}
prop.default = innerDef.defaultValue();
}
} else if (def.typeName === "ZodDefault") {
const innerDef = def.innerType._def;
if (innerDef.typeName === "ZodString") {
prop.type = "string";
} else if (innerDef.typeName === "ZodBoolean") {
prop.type = "boolean";
} else if (innerDef.typeName === "ZodEnum") {
prop.type = "string";
prop.enum = innerDef.values;
}
prop.default = def.defaultValue();
}
if (def.description) {
prop.description = def.description;
} else if (def.innerType?._def?.description) {
prop.description = def.innerType._def.description;
}
properties[key] = prop;
if (def.typeName !== "ZodOptional" && def.typeName !== "ZodDefault") {
required.push(key);
}
}
return {
type: "object",
properties,
required: required.length > 0 ? required : undefined,
};
}