intentkit
v5.0.0
Published
The agent-native framework. Define intent, not routes.
Maintainers
Readme
IntentKit
The agent-native framework. Define intent, not routes.
IntentKit is a TypeScript framework for building services that AI agents consume directly via MCP. No REST controllers. No middleware chains. No frontend. Just functions with intent, typed schemas, and direct database access.
- Three primitives —
defineFunction(),defineTrigger(),defineGoal() - Capability-based permissions — agents without access never see the function (invisible deny, not 403)
- Event bus —
ctx.emit()decouples side effects from business logic - 31 adapter packages — service adapters, macOS automation, agent bridges, observability
- Agent-to-agent — identity, discovery, trust, negotiation, service mesh
- Agent economy — function pricing, wallets, escrow, marketplace
- SQLite + PostgreSQL — direct
ctx.dbaccess with transactions, no ORM - 6 project templates —
npx intentkit init my-api --template saas-api
import { defineFunction, z } from 'intentkit';
export const createTask = defineFunction({
name: 'create_task',
intent: 'Create a new task with a title and priority level',
permissions: ['task:create'],
input: z.object({
title: z.string(),
priority: z.enum(['low', 'medium', 'high']).optional(),
}),
output: z.object({
id: z.number(),
message: z.string(),
}),
execute: async (input, ctx) => {
const { lastInsertRowid } = await ctx.db.run(
`INSERT INTO tasks (title, priority) VALUES (?, ?)`,
[input.title, input.priority ?? 'medium'],
);
await ctx.emit('task.created', { id: Number(lastInsertRowid), title: input.title });
return { id: Number(lastInsertRowid), message: `Task "${input.title}" created` };
},
});One function. Intent for agents. Schema for validation. Permissions for access control. Events for side effects. Direct database access. No ceremony.
Who is this for?
- Backend developers building services that AI agents consume via MCP
- Teams adding an agent layer to existing Express/NestJS/Fastify apps (sidecar pattern)
- Developers who want conventions without ceremony — structure and opinions, not boilerplate
- Anyone building for the agent economy — Claude Desktop, Dispatch, OpenClaw, custom agents
Architecture
graph LR
Agent["AI Agent<br/>(Claude, OpenClaw, custom)"] -->|MCP / REST / OpenAI| Server["IntentKit<br/>serve()"]
Server --> Registry["Registry<br/>defineFunction()"]
Server --> Triggers["Triggers<br/>defineTrigger()"]
Server --> Goals["Goals<br/>defineGoal()"]
Registry --> Functions["Your Functions"]
Functions --> DB["Database<br/>ctx.db"]
Functions --> Providers["Adapters<br/>ctx.providers"]
Functions --> Events["Event Bus<br/>ctx.emit()"]
Server --> Discovery["Discovery<br/>AgentIdentity"]
Server --> Economy["Economy<br/>AgentWallet"]The MCP SDK handles the protocol. IntentKit handles the architecture. Adapters handle service connections. They compose — they don't compete.
Quick Start
Option 1: Scaffold from template
npx intentkit init my-api --template saas-api
cd my-api
npm install
npm run seed
npx intentkit dev src/serve.ts --watchAvailable templates: saas-api, customer-support, internal-tools, data-pipeline, devops-bot, personal-assistant
Option 2: Scaffold a blank project
npx intentkit init my-api
cd my-api
npm install
npm run seed
npx intentkit dev src/serve.ts --watchOption 3: Add to existing project
npm install intentkitCore API
defineFunction() — The Foundation
import { defineFunction, z } from 'intentkit';
export const myFunction = defineFunction({
name: 'do_something', // MCP tool name
intent: 'What this does...', // Agents read this to decide to call it
permissions: ['resource:action'], // Required capabilities
requires: ['mail'], // Provider dependencies (validated at startup)
cost: { amount: 0.5, currency: 'credits' }, // Optional pricing (v5.0)
input: z.object({ /* ... */ }), // Zod schema — validates + documents
output: z.object({ /* ... */ }), // What agents get back
execute: async (input, ctx) => {
// ctx.db — direct database access
// ctx.log — structured logger
// ctx.caller — who's calling (role, capabilities)
// ctx.emit — fire events for side effects
// ctx.providers — adapter instances (mail, cache, etc.)
// ctx.meta — mutable request-scoped data (shared between hooks)
// ctx.requestId — unique UUID per MCP tool call
return { /* ... */ };
},
});defineTrigger() — Proactive Agents (v2.0)
import { defineTrigger } from 'intentkit';
export const followUpTrigger = defineTrigger({
name: 'overdue_follow_up',
intent: 'When an overdue invoice email arrives, block calendar time for follow-up',
condition: {
watcher: 'email',
filter: { unread: true },
and: [
{ check: 'list_invoices', input: (email) => ({ client: email.from, status: 'overdue' }), expect: { count: { gte: 1 } } },
],
},
action: {
call: 'create_event',
input: (email) => ({ title: `Follow up: ${email.subject}`, duration: 60, date: 'tomorrow' }),
},
cooldown: { intervalSeconds: 3600, maxPerHour: 3 },
});defineGoal() — Persistent Objectives (v4.0)
import { defineGoal } from 'intentkit';
export const inboxZero = defineGoal({
name: 'inbox_zero',
intent: 'Process all unread emails down to zero',
metrics: [
{ name: 'unread_count', check: 'check_mail', input: { unreadOnly: true }, target: 0, operator: 'eq' },
],
timeHorizon: { hours: 24 },
triggers: ['overdue_follow_up'],
functions: ['search_mail', 'create_reminder'],
});IntentRegistry — The Catalog
import { IntentRegistry } from 'intentkit';
const registry = new IntentRegistry()
.register(createTask, moveTask, listTasks, deleteTask);
// Merge multiple registries
const combined = IntentRegistry.merge(taskRegistry, orderRegistry);serve() — MCP Server with Everything
import { serve } from 'intentkit';
await serve({
name: 'my-service',
version: '1.0.0',
registry,
context,
providers: [createMailProvider({ ... })],
triggers: triggerRegistry, // Proactive agents (v2.0)
identity: createIdentity(), // Agent-to-agent (v3.0)
discovery: new InMemoryDiscovery(), // Service discovery (v3.0)
transport: 'stdio', // or 'http'
roles: {
admin: ['*'],
member: ['task:read', 'task:create', 'task:update'],
viewer: ['task:read'],
},
defaultRole: 'member',
});Context API
Every function receives ctx with:
| Property | Type | Description |
|----------|------|-------------|
| ctx.db.run(sql, params) | { changes, lastInsertRowid } | Execute INSERT/UPDATE/DELETE |
| ctx.db.get(sql, params) | T \| undefined | Get one row |
| ctx.db.all(sql, params) | T[] | Get all matching rows |
| ctx.db.transaction(fn) | T | Atomic operations with automatic rollback on error |
| ctx.log.info/warn/error | void | Structured logging (JSON or pretty format) |
| ctx.caller | CallerIdentity | Role + capabilities of the caller |
| ctx.emit(name, data) | Promise<void> | Fire an event |
| ctx.meta | Record<string, unknown> | Mutable request-scoped data shared between hooks |
| ctx.providers | Record<string, unknown> | Named adapter instances (e.g., ctx.providers.mail) |
| ctx.requestId | string | Unique UUID per MCP tool call |
Permissions
IntentKit uses capability-based access control that's fundamentally different from REST:
In REST: an agent tries to call an endpoint -> gets 403 -> already knew the endpoint existed.
In IntentKit: an agent without permission never sees the function at all. Invisible deny.
// The function declares what it needs
export const deleteTask = defineFunction({
permissions: ['task:delete'],
// ...
});
// Roles define what agents have
roles: {
admin: ['task:*'], // wildcard — all task operations
member: ['task:read'], // can't see delete_task
}Wildcard matching: task:* matches any task action. *:read matches read on any resource. * matches everything.
Adapters & Providers
IntentKit stays lean — adapters handle external service connections as separate packages.
Service Adapters
| Adapter | Description | Install |
|---------|-------------|---------|
| intentkit-http | Generic HTTP/REST client | npm i intentkit-http |
| intentkit-mail | IMAP reading + SMTP sending | npm i intentkit-mail |
| intentkit-cache | Redis caching via ioredis | npm i intentkit-cache |
| intentkit-queue | BullMQ job queue | npm i intentkit-queue |
| intentkit-storage | S3-compatible file storage | npm i intentkit-storage |
| intentkit-auth | JWT signing/verification | npm i intentkit-auth |
| intentkit-calendar | CalDAV calendar access | npm i intentkit-calendar |
| intentkit-slack | Slack messaging | npm i intentkit-slack |
Agent Bridges
| Adapter | Description | Install |
|---------|-------------|---------|
| intentkit-rest | REST API bridge (non-MCP consumers) | npm i intentkit-rest |
| intentkit-openai | OpenAI function calling format | npm i intentkit-openai |
| intentkit-openclaw | OpenClaw skills generator | npm i intentkit-openclaw |
| intentkit-graphql | GraphQL bridge | npm i intentkit-graphql |
| intentkit-websocket | WebSocket bridge | npm i intentkit-websocket |
Observability
| Adapter | Description | Install |
|---------|-------------|---------|
| intentkit-observe | Telemetry middleware | npm i intentkit-observe |
| intentkit-dashboard | Web UI for telemetry | npm i intentkit-dashboard |
| intentkit-otel | OpenTelemetry exporter | npm i intentkit-otel |
| intentkit-audit | Immutable audit log | npm i intentkit-audit |
| intentkit-studio | Interactive function testing UI | npm i intentkit-studio |
macOS Automation
| Adapter | Description | Install |
|---------|-------------|---------|
| intentkit-macos-core | Shared JXA runner | npm i intentkit-macos-core |
| intentkit-macos-mail | Mail.app automation | npm i intentkit-macos-mail |
| intentkit-macos-calendar | Calendar.app automation | npm i intentkit-macos-calendar |
| intentkit-macos-fs | File system + Spotlight | npm i intentkit-macos-fs |
| intentkit-macos-notes | Notes.app automation | npm i intentkit-macos-notes |
| intentkit-macos-reminders | Reminders.app automation | npm i intentkit-macos-reminders |
| intentkit-macos-shell | Safe shell execution | npm i intentkit-macos-shell |
| intentkit-macos-browser | Safari automation | npm i intentkit-macos-browser |
| intentkit-macos-system | System controls | npm i intentkit-macos-system |
| intentkit-macos-notify | Native notifications | npm i intentkit-macos-notify |
| intentkit-macos-search | Spotlight search | npm i intentkit-macos-search |
| intentkit-macos-contacts | Contacts.app automation | npm i intentkit-macos-contacts |
| intentkit-macos-apps | App management | npm i intentkit-macos-apps |
import { createMailProvider, type MailClient } from 'intentkit-mail';
await serve({
registry, context,
providers: [
createMailProvider({
host: 'imap.gmail.com',
user: '[email protected]',
password: process.env.GMAIL_APP_PASSWORD!,
smtp: { host: 'smtp.gmail.com' },
}),
],
});
// Inside functions: ctx.providers.mail as MailClientProviders support dependency ordering (dependsOn), health checks, and graceful shutdown (destroyed in reverse init order).
Run npx intentkit list to see all adapters. Anyone can publish intentkit-{service} packages.
Agent Economy (v5.0)
Functions can declare per-call cost. Wallets track spending. Escrow holds funds for cross-service transactions.
export const analyzeData = defineFunction({
name: 'analyze_data',
intent: 'Run ML analysis on dataset',
cost: { amount: 2.50, currency: 'credits', per: 'call' },
// ...
});
// Wallet tracks spending
const wallet = new AgentWallet({ initialBalance: 100, currency: 'credits' });
wallet.spend(2.50, 'analyze_data call');
// Escrow for cross-service transactions
const escrow = new Escrow(wallet);
const entry = escrow.create({ amount: 10, payee: 'data-service', reason: 'batch analysis' });
escrow.release(entry.id); // or escrow.refund(entry.id)
// Marketplace for discovering services
const marketplace = new Marketplace();
const cheapest = marketplace.cheapest('analyze_data');Testing
import { createTestContext } from 'intentkit/testing';
const ctx = createTestContext({ schema: './db/schema.sql', role: 'member' });
const result = await createTask.execute({ title: 'Test' }, ctx);
assert(result.id > 0);
assert(ctx.emitted[0].name === 'task.created');
ctx.withRole('viewer'); // Switch identity for permission testing
ctx.close();In-memory SQLite. Captured events via ctx.emitted. Role switching. Zero file cleanup.
Scenario Testing
npx intentkit test scenarios/Write multi-step test scenarios in YAML:
name: "Task lifecycle"
steps:
- call: create_task
input: { title: "New task" }
expect:
id: { type: number }
events: [task.created]
- call: move_task
input: { id: 1, status: "done" }
expect:
success: { eq: true }CLI
# Scaffold a new project
npx intentkit init my-api
npx intentkit init my-api --template saas-api
# Add functions
npx intentkit add create_task
# Development
npx intentkit dev src/serve.ts --watch # Hot reload (HTTP transport)
npx intentkit dev --mock # Mock all adapters
# Diagnostics
npx intentkit doctor # Pre-flight checks
npx intentkit types # Generate TypeScript types
npx intentkit test scenarios/ # Run YAML test scenarios
npx intentkit docs # Generate documentation site
npx intentkit list # List available adaptersConnect to Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"my-service": {
"command": "npx",
"args": ["tsx", "src/serve.ts"],
"cwd": "/path/to/my-project"
}
}
}Then ask Claude: "Show me all tasks on the board" -- Claude discovers list_tasks via MCP and calls it.
Philosophy
- Intent over interface. A function's purpose in natural language matters more than its URL path.
- Schema over ceremony. Zod replaces DTOs, validation pipes, and serializers in one declaration.
- Direct over abstracted.
ctx.db.run(sql)over repository patterns. SQL is already a great interface. - Invisible over forbidden. Agents don't get 403s. They never see what they can't use.
- Catalog over configuration. Register functions, serve. No module trees or decorator magic.
Roadmap
- [x]
defineFunction()core primitive (v0.3.0) - [x] Auto-generated MCP server (v0.3.0)
- [x] SQLite context with direct db access (v0.3.0)
- [x] Permission system with wildcards (v0.3.0)
- [x] Capability-based discovery (v0.3.0)
- [x] Event bus with
ctx.emit()(v0.3.0) - [x] CLI scaffolding (v0.3.0)
- [x] PostgreSQL context adapter (v0.4.0)
- [x] Testing utilities (v0.4.0)
- [x] Middleware/hooks system (v0.4.0)
- [x] Structured logging (v0.4.0)
- [x] HTTP transport with health checks (v0.4.0)
- [x] Typed error handling (v0.4.0)
- [x] Provider/adapter system (v0.5.0)
- [x] Lifecycle manager + graceful shutdown (v0.5.0)
- [x] Request-scoped context + requestId (v0.5.0)
- [x] Startup validation (v0.5.0)
- [x] Dependency-aware providers (v0.6.0)
- [x] Composable middleware chain (v0.6.0)
- [x] Per-request role switching (v0.6.0)
- [x] Webhook event forwarding (v0.7.0)
- [x] Hot reload / watch mode (v0.7.0)
- [x] Documentation generator (v0.7.0)
- [x] 8 service adapter packages (v1.0.0)
- [x] Observability: observe, dashboard, OTEL, audit (v1.1.0)
- [x] Developer tools: Studio, test runner, inspector, mock mode, doctor, types (v1.2.0)
- [x] Agent bridges: REST, OpenAI, OpenClaw, GraphQL, WebSocket (v1.3.0)
- [x] 6 project templates + CLI --template flag (v1.4.0)
- [x] macOS adapters: core + 12 macOS automation packages (v1.5.0)
- [x]
defineTrigger()+ watchers + condition engine + rate limiting (v2.0.0) - [x] Agent-to-agent: identity, discovery, trust, negotiation, mesh (v3.0.0)
- [x]
defineGoal()+ persistent memory + goal tree + monitoring (v4.0.0) - [x] Agent economy: pricing, wallets, escrow, marketplace, contracts (v5.0.0)
79 features across 9 phases. 360 tests. 31 adapter packages. From brainstorming to v5.0 in one session.
License
MIT
