Tektii just went live. We're shipping fixes daily — spot a bug? Let us know

Claude Code (MCP)

The Tektii MCP server exposes the platform's strategy, version, scenario-configuration, and scenario lifecycle tools to any MCP-compatible AI agent. This guide covers Claude Code as the first reference agent. The end state: from a fresh Claude Code session, you can ask the agent to register a strategy version, kick off a backtest, and read the results — all in chat.

The server is hosted at https://api.tektii.com/mcp and uses the Streamable HTTP transport. Authentication is by API key against the existing API Keys page — there is no separate MCP credential surface.

Prerequisites

  • A Tektii account with at least one strategy. New users can create a strategy from the Strategies page in the web app. If list_strategies returns [], you're on a brand-new account — create a strategy from the web app first; you can't bootstrap one through MCP.
  • Claude Code installed locally (claude on the command line).
  • Docker logged in and ready to push images. The register_strategy_version tool returns a short-lived access token for the container registry; you push the image yourself before calling confirm_strategy_version.

1. Mint an API key

The Tektii MCP server reuses the same API keys as the management REST API. There is no MCP-specific UI.

  1. Sign in to the web app at app.tektii.com.
  2. Open Developers → API Keys: https://app.tektii.com/developers/api-keys.
  3. Click Create Key, give it a recognisable name (for example, claude-code-laptop), and copy the value.

Heads up — the full key is shown only once. Once you close the dialog it cannot be retrieved again. If you lose it, retire the key and mint a new one.

The key is a 64-character hex string. Treat it like a password: store it in a secrets manager rather than committing it to a repo or a shared chat.

Don't paste your key into chat transcripts. Step 2 below has you putting the raw key on a claude mcp add command line. If you later share a Claude Code transcript or your mcp config file to debug a problem, redact the X-API-Key value first. If a key leaks, retire it immediately on the API Keys page and mint a new one.

2. Register the server in Claude Code

Add the Tektii server with claude mcp add from a shell:

claude mcp add tektii \
  --transport http \
  https://api.tektii.com/mcp \
  --header "X-API-Key: <your-api-key>"

This writes the server entry into your local Claude Code configuration.

Restart Claude Code after adding the server. claude mcp add writes the config and claude mcp list will show the server as ✓ Connected, but the Tektii tools do not appear in a session that was already running — they register only on a fresh start. Quit and reopen Claude Code (or start a new session); the 15 Tektii tools then show up in the tool list (/mcp inside Claude Code lists registered servers and their tool counts).

If you prefer to edit the config file directly, the equivalent entry under mcpServers looks like:

{
  "mcpServers": {
    "tektii": {
      "type": "http",
      "url": "https://api.tektii.com/mcp",
      "headers": {
        "X-API-Key": "<your-api-key>"
      }
    }
  }
}

Note — the auth header is X-API-Key, not Authorization: Bearer. The /mcp mount accepts API-key auth only; browser session tokens are rejected.

3. The end-to-end loop

A typical "register a new version and backtest it" cycle uses seven tools. You can ask Claude Code for any of these in plain English; the tool names below are what you'll see in the tool-call previews.

3.1 Discover what you have

list_strategies

Returns the calling tenant's strategies. Keep the id of the strategy you want to operate on — every later tool takes a strategy_id.

3.2 Register a new strategy version

register_strategy_version(strategy_id, git_sha, parent_version_id?)

This creates a new version in copying state and returns:

  • data.id — the version UUID, used in the next two steps.
  • repositoryUrl — an opaque registry path to push to. Treat it as a black box: copy it verbatim into the docker login / docker tag / docker push commands below — don't parse it or hand-construct it.
  • accessToken — a short-lived OAuth token with push permission scoped to that path only. Copy it verbatim into docker login; don't log or store it.

git_sha is the 7–40 hex commit SHA you're registering. parent_version_id is optional and used for lineage tracking.

The version is now in copying state. It won't accept run_scenario calls until you push the image and call confirm_strategy_version (step 3.4). If you call run_scenario against a copying version, the call fails with ImageNotUploaded.

3.3 Push the Docker image

The MCP server doesn't push the image for you — it issues credentials so you can. Outside Claude Code, run:

echo "<accessToken>" | docker login -u oauth2accesstoken --password-stdin <repositoryUrl>
docker tag my-strategy:<git-sha> <repositoryUrl>:<git-sha>
docker push <repositoryUrl>:<git-sha>

Build for linux/amd64. The scenario runner is linux/amd64. If you build on Apple Silicon (or any ARM64 host) without forcing the platform, the push will succeed and the runtime will silently fail when the scenario tries to start. Always build with docker buildx build --platform linux/amd64 -t my-strategy:<git-sha> . so the manifest matches what the runner expects.

The token is short-lived: roughly 5–15 minutes from the moment register_strategy_version returned it. If the push outlasts that window, Docker itself surfaces the failure as unauthorized: authentication required (typically alongside an HTTP 401 Unauthorized on the registry response) — not a Tektii error message. Call register_strategy_version again with the same git_sha to recover: every call mints a new version_id with fresh credentials — nothing is reused or upserted by git_sha — so push to the repositoryUrl you just received and confirm that new version_id. If the token expired mid-transfer on a large image, any orphaned or partial layer from the abandoned push is safe to leave in place — the container registry reclaims it on its own; just retry the push with the fresh credentials.

3.4 Confirm the upload

confirm_strategy_version(strategy_id, version_id)

Transitions the version to ready. Idempotent — calling it twice on a ready version is a no-op.

3.5 Define the backtest inputs

create_scenario_configuration(name, subscriptions, start_time, end_time, position_mode?, initial_capital?, env?, description?, is_active?)

A configuration captures what the backtest subscribes to and over what window. Subscriptions are 1–50 entries of { instrument, events }. position_mode is netting (default) or hedging. initial_capital defaults to 100,000 and accepts 1.0100,000,000.0. start_time / end_time are RFC3339 timestamps; end_time is exclusive. env is an optional string→string map of strategy parameters that overrides the image's baked ENV defaults — store the parameter set on the config so every run from it inherits the same overrides (see §3.6 for per-run overrides and sweeps).

You can list and inspect existing configurations with list_scenario_configurations and get_scenario_configuration.

3.6 Kick off the run

run_scenario(strategy_id, version_id, subscriptions, start_time, end_time, position_mode?, initial_capital?, env?)

Returns a scenario id and state: QUEUED immediately — submission is asynchronous, so the run is enqueued and the call returns without waiting for it to start. The run moves to PENDING once it is dispatched, then RUNNING. The MCP layer is polling-only — there is no streaming progress in v1.

env is an optional string→string map of strategy parameters for this run. It layers over the saved config's env and over the image's baked ENV, with the per-run value winning — so you vary parameters against one image with no rebuild. A baseline 10/20 image launched with env={"MA_SHORT": "20", "MA_LONG": "50"} reproduces a dedicated 20/50 image exactly.

Sweeping parameters with env_grid

For a parameter sweep, batch_run_scenarios takes an env_grid — a map of parameter name to a list of values. The batch expands to the cartesian product of those value arrays, one scenario per combination, all against the same image:

batch_run_scenarios(
  strategy_id, version_id, subscriptions, start_time, end_time,
  env_grid={"MA_SHORT": ["10", "20"], "MA_LONG": ["50", "200"]},
)

That env_grid expands to four runs — 10/50, 10/200, 20/50, 20/200 — each launched against the one baked image, so a moving-average (or any-parameter) sweep is one call, not one Docker build per parameter set. There is no fixed per-batch cap: the expanded run count is bounded only by your remaining monthly quota, and the check is all-or-nothing — if the batch would exceed it, the whole batch is rejected before anything launches. A flat env on the same call applies to every run and layers under the grid, but a given key may appear in only one of env / env_grid.

env keys and values are strings, stored in plain text — never put secrets in them. Values are redacted in the batch response: scenarios come back scenario-major with env_grid keys in ascending order, the last key varying fastest, so use that ordering to map the returned scenario ids back to their grid points.

3.7 Poll for completion

get_scenario(scenario_id)

Call this on a cadence (every 5–15 seconds is reasonable) until state is one of COMPLETE, FAILED, or CANCELLED — the three terminal states. The success state is COMPLETE, not COMPLETED; a poll keyed on COMPLETED never matches and loops forever. See Scenario states for the full state machine. The summary metrics live on the response; for the full breakdown, fetch results in the next step.

You can list scenarios across a strategy with list_scenarios(strategy_id, state?, strategy_version_id?) — useful for triage.

3.8 Read the results

get_scenario_results(scenario_id)

Returns the full performance metrics — return, drawdown, Sharpe, trade stats — for a COMPLETE scenario. Calling this on a non-complete scenario returns an error with the current state in the message. Completed scenarios are also viewable in the web app, with the equity curve and metrics rendered as charts.

If you need to abort a scenario before or during the run, cancel_scenario(scenario_id) transitions a QUEUED, PENDING, or RUNNING scenario to CANCELLED.

Tool reference (summary)

ToolPurpose
list_strategiesPaginated list of the caller's strategies.
get_strategySingle strategy by id.
list_strategy_versionsPaginated list of versions for a strategy.
get_strategy_versionSingle version by id.
register_strategy_versionMint a new version + push credentials.
confirm_strategy_versionMark an uploaded version ready. Idempotent.
list_scenario_configurationsPaginated list of scenario configurations.
get_scenario_configurationSingle configuration by id.
create_scenario_configurationCreate a new configuration.
run_scenarioRun a single backtest, with an optional env override. Returns id + QUEUED.
batch_run_scenariosRun a batch of backtests in one call (bounded by remaining monthly quota); env_grid sweeps parameters over one image.
cancel_scenarioCancel a QUEUED, PENDING, or RUNNING scenario.
get_scenarioFetch a scenario for polling.
list_scenariosPaginated list of scenarios for a strategy.
get_scenario_resultsFull metrics for a COMPLETE scenario.

Troubleshooting

401 Unauthorized on every call

Either no X-API-Key header is being sent, or the value is wrong. Check:

  • The claude mcp add command included --header "X-API-Key: <key>". If you edited the JSON config by hand, the header lives under headers, not env.
  • The key wasn't truncated when copied. The full key is 64 hex characters.
  • The key hasn't been retired from the API Keys page.

403 Forbidden

The key is valid but the tenant doesn't own the resource (strategy, version, configuration, or scenario) being addressed. Run list_strategies to confirm what the calling key can see.

400 Bad Request with "host not allowed"

The MCP transport enforces a host allowlist. If you're connecting through a custom proxy or rewriting the Host header, the request is rejected at the transport layer. Connect directly to api.tektii.com and let the default Host flow through.

Tools never appear in Claude Code

/mcp inside Claude Code shows registered servers and their connection state. If you just ran claude mcp add and the tools aren't in the running session, restart Claude Code first — newly added MCP servers register only on a fresh start, even though claude mcp list already shows them ✓ Connected. If the Tektii server is listed as connected but the tool count is 0, the most likely causes are:

  • The auth header is missing or wrong (look for 401 in the server's errors line).
  • The transport type is set to sse or stdio instead of http. The Tektii server speaks Streamable HTTP only.

Image push fails with "unauthorized"

The accessToken returned by register_strategy_version is short-lived and has expired. See §3.3 for the recovery steps — re-call register_strategy_version and use the fresh credentials and new version_id it returns.

get_scenario_results returns "scenario not in COMPLETE state"

You called the results tool before the scenario finished. Poll get_scenario until state is COMPLETE, then call get_scenario_results. If the state is FAILED or CANCELLED, the scenario won't have results — inspect the error field on the scenario for cause.

What's next

This guide covers Claude Code; guides for other MCP-compatible agents (Cursor, custom clients) are coming. The wire protocol is the same — a Streamable HTTP server with X-API-Key auth — so only the configuration steps differ per agent.