MainSDK
Search⌘K
Docs

Actions and MCP

Write an action once and get an API endpoint, a server action, a CLI command and, if you choose, an MCP tool, all behind one policy.

Preview

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

Apps are used two ways now: people click, and agents ask. An agent arrives through MCP with an intent ("refund my last order") and needs to act inside your app. MainSDK makes you write each action once, and every door into it runs the same checks.

One definition, every surface

app/actions/orders.ts
import { action, db, pay } from "mainsdk";
import { s } from "mainsdk/schema";

export const refundOrder = action({
  name: "orders.refund",
  intent: "Refund an order the signed-in user owns",
  input: s.object({ orderId: s.string(), reason: s.string() }),
  risk: "destructive",          // read | write | destructive
  mcp: true,                    // opt in; default false
  run: async ({ input, user }) => {
    const order = await db.orders.for(user).get(input.orderId);
    return pay.refund(order.paymentId, { reason: input.reason });
  },
});
flowchart LR
  H[HTTP<br/>POST /api/actions/orders.refund] --> C
  S[Server action] --> C
  L[CLI<br/>mainsdk run orders.refund] --> C
  M[MCP tool] --> C
  C[Same checks:<br/>identity, owner scope,<br/>rate limit, validation, policy] --> R[run] --> A[Audit event]
Diagram

That one definition becomes:

  • an HTTP endpoint, POST /api/actions/orders.refund;
  • a server action for your UI;
  • a CLI command, mainsdk run orders.refund;
  • an MCP tool, only because it is marked mcp: true;
  • an audit event for every call, recording the surface, user, decision and input hash.

There is no second door. A call through MCP can't skip any check the HTTP path runs. A user who doesn't own the order gets MAIN-DATA-0007 on every surface.

Full calls and options are on the action interface page.

Agents get a narrow, short-lived token

main.yaml
mcp:
  expose: [orders.refund, orders.list]     # or mark actions with mcp: true
  confirm: { write: true, destructive: true }
  auth: { issuer: auth, scopes: [orders:read, orders:refund], token-ttl: 10m }
  • The MCP endpoint follows the MCP authorization spec: OAuth 2.1 with PKCE and protected resource metadata. A request without a token gets 401 with a WWW-Authenticate header pointing to that metadata.
  • An agent acting for a person gets a short-lived token with narrow scopes through OAuth token exchange (RFC 8693). A token for orders:read can't call orders.refund.
  • The audit log records both the agent and the person who approved it.

A person says yes to risky actions

riskWhat an agent needs
readNothing extra. Read-only actions can also be exposed as MCP resources.
writeA confirmation, by default (mcp.confirm.write: true)
destructiveAlways a person's yes, through MCP elicitation, before anything runs. A decline returns an error and nothing runs.

Signed tool descriptions

A changed tool description can trick an agent (tool poisoning). MainSDK signs each tool description with the project's signing key and verifies it when tools are listed. A description edited after signing is left out and MAIN-MCP-0003 is logged. Each tool carries a schema version, so changes follow versioned contracts.

The MainSDK MCP server for your coding agent

Separate from your app's actions, mainsdk init registers the MainSDK MCP server with your coding agent. It lets the agent run the check, read logs and errors, and work through adopting an existing app (adopt.scan, adopt.report, adopt.lock, adopt.plan_module, adopt.verify_module, adopt.finish). See Use MainSDK with your coding agent.

Not decided yet

  • Which key signs tool descriptions (the project key or a per-release key) and in what format.
  • The full tool list of the coding-agent MCP server beyond check, logs and the adopt.* tools.

Approval on the person's phone (OpenID CIBA) and signed approvals for confirmed actions arrive in phase 3. Agent-to-agent calls over A2A arrive in phase 5.

Next

Was this page helpful?

↑↓ moveEnter openEsc close

Not decided yet