Connect AI trading agents and clients to CO-DASH through a single MCP route. Seven read-only tools surface portfolio state, PnL, NAV history, and canonical trade events. The same workspace API key gates every tool and the full REST surface.
MCP (Model Context Protocol) lets an AI agent discover and call tools on a remote server through a JSON-RPC 2.0 envelope. CO-DASH implements the streamable-HTTP transport on a single POST route; the same workspace API key used for the REST API also gates MCP.
The MCP surface is intentionally read-only. Account creation, AI-provider credentials, and automations remain on the REST API and are documented below under External REST API.
The registration endpoint is public and rate limited. It returns a kv_live_… key once. Store it server-side immediately — the plaintext is unrecoverable.
Every MCP and REST request carries the workspace API key in one of two headers. Both forms are equivalent; Authorization is preferred for clients that already speak Bearer auth.
Header
Value
Authorization
Bearer kv_live_…
x-api-key
kv_live_…
Bearer tokens that do not start with kv_live_ are treated as web-app JWTs, not external API keys. Keys can be additionally restricted by expiry, source-IP allowlist, and account allowlist — all enforced server-side.
Any MCP client that speaks streamable HTTP and supports a custom Authorization header works without a transport adapter. Point it at https://p-api.kvants.ai/mcp and send the workspace key.
MCP is JSON-RPC 2.0 over a single POST route. The server supports these methods:
Method
Purpose
initialize
Negotiate the protocol version. Returns server capabilities and instructions.
ping
Liveness check. Returns an empty object.
tools/list
List tools the current key is allowed to call. Scope filtered server-side.
tools/call
Invoke a tool by name with validated arguments.
resources/list
Returns an empty list. CO-DASH does not expose MCP resources.
prompts/list
Returns an empty list. CO-DASH does not expose MCP prompts.
notifications/*
Any client notification (no id). Server responds with 202 No Content.
Supported protocol versions: 2025-11-25 (default), 2025-06-18, 2025-03-26, 2024-11-05. The server echoes whichever version the client requested if it's in the supported set.
Parse structuredContent for deterministic data — the content[].text member is a JSON-stringified mirror for model consumption. Tool-level validation errors come back as isError: true on the same envelope (not as a JSON-RPC error).
Seven read-oriented tools. Each defines its own argument schema with zod, rejects unknown keys, and re-checks scope before reading data. A key with the broad workspace:read scope can call every tool; narrower keys only see matching tools in tools/list.
list_accounts
workspace:read:summary
Discover exchange accounts visible to the current API key. Call this first — the ids it returns are required by every account-scoped tool.
Latest stored NAV and exposure snapshot. Numbers are summed across all selected accounts. BTC/ETH prices are returned only when a single account is selected.
Arguments
Name
Type
Default
Notes
accountId
string
—
Optional. Single visible exchange account id.
accountIds
string[]
—
Optional. Up to 100 ids. When both are omitted, all visible accounts are queried.
Paginated canonical events: trades, funding, closed-PnL, liquidations, ADL, system-close. Persist nextCursor between calls and pass it back as cursor to fetch the next page. The window is only resolved on the first call.
Arguments
Name
Type
Default
Notes
accountId
string
—
Optional. Single visible exchange account id.
accountIds
string[]
—
Optional. Up to 100 ids. When both are omitted, all visible accounts are queried.
from
ISO datetime
now - 90d
Inclusive lower bound. Ignored when cursor is set.
to
ISO datetime
now
Inclusive upper bound. Ignored when cursor is set.
The same workspace API key authenticates the full REST surface. REST writes provision resources (accounts, AI keys, automations); REST reads expose data MCP also covers, plus the full Grafana dataset and bulk exports.
Most successful responses use a uniform envelope. Errors always use the same shape with success: false. Treat the error string as human-readable; rely on the HTTP status for control flow.
Exceptions: GET /health/*, GET /api/status, and most GET /api/grafana/* routes return the payload at the top level (no wrapper). Inspect HTTP status first there.
Timestamps are ISO 8601 / RFC 3339 UTC strings. PnL-analysis endpoints also accept date-only strings like 2026-04-13.
Money values are numbers, not strings. USD unless a *Btc / *Eth field name says otherwise.
Percent fields are percentages, not fractions. 8.32 means 8.32%, not 0.0832.
Forensics endpoints echo the resolved window in data.window. When from/to are omitted, the server substitutes a 90-day default — inspect window.defaulted before narrating per-symbol detail.
Cursor-paginated endpoints (/api/trade-events, /api/forensics/forced-events) skip the default window on subsequent pages and return window: null.
Grouped by scope family. Grafana read routes (GET /api/grafana/*) are also API-key accessible under the relevant read scope and back the same datasets the in-app dashboard renders.
workspace:read:summarysummary
GET /api/accounts Account ids, labels, status
GET /api/portfolios Portfolio groups (tree=true for nested)
GET /api/portfolio/report One-call agent report
GET /api/pnl-analysis/summary Windowed PnL summary
GET /api/forensics/account-summary Postmortem-style summary
GET /api/forensics/pnl-by-symbol Symbol-level PnL
GET /api/forensics/pnl-buckets PnL by hour / weekday / venue
workspace:read:livelive
GET /api/portfolio Live workspace snapshot
GET /api/portfolio/history NAV and exposure history
GET /api/portfolio/exposure-summary Latest exposure & position-risk
GET /api/status Exchange connectivity status
GET /api/metrics Rolling workspace performance
workspace:read:rawraw
GET /api/trades Raw trade fills, newest first
GET /api/trade-events Canonical events, cursor pagination
GET /api/trade-events/activity-summary All-account activity rollup
GET /api/forensics/forced-events Liquidations / ADL / system close
GET /api/forensics/largest-loss-events Worst events by impact
GET /api/forensics/risk-timeline Position risk over a window
POST /api/pnl-analysis/query Grouped / pivoted PnL rows
workspace:read:exportsexports
GET /api/export/counts Row counts per exportable table
GET /api/export/:table/count Row count for one table
GET /api/export/:table csv / json / jsonl, gzip optional
Two one-minute sliding-window limits apply to API-key traffic:
Credential attempt: 300 requests / minute / caller IP on any request that carries an API-key credential (valid or invalid). Coarse IP backstop.
Read usage: 100 requests / minute / API key hash on authenticated reads. Current usage is surfaced in X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset response headers.
When limited, the response is 429 with a Retry-After header. Honor it — don't tight-loop.
Treat MCP as a financial data boundary. The route uses the same expiration, IP allowlist, account allowlist, and rate-limit middleware as the external API.
Keep kv_live_ keys out of prompts, logs, browser storage, and client-side bundles.
Use account allowlists when a user or agent should only inspect a subset of exchange accounts.
Prefer summary or live scopes unless the agent truly needs raw trade events.
Treat every MCP response as data: parse structuredContent, validate ranges, and keep evidence links in your own audit trail.
Set timeouts around tool calls. Retry only idempotent reads with bounded backoff.
Rotate the workspace key when a user, agent, tenant, or execution environment is retired.