MCP Server
Connect an AI agent directly to the Backtesting API via the Model Context Protocol — no custom integration code required.
#Overview
emidlabs-backtest-mcp is a hosted Model Context Protocol (MCP) server that wraps the Backtesting API as agent-callable tools. Point any MCP-compatible client or coding agent at it and it can submit backtests, read results, and discover available assets directly, without you writing a REST integration.
Endpoint: https://mcp.backtest.emidlabs.com/mcp (Streamable HTTP transport).
backtest.emidlabs.com. Every backtest it submits still uses your own EmidLabs API key and consumes credits from your account the same as calling the REST API directly.#Connecting an agent
Pick your client
Claude and ChatGPT discover this server automatically over OAuth. Cursor and most coding agents instead want the raw JSON config block below.
Add the connector
Add the server to your MCP client's configuration. The exact file differs by client, but the shape is the same everywhere — a name and a URL:
{
"mcpServers": {
"emidlabs-backtest": {
"url": "https://mcp.backtest.emidlabs.com/mcp"
}
}
}/mcp path — https://mcp.backtest.emidlabs.com/mcp, not just mcp.backtest.emidlabs.com. Dropping the path is the most common reason a connector fails to register, and the resulting error rarely makes the real cause obvious.Authenticate
Three ways to authenticate, and you can combine more than one — the connector tries them in the order below:
| Method | How | Best for |
|---|---|---|
| OAuth login (recommended) | Just add the connector with no extra config. Claude/ChatGPT discover the login flow automatically and prompt you to sign in with your EmidLabs account — a dedicated API key is created for your account the first time and reused after that. | Personal Claude.ai / ChatGPT connectors. Nothing to copy, paste, or type — ever. |
| x-api-key header | Configure a specific EmidLabs API key once when adding the connector (e.g. Claude’s "custom header" / static_headers option) — attached automatically to every request from then on. | Org-managed connectors where an admin wants every member sharing one specific key instead of individual OAuth logins. |
| Tool argument | Pass an API key as backtestApiKey on each call — the agent is told the key directly (e.g. in conversation). | Coding agents, local/CLI use, or any client without connector-level auth support. |
/.well-known/oauth-protected-resource, /register), so a compliant client (Claude, ChatGPT) finds and registers itself automatically — leave any "OAuth Client ID / Secret" field in your client's connector dialog blank, it's only needed for servers that don't support automatic registration.#Tools
submit_backtest
Submits a strategy for backtesting. Returns immediately with an id and status — the agent then calls get_backtest_result to fetch the outcome.
| Argument | Type | Description |
|---|---|---|
| backtestApiKey | string (optional) | Not needed if you connected via OAuth login or the x-api-key header is configured on this connector. |
| assetPair | string | e.g. "BTC-USDC". |
| initialDate | string | ISO date, e.g. "2025-01-01". |
| finalDate | string | ISO date, e.g. "2025-06-01". |
| strategySnapshotJson | object | The Strategy DSL object — see the strategy-dsl-spec resource below. |
| backtestBaseUrl | string (optional) | Defaults to the public production API. |
get_backtest_result
Fetches a submitted backtest by id. By default it polls internally until the backtest finishes, so the agent gets one call → one final answer, with no client-side polling loop needed.
| Argument | Type | Description |
|---|---|---|
| backtestApiKey | string (optional) | Not needed if you connected via OAuth login or the x-api-key header is configured on this connector. |
| id | string | The id returned by submit_backtest. |
| waitForCompletion | boolean | Default true — polls until finished or pollTimeoutMs elapses. |
| pollTimeoutMs | number | Default 120000 (2 minutes). |
| backtestBaseUrl | string (optional) | Defaults to the public production API. |
No trade-by-trade detail here — the upstream GET /backtest/:iditself doesn't return any anymore, only aggregate metrics (pnlR, winRate, expectancyR, etc.), the same fields documented in the Metrics Reference. The full trade list lives on its own paginated endpoint (see GET /backtest/:id/trades), but this tool doesn't wrap it yet — a raw HTTP call is the way to reach it from an agent today.
list_available_assets
Lists every asset pair with real historical data, each with its supported timeframes and the date range actually available.
| Argument | Type | Description |
|---|---|---|
| backtestApiKey | string (optional) | Not needed if you connected via OAuth login or the x-api-key header is configured on this connector. |
| backtestBaseUrl | string (optional) | Defaults to the public production API. |
Optional, not a required step before every submit_backtest call. When a request has an unknown asset or a date range with no overlapping data, submit_backtestalready returns a clear error — for a non-overlapping range, it quotes the real available range directly. Use this tool for up-front exploration, or to recover from an "unknown asset" error by seeing what actually exists.
#Resources
strategy-dsl-spec
A condensed, markdown reference for the exact Strategy DSL JSON shape expected by submit_backtest's strategySnapshotJson field (emidlabs://strategy-dsl-spec) — it mirrors the Copy Spec for AI block on the Strategy System page.
inputs/conditions/score section — identical no matter where the strategy runs — is fetched live from emidlabs-strategy-mcp's domain owner (strategy-api's own GET /dsl-core-reference) and cached for a few minutes, rather than hand-copied — the same core emidlabs-live-mcp and emidlabs-strategy-mcp fetch too, so it can't drift out of sync between them. What stays local to this server — entryFeePct/exitFeePct, and riskManagementactually closing a simulated position — is specific to backtesting and doesn't apply to a live subscription.Reading this resource first is optional, not required — every field in submit_backtest's own tool schema already documents its exact shape (function list, execution-model rules, risk management format) directly, since MCP clients don't reliably fetch a separate resource before generating a call. This resource is a deeper reference for cases the inline schema doesn't need to cover in full.
#Rate limits
Two independent limits apply, and they surface differently on purpose so they're never confused for each other:
| Limit | Scope | What it protects | What you see |
|---|---|---|---|
| Backtest-api quota | Per API key | Your account’s real usage/billing limit | A 429 from backtest-api, passed through as an MCP tool error. |
| MCP infra guard | Per IP/connection | This MCP process itself, against runaway loops or malformed floods | A 429 with a message explicitly identifying it as the MCP layer’s own guard, not your account quota. |
#Scope
This server only exposes backtesting — submitting and reading backtests. It does not expose any Console actions (creating or rotating API keys, changing billing plans, account management). Those remain human-only actions in the Console UI, deliberately kept out of what an autonomous agent can do unsupervised.