# ship your agent as a live service — any channel, any app

Vibe first.
Then FastAgent.

An agent is just a directory (persona.md, skills/, tools/, channels/) — or embedded right in your app. FastAgent serves it: GitHub, Telegram, Slack, Feishu, or any channel you compose. No rewrite, no new format, no platform.

start
Read https://fastagent.sh/start.md and build an agent in this project.
$npm i -g @fastagent-sh/fastagent# cli
$npm i @fastagent-sh/fastagent# embed in your app

MIT · Node ≥ 22.19 / Bun · built on pi · model and platform neutral · no account, no telemetry

FastAgent Cloud — managed hosting, coming soon · self-hosting stays free forever · join the waitlist →

# the ecosystem — plugs into your stack and infrastructure
channels
  • GitHub
  • Telegram
  • Slack
  • Feishu · Lark
  • Discord (coming soon)
  • WhatsApp (coming soon)
  • GitLab (coming soon)
  • + more
connect
  • Stripe (coming soon)
  • Shopify (coming soon)
  • Notion (coming soon)
  • Salesforce (coming soon)
  • Google Docs (coming soon)
  • Facebook (coming soon)
  • Pinterest (coming soon)
  • + more
embed in
  • Next.js
  • Hono
  • Express
  • Astro
  • Nuxt
  • SvelteKit
  • Fastify
  • Bun
  • Node
deploy to
  • Fly.io
  • Railway
  • Docker
  • Cloudflare (coming soon)
  • Vercel (coming soon)
  • Render (coming soon)
  • AWS (coming soon)
  • Kubernetes (coming soon)
  • Google Cloud (coming soon)
models
  • Anthropic
  • OpenAI
  • Gemini
  • DeepSeek
  • Qwen
  • Kimi
  • Grok
  • GLM
  • Llama
  • + more
more integrations coming soon
# 01 · the core

Small core. Clear seams.

FastAgent owns exactly one thing: a single function — the Agent Handler. It decouples your channels, your agent, the harness, and your infra — swap any of them without touching the others.

channelsturn external events into invocations
  • GitHub webhooks
  • Telegram · Discord · Slack bots
  • HTTP API · SSE streaming
  • cron schedules
  • + yours · via the channel kit
modelsthe harness's own axis — a runtime option, not an architecture decision
  • Anthropic Claude · OpenAI GPT · Gemini
  • Kimi · Grok · DeepSeek · GLM
  • + yours · register a provider
agent handler · the contract
invoke(scope, prompt)
  => AsyncIterable<AgentEvent>
powered by pi — the built-in agent harness+ or bring your own harness
infrayours — owns auth, storage, deployment
  • your app's route
  • node · bun server
  • fly · railway · docker
  • + yours · anything fetch-shaped

# one contract collapses the channels × models/harnesses × infra matrix — swap one axis, the other two don't move

each turn streamstextthinkingtool_startedtool_endedcompleted|failed
# the whole system in five words
Agent Definition
the directory that describes the agent
Agent Handler
the callable contract: invoke
Tool
a typed action, validated before execution
Skill
reusable expertise loaded from the definition
Channel
turns an external event — a webhook, a message, even the clock — into invocations

# deliberately not a workflow engine — for deterministic orchestration, call invoke() from your own queue

Read the Agent Handler SPEC → · Design principles →

# 02 · composition

Compose the modules.
Extend with a file.

Every part of FastAgent is a module: model, sessions, providers, channels, tools. Composing them is plain TypeScript — and extending is dropping a file that composes in. Built for your scenario and your stack, without forks or waiting.

compose · every option is a module

const { agent } = await createPiAgentFromDefinition("./agent", {

model: "anthropic/claude-sonnet-4", // any provider

sessions: jsonlSessionStore({ dir: ".state" }), // or your own store

providers: [myGateway], // or your model gateway

});

▸ assembly is code — swap any module, the rest keeps working
+extend · tools/lookup-order.ts

// the filename becomes the tool name

export default defineTool({

description: "Look up an order by id.",

input: z.object({ orderId: z.string() }),

async execute({ orderId }) { return db.find(orderId); },

});

▸ drop a file into tools/, channels/, or schedules/ — it composes in

Build a channel adapter → · Public API surface →

# 03 · the definition

The directory is the agent.

Vibed with a coding agent, written by hand, or inherited from a repo — a plain directory is the definition. FastAgent assembles it at boot. No DSL, no rewrite.

agent/
├─ persona.md            # identity + standing instructions
├─ AGENTS.md             # project context — yours, or a host repo's
├─ skills/               # reusable markdown expertise
├─ tools/                # code tools · the filename is the name
│   ├─ lookup-order.ts
│   └─ post-review.ts
├─ channels/             # a file IS a channel
│   ├─ github.ts
│   └─ telegram.ts
├─ schedules/            # cron time triggers
├─ reference.md          # any markdown becomes context
└─ fastagent.config.mjs  # deployment choices
  • Bring your existing definition. An AGENTS.md plus skills/ your coding agent already vibed is enough.
  • No registry, no boilerplate. A file in channels/ is a channel. A file in tools/ is a tool. Drop it in — it's live.
  • Typed tools. Zod validates every input before it runs — nothing hidden between the model and your code.
  • No build step. fastagent dev runs the directory. Ship the directory. That's the artifact.

# every file is optional · point at a directory get a live agent

# 04 · add it to your app

One import.
An agent inside your app.

Call it like any function — from a request handler, a queue consumer, a background job — or mount it as a route in the app you already ship. FastAgent composes with your app, never owns it.

// anywhere in your codebase — a job, a queue consumer, a test
import { collect } from "@fastagent-sh/fastagent";

const { text } = await collect(
  agent.invoke({ session: "job-42" }, { text: "summarize today's failures" })
);
// app/api/invoke/route.ts
import { createInvokeHandler, createPiAgentFromDefinition } from "@fastagent-sh/fastagent";

const { agent } = await createPiAgentFromDefinition("./agent", {
  model: "openai-codex/gpt-5.5",
});

export const POST = createInvokeHandler(agent);
export const runtime = "nodejs";
// app.ts
import { Hono } from "hono";
import { createInvokeHandler, createPiAgentFromDefinition } from "@fastagent-sh/fastagent";

const { agent } = await createPiAgentFromDefinition("./agent", {
  model: "anthropic/claude-sonnet-4",
});

const app = new Hono();
app.post("/invoke", createInvokeHandler(agent));

export default app;
// app.ts
import express from "express";
import { createInvokeHandler, createPiAgentFromDefinition, nodeListener } from "@fastagent-sh/fastagent";

const { agent } = await createPiAgentFromDefinition("./agent", {
  model: "anthropic/claude-sonnet-4",
});

const app = express();
app.post("/invoke", nodeListener(createInvokeHandler(agent))); // no body parser on this route
app.listen(8787);
// src/pages/api/invoke.ts
import { createInvokeHandler, createPiAgentFromDefinition } from "@fastagent-sh/fastagent";

const { agent } = await createPiAgentFromDefinition("./agent", {
  model: "openai-codex/gpt-5.5",
});

export const POST = createInvokeHandler(agent);
// server/api/invoke.post.ts
import { createInvokeHandler, createPiAgentFromDefinition } from "@fastagent-sh/fastagent";

const { agent } = await createPiAgentFromDefinition("./agent", {
  model: "openai-codex/gpt-5.5",
});
const handler = createInvokeHandler(agent);

export default defineEventHandler((event) => handler(toWebRequest(event)));
// src/routes/invoke/+server.ts
import { createInvokeHandler, createPiAgentFromDefinition } from "@fastagent-sh/fastagent";

const { agent } = await createPiAgentFromDefinition("./agent", {
  model: "anthropic/claude-sonnet-4",
});
const handler = createInvokeHandler(agent);

export const POST = ({ request }) => handler(request);
// server.ts — keep the body stream unread, hijack the reply
import { createInvokeHandler, createPiAgentFromDefinition, nodeListener } from "@fastagent-sh/fastagent";

const { agent } = await createPiAgentFromDefinition("./agent", {
  model: "openai-codex/gpt-5.5",
});
const listener = nodeListener(createInvokeHandler(agent));

app.register(async (scope) => {
  scope.removeAllContentTypeParsers();
  scope.addContentTypeParser("*", (_req, payload, done) => done(null, payload));
  scope.post("/invoke", (req, reply) => { reply.hijack(); listener(req.raw, reply.raw); });
});
// server.ts
import { createInvokeHandler, createPiAgentFromDefinition } from "@fastagent-sh/fastagent";

const { agent } = await createPiAgentFromDefinition("./agent", {
  model: "openai-codex/gpt-5.5",
});

const invoke = createInvokeHandler(agent);

Bun.serve({
  port: 8787,
  fetch(req) {
    const url = new URL(req.url);
    return url.pathname === "/invoke" ? invoke(req) : new Response("ok");
  },
});
// server.ts
import { createInvokeHandler, createPiAgentFromDefinition, router, serveNode } from "@fastagent-sh/fastagent";

const { agent } = await createPiAgentFromDefinition("./agent", {
  model: "openai-codex/gpt-5.5",
});

serveNode(router({ "POST /invoke": createInvokeHandler(agent) }), { port: 8787 });

Embedding guide →

# 05 · run it as a live service

Leave the terminal.
Become a live service.

In the terminal, your agent runs only while you're watching. The stretch between there and production is where teams either rewrite into a framework or quietly drop the project. FastAgent is that stretch — and it doesn't ask for the rewrite.

  • verified webhooks
  • streaming responses
  • session state
  • cron triggers
  • deployment
# 06 · the directory ships

Deploy anywhere.

The harness is decoupled from the deployment: the agent runs anywhere Node runs — your laptop, a microVM, Kubernetes, bare metal. Idle agents scale to zero — and resume in hundreds of milliseconds when the next webhook lands.

 fastagent deploy fly
 wrote fly.toml         # microVM · scale-to-zero · fast resume
 wrote Dockerfile       # portable — the agent isn't tied to a host
 wrote .dockerignore
 runbook printed · or --run drives the deploy to completion
supported today
  • Fly.io
  • Railway
  • Docker
  • + your own infrastructure
coming soon
  • Cloudflare
  • Vercel
  • Render
  • AWS
  • Kubernetes
  • Google Cloud

Deployment guide →

# 07 · for the org

What you're not signing up for.

Adopting a library is not the same decision as starting a migration. Four things this doesn't ask of you.

No platform to migrate to
No console, no runtime to deploy into. MIT, no account, no telemetry.
Infrastructure stays yours
Auth, database, routing, deployment, policy stay in your app. A security review asks what leaves the boundary — here it can be nothing.
The artifact is a git directory
Reviewed in a PR, diffed, rolled back, owned by a team. Not a prompt buried in a SaaS console.
Delivered where people already are
Nobody changes their habits for a new tool. They do reply in the group chat.

# the contract is one function — stop using it and you lose a few adapter files, not your app

What FastAgent does and doesn't do →

# 08 · start the vibe

Keep vibing. FastAgent will follow.

Paste this into Claude Code, Codex, Cursor, or any agent that reads the web. It inspects your project first, then builds the agent — fresh, from your existing files, or embedded in your app.

Read https://fastagent.sh/start.md and build an agent in this project.

For humans: npm i -g @fastagent-sh/fastagent then fastagent init · full quickstart →