Build production-ready AI agents in any language with no SDK
Pre-1.0: APIs and the wire protocol may change between releases.
Substructure is an open-source engine that drives the agent loop over HTTP. It keeps sessions durable and streams AG-UI events to your frontend. Your code stays on your infrastructure. It's just HTTP, so the engine is language agnostic. You don't even need an SDK.
- In an existing codebase: Your agent is just an HTTP endpoint. Tools are functions in your codebase, so they already have your database and your permissions.
- In a new project: The engine already handles sessions, history, streaming, and the client API. You write the agent and its tools, and that's most of the backend.
- Long-running work: First class support for async tools & async interrupt continuation. So tools can be background jobs and human approval can wait for days if needed.
- Durability: Every step is saved before it runs, so a deploy, a crash or a client reconnect doesn't lose anything.
Install the CLI and declare an agent.
npm install -g @substructure.ai/cli
subs init engineThat writes a substructure.toml. The part that matters is four lines:
[llm.claude]
type = "anthropic"
[agent.assistant]
llm = "claude"
model = "claude-sonnet-4-5"Send it a message:
export ANTHROPIC_API_KEY=sk-ant-...
subs run --agent assistant -o pretty "hi"There is no worker yet — the engine derives every step of the turn and, because
this agent has no worker URL, takes its own proposal.
Add a worker when you outgrow the file. A worker is an HTTP endpoint the engine asks before each step, so you can override any decision it would have made. Point one agent at it:
[agent.assistant]
llm = "claude"
model = "claude-sonnet-4-5"
worker = "http://localhost:4444"// A complete worker, served with Node's built-in http server. No dependencies.
import { createServer } from "node:http";
function decide({ proposed }) {
// The declared agent arrives as the `session.start` proposal, and every
// other proposal is the step the engine would have taken. Echo them all,
// then start overriding the ones you care about.
return proposed;
}
const server = createServer((req, res) => {
let body = "";
req.on("data", (chunk) => (body += chunk));
req.on("end", () => {
const decision = decide(JSON.parse(body));
res.writeHead(200, { "content-type": "application/json" });
res.end(JSON.stringify(decision));
});
});
server.listen(4444, () =>
console.log("worker listening on http://localhost:4444"));node server.mjs # one terminal
subs run --agent assistant "hi" # anotherThe run command spins up an engine, which drives the turn and streams the response back to the terminal.
Next:
- The quick start continues from here: add a tool, continue the conversation, add a teammate, then attach a worker.
- The same agent in Python, or in Go with tools and generated types.
- A full chat UI talking to the engine over AG-UI.
At each step of the loop, the engine tells your code what it plans to do next. Your code can approve it or do something else. A working agent is a few lines. Custom behavior is added by overriding the proposed decision.
Docs: Core concepts
Your agent is an HTTP endpoint. There is no SDK to install, and typed bindings can be generated from the published JSON schema.
Docs: Typed bindings
Examples: Go, Python, TypeScript, Elixir
Every step is saved before it runs. If the engine or your service dies mid-run, the run continues where it stopped. Submitting the same message twice won't run the turn twice. Client reconnects are handled.
Docs: Durability
An agent can stop and wait for a person to respond or approve then pick up where it left off. A waiting agent costs nothing to keep around.
Docs: Interrupts
A tool doesn't have to answer right away. Your service can acknowledge the call, do the work on its own schedule and report the result later. The run waits and resumes when the answer arrives.
Docs: Deferred tools
History, editing, regeneration, and branching are built into the engine. You don't build a chat backend. Users can edit an earlier message and go a new direction without losing the original thread.
Docs: Conversations
The engine streams AG-UI events directly, so frontends like assistant-ui and CopilotKit connect to it.
Docs: AG-UI
Examples: assistant-ui, CopilotKit
A tool can execute in the user's browser instead of on your server. The run pauses until the browser answers, then continues.
Docs: Client-side tools
Examples: Node
The engine stores your agent's working state next to the conversation. Your code gets it on every request and can write back changes.
Docs: Agent state
An agent can hand work to other agents. Each one runs in its own session, and the cost and token usage roll up to the parent.
Docs: Sub-agents
The engine can call Anthropic, OpenAI, or OpenRouter with your keys or you can make the LLM calls yourself, through your own gateway or a provider the engine doesn't know about right inside your worker.
Docs: LLMs
Examples: Node + Anthropic, Node + OpenAI, Node + OpenRouter, Python + Anthropic, Python + OpenAI
Set a timeout and retry policy on any tool or LLM call. The engine enforces them, including across restarts.
Docs: Retries and timeouts
Give a tool an input and output schema and the engine checks every call against it. Malformed calls go back to the model to fix itself instead of reaching your code.
Docs: Tool calls
Your worker can connect to an MCP server, expose its tools to the model, and forward each call back to it. The engine never needs to know MCP exists.
- Server: The engine that drives the agent loop, written in Rust. It can be run locally on your machine, embedded in process, or as a cloud hosted version available at https://app.substructure.ai. The server drives the loop, handles durability, retries, llm calls (optionally), realtime streaming, subagent supervision and more.
- Workers: Your agent logic. Receives a decision trigger, returns actions. Runs in your codebase with your dependencies. Can be an HTTP endpoint for use with the cloud/local server, or a callback passed to embedded substructure.
- Clients: Submit work and stream events back, backend-to-backend or straight from the browser. The engine also serves a native AG-UI endpoint, so any AG-UI chat frontend connects and streams directly.
- CLI: Substructure comes with a CLI to help you provision, observe, and debug from the terminal. You can also start a local server.
The CLI is available at:
npm i -g @substructure.ai/cliFull documentation in docs/.