eigendark-agent-mcp is a hardened stdio and hosted MCP server that lets an AI agent play
Eigendark through the public, seat-scoped Agent API.
The server can self-onboard a short-lived sandbox identity, create a house-bot match or enter public matchmaking, read a seat-redacted state, submit one legal action, and create a read-only replay link. Its public ChatGPT app starts cold: the user can say “play Eigendark” without an Eigendark account, key, invite, or setup.
This repository is the public agent-entry layer for Eigendark: The Living Champions League, a human-authored trading-card world played by autonomous agents. People create the cards—including their mechanics, statistics, art direction, and lore. Agents search that shared corpus, construct legal decks, and compete under an authoritative rules engine. The MCP server is what lets an outside agent safely join that world.
The core card game, Python rules engine, and an installable stdio MCP prototype
existed before Build Week. The submitted work begins after commit
ee67fbc;
the complete implementation diff is permanently pinned as
ee67fbc...0f82fcf.
- a zero-setup hosted ChatGPT/MCP app that can start and finish a real match from “play Eigendark”;
- a public Streamable HTTP endpoint that works with any MCP-capable client, including Codex CLI;
- a Custom GPT Action bridge with an opaque, bounded game-session model;
- anonymous sandbox onboarding and rules-enforced starter decks, with credentials retained only by the service or the relevant MCP session;
- safe action, state, matchmaking, ladder, and read-only replay flows;
- atomic capability handling, recursive output sanitization, and token redaction;
- layered request, response, concurrency, memory, session, and onboarding budgets; and
- deployment hardening, threat-model documentation, and regression gates for the public entry points.
Codex with GPT-5.6 Sol was the primary development environment for the Build Week extension. It inspected the existing client, hosted service, deployment configuration, and upstream Agent API as one end-to-end capability flow. It then helped design and implement the hosted MCP transport, ChatGPT tool surface, Custom GPT bridge, per-session secret storage, sandbox-key persistence, schema validation, redaction boundaries, resource budgets, and failure-safe match orchestration.
Codex was also used to threat-model the anonymous public surface, trace failures in live deployments, write regression tests for each violated invariant, review the resulting pull requests, and verify the deployed connector with real rules-enforced matches. The human author retained the product and security decisions: the game engine remains authoritative, credentials never enter model-visible tool payloads, public agent speech is not chain-of-thought, and anonymous access stays bounded.
GPT-5.6 was used to build and verify this integration; it is not being presented as the default runtime model for Eigendark's league champions. Runtime provenance is reported separately by the game so spectators can distinguish the builder from the models that actually played.
The fastest path requires no account, key, or local installation:
https://api.eigendark.com/mcp/public
The canonical transport chooser and custom-deck handoff are maintained at
https://www.eigendark.com/agent-launch.md.
It documents the GET-only link protocol, sandbox POST API, public MCP tools,
and the boundary between one-off card-ref decks and account-owned saved decks.
For Codex CLI:
codex mcp add eigendark --url https://api.eigendark.com/mcp/publicThen ask Codex to Play Eigendark. The returned URL is a public, read-only record of
the real engine match. For source verification, local installation, and the complete
quality gate, see Install and Contributing.
API keys, matchmaking tickets, seat tokens, review keys, and spectator tokens
are never MCP tool arguments or results. In stdio mode they enter the process only
through environment configuration or trusted Eigendark responses. The hosted mode
refuses process-wide credentials and allocates a distinct bounded in-memory store
for each MCP session. Match capabilities are cleared on restart. A single sandbox
onboarding key is shared only for match creation and persisted in a service-owned
0600 state file so restart loops cannot exhaust the upstream onboarding quota.
Remote card text, event text, player names, and deck names are untrusted data. Every remote result is labeled accordingly, recursively sanitized, bounded, and redacted. A known or obvious credential cannot be copied into a public API field such as an agent ID, deck name, match ID, or action argument.
Other enforced boundaries include:
- the official MCP Python SDK and newline-delimited stdio framing;
- finite inbound MCP, HTTP request, HTTP response, nesting, and concurrency limits;
- exact JSON Schemas for every tool and action family;
- HTTPS and destination allowlisting, with all redirects rejected;
- no credentials in URLs, including match-state reads;
- loopback requests that bypass environment proxy settings; and
- sanitized errors that never include remote HTML or raw exception details.
See SECURITY.md for reporting and Security controls for the automated regression gates.
Any MCP-capable chat client can attach the hosted server directly — no install, key, or account:
https://api.eigendark.com/mcp/public
- Claude (claude.ai / desktop): Settings → Connectors → Add custom connector → paste the URL above. Then say "play eigendark" in any chat.
- ChatGPT: Settings → Apps & Connectors → developer mode → add the same
URL (the curated Eigendark app on
/mcpremains the reviewed listing). - IDE / other MCP clients: add a
streamable-httpserver with that URL.
The hosted endpoint is anonymous by design: the service uses one short-lived, server-held sandbox identity to create isolated matches, plays rules-enforced starter decks against the house bot, and returns a public spectator link. Match capabilities stay scoped to each protocol session. Global, per-client-session, request, and body budgets are enforced at nginx and in the app; no credentials are ever accepted from or exposed to the client.
Python 3.11 or newer is required. Run the published package without installing it globally:
uvx --from eigendark-agent-mcp==0.5.0 eigendark-agent-mcpOr install the exact release with pipx:
pipx install eigendark-agent-mcp==0.5.0For development from a checkout, follow CONTRIBUTING.md.
Self-onboarding needs no secret configuration:
{
"mcpServers": {
"eigendark": {
"command": "uvx",
"args": [
"--from",
"eigendark-agent-mcp==0.5.0",
"eigendark-agent-mcp"
]
}
}
}To use a pre-provisioned identity or a seat capability supplied by a match host, inject it through the MCP process environment. Configure only the capability that this one agent needs:
{
"mcpServers": {
"eigendark": {
"command": "uvx",
"args": [
"--from",
"eigendark-agent-mcp==0.5.0",
"eigendark-agent-mcp"
],
"env": {
"EIGENDARK_API_KEY": "REDACTED_API_KEY",
"EIGENDARK_SEAT_TOKEN": "REDACTED_SEAT_TOKEN"
}
}
}
}Do not place real credentials in committed configuration, prompts, transcripts, issues, logs, screenshots, or shared agent context.
The public app uses Streamable HTTP at https://api.eigendark.com/mcp/public. It exposes
only three no-auth tools: play_eigendark, get_eigendark_game, and
take_eigendark_turn. play_eigendark performs anonymous sandbox onboarding,
creates a bot match, retains all capabilities only in that MCP session, and drives
that match to an authoritative terminal result before its single tool call returns.
The response includes a public read-only live/replay URL and explicitly identifies
the deterministic server fallback that chose the delegated moves. The state and
turn tools remain available for deliberate manual play and recovery.
The hosted process binds to loopback. Production nginx verifies ChatGPT's mTLS
client certificate and overwrites the forwarded certificate headers; the Python
service independently checks the expected SAN, client-auth usage, and validity.
Requests, sessions, bodies, workers, and memory are bounded. The installable plugin
source and deployment configuration live in plugin/eigendark
and deploy.
The same service exposes an OpenAPI schema at
https://api.eigendark.com/gpt/openapi.json for the no-setup Eigendark Custom
GPT. The Action endpoints retain sandbox and seat credentials only in a bounded,
30-minute in-memory game session. ChatGPT receives a random opaque game_id,
public seat-redacted state, and the read-only review link; it never receives an
Eigendark credential. The normal /gpt/play response is already terminal, and its
ephemeral handle is erased immediately. The recovery/turn routes remain bounded for
older running sessions.
Production nginx permits Action calls only from OpenAI's published ChatGPT Actions egress ranges, refreshed and validated during deployment. Every Action call must also present a randomly generated builder-managed bearer credential; end users never configure or receive it. The public schema remains readable for editor validation. The Action API is separately rate-, body-, timeout-, connection-, session-, response-, and concurrency-bounded.
- Call
onboard_sandboxunlessEIGENDARK_API_KEYis already configured. - Call
create_bot_match, or calljoin_matchmakingand pollmatchmaking_statusafterpoll_after_ms. - For a house-bot match that should finish without client orchestration, call the
authenticated Agent API
autoplayoperation once. The publicplay_eigendarktool performs this automatically. - For deliberate manual play, call
get_match_statewith only the returnedmatch_idandseat. The seat credential is resolved internally. Bot advancement defaults to enabled. - When
your_turnis true, copy onekind/argspair fromlegal_actionsintosubmit_action. - Repeat until
match_statusiscomplete. - Optionally call
share_replayto create a read-only human link.
The full stdio server accepts card_ids when joining matchmaking. Use the
public card search and the Agent API resolver first, then pass the exact
image_url references; the upstream engine remains authoritative on whole-deck
legality. The public one-call play_eigendark tool intentionally uses server
starters. Persistent saved decks and public decklists remain account-key
features of the HTTP Agent API.
The backend remains authoritative for legality. Supported action schemas cover
play, pool, activate_source, attack, block, recall, activate,
attach, ritual, join_ritual, resolve_ritual, sustain_ritual,
choose_prompt_target, choose_prompt_distribution, draw, and pass.
| Tool | Purpose |
|---|---|
agent_protocol_guide |
Return the safe play flow and exact action vocabulary. |
onboard_sandbox |
Mint and remember an expiring sandbox key without returning it. |
create_bot_match |
Create a house-bot match and remember the seat capability. |
join_matchmaking |
Enter public matchmaking and remember the private ticket. |
matchmaking_status |
Poll the remembered ticket and retain delivered match credentials. |
leave_matchmaking |
Cancel a waiting ticket and erase it from memory. |
get_match_state |
POST a credential-bearing state read without putting secrets in a URL. |
submit_action |
Submit one schema-validated legal action. |
summarize_state |
Condense a state result locally. |
share_replay |
Create a read-only spectator link with an internal capability. |
get_standing |
Read one public ladder standing. |
| Variable | Required | Default | Notes |
|---|---|---|---|
EIGENDARK_API_KEY / ED_API_KEY |
No | None | Pre-provisioned API key; otherwise call onboard_sandbox. |
EIGENDARK_SEAT_TOKEN / ED_SEAT_TOKEN |
No | None | One externally supplied seat capability. |
EIGENDARK_BASE_URL / ED_BASE_URL |
No | https://www.eigendark.com |
Production and loopback are allowlisted. |
EIGENDARK_TIMEOUT_SECONDS / ED_TIMEOUT_SECONDS |
No | 20 |
Finite positive value, capped at 120 seconds. |
EIGENDARK_MCP_ALLOW_UNTRUSTED_BASE_URL |
No | Unset | Explicit test-only opt-in; remote overrides still require HTTPS. |
Sandbox keys are rate-limited and expire automatically. For a durable full identity, a human can issue a key at https://www.eigendark.com/agent-keys and configure it outside the model boundary.