From 48f677e0e4733b254d4466e26593b8cb8f8522f3 Mon Sep 17 00:00:00 2001 From: Drew Stone Date: Sun, 24 May 2026 14:38:24 -0600 Subject: [PATCH] fix(mcp): accept TANGLE_SANDBOX_API_KEY (canonical) with TANGLE_API_KEY fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 2 wiring surfaced the confusion: TANGLE_API_KEY in product envs is the ROUTER key (sk-tan-*); the MCP bin needs a SANDBOX-scope key (sk_sb_* or orch_prod_*). The 0.20.2 rename to TANGLE_API_KEY broke the intuition — operators reading the same env var name expected different meaning across contexts. TANGLE_SANDBOX_API_KEY is the canonical name. TANGLE_API_KEY accepted as fallback (no behavior change for callers that already passed a sandbox- scope key under TANGLE_API_KEY in 0.20.2). Top-of-file doc + error message both updated to reference the canonical name. 215 tests pass unchanged. --- package.json | 2 +- src/mcp/bin.ts | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 4ccac34..089f281 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@tangle-network/agent-runtime", - "version": "0.20.2", + "version": "0.20.3", "description": "Reusable runtime lifecycle for domain-specific agents.", "homepage": "https://github.com/tangle-network/agent-runtime#readme", "repository": { diff --git a/src/mcp/bin.ts b/src/mcp/bin.ts index 9ead4a6..e1605e9 100644 --- a/src/mcp/bin.ts +++ b/src/mcp/bin.ts @@ -11,7 +11,8 @@ * delegate against `multiHarnessResearcherFanout`. * * Environment variables: - * TANGLE_API_KEY required — passed to `new Sandbox({ apiKey })` + * TANGLE_SANDBOX_API_KEY required (sandbox-scope: sk_sb_* or orch_prod_*) + * TANGLE_API_KEY fallback — accepted but expected to be a router key in product envs * SANDBOX_BASE_URL optional — sandbox-SDK base URL override * MCP_MAX_CONCURRENT_SANDBOXES default 4 — kernel maxConcurrency cap * MCP_CODER_FANOUT_HARNESSES comma-separated harness ids to use for variants > 1 @@ -38,10 +39,16 @@ async function main(): Promise { const needsSandbox = wantCoder || wantResearcher let sandboxClient: LoopSandboxClient | undefined if (needsSandbox) { - const apiKey = process.env.TANGLE_API_KEY + // TANGLE_SANDBOX_API_KEY is the canonical sandbox-scope key. TANGLE_API_KEY + // is accepted as a fallback for parity with products that already export it + // — but in product environments TANGLE_API_KEY typically means the router + // key (sk-tan-*), which sandbox.create() will REJECT. Operators mounting + // the MCP server inside a product should set TANGLE_SANDBOX_API_KEY + // explicitly (sk_sb_* or orch_prod_*). + const apiKey = process.env.TANGLE_SANDBOX_API_KEY ?? process.env.TANGLE_API_KEY if (!apiKey && !process.env.AGENT_RUNTIME_MCP_ALLOW_NO_KEY) { process.stderr.write( - 'agent-runtime-mcp: TANGLE_API_KEY is required (set AGENT_RUNTIME_MCP_ALLOW_NO_KEY=1 to run without it for diagnostics, or set MCP_DISABLE_CODER=1 MCP_DISABLE_RESEARCHER=1 to run the queue-only subset)\n', + 'agent-runtime-mcp: TANGLE_SANDBOX_API_KEY is required (sandbox-scope key — sk_sb_* or orch_prod_*). TANGLE_API_KEY is accepted as a fallback. Set AGENT_RUNTIME_MCP_ALLOW_NO_KEY=1 to run without it for diagnostics, or MCP_DISABLE_CODER=1 MCP_DISABLE_RESEARCHER=1 to run the queue-only subset.\n', ) process.exit(2) } @@ -85,7 +92,7 @@ async function loadSandboxClient(apiKey: string | undefined): Promise