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: true when 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.