Recipes
This page collects practical integration patterns built on top of the public Ageniti API.
CLI-First Internal Tool
Use this pattern when the main interface is a terminal command for operators or scripts.
import { createAgenitiApp, defineAction, s } from "@ageniti/core";
const searchTasks = defineAction({
name: "search_tasks",
description: "Search tasks by status.",
input: s.object({
status: s.enum(["open", "blocked", "done"]),
}),
run(input, ctx) {
return ctx.services.tasks.search(input);
},
});
const app = createAgenitiApp({
name: "ops-tools",
actions: [searchTasks],
services: { tasks },
});
await app.createCli().main();Why this works well:
- one command entry point
- JSON schema for arguments
- structured results for shell wrappers and tests
Existing React App Button
Use this pattern when the app already has UI handlers and you want to reuse the same business capability for agent-facing flows.
const { useAction } = app.createReactAdapter();
const runSearchTasks = useAction(searchTasks);
async function onClick() {
const result = await runSearchTasks({ status: "open" });
if (result.ok) {
setTasks(result.data.items);
}
}Recommended practice:
- keep confirmation UX in the UI
- keep permission context in
invokeOptions - reuse the same action in non-UI surfaces later
JSON Runner For Automation
Use this pattern for scripts, queues, cron jobs, or test harnesses.
const runner = app.createJsonRunner();
const result = await runner.invoke({
action: "search_tasks",
input: { status: "open" },
metadata: { source: "nightly-job" },
});This is often the simplest way to wrap Ageniti behind your own transport.
Repository example:
ageniti/examples/task-app.js
MCP Tools For Agent Platforms
Use this when a host system already speaks MCP-style JSON-RPC.
const handle = app.createMcpHandler();
const list = await handle({
jsonrpc: "2.0",
id: 1,
method: "tools/list",
});
const call = await handle({
jsonrpc: "2.0",
id: 2,
method: "tools/call",
params: {
name: "search_tasks",
arguments: { status: "open" },
},
});If the host expects stdio instead of an in-process handler, use:
await app.createCli().run(["mcp", "--stdio"]);Repository example:
ageniti/examples/mcp-host.js
OpenAI And AI SDK Tool Adapters
Use this when your host application already knows how to call tools through OpenAI-compatible schemas or AI SDK tool objects.
const openaiTools = app.createOpenAITools();
const responsesTools = app.createOpenAIResponsesTools();
const aiSdkTools = app.createAISDKTools({ returnEnvelope: true });Recommendations:
- keep destructive actions excluded by default
- add explicit filters for high-risk tools
- use
returnEnvelope: truewhen the caller needs logs or artifacts
OpenAI Responses Host
Use this pattern when your host app is already calling the OpenAI Responses API and only needs a stable tool set from the app SDK.
import OpenAI from "openai";
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const tools = app.createOpenAIResponsesTools();
const response = await client.responses.create({
model: "gpt-4.1",
input: "Find the blocked tasks and summarize the owners.",
tools,
});Why this matters:
- the host owns model choice and prompting
- Ageniti owns app capability contracts
- tool schemas stay aligned with the runtime
Repository example:
ageniti/examples/openai-responses-host.js
Vercel AI SDK Route Handler
Use this when a Next.js or server-side host already uses the AI SDK and you want executable tools backed by the shared runtime.
import { generateText } from "ai";
const tools = app.createAISDKTools({ returnEnvelope: true });
const result = await generateText({
model,
prompt: "Create a high-priority follow-up task for the open incident.",
tools,
});Recommended practice:
- keep app auth outside the model call and inject it through runtime options
- return envelopes when the host needs logs or artifacts
- keep risky tools filtered unless the host is trusted
Repository example:
ageniti/examples/ai-sdk-route.js
HTTP Gateway Wrapper
Use this when your own backend should own transport, auth, and deployment, while Ageniti stays focused on action execution.
const handle = app.createHttpHandler();
const response = await handle({
method: "POST",
url: "/ageniti/actions/create_task/invoke",
body: {
input: {
title: "Write the release review summary",
priority: "high",
},
auth: {
permissions: ["task:create"],
},
},
});Recommended practice:
- keep caller identity and authorization in the gateway layer
- pass only the app-specific permission context into Ageniti
- keep the HTTP payload close to the runtime envelope for debugging
Repository example:
ageniti/examples/http-gateway.js
Restricting Surface Exposure
Use action metadata to keep high-risk operations off agent-facing surfaces.
const deleteTask = defineAction({
name: "delete_task",
description: "Delete a task by id.",
sideEffects: "destructive",
supportedSurfaces: ["cli", "react", "dev"],
input: s.object({
taskId: s.string(),
}),
run(input, ctx) {
return ctx.services.tasks.remove(input.taskId);
},
});This keeps the action available for trusted operator paths while excluding it from MCP and LLM tool exposure.
Manifest And Lint In CI
Use this pattern to keep the contract observable during release checks.
const manifest = app.manifest();
const lint = app.lint();
if (!lint.ok) {
process.exitCode = 1;
}You can snapshot the manifest in tests, or publish it as an internal contract artifact for tooling teams.