Attach one environment to a suite (atomic append)
APPEND one project environment to the suite’s attachments, if it is not already there.
Distinct from PATCH /eval-suites/{suiteId} with environmentIds, which REPLACES the whole list. An append built on the replace door is a read-modify-write across two round trips, and a concurrent attach landing in between is silently DETACHED. The compose-and-run path attaches on every launch, which makes that race ordinary rather than theoretical, so the append happens inside one transaction instead.
Idempotent: attaching an already-attached environment reports attached: false and changes nothing, which is what lets a retried launch converge instead of erroring. Subject to the same cap, membership and schedule-pin rules as the replace door — growing a single-environment suite to two is exactly the edit that strands an enabled unpinned schedule.
Authorizations
MCPJam API key (sk_…). Create one at Settings → API keys. Guest sessions cannot use the API, and API keys cannot manage other API keys.
Headers
Which vocabulary this request and its response speak. Absent means 1, which is byte-for-byte today's contract: the same request fields, the same refusals, the same response projection. 2 is the canonical vocabulary. Any other value is a 400 with code: "VALIDATION_ERROR".
Today it decides one thing: the spelling of an evaluator's policy role. Vocabulary 1 accepts and returns gating; vocabulary 2 accepts both spellings and returns the canonical required. Sending required without the header is a 400, deliberately — vocabulary 1 is not widened to meet vocabulary 2 half way, because a boundary that accepts a spelling it does not announce is one two implementations can disagree about.
A response that varies by vocabulary sends Vary: x-mcpjam-eval-vocabulary.
1, 2 Path Parameters
ID of the hosted project that contains the server.
Eval suite ID, as returned by POST /eval-runs.

