TypeScript
The TypeScript SDK.
@subako-ai/function-sdk runs TypeScript and JavaScript as a function package. Each tool becomes a function an agent can call.
Every JavaScript package shares one prebuilt runtime module. build bundles your code and compiles it to bytecode the runtime loads from the package’s assets, so there is no WebAssembly toolchain to install: Node 20 or newer is all it needs.
Start a package
git clone https://github.com/subako-ai/subako-function-template-typescript orders
Rename my-function in package.json and subako-package.json, and describe the package there. Or set up a project of your own:
npm install @subako-ai/function-sdk zod
{
"compilerOptions": {
"lib": ["ES2023"],
"types": ["@subako-ai/function-sdk/env"],
"strict": true
}
}
The env types declare exactly the globals the runtime provides, so a DOM or Node API is a type error rather than a crash.
Tools
import { serve, tool } from "@subako-ai/function-sdk";
import { z } from "zod";
const lookup = tool({
name: "lookup",
description: "Look an order up by its number.",
input: z.object({ order: z.number().int().describe("the order's number, such as 1042") }),
output: z.object({ status: z.string(), total_cents: z.number() }),
run: async ({ order }) => {
const response = await fetch(`https://api.acme.example/orders/${order}`);
if (!response.ok) throw new Error(`the orders API answered ${response.status}`);
return await response.json();
},
});
serve([lookup]);
inputandoutputtake any Standard Schema that also emits JSON Schema — zod 4, valibot, arktype — or a plain JSON Schema object, which describes but does not validate. The input must be an object.- Input is validated before
run; output is checked againstoutputwhen it validates. - A thrown error is a failed call the model reads. A returned object is sent as JSON; a string as text.
servetakes every tool, once, from the entry point.
Prefer the official MCP TypeScript SDK? serveMcp from @subako-ai/function-sdk/mcp serves an McpServer instead.
Files
import { fs } from "@subako-ai/function-sdk";
const csv = fs.readText("/volume/tmp/export.csv");
fs.mkdir("/volume/data/reports");
fs.writeText("/volume/data/reports/q3.md", summary);
fs also offers readBytes, writeBytes, appendText, remove, stat, exists, and list.
HTTP
fetch goes through Subako, under the grant’s network and credentials. It carries text bodies; for a volume file as the body, or a response saved to the volume, use http.request:
import { http } from "@subako-ai/function-sdk";
const saved = await http.request({
url: "https://api.acme.example/exports/q3",
saveTo: "/volume/tmp/q3.csv",
});
// saved.file: { path: "/volume/tmp/q3.csv", bytes: 48211 }
A failure rejects with an HttpError carrying a kind and a message.
Images
import { answer } from "@subako-ai/function-sdk";
return answer({ path }).withImageFile(path);
The runtime
The runtime is not Node and not a browser. Alongside the language itself it provides console (to stderr), timers, TextEncoder and TextDecoder, crypto.getRandomValues and randomUUID, URL, Headers, Response, AbortController, and fetch — and nothing else. Packages from npm that need only these bundle and run; ones that reach for Node’s modules do not.
Build
npx @subako-ai/function-sdk build # writes dist/: module.wasm, subako-package.json, assets/
npx @subako-ai/function-sdk pack # packs dist/ into <name>.tar.gz
build needs subako-package.json in the current directory, and takes the entry from src/index.ts unless you name another. Upload the .tar.gz in the console, or the dist/ directory with subako function create dist.