.do
McpTools

The do Tool

Complete reference for the single unified do tool

The do Tool

The do tool is MCP.do's unified interface for Business-as-Code. Instead of 69+ narrow-purpose tools, AI models write TypeScript code directly against the SDK.

Why Code Mode?

"LLMs have seen a lot of code. They have not seen a lot of 'tool calls'."

Cloudflare Code Mode Blog

Traditional Approach requires synthetic tool schemas:

{
  "name": "database_create_order",
  "arguments": {
    "customer_name": "Alice",
    "items": [{ "product": "Widget", "price": 10 }]
  }
}

Code Mode uses natural TypeScript:

await db.create('Order', {
  customer: $.Person({ name: 'Alice' }),
  items: [$.Product({ name: 'Widget', price: 10 })],
})

AI models excel at writing code. Code mode leverages this native capability.

Tool Definition

The do tool accepts two parameters:

{
  "name": "do",
  "description": "Execute TypeScript against sdk.do modules. Semantic Business-as-Code primitives.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "script": {
        "type": "string",
        "description": "TypeScript code to execute, or '{module}.md' for documentation"
      },
      "module": {
        "type": "string",
        "description": "MDXLD module definition with yaml frontmatter ($type, $id, $context), markdown content, and MDX/JSX components. Defines persistent entities like Workflow, Agent, or WebSite."
      }
    }
  }
}

Parameters

  • script (optional): TypeScript code to execute immediately. Returns the result of the final expression.
  • module (optional): MDXLD module with:
    • YAML frontmatter: Define $type (e.g., Workflow, Agent, WebSite), $id (pathname, full URL or relative to $context), and $context (namespace)
    • Imports/Exports: Standard ES module syntax
    • Markdown content: Documentation and metadata
    • MDX/JSX components: UI components and interactive elements

At least one parameter must be provided. When both are provided, the module is registered first, then the script executes.

Type Signatures

The tool description includes concise type signatures for all SDK modules:

$: Proxy<$.Subject.predicate.Object>

db: {
  ;(list<T>(type), get<T>(type, id), create<T>(type, data), update<T>(type, id, data), delete (type, id), relate(from, rel, to))
}

ai: {
  ;(generate<T>(opts), embed(text), batch(requests))
}

api: {
  ;(fetch(url, opts), proxy(service, path, opts))
}

on: <Event>(pattern, handler)

send: <Event>(pattern, data)

every: (schedule, handler)

user: {
  ;(current(), session(), can(action, resource))
}

These signatures provide just enough information for AI models to understand capabilities without overwhelming the context window.

Execution Modes

The do tool supports two primary execution modes: Script Execution and Module Execution. Understanding when and how to use each mode is critical for effective Business-as-Code development.

Script Execution: Imperative Operations

Scripts are ephemeral, self-contained programs that execute immediately and return a result. They're ideal for one-time operations, queries, and data transformations.

Script Lifecycle

  1. Parse: TypeScript code is parsed and validated
  2. Execute: Code runs in a sandboxed environment with SDK access
  3. Return: Final expression value is returned to caller
  4. Cleanup: Execution context is destroyed

Script Characteristics

// Simple query - executes and returns immediately
await db.list('Business')

// Complex multi-step operation
const customer = await db.create('Person', {
  name: 'Alice',
  email: '[email protected]',
})

const order = await db.create('Order', {
  customer,
  items: [$.Product({ name: 'Widget', price: 29.99 })],
  total: 29.99,
})

// Send notification
await send($.Email.send, {
  to: customer.email,
  subject: 'Order Confirmation',
  body: `Your order #${order.id} has been confirmed`,
})

// Return final result
order

Key Points:

  • Executes in a sandboxed environment
  • Has access to all SDK primitives
  • Returns the final expression value
  • Times out after 10 seconds (configurable)
  • No persistence after execution
  • Ideal for: queries, one-time operations, data analysis, API calls

Script Return Semantics

Scripts return the final expression in the code:

// Returns the result of db.list
await db.list('Order')

// Returns 42
const x = 10
const y = 32
x + y

// Returns the order object
const order = await db.create('Order', { ... })
order

// Returns nothing (undefined)
const result = await db.create('Order', { ... })
await send($.Email.send, { ... })

// Returns array of results
const orders = await db.list('Order')
orders.map(o => ({ id: o.id, total: o.total }))

Module Execution: Persistent Workflows

Modules define persistent behavior that continues running after the initial execution. They're ideal for event handlers, scheduled tasks, and background workflows.

Module Lifecycle

  1. Parse: TypeScript code is parsed and validated
  2. Register: Event handlers and schedules are registered in the system
  3. Persist: Subscriptions remain active indefinitely
  4. Trigger: Handlers execute when events occur or schedules fire
  5. Continue: Module continues processing events/schedules

Module Characteristics

// Event handler - registers and persists
on($.Order.created, async (order) => {
  await send($.Email.send, {
    to: order.customer.email,
    subject: 'Order Confirmation',
  })
})

// Scheduled task - registers and persists
every('0 9 * * *', async () => {
  const pending = await db.list('Order', {
    where: { status: 'pending' },
  })

  for (const order of pending) {
    await send($.Order.reminder, { orderId: order.id })
  }
})

Key Points:

  • Creates persistent subscriptions/schedules
  • Handlers execute when triggered (not immediately)
  • No return value (subscriptions are the "output")
  • Continues running indefinitely
  • Requires authentication
  • Ideal for: event handlers, scheduled tasks, background jobs, workflows

Module Registration Semantics

Modules don't return values - they register behaviors:

// This registers a handler but doesn't execute it immediately
on($.Order.created, async (order) => {
  console.log('New order:', order.id)
  await send($.Email.send, { ... })
})

// Multiple handlers can be registered
on($.Order.created, handler1)
on($.Order.updated, handler2)
every('0 * * * *', handler3)

// Handlers execute when triggered:
// - Order created → handler1 fires
// - Order updated → handler2 fires
// - Every hour → handler3 fires

Using the module Parameter

The module parameter accepts MDXLD (MDX with Linked Data) to define persistent entities with metadata, documentation, and code:

MDXLD Module Structure

---
$type: Workflow
$id: /workflows/order-fulfillment
$context: https://platform.do
title: Order Fulfillment Workflow
description: Automated order processing and notification
---

# Order Fulfillment

This workflow handles the complete order lifecycle from creation to delivery.

## Features

- Automatic customer notifications
- Payment processing
- Inventory management
- Shipping coordination

import { OrderStatus } from './components'

export const config = {
  timeout: 30000,
  retries: 3,
}

<OrderStatus showProgress={true} />

on($.Order.created, async (order) => {
  await send($.Payment.process, { orderId: order.id })
  await send($.Email.send, {
    to: order.customer.email,
    subject: 'Order Confirmation',
  })
})

on($.Payment.completed, async (payment) => {
  await db.update('Order', payment.orderId, { status: 'paid' })
  await send($.Inventory.allocate, { orderId: payment.orderId })
})

every('0 */6 * * *', async () => {
  const stale = await db.list('Order', {
    where: { status: 'pending', createdAt: { before: '24 hours ago' } },
  })
  for (const order of stale) {
    await send($.Order.reminder, { orderId: order.id })
  }
})

Module Parameter Features

  • $type: Entity type (Workflow, Agent, WebSite, Service, etc.)
  • $id: Unique identifier pathname (absolute or relative to $context)
  • $context: Namespace/base URL for the module
  • Frontmatter: YAML metadata for module configuration
  • Markdown: Human-readable documentation
  • Imports/Exports: Standard ES modules for code reuse
  • MDX/JSX: UI components for visualization
  • Code: Event handlers, scheduled tasks, and business logic

When to Use module vs script

Use module when...Use script when...
Defining workflowsRunning queries
Creating agentsOne-time operations
Building servicesData analysis
Registering event handlersAPI calls
Scheduling tasksTesting code
Need documentationQuick experiments
Need UI componentsSimple calculations

Example: Creating a Module

await client.useTool('do', {
  module: `---
$type: Agent
$id: /agents/customer-support
$context: https://platform.do
---

# Customer Support Agent

Handles customer inquiries and support tickets.

on($.Ticket.created, async (ticket) => {
  const response = await ai.generate({
    prompt: \`Respond to: \${ticket.message}\`,
    schema: $.Response
  })

  await db.create('Response', {
    ticketId: ticket.id,
    message: response.text,
    sentiment: response.sentiment
  })

  if (response.escalate) {
    await send($.Human.review, { ticketId: ticket.id })
  }
})
`,
})

Combined Execution: Scripts + Modules

You can combine both modes to set up workflows and trigger immediate actions:

// Register persistent event handlers
on($.Order.created, async (order) => {
  await send($.Email.send, {
    to: order.customer.email,
    subject: 'Order Confirmation',
  })
})

on($.Payment.succeeded, async (payment) => {
  await db.update('Order', payment.orderId, {
    status: 'paid',
  })
})

// Execute immediate operation
const order = await db.create('Order', {
  customer: $.Person({ name: 'Alice' }),
  items: [$.Product({ name: 'Widget', price: 29.99 })],
  total: 29.99,
})

// Trigger the event (handlers above will fire)
await send($.Order.created, order)

// Return the order
order

Use Case: Set up event handlers and then perform an action that triggers them.

Documentation Requests

Pass {module}.md to request documentation:

// Request $ documentation
'$.md'

// Request db documentation
'db.md'

// Request ai documentation
'ai.md'

Returns comprehensive module documentation with:

  • Complete type signatures
  • Usage examples
  • Best practices
  • Related modules

Available Modules

$ - Semantic Context Proxy

Create semantic types using Schema.org vocabulary:

const person = $.Person({ name: 'Alice' })
const org = $.Organization({ name: 'Acme Corp' })
const order = $.Order({ customer: person, seller: org })

// Event patterns
on($.Order.created, handler)
await send($.Email.send, { ... })

Full Documentation →

db - Database Operations

Type-safe database with semantic types:

// CRUD operations
await db.list('Business')
await db.get('Order', 'ord_123')
await db.create('Order', { ... })
await db.update('Order', 'ord_123', { ... })
await db.delete('Order', 'ord_123')

// Relationships
await db.relate(order, 'customer', person)

Full Documentation →

ai - AI Operations

AI generation, embeddings, and batch processing:

// Generate with schema
const plan = await ai.generate({
  prompt: 'Create a business plan',
  schema: $.BusinessPlan,
  model: 'claude-sonnet-4.5',
})

// Embeddings
const embedding = await ai.embed('search text')

// Batch processing
const results = await ai.batch([
  { type: 'generate', prompt: 'Write a slogan' },
  { type: 'embed', text: 'product description' },
])

Full Documentation →

api - HTTP Operations

HTTP client and service proxy:

// External APIs
const data = await api.fetch('https://api.example.com/data')

// Integrated services
const customer = await api.proxy('stripe', '/v1/customers/cus_123')

Full Documentation →

on - Event Handlers

Subscribe to semantic events:

on($.Order.created, async (order) => {
  await send($.Email.send, {
    to: order.customer.email,
    subject: 'Order Confirmation',
  })
})

on($.Payment.succeeded, async (payment) => {
  await db.update('Order', payment.orderId, {
    status: 'paid',
  })
})

Full Documentation →

send - Event Publishing

Publish events to queues:

// Send email
await send($.Email.send, {
  to: '[email protected]',
  subject: 'Welcome!',
  body: 'Thank you for signing up',
})

// Domain event
await send($.Order.created, {
  orderId: 'ord_123',
  customerId: 'cus_456',
})

Full Documentation →

every - Scheduled Workflows

Schedule recurring tasks:

// Run every hour
every('0 * * * *', async () => {
  const pending = await db.list('Order', {
    where: { status: 'pending' },
  })

  for (const order of pending) {
    await send($.Order.reminder, { orderId: order.id })
  }
})

// Daily at 9 AM
every('0 9 * * *', async () => {
  await send($.Report.daily, { date: new Date() })
})

Full Documentation →

user - User Context

Access user information and permissions:

// Current user
const currentUser = user.current()
if (currentUser) {
  console.log('User ID:', currentUser.id)
  console.log('Email:', currentUser.email)
}

// Permissions
if (user.can('create', 'Order')) {
  await db.create('Order', { ... })
}

Full Documentation →

Examples

Simple Queries

// List all businesses
await db.list('Business')

// Get specific order
await db.get('Order', 'ord_123')

// Filter and sort
await db.list('Order', {
  where: { status: 'pending' },
  sort: { createdAt: 'desc' },
  limit: 10,
})

Creating Records

// Create customer
const customer = await db.create('Person', {
  name: 'Alice',
  email: '[email protected]',
})

// Create order with relationship
const order = await db.create('Order', {
  customer: customer,
  items: [$.Product({ name: 'Widget', price: 29.99 })],
  total: 29.99,
  status: 'pending',
})

// Send confirmation
await send($.Email.send, {
  to: customer.email,
  subject: 'Order Confirmation',
  body: `Your order #${order.id} has been confirmed`,
})

order

AI-Powered Workflows

// Get all products
const products = await db.list('Product')

// Generate descriptions with AI
const results = await Promise.all(
  products.map(async (product) => {
    const description = await ai.generate({
      prompt: `Write a compelling product description for ${product.name}`,
      schema: $.Text,
      model: 'gpt-5',
    })

    await db.update('Product', product.id, {
      description: description.text,
    })

    return { id: product.id, updated: true }
  })
)

results

Event-Driven Architecture

// Order created handler
on($.Order.created, async (order) => {
  // Send confirmation email
  await send($.Email.send, {
    to: order.customer.email,
    subject: 'Order Confirmation',
    body: `Your order #${order.id} has been received`,
  })

  // Update inventory
  for (const item of order.items) {
    const product = await db.get('Product', item.productId)
    await db.update('Product', item.productId, {
      stock: product.stock - item.quantity,
    })
  }

  // Trigger fulfillment
  await send($.Fulfillment.start, { orderId: order.id })
})

// Payment succeeded handler
on($.Payment.succeeded, async (payment) => {
  await db.update('Order', payment.orderId, {
    status: 'paid',
    paidAt: new Date(),
  })

  await send($.Order.paid, {
    orderId: payment.orderId,
    amount: payment.amount,
  })
})

Scheduled Tasks

// Clean up stale orders every 6 hours
every('0 */6 * * *', async () => {
  const staleOrders = await db.list('Order', {
    where: {
      status: 'pending',
      createdAt: { lt: Date.now() - 24 * 60 * 60 * 1000 }, // 24 hours ago
    },
  })

  for (const order of staleOrders) {
    await db.update('Order', order.id, {
      status: 'cancelled',
      cancelledAt: new Date(),
    })

    await send($.Order.cancelled, { orderId: order.id })
  }
})

// Daily report at 9 AM
every('0 9 * * *', async () => {
  const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000)

  const orders = await db.list('Order', {
    where: { createdAt: { gte: yesterday } },
  })

  const revenue = orders.reduce((sum, o) => sum + o.total, 0)

  await send($.Email.send, {
    to: '[email protected]',
    subject: 'Daily Report',
    body: `Orders: ${orders.length}, Revenue: $${revenue}`,
  })
})

Multi-Step Operations

// Complex workflow in single script
const businesses = await db.list('Business', {
  where: { industry: 'Technology' },
})

// Filter high-growth businesses
const highGrowth = businesses.filter((b) => b.growthRate > 0.5)

// Generate AI insights for each
const insights = await Promise.all(
  highGrowth.map(async (biz) => {
    const insight = await ai.generate({
      prompt: `Analyze growth trends for ${biz.name}. Growth rate: ${biz.growthRate}`,
      schema: $.Article,
      model: 'claude-sonnet-4.5',
    })

    // Store insight
    await db.create('Article', {
      about: $.Organization({ id: biz.id }),
      headline: insight.headline,
      articleBody: insight.articleBody,
      author: $.Person({ name: 'AI Analyst' }),
    })

    // Notify stakeholders
    await send($.Notification.send, {
      type: 'business-insight',
      businessId: biz.id,
      insightId: insight.id,
    })

    return insight
  })
)

insights

Security & Permissions

Readonly Mode (Anonymous)

Anonymous users (no auth header) are restricted to readonly operations via AST analysis:

Allowed:

await db.list('Business')
await db.get('Order', 'ord_123')
await ai.embed('search text')
await api.fetch('https://api.example.com/data') // GET only

Blocked:

await db.create('Order', { ... })      // Write operation
await db.update('Order', id, { ... })  // Write operation
await send($.Email.send, { ... })      // Side effect
await ai.generate({ ... })             // Side effect (cost)
every('0 * * * *', handler)            // Persistent subscription
on($.Order.created, handler)           // Persistent subscription

Authenticated Mode

With OAuth or API key, all operations are available:

// Check authentication
const currentUser = user.current()

if (currentUser) {
  // Full access
  await db.create('Order', { ... })
  await send($.Email.send, { ... })
  await ai.generate({ ... })
} else {
  // Readonly only
  await db.list('Business')
  await db.get('Order', 'ord_123')
}

Permission Checks

// Check permissions before operations
if (user.can('create', 'Order')) {
  await db.create('Order', { ... })
} else {
  throw new Error('Permission denied: cannot create orders')
}

if (user.can('delete', 'Order')) {
  await db.delete('Order', orderId)
}

// Admin check
if (user.can('admin', '*')) {
  // Admin operations
}

Error Handling

The do tool returns errors as text content with isError: true:

Execution Errors

{
  "content": [
    {
      "type": "text",
      "text": "Error: Cannot read property 'name' of undefined\n  at line 3:15"
    }
  ],
  "isError": true
}

Readonly Violations

{
  "content": [
    {
      "type": "text",
      "text": "Readonly violations detected:\n- db.create() at line 5\n- send() at line 8"
    }
  ],
  "isError": true
}

Timeout Errors

{
  "content": [
    {
      "type": "text",
      "text": "Execution timeout after 10000ms"
    }
  ],
  "isError": true
}

Authentication Errors

{
  "content": [
    {
      "type": "text",
      "text": "Authentication required for write operations"
    }
  ],
  "isError": true
}

Best Practices

1. Use Semantic Types

// ✅ Good: Semantic types
await db.create('Order', {
  customer: $.Person({ name: 'Alice' }),
  seller: $.Organization({ name: 'Acme' }),
})

// ❌ Bad: Plain objects
await db.create('Order', {
  customer_name: 'Alice',
  seller_name: 'Acme',
})

2. Handle Async Properly

// ✅ Good: Await async operations
const result = await db.list('Business')

// ❌ Bad: Forgot await
const result = db.list('Business') // Returns Promise

3. Use Type Inference

// ✅ Good: Types inferred from schema
const plan = await ai.generate({
  prompt: 'Create a business plan',
  schema: $.BusinessPlan,
})
// plan.executive_summary is type-safe

// ❌ Bad: No schema
const plan = await ai.generate({
  prompt: 'Create a business plan',
})
// plan is 'any' type

4. Compose Operations

// ✅ Good: Single script with multiple operations
const customer = await db.create('Person', { ... })
const order = await db.create('Order', { customer, ... })
await send($.Email.send, { to: customer.email })
order

// ❌ Bad: Multiple separate tool calls
// (Requires AI round-trips between each step)

5. Use Batch Operations

// ✅ Good: Single batch call
await ai.batch([
  { type: 'embed', text: texts[0] },
  { type: 'embed', text: texts[1] },
  { type: 'embed', text: texts[2] },
])

// ❌ Bad: Multiple separate calls
for (const text of texts) {
  await ai.embed(text)
}

6. Parallel Execution

// ✅ Good: Parallel
const [orders, customers] = await Promise.all([db.list('Order'), db.list('Customer')])

// ❌ Bad: Sequential
const orders = await db.list('Order')
const customers = await db.list('Customer')

Performance Tips

1. Limit Results

// Fetch only what you need
const recentOrders = await db.list('Order', {
  limit: 10,
  sort: { createdAt: 'desc' },
})

2. Use Filters

// Filter at database level
const activeOrders = await db.list('Order', {
  where: { status: 'active' },
})

// Not in application code
const allOrders = await db.list('Order')
const activeOrders = allOrders.filter((o) => o.status === 'active')

3. Batch AI Operations

// Process multiple items in single batch
const results = await ai.batch(
  products.map((p) => ({
    type: 'generate',
    prompt: `Describe ${p.name}`,
    schema: $.Text,
  }))
)

4. Cache Expensive Operations

// Cache AI results
const cached = await db.get('Cache', cacheKey)
if (cached) {
  return cached.value
}

const result = await ai.generate({ ... })
await db.create('Cache', {
  key: cacheKey,
  value: result,
  expiresAt: Date.now() + 3600000  // 1 hour
})

return result

Debugging

1. Use Console Logging

const orders = await db.list('Order')
console.log('Found orders:', orders.length)

for (const order of orders) {
  console.log('Processing order:', order.id)
  await processOrder(order)
}

2. Inspect Variables

const customer = await db.get('Person', 'per_123')
console.log('Customer:', JSON.stringify(customer, null, 2))

const order = await db.create('Order', { customer, ... })
console.log('Created order:', order)

3. Error Handling

try {
  const result = await db.create('Order', { ... })
  console.log('Success:', result)
} catch (error) {
  console.error('Failed:', error.message)
  throw error
}

Module Exports and Composition

Modules can export reusable functions, workflows, and event handlers for composition.

Exporting Functions from Modules

Create reusable functions that can be called from scripts or other modules:

// Define reusable order processing function
export async function processOrder(orderId: string) {
  const order = await db.get('Order', orderId)
  const customer = await db.get('Person', order.customerId)

  // Validate order
  if (!order || order.status !== 'pending') {
    throw new Error('Invalid order state')
  }

  // Process payment
  const payment = await api.proxy('stripe', '/v1/charges', {
    method: 'POST',
    body: {
      amount: order.total * 100,
      currency: 'usd',
      customer: customer.stripeId,
    },
  })

  // Update order
  await db.update('Order', orderId, {
    status: 'paid',
    paidAt: new Date(),
    paymentId: payment.id,
  })

  // Send confirmation
  await send($.Email.send, {
    to: customer.email,
    subject: 'Payment Confirmed',
    body: `Your payment of $${order.total} has been processed.`,
  })

  return { orderId, paymentId: payment.id, status: 'paid' }
}

// Export utility function
export async function calculateOrderTotal(items: Array<any>) {
  let total = 0
  for (const item of items) {
    const product = await db.get('Product', item.productId)
    total += product.price * item.quantity
  }
  return total
}

Exporting Workflows

Export complete workflows that orchestrate multiple operations:

// Customer onboarding workflow
export async function onboardCustomer(data: { name: string; email: string; company?: string }) {
  // Step 1: Create customer record
  const customer = await db.create('Person', {
    name: data.name,
    email: data.email,
    status: 'active',
    createdAt: new Date(),
  })

  // Step 2: Create company if provided
  if (data.company) {
    const company = await db.create('Organization', {
      name: data.company,
      legalName: data.company,
    })

    await db.relate(customer, 'worksFor', company)
  }

  // Step 3: Generate welcome email with AI
  const welcomeEmail = await ai.generate({
    prompt: `Write a warm welcome email for ${data.name}. Include a brief intro to our platform and next steps.`,
    schema: $.EmailMessage,
    model: 'claude-sonnet-4.5',
  })

  // Step 4: Send welcome email
  await send($.Email.send, {
    to: data.email,
    from: '[email protected]',
    subject: welcomeEmail.subject,
    body: welcomeEmail.body,
  })

  // Step 5: Schedule follow-up
  await send($.Schedule.create, {
    type: 'email',
    recipientId: customer.id,
    template: 'onboarding-day-3',
    sendAt: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000),
  })

  // Step 6: Trigger onboarding event
  await send($.Customer.onboarded, {
    customerId: customer.id,
    timestamp: new Date(),
  })

  return customer
}

// Product catalog sync workflow
export async function syncProductCatalog(source: string) {
  const products = await api.fetch(`https://api.${source}.com/products`)

  const results = []
  for (const productData of products) {
    // Check if product exists
    const existing = await db.list('Product', {
      where: { sku: productData.sku },
      limit: 1,
    })

    if (existing.length > 0) {
      // Update existing product
      const updated = await db.update('Product', existing[0].id, {
        name: productData.name,
        price: productData.price,
        stock: productData.stock,
        updatedAt: new Date(),
      })
      results.push({ sku: productData.sku, action: 'updated', id: updated.id })
    } else {
      // Create new product
      const created = await db.create('Product', {
        sku: productData.sku,
        name: productData.name,
        price: productData.price,
        stock: productData.stock,
        source: source,
        createdAt: new Date(),
      })
      results.push({ sku: productData.sku, action: 'created', id: created.id })
    }
  }

  return {
    source,
    totalProcessed: results.length,
    created: results.filter((r) => r.action === 'created').length,
    updated: results.filter((r) => r.action === 'updated').length,
    timestamp: new Date(),
  }
}

Exporting Event Handlers

Export event handler modules that can be registered together:

// E-commerce event handlers module
export function setupEcommerceHandlers() {
  // Order lifecycle handlers
  on($.Order.created, async (order) => {
    const customer = await db.get('Person', order.customerId)

    await send($.Email.send, {
      to: customer.email,
      subject: `Order Confirmation #${order.orderNumber}`,
      body: `Thank you for your order! Total: $${order.total}`,
    })

    await send($.Analytics.track, {
      event: 'order_created',
      userId: customer.id,
      properties: { orderId: order.id, total: order.total },
    })
  })

  on($.Order.paid, async (event) => {
    await db.update('Order', event.orderId, {
      status: 'processing',
      processingStartedAt: new Date(),
    })

    await send($.Fulfillment.start, {
      orderId: event.orderId,
      priority: event.amount > 500 ? 'high' : 'normal',
    })
  })

  on($.Order.shipped, async (event) => {
    const order = await db.get('Order', event.orderId)
    const customer = await db.get('Person', order.customerId)

    await send($.Email.send, {
      to: customer.email,
      subject: 'Your Order Has Shipped!',
      body: `Tracking: ${event.trackingNumber}`,
    })
  })

  // Inventory management handlers
  on($.Product.lowStock, async (product) => {
    if (product.stock < 10) {
      await send($.Email.send, {
        to: '[email protected]',
        subject: `Low Stock Alert: ${product.name}`,
        body: `Current stock: ${product.stock}. Reorder recommended.`,
      })
    }
  })

  // Customer engagement handlers
  on($.Customer.inactive, async (customer) => {
    const email = await ai.generate({
      prompt: `Write a re-engagement email for ${customer.name}. They haven't made a purchase in 90 days. Offer 15% discount code: COMEBACK15`,
      schema: $.EmailMessage,
      model: 'gpt-5',
    })

    await send($.Email.send, {
      to: customer.email,
      subject: email.subject,
      body: email.body,
    })
  })
}

// Register all handlers
setupEcommerceHandlers()

Exporting Scheduled Tasks

Export scheduled task modules for recurring operations:

// Analytics and reporting tasks
export function setupAnalyticsTasks() {
  // Daily sales report
  every('0 9 * * *', async () => {
    const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000)
    const orders = await db.list('Order', {
      where: { createdAt: { gte: yesterday.toISOString() } },
    })

    const report = {
      date: yesterday.toLocaleDateString(),
      totalOrders: orders.length,
      totalRevenue: orders.reduce((sum, o) => sum + o.total, 0),
      avgOrderValue: orders.length > 0 ? orders.reduce((sum, o) => sum + o.total, 0) / orders.length : 0,
    }

    await send($.Email.send, {
      to: '[email protected]',
      subject: `Daily Sales Report - ${report.date}`,
      body: JSON.stringify(report, null, 2),
    })
  })

  // Weekly customer segmentation
  every('0 8 * * MON', async () => {
    const customers = await db.list('Person')

    for (const customer of customers) {
      const orders = await db.list('Order', {
        where: { customerId: customer.id },
      })

      const totalSpent = orders.reduce((sum, o) => sum + o.total, 0)
      const segment = totalSpent > 1000 ? 'VIP' : totalSpent > 500 ? 'Premium' : 'Regular'

      await db.update('Person', customer.id, {
        segment,
        totalSpent,
        lastSegmentedAt: new Date(),
      })
    }
  })

  // Hourly stale order cleanup
  every('0 * * * *', async () => {
    const staleOrders = await db.list('Order', {
      where: {
        status: 'pending',
        createdAt: { lt: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString() },
      },
    })

    for (const order of staleOrders) {
      await db.update('Order', order.id, {
        status: 'cancelled',
        cancelledAt: new Date(),
        cancelReason: 'stale',
      })

      await send($.Order.cancelled, { orderId: order.id })
    }
  })
}

setupAnalyticsTasks()

Module Composition and Reusability

Compose modules by importing and calling exported functions:

// Import from another module
import { processOrder, calculateOrderTotal } from './order-processing'
import { onboardCustomer } from './customer-workflows'
import { setupEcommerceHandlers } from './event-handlers'

// Use imported functions in scripts
const items = [
  { productId: 'prod_123', quantity: 2 },
  { productId: 'prod_456', quantity: 1 },
]

const total = await calculateOrderTotal(items)
console.log('Order total:', total)

// Use in event handlers
on($.Order.pendingPayment, async (order) => {
  try {
    await processOrder(order.id)
  } catch (error) {
    console.error('Failed to process order:', error)
    await send($.Order.paymentFailed, { orderId: order.id, error: error.message })
  }
})

// Compose workflows
const newCustomer = await onboardCustomer({
  name: 'Alice Smith',
  email: '[email protected]',
  company: 'Acme Corp',
})

// Set up all event handlers
setupEcommerceHandlers()

Script Composition Patterns

Scripts can build complex operations from simple primitives using composition patterns.

Multi-Step Script Workflows

Chain operations together in a single script:

// Complete order fulfillment in one script
async function fulfillOrder(orderId: string) {
  // Step 1: Get order and validate
  const order = await db.get('Order', orderId)
  if (!order || order.status !== 'paid') {
    throw new Error('Order not ready for fulfillment')
  }

  // Step 2: Check inventory
  const inventoryOk = true
  for (const item of order.items) {
    const product = await db.get('Product', item.productId)
    if (product.stock < item.quantity) {
      inventoryOk = false
      break
    }
  }

  if (!inventoryOk) {
    await send($.Order.backorder, { orderId })
    return { status: 'backorder' }
  }

  // Step 3: Reserve inventory
  for (const item of order.items) {
    const product = await db.get('Product', item.productId)
    await db.update('Product', item.productId, {
      stock: product.stock - item.quantity,
    })
  }

  // Step 4: Generate shipping label
  const customer = await db.get('Person', order.customerId)
  const shipping = await api.proxy('shippo', '/shipments', {
    method: 'POST',
    body: {
      address_to: customer.address,
      parcels: order.items.map((i) => ({ weight: i.weight })),
    },
  })

  // Step 5: Update order
  await db.update('Order', orderId, {
    status: 'shipped',
    trackingNumber: shipping.tracking_number,
    shippedAt: new Date(),
  })

  // Step 6: Notify customer
  await send($.Email.send, {
    to: customer.email,
    subject: 'Your Order Has Shipped!',
    body: `Tracking: ${shipping.tracking_number}`,
  })

  // Step 7: Trigger analytics
  await send($.Analytics.track, {
    event: 'order_shipped',
    properties: { orderId, trackingNumber: shipping.tracking_number },
  })

  return {
    status: 'shipped',
    trackingNumber: shipping.tracking_number,
    trackingUrl: shipping.tracking_url,
  }
}

// Execute workflow
await fulfillOrder('ord_123')

Error Handling Patterns

Implement robust error handling in scripts:

// Error handling with try-catch
async function processOrderWithErrorHandling(orderId: string) {
  try {
    const order = await db.get('Order', orderId)

    if (!order) {
      throw new Error('Order not found')
    }

    const result = await api.proxy('stripe', '/v1/charges', {
      method: 'POST',
      body: { amount: order.total * 100, currency: 'usd' },
    })

    await db.update('Order', orderId, {
      status: 'paid',
      paymentId: result.id,
    })

    return { success: true, paymentId: result.id }
  } catch (error) {
    console.error('Payment failed:', error.message)

    await db.update('Order', orderId, {
      status: 'payment_failed',
      errorMessage: error.message,
    })

    await send($.Order.paymentFailed, {
      orderId,
      error: error.message,
      timestamp: new Date(),
    })

    return { success: false, error: error.message }
  }
}

// Retry pattern with exponential backoff
async function withRetry<T>(fn: () => Promise<T>, maxRetries = 3, baseDelay = 1000): Promise<T> {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn()
    } catch (error) {
      if (i === maxRetries - 1) throw error

      const delay = baseDelay * Math.pow(2, i)
      console.log(`Attempt ${i + 1} failed, retrying in ${delay}ms...`)
      await new Promise((resolve) => setTimeout(resolve, delay))
    }
  }
  throw new Error('Max retries reached')
}

// Use retry pattern
const result = await withRetry(
  async () => {
    return await api.fetch('https://api.external.com/data')
  },
  3,
  1000
)

Transaction Patterns

Implement transaction-like patterns with rollback:

// Transaction pattern with rollback
async function transferInventory(fromProductId: string, toProductId: string, quantity: number) {
  const operations = []

  try {
    // Step 1: Deduct from source
    const fromProduct = await db.get('Product', fromProductId)
    if (fromProduct.stock < quantity) {
      throw new Error('Insufficient stock')
    }

    const updatedFrom = await db.update('Product', fromProductId, {
      stock: fromProduct.stock - quantity,
    })
    operations.push({ type: 'update', id: fromProductId, rollback: { stock: fromProduct.stock } })

    // Step 2: Add to destination
    const toProduct = await db.get('Product', toProductId)
    const updatedTo = await db.update('Product', toProductId, {
      stock: toProduct.stock + quantity,
    })
    operations.push({ type: 'update', id: toProductId, rollback: { stock: toProduct.stock } })

    // Step 3: Create transfer record
    const transfer = await db.create('InventoryTransfer', {
      fromProductId,
      toProductId,
      quantity,
      timestamp: new Date(),
    })
    operations.push({ type: 'create', id: transfer.id })

    return { success: true, transferId: transfer.id }
  } catch (error) {
    console.error('Transfer failed, rolling back:', error.message)

    // Rollback in reverse order
    for (const op of operations.reverse()) {
      try {
        if (op.type === 'update') {
          await db.update('Product', op.id, op.rollback)
        } else if (op.type === 'create') {
          await db.delete('InventoryTransfer', op.id)
        }
      } catch (rollbackError) {
        console.error('Rollback failed:', rollbackError.message)
      }
    }

    throw error
  }
}

// Execute with rollback support
await transferInventory('prod_123', 'prod_456', 10)

Batch Processing Patterns

Process large datasets efficiently:

// Batch processing with progress tracking
async function batchUpdateProducts(updates: Array<{ id: string; data: any }>) {
  const batchSize = 10
  const results = []

  for (let i = 0; i < updates.length; i += batchSize) {
    const batch = updates.slice(i, i + batchSize)

    console.log(`Processing batch ${i / batchSize + 1} of ${Math.ceil(updates.length / batchSize)}`)

    const batchResults = await Promise.allSettled(batch.map((update) => db.update('Product', update.id, update.data)))

    batchResults.forEach((result, idx) => {
      if (result.status === 'fulfilled') {
        results.push({ id: batch[idx].id, success: true })
      } else {
        results.push({ id: batch[idx].id, success: false, error: result.reason.message })
      }
    })

    // Progress notification
    if ((i + batchSize) % 100 === 0) {
      await send($.Job.progress, {
        type: 'product_update',
        processed: Math.min(i + batchSize, updates.length),
        total: updates.length,
      })
    }

    // Rate limiting
    await new Promise((resolve) => setTimeout(resolve, 100))
  }

  return {
    total: updates.length,
    successful: results.filter((r) => r.success).length,
    failed: results.filter((r) => !r.success).length,
    errors: results.filter((r) => !r.success).map((r) => ({ id: r.id, error: r.error })),
  }
}

// Execute batch operation
const updates = [
  { id: 'prod_1', data: { price: 29.99 } },
  { id: 'prod_2', data: { price: 39.99 } },
  // ... hundreds more
]

await batchUpdateProducts(updates)

Parallel Execution Patterns

Execute independent operations in parallel:

// Parallel data fetching
async function loadDashboardData() {
  const [orders, customers, products, revenue] = await Promise.all([
    db.list('Order', { limit: 100, sort: { createdAt: 'desc' } }),
    db.list('Person', { limit: 50 }),
    db.list('Product', { where: { stock: { lt: 10 } } }),
    calculateRevenue(),
  ])

  return {
    recentOrders: orders,
    topCustomers: customers,
    lowStockProducts: products,
    revenueMetrics: revenue,
  }
}

async function calculateRevenue() {
  const orders = await db.list('Order', {
    where: { status: 'paid' },
  })

  return {
    total: orders.reduce((sum, o) => sum + o.total, 0),
    count: orders.length,
    average: orders.length > 0 ? orders.reduce((sum, o) => sum + o.total, 0) / orders.length : 0,
  }
}

// Execute with parallel operations
const dashboard = await loadDashboardData()

Real-World Scenarios

Complete, production-ready examples for common business scenarios.

Scenario 1: E-Commerce Order Processing (Script + Module)

Complete order processing system with event-driven fulfillment:

// Module: Set up event handlers for order lifecycle
on($.Order.created, async (order) => {
  console.log(`New order created: ${order.id}`)

  // Validate order
  const customer = await db.get('Person', order.customerId)
  if (!customer) {
    throw new Error('Customer not found')
  }

  // Send confirmation email
  await send($.Email.send, {
    to: customer.email,
    subject: `Order Confirmation #${order.orderNumber}`,
    from: '[email protected]',
    body: `
Thank you for your order!

Order #${order.orderNumber}
Total: $${order.total}

We'll notify you when your order ships.
    `
  })

  // Update inventory
  for (const item of order.items) {
    const product = await db.get('Product', item.productId)
    await db.update('Product', item.productId, {
      stock: product.stock - item.quantity
    })

    // Check for low stock
    if (product.stock - item.quantity < 10) {
      await send($.Inventory.lowStock, {
        productId: product.id,
        currentStock: product.stock - item.quantity
      })
    }
  }

  // Trigger fulfillment after 1 hour (to allow for cancellations)
  await send($.Schedule.create, {
    type: 'fulfillment',
    orderId: order.id,
    executeAt: new Date(Date.now() + 60 * 60 * 1000)
  })
})

on($.Payment.succeeded, async (payment) => {
  console.log(`Payment succeeded: ${payment.id}`)

  await db.update('Order', payment.orderId, {
    status: 'paid',
    paidAt: new Date(),
    paymentMethod: payment.method,
    paymentId: payment.id
  })

  // Trigger immediate fulfillment for express orders
  const order = await db.get('Order', payment.orderId)
  if (order.shippingMethod === 'express') {
    await send($.Fulfillment.start, {
      orderId: payment.orderId,
      priority: 'high'
    })
  }
})

on($.Fulfillment.start, async (fulfillment) => {
  console.log(`Starting fulfillment: ${fulfillment.orderId}`)

  const order = await db.get('Order', fulfillment.orderId)
  const customer = await db.get('Person', order.customerId)

  // Update order status
  await db.update('Order', order.id, {
    status: 'processing',
    processingStartedAt: new Date()
  })

  // Generate shipping label
  const shipping = await api.proxy('shippo', '/shipments', {
    method: 'POST',
    body: {
      address_to: {
        name: customer.name,
        street1: customer.address.street1,
        city: customer.address.city,
        state: customer.address.state,
        zip: customer.address.zip,
        country: 'US'
      },
      parcels: order.items.map(item => ({
        length: '10',
        width: '8',
        height: '4',
        distance_unit: 'in',
        weight: item.weight || '1',
        mass_unit: 'lb'
      })),
      async: false
    }
  })

  // Update with tracking
  await db.update('Order', order.id, {
    status: 'shipped',
    shippedAt: new Date(),
    trackingNumber: shipping.tracking_number,
    carrier: shipping.carrier
  })

  // Notify customer
  await send($.Email.send, {
    to: customer.email,
    subject: 'Your Order Has Shipped!',
    from: '[email protected]',
    body: `
Your order #${order.orderNumber} has shipped!

Tracking Number: ${shipping.tracking_number}
Carrier: ${shipping.carrier}

Track your package: ${shipping.tracking_url}
    `
  })

  // Trigger shipped event
  await send($.Order.shipped, {
    orderId: order.id,
    trackingNumber: shipping.tracking_number,
    trackingUrl: shipping.tracking_url
  })
})

// Script: Create a new order and trigger the workflow
const customer = await db.create('Person', {
  name: 'Bob Smith',
  email: '[email protected]',
  address: {
    street1: '123 Main St',
    city: 'San Francisco',
    state: 'CA',
    zip: '94102'
  }
})

const products = await Promise.all([
  db.get('Product', 'prod_widget_001'),
  db.get('Product', 'prod_gadget_002')
])

const order = await db.create('Order', {
  orderNumber: `ORD-${Date.now()}`,
  customer: customer,
  customerId: customer.id,
  items: [
    {
      productId: products[0].id,
      name: products[0].name,
      quantity: 2,
      price: products[0].price,
      weight: products[0].weight
    },
    {
      productId: products[1].id,
      name: products[1].name,
      quantity: 1,
      price: products[1].price,
      weight: products[1].weight
    }
  ],
  total: products[0].price * 2 + products[1].price,
  shippingMethod: 'standard',
  status: 'pending',
  createdAt: new Date()
})

// Trigger order created event (activates handlers above)
await send($.Order.created, order)

// Return order details
{
  orderId: order.id,
  orderNumber: order.orderNumber,
  customer: customer.name,
  total: order.total,
  status: order.status,
  items: order.items.length
}

Scenario 2: Content Management Workflows (Module)

Automated content creation and publishing pipeline:

// Content approval workflow
on($.Article.submitted, async (article) => {
  console.log(`Article submitted: ${article.id}`)

  // Run AI content analysis
  const analysis = await ai.generate({
    prompt: `Analyze this article for quality, readability, and SEO:

Title: ${article.headline}
Content: ${article.articleBody}

Provide:
1. Quality score (1-10)
2. Readability grade level
3. SEO recommendations
4. Suggested improvements`,
    schema: $.Analysis,
    model: 'claude-sonnet-4.5',
  })

  // Store analysis
  await db.update('Article', article.id, {
    analysis: analysis,
    analyzedAt: new Date(),
  })

  // Auto-approve high-quality content
  if (analysis.qualityScore >= 8) {
    await send($.Article.approved, {
      articleId: article.id,
      approvedBy: 'ai-auto-approve',
      reason: `High quality score: ${analysis.qualityScore}/10`,
    })
  } else {
    // Send to human review
    await send($.Email.send, {
      to: '[email protected]',
      subject: `Article Needs Review: ${article.headline}`,
      body: `
Article: ${article.headline}
Author: ${article.author.name}
Quality Score: ${analysis.qualityScore}/10

Analysis:
${JSON.stringify(analysis, null, 2)}

Review: https://cms.acme.com/articles/${article.id}/review
      `,
    })
  }
})

on($.Article.approved, async (event) => {
  const article = await db.get('Article', event.articleId)

  // Generate social media posts
  const socialPosts = await ai.generate({
    prompt: `Create social media posts for this article:

Title: ${article.headline}
Summary: ${article.description}

Generate posts for:
1. Twitter (280 chars)
2. LinkedIn (professional tone)
3. Facebook (engaging, conversational)`,
    schema: $.SocialMediaPosts,
    model: 'gpt-5',
  })

  // Generate featured image
  const imagePrompt = await ai.generate({
    prompt: `Create an image generation prompt for this article: ${article.headline}`,
    schema: $.Text,
    model: 'gpt-5',
  })

  // Publish article
  await db.update('Article', article.id, {
    status: 'published',
    publishedAt: new Date(),
    socialPosts: socialPosts,
  })

  // Schedule social posts
  const platforms = ['twitter', 'linkedin', 'facebook']
  for (let i = 0; i < platforms.length; i++) {
    await send($.Schedule.create, {
      type: 'social_post',
      platform: platforms[i],
      content: socialPosts[platforms[i]],
      articleId: article.id,
      executeAt: new Date(Date.now() + i * 60 * 60 * 1000), // Stagger by 1 hour
    })
  }

  // Notify author
  await send($.Email.send, {
    to: article.author.email,
    subject: `Your Article Has Been Published!`,
    body: `
Congratulations! Your article "${article.headline}" is now live.

View it here: https://blog.acme.com/articles/${article.slug}

Social posts scheduled:
- Twitter: ${new Date(Date.now() + 0 * 60 * 60 * 1000).toLocaleString()}
- LinkedIn: ${new Date(Date.now() + 1 * 60 * 60 * 1000).toLocaleString()}
- Facebook: ${new Date(Date.now() + 2 * 60 * 60 * 1000).toLocaleString()}
    `,
  })
})

// Scheduled content optimization
every('0 2 * * *', async () => {
  console.log('Running content optimization...')

  const articles = await db.list('Article', {
    where: {
      status: 'published',
      updatedAt: { lt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString() },
    },
    limit: 20,
  })

  for (const article of articles) {
    // Generate updated SEO recommendations
    const seo = await ai.generate({
      prompt: `Analyze and improve SEO for this article:

Title: ${article.headline}
Content: ${article.articleBody.substring(0, 500)}...

Provide updated:
1. Meta description
2. Keywords
3. Internal linking suggestions`,
      schema: $.SEORecommendations,
      model: 'claude-sonnet-4.5',
    })

    await db.update('Article', article.id, {
      seoRecommendations: seo,
      lastOptimized: new Date(),
    })
  }
})

Scenario 3: Analytics Pipeline (Script)

Data analysis and reporting pipeline:

// Comprehensive analytics pipeline
async function generateMonthlyReport() {
  console.log('Generating monthly analytics report...')

  const startDate = new Date()
  startDate.setDate(1) // First day of current month
  startDate.setHours(0, 0, 0, 0)

  // Parallel data fetching
  const [orders, customers, products, revenue, traffic] = await Promise.all([
    db.list('Order', {
      where: { createdAt: { gte: startDate.toISOString() } },
    }),
    db.list('Person', {
      where: { createdAt: { gte: startDate.toISOString() } },
    }),
    db.list('Product'),
    api.fetch('https://analytics.acme.com/api/revenue', {
      method: 'POST',
      body: JSON.stringify({ start: startDate.toISOString() }),
    }),
    api.fetch('https://analytics.acme.com/api/traffic', {
      method: 'POST',
      body: JSON.stringify({ start: startDate.toISOString() }),
    }),
  ])

  // Calculate metrics
  const metrics = {
    orders: {
      total: orders.length,
      revenue: orders.reduce((sum, o) => sum + o.total, 0),
      avgValue: orders.length > 0 ? orders.reduce((sum, o) => sum + o.total, 0) / orders.length : 0,
      byStatus: orders.reduce((acc, o) => {
        acc[o.status] = (acc[o.status] || 0) + 1
        return acc
      }, {}),
    },
    customers: {
      new: customers.length,
      returning: orders.filter((o) => {
        const customerOrders = orders.filter((ord) => ord.customerId === o.customerId)
        return customerOrders.length > 1
      }).length,
      churnRate: calculateChurnRate(customers, orders),
    },
    products: {
      total: products.length,
      lowStock: products.filter((p) => p.stock < 10).length,
      topSellers: getTopProducts(orders, products, 10),
    },
    revenue: {
      total: revenue.total,
      growth: revenue.growth,
      forecast: revenue.forecast,
    },
    traffic: {
      visitors: traffic.visitors,
      pageViews: traffic.pageViews,
      conversionRate: orders.length / traffic.visitors,
    },
  }

  // Generate AI insights
  const insights = await ai.generate({
    prompt: `Analyze this monthly business data and provide strategic insights:

${JSON.stringify(metrics, null, 2)}

Provide:
1. Key achievements and wins
2. Areas of concern
3. Growth opportunities
4. Specific action items
5. Forecast for next month`,
    schema: $.BusinessInsights,
    model: 'claude-sonnet-4.5',
    temperature: 0.3,
  })

  // Create comprehensive report
  const report = await db.create('Report', {
    type: 'monthly',
    period: startDate.toISOString(),
    metrics: metrics,
    insights: insights,
    generatedAt: new Date(),
  })

  // Generate visualizations
  const charts = await Promise.all([
    generateChart('revenue-trend', metrics.revenue),
    generateChart('order-status', metrics.orders.byStatus),
    generateChart('top-products', metrics.products.topSellers),
  ])

  // Send email report
  await send($.Email.send, {
    to: '[email protected]',
    subject: `Monthly Business Report - ${startDate.toLocaleDateString('en-US', { month: 'long', year: 'numeric' })}`,
    from: '[email protected]',
    body: `
# Monthly Business Report

## Executive Summary

${insights.summary}

## Key Metrics

**Orders**
- Total Orders: ${metrics.orders.total}
- Revenue: $${metrics.orders.revenue.toFixed(2)}
- Average Order Value: $${metrics.orders.avgValue.toFixed(2)}

**Customers**
- New Customers: ${metrics.customers.new}
- Returning Customers: ${metrics.customers.returning}
- Churn Rate: ${(metrics.customers.churnRate * 100).toFixed(2)}%

**Products**
- Low Stock Items: ${metrics.products.lowStock}

## AI Insights

${insights.analysis}

## Recommendations

${insights.recommendations.map((r, i) => `${i + 1}. ${r}`).join('\n')}

View full report: https://analytics.acme.com/reports/${report.id}
    `,
    attachments: charts,
  })

  return {
    reportId: report.id,
    metrics,
    insights,
    generated: new Date(),
  }
}

function calculateChurnRate(customers, orders) {
  const activeCustomers = new Set(orders.map((o) => o.customerId))
  const churnedCustomers = customers.filter((c) => !activeCustomers.has(c.id)).length
  return customers.length > 0 ? churnedCustomers / customers.length : 0
}

function getTopProducts(orders, products, limit) {
  const sales = {}
  orders.forEach((order) => {
    order.items.forEach((item) => {
      sales[item.productId] = (sales[item.productId] || 0) + item.quantity
    })
  })

  return Object.entries(sales)
    .sort(([, a], [, b]) => b - a)
    .slice(0, limit)
    .map(([id, quantity]) => {
      const product = products.find((p) => p.id === id)
      return {
        id,
        name: product?.name || 'Unknown',
        quantity,
        revenue: quantity * (product?.price || 0),
      }
    })
}

async function generateChart(type, data) {
  // Generate chart using visualization service
  return await api.fetch('https://charts.acme.com/api/generate', {
    method: 'POST',
    body: JSON.stringify({ type, data }),
  })
}

// Execute monthly report
await generateMonthlyReport()

Scenario 4: Customer Support Automation (Module)

AI-powered customer support system:

// Customer support ticket handling
on($.Ticket.created, async (ticket) => {
  console.log(`New support ticket: ${ticket.id}`)

  // Classify ticket urgency and category
  const classification = await ai.generate({
    prompt: `Classify this support ticket:

Subject: ${ticket.subject}
Description: ${ticket.description}

Provide:
1. Urgency (low/medium/high/critical)
2. Category (technical/billing/general/feature-request)
3. Estimated resolution time (hours)
4. Suggested response`,
    schema: $.TicketClassification,
    model: 'claude-sonnet-4.5',
  })

  // Update ticket
  await db.update('Ticket', ticket.id, {
    urgency: classification.urgency,
    category: classification.category,
    estimatedResolutionTime: classification.estimatedResolutionTime,
    classification: classification,
  })

  // Auto-respond to low-urgency tickets
  if (classification.urgency === 'low' && classification.suggestedResponse) {
    await send($.Email.send, {
      to: ticket.customerEmail,
      subject: `Re: ${ticket.subject}`,
      from: '[email protected]',
      body: classification.suggestedResponse,
    })

    await db.update('Ticket', ticket.id, {
      status: 'auto-responded',
      autoResponseSent: true,
      autoResponseSentAt: new Date(),
    })
  }

  // Assign to appropriate team
  const teamMapping = {
    technical: '[email protected]',
    billing: '[email protected]',
    general: '[email protected]',
    'feature-request': '[email protected]',
  }

  await send($.Email.send, {
    to: teamMapping[classification.category],
    subject: `[${classification.urgency.toUpperCase()}] New Ticket: ${ticket.subject}`,
    from: '[email protected]',
    body: `
New support ticket requires attention:

Ticket ID: ${ticket.id}
Customer: ${ticket.customerName} (${ticket.customerEmail})
Urgency: ${classification.urgency}
Category: ${classification.category}
Estimated Resolution: ${classification.estimatedResolutionTime} hours

Subject: ${ticket.subject}
Description: ${ticket.description}

View ticket: https://support.acme.com/tickets/${ticket.id}
    `,
  })

  // Escalate critical tickets
  if (classification.urgency === 'critical') {
    await send($.SMS.send, {
      to: '+1-555-ONCALL',
      message: `CRITICAL support ticket ${ticket.id}: ${ticket.subject}`,
    })

    await send($.Slack.message, {
      channel: '#support-critical',
      text: `🚨 Critical ticket: ${ticket.subject}`,
      link: `https://support.acme.com/tickets/${ticket.id}`,
    })
  }
})

// Ticket response quality check
on($.Ticket.responded, async (event) => {
  const ticket = await db.get('Ticket', event.ticketId)

  // Analyze response quality
  const analysis = await ai.generate({
    prompt: `Analyze this support response for quality:

Original Issue: ${ticket.description}
Agent Response: ${event.response}

Rate:
1. Helpfulness (1-10)
2. Clarity (1-10)
3. Professionalism (1-10)
4. Completeness (1-10)
5. Provide improvement suggestions`,
    schema: $.ResponseAnalysis,
    model: 'claude-sonnet-4.5',
  })

  // Store analysis
  await db.update('Ticket', ticket.id, {
    responseAnalysis: analysis,
    analyzedAt: new Date(),
  })

  // Flag low-quality responses for review
  const avgScore = (analysis.helpfulness + analysis.clarity + analysis.professionalism + analysis.completeness) / 4

  if (avgScore < 7) {
    await send($.Email.send, {
      to: '[email protected]',
      subject: `Response Quality Alert: Ticket ${ticket.id}`,
      body: `
A support response may need review:

Ticket: ${ticket.id}
Agent: ${event.agentId}
Average Quality Score: ${avgScore}/10

Analysis:
${JSON.stringify(analysis, null, 2)}

Review: https://support.acme.com/tickets/${ticket.id}/review
      `,
    })
  }
})

// Daily support metrics
every('0 18 * * *', async () => {
  const today = new Date()
  today.setHours(0, 0, 0, 0)

  const tickets = await db.list('Ticket', {
    where: { createdAt: { gte: today.toISOString() } },
  })

  const metrics = {
    total: tickets.length,
    resolved: tickets.filter((t) => t.status === 'resolved').length,
    pending: tickets.filter((t) => t.status === 'pending').length,
    avgResponseTime: calculateAvgResponseTime(tickets),
    avgResolutionTime: calculateAvgResolutionTime(tickets),
    satisfactionScore: calculateSatisfactionScore(tickets),
    byCategory: tickets.reduce((acc, t) => {
      acc[t.category] = (acc[t.category] || 0) + 1
      return acc
    }, {}),
  }

  await send($.Email.send, {
    to: '[email protected]',
    subject: `Daily Support Metrics - ${today.toLocaleDateString()}`,
    body: `
# Daily Support Summary

**Overview**
- Total Tickets: ${metrics.total}
- Resolved: ${metrics.resolved}
- Pending: ${metrics.pending}

**Performance**
- Avg Response Time: ${metrics.avgResponseTime} minutes
- Avg Resolution Time: ${metrics.avgResolutionTime} hours
- Satisfaction Score: ${metrics.satisfactionScore}/10

**By Category**
${Object.entries(metrics.byCategory)
  .map(([cat, count]) => `- ${cat}: ${count}`)
  .join('\n')}
    `,
  })
})

function calculateAvgResponseTime(tickets) {
  const times = tickets.filter((t) => t.firstResponseAt).map((t) => (new Date(t.firstResponseAt) - new Date(t.createdAt)) / 1000 / 60)
  return times.length > 0 ? times.reduce((a, b) => a + b) / times.length : 0
}

function calculateAvgResolutionTime(tickets) {
  const times = tickets.filter((t) => t.resolvedAt).map((t) => (new Date(t.resolvedAt) - new Date(t.createdAt)) / 1000 / 60 / 60)
  return times.length > 0 ? times.reduce((a, b) => a + b) / times.length : 0
}

function calculateSatisfactionScore(tickets) {
  const scores = tickets.filter((t) => t.satisfactionScore).map((t) => t.satisfactionScore)
  return scores.length > 0 ? scores.reduce((a, b) => a + b) / scores.length : 0
}

When to Use Each Mode

Understanding when to use scripts vs modules is critical for effective development.

Decision Tree

Is the operation persistent (continues after execution)?
├─ YES → Use Module (on/every)
│  ├─ Need to react to events? → Use on()
│  ├─ Need scheduled execution? → Use every()
│  └─ Need both? → Combine on() + every()

└─ NO → Use Script
   ├─ Single operation? → Simple script
   ├─ Multiple steps? → Multi-step script
   └─ Need immediate result? → Script with return value

Use Script For:

Queries and Data Retrieval

// ✅ Script - immediate result needed
await db.list('Order', { where: { status: 'pending' } })

One-Time Operations

// ✅ Script - execute once and return
const customer = await db.create('Person', { ... })
const order = await db.create('Order', { customer, ... })
order

Data Analysis and Transformation

// ✅ Script - process and return results
const orders = await db.list('Order')
const revenue = orders.reduce((sum, o) => sum + o.total, 0)({ totalOrders: orders.length, revenue })

API Calls and External Integrations

// ✅ Script - fetch and process external data
const data = await api.fetch('https://api.example.com/data')
const processed = data.map(item => ({ ... }))
processed

AI Content Generation

// ✅ Script - generate content and return
const description = await ai.generate({
  prompt: 'Write a product description',
  schema: $.Text,
})
description.text

Ad-Hoc Administrative Tasks

// ✅ Script - one-time bulk update
const products = await db.list('Product', { where: { category: 'old' } })
for (const product of products) {
  await db.update('Product', product.id, { category: 'updated' })
}
;({ updated: products.length })

Use Module For:

Event Handlers

// ✅ Module - persistent event subscription
on($.Order.created, async (order) => {
  await send($.Email.send, { to: order.customer.email, ... })
})

Scheduled Tasks

// ✅ Module - recurring cron job
every('0 9 * * *', async () => {
  const report = await generateDailyReport()
  await send($.Email.send, { to: '[email protected]', body: report })
})

Workflow Orchestration

// ✅ Module - multi-step workflow with events
on($.Payment.succeeded, async (payment) => {
  await db.update('Order', payment.orderId, { status: 'paid' })
  await send($.Fulfillment.start, { orderId: payment.orderId })
})

on($.Fulfillment.start, async (fulfillment) => {
  // Process fulfillment...
  await send($.Order.shipped, { orderId: fulfillment.orderId })
})

Background Job Processing

// ✅ Module - async job queue
on($.Job.export, async (job) => {
  const data = await db.list(job.collection)
  const csv = convertToCSV(data)
  await send($.Email.send, {
    to: job.userEmail,
    subject: 'Export Complete',
    attachments: [{ filename: 'export.csv', content: csv }],
  })
})

Use Both For:

Setup + Trigger Pattern

// ✅ Combined - register handlers then execute
on($.Order.created, handler1)
on($.Payment.succeeded, handler2)

const order = await db.create('Order', { ... })
await send($.Order.created, order)  // Triggers handler1

order  // Return value from script

Immediate Action + Future Monitoring

// ✅ Combined - do something now, watch later
const deployment = await db.create('Deployment', { status: 'starting' })

on($.Deployment.failed, async (event) => {
  if (event.deploymentId === deployment.id) {
    await send($.Alert.send, { type: 'deployment_failed', ... })
  }
})

// Start deployment
await api.fetch('https://deploy.acme.com/start', {
  method: 'POST',
  body: JSON.stringify({ deploymentId: deployment.id })
})

deployment

Anti-Patterns to Avoid

❌ Using Module for One-Time Query

// ❌ Bad - module for simple query
on($.Query.business, async () => {
  return await db.list('Business')
})

// ✅ Good - script for immediate result
await db.list('Business')

❌ Using Script for Persistent Handler

// ❌ Bad - handler won't persist after script ends
const orders = await db.list('Order')
for (const order of orders) {
  // This won't continue running
  on($.Order.updated, handler)
}

// ✅ Good - module registers persistent handler
on($.Order.updated, async (order) => {
  // Handles all future order updates
})

❌ Forgetting to Return from Script

// ❌ Bad - no return value
const result = await db.create('Order', { ... })
await send($.Email.send, { ... })
// Returns undefined

// ✅ Good - explicit return
const result = await db.create('Order', { ... })
await send($.Email.send, { ... })
result  // Returns the order

❌ Using Script for Recurring Task

// ❌ Bad - script runs once then stops
const pending = await db.list('Order', { where: { status: 'pending' } })
for (const order of pending) {
  await send($.Order.reminder, { orderId: order.id })
}

// ✅ Good - scheduled task runs automatically
every('0 * * * *', async () => {
  const pending = await db.list('Order', { where: { status: 'pending' } })
  for (const order of pending) {
    await send($.Order.reminder, { orderId: order.id })
  }
})

Module vs Script at a Glance

Comparison Table

AspectScriptModule
ExecutionImmediate, onceTriggered, recurring
LifecycleEphemeral (seconds)Persistent (indefinite)
Return ValueFinal expressionNone (registers behaviors)
Primary UseQueries, operationsEvent handlers, schedules
AuthenticationOptional for readsRequired
Timeout10 seconds (configurable)Per-trigger timeout
StateNo persistent stateMaintains subscriptions
Examplesawait db.list()on(), every()

Execution Model Differences

Script Execution Flow:

Parse → Execute → Return → Cleanup
   ↓       ↓        ↓       ↓
  AST    Run code  Value   End

Module Execution Flow:

Parse → Register → Wait → Trigger → Execute → Repeat
   ↓        ↓        ↓       ↓        ↓         ↓
  AST  Subscribe  Idle   Event   Run code   Continue

Persistence Differences

Scripts:

  • Execute once
  • Return result
  • Context destroyed
  • No memory of execution
  • Suitable for: queries, one-time tasks, data analysis

Modules:

  • Register subscriptions
  • Run on triggers
  • Context persists
  • Maintains state between triggers
  • Suitable for: workflows, monitoring, automation

Return Value Differences

Scripts:

// Returns order object
const order = await db.create('Order', { ... })
order

// Returns array
await db.list('Business')

// Returns computed value
const x = 10
const y = 20
x + y  // Returns 30

Modules:

// Returns nothing (registers handler)
on($.Order.created, async (order) => {
  await send($.Email.send, { ... })
})

// Returns nothing (registers schedule)
every('0 9 * * *', async () => {
  await generateReport()
})

Use Case Differences

Script Use Cases:

  1. Database queries
  2. Data transformations
  3. API integrations
  4. One-time migrations
  5. Ad-hoc reports
  6. Immediate operations

Module Use Cases:

  1. Event-driven workflows
  2. Scheduled tasks
  3. Background processing
  4. Continuous monitoring
  5. Automated responses
  6. Long-running processes

Next Steps

On this page

The do ToolWhy Code Mode?Tool DefinitionParametersType SignaturesExecution ModesScript Execution: Imperative OperationsScript LifecycleScript CharacteristicsScript Return SemanticsModule Execution: Persistent WorkflowsModule LifecycleModule CharacteristicsModule Registration SemanticsUsing the module ParameterMDXLD Module StructureModule Parameter FeaturesWhen to Use module vs scriptExample: Creating a ModuleCombined Execution: Scripts + ModulesDocumentation RequestsAvailable Modules$ - Semantic Context Proxydb - Database Operationsai - AI Operationsapi - HTTP Operationson - Event Handlerssend - Event Publishingevery - Scheduled Workflowsuser - User ContextExamplesSimple QueriesCreating RecordsAI-Powered WorkflowsEvent-Driven ArchitectureScheduled TasksMulti-Step OperationsSecurity & PermissionsReadonly Mode (Anonymous)Authenticated ModePermission ChecksError HandlingExecution ErrorsReadonly ViolationsTimeout ErrorsAuthentication ErrorsBest Practices1. Use Semantic Types2. Handle Async Properly3. Use Type Inference4. Compose Operations5. Use Batch Operations6. Parallel ExecutionPerformance Tips1. Limit Results2. Use Filters3. Batch AI Operations4. Cache Expensive OperationsDebugging1. Use Console Logging2. Inspect Variables3. Error HandlingModule Exports and CompositionExporting Functions from ModulesExporting WorkflowsExporting Event HandlersExporting Scheduled TasksModule Composition and ReusabilityScript Composition PatternsMulti-Step Script WorkflowsError Handling PatternsTransaction PatternsBatch Processing PatternsParallel Execution PatternsReal-World ScenariosScenario 1: E-Commerce Order Processing (Script + Module)Scenario 2: Content Management Workflows (Module)Scenario 3: Analytics Pipeline (Script)Scenario 4: Customer Support Automation (Module)When to Use Each ModeDecision TreeUse Script For:Use Module For:Use Both For:Anti-Patterns to AvoidModule vs Script at a GlanceComparison TableExecution Model DifferencesPersistence DifferencesReturn Value DifferencesUse Case DifferencesNext Steps