Skip to content

Feat: accept per-client API key via headers in HTTP mode#46

Open
liranfarage89 wants to merge 2 commits into
mainfrom
feat/per-client-api-key-http
Open

Feat: accept per-client API key via headers in HTTP mode#46
liranfarage89 wants to merge 2 commits into
mainfrom
feat/per-client-api-key-http

Conversation

@liranfarage89

Copy link
Copy Markdown
Contributor

Context

In HTTP mode (MCP_TRANSPORT=http) the server baked a single env-var API key into one shared Env0Client/Env0Service at startup and reused it across every session. So all connected clients shared one key and one RBAC role — making it impossible to self-host the server for multiple users where each acts under their own env0 role.

RBAC is enforced by env0's backend based on the API key's permissions; it isn't implemented in this repo. So forwarding each client's own key is the RBAC mechanism — no RBAC code was needed.

What changed

HTTP mode now builds a per-session Env0Service from the request headers:

  • Client sends Authorization: Basic base64(keyId:keySecret) — forwarded verbatim to the env0 API.
  • Client sends x-env0-organization-id for the org.
  • Requests without Authorization are rejected with 401.
  • Each session gets its own server bound to its own credentials (streamable + SSE), so sessions are isolated.

stdio mode is unchanged — already per-user via the client's env block.

Files

  • env0-client.ts — optional authHeader on Env0Config, used verbatim with fallback to Basic base64(id:secret).
  • config.tsgetAndValidateConfig(overrides, requireCredentials = true); HTTP passes false so the server starts without env-var keys.
  • server.tsstartHttpServer takes a (headers) => McpServer factory; buildSessionServer does the auth check + per-session build; per-session servers map. Also removed /sse header logging that leaked the secret.
  • cli.ts — wires the header→server factory for HTTP; stdio path unchanged.
  • README.md — documents the per-client header contract.

Verification

  • tsc + eslint clean.
  • HTTP starts with no env key set.
  • Missing Authorization401; valid header → 200 + session initialized.
  • Per-session isolation is structural (each session's server carries its own authHeader).

Not yet exercised (needs live env0 keys): an actual tool call returning that key's data, and two-key cross-isolation.

🤖 Generated with Claude Code

In HTTP mode the server baked a single env-var API key into one shared
Env0Service, so all connected clients shared one key and one RBAC role.
This prevented self-hosting the server for multiple users.

Now HTTP mode builds a per-session Env0Service from the request headers:
the client sends Authorization (forwarded verbatim to env0) and
x-env0-organization-id, so each user acts under their own env0 key and
role. Requests without Authorization are rejected with 401. stdio mode is
unchanged (still per-user via env vars).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per-request API keys must not traverse plain HTTP. Note suggested by Yariv.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant