DwiziDwizi

Browse docs

Technical Guides

Writing stateless, type-safe functions for AI execution.

Authoring Tools for Dwizi

Dwizi tools are intentionally simple in structure, so they can be reliably executed by the platform and reliably called by LLMs.

A tool is just:

  • One file
  • One default export
  • One async function
  • One input object
  • One JSON output

That's the contract.

The Only Valid Shape

Required:

  • export default
  • async
  • Exactly one parameter
  • That parameter is an object
export default async function run(input: { name: string }) {
  return { greeting: `Hello, ${input.name}` };
}

Why we enforce this:

  • Dwizi can infer the input schema
  • MCP / JSON-RPC clients can call tools predictably
  • LLMs don't guess what your function wants

Input Schema Inference

Dwizi reads your input schema from the first parameter type.

Best: Inline TypeScript object type

This produces the cleanest schema and best tool-call reliability.

export default async function run(input: { email: string; days?: number }) {
  return { ok: true };
}

Also great: Destructuring (with an explicit type)

export default async function run(
  { email, days }: { email: string; days?: number }
) {
  return { ok: true };
}

Works, but weaker: Plain JavaScript

Dwizi can infer from property usage, but it's heuristic.

export default async function run(input) {
  const total = Number(input.amount);
  return { total };
}

Use JavaScript only if you have a strong reason. For production tools, prefer TypeScript-first.

Runtime Examples: Deno vs Node

Dwizi supports multiple runtimes, but the tool contract stays identical.

import { z } from "https://esm.sh/zod@3.24.1";

export default async function run(input: { name: string }) {
  const parsed = z.object({ name: z.string() }).parse(input);
  return { greeting: `Hello, ${parsed.name}` };
}

Why Deno wins for tools:

  • TypeScript runs natively
  • URL imports remove package manager friction
  • Great for "LLM-generated tooling" workflows

Node.js: Built-in APIs (legacy compatibility)

import crypto from "node:crypto";

export default async function run(input: { value: string }) {
  const hash = crypto.createHash("sha256").update(input.value).digest("hex");
  return { hash };
}

Use Node when you specifically need Node-only APIs or legacy code.

Output Rules

Your return value must be JSON-serializable:

✅ Allowed:

  • Objects, arrays
  • Strings, numbers, booleans
  • null

🚫 Avoid:

  • Dates (convert to ISO strings)
  • Class instances
  • Functions
  • Circular references
  • BigInt (convert to string)

Example (good):

export default async function run() {
  return { nowISO: new Date().toISOString() };
}

Common Failure Modes

  • No default export → Dwizi can't load the tool
  • Not async → you'll fight I/O immediately
  • No parameters → schema inference breaks (LLMs won't know what to pass)
  • Multiple parameters → only the first is used; the rest are ignored
  • Non-JSON output → tool fails at serialization

LLM Authoring Pattern (This is the secret sauce)

LLMs should write the words, tools should do the work.

Examples:

  • LLM writes the Slack summary → tool posts it to Slack
  • LLM drafts the email → tool sends it via Resend
  • LLM builds a table → tool appends it to Google Sheets

So when you prompt an LLM, your goal is: structured input + deterministic action.

Prompt Template for Tool-Ready Code

Use this template to get code that works on the first deploy:

Write a TypeScript tool as a single file.

Hard requirements:
- export default async function run(input: {...}) { ... }
- TypeScript
- Inline input type (object)
- Return JSON-serializable output only
- No external files, no package.json
- Target runtime: Deno (use URL imports if needed)

Tool intent:
- The LLM will generate text content (summaries/emails)
- The tool must perform the external action (API call) using that content
- Inputs must be explicit and minimal

Tool description:
"Post a list of bullet points to Slack using an Incoming Webhook."

Quick Checklist Before You Deploy

  • One file
  • export default async function
  • One typed input object
  • Return JSON-only
  • Use Deno URL imports when you need dependencies
  • Inputs are explicit enough for an LLM to call without guessing

Clear inputs create reliable tools. Reliable tools create real workflows.