Schema-Driven Dispatch
This document specifies the schema-driven dispatch pattern that enables declarative operation definitions with automatic parameter resolution, validation, and handler invocation. This is an optional but recommended patte
On this page
Jump to a section
Use the outline to move through longer pages without losing your place.
- Abstract
- 1. Introduction
- 1.1 Purpose
- 1.2 Benefits
- 1.3 When to Use
- 2. Operation Schema
- 2.1 Schema Structure
- 2.2 Parameter Definition
- 2.3 Example Schema Definition
- 3. Parameter Resolution
- 3.1 Resolution Algorithm
- 3.2 Source Path Syntax
- 3.3 Resolution Examples
- 4. Handler Invocation
- 4.1 Parameter Styles
- 4.2 Handler Reference
- 5. Security Considerations
- 5.1 Path Traversal Protection
- 5.2 Input Validation
- 5.3 Value Validation
- 6. Introspection Integration
- 6.1 Auto-Generated Responses
- 6.2 Single Source of Truth
- References
Version: 1.0.0-draft Status: Draft Last Updated: 2026-01-26 Moved from: MCPAQL/spec (formerly docs/architecture/schema-driven-dispatch.md)
Note: This document describes an implementation pattern for adapter dispatch. For normative protocol specifications, see the spec repository.
Abstract
This document specifies the schema-driven dispatch pattern that enables declarative operation definitions with automatic parameter resolution, validation, and handler invocation. This is an optional but recommended pattern for adapter implementation.
1. Introduction
1.1 Purpose
Schema-driven dispatch separates the definition of operations from their implementation, providing:
- Declarative configuration - Operations defined as data, not code
- Automatic validation - Parameters validated against schema
- Flexible resolution - Parameters found from multiple sources
- Generated documentation - Introspection responses auto-generated
- Consistent behavior - All operations follow same patterns
1.2 Benefits
| Aspect | Traditional | Schema-Driven |
|---|---|---|
| Adding operations | Code changes | Schema entry |
| Parameter validation | Manual code | Automatic |
| Introspection | Manual sync | Auto-generated |
| Documentation | Separate files | Single source |
| Testing | Per-operation | Schema-based |
1.3 When to Use
Schema-driven dispatch is recommended when:
- Adapter has many operations
- Operations have similar parameter patterns
- Introspection accuracy is critical
- Consistency is valued over flexibility
2. Operation Schema
2.1 Schema Structure
interface OperationSchema {
// Identity
operation: string; // Operation name (snake_case)
endpoint: CRUDEndpoint; // CRUDE endpoint routing
// Handler
handler: HandlerReference; // How to invoke handler
paramStyle: ParamStyle; // Argument passing pattern
// Parameters
params: ParamDefinition[]; // Parameter specifications
// Documentation
description: string; // Operation description
examples?: Example[]; // Usage examples
}
type CRUDEndpoint = 'CREATE' | 'READ' | 'UPDATE' | 'DELETE' | 'EXECUTE';
type ParamStyle = 'single' | 'named' | 'spread';2.2 Parameter Definition
interface ParamDefinition {
// Identity
name: string; // Parameter name (snake_case)
// Type
type: ParamType; // string, number, boolean, object, array
// Requirements
required: boolean;
default?: unknown;
// Resolution
sources: string[]; // Resolution paths in priority order
// Validation
enum?: string[]; // Allowed values
minLength?: number;
maxLength?: number;
min?: number;
max?: number;
pattern?: string; // Regex pattern
// Documentation
description: string;
}
type ParamType = 'string' | 'number' | 'boolean' | 'object' | 'array';2.3 Example Schema Definition
const CREATE_USER_SCHEMA: OperationSchema = {
operation: 'create_user',
endpoint: 'CREATE',
handler: 'Users.create',
paramStyle: 'named',
params: [
{
name: 'email',
type: 'string',
required: true,
sources: ['params.email', 'email'],
description: 'User email address'
},
{
name: 'name',
type: 'string',
required: true,
sources: ['params.name', 'name'],
description: 'User display name'
},
{
name: 'role',
type: 'string',
required: false,
sources: ['params.role', 'role'],
default: 'user',
enum: ['admin', 'user', 'guest'],
description: 'User role'
}
],
description: 'Create a new user account',
examples: [
{
name: 'Basic user',
request: {
operation: 'create_user',
params: {
email: 'alice@example.com',
name: 'Alice'
}
}
}
]
};3. Parameter Resolution
3.1 Resolution Algorithm
For each parameter defined in the schema:
1. FOR each source in param.sources (in order):
a. Parse source path (e.g., "params.email")
b. Traverse input object along path
c. IF value found AND not undefined:
RETURN value
2. IF no value found:
a. IF param.default defined:
RETURN param.default
b. IF param.required:
THROW MissingParameterError
c. ELSE:
RETURN undefined
3.2 Source Path Syntax
Source paths use dot notation to traverse the input object:
| Path | Resolves From |
|---|---|
email |
input.email |
params.email |
input.params.email |
params.options.format |
input.params.options.format |
3.3 Resolution Examples
Input:
{
operation: "create_user",
params: {
email: "alice@example.com",
name: "Alice"
}
}Resolution with sources ['params.email', 'email']:
- Check
input.params.email→ "alice@example.com" ✓ - Return "alice@example.com"
Resolution with sources ['params.role', 'role'] and default "user":
- Check
input.params.role→ undefined - Check
input.role→ undefined - Return default "user"
4. Handler Invocation
4.1 Parameter Styles
The paramStyle field determines how resolved parameters are passed to handlers.
4.1.1 Named Style
Pass all parameters as a single object.
// Schema: paramStyle: 'named'
// Invocation:
handler({ email, name, role })Use case: Most common - handlers with multiple parameters.
4.1.2 Single Style
Pass parameters as individual arguments in schema order.
// Schema: paramStyle: 'single', params: [email, name, role]
// Invocation:
handler(email, name, role)Use case: Simple handlers with fixed signatures.
4.1.3 Spread Style
Pass primary parameter separately from options object.
// Schema: paramStyle: 'spread', params: [query, limit, offset]
// Invocation:
handler(query, { limit, offset })Use case: Search operations with query + options.
4.2 Handler Reference
Handlers are referenced using strings that the adapter resolves:
// Common patterns:
"Users.create" // Module.method
"handlers/users:create" // Path:method
"createUser" // Direct function nameThe resolution mechanism is adapter-specific.
5. Security Considerations
5.1 Path Traversal Protection
Source paths MUST be validated to prevent prototype pollution:
const SAFE_PATH_PATTERN = /^[a-zA-Z_$][a-zA-Z0-9_$.]*$/;
const FORBIDDEN_PATHS = new Set([
'__proto__',
'constructor',
'prototype'
]);
function validatePath(path: string): boolean {
if (!SAFE_PATH_PATTERN.test(path)) return false;
const segments = path.split('.');
for (const segment of segments) {
if (FORBIDDEN_PATHS.has(segment)) return false;
}
return true;
}5.2 Input Validation
Before resolution, input MUST be validated:
- Type check - Input is object
- Operation check -
operationfield is string - Depth limit - Nested objects limited (default: 10 levels)
- Size limit - Total input size limited
5.3 Value Validation
After resolution, values MUST be validated against schema:
function validateValue(value: unknown, param: ParamDefinition): void {
// Type validation
if (param.type === 'string' && typeof value !== 'string') {
throw new ValidationError(`${param.name} must be string`);
}
// Enum validation
if (param.enum && !param.enum.includes(value as string)) {
throw new ValidationError(`${param.name} must be one of: ${param.enum.join(', ')}`);
}
// Length validation
if (param.minLength && (value as string).length < param.minLength) {
throw new ValidationError(`${param.name} must be at least ${param.minLength} characters`);
}
}6. Introspection Integration
6.1 Auto-Generated Responses
Operation schemas directly generate introspection responses:
function generateOperationInfo(schema: OperationSchema): OperationInfo {
return {
name: schema.operation,
endpoint: schema.endpoint,
description: schema.description,
parameters: schema.params.map(p => ({
name: p.name,
type: p.type,
required: p.required,
default: p.default,
description: p.description,
enum: p.enum
})),
examples: schema.examples
};
}6.2 Single Source of Truth
The operation schema serves as the canonical source for:
- Parameter definitions
- Validation rules
- Introspection responses
- Documentation generation
Changes to the schema automatically propagate to all consumers.