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
Install globally (or invoke once via npx / pnpm dlx):
pnpm add -g @stableops/mcp-serverSTABLEOPS_API_URL=https://api.stableops.dev \
STABLEOPS_API_KEY=sk_sandbox_xxx \
STABLEOPS_AGENT_SESSION_ID=agent_sess_01 \
stableops-mcpEvery variable is read from the process environment:
| Variable | Required | Default | Notes |
|---|---|---|---|
STABLEOPS_API_URL | no | https://api.stableops.dev | Override for self-hosted or local debugging. |
STABLEOPS_API_KEY | yes | — | Sent as Authorization: Bearer …. The environment (sandbox / live) is determined by the key itself. |
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.
Install it into an MCP host
Every host wants the same three things: the command to run (stableops-mcp or
npx -y @stableops/mcp-server), no arguments, and the env block above.
Use whichever form your host expects.
Claude Desktop
~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"stableops": {
"command": "npx",
"args": ["-y", "@stableops/mcp-server"],
"env": {
"STABLEOPS_API_URL": "https://api.stableops.dev",
"STABLEOPS_API_KEY": "sk_sandbox_xxx",
"STABLEOPS_AGENT_SESSION_ID": "agent_sess_01"
}
}
}
}Claude Code (CLI)
One-shot CLI registration — scope to the current project by default, drop -s user to share globally:
claude mcp add stableops -s user -- npx -y @stableops/mcp-server \
-e STABLEOPS_API_URL=https://api.stableops.dev \
-e STABLEOPS_API_KEY=sk_sandbox_xxx \
-e STABLEOPS_AGENT_SESSION_ID=agent_sess_01Or hand-edit ~/.claude.json under mcpServers with the same JSON shape as Claude Desktop.
Codex CLI
~/.codex/config.toml — Codex reads TOML, not JSON:
[mcp_servers.stableops]
command = "npx"
args = ["-y", "@stableops/mcp-server"]
env = { STABLEOPS_API_URL = "https://api.stableops.dev", STABLEOPS_API_KEY = "sk_sandbox_xxx", STABLEOPS_AGENT_SESSION_ID = "agent_sess_01" }opencode
~/.config/opencode/opencode.json (or opencode.json at the project root):
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"stableops": {
"type": "local",
"command": ["npx", "-y", "@stableops/mcp-server"],
"environment": {
"STABLEOPS_API_URL": "https://api.stableops.dev",
"STABLEOPS_API_KEY": "sk_sandbox_xxx",
"STABLEOPS_AGENT_SESSION_ID": "agent_sess_01"
}
}
}
}Cursor
~/.cursor/mcp.json (global) or .cursor/mcp.json (per project):
{
"mcpServers": {
"stableops": {
"command": "npx",
"args": ["-y", "@stableops/mcp-server"],
"env": {
"STABLEOPS_API_URL": "https://api.stableops.dev",
"STABLEOPS_API_KEY": "sk_sandbox_xxx",
"STABLEOPS_AGENT_SESSION_ID": "agent_sess_01"
}
}
}
}VS Code (GitHub Copilot Agent)
.vscode/mcp.json:
{
"servers": {
"stableops": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@stableops/mcp-server"],
"env": {
"STABLEOPS_API_URL": "https://api.stableops.dev",
"STABLEOPS_API_KEY": "sk_sandbox_xxx",
"STABLEOPS_AGENT_SESSION_ID": "agent_sess_01"
}
}
}
}Other hosts
Cline, Continue, Gemini CLI, Windsurf, Zed and any other MCP-aware client
take the same shape — a stdio command, optional args, and the env block.
Substitute their config file path and key naming.
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,
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.
How is this guide?
Last updated