Handler signature
Every exported async function with a (ctx, args) signature becomes a manifest entry at build time; the host registers from the manifest, never by introspecting the code.
import type { GatewayContext } from "../../.embabel/gateway";
/** * Return the heading outline of a Google Doc. PREFER this over * `gateway.docs.documentsGet` when you only need structure. */export async function getOutline( ctx: GatewayContext, args: { documentId: string },): Promise<{ revisionId: string; spans: Array<{ anchor: string; level: number; text: string }> }> { const doc = await ctx.docs.documentsGet({ documentId: args.documentId }); return { revisionId: doc.revisionId ?? "", spans: /* walk doc.body.content */ [] };}The host’s manifest extractor walks the TypeScript AST, converts the args parameter type and the unwrapped return type to JSON Schema, and writes them to dist/manifest.json. The first JSDoc paragraph becomes the LLM-visible description — invest in JSDoc: it’s how the LLM picks the right method when multiple gateway surfaces overlap.
Authoring before sync — GenericGatewayContext
Section titled “Authoring before sync — GenericGatewayContext”embabel-realm sync (which generates .embabel/gateway.d.ts with the host’s fully-typed GatewayContext) is still in flight. Until it lands, a realm whose handlers only need to call gateway ops — not the static types of their results — can type ctx as GenericGatewayContext from @embabel/runtime-types (a loose Record<string, Record<string, (args) => Promise<unknown>>>). The manifest extractor reads each handler’s args and return types, not ctx, so the typed LLM surface is identical either way.
A namespace function that only needs to call gateway ops can type ctx loosely:
import type { GenericGatewayContext } from "@embabel/runtime-types";
/** Current conditions for a city. */export async function current( ctx: GenericGatewayContext, args: { city: string },): Promise<unknown> { return ctx.openWeather.getCurrent({ q: args.city });}Caveat: the Record index access trips noUncheckedIndexedAccess, so leave that
flag off in tsconfig.json when using GenericGatewayContext (the generated
GatewayContext has concrete properties and doesn’t need it). Once sync lands,
swap GenericGatewayContext → GatewayContext for full result typing — nothing
else changes. The same applies to a type method’s this.gateway.

