Skip ahead — hand this to your coding agent
Add a type-inferred tool to an existing route.
Tools
Tools are the units of work a route's entry can invoke. They live in a tools/ subdirectory inside a route, are discovered automatically, and have their types inferred from source — no Zod schemas, no manual type wiring.
A minimal tool
// src/app/(public)/hello/[tenant]/tools/greet.ts
export default async (input: { readonly tenant: string }) => {
return { greeting: `Hello, ${input.tenant}!` }
}
That's it. Dawn extracts the input and output types at build time using the TypeScript compiler API and writes them into dawn.generated.d.ts. The tool becomes available as ctx.tools.greet inside the route's entry, fully typed.
Invoking a tool
// in the route's index.ts
export async function workflow(state, ctx) {
const result = await ctx.tools.greet({ tenant: state.tenant })
return { ...state, greeting: result.greeting }
}
ctx.tools.greet has full IntelliSense — input shape, return shape, everything.
Input and output rules
- 1
Use a typed input parameter
Annotate the input parameter with an inline type. This is what Dawn's compiler pass extracts.
export default async (input: { readonly tenant: string; readonly limit?: number }) => { ... } - 2
Mark fields readonly
readonlyis preserved through type generation. Use it on every field — tool inputs are pure data, never mutated. - 3
Keep inputs and outputs JSON-serializable
Dawn serializes through the runtime boundary. Classes, Dates, Maps, functions — none of them survive. Use primitives, plain objects, and arrays.
- 4
Return a plain object
Always return an object literal, not a primitive or array. Dawn's type inference handles shape-based returns cleanly; a bare
return someStringmuddles the generated types.
The generated declaration
Dawn writes a dawn.generated.d.ts file at your app root that looks roughly like:
declare module "dawn:routes" {
export type RouteTools<P> = DawnRouteTools[P]
// greet signature inferred from tools/greet.ts export
}
Regenerate manually if needed:
dawn typegen
Common patterns
- External API wrappers — one tool per endpoint. Input shape mirrors the endpoint's parameters; output is the parsed response.
- Database reads — input includes the filters; output is the record(s). Keep queries cheap — tools are meant to be fast.
- LLM calls — input is the prompt variables; output is the parsed model response. Defer orchestration to the route entry.
- Pure transformations — no side effects, just shape-to-shape mapping. Dawn's testing makes these trivial to verify.