MCP
Incarna speaks the Model Context Protocol over HTTP. Point an agent runtime at one URL and every capability in the REST API appears as a tool.
https://api.incarna.io/mcp
Transport is streamable HTTP, stateless. Stateless is deliberate: the API runs behind a load balancer, and a session pinned to one instance's memory would break as soon as a second instance existed — intermittently, which is the worst way for it to break.
Connecting
{
"mcpServers": {
"incarna": {
"url": "https://api.incarna.io/mcp",
"headers": { "Authorization": "Bearer ik_live_..." }
}
}
}
API keys only. The console's session path is not accepted here — that path is a shared secret held by the browser tier, and MCP callers are programmatic agents who should carry a credential that can be revoked on its own.
The organization is resolved from the bearer token on every call, never cached. One process serves every customer, so caching the first caller's org would hand their agents to everyone after them — and every response would still look correct.
Verifying the connection
curl -X POST https://api.incarna.io/mcp/ \
-H "Authorization: Bearer $INCARNA_KEY" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Responses may arrive as JSON or as a single-event SSE stream; both are valid and clients should handle either.
Calls without a key, or with an invalid one, are refused rather than served the environment's default organization.
Tools
Bodies
| Tool | Arguments |
|---|---|
create_agent |
name, region="us", device="auto", direction="" |
list_agents |
— |
get_agent |
agent_id |
delete_agent |
agent_id |
Reference
| Tool | Arguments |
|---|---|
list_countries |
— |
list_devices |
— |
| Tool | Arguments |
|---|---|
attach_email |
agent_id, address="" |
send_email |
agent_id, to, subject, body |
read_inbox |
agent_id, limit=10 |
X
| Tool | Arguments |
|---|---|
import_x |
agent_id, handle, auth_token, ct0, login_cookie |
post_tweet |
agent_id, account_id, text |
GitHub
| Tool | Arguments |
|---|---|
attach_github |
agent_id, token, totp_secret="" |
github_totp |
agent_id, account_id |
github_act |
agent_id, account_id, action, plus action-specific fields |
github_act actions: post · comment · repo · profile · follow · star.
| Tool | Arguments |
|---|---|
attach_reddit |
agent_id, client_id, client_secret, username, password |
reddit_act |
agent_id, account_id, action, plus action-specific fields |
reddit_act actions: post · comment · vote.
Pricing
| Tool | Arguments |
|---|---|
x402_pricing |
— |
x402_quote |
tool |
These exist so a budgeted agent can learn a price before it acts, and so the same numbers are discoverable whether it reaches us over MCP or plain HTTP.
{
"enabled": true,
"network": "eip155:84532",
"prices": {
"identity.x.post": "$0.05",
"identity.email.send": "$0.02",
"identity.email.inbox": "$0.01",
"identity.github.act": "$0.05",
"identity.reddit.act": "$0.05"
},
"pay_to": "0xCa1fBb1900e1C17Cc443e34f312720960E72a83F"
}
MCP tool calls are billed to the API key that made them. Pay-per-call with no account is the HTTP surface — see x402.
stdio
For local development the same server runs over stdio, serving the single
organization that owns INCARNA_API_KEY:
cd apps/api && INCARNA_API_KEY=ik_live_... python3 -m incarna.mcp_server
Set INCARNA_AUTH_DISABLED=1 to use the default org without a key. Local only —
it removes tenant isolation.