Cookbook
Five complete examples: structured extraction, Q&A over internal data, streaming to a frontend, batch runs, file edits with built-in tools.
Five examples, all against the current API.
1. Structured extraction
No tools — just the model and one run():
import { Agent } from '@neoxlabs/sdk';
const agent = new Agent({
model: 'claude-sonnet-4-6',
systemPrompt: 'Return JSON only. No commentary.',
});
const { text, usage } = await agent.run(`Extract this résumé as JSON:\n${resume}`);
console.log(JSON.parse(text), usage);2. Q&A over internal data (read-only tools)
import { Agent, tool } from '@neoxlabs/sdk';
import { z } from 'zod';
const searchOrders = tool({
name: 'search_orders',
description: 'Look up orders by customer id; returns the 20 most recent with status and amount. Read-only.',
schema: z.object({ customerId: z.string() }),
handler: async ({ customerId }) => db.orders.byCustomer(customerId, { limit: 20 }),
});
const agent = new Agent({
model: 'claude-sonnet-4-6',
tools: [searchOrders],
maxSteps: 12,
});
const res = await agent.run('Did customer C-88 have any failed payments recently?');
console.log(res.text);3. Stream the run to a frontend
export async function POST(req: Request) {
const { prompt } = await req.json();
const agent = new Agent({ model: 'claude-sonnet-4-6', tools });
const stream = new ReadableStream({
async start(controller) {
for await (const ev of agent.stream(prompt)) {
controller.enqueue(new TextEncoder().encode(`data: ${JSON.stringify(ev)}\n\n`));
}
controller.close();
},
});
return new Response(stream, { headers: { 'Content-Type': 'text/event-stream' } });
}The client renders text, tool calls and completion by ev.type.
4. Batch work with caps and timeouts
const results = [];
for (const item of items) {
const agent = new Agent({
model: 'deepseek-chat',
tools: [classify],
maxSteps: 6, // per-item cap
signal: AbortSignal.timeout(60_000),
});
const r = await agent.run(item.text);
results.push({ id: item.id, text: r.text, stopReason: r.stopReason, usage: r.usage });
}
const spent = results.reduce((n, r) => n + r.usage.inputTokens + r.usage.outputTokens, 0);One Agent per item keeps contexts clean; stopReason flags the rows that need human review.
5. Let the agent touch files (built-in tools)
import { Agent } from '@neoxlabs/sdk';
import { builtinTools } from '@neoxlabs/sdk/tools';
const agent = new Agent({
model: 'claude-sonnet-4-6',
tools: [
...builtinTools.fs({ root: './src', allowWrite: true }),
...builtinTools.shell({ allowedCommands: ['npm'] }),
],
maxSteps: 20,
// writes and commands are dangerous — approve them one by one
permission: async ({ tool, input, dangerous }) => {
if (!dangerous) return { approved: true };
return { approved: await confirmWithUser(tool, input), reason: 'needs confirmation' };
},
});
const stream = agent.stream('Add unit tests for untested functions in utils, then run npm test');
for await (const ev of stream) render(ev);
const { stopReason, usage } = await stream.result();Every fs path is pinned inside root (symlink escapes included); without allowWrite you only get read tools.
Choosing
| Situation | Use |
|---|---|
| One question, one answer, no tools | agent.run() |
| Fixed steps that never change | plain code calling the model — skip the agent |
| Steps depend on intermediate results | agent.run() with tools |
| The process must be visible | agent.stream() |
| Irreversible side effects | approval logic inside handler, plus dangerous |

