Create several eval cases
Adds up to 100 cases to the suite in one call — the bulk form of the single create, and the way to import a suite or convert a repo’s test files without a round trip per case. Every entry needs a title and a non-empty steps array. Cases are validated together and reported individually: a failed entry does not roll back its committed siblings, so the response is 201 even when some entries were refused — read failed rather than branching on the status alone. Send an Idempotency-Key header to make a retry land on the same rows.
curl --request POST \
--url https://app.mcpjam.com/api/v1/projects/{projectId}/eval-suites/{suiteId}/cases/batch \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"cases": [
{
"id": "c_lists_files",
"title": "lists files",
"steps": [
{
"id": "s1",
"kind": "prompt",
"prompt": "List the files in the project root."
},
{
"id": "s2",
"kind": "assert",
"assertion": {
"type": "toolCalledWith",
"toolName": "list_files",
"args": {
"args": {}
}
}
}
]
},
{
"title": "reads a file",
"steps": [
{
"id": "s1",
"kind": "prompt",
"prompt": "Read README.md."
},
{
"id": "s2",
"kind": "assert",
"assertion": {
"type": "toolCalledWith",
"toolName": "read_file",
"args": {
"args": {}
}
}
}
]
}
]
}
'import requests
url = "https://app.mcpjam.com/api/v1/projects/{projectId}/eval-suites/{suiteId}/cases/batch"
payload = { "cases": [
{
"id": "c_lists_files",
"title": "lists files",
"steps": [
{
"id": "s1",
"kind": "prompt",
"prompt": "List the files in the project root."
},
{
"id": "s2",
"kind": "assert",
"assertion": {
"type": "toolCalledWith",
"toolName": "list_files",
"args": { "args": {} }
}
}
]
},
{
"title": "reads a file",
"steps": [
{
"id": "s1",
"kind": "prompt",
"prompt": "Read README.md."
},
{
"id": "s2",
"kind": "assert",
"assertion": {
"type": "toolCalledWith",
"toolName": "read_file",
"args": { "args": {} }
}
}
]
}
] }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
cases: [
{
id: 'c_lists_files',
title: 'lists files',
steps: [
{id: 's1', kind: 'prompt', prompt: 'List the files in the project root.'},
{
id: 's2',
kind: 'assert',
assertion: {type: 'toolCalledWith', toolName: 'list_files', args: {args: {}}}
}
]
},
{
title: 'reads a file',
steps: [
{id: 's1', kind: 'prompt', prompt: 'Read README.md.'},
{
id: 's2',
kind: 'assert',
assertion: {type: 'toolCalledWith', toolName: 'read_file', args: {args: {}}}
}
]
}
]
})
};
fetch('https://app.mcpjam.com/api/v1/projects/{projectId}/eval-suites/{suiteId}/cases/batch', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.mcpjam.com/api/v1/projects/{projectId}/eval-suites/{suiteId}/cases/batch",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'cases' => [
[
'id' => 'c_lists_files',
'title' => 'lists files',
'steps' => [
[
'id' => 's1',
'kind' => 'prompt',
'prompt' => 'List the files in the project root.'
],
[
'id' => 's2',
'kind' => 'assert',
'assertion' => [
'type' => 'toolCalledWith',
'toolName' => 'list_files',
'args' => [
'args' => [
]
]
]
]
]
],
[
'title' => 'reads a file',
'steps' => [
[
'id' => 's1',
'kind' => 'prompt',
'prompt' => 'Read README.md.'
],
[
'id' => 's2',
'kind' => 'assert',
'assertion' => [
'type' => 'toolCalledWith',
'toolName' => 'read_file',
'args' => [
'args' => [
]
]
]
]
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.mcpjam.com/api/v1/projects/{projectId}/eval-suites/{suiteId}/cases/batch"
payload := strings.NewReader("{\n \"cases\": [\n {\n \"id\": \"c_lists_files\",\n \"title\": \"lists files\",\n \"steps\": [\n {\n \"id\": \"s1\",\n \"kind\": \"prompt\",\n \"prompt\": \"List the files in the project root.\"\n },\n {\n \"id\": \"s2\",\n \"kind\": \"assert\",\n \"assertion\": {\n \"type\": \"toolCalledWith\",\n \"toolName\": \"list_files\",\n \"args\": {\n \"args\": {}\n }\n }\n }\n ]\n },\n {\n \"title\": \"reads a file\",\n \"steps\": [\n {\n \"id\": \"s1\",\n \"kind\": \"prompt\",\n \"prompt\": \"Read README.md.\"\n },\n {\n \"id\": \"s2\",\n \"kind\": \"assert\",\n \"assertion\": {\n \"type\": \"toolCalledWith\",\n \"toolName\": \"read_file\",\n \"args\": {\n \"args\": {}\n }\n }\n }\n ]\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.mcpjam.com/api/v1/projects/{projectId}/eval-suites/{suiteId}/cases/batch")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"cases\": [\n {\n \"id\": \"c_lists_files\",\n \"title\": \"lists files\",\n \"steps\": [\n {\n \"id\": \"s1\",\n \"kind\": \"prompt\",\n \"prompt\": \"List the files in the project root.\"\n },\n {\n \"id\": \"s2\",\n \"kind\": \"assert\",\n \"assertion\": {\n \"type\": \"toolCalledWith\",\n \"toolName\": \"list_files\",\n \"args\": {\n \"args\": {}\n }\n }\n }\n ]\n },\n {\n \"title\": \"reads a file\",\n \"steps\": [\n {\n \"id\": \"s1\",\n \"kind\": \"prompt\",\n \"prompt\": \"Read README.md.\"\n },\n {\n \"id\": \"s2\",\n \"kind\": \"assert\",\n \"assertion\": {\n \"type\": \"toolCalledWith\",\n \"toolName\": \"read_file\",\n \"args\": {\n \"args\": {}\n }\n }\n }\n ]\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.mcpjam.com/api/v1/projects/{projectId}/eval-suites/{suiteId}/cases/batch")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"cases\": [\n {\n \"id\": \"c_lists_files\",\n \"title\": \"lists files\",\n \"steps\": [\n {\n \"id\": \"s1\",\n \"kind\": \"prompt\",\n \"prompt\": \"List the files in the project root.\"\n },\n {\n \"id\": \"s2\",\n \"kind\": \"assert\",\n \"assertion\": {\n \"type\": \"toolCalledWith\",\n \"toolName\": \"list_files\",\n \"args\": {\n \"args\": {}\n }\n }\n }\n ]\n },\n {\n \"title\": \"reads a file\",\n \"steps\": [\n {\n \"id\": \"s1\",\n \"kind\": \"prompt\",\n \"prompt\": \"Read README.md.\"\n },\n {\n \"id\": \"s2\",\n \"kind\": \"assert\",\n \"assertion\": {\n \"type\": \"toolCalledWith\",\n \"toolName\": \"read_file\",\n \"args\": {\n \"args\": {}\n }\n }\n }\n ]\n }\n ]\n}"
response = http.request(request)
puts response.read_bodyAuthorizations
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
Makes the write retry-safe. A repeat of the same request under the same key lands on the rows the first attempt authored instead of creating a second copy; those entries come back with replayed: true.
256Path Parameters
ID of the hosted project that contains the server.
Eval suite ID, as returned by POST /eval-runs.
Body
Author several cases in one call. Each entry takes the same fields as a single create. The 100-case cap is deliberately smaller than a suite file's 500-case limit, so a maximal file uploads in several calls.
1 - 100 elementsShow child attributes
Show child attributes
What to do with a case whose definition already matches one in the suite. An unrecognized value coerces to block and reports the coercion in the response's duplicatePolicy rather than failing the call.
block, warn, create_anyway Required by warn and create_anyway. Recorded on the case's revision.
1Response
Per-case outcomes. Some entries may have failed; see failed.
A partial outcome by design: the cases in created were written even when failed is non-empty. Both arrays carry the index of the request entry they describe.
Was this page helpful?
curl --request POST \
--url https://app.mcpjam.com/api/v1/projects/{projectId}/eval-suites/{suiteId}/cases/batch \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"cases": [
{
"id": "c_lists_files",
"title": "lists files",
"steps": [
{
"id": "s1",
"kind": "prompt",
"prompt": "List the files in the project root."
},
{
"id": "s2",
"kind": "assert",
"assertion": {
"type": "toolCalledWith",
"toolName": "list_files",
"args": {
"args": {}
}
}
}
]
},
{
"title": "reads a file",
"steps": [
{
"id": "s1",
"kind": "prompt",
"prompt": "Read README.md."
},
{
"id": "s2",
"kind": "assert",
"assertion": {
"type": "toolCalledWith",
"toolName": "read_file",
"args": {
"args": {}
}
}
}
]
}
]
}
'import requests
url = "https://app.mcpjam.com/api/v1/projects/{projectId}/eval-suites/{suiteId}/cases/batch"
payload = { "cases": [
{
"id": "c_lists_files",
"title": "lists files",
"steps": [
{
"id": "s1",
"kind": "prompt",
"prompt": "List the files in the project root."
},
{
"id": "s2",
"kind": "assert",
"assertion": {
"type": "toolCalledWith",
"toolName": "list_files",
"args": { "args": {} }
}
}
]
},
{
"title": "reads a file",
"steps": [
{
"id": "s1",
"kind": "prompt",
"prompt": "Read README.md."
},
{
"id": "s2",
"kind": "assert",
"assertion": {
"type": "toolCalledWith",
"toolName": "read_file",
"args": { "args": {} }
}
}
]
}
] }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
cases: [
{
id: 'c_lists_files',
title: 'lists files',
steps: [
{id: 's1', kind: 'prompt', prompt: 'List the files in the project root.'},
{
id: 's2',
kind: 'assert',
assertion: {type: 'toolCalledWith', toolName: 'list_files', args: {args: {}}}
}
]
},
{
title: 'reads a file',
steps: [
{id: 's1', kind: 'prompt', prompt: 'Read README.md.'},
{
id: 's2',
kind: 'assert',
assertion: {type: 'toolCalledWith', toolName: 'read_file', args: {args: {}}}
}
]
}
]
})
};
fetch('https://app.mcpjam.com/api/v1/projects/{projectId}/eval-suites/{suiteId}/cases/batch', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.mcpjam.com/api/v1/projects/{projectId}/eval-suites/{suiteId}/cases/batch",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'cases' => [
[
'id' => 'c_lists_files',
'title' => 'lists files',
'steps' => [
[
'id' => 's1',
'kind' => 'prompt',
'prompt' => 'List the files in the project root.'
],
[
'id' => 's2',
'kind' => 'assert',
'assertion' => [
'type' => 'toolCalledWith',
'toolName' => 'list_files',
'args' => [
'args' => [
]
]
]
]
]
],
[
'title' => 'reads a file',
'steps' => [
[
'id' => 's1',
'kind' => 'prompt',
'prompt' => 'Read README.md.'
],
[
'id' => 's2',
'kind' => 'assert',
'assertion' => [
'type' => 'toolCalledWith',
'toolName' => 'read_file',
'args' => [
'args' => [
]
]
]
]
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.mcpjam.com/api/v1/projects/{projectId}/eval-suites/{suiteId}/cases/batch"
payload := strings.NewReader("{\n \"cases\": [\n {\n \"id\": \"c_lists_files\",\n \"title\": \"lists files\",\n \"steps\": [\n {\n \"id\": \"s1\",\n \"kind\": \"prompt\",\n \"prompt\": \"List the files in the project root.\"\n },\n {\n \"id\": \"s2\",\n \"kind\": \"assert\",\n \"assertion\": {\n \"type\": \"toolCalledWith\",\n \"toolName\": \"list_files\",\n \"args\": {\n \"args\": {}\n }\n }\n }\n ]\n },\n {\n \"title\": \"reads a file\",\n \"steps\": [\n {\n \"id\": \"s1\",\n \"kind\": \"prompt\",\n \"prompt\": \"Read README.md.\"\n },\n {\n \"id\": \"s2\",\n \"kind\": \"assert\",\n \"assertion\": {\n \"type\": \"toolCalledWith\",\n \"toolName\": \"read_file\",\n \"args\": {\n \"args\": {}\n }\n }\n }\n ]\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.mcpjam.com/api/v1/projects/{projectId}/eval-suites/{suiteId}/cases/batch")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"cases\": [\n {\n \"id\": \"c_lists_files\",\n \"title\": \"lists files\",\n \"steps\": [\n {\n \"id\": \"s1\",\n \"kind\": \"prompt\",\n \"prompt\": \"List the files in the project root.\"\n },\n {\n \"id\": \"s2\",\n \"kind\": \"assert\",\n \"assertion\": {\n \"type\": \"toolCalledWith\",\n \"toolName\": \"list_files\",\n \"args\": {\n \"args\": {}\n }\n }\n }\n ]\n },\n {\n \"title\": \"reads a file\",\n \"steps\": [\n {\n \"id\": \"s1\",\n \"kind\": \"prompt\",\n \"prompt\": \"Read README.md.\"\n },\n {\n \"id\": \"s2\",\n \"kind\": \"assert\",\n \"assertion\": {\n \"type\": \"toolCalledWith\",\n \"toolName\": \"read_file\",\n \"args\": {\n \"args\": {}\n }\n }\n }\n ]\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.mcpjam.com/api/v1/projects/{projectId}/eval-suites/{suiteId}/cases/batch")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"cases\": [\n {\n \"id\": \"c_lists_files\",\n \"title\": \"lists files\",\n \"steps\": [\n {\n \"id\": \"s1\",\n \"kind\": \"prompt\",\n \"prompt\": \"List the files in the project root.\"\n },\n {\n \"id\": \"s2\",\n \"kind\": \"assert\",\n \"assertion\": {\n \"type\": \"toolCalledWith\",\n \"toolName\": \"list_files\",\n \"args\": {\n \"args\": {}\n }\n }\n }\n ]\n },\n {\n \"title\": \"reads a file\",\n \"steps\": [\n {\n \"id\": \"s1\",\n \"kind\": \"prompt\",\n \"prompt\": \"Read README.md.\"\n },\n {\n \"id\": \"s2\",\n \"kind\": \"assert\",\n \"assertion\": {\n \"type\": \"toolCalledWith\",\n \"toolName\": \"read_file\",\n \"args\": {\n \"args\": {}\n }\n }\n }\n ]\n }\n ]\n}"
response = http.request(request)
puts response.read_body
