Getting Started

Ageniti helps an existing React or TypeScript application expose selected app capabilities to agents and automation tools.

It does not create an agent. It makes your app callable by agents.

Install

npm install @ageniti/core

Requirements:

  • ESM-only package
  • Node.js 20 or newer

1. Pick One Existing Capability

Start with one application capability you already trust:

  • create a task
  • search workspace records
  • export a report
  • send a message
  • summarise a document

Do not start by rewriting the app. Start by wrapping one stable capability.

2. Define An Action

import { defineAction, s } from "@ageniti/core";
 
export const createTask = defineAction({
  name: "create_task",
  description: "Create a workspace task.",
  visibility: "public",
  sideEffects: "write",
  idempotency: "conditional",
  permissions: ["task:create"],
  input: s.object({
    title: s.string().min(1).describe("Task title"),
    assignee: s.string().optional().describe("Optional assignee id"),
    priority: s.enum(["low", "normal", "high"]).default("normal"),
  }),
  output: s.object({
    taskId: s.string(),
    status: s.string(),
    priority: s.string(),
  }),
  async run(input, ctx) {
    ctx.logger.info("Creating task.", { title: input.title });
    return ctx.services.tasks.create(input);
  },
});

Useful defaults from defineAction():

  • title defaults to a humanised version of name.
  • input defaults to s.object({}).
  • visibility defaults to "local".
  • sideEffects defaults to "read".
  • idempotency defaults to "unspecified".
  • supportedSurfaces defaults to cli, json, http, mcp, react, dev, and ai-sdk.
  • Destructive actions require confirmation unless you override requiresConfirmation.

3. Create An App

import { createAgenitiApp } from "@ageniti/core";
import { createTask } from "./actions/create-task.js";
import { tasks } from "./services/tasks.js";
 
export const app = createAgenitiApp({
  name: "task-app",
  description: "Workspace task operations exposed to agents.",
  docs: {
    summary: "Use this app to create tasks and inspect task state.",
  },
  actions: [createTask],
  services: {
    tasks,
  },
  permissionChecker({ action, context }) {
    return action.permissions.every((permission) =>
      context.auth?.permissions?.includes(permission)
    ) || "Missing required permission.";
  },
});

createAgenitiApp() gives you a shared runtime plus convenience methods for every supported surface.

4. Write Natural-Language Guidance

Ageniti can export one unified guide from natural-language fields on the app and actions. It does not call a model or infer hidden behaviour from your UI. It reads the contract you explicitly wrote.

export const createTask = defineAction({
  name: "create_task",
  description: "Create a workspace task.",
  docs: {
    whenToUse: "Use this when an agent needs to create a trackable task.",
    whenNotToUse: "Do not use this for multi-step approvals.",
    usageNotes: [
      "The caller should provide a short, human-readable title.",
      "High priority tasks may trigger downstream notifications.",
    ],
    inputExample: {
      title: "Follow up with design review",
      priority: "high",
    },
    outputExample: {
      taskId: "task_123",
      status: "open",
      priority: "high",
    },
  },
  run(input, ctx) {
    return ctx.services.tasks.create(input);
  },
});
 
export const app = createAgenitiApp({
  name: "task-app",
  description: "Workspace task operations exposed to agents.",
  docs: {
    summary: "Use this app to create tasks and inspect task state.",
    audience: "Internal agents, automation scripts, and operator tools.",
    whenToUse: [
      "Use it when an agent needs structured task creation or lookup.",
      "Use it when the caller needs CLI, MCP, HTTP, or LLM tool surfaces.",
    ],
    setup: [
      "Make sure the caller has the required permissions.",
      "Test action input through the dev console or CLI first.",
    ],
    operationalNotes: [
      "Destructive actions are not exposed to MCP and LLM tools by default.",
      "Put internal-only details in metadata, and exposed guidance in publicMetadata or docs.",
    ],
  },
  actions: [createTask],
});

Those fields are merged into one GUIDE.md, which can include:

  • app overview
  • intended audience
  • when to use it
  • setup and usage steps
  • operational notes
  • available actions
  • each action's description, permissions, side effects, surfaces, example input, and example output

Export it with:

task-app docs
task-app docs --out-dir ./dist/ageniti
task-app build docs --out-dir ./dist/ageniti
task-app build bundle --out-dir ./dist/ageniti

task-app docs prints the guide to stdout. With --out-dir, it writes GUIDE.md. build bundle automatically includes GUIDE.md in the final bundle.

The guide is useful for:

  • agent host documentation
  • MCP server installation notes
  • internal operator docs
  • package-level usage instructions
  • human review of the action contract

5. Expose CLI

#!/usr/bin/env node
import { app } from "./app.js";
 
await app.createCli().main();

Example commands:

task-app create-task --title "Follow up with design review" --priority high
task-app create-task --json '{"title":"Follow up with design review","priority":"high"}'
task-app create-task --schema
task-app actions
task-app manifest
task-app build
task-app build docs
task-app docs
task-app package
task-app publish
task-app lint
task-app mcp
task-app mcp --stdio
task-app dev --port 4321

Notes:

  • Action names can be called as snake_case or kebab-case.
  • --schema prints the input JSON Schema.
  • --confirm is required for actions that require confirmation.
  • CLI exit codes are mapped from runtime error codes. See Runtime Semantics.

6. Expose JSON Automation

const runner = app.createJsonRunner();
 
const result = await runner.invoke({
  action: "create_task",
  input: {
    title: "Follow up with design review",
    priority: "high",
  },
});

Use the JSON runner when a script, test harness, worker, or HTTP wrapper already has structured input and output.

7. Expose MCP

const handleMcp = app.createMcpHandler();
 
await handleMcp({
  jsonrpc: "2.0",
  id: 1,
  method: "tools/list",
});
 
await handleMcp({
  jsonrpc: "2.0",
  id: 2,
  method: "tools/call",
  params: {
    name: "create_task",
    arguments: {
      title: "Follow up with design review",
      priority: "high",
    },
  },
});

MCP exposure is filtered by default:

  • actions must support the mcp surface
  • private actions are excluded unless you opt in
  • destructive actions are excluded unless you opt in

8. Expose OpenAI And AI SDK Tools

const openaiTools = app.createOpenAITools();
const responsesTools = app.createOpenAIResponsesTools();
const aiSdkTools = app.createAISDKTools();
 
const task = await aiSdkTools.create_task.execute({
  title: "Follow up with design review",
  priority: "high",
});

Default LLM tool filtering:

  • private actions are excluded
  • destructive actions are excluded
  • the action must support the ai-sdk surface

If you want the full runtime envelope instead of raw data, pass returnEnvelope: true when you build AI SDK tools.

9. Use From Existing React UI

const { useAction } = app.createReactAdapter();
const runCreateTask = useAction(createTask);
 
const result = await runCreateTask({
  title: "Follow up with design review",
  priority: "high",
});

React invocation uses the same runtime but forces the surface to react. Confirmation is not blocked at runtime for the react surface, so your UI should own the confirmation flow.

10. Start The Local Dev Console

task-app dev --port 4321

The dev console provides:

  • action list
  • generated input schema
  • JSON input editor
  • structured runtime result preview

11. Build Official Artifacts

When you want to ship CLI or MCP launchers from the same app definition:

await app.build({
  targets: ["bundle"],
  appModule: "./src/ageniti/app.js",
  appExport: "app",
  outDir: "./dist/ageniti",
});

If you prefer the CLI flow:

task-app build bundle --app-module ./src/ageniti/app.js --app-export app --out-dir ./dist/ageniti
task-app build docs --out-dir ./dist/ageniti
task-app docs --out-dir ./dist/ageniti

If appModule is omitted, Ageniti tries to discover a default Node-safe entry such as ./src/ageniti/app.js.

Bundle output includes:

  • ageniti.manifest.json
  • ageniti.actions.json
  • cli.mjs
  • mcp-stdio.mjs
  • ageniti.mcp.json
  • GUIDE.md
  • package.json
  • README.md
  • ageniti.bundle.json

If you also want a distributable tarball, run:

task-app package --app-module ./src/ageniti/app.js --app-export app --out-dir ./dist/ageniti

That command builds the bundle and runs npm pack inside it.

For publish rehearsal or release:

task-app publish --app-module ./src/ageniti/app.js --app-export app --out-dir ./dist/ageniti
task-app publish --live --access public --tag latest --app-module ./src/ageniti/app.js --app-export app --out-dir ./dist/ageniti

publish defaults to npm publish --dry-run. Pass --live only for a real publish.

12. Bootstrap Or Diagnose A Project

If you are starting from an existing React or Expo application, Ageniti also includes project tools:

npx @ageniti/core init react
npx @ageniti/core init expo
npx @ageniti/core init next
npx @ageniti/core doctor

Use init to scaffold a headless entry. Use doctor to inspect whether the current project already has a valid build entry and whether it looks safe for launcher generation.

If you want reproducible defaults for every teammate, add ageniti.config.json:

{
  "build": {
    "appModule": "./src/ageniti/app.ts",
    "appExport": "app",
    "outDir": "./dist/ageniti",
    "includePackageJson": true,
    "typescriptRuntime": "tsx"
  },
  "mcp": {
    "transport": "stdio"
  },
  "package": {
    "name": "task-app-bundle",
    "version": "0.1.0",
    "description": "CLI and MCP bundle for task-app",
    "private": false,
    "binName": "task-app"
  }
}

After that, your generated app CLI can run build without repeating the same flags, for example task-app build.

13. TypeScript-Only Entrypoints

If your project uses src/ageniti/app.ts without a JavaScript wrapper, Ageniti can still generate CLI and MCP launchers when the tsx runtime is available.

Typical options:

  • install tsx and set build.typescriptRuntime to tsx
  • point --app-module at compiled JavaScript
  • create a tiny src/ageniti/app.js wrapper that re-exports the app

When Ageniti detects a React page, layout, or component file instead of a headless entry, doctor and build fail with a targeted error so you do not accidentally package UI modules as MCP or CLI launchers.