Accuracy note (mt#3212, 2026-08-12). The code samples below are ILLUSTRATIVE of the intended pattern, and two of them no longer describe the live wiring. (a) The import paths are pre-mt#2108: the schemas moved to
packages/domain/src/schemas/, notsrc/domain/schemas/. (b) §MCP Integration shows a tool derivinginputSchemafromSessionStartParametersSchema. No tool does. The MCP and CLI surfaces are generated from the shared command registry — forsession.startthat is the hand-declaredsessionStartCommandParamsmap insrc/adapters/shared/commands/session/session-parameters.ts— and the domain schemas have no consumer outsidepackages/domain. That separation is now pinned by a test (session-parameter-family-parity.test.ts), so a reader can check it rather than trust this note. Rewriting the document is out of mt#3212's scope; this note bounds what it can be relied on for.
This document describes the domain-wide schema architecture implemented in Task #329, which extends the type composition patterns from Task #322 to create interface-agnostic schemas that can be used across CLI, MCP, and future API interfaces.
The domain schemas are organized into modular libraries in src/domain/schemas/:
common-schemas.ts- Base cross-interface types and utilitiestask-schemas.ts- Task domain operationssession-schemas.ts- Session domain operationsfile-schemas.ts- File domain operationsvalidation-utils.ts- Cross-interface validation utilitiesindex.ts- Central export point
All schemas are designed to work across CLI, MCP, and API interfaces without modification:
// Same schema works for CLI, MCP, and API
import { TaskCreateParametersSchema } from "src/domain/schemas";
// CLI usage
const cliValidation = validateCliArguments(TaskCreateParametersSchema, cliArgs, "tasks create");
// MCP usage
const mcpValidation = validateMcpArguments(TaskCreateParametersSchema, mcpArgs, "tasks.create");
// API usage
const apiValidation = validateApiRequest(TaskCreateParametersSchema, requestBody, "POST /tasks");Schemas are built through composition of smaller, reusable components:
// Base components
export const TaskIdSchema = z.string().min(1, "Task ID cannot be empty");
export const BaseBackendParametersSchema = z.object({
backend: BackendIdSchema.optional(),
repo: RepoIdSchema.optional(),
// ...
});
// Composed schemas
export const TaskGetParametersSchema = z
.object({
taskId: TaskIdSchema,
})
.merge(BaseBackendParametersSchema)
.merge(BaseExecutionContextSchema);import {
TaskCreateParametersSchema,
TaskListParametersSchema,
validateOperationParameters,
createSuccessResponse,
} from "src/domain/schemas";
// Validate task creation parameters
const createParams = {
title: "Implement feature X",
description: "Add new functionality",
priority: "high",
backend: "local",
};
const validation = validateOperationParameters(
TaskCreateParametersSchema,
createParams,
"task creation"
);
if (validation.success) {
// Use validated data
const task = await createTask(validation.data);
return createSuccessResponse({ task });
} else {
// Handle validation error
return createValidationErrorResponse(validation, "task creation");
}import {
SessionStartParametersSchema,
SessionPRParametersSchema,
validateOperationParameters,
} from "src/domain/schemas";
// Start a new session
const sessionParams = {
name: "feature-branch",
description: "Working on feature X",
task: "123",
packageManager: "bun",
};
const validation = validateOperationParameters(
SessionStartParametersSchema,
sessionParams,
"session start"
);import {
FileReadSchema,
FileWriteSchema,
DirectoryListSchema,
validateOperationParameters,
} from "src/domain/schemas";
// Read a file with line range
const readParams = {
sessionId: "my-session",
path: "src/file.ts",
start_line_one_indexed: 10,
end_line_one_indexed_inclusive: 20,
};
const validation = validateOperationParameters(FileReadSchema, readParams, "file read");// In CLI command handler
import { validateCliArguments, TaskCreateParametersSchema } from "src/domain/schemas";
export function createTaskCommand() {
return new Command("create")
.option("--title <title>", "Task title")
.option("--description <description>", "Task description")
.option("--priority <priority>", "Task priority")
.action(async (options) => {
const validation = validateCliArguments(TaskCreateParametersSchema, options, "tasks create");
if (!validation.success) {
console.error(validation.error);
process.exit(1);
}
// Use validated parameters
await handleTaskCreation(validation.data);
});
}// In MCP tool implementation
import { validateMcpArguments, SessionStartParametersSchema } from "src/domain/schemas";
export const sessionStartTool = {
name: "session.start",
description: "Start a new development session",
inputSchema: zodToJsonSchema(SessionStartParametersSchema),
handler: async (args: unknown) => {
const validation = validateMcpArguments(SessionStartParametersSchema, args, "session.start");
if (!validation.success) {
return createValidationErrorResponse(validation, "session start");
}
// Use validated parameters
const result = await startSession(validation.data);
return createSuccessResponse({ session: result });
},
};// In API route handler
import { validateApiRequest, TaskListParametersSchema } from "src/domain/schemas";
app.get("/api/tasks", async (req, res) => {
const validation = validateApiRequest(TaskListParametersSchema, req.query, "GET /api/tasks");
if (!validation.success) {
return res.status(400).json(createValidationErrorResponse(validation));
}
// Use validated parameters
const tasks = await listTasks(validation.data);
res.json(createSuccessResponse({ tasks }));
});- Single source of truth for domain concepts
- Same validation logic across all interfaces
- Consistent parameter structures everywhere
- Full TypeScript coverage for all parameters and responses
- Compile-time validation of schema usage
- Autocomplete and IntelliSense support
- Easy updates - change once, applies everywhere
- Clear separation between interface and domain logic
- Standardized error handling patterns
- Identical behavior across CLI, MCP, and API
- Uniform response formats for all interfaces
- Standard validation messages and error codes
- Easy to add new interfaces using existing schemas
- Simple to extend existing schemas with new fields
- Modular composition allows flexible combinations
The domain-wide schemas replace the MCP-specific schemas from Task #322:
// Before (MCP-specific)
import { SessionIdentifierSchema } from "src/adapters/mcp/schemas/common-parameters";
// After (domain-wide)
import { SessionIdSchema } from "src/domain/schemas";- Replace MCP imports with domain schema imports
- Use validation utilities for consistent error handling
- Leverage response builders for standardized responses
- Apply to CLI and API interfaces using the same schemas
This architecture is designed to support future enhancements:
- Additional Domains - Easy to add git, config, or other domain schemas
- API Interfaces - Ready for REST API or GraphQL implementations
- Advanced Validation - Can extend with custom validation rules
- Schema Evolution - Supports versioning and migration patterns
The domain-wide schema architecture provides a solid foundation for type-safe, consistent, and maintainable interfaces across the entire Minsky application. By centralizing domain concepts and validation logic, it reduces duplication, improves reliability, and makes the codebase more approachable for new developers.