subs MCP server
Commit Nov 6, 2026An agent harness that runs unprivileged agent loops using MCP servers for tool execution, supporting local, remote, and team deployments.
Also known as substructureai/subs
- Stars
- 13
- Tools
- —
- Downloads, 30 days
- —
- Latest version
- —
- Last commit
- Nov 6, 2026
- License
- MIT
What is subs MCP?
subs is an agent harness that runs unprivileged agent loops using MCP servers for tool execution, supporting local, remote, and team deployments.
Summary generated from the project's README
About subs MCP server
Running unprivileged agent loops with tool execution capabilities is what subs enables for developers building cloud-based AI agents. This harness manages the entire lifecycle of an agent, including durability, retries, timeouts, and session state. It can be deployed locally via a CLI, run as a self-hosted server, or used through a hosted engine. Users configure agents using a toml file, defining LLM providers like OpenRouter, Anthropic, or OpenAI.
Developers can connect agents to MCP servers for specialized tools, such as Sentry or Linear. The system supports various deployment modes, including integrating agents directly into Slack via DMs or mentions. For those needing granular control, the harness allows pointing an agent at an HTTP endpoint to handle decisions through a webhook. This setup enables custom logic in any language without requiring a specific SDK, as the engine communicates via standard webhooks and JSON schemas.
What you can do with it
- 1Deploy agents to SlackConnect an agent to a Slack workspace so it can respond to direct messages or channel mentions.
- 2Control agent logic with webhooksPoint an agent at a custom URL to intercept and modify every decision the engine proposes.
- 3Manage user-specific tool credentialsConfigure an agent to use a user-scoped connection so each person interacts with their own account.
- 4Execute long-running background tasksUse asynchronous tools that accept a call and report the result later rather than requiring an immediate response.
Written from the project's README. Every use case is checked against a quote from it.
Configuration
OPENROUTER_API_KEY
| Name | Description | |
|---|---|---|
| OPENROUTER_API_KEY | — |
Automated checks
Ran Nov 15, 2026Full report →Not verified yet. A server needs a score of 80, no serious finding, and enough data for us to check. How checks work · What to fix for more points
- Source repository linked and reachablesubstructureai/subs is public and reachablegithub.com/substructureai/subs ↗Pass
- Package names this repository as its sourceNot checked
- Build provenance (Sigstore attestation)Not checked
- Open source license (OSI)MITspdx.org/licenses/MIT.html ↗Pass
- Commit or release in the last yearLast commit or release 2026-11-06github.com/substructureai/subs ↗Pass
- OpenSSF ScorecardNot checked
- Package name not close to a popular packageNot checked
- No critical advisory in the package or its dependenciesNot checked
- No hidden instructions to the model0 tool descriptions and the README read; no override phrases, credential paths or hidden charactersPass
- Destructive tools are labelledNot checked
- Remote endpoint answers the MCP handshakeNot checked
README
View on GitHubsubs
Pre-1.0: APIs and the wire protocol can change between releases.
subs is an agent harness for the cloud.
It runs an unprivileged agent loop with no system access. It uses MCP servers for tools. It runs locally or as a client and a server.
Declare your agents in a config file. To customize the loop, point an agent at an HTTP endpoint and answer a webhook.
subs handles durability, retries, timeouts, MCP connection management, session state, session branching, AG-UI, Slack connection, LLM calls, subagents, interrupts and more.
To turn a sandbox into an MCP server, see mcpd.
Get started
Install the CLI
curl -fsSL https://subs.dev/cli.sh | bash
The CLI is also the engine.
Run an agent on your machine
Create a subs.toml.
name = "example"
[llm.openrouter]
type = "openrouter"
[agent.teammate]
llm = "openrouter"
model = "deepseek/deepseek-v4-flash-0731"
system = "You are a helpful teammate."
Set your provider key and talk to the agent.
export OPENROUTER_API_KEY=sk-or-...
subs chat teammate -c subs.toml
Run the engine as a server
Add a [serve] section and a [remote] that points at it.
[serve]
port = 9999
auth = false
[remote]
url = "http://localhost:9999"
Start the server.
subs serve -c subs.toml
In another terminal, the same chat command now talks to it.
subs chat teammate -c subs.toml
Run the agent on substructure.ai
Point [remote] at the hosted engine instead of your own.
[remote]
url = "https://api.substructure.ai"
Create the project from the file, then upload your LLM key.
subs apply
subs auth llm.openrouter
The same chat command now runs the turn on the hosted engine.
subs chat teammate -c subs.toml
Put the agent in Slack
Say which agent takes a DM and which one answers a mention.
[slack]
dm = "teammate"
mentions = "teammate"
Apply the file again, then connect your workspace.
subs apply
subs slack connect
Mention the bot in a channel and it answers in the thread.
Connect the agent to an MCP server
Declare the server and give it to an agent. Every user of the agent shares one credential.
[mcp.sentry]
url = "https://mcp.sentry.dev/mcp"
[agent.teammate]
llm = "openrouter"
model = "deepseek/deepseek-v4-flash-0731"
system = "You are a helpful teammate."
mcp = ["mcp.sentry"]
Authorize the connection.
subs auth mcp.sentry
Give each user their own MCP connection
Set credential = "user" and each user connects their own account. A
user-scoped connection works only in a one-on-one chat between the agent and
that user.
[mcp.linear]
url = "https://mcp.linear.app/mcp"
credential = "user"
[agent.personal]
llm = "openrouter"
model = "deepseek/deepseek-v4-flash-0731"
system = "Help me with my Linear issues."
mcp = ["mcp.linear"]
[slack]
dm = "personal"
mentions = "teammate"
Control the agent loop with a webhook
Point an agent at a URL and the engine sends every decision for that agent to your code. This gives you full control of the loop, including how the agent behaves in Slack.
[agent.teammate]
llm = "openrouter"
model = "deepseek/deepseek-v4-flash-0731"
system = "You are a helpful teammate."
mcp = ["mcp.sentry"]
worker = "https://example.com/agent"
Your endpoint reads the engine's proposal and returns it, changing only the steps you care about. There is no SDK to install.
import { serve } from "@hono/node-server";
import { Hono } from "hono";
import type { DecisionRequest, DecisionResponse } from "./protocol.ts";
function decide({ trigger, proposed }: DecisionRequest): DecisionResponse {
if (trigger.type === "session.start") {
return {
agent: {
...proposed.agent,
tools: [{ name: "current_time", description: "Get the current time" }]
}
};
}
// Run our tool when the model calls it.
if (trigger.type === "tool.execute" && trigger.name === "current_time") {
return { actions: [{ type: "tool.result", result: new Date().toISOString() }] };
}
// Accept the engine's proposal for everything else.
return proposed;
}
const app = new Hono();
app.post("/", async (c) => c.json(decide(await c.req.json())));
serve({ fetch: app.fetch, port: 4444 });
Only the agents that name a worker use one. The rest stay with the engine, in the same project and the same file.
Full walkthrough in the quick start. Docs: Workers, Connectors, Slack, Local development
Features
Slack
Mention the bot or DM it. The thread is the session. Route different channels to different agents.
Docs: Slack
Terminal chat
subs chat holds one session open, streams the reply as it is written, and
turns an approval prompt into a picker. The session is the same kind a Slack
thread is.
Docs: Chat
Examples: no-code-chat
Custom decisions
At each step the engine tells your code what it plans to do next. Accept the plan or do something else. A working agent is a few lines.
Docs: How it works
Any language
Your agent is an HTTP endpoint. Generate typed bindings from the published JSON schema.
Docs: Typed bindings
Examples: Go, Python, TypeScript, Elixir
MCP connectors
Declare an MCP server and the engine handles the authorization, reads the tools it offers, and runs every call. Your code never holds a token.
Docs: Connectors
Examples: Node
Plugins
Point an agent at an agent-plugins directory and it gets that plugin's skills and MCP servers.
Docs: Plugins
Any LLM
The engine calls Anthropic, OpenAI, or OpenRouter with your key. Or your worker makes the call and the engine never sees a key.
Docs: LLMs
Examples: Anthropic, OpenAI, OpenRouter
System One models
Your worker asks a fast model typed questions, such as TypeSafe's Jev or Cloudflare's Clef, and gets answers with a confidence. Route, guard, or score in the middle of a turn. The engine holds the key and saves every call.
Docs: System One models
Examples: Node
Crash recovery
Every step is saved before it runs. A run continues from where it stopped. The same message submitted twice runs once.
Docs: Durability
Human approval
An agent can stop and wait for a person to approve, then continue. A waiting agent uses no compute. In Slack this is a button.
Docs: Interrupts
Tools that take hours
A tool does not have to answer immediately. Accept the call, do the work on your own schedule, and report the result later.
Docs: Async tools
Chat history, editing, and branching
History, editing, regeneration, and branching belong to the engine. A user can edit an earlier message and go a new direction. The original branch stays.
Docs: Conversations
Existing chat UIs
The engine streams AG-UI events, so assistant-ui and CopilotKit connect to it directly.
Docs: AG-UI
Examples: assistant-ui, CopilotKit
Tools that run in the browser
A tool can run in the user's browser instead of on your server. The run waits for the browser, then continues.
Docs: Client-side tools
Examples: Node
Agent state without a database
The engine stores your agent's state with the conversation. Your code gets it on every request and writes changes back.
Docs: Agent state
Subagents
An agent can give work to other agents. Each child runs in its own session. The parent's totals include each child's cost and token use.
Docs: Subagents
Validated tool calls
Give a tool an input and output schema. The engine checks every call against it.
Docs: Tool calls
Retries and timeouts
Set a policy on any tool or model call. The engine applies it, and keeps applying it after a restart.
Docs: Retries and timeouts
Self-hosting
Run the engine on your own servers and hold every credential.
Docs: Self-hosting
Parts of the system
- Engine. Runs the agent loop, in Rust. It calls the model, runs tools, saves each step, retries failures, streams events, and supervises subagents. Use the hosted version at app.substructure.ai, run it from the CLI, or embed it in your process.
- Workers. Your agent code. It receives a trigger and returns actions. It runs in your codebase with your dependencies.
- Clients. They send work and stream events back, from your backend or from the browser. Slack and AG-UI are clients.
- CLI. Set up, deploy, watch, and debug from the terminal. It also runs the engine locally.
Install
curl -fsSL https://subs.dev/cli.sh | bash
The script verifies the release checksum and installs to ~/.local/bin. Set
SUBS_INSTALL_DIR to install elsewhere and SUBS_VERSION to pin a release.
Docs
Full documentation in docs/.
License
subs MCP questions
Can I run the agent in Slack?
Yes, you can put the agent in Slack by specifying which agent handles DMs and mentions in your configuration file.
How does subs handle tool execution?
It uses MCP servers for tools and can handle authorization, tool reading, and execution automatically.
Can I use my own LLM provider?
Yes, the engine can call Anthropic, OpenAI, or OpenRouter using your provided API keys.