MCP Server
Expose scoped tools to AI agents over MCP.
@stableops/mcp-server is a stdio MCP
server that wraps the StableOps SDK as a fixed set of tools. AI agents
(Claude Desktop, Cursor, any MCP-compatible host) can call these tools
only — they cannot talk to the API directly, and every call is gated by the
workspace's agent policy.
What the toolkit exposes
| Tool | Kind | Description |
|---|---|---|
get_order | read | Look up one payment order by id. |
list_events | read | Query the normalized chain-event log. |
list_webhook_deliveries | read | Read recent webhook deliveries. |
create_payment_order | write | Open a new payment order (policy-gated). |
request_action_approval | write | Queue a sensitive action for a human to approve. |
Read tools default to auto_allowed. Write tools always go through
POST /v1/agent/actions first; if the policy says
require_approval=true, the call returns pending_approval and the agent
must wait for a human to approve in the dashboard.
Run the server
pnpm add -g @stableops/mcp-server # or use npx for one-shot usage
STABLEOPS_API_URL=https://api.stableops.dev \
STABLEOPS_API_KEY=sk_sandbox_xxx \
STABLEOPS_ORG_SLUG=demo \
STABLEOPS_ENVIRONMENT=sandbox \
STABLEOPS_AGENT_SESSION_ID=agent_sess_01 \
stableops-mcpEvery variable is read from the process environment:
| Variable | Required | Default | Notes |
|---|---|---|---|
STABLEOPS_API_URL | no | http://localhost:3001 | Override for self-hosted or sandbox. |
STABLEOPS_API_KEY | yes | — | Sent as Authorization: Bearer …. |
STABLEOPS_ORG_SLUG | no | demo | Sent as x-stableops-org. |
STABLEOPS_ENVIRONMENT | no | sandbox | sandbox or live. |
STABLEOPS_AGENT_SESSION_ID | yes | — | Bind this MCP process to one audited session. |
Create the session id first with POST /v1/agent/sessions so the dashboard
can show its actions and revoke it later.
Wire it into Claude Desktop
~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"stableops": {
"command": "stableops-mcp",
"env": {
"STABLEOPS_API_URL": "https://api.stableops.dev",
"STABLEOPS_API_KEY": "sk_sandbox_xxx",
"STABLEOPS_ORG_SLUG": "demo",
"STABLEOPS_ENVIRONMENT": "sandbox",
"STABLEOPS_AGENT_SESSION_ID": "agent_sess_01"
}
}
}
}Cursor and other MCP hosts take the same shape — a command, args, and env.
Embed in your own host
If you build a custom Node host you can create the server directly without shelling out to the binary:
import { createAgentToolkitServer } from '@stableops/mcp-server'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
const server = createAgentToolkitServer({
apiKey: process.env.STABLEOPS_API_KEY!,
baseUrl: process.env.STABLEOPS_API_URL,
organizationSlug: 'demo',
environment: 'sandbox',
agentSessionId: 'agent_sess_01',
})
await server.connect(new StdioServerTransport())Lifecycle of a tool call
agent ─▶ tool call ─▶ POST /v1/agent/actions
│
├── decision = auto_allowed ─▶ SDK call ─▶ POST /actions/:id/executed
└── decision = pending_approval ─▶ blocked response
│
(human approves in dashboard,
agent re-attempts the tool call)Every tool returns either a structured result (typed against the tool's
outputSchema) or an isError envelope. The structured shape mirrors what
the SDK returns from the underlying resource call — camelCase keys, the
same enum values you see on /v1/payment-orders.
Safety properties
- The agent cannot sign chain transactions. The toolkit only opens StableOps payment orders; outbound treasury movements need a human in the dashboard.
- The agent cannot bypass the policy. Even read tools go through
/v1/agent/actions, so revoking the session blocks every subsequent call mid-conversation. - Prompt injection that asks the agent to call
create_payment_orderoutside the allow-list falls through topending_approval— the worst case is a queued action a human sees, not a live payment.
Next
- Agent policies — set allowed tools, per-action limits, daily limits, and approval rules.
- API Reference → Create payment order — the underlying contract every write MCP tool talks to.