Documentation

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).

This server is a thin protocol adapter — it has no logic of its own beyond validating input and forwarding to 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:

mcp config (e.g. claude_desktop_config.json)
{
  "mcpServers": {
    "emidlabs-backtest": {
      "url": "https://mcp.backtest.emidlabs.com/mcp"
    }
  }
}
Some clients (e.g. Claude's "Add custom connector" dialog) ask for a plain URL instead of this JSON block. Paste the full address including the /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:

MethodHowBest 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 headerConfigure 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 argumentPass 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.
💡
Precedence when more than one is present: header, then OAuth, then the argument. If none apply, every tool call fails fast with a clear error naming all three options — never a silent or ambiguous auth failure.
OAuth login doesn't require any manual client setup on your end — this server implements standard OAuth discovery and Dynamic Client Registration (/.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.

ArgumentTypeDescription
backtestApiKeystring (optional)Not needed if you connected via OAuth login or the x-api-key header is configured on this connector.
assetPairstringe.g. "BTC-USDC".
initialDatestringISO date, e.g. "2025-01-01".
finalDatestringISO date, e.g. "2025-06-01".
strategySnapshotJsonobjectThe Strategy DSL object — see the strategy-dsl-spec resource below.
backtestBaseUrlstring (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.

ArgumentTypeDescription
backtestApiKeystring (optional)Not needed if you connected via OAuth login or the x-api-key header is configured on this connector.
idstringThe id returned by submit_backtest.
waitForCompletionbooleanDefault true — polls until finished or pollTimeoutMs elapses.
pollTimeoutMsnumberDefault 120000 (2 minutes).
backtestBaseUrlstring (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.

ArgumentTypeDescription
backtestApiKeystring (optional)Not needed if you connected via OAuth login or the x-api-key header is configured on this connector.
backtestBaseUrlstring (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.

💡
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:

LimitScopeWhat it protectsWhat you see
Backtest-api quotaPer API keyYour account’s real usage/billing limitA 429 from backtest-api, passed through as an MCP tool error.
MCP infra guardPer IP/connectionThis MCP process itself, against runaway loops or malformed floodsA 429 with a message explicitly identifying it as the MCP layer’s own guard, not your account quota.
The MCP server does not enforce its own copy of your account's backtest quota — that would risk two counters disagreeing for the same call. It only adds a loose, generous IP-based guard to protect the server process itself.

#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.