Fabric System
Prerequisites: Understanding of service handlers
Overview
Section titled “Overview”The Fabric system provides a pattern for creating handlers with typed inputs that automatically convert values and work across CLI, Lambda, LLM, and MCP contexts.
Core Concept
Section titled “Core Concept”Service handlers define their input schema declaratively, enabling:
- Type conversion -
"true"becomestrue,"42"becomes42 - Validation - Required fields, custom validators
- Adapter generation - Auto-generate CLI options, LLM tool schemas, etc.
Service Handler
Section titled “Service Handler”Definition
Section titled “Definition”import { fabricService } from "@jaypie/fabric";
const createUser = fabricService({ alias: "create_user", description: "Create a new user", input: { name: { type: String, required: true, description: "User's full name", }, email: { type: String, required: true, description: "User's email address", }, age: { type: Number, required: false, default: 0, description: "User's age", }, }, service: async ({ name, email, age }) => { return await db.users.create({ name, email, age }); },});Input Types
Section titled “Input Types”| Type | Description | Conversion |
|---|---|---|
String |
Text value | .toString() |
Number |
Numeric value | parseFloat(), NaN → default |
Boolean |
True/false | "true", "1", 1 → true |
Array |
List of values | Single → [value] |
Object |
Object value | JSON parse if string |
Date |
Date value | new Date() |
Type Conversion Philosophy
Section titled “Type Conversion Philosophy”“Smooth, pliable” - inputs should work naturally:
// All these become boolean truehandler({ active: true });handler({ active: "true" });handler({ active: "TRUE" });handler({ active: 1 });handler({ active: "1" });
// All these become number 42handler({ count: 42 });handler({ count: "42" });handler({ count: "42.0" });
// Single values become arrayshandler({ tags: "javascript" });// Receives: { tags: ["javascript"] }Invalid Values
Section titled “Invalid Values”Invalid conversions fail fast with clear errors:
// Throws BadRequestErrorhandler({ count: "not-a-number" });handler({ email: null }); // if requiredAdapters
Section titled “Adapters”Commander CLI
Section titled “Commander CLI”import { fabricCommand } from "@jaypie/fabric/commander";
fabricCommand({ service: createUser, program });
// Generates:// my-cli create_user --name "Alice" --email "alice@example.com" --age 25Lambda
Section titled “Lambda”import { fabricLambda } from "@jaypie/fabric/lambda";
export const handler = fabricLambda({ service: createUser, secrets: ["MONGODB_URI"],});
// Extracts input from:// - Direct: event.name, event.email// - API Gateway: body, queryStringParameters// - SQS: Records[].bodyLLM Tool
Section titled “LLM Tool”import { fabricTool } from "@jaypie/fabric/llm";
const { tool } = fabricTool({ service: createUser });// Generates JSON Schema for tool parametersMCP Tool
Section titled “MCP Tool”import { fabricMcp } from "@jaypie/fabric/mcp";
fabricMcp({ service: createUser, server });// Registers as MCP tool with schemaJSON Schema Mapping
Section titled “JSON Schema Mapping”| Fabric | JSON Schema |
|---|---|
String |
{ type: "string" } |
Number |
{ type: "number" } |
Boolean |
{ type: "boolean" } |
Array |
{ type: "array" } |
Object |
{ type: "object" } |
Date |
{ type: "string", format: "date-time" } |
Required fields go in JSON Schema required array.
Validation
Section titled “Validation”Built-in
Section titled “Built-in”{ input: { email: { type: String, required: true, validate: (value) => value.includes("@"), }, },}Custom Validator
Section titled “Custom Validator”{ input: { age: { type: Number, validate: (value) => { if (value < 0) throw BadRequestError("Age cannot be negative"); if (value > 150) throw BadRequestError("Age too high"); return true; }, }, },}Callbacks
Section titled “Callbacks”Adapters support lifecycle callbacks:
fabricCommand({ service: handler, program, onMessage: (message) => { // Progress updates console.log(message); }, onComplete: (result) => { // Success console.log(JSON.stringify(result, null, 2)); }, onError: (error) => { // Handled error console.error(error.message); }, onFatal: (error) => { // Unhandled error console.error("Fatal:", error); process.exit(1); },});Error Handling
Section titled “Error Handling”Errors propagate through all adapters:
| Context | Behavior |
|---|---|
| CLI | Print message, exit code 1 |
| Lambda | Return error response |
| LLM | Return error to model |
| MCP | Return MCP error |
Building Adapters
Section titled “Building Adapters”Adapter Pattern
Section titled “Adapter Pattern”function myAdapter(service, options) { return async (rawInput) => { // 1. Extract input from context const extracted = extractInput(rawInput);
// 2. Convert types (handled by fabricService) // 3. Validate (handled by fabricService)
// 4. Execute handler try { const result = await service(extracted); options.onComplete?.(result); return result; } catch (error) { options.onError?.(error); throw error; } };}Related
Section titled “Related”- Fabric - Full API reference
- @jaypie/llm - LLM integration
- @jaypie/mcp - MCP integration
- @jaypie/lambda - Lambda handlers