---
title: "Update an eval case"
method: PATCH
path: "/projects/{projectId}/eval-suites/{suiteId}/cases/{caseId}"
tags: ["Eval runs"]
---

# Update an eval case

`PATCH /projects/{projectId}/eval-suites/{suiteId}/cases/{caseId}`

Partial update — omitted fields are left as they are. Supplying `steps` REPLACES the case's definition rather than merging into it. `matchOptions` and `checks` accept `null` to clear the case-level override and fall back to the suite's.

## Path parameters

- `projectId` string, required
- `suiteId` string, required
- `caseId` string, required

## Request body

- EvalCaseUpdateRequest — Partial update. Every field is optional; omitted fields are left as they are. `steps` REPLACES the definition rather than merging into it.
  - `title` string
  - `steps` EvalTestStep[] — REPLACES the case's test definition wholesale when provided.
    - `id` string, required
    - `kind` 'prompt' | 'toolCall' | 'interact' | 'assert', required
    - `prompt` string — User message (`kind: prompt`).
    - `serverName` string — Server that owns the tool (`kind: toolCall`).
    - `toolName` string — Tool name (`kind: toolCall` / `interact`).
    - `arguments` object — Tool-call arguments (`kind: toolCall`).
    - `action` object — Widget action (`kind: interact`).
    - `assertion` object — Predicate or widget assertion (`kind: assert`).
  - `expectedOutput` string
  - `iterations` integer
  - `repetitions` integer — Per-case repetitions under verdict policy 2; omitted cases inherit the suite default.
  - `passThreshold` number — Per-case pass threshold fraction under verdict policy 2.
  - `isNegative` boolean
  - `scenario` string
  - `intent` string, nullable — Optional authored analytics grouping label. Must be already trimmed; omitted leaves the stored value unchanged and null clears it.
  - `models` object[]
    - `model` string, required
    - `provider` string — Derived from a `provider/model` id when omitted.
  - `matchOptions` object, nullable — `null` clears the case override and inherits the suite's.
  - `checks` object, nullable — `null` clears the case override.
    - `mode` 'inherit' | 'replace' | 'extend'
    - `list` object[]
  - `import` EvalCaseImportClaim — What a converter CLAIMED about one imported case. `exact` is CONVERTER-CLAIMED exact — the converter says it applied a structural mapping rule, cited in `note`. MCPJam has NOT verified semantic equivalence, so user-facing copy must say "claimed exact", never "verified" or "accepted". Claim-only: who approved an approximation, when, and why is a PER-RUN decision frozen on the run (`ImportEligibility.approvedApproximationReceipts`), never stored on the case — an approval that lived on a case would outlive the run it was granted for and the edit that invalidated it. Approval and internal keys are rejected with 400, never stripped.
    - `status` 'exact' | 'approximated' | 'unsupported' | 'unresolved', required — `exact`: the converter claims a 1:1 structural mapping, and must cite it in `note`. `approximated`: behaviour was intentionally approximated; a human must approve it for EVERY run. `unsupported`: the source behaviour cannot currently be represented. `unresolved`: a deterministic reference does not resolve against the live target. A selected `unsupported` or `unresolved` case cannot run.
    - `sourceCaseKey` string — The case's identity in the source system, when it had one.
    - `note` string — Why the status is what it is — the mapping rule cited, or what was lost. REQUIRED when `status` is `exact`.

## Response `200`

The updated case.

- EvalCase — A persisted eval case, in the public steps-first shape. Note this is NOT `EvalTestCase`, which is the INLINE authoring shape accepted by suite creation.
  - `id` string, required
  - `declaredId` string — The case's effective declared id. Absent on cases authored before declared identity existed.
  - `title` string, required
  - `steps` EvalTestStep[], required — Ordered test steps. A `prompt` step is a model turn; a single model-free `toolCall` step is a render-check; `assert` steps hold the expectations.
    - `id` string, required
    - `kind` 'prompt' | 'toolCall' | 'interact' | 'assert', required
    - `prompt` string — User message (`kind: prompt`).
    - `serverName` string — Server that owns the tool (`kind: toolCall`).
    - `toolName` string — Tool name (`kind: toolCall` / `interact`).
    - `arguments` object — Tool-call arguments (`kind: toolCall`).
    - `action` object — Widget action (`kind: interact`).
    - `assertion` object — Predicate or widget assertion (`kind: assert`).
  - `expectedOutput` string
  - `iterations` integer, required
  - `repetitions` integer — Trials this case runs under verdict policy 2, overriding the suite default. Absent means the case inherits it. NOT a second spelling of `iterations`: that one is the legacy count, which the legacy resolver reads as a FLOOR (`max(iterations, suite.minimumIterations)`) and which a policy-2 case still reports for compatibility. This one is exact.
  - `passThreshold` number — Fraction of this case's trials that must pass, overriding the suite default. Absent means the case inherits it. Never derived from the suite's `minimumAccuracy`, which is a PERCENT under a different resolver.
  - `isNegative` boolean, required — When true, the case passes if NO tools are called.
  - `scenario` string
  - `intent` string — Optional authored analytics grouping label. Must be already trimmed; absent means unlabelled.
  - `models` object[], required
    - `model` string, required
    - `provider` string
  - `matchOptions` object — Absent when the case sets none — omitted from the response rather than sent as `null`.
  - `checks` object — Absent when the case sets none — omitted from the response rather than sent as `null`.
    - `mode` 'inherit' | 'replace' | 'extend'
    - `list` object[]
  - `import` EvalCaseImportClaim — What a converter CLAIMED about one imported case. `exact` is CONVERTER-CLAIMED exact — the converter says it applied a structural mapping rule, cited in `note`. MCPJam has NOT verified semantic equivalence, so user-facing copy must say "claimed exact", never "verified" or "accepted". Claim-only: who approved an approximation, when, and why is a PER-RUN decision frozen on the run (`ImportEligibility.approvedApproximationReceipts`), never stored on the case — an approval that lived on a case would outlive the run it was granted for and the edit that invalidated it. Approval and internal keys are rejected with 400, never stripped.
    - `status` 'exact' | 'approximated' | 'unsupported' | 'unresolved', required — `exact`: the converter claims a 1:1 structural mapping, and must cite it in `note`. `approximated`: behaviour was intentionally approximated; a human must approve it for EVERY run. `unsupported`: the source behaviour cannot currently be represented. `unresolved`: a deterministic reference does not resolve against the live target. A selected `unsupported` or `unresolved` case cannot run.
    - `sourceCaseKey` string — The case's identity in the source system, when it had one.
    - `note` string — Why the status is what it is — the mapping rule cited, or what was lost. REQUIRED when `status` is `exact`.
  - `createdAt` number, nullable
  - `updatedAt` number, nullable

## Other responses

- `400` — Malformed body or parameters.
- `401` — Missing, invalid, revoked, or orphaned key (`UNAUTHORIZED`) — or the **target MCP server** needs an OAuth grant (`OAUTH_REQUIRED`), which is a property of the server, not your key.
- `403` — Key is valid but not allowed to do this.
- `404` — Unknown project, server, or resource.
- `409` — The resource is not in a state that accepts this write — a stale `expectedRevision`, a duplicate name, or an environment that cannot currently be launched. The request was well-formed; re-read the resource and retry.
- `429` — Per-key rate limit exceeded (60 requests/minute sustained, bursts up to 10). Honor `Retry-After` and back off with jitter.
- `500` — Something failed on MCPJam's side.
- `502` — Could not connect to the target MCP server.

## Changes

- **2026-08-27** `d9a00becd476` — 2 info
  - added the new optional request property `intent`
  - added the optional property `intent` to the response with the `200` status
- **2026-08-26** `e81e8ba8baae` — 2 info
  - added the new optional request property `import`
  - added the optional property `import` to the response with the `200` status
- **2026-08-24** `c479eeceb7a3` — 4 info
  - added the new optional request property `passThreshold`
  - added the new optional request property `repetitions`
  - added the optional property `passThreshold` to the response with the `200` status
  - added the optional property `repetitions` to the response with the `200` status
- **2026-08-19** `879ca349c804` — 1 info
  - added the optional property `declaredId` to the response with the `200` status
- **2026-08-09** `c5f6167955e4` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/mcpjam/apis/mcpjam-api/changes/projects/:projectId/eval-suites/:suiteId/cases/:caseId/patch.md)

---

[API](https://skmtc.dev/mcpjam/apis/mcpjam-api.md) · [All operations](https://skmtc.dev/mcpjam/apis/mcpjam-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/mcpjam/mcpjam-api/revisions/c628a0e95b06/schema)
