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
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.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" fields in your client's connector dialog blank — they're 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. |
tradesDetail is intentionally omitted from the result — only aggregate metrics (pnlR, winRate, expectancyR, etc.) are returned, the same fields documented in the Metrics Reference. Trade-level detail isn't paginated upstream, so it's dropped here rather than risk flooding an agent's context with thousands of rows.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. |
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.
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.