MainSDK
Search⌘K
Interfaces

AI and agents

Ops server

ops

Let a coding agent ask a running app how it is, what it has seen and what a playbook would do, through nine MCP tools on a user-only socket, with every change waiting for a person.

Building for 0.4ships as a beta APIPhase 1 · Core, public betaSpec ops-mcpFree in your app, Apache-2.0

Status Status is where the work is: Planned (in a milestone, not started), Building (tickets in progress), Preview (usable, may change), then the stability it ships at.

Stability The promise an interface ships with.

  • Stable Follows semantic versioning; breaking changes need a new major, 12 months of overlap and a codemod.
  • Beta Usable in production; may change before it becomes stable.
  • Experimental May change or go away; needs an explicit opt-in in main.yaml.
Stability levels
Updated 2026-10-28

A running app can answer an MCP client such as Claude Code about its health, signals and playbooks. Anything that changes the app waits for a person to say yes.

The agent can also ask what a playbook would do now and why a reaction ran. The server listens only on this machine, can't call arbitrary actions, change config or read app data, and every call acts as the app agent ops, so its budget, kill switch and audit apply.

At a glance

ops:
  mcp: { write: approve }
  agent: ops

Calls

  • new OpsServer(opts)
  • parseOps(section, env)
  • opsIssues(section, ctx)
  • OPS_TOOL_NAMES(const: the nine tool names)

Guarantees

Whatever the driver.

  • Nothing leaves the machine
  • No tool calls an app action, changes config or reads app data
  • A run or revert always waits for a person; an agent never approves

Drivers

1 drivers. Select one to see its line in main.yaml.

main.yamlBuilt in (a user-only socket and a token file) (default)
ops:
  driver: builtin

Change this one line to swap providers; your calls stay the same.Built-in and local drivers integration

Codes

CodeWhere it appearsMeaning
MAIN-OPS-0001Checkmainsdk check, before deployAn ops value is invalid: write: auto, a port outside 1024 to 65535, or http: on in production without ops.agent declared under agents
MAIN-OPS-0002RuntimeError responses and logsSee the spec
MAIN-OPS-0004RuntimeError responses and logsSee the spec
MAIN-OPS-0005RuntimeError responses and logsmainsdk ops mcp found no running app with an ops socket for the given folder or project
MAIN-OPS-0006RuntimeError responses and logsSee the spec

Features

Nothing here has shipped yet. Each feature lands in its phase; the spec ticket builds it.

FeaturePhaseMilestoneSpec ticket
Read toolsPhase 10.4ops-mcp#tools-read
Approval-only run and revertPhase 10.4ops-mcp#tools-write
mainsdk ops mcpPhase 10.4ops-mcp#stdio

Quick start

main.yaml
ops:
  mcp: { write: approve }
agents:
  ops: { budget: { calls: 500/day } }
bash
npx mainsdk dev          # the ops socket is on by default in dev
npx mainsdk ops mcp      # the stdio MCP server an agent starts; mainsdk init registers it

Configuration

KeyWhat it does
ops.mcp.socketon or off. Default on in dev, off elsewhere.
ops.mcp.writeapprove (default) or off, which makes the write tools unavailable (MAIN-OPS-0004). There is no auto.
ops.mcp.socket-portsThe loopback range used only where a Unix socket is refused. Default 47600-47699.
ops.agentThe app agent clients act as. Default ops.

| ops.mcp.http | on serves MCP over HTTP at http://127.0.0.1:<port>/mcp and, on a host with IPv6 loopback, [::1]. Default off. | | ops.mcp.port | The loopback HTTP port, 1024 to 65535. Default 4950. | | ops.card | on (default) serves the agent card at /.well-known/agent-card.json on the HTTP endpoint and as the resource mainsdk://ops/agent-card. |

The HTTP endpoint takes Authorization: Bearer <token> with the token in <run>/ops/<project>.token, and refuses a request with no token (401, MAIN-OPS-0002) or with a Host or Origin that is not loopback (403, MAIN-OPS-0003). It never goes through the app's public adapter, so the public app does not serve the agent card.

What you get

The app opens <run>/ops/<project-id>.sock (mode 0600 in a 0700 folder) and writes a fresh token file beside it at each start. A client presents the token first; a missing or wrong one is refused with MAIN-OPS-0002. The socket is on by default only when the environment is dev. Messages are JSON-RPC 2.0, one per line. Tool descriptions are signed with the token, so a client can spot a changed description.

ToolRiskWhat it does
ops.statusreadState, health, resources, posture, active runs and signal counts
ops.signalsreadA page of recent signals
ops.normsreadWhat normal looks like for each metric
ops.playbooksreadThe declared rules with step classes and modes
ops.reactionsreadRuns with their overlays and expiries
ops.planreadWhat a rule would change now and the guard's verdict; changes nothing
ops.explainreadWhy a run happened: trigger, steps, guard, approval, rollback
ops.runwriteFiles a run that waits for a person
ops.revertwriteFiles a revert that waits for a person

Nothing in an answer holds a tenant id, user id, config value or secret. ops.run always ends waiting, whatever the rule's run mode, and asks the person at the client to read the plan; a yes there is recorded in the audit and never approves. A person approves with mainsdk playbooks approve <run>, and an agent that tries gets MAIN-AGENT-0003. A rule with a step that is never a reaction is refused with MAIN-GUARD-0002.

Helpers for wiring and tests

CallWhat it does
new OpsServer(opts)The ops server of one app instance, for tests and custom wiring.
parseOps(section, env)Read the ops section of main.yaml for an environment.
opsIssues(section, ctx)List what is wrong in the ops section, without throwing.
OPS_TOOL_NAMES(const)The names of the nine ops tools.

Errors and checks

  • MAIN-OPS-0001: an ops value is invalid (mainsdk check).
  • MAIN-OPS-0002: no token or a wrong one.
  • MAIN-OPS-0004: a write tool with write: off, or a name outside the nine tools.
  • MAIN-OPS-0005: mainsdk ops mcp found no running app.
  • MAIN-OPS-0006: the ops agent's budget is spent.

See check codes.

Swapping drivers

ops has no drivers. It is a server over the app's own state, and the tools answer the same whatever drivers the app uses.

Testing

new OpsServer(opts) builds the server of one app instance for tests, and the testing kit's MCP client in @mainsdk/testing talks to it against a fixture app. Use parseOps and opsIssues to check the ops section without throwing. See Test your app.

Next

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet