Getting Started
Ageniti is the action primitive layer for agentic apps. You define an action once and the runtime makes it callable from CLI, HTTP, MCP, OpenAI / AI SDK tools, the typed client, and React — all from the same contract.
If you already have a React or TypeScript app, you don't need to restructure it — just declare which capabilities should be addressable as actions and mount the surfaces you want.
For the conceptual tour of the five core primitives (action contract, schema interop, bulk handlers, streaming events, typed client), see Primitives.
Open in your coding assistant
Download a bootstrap markdown file for your local coding assistant. It tells the assistant to clone the repo, find the SDK package root, read the right docs, and start from there without extra setup from the user.
Install
npm install @ageniti/coreRequirements:
- 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():
titledefaults to a humanised version ofname.inputdefaults tos.object({}).visibilitydefaults to"public".sideEffectsdefaults to"read".idempotencydefaults to"unspecified".supportedSurfacesdefaults tocli,json,http,mcp,react,dev, andai-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 external tools, automation, and agent callers.",
attribution: {
text: "Powered by Ageniti",
vendor: "Ageniti",
product: "Ageniti Core",
url: "https://ageniti.dev",
docsUrl: "https://ageniti.dev/docs",
},
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.
If you want generated CLI help, MCP manifests, tool metadata, and bundle artifacts to include visible origin information, set the optional attribution field on the app.
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 external caller 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 external tools, automation, and agent callers.",
docs: {
summary: "Use this app to create tasks and inspect task state.",
audience: "Internal tools, automation scripts, agent hosts, and operators.",
whenToUse: [
"Use it when an external caller 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/agenititask-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 4321Notes:
- Action names can be called as snake_case or kebab-case.
--schemaprints the input JSON Schema.--confirmis 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
mcpsurface privateactions are excluded unless you opt indestructiveactions 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-sdksurface
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 4321The 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/agenitiIf appModule is omitted, Ageniti tries to discover a default Node-safe entry such as ./src/ageniti/app.js.
Bundle output includes:
ageniti.manifest.jsonageniti.actions.jsoncli.mjsmcp-stdio.mjsageniti.mcp.jsonGUIDE.mdpackage.jsonREADME.mdageniti.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/agenitiThat 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/agenitipublish defaults to npm publish --dry-run. Pass --live only for a real publish.
The generated bundle also includes its own README.md. That file explains the concrete deployment shapes:
- CLI: publish or distribute the generated npm package, then users run the generated bin.
- MCP: point a host at
node ./mcp-stdio.mjsduring local development, or at the published<bin-name>-mcpcommand after install. - HTTP: deploy your own backend and mount
app.createHttpHandler()so your existing auth and infrastructure stay in control.
See Packaging for the full install, publish, and MCP client examples.
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 doctorUse 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-ageniti",
"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
tsxand setbuild.typescriptRuntimetotsx - point
--app-moduleat compiled JavaScript - create a tiny
src/ageniti/app.jswrapper 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.
14. Know What To Read Next
- API Reference for the full public API.
- Entry Points for package export paths.
- Runtime Semantics for envelopes, retry, timeout, and errors.
- Recipes for common integration patterns.
- Release Checklist for packaging and publish checks.