POST /apps/{appId}/test-suites/bulk-delete — Bulk-delete test suites — Requires scope: `write`.
Parameters (2)
appIdstringrequired
Path parameter: appId
test_suite_idsarrayrequired
createAPIKey
POST /api-keys — Create an API key — Requires scope: `admin`. The raw key is returned only once in the response.
Parameters (3)
namestringrequired
scopesarrayrequired
ttl_daysnumber
createApp
POST /apps — Create an app — Requires scope: `write`.
Parameters (9)
api_examplesstring
Sample request/response pairs the AI consults when authoring suites.
authobject
Authentication configuration for test execution. The runner injects the matching headers on every step request.
disable_schema_assertionboolean
docsstring
Free-form developer docs the AI uses as additional context.
endpointstring
max_test_suitesnumber
Cap on how many suites generate-tests will mint at once. Server default applies if omitted.
namestringrequired
App name. IMMUTABLE — cannot be changed via updateApp.
schemastring
OpenAPI/Swagger doc the validators use to suggest test cases.
webhook_urlstring
Optional webhook URL invoked at run lifecycle events.
createCIBranch
POST /apps/{appId}/branches/ci — Find-or-create a CI branch — Creates a new Keploy branch (or returns the existing one) for a CI pipeline run. Idempotent on the (appId, name) pair. Requires scope: `write`.
Parameters (3)
appIdstringrequired
Path parameter: appId
git_refobject
Optional Git provider context (PR, repo, etc.)
namestringrequired
Branch name (e.g., `pr-123`)
createCluster
POST /clusters — Create a cluster — Provisions a new cluster in the authenticated company and returns its access key. The access key is shown only once. Requires scope: `admin`.
Parameters (2)
deployment_typestring
Defaults to self-hosted when omitted.
namestringrequired
Cluster name. Must be unique within the company.
createMock
POST /apps/{appId}/recordings/{testSetId}/mocks — Author one mock under a recording — Insert a single mock into the given test set. When `branch_id` is
supplied, the mock lands on that branch's overlay
(`branch_sandbox_ops`) and only surfaces to main on merge.
Without `branch_id` the mock writes straight to main — same
behaviour as the recording-driven agent path.
Authoring shape — pick ONE:
- **`mock_yaml`** (PREFERRED) — paste the canonical mock YAML
envelope (`version` / `kind` / `name` / `spec` with the
per-kind payload, exactly as it lives in `mocks.yaml` on
disk). The server decodes via OSS DecodeMocks so kind-
specific Spec contents (`req`, `resp`, `metadata`, …)
round-trip without field-name loss. This is the only path
that preserves payloads pasted from existing mocks.
- **`mock`** — typed OSS Mock JSON object. Brittle: the OSS
struct uses PascalCase JSON tags (`Metadata`, `Req`, `Res`),
so lowercase canonical keys are silently dropped. Use only
when authoring programmatically from typed Go shapes.
When both are sent, `mock_yaml` wins.
Requires scope: `write`.
Parameters (5)
appIdstringrequired
Path parameter: appId
branch_idstring
Optional branch overlay id. Absent → write lands on main.
mockobject
OSS Mock — see schema in keploy.io/server/v3 pkg/models/mock.go. Use only when authoring from typed Go shapes. Lowercase YAML keys are dropped; prefer mock_yaml.
POST /apps/{appId}/test-suites — Create a test suite — Requires scope: `write`.
Parameters (2)
appIdstringrequired
Path parameter: appId
branch_idstring
Optional Keploy branch UUID. When set, scopes the read/write to that branch's overlay
(copy-on-write — see /apps/{appId}/branches). When absent or empty, operations target
the main branch (the historical default). Required for writes against a branch (the
api-server's branch gate rejects 400 otherwise); reads are tolerant of absence.
create_branch
Create a Keploy branch on an app — find-or-create on name conflict.
Pass the dev's CURRENT GIT BRANCH as the name. Detect it BEFORE calling this tool:
Bash: `git rev-parse --abbrev-ref HEAD` in the dev's app_dir.
Exit non-zero or output "HEAD" → not in a git repo / detached HEAD; ASK the dev for a name and re-call.
Find-or-create semantics: if a writable Keploy branch with that name already exists, it's returned (no error, no duplicate). Idempotent — safe to call on every retry.
Output: {branch_id, name, status, created} where created=true means a fresh branch was minted, created=false means an existing one was reused.
Pass the returned branch_id to subsequent write tools (create_test_suite, replay_sandbox_test, record_sandbox_test, etc.). Branch_id is sticky for the conversation — don't re-call create_branch unless the dev's git branch changed.
Parameters (2)
app_idstringrequired
Keploy app ID
namestringrequired
REQUIRED. Pass the dev's current git branch as the name. Detect via Bash `git rev-parse --abbrev-ref HEAD` in app_dir before calling. Don't invent a name — ASK the dev when not in a git repo / detached HEAD.
create_mock
Author a single mock inside a test set. Use this when the dev wants to add a hand-written downstream-call recording (HTTP response, DB result, etc.) without re-running the full record session.
Authoring shape — pick ONE:
* `mock_yaml` (PREFERRED) — canonical single-doc YAML envelope (version / kind / name / spec). Round-trips kind-specific contents losslessly.
* `mock` — typed OSS Mock object { Name, Kind, Spec }. Brittle: lowercase canonical keys are dropped during decode; use only when authoring from typed Go shapes.
When both are sent, `mock_yaml` wins. `branch_id` is REQUIRED — direct writes to main via MCP are blocked. Resolve via list_branches / create_branch before calling.
Returns the persisted mock. Idempotent on (Name, test_set_id, branch) at the storage layer — a retry with the same name and branch returns the existing row instead of duplicating.
Parameters (5)
app_idstringrequired
Keploy app ID
branch_idstringrequired
REQUIRED. Keploy branch UUID — resolve via list_branches / create_branch. Direct writes to main are blocked.
mockobject
OSS Mock typed body — { Name, Kind, Spec }. Optional when mock_yaml is set.
Test set id the mock attaches to. Use list_recordings to discover.
create_test_suite
Create a new API test suite with test steps. Each step defines an HTTP request and assertions to validate the response. Steps can extract values from responses into variables for chaining requests.
═══════════════════════════════════════════════════════════════════
TEMPLATES FIRST — the fastest path to a passing suite:
═══════════════════════════════════════════════════════════════════
If you've called get_app_testing_context, its response includes `authoring_templates` — ready-to-fill suite skeletons keyed by endpoint shape (POST create, GET single, GET list self-contained, DELETE with verify, PUT update, GraphQL mutation, GraphQL query, auth+protected). Each template encodes the opinionated default for one common pattern: correct step count, correct assertion mix, correct use of app-level generators, correct cleanup.
WORKFLOW for any common authoring intent:
1. Find the template whose endpoint_shape matches your intent (e.g. "POST /<resource>" for create-user).
2. Copy its steps verbatim.
3. Fill every <FILL: ...> blank with the real value (URL path, captured response body, expected status, etc.).
4. Replace {{genXxx}} placeholders with the matching keys from recommended_substitutions — call update_app_custom_variables first if a needed gen* doesn't exist yet.
5. Capture real responses via curl before pasting (R10 — MANDATORY).
6. Call this tool (create_test_suite) with the filled steps.
Templates produce suites that pass the validator in one shot. Drafting from scratch (skipping templates) is the failure-iteration path — the rules block below documents why.
WHEN TO SKIP TEMPLATES: only for genuine edge-case patterns (smoke tests that legitimately only do /health; contract tests asserting empty-state behaviour on fresh tenants; multi-tenant isolation checks; long-running auth flows). The rules below apply directly.
═══════════════════════════════════════════════════════════════════
STEP 0 — read the canonical schema BEFORE drafting:
═══════════════════════════════════════════════════════════════════
If you've already called get_app_testing_context, the canonical step schema is in its response under the `step_schema` field — read it from there. Otherwise run `keploy test-suite-format` once before writing any suite JSON. The schema describes the MANDATORY rules below in detail plus the two-step prelude+POST skeleton you must follow. Authors who skip this and draft from training-data priors burn ~50s per validator rejection on iter 1.
═══════════════════════════════════════════════════════════════════
APP-LEVEL GENERATORS FIRST — check before authoring a prelude step:
═══════════════════════════════════════════════════════════════════
For dynamic values needed in POST / PUT / PATCH bodies (random emails, fresh IDs, per-run tokens), the FIRST place to look is the app's existing appLevelCustomVariables — surfaced as `recommended_substitutions` in the get_app_testing_context response.
Decision flow:
1. Read recommended_substitutions.dynamic_generators. Each entry has {key, reference, suggested_for}.
2. If a gen* generator fits your field (e.g., genEmail for an email field):
→ reference it directly via {{genEmail}} in the body. NO prelude step needed.
3. If no existing generator fits:
→ call update_app_custom_variables to add one (e.g., genUserId backed by a JS function
literal). Future suites on this app reuse it.
→ THEN reference it in the body. Still no prelude step needed.
4. Only fall back to the per-suite prelude pattern (declare generator on step 0's extract,
reference in step 1+) when the generator is truly suite-local — e.g., chains an extract from
a previous step's response, which an app-level entry can't.
Why this matters: each prelude step is a no-op /health hit that adds nothing to coverage. Skipping it shortens the suite, reduces author iteration time, and makes app-level generators a real reuse primitive instead of decorative metadata.
═══════════════════════════════════════════════════════════════════
MANDATORY FOR EVERY STEP — the validat
Parameters (8)
app_dirstring
Absolute path to the dev's repo root (where the app was started). Defaults to '.' (cwd). The CLI invocation cd's here.
app_idstringrequired
Keploy app ID
app_urlstringrequired
Base URL the dev's local app is listening on, e.g. http://localhost:8080. The enterprise CLI hits this when running the suite twice for the idempotency, generator-dynamism, and GET-coupling checks.
branch_idstringrequired
REQUIRED. Keploy branch ID (uuid). Resolve via the explicit two-step flow BEFORE calling: (1) Bash `git rev-parse --abbrev-ref HEAD` in app_dir to detect the dev's git branch; (2) call the create_branch MCP tool with {app_id, name: <git branch>} — find-or-create returns {branch_id, ...}; pass that branch_id here. Direct writes to main are blocked.
descriptionstring
Suite description
labelsstring
Comma-separated labels for filtering
namestringrequired
Suite name
steps_jsonstringrequired
JSON array of test steps
deleteApp
DELETE /apps/{appId} — Delete an app — Requires scope: `admin`.
Parameters (1)
appIdstringrequired
Path parameter: appId
deleteMock
DELETE /apps/{appId}/recordings/{testSetId}/mocks/{mockId} — Drop one mock — Idempotent — returns 200 even if the mock is already gone. Path
`{mockId}` accepts both the UUID `_id` and the human-readable
Name (resolved within the test set).
Branch-aware via optional `branch_id` query param.
Requires scope: `write`.
Parameters (4)
appIdstringrequired
Path parameter: appId
branch_idstring
Optional branch overlay id. When set, the delete writes a
tombstone op onto the branch's overlay (main untouched
until the branch merges). When absent, the delete applies
to main directly — no tombstone is involved.
mockIdstringrequired
Mock id (UUID assigned at insert) OR human-readable mock name. The server resolves names within the test set, so the UI can pass either form.
testSetIdstringrequired
Path parameter: testSetId
deleteSmartMock
DELETE /apps/{appId}/smart-set/mocks/{name} — Delete a smart-set mock (branch-only) — Tombstone a smart-set mock on a branch. `branch_id` is REQUIRED — edits
are branch-only. Requires scope: `write`.
Parameters (3)
appIdstringrequired
Path parameter: appId
branch_idstringrequired
Branch UUID — required (edits are branch-only).
namestringrequired
Smart-set mock name (e.g. "mock-2").
deleteSmartTestCase
DELETE /apps/{appId}/smart-set/cases/{name} — Delete a smart test case (branch-only) — Delete a smart case on a branch — a tombstone overlay; main is untouched
until merge. `branch_id` is REQUIRED: edits, including delete, are
branch-only. Requires scope: `write`.
Parameters (3)
appIdstringrequired
Path parameter: appId
branch_idstringrequired
Branch UUID — required (delete is branch-only).
namestringrequired
Smart-set-local case name (e.g. "test-3").
deleteTestSuite
DELETE /apps/{appId}/test-suites/{suiteId} — Delete a test suite — Requires scope: `write`.
Parameters (3)
appIdstringrequired
Path parameter: appId
branch_idstring
Optional Keploy branch UUID. When set, scopes the read/write to that branch's overlay
(copy-on-write — see /apps/{appId}/branches). When absent or empty, operations target
the main branch (the historical default). Required for writes against a branch (the
api-server's branch gate rejects 400 otherwise); reads are tolerant of absence.
suiteIdstringrequired
Path parameter: suiteId
delete_mock
Drop one mock from a test set. Idempotent — returns 200 even if the mock is already gone, so retries on a network blip are safe.
Accepts UUID or Name as `mock_id`. `branch_id` is REQUIRED — the delete lays a tombstone overlay on the branch (mergeable). Direct deletes from main via MCP are blocked.
Parameters (4)
app_idstringrequired
Keploy app ID
branch_idstringrequired
REQUIRED. Keploy branch UUID. Direct deletes from main are blocked.
mock_idstringrequired
Mock UUID OR human-readable Name.
test_set_idstringrequired
Test set id
delete_recording
Wholesale-delete a recording (test set + its cases + mocks + mapping). `branch_id` is REQUIRED — the delete lays a tombstone overlay on the branch (mergeable). Direct deletes from main via MCP are blocked.
Returns { deleted: true } on success, 404 when the (app_id, test_set_id) tuple doesn't resolve to a recording.
Parameters (3)
app_idstringrequired
Keploy app ID
branch_idstringrequired
REQUIRED. Keploy branch UUID. Direct deletes from main are blocked.
test_set_idstringrequired
Recording (test set) UUID to delete.
delete_test_suite
Delete a test suite on a Keploy branch — synchronous, no playbook to walk.
USE THIS when:
* The dev's update_test_suite call was rejected with "preserves no steps from the existing suite — that's a full rewrite, not an edit". Delete the existing suite and re-author from scratch via create_test_suite. The error message itself routes here.
* The dev explicitly says "delete the suite", "remove suite X", "wipe my orderflow suite".
* A genuine wholesale redesign — every step changed in shape — that the audit trail shouldn't try to reconcile as edits.
DO NOT USE THIS when:
* The dev wants a real edit (one assertion, one step's body). Use update_test_suite + preserve existing step IDs instead — keeps audit history intact.
* The dev wants to "redo" a single failed run. Test runs are independent of suite state; just rerun via replay_test_suite.
INPUT
* app_id (required) — Keploy app id
* suite_id (required) — UUID of the suite to delete
* branch_id (required) — Keploy branch UUID. The delete creates a branch-scoped DeleteTestSuite audit event so reads on the same branch see the suite as gone. Direct main writes are blocked.
OUTPUT
* On success: {"deleted": true} — suite is tombstoned at the branch overlay; subsequent reads (getTestSuite / listTestSuites) on this branch return 404 / exclude it.
* 404 if the suite_id doesn't exist on this app/branch (verify via getTestSuite or listTestSuites first if you're unsure).
After delete, the standard re-create flow is: (1) call create_test_suite with a freshly authored steps_json. The new suite gets a fresh suite_id; the old id is tombstoned, not reusable.
═══════════════════════════════════════════════════════════════════
DISCOVERY — when the dev hands you a bare suite_id with no app_id / branch_id:
═══════════════════════════════════════════════════════════════════
Suites live on a (app_id, branch_id) tuple. A bare suite_id has no on-disk hint about which app or branch holds it; you have to RESOLVE both before calling this tool. Walk these steps in order — STOP as soon as getTestSuite returns 200:
1. Detect the dev's git branch: Bash `git rev-parse --abbrev-ref HEAD` in app_dir.
If exit non-zero / output is "HEAD" → not a git repo / detached HEAD; ASK the dev for the Keploy branch name (don't invent one).
2. Resolve candidate apps via the cwd basename: Bash `basename $(pwd)` → call listApps with q=<basename>. Usually 1–2 candidates. If 0 → ASK; if >1 → walk every candidate in step 4.
3. For each candidate app, call list_branches({app_id}) and find the branch whose `name` matches the git branch from step 1. That gives you {branch_id}. If no match → not this app, try next.
4. Verify with getTestSuite({app_id, suite_id, branch_id=<from step 3>}). 200 → resolved; 404 → wrong app/branch, try next.
5. If steps 2–4 exhaust, walk every OPEN branch on each candidate app, then try main (branch_id omitted). If still nothing → ASK the dev for the {app_id, branch_id} pair.
After resolving once in a session, REUSE the {app_id, branch_id} for subsequent suite-targeted calls; don't re-walk discovery for every action.
Parameters (4)
app_idstringrequired
Keploy app ID
branch_idstringrequired
REQUIRED. Keploy branch UUID. Resolve via the two-step flow: `git rev-parse --abbrev-ref HEAD` → create_branch tool. Direct main writes are blocked.
reasonstring
Optional human-readable reason recorded on the audit event ("AI rewrite", "deprecated endpoint", etc).
suite_idstringrequired
UUID of the test suite to delete
devloop_begin_oauth_install
Start the Path B agent-walked install for the V1 flow. Returns a playbook the AI walks with the dev to land a kep_* PAT in their editor's MCP config.
Current implementation routes through app.keploy.io/settings/api-keys (the existing PAT-issuance surface). The full auto-provision OAuth round-trip (browser OAuth → /auth/temp-code/exchange → /user/mcp/provision → PAT auto-pasted) lands when the enterprise-ui /auth/cli-install page is built; this tool's interface won't change when that lifts, only the underlying steps will get shorter.
ABSOLUTE: this tool surfaces a public URL. Do NOT print API keys, PATs, or OAuth tokens to the conversation. The dev pastes the secret into their editor config directly; the agent never sees it.
Parameters (1)
editorstring
Editor the dev is using (cursor / windsurf / claude-code / claude-desktop / vscode / trae). Tailors the config-path hint in the playbook. Omit if unknown.
devloop_detect_app
Resolve a Keploy app and return a playbook the AI should walk to inspect the dev's repo for V1 ("user maintains the flow") test generation.
This MCP server has no filesystem access — the AI is the inspector. The tool returns: the resolved app metadata (id, name, base_url, storage_mode) plus a `detection_playbook` field that lists exactly what the AI should look for in app_dir (framework signatures, top-N candidate resources by handler count, port hints, existing keploy/api-tests/ directory). The AI uses its native read/grep tools to answer those questions, then calls devloop_generate_resource_flow with the findings.
Resolution model — pass EITHER:
* app_id (UUID) — exact match, fast path.
* app_name_hint (case-insensitive substring) — matches against existing apps in your tenant; resolves to one app or errors with the candidate list when ambiguous.
If storage_mode is unset, the DEVLOOP decision-gate defaults require devloop_resolve_storage to run first. This tool does not re-ask the storage question — it just surfaces whatever devloop_resolve_storage already persisted (or "" if neither has run yet).
Parameters (3)
app_dirstringrequired
Absolute path to the dev's repo root. Echoed back in the detection_playbook so the AI knows where to grep.
app_idstring
Keploy app UUID (preferred when known)
app_name_hintstring
Case-insensitive substring of the app name (e.g. the cwd basename). Falls back to app_id when this matches multiple apps.
devloop_expand_coverage
Expand V1 API-test coverage from the single seed flow to the remaining detected resources. Use this AFTER devloop_mutation_demo has surfaced a positive catch result for the FIRST resource — that's the "manufactured proof" gate the dev needed before agreeing to scale.
Returns a procedure that loops over the dev-approved candidates:
for each resource:
devloop_generate_resource_flow(app_id, resource, app_dir, base_url)
ASK dev: continue / stop / pick a different resource
end
Mutation demo is NOT in the per-resource loop. Once a session has seen
mutation_demo run on the seed resource and prove its catch behavior,
re-firing it for every new resource produces busywork. The dev opts in
to mutation-on-expanded-resources via the post-expand multi-option
menu (see DevloopInstructionsAddendum "After devloop_expand_coverage"),
not as a default step inside this loop. If the dev asks "mutate this
too" mid-expand, fine — fire devloop_mutation_demo on that resource
on demand.
Stop conditions:
* Dev says "stop" / "enough" / "later" → exit cleanly.
* Any generate step errors → surface to dev, ASK whether to retry, skip, or stop.
DO NOT silently run all candidates without dev confirmation per
resource — the DEVLOOP decision-gate defaults explicitly require an
opt-in between each resource, because (a) the dev may want to inspect
each test before approving the next, and (b) a tangentially-named
candidate may be the wrong fit and the dev wants to swap.
Parameters (3)
app_dirstringrequired
Absolute path to the dev's repo root
app_idstringrequired
Keploy app UUID
base_urlstringrequired
Base URL the tests run against
devloop_generate_resource_flow
Generate one chained-CRUD API test for a single resource. Behavior depends on the app's devloop_storage_mode (set this first via devloop_resolve_storage / devloop_set_storage_mode):
* repo mode → returns a PLAYBOOK for you to walk. Steps: (1) run "keploy test-gen generate-from-code --app-dir <dir> --resource <name>" to scaffold the directory + empty config.yaml; (2) use your Write tool to author keploy/api-tests/<resource>/test.yaml using the schema returned by devloop_detect_app; (3) run "keploy test-gen run --test-dir keploy/api-tests --suite <Name>_CRUD --base-url <url> --ci" to verify the test parses and passes; (4) call devloop_mutation_demo next (auto, per the DEVLOOP instructions).
* cloud mode → returns guidance to call the existing create_test_suite tool instead. The repo-mode playbook is NOT used in cloud mode.
ARGUMENTS — you should already have these from your devloop_detect_app call:
* app_id, resource, app_dir, base_url, framework, handler_files. If any are missing, call devloop_detect_app again.
The tool does NOT generate the YAML body itself — you do, using the schema from devloop_detect_app's detection_playbook. This is intentional: ATG quality depends on the AI seeing the actual handler implementations (which it can read via its own tools) far better than a server-side generator could. Aim for ≤ 30 lines per test.yaml, idempotent mutating steps, chained extract/{{var}} flow.
Parameters (5)
app_dirstringrequired
Absolute path to the dev's repo root.
app_idstringrequired
Keploy app UUID
base_urlstringrequired
Base URL the test will run against (e.g. http://localhost:8080).
frameworkstring
Framework you detected (express/gin/fastapi/etc.). Used to hint examples in the playbook.
resourcestringrequired
The resource you decided to generate a flow for (e.g. "users", "orders"). Becomes the per-resource subdirectory name.
devloop_install_repo_artifacts
Generate the agent-discovery files that let dev #2 onboard via their AI agent without a human pointing at docs. The doc-stated "loop closure" step.
Emits THREE files (the AI writes them with its Write tool):
* `.claude/skills/keploy/SKILL.md` — Claude Code skill. Has YAML frontmatter with a `description:` that triggers the skill semantically when the dev mentions Keploy / sandbox tests / mocks / api-tests / etc. Body describes the on-disk layout + how to drive Keploy via the MCP. Lazy-loaded — only burns context when the matcher fires.
* `.cursor/rules/keploy.md` — Cursor MDC equivalent for Cursor users (Cursor doesn't read .claude/skills/).
* `CLAUDE.md` — a tiny 3-line POINTER that says "Keploy is configured here; see .claude/skills/keploy/SKILL.md for details." Caters to editors that don't yet understand skills but DO read CLAUDE.md.
Idempotent. Re-running this tool against the same repo replaces the Keploy skill body in-place (overwrite-safe — we own the file entirely; the user is not expected to hand-edit it). The pointer in CLAUDE.md uses upsert semantics — only the Keploy line is touched; everything else in the file is preserved.
Returns the file paths + content + per-file action. The AI uses its Write tool per the `action` field.
Parameters (1)
app_dirstringrequired
Absolute path to the dev's repo root
devloop_mutation_demo
Prove the just-generated API test actually catches bugs by applying 3 real source-level mutations to the handler, running the test against each, and reverting. The doc-stated "manufactured proof in the first session" moment.
OPT-IN, NOT OPT-OUT — this tool TOUCHES THE DEV'S SOURCE FILES (temporarily). Always ASK the dev for explicit consent before walking the playbook: "I'll apply 3 small temporary changes to <handler file> to prove the test catches them, then revert every change. Proceed?" Only run the playbook on "yes".
What the playbook does:
1. Identify the handler file(s) the test exercises by reading <app_dir>/keploy/api-tests/<resource>/test.yaml and grepping for the route paths in the dev's code.
2. Pick 3 concrete mutations the test assertion set should catch — e.g. change a response field's type (Name string → Name int), rename a field (email → mail), remove a field. Choose mutations that map to fields the test ACTUALLY asserts on (read the suite's assertions to inform the pick).
3. For each mutation: apply via Edit, restart the dev's app if needed (hot-reload usually handles this), run keploy test-gen run, capture pass/fail, REVERT via Edit before moving to the next mutation.
4. Run a final "git diff -- <handler file>" to verify all reverts succeeded. If non-empty, HALT and ask the dev to run "git checkout <file>" before continuing.
5. Report: "I made 3 small changes, your test caught M/3. Caught: [concrete list]. Missed: [concrete list, with recommendation]."
ABSOLUTE RULES:
* Revert is non-negotiable. The dev's working tree must be clean at the end.
* Never modify test.yaml, config files, or anything outside the handler source(s) for this resource.
* Never run more than 3 mutations in one playbook (more is noise, less is unconvincing).
* If you can't identify a clear handler file, ASK the dev rather than guessing.
When the dev says "expand coverage to the other resources" → call devloop_expand_coverage next.
Parameters (4)
app_dirstringrequired
Absolute path to the dev's repo root
app_idstringrequired
Keploy app UUID
base_urlstring
Base URL the test runs against (e.g. http://localhost:8080). Required so the playbook's `keploy test-gen run` invocation is complete.
resourcestringrequired
The resource name you just generated for (e.g. "users"). Used to build the suite name (<resource>_CRUD) and locate the test YAML.
devloop_record_sandbox
Record mocks for V1 repo-mode API tests using the V1-native CLI command `keploy sandbox local record`. Runs the dev's app under the keploy eBPF agent, drives the V1 chained-CRUD tests from `keploy/api-tests/<resource>/test.yaml`, captures every outbound call (DB queries, Redis ops, downstream HTTP) as mocks, and lays them out at `<app_dir>/keploy/<suite-name>/{tests/, mocks.yaml, config.yaml}` in the standard OSS test-set tree. On success, mocks upload to the Keploy canonical pool by content hash; the hash lands in config.yaml so a teammate's later replay fetches the same bytes.
CRITICAL — DO NOT CONFUSE WITH `keploy record sandbox`:
* `keploy sandbox local record` (V1, repo-mode) ← this is what the playbook below uses
* `keploy record sandbox` (legacy, cloud-mode) ← DO NOT call this for V1
The two are entirely different commands. Cloud-mode requires server-side suites (queried via --suite-ids) — V1 repo-mode reads tests from the local filesystem and never registers them in the cloud. If the dev is in repo storage mode (verify via devloop_resolve_storage's source=persisted, mode=repo), V1 is the ONLY correct sandbox path.
STRICT — TIME-FREEZING DOES NOT APPLY TO RECORD.
Recording MUST use the dev's regular (prod) Dockerfile or native binary. NEVER spawn the app via Dockerfile.keploy / "-f docker-compose.keploy.yml" / "-tags=faketime" build during record. The faketime binary writes wrong timestamps into captured mocks (it reads time from the offset file, not the wall clock) and the entire capture becomes corrupt — recovery requires re-recording from scratch with the prod binary. If a previous replay failed with expired-JWT and the dev wants to "fix" it, the fix is to re-RUN the replay with --freezeTime, NOT to re-record. The recorded mocks captured against the prod binary are exactly what replay's clock-rewind is designed to validate; touching the record path defeats the whole mechanism.
ONLY call this with an explicit dev opt-in. The valid triggers:
* Dev directly asks ("capture mocks", "sandbox record", "rerecord the users mocks").
* Post-resource menu (Step 5 of devloop_generate_resource_flow) — dev picks "Capture mocks so CI runs in seconds".
* get_session_report shows mock_mismatch_dominant=true AND the dev says yes to your "rerecord?" prompt.
Pre-conditions:
* Dev's app must NOT already be running (keploy spawns its own copy of the app under the agent's eBPF hooks via the -c command). If a server is up at the target port, KILL IT first or the agent's network capture won't see the traffic.
* Real downstream deps (MySQL, Redis, Kafka, etc.) MUST be running — the capture proxies through to them on first contact so the recorded mocks contain real responses.
* The test YAML must exist at <app_dir>/keploy/api-tests/<resource>/test.yaml.
Returns a playbook for `keploy sandbox local record` with the V1 flag surface: --test-dir, --app-url, -c (spawn command), --container-name (docker-compose only), --skip-mock-upload (offline), --skip-report-upload (offline). Mocks land per-suite at keploy/<suite-name>/. NDJSON progress at --progress-file for the standard tail-til-done loop.
Parameters (8)
app_commandstringrequired
Shell command keploy should spawn to bring the dev's app up under the agent. Examples: `./bin/server` (native), `docker compose -f docker-compose.yml up producer` (docker-compose). For env vars, wrap in a /tmp/*.sh script that exec's the binary — inline `env VAR=… ./binary` does not parse reliably in -c.
app_dirstringrequired
Absolute path to the service directory (the one whose keploy/api-tests/ holds the V1 tests)
app_idstringrequired
Keploy app UUID (required for cloud uploads; pass empty string + skip-uploads for fully-offline)
app_urlstringrequired
Localhost URL the dev's app listens on, e.g. http://localhost:8080. The V1 test runner uses this as base-url AND keploy waits for this port to become reachable after spawning the app.
container_namestring
Only needed when app_command uses docker / docker-compose. The container instance name keploy should hook into (e.g. orderflow-producer). Read this from the docker-compose.yml's `container_name:` field for the service.
skip_mock_uploadboolean
Don't upload captured mocks to Keploy cloud (local-only). Default false. Set true for fully-offline runs.
skip_report_uploadboolean
Don't upload TestSuiteRun + TestSuiteReport to Keploy cloud. Default false. Set true for fully-offline runs.
suite_namestring
Optional. When set, records only the V1 suite with this name (matches the `name:` field at the top of each test.yaml document). Empty = record every V1 suite under <app_dir>/keploy/api-tests/. Pass this for targeted rerecord; omit for the first full capture.
devloop_resolve_storage
Resolve the storage mode for V1 ("user maintains the flow") API tests on this app.
═══════════════════════════════════════════════════════════════════
**MUST BE YOUR FIRST MCP CALL** for ANY of these dev verbs/intents:
═══════════════════════════════════════════════════════════════════
* "run the sandbox tests" / "run the API tests" / "test sandbox" / "run keploy tests"
* "record the sandbox" / "rerecord" / "refresh the mocks" / "capture mocks"
* "replay the sandbox" / "replay the tests" / "show me the report" / "what failed in the last run"
* "generate keploy tests" / "add a keploy test for <endpoint>"
* "set up keploy in this repo" / "onboard this service to keploy"
* any other reference to keploy/api-tests/, sandbox tests, integration tests, mocks, suites
REASON: this is the gate that determines whether the app is on the V1 (repo-mode) code path
or the legacy cloud-mode code path. **The two paths use entirely different MCP tool surfaces**:
┌───────────────────────┬─────────────────────────────────────────────────────────┐
│ Storage mode │ Tools to use │
├───────────────────────┼─────────────────────────────────────────────────────────┤
│ "repo" │ devloop_* tools only. NO cloud-mode tools. │
│ │ (record_sandbox_test, replay_sandbox_test, │
│ │ replay_test_suite, create_test_suite, list_branches, │
│ │ get_app_testing_context, listTestSuites etc. will │
│ │ REFUSE with a redirect to the V1 surface.) │
├───────────────────────┼─────────────────────────────────────────────────────────┤
│ "cloud" or "" (unset) │ Cloud-mode tools (record_sandbox_test, │
│ │ replay_sandbox_test, replay_test_suite, │
│ │ create_test_suite, list_branches, get_app_testing_ │
│ │ context, listTestSuites, etc.). devloop_* tools may │
│ │ also be called for the V1 cloud-mode path. │
└───────────────────────┴─────────────────────────────────────────────────────────┘
DO NOT SKIP THIS. If you reach for cloud-mode tools first (replay_sandbox_test, list_branches,
listTestSuites, etc.) without calling devloop_resolve_storage, you WILL misroute repo-mode apps
and tell the dev to "upload local tests as suites and record into the cloud" — the EXACT
regression that prompted these MCP-side guardrails. The cloud-mode tools server-side gate on
devloop_storage_mode == "repo" and will refuse the call with a redirect message; devloop_resolve_
storage front-runs that refusal cleanly.
Resolution order:
1. If app.devloop_storage_mode is set → return {mode, source: "persisted"}; do NOT re-ask.
2. Else if the dev's repo (app_dir) already contains keploy/api-tests/ → ATEMPT to infer repo mode. This tool returns source="asked" with a hint asking you to check the dev's filesystem; if you confirm keploy/api-tests/ exists, call devloop_set_storage_mode({app_id, mode:"repo", reason:"inferred_local_tests_exist"}) and proceed silently.
3. Else → return {source: "asked"} with the trade-off text in `message`; surface that to the dev, get yes/no, persist via devloop_set_storage_mode.
The AI is responsible for inspecting the repo (this MCP server does not have filesystem access). Use your native filesystem tools (read/grep) to check whether keploy/api-tests/ exists under app_dir.
APP RESOLUTION — the dev should NEVER have to type an app_id. Pass EITHER:
* app_id (UUID) — exact, fast path. Use this once you've resolved it earlier in the conversation.
* app_name_hint — a case-insensitive substring of the app name (typically the cwd basename). The tool calls listApps(q=hint) and resolves to a unique match.
If neither is set, the tool errors with the candidate list so you can ask the dev. If app_name_hint matches multiple apps, the error names them and asks you to disambiguate. If
Parameters (3)
app_dirstring
Absolute path to the dev's repo root (used in the hint to check for an existing keploy/api-tests/ directory)
app_idstring
Keploy app UUID. Optional if app_name_hint is set.
app_name_hintstring
Case-insensitive substring of the app name (typically the cwd basename). Optional if app_id is set; one of the two is required.
devloop_scaffold_ci
Scaffold the GitHub Actions workflow that runs the V1 API tests on every PR. Returns the exact YAML content to write to .github/workflows/keploy.yml + the Bash command to set the KEPLOY_API_KEY secret. The AI walks the playbook with its Write tool + the `gh` CLI.
PRECONDITIONS — CHECK BEFORE CALLING. Calling this tool out of order is a DEVLOOP violation; the doc-stated user-flow ordering is generate → run → mutation-prove (opt-in) → expand (opt-in) → CI (opt-in). Specifically you must have:
1. Generated at least one test via devloop_generate_resource_flow AND watched it pass via "keploy test-gen run --ci".
2. SURFACED the mutation-prove opt-in to the dev verbatim: "Want me to prove the test catches bugs by applying 3 small mutations to your handler and reverting?" — and the dev answered (yes-walked through devloop_mutation_demo, or explicit no/skip/later). Doing the test runs is NOT the same as offering mutation-prove; the offer is a separate dev-facing question.
3. ASKED the dev "want me to wire this into CI?" — explicit yes from the dev.
If ANY of those three are missing, STOP and back up. The mutation-prove gate is what builds the dev's trust before they commit Keploy to CI; skipping it ships shallow tests into a workflow the dev hasn't validated.
What this tool does NOT do (intentionally — the dev keeps custody):
* Mint the CI API key server-side. The dev provisions it themselves in the Keploy dashboard (Step 2 of the returned playbook walks them through it). The AI never sees the kep_* value — it transits dashboard clipboard → terminal stdin → gh CLI's encrypted POST. This is a security property, not a limitation.
* Post structured PR comments from api-server. V1 relies on GitHub Actions' native status-check rendering; the structured comment renderer is a V1.5 lift.
The emitted workflow runs on pull_request (default base branch) and reads app_id / test-dir / context-dir from keploy/api-tests/keploy-test-gen.yaml — the dev never has to thread flags through the workflow.
TIME-FREEZING — DEFAULT ON, ALMOST ALWAYS NEEDED FOR BACKEND APPS.
Almost every backend app has authentication (login → JWT/session/OAuth). The dev's recorded tests carry those tokens in headers. Between record time and the first PR's CI run, the tokens' exp claims pass real wall-clock — CI then 401s on every authenticated step, and the dev blames Keploy. Keploy's time-freezing rewinds the app's clock to the record moment so the recorded tokens validate.
Default policy: time_freezing=true. The AI MUST inspect the dev's test suites BEFORE calling this tool:
- <app_dir>/keploy/api-tests/<resource>/test.yaml (V1 sources)
- <app_dir>/keploy/<SuiteName>/tests/*.yaml (captured sandbox tests)
Look for: Authorization Bearer headers; steps hitting /login /auth /signin /token /oauth; response bodies containing jwt / token / access_token / refresh_token / expires_in / iat / exp. If any of those signals appear (or you're unsure), keep time_freezing=true. Only pass time_freezing=false when you've audited every suite and confirmed zero time-sensitive tokens (rare for a real backend).
When time_freezing=true, this tool also requires app_language (go / node / python / java / ruby / other) and app_service (docker-compose service name). Output then includes:
- Modified workflow YAML (pre-populates keploy-sockets-vol; uses -f docker-compose.yml -f docker-compose.keploy.yml; passes --freezeTime)
- docker-compose.keploy.yml override (volume mount + LD_PRELOAD for non-Go, or Dockerfile.keploy build for Go)
- Dockerfile.keploy (Go ONLY — vDSO bypasses LD_PRELOAD, requires -tags=faketime rebuild)
The dev's plain "docker compose up" is unaffected. Time-freezing only activates when CI (or the dev locally) explicitly passes both compose files.
TIME-FREEZING IS REPLAY-ONLY — STRICT INVARIANT.
The Dockerfile.keploy / docker-compose.keploy.yml / --freezeTime flag this tool emits exist purely to make recorded JWTs validate at REPLAY time. They MUST NEVER apply when recording. Concretely:
- Record uses the dev's PROD Dockerfile + plai
Parameters (12)
app_binarystring
Path to the dev's native app binary (or script that spawns it), relative to app_dir. REQUIRED when setup_mode=native — used in the workflow's `keploy sandbox local replay -c "<app_binary>"` boot command. Examples: "./bin/server", "./scripts/run-server.sh". For Go + time_freezing, the workflow builds a SEPARATE faketime binary at <app_binary>.replay (e.g. ./bin/server.replay) — the prod path is never overwritten. NOT used in setup_mode=docker.
app_dirstringrequired
Absolute path to the dev's repo root
app_idstringrequired
Keploy app UUID
app_languagestring
Primary language of the dev's app (go / node / python / java / ruby / other). REQUIRED when time_freezing=true. Determines the time-freezing setup. For Go: needs build-time helper (run `/lib/keploy/go_freeze_time_<arch>` to patch the toolchain) + `-tags=faketime` rebuild — separate binary path in native mode (NEVER overwrite prod), Dockerfile.keploy in docker mode. For non-Go: LD_PRELOAD shim is enough — the Keploy CLI auto-injects it in native mode (zero extra setup beyond `--freezeTime`), or the compose override sets the ENV in docker mode. Detect via repo signals: go.mod → go; package.json → node; requirements.txt|pyproject.toml → python; pom.xml|build.gradle → java; Gemfile → ruby; anything else → other.
app_servicestring
Docker-compose service name for the dev's app (e.g. 'app', 'producer', 'taskmgr-app'). REQUIRED when setup_mode=docker so the compose override can target the right service for volume mount + ENV. Read from <app_dir>/docker-compose.yml (or the file the dev uses). NOT used in setup_mode=native.
app_startup_delay_secondsnumber
Seconds Keploy waits after spawning the app (via `-c`) before sending the first test request. The flag becomes `--delay <N>` on the CLI. Default 10 — covers most Go / Node / Python / typical web apps. Bump for slow-boot frameworks (Spring Boot: 30–60s, Java with large heap: 30s+, anything that runs DB migrations on boot: ~10–20s + migration time). Pass 0 to omit the flag entirely (uses CLI default of 5s). Without enough delay, the first test step gets `connection refused: dial tcp 127.0.0.1:<port>` because the app hasn't bound the port yet.
base_branchstring
Branch that PRs target (default: main).
ci_modestring
Which test surface CI replays. Values: "api-tests" (run V1 source under keploy/api-tests/ against a live app + live deps via `keploy test-gen run`) or "sandbox-replay" (replay the captured sandbox suites under keploy/<SuiteName>/ via `keploy sandbox local replay`; DB/Redis/HTTP downstream calls are served from recorded mocks, app still boots). REQUIRED. The AI MUST inspect <app_dir>/keploy/ first: list subdirs, identify any non-reserved subdir containing a config.yaml (e.g. auth_CRUD, tasks_CRUD, users_CRUD) as a captured sandbox suite. Reserved siblings to ignore: api-tests, reports, schema, keploy.yml, test-set-* (legacy OSS). Routing: only api-tests/ → ci_mode=api-tests; only captured suites → ci_mode=sandbox-replay; BOTH present → YOU MUST ASK THE DEV which (non-negotiable) — surface a clear choice with sandbox-replay marked (Recommended). The asking is non-negotiable when both are present, even if the dev's request already hinted at one mode; confirmation prevents silent misroutes.
compose_filestring
Compose file path relative to the repo root. Default: docker-compose.yml. Only honored when setup_mode=docker (the override extends this base file).
go_versionstring
Go version to install at /usr/local/go in CI. ONLY honored when setup_mode=native + app_language=go + time_freezing=true. Default 1.22.10. The faketime helper hardcodes /usr/local/go as GOROOT, so the workflow MUST install Go there — not into the toolcache via actions/setup-go. Read the dev's go.mod to pick a matching minor (the Go release archive at https://go.dev/dl/go<version>.linux-amd64.tar.gz is what gets downloaded).
setup_modestring
How the dev's app actually runs in CI. Values: "native" (Go/Node/Python binary or `./bin/server` directly, no Docker for the app — deps may still be in Docker) or "docker" (app boots via `docker compose up <svc>`, with a Dockerfile + compose service). REQUIRED. Pick based on the dev's prior `devloop_record_sandbox` app_command: if it was a binary path or shell script (e.g. `./bin/server`, `./scripts/run-server.sh`, `node index.js`), setup_mode=native. If it was `docker compose up <svc>` or similar, setup_mode=docker. If BOTH are possible (Dockerfile + native binary both exist), YOU MUST ASK THE DEV — don't guess. Native + Docker emit fundamentally different CI workflows (native skips Dockerfile.keploy + compose override entirely; Docker uses them). Cross-wiring (Docker scaffold for a native dev or vice versa) produces a workflow that doesn't match how the dev actually runs the app, and the AI has to improvise — that's the bug this arg fixes.
time_freezingboolean
Whether the workflow needs Keploy time-freezing (clock-rewinding so recorded JWTs and other time-sensitive tokens validate at replay). Defaults depend on ci_mode: sandbox-replay = true (force-true; non-negotiable, any explicit false is silently overridden), api-tests = false (live deps + current wall-clock means recorded tokens never enter the picture, no benefit). For api-tests, the AI can pass true to pre-stage Dockerfile.keploy + docker-compose.keploy.yml in the repo for a later sandbox-replay switch. For sandbox-replay there is no opt-out — the cost of force-ON for a zero-token app is a dormant volume mount, the cost of force-OFF for a token-bearing app is every PR silently failing 401.
devloop_schema_drift_report
Report the gap between the app's OpenAPI spec and its V1 API-tests coverage. Reuses the existing schema-coverage endpoint that get_coverage_gaps consumes, but frames the result for V1: which endpoints are uncovered, the recommended order to generate tests for them next (highest-fan-in first), and the count gap.
V1 covers the "spec-vs-tests" half of drift. The deeper "spec-vs-actual-handler-behavior" half (run the live app, diff responses against the OpenAPI spec) is a V2 lift — defer until V1 telemetry shows it would unblock real dev journeys.
Parameters (1)
app_idstringrequired
Keploy app UUID
devloop_self_heal
Diagnose a failing V1 API test and propose an action. Triggered when:
* keploy test-gen run exited non-zero in a fresh agent session (the dev's code changed since the test was authored).
* A PR Keploy comment surfaced a failure and the dev says "fix it".
Action classes (mutually exclusive — the CLI picks one):
* auto_update — diff is an unambiguous rename or refactor (same value moves to a different field name, or a step name changed without semantic effect). The AI can apply the proposed patch directly.
* ask_user — the diff is ambiguous (a field disappeared, a value changed in a way that could be intentional). The AI surfaces the proposed alternatives and waits for the dev.
* revert_suggestion — the diff looks like a real regression (status code drifted, expected response shape is incompatible). The AI suggests the dev revert the offending code; does NOT silently update the test.
HARD RULE: never rewrite an assertion to make a red test green. The CLI enforces this by refusing to emit an auto_update for any change that weakens an assertion. The AI must also enforce this — if the CLI's proposal includes "change expected_string from X to Y" where X was the correct contract, REJECT the proposal and ask the dev.
Parameters (3)
app_dirstringrequired
Absolute path to the dev's repo root
report_jsonstring
Path to a failing test-run JSON report (output of `keploy test-gen run --output json`). The CLI reads failed-step details from this file.
test_dirstring
Test directory to analyse (defaults to <app_dir>/keploy/api-tests)
devloop_set_storage_mode
Persist the V1 storage mode on an app. Call this AFTER devloop_resolve_storage returned source="asked" (the dev gave you a yes/no) or source="inferred" (you confirmed keploy/api-tests/ exists on disk).
Switching repo→cloud when local tests exist warns the dev that the existing local tests will be orphaned (Keploy will start sourcing tests from MongoDB; the keploy/api-tests/ files are no longer the source of truth). Surface the warning before calling this tool with mode="cloud" against a repo that has local tests.
Parameters (3)
app_idstringrequired
Keploy app UUID
modestringrequired
Storage mode: "repo" (tests in user's git repo) or "cloud" (tests in Keploy MongoDB). Pass "" to clear.
reasonstring
Free-form note recorded with the change (e.g. "asked", "inferred_local_tests_exist", "explicit_switch_with_obsolescence_ack"). Helpful for telemetry; not validated.
devloop_setup_instructions
Return the V1 "user maintains the flow" install guide — covers Path A (clipboard one-liner), Path B (agent-walked OAuth), and the keploy/api-tests/ on-disk layout V1 creates. Safe to call without authentication; call this when the dev asks 'how do I add Keploy to this repo?' or when a devloop_* tool returns missing-API-key.
No parameters.
devloop_switch_to_sandbox
Switch the app's V1 CI from "boot the real app + deps" mode to sandbox mode (mocks fetched by content-hash from the cloud canonical pool). The doc-stated trigger: ~1 week after CI is wired, when the dev has felt the slow runs / flakes and you can pitch "your CI takes 90s and flaked twice this week — rerecord mocks and CI drops to ~8s."
What flips:
* The CI workflow YAML gets a --sandbox flag on `keploy test-gen run` and the docker-compose-up step removed. This tool returns the updated YAML; you re-PR it.
Pre-condition: every resource you want in CI must have recorded mocks (config.yaml.mockRegistry.mock populated). Resources without mocks will fail in sandbox mode because there's nothing to serve. Run devloop_record_sandbox per resource first; verify via devloop_schema_drift_report-style checks before proposing the switch.
Parameters (3)
app_dirstringrequired
Absolute path to the dev's repo root
app_idstringrequired
Keploy app UUID
base_branchstring
PR base branch (default: main)
download_recording
Download a recording — a session of captured API traffic (request/response pairs + outbound mocks) stored as a test_set. Recordings are INPUT artifacts captured by `keploy record`: they're raw traffic that AI generation (generate_and_wait) and manual create_test_suite flows turn into test suites. Use this to inspect what was captured before deciding how to turn it into suites.
NOT a sandbox-test export. A "sandbox test" (the suite + its captured mocks, produced by record_sandbox_test) lives behind a suite's `test_set_id` link — to inspect a suite, use getTestSuite for the step shape or replay_sandbox_test to see behavior. For the suite's mock bundle, do this two-step: (1) call getTestSuite to read the suite's test_set_id; (2) call listMocks({app_id, test_set_id}) (use ?include_specs=true to also fetch parsed mock YAML). Or for raw mock files on disk, point the dev at the artifact directory printed by record_sandbox_test (data.artifact_dir on the phase=done event).
Parameters (3)
app_idstringrequired
Keploy app ID
include_mocksboolean
Include dependency mocks in the download (default true)
test_set_idstringrequired
ID of the recording session to download
editMockMapping
POST /apps/{appId}/recordings/{testSetId}/test-cases/{testCaseId}/mock-mapping — Link or unlink a mock from a test case — Targeted mutation of the test case's entry in the mapping doc.
Add appends a mock entry if not already present; remove drops
the entry by name. Both idempotent — safe to retry on a network
blip.
The MCP layer exposes this as TWO tools (`link_mock` /
`unlink_mock`) — they both call this endpoint with the
appropriate `action`. Splitting at the MCP layer keeps each
tool's description tighter and avoids the LLM having to
remember the enum spelling.
Requires scope: `write`.
Parameters (7)
actionstringrequired
appIdstringrequired
Path parameter: appId
branch_idstring
mock_kindstring
Optional. Stamped on the mapping entry; useful when callers want the kind preserved on the mapping doc for downstream readers.
mock_namestringrequired
testCaseIdstringrequired
Test case name (the mapping doc keys cases by name, not _id).
testSetIdstringrequired
Path parameter: testSetId
exportRecording
GET /apps/{appId}/recordings/{testSetId}/export — Export a recording bundle — Export a complete recording bundle: test set metadata, all test cases, mocks, and test-to-mock mappings as a single JSON response. Use ?include_mocks=false to exclude mocks. Requires scope: `read`.
Parameters (3)
appIdstringrequired
Path parameter: appId
include_mocksboolean
Include dependency mocks in the export (default true).
testSetIdstringrequired
Path parameter: testSetId
generateTestSuites
POST /apps/{appId}/test-suites/generate — Generate test suites via AI — Requires scope: `write`.
Parameters (13)
appIdstringrequired
Path parameter: appId
authobject
Authentication configuration for test execution. The runner injects the matching headers on every step request.
base_urlstringrequired
code_snippetstring
Relevant source code for context
docsstring
API documentation text
examplesstring
Example curls or request/response pairs
ignore_endpointsarray
max_test_suitesnumber
rate_limitnumber
schemastring
OpenAPI spec (YAML or JSON)
timeoutnumber
user_promptstring
Additional instructions for AI generation
webhook_urlstring
generate_and_wait
Generate test suites from an OpenAPI spec and wait for completion.
Parameters (5)
app_idstringrequired
Keploy app ID
base_urlstringrequired
Target API base URL
max_test_suitesnumber
Max suites to generate (default 30)
schemastring
OpenAPI spec (YAML or JSON)
user_promptstring
Instructions for the AI generator
getApp
GET /apps/{appId} — Get an app — Requires scope: `read`.
Optional `fields` query parameter projects the response to a subset
of properties — useful for MCP / AI callers that only need a few
identity fields (e.g. `["name","namespace","deployment","origin.clusterName"]`)
and don't want the full ~16k-token embedded schema in their context.
Supports dotted paths for nested objects. Omitting `fields` returns
the full envelope as before.
Parameters (2)
appIdstringrequired
Path parameter: appId
fieldsstring
Optional comma-separated list of response field paths to keep.
Each path is dotted (e.g. `origin.clusterName`). When set, the
response is projected to just those paths inside `data`; the
envelope shape (`{data, meta?}`) is preserved.
getCurrentUser
GET /users/me — Get current user — Requires scope: `read`. Returns the user associated with the API key.
No parameters.
getGeneratedSchema
GET /apps/{appId}/generated-schema — Get auto-generated OpenAPI schema — Returns the OpenAPI schema auto-generated from recorded traffic. Requires scope: `read`.
Parameters (1)
appIdstringrequired
Path parameter: appId
getGenerationHistoryDetails
GET /apps/{appId}/generation-history/{jobId} — Get generation history details — Requires scope: `read`.
Parameters (2)
appIdstringrequired
Path parameter: appId
jobIdstringrequired
Path parameter: jobId
getJob
GET /jobs/{jobId} — Get a job — Requires scope: `read`.
Parameters (1)
jobIdstringrequired
Path parameter: jobId
getLoadTestReport
GET /apps/{appId}/load-tests/{runId} — Get a load test report — Requires scope: `read`.
Parameters (2)
appIdstringrequired
Path parameter: appId
runIdstringrequired
Path parameter: runId
getMock
GET /apps/{appId}/recordings/{testSetId}/mocks/{mockId} — Read one mock's canonical YAML — Returns the canonical mock YAML doc (version/kind/name/spec)
for the named mock in the given test set. Branch-aware: when
`branch_id` is supplied, a branch-only upsert or tombstone
takes precedence over main.
Authoring workflow for AI agents: call this BEFORE updateMock
to fetch the existing payload, edit fields locally, then
round-trip the result through `mock_yaml` on updateMock.
Requires scope: `read`.
Parameters (4)
appIdstringrequired
Path parameter: appId
branch_idstring
Optional branch overlay id. Absent → reads from main.
mockIdstringrequired
Mock id (UUID assigned at insert) OR human-readable mock name. The server resolves names within the test set, so the UI can pass either form.
testSetIdstringrequired
Path parameter: testSetId
getMockMapping
GET /apps/{appId}/recordings/{testSetId}/test-cases/{testCaseId}/mock-mapping — Read the mocks currently linked to a test case — Returns the mock entries in the mapping doc for the named
test case. Branch-aware via `branch_id` — when set, the
branch overlay's mapping wins over main's.
Workflow: AI agents should call this BEFORE editMockMapping
to inspect what's linked, then issue targeted add / remove
ops with confidence. Empty result (mocks: []) is normal —
means no mocks linked yet.
Requires scope: `read`.
Parameters (4)
appIdstringrequired
Path parameter: appId
branch_idstring
Optional branch overlay id. Absent → reads from main.
testCaseIdstringrequired
Test case name (the mapping doc keys cases by name, not _id).
testSetIdstringrequired
Path parameter: testSetId
getRecording
GET /apps/{appId}/recordings/{testSetId} — Get recorded test cases — Returns individual recorded test cases within a test set, including HTTP request/response data. Requires scope: `read`.
Parameters (4)
appIdstringrequired
Path parameter: appId
limitnumber
Page size. Defaults to 20 when omitted; `limit=0` is also treated
as "use default" (so existing clients sending an explicit zero
keep the prior behaviour). Capped at 100 — the spec rejects
higher values with 400 so callers fail loudly instead of being
silently clamped, and the handler enforces the same cap as a
defence-in-depth fallback.
offsetnumber
Zero-based pagination offset. Negative values are rejected — the
handler also clamps to 0 as a defence-in-depth fallback.
testSetIdstringrequired
Path parameter: testSetId
getSchemaCoverage
GET /apps/{appId}/schema-coverage — Get schema coverage — Requires scope: `read`.
Parameters (1)
appIdstringrequired
Path parameter: appId
getSubscription
GET /company/subscription — Get company subscription — Requires scope: `read`.
No parameters.
getSuiteReport
GET /apps/{appId}/test-runs/{runId}/suite-reports/{reportId} — Get a suite report — Requires scope: `read`.
Parameters (3)
appIdstringrequired
Path parameter: appId
reportIdstringrequired
Path parameter: reportId
runIdstringrequired
Path parameter: runId
getTestCase
GET /apps/{appId}/recordings/{testSetId}/test-cases/{testCaseId} — Get a single test case — Returns a single recorded test case identified by its friendly **name** (e.g. `test-4` — the name in the recording yaml) within a recording session. Within `(testSetId, branchId)` the name is unambiguous; this is the same identifier callers see in the on-disk recording bundle. Pass `branch_id` to scope the read to a branch overlay (bundle-uploaded test cases on a branch are invisible to main reads). Requires scope: `read`.
Parameters (4)
appIdstringrequired
Path parameter: appId
branch_idstring
Optional Keploy branch UUID. When set, scopes the read/write to that branch's overlay
(copy-on-write — see /apps/{appId}/branches). When absent or empty, operations target
the main branch (the historical default). Required for writes against a branch (the
api-server's branch gate rejects 400 otherwise); reads are tolerant of absence.
testCaseIdstringrequired
Path parameter: testCaseId
testSetIdstringrequired
Path parameter: testSetId
getTestReport
GET /apps/{appId}/test-reports/{reportId} — Get a test run report — Returns the rollup view of a single test run by ID — counts, CI
metadata, normalize/coverage data. Cheap and lightweight. For
per-test-case diffs and mock mismatches use `getTestReportFull`.
Requires scope: `read`.
Parameters (2)
appIdstringrequired
Path parameter: appId
reportIdstringrequired
Path parameter: reportId
getTestReportFull
GET /apps/{appId}/test-reports/{reportId}/full — Get a fully-inflated test run report — Returns everything about a single legacy /tr test run in one call:
the rollup (counts, CI metadata, normalize/coverage data), every
test-set report, and — when `include_oss_report=true` (default) —
every per-test-case result with request/response diff and
`mock_mismatches`. Designed for AI / MCP analysis so the model can
ground answers in actual diffs without chaining listTestSetReports
+ listTestCaseReports calls.
Use `mock_mismatches_only=true` to restrict per-test-case rows to
those with non-empty `mock_mismatches` OR the legacy
`failure_info.mock_mismatch` fallback (for reports produced by
replayers older than v3.5.49 that pre-date the top-level
`mock_mismatches` field). Use `failed_only=true` to restrict
per-test-case rows to those whose `status` is `FAILED` — covers
all failure modes (response divergence, schema delta, mock
mismatch). `failed_only` and `mock_mismatches_only` AND together:
passing both keeps only tests that are both FAILED and have mock
mismatches. Use `max_test_cases_per_set` to cap response size on
large suites; `truncated.test_cases_dropped` reports how many
were elided.
Requires scope: `read`.
Parameters (7)
appIdstringrequired
Path parameter: appId
failed_onlyboolean
When `true`, drops every test case whose `status` is not
`FAILED` (passed/skipped/pending/running are elided). Use
this to slim the report down to just the failures the model
needs to analyze — typically 1–5 cases out of 50. Combine
with `fields` for the smallest possible response. Combines
with `mock_mismatches_only` via AND.
fieldsstring
Optional comma-separated list of response field paths to keep
inside `data`. Supports dotted nested paths and `[].` for
array wildcards (e.g. `failed_steps[].diff`,
`mock_mismatches`). Typical AI use:
`?fields=failed_steps[].diff,mock_mismatches` — projects the
~34k-token full report down to ~5k while preserving the diff
content the model needs to ground its analysis. Omitting
`fields` returns the full report as before.
include_oss_reportboolean
Query parameter: include_oss_report
max_test_cases_per_setnumber
Query parameter: max_test_cases_per_set
mock_mismatches_onlyboolean
Query parameter: mock_mismatches_only
reportIdstringrequired
Path parameter: reportId
getTestRun
GET /apps/{appId}/test-runs/{runId} — Get a test run — Requires scope: `read`.
Parameters (2)
appIdstringrequired
Path parameter: appId
runIdstringrequired
Path parameter: runId
getTestSuite
GET /apps/{appId}/test-suites/{suiteId} — Get a test suite — Requires scope: `read`.
Parameters (3)
appIdstringrequired
Path parameter: appId
branch_idstring
Optional Keploy branch UUID. When set, scopes the read/write to that branch's overlay
(copy-on-write — see /apps/{appId}/branches). When absent or empty, operations target
the main branch (the historical default). Required for writes against a branch (the
api-server's branch gate rejects 400 otherwise); reads are tolerant of absence.
suiteIdstringrequired
Path parameter: suiteId
getUsage
GET /company/usage — Get company usage — Requires scope: `read`.
No parameters.
getValidationResult
GET /jobs/{jobId}/validation-result — Get job validation result — Requires scope: `read`.
Parameters (1)
jobIdstringrequired
Path parameter: jobId
get_app_testing_context
Fetch comprehensive context about an app's API plus the canonical test-suite authoring schema. Returns:
* app — config, auth shape, appLevelCustomVariables (READ THIS for R32 — your extract keys must not collide with these names)
* coverage — API coverage report (which endpoints have tests, which don't)
* recordings — summaries of captured traffic sessions
* test_suites — existing suites (check before authoring to avoid duplicates)
* generated_schema — AI-extracted OpenAPI for the app
* step_schema — THE CANONICAL TEST SUITE STEP SCHEMA. Same content as `keploy test-suite-format`, shipped inline so you don't need a separate tool-call hop. Read this BEFORE authoring or curling endpoints — it contains the MANDATORY rule block (R10 / R9 / R2 / R15 / R32) the validator enforces on iter 1, plus the canonical two-step prelude+POST skeleton.
* recommended_substitutions — actionable mapping of existing app-level vars to the field types they cover. Structure: {dynamic_generators: [{key, reference, type, suggested_for}], static_fixtures: [...], usage_guidance: "..."}. PREFER referencing one of these over declaring a prelude step. Dynamic generators satisfy R9/G24 for POST/PUT/PATCH bodies; Static fixtures do NOT (use them only in headers/query/asserts). When this list is empty for the field you need, call update_app_custom_variables to add a gen* var instead of building a prelude step.
* authoring_templates — concrete, ready-to-fill suite skeletons indexed by endpoint shape. Structure: {overview: "...", templates: [{endpoint_shape, when_to_use, steps, notes}, ...]}. PRIMARY AUTHORING PATH: match your intent to the closest template's endpoint_shape, copy its steps, fill the <FILL: ...> blanks, replace {{genXxx}} placeholders with the matching keys from recommended_substitutions (or call update_app_custom_variables to add a missing one), capture real responses via curl (R10), then validate via the create_test_suite playbook. Templates encode the opinionated default for common patterns — author from scratch only for genuine edge cases.
* authoring_directive — one-line reminder pointing at step_schema and authoring_templates.
Call this FIRST when authoring suites. The step_schema field eliminates the most common iter-1 failure (AI authors based on training-data priors before reading the validator's rules).
Parameters (1)
app_idstringrequired
Keploy app ID
get_auth_status
Check whether the current MCP session has a valid-looking Keploy PAT attached. Returns { authenticated, auth_header_present, hint }. Useful as a first step for an agent to decide whether to call other tools or show setup instructions — differentiates 'no header at all' from 'header present but wrong prefix / unsupported format'.
No parameters.
get_coverage_gaps
Get API coverage and prioritized suggestions for uncovered endpoints.
Parameters (1)
app_idstringrequired
Keploy app ID
get_session_report
Fetch the report for a completed run. ONE tool, THREE report kinds — the response's top-level `kind` field discriminates which kind it is (rerecord / sandbox_run / test_suite_run) and which question the report answers (see core glossary's "three reports"). Read `kind` first, then pick the matching reading rules below; do NOT assume the kind from how you got here.
Call this as the final step of the playbook, AFTER you read the terminal NDJSON event (phase=done) and confirmed data.ok=true. Pass app_id and test_run_id — extract test_run_id from data.test_run_id on the phase=done line of the progress_file returned by record_sandbox_test or replay_sandbox_test (for replay_test_suite, the CLI prints test_run_id to stdout instead).
===== OUTPUT SHAPE =====
(Conditional verbosity so the dev isn't drowned in noise on a green run.)
* Always includes totals at the SUITE level only (total_suites / passed_suites / failed_suites) and a per_suite array where each entry carries suite_id, suite_name, total_steps, passed_steps, failed_steps. Aggregate step counts across suites are intentionally omitted — they hide where damage actually is.
* PER-KIND READING of passed_steps / failed_steps — same column names, different meaning per kind:
- RERECORD (kind=rerecord): passed_steps = steps whose auto-replay byte-comparison matched the live capture. failed_steps = steps that diverged on auto-replay. EVEN IF every suite shows passed_steps == total_steps, the rerecord is only successful when every suite is also linked=true (a sandbox test got produced). Always check `linked`; the step counts alone do not indicate "did the rerecord work".
- SANDBOX_RUN (kind=sandbox_run): passed_steps = steps whose assertions held under captured-mock replay. failed_steps = assertion failures or response diffs against the captured baseline.
- TEST_SUITE_RUN (kind=test_suite_run): passed_steps = steps whose assertions held against the live app. failed_steps = same against live, no mocks involved. No linkage to report.
* Top-level `kind` discriminates the report: `"rerecord"` for record_sandbox_test runs (rerecord report — answers "did the sandbox test get created and linked?"), `"sandbox_run"` for replay_sandbox_test runs (sandbox run report — answers "does the suite still hold up against its captured baseline?"), `"test_suite_run"` for replay_test_suite runs (test suite report — live execution, no mocks; answers "does the suite hold up against the actual current system?"). Use kind to pick the right reading; do NOT mix them in one response.
* RERECORD runs (kind="rerecord") carry a `linked` bool + `test_set_id` string on every per_suite[] entry. linked=true means the rerecord produced a sandbox test for the suite (replay-ready). linked=false means rerecord did NOT produce a sandbox test for the suite — it cannot be replayed until rerecord succeeds. ALWAYS surface this on rerecord output — even when every step's capture passed at the wire level, a suite without a sandbox test is a real failure. For the per-suite table, add a "Linked" column (yes/no from per_suite[].linked). For the one-line all-green reply, report "N/N suites passed, L/N have a sandbox test (test_run_id=<id>)".
* When any suite has failures (or verbose=true), also includes failed_steps[] with per-step diagnostics (suite, step name, method+url, diff excerpt, error, mock_mismatches, assertion_failures, mock_mismatch_failure, authored_assertions, authored_response_body) PLUS mock_mismatch_failed_steps (count) and mock_mismatch_dominant (bool — true when the majority of failed steps have unconsumed recorded mocks, which points at a keploy-side egress-hook issue rather than dev app breakage). On RERECORD, failed_steps[] also carries `linked` (whether the owning suite has a sandbox test after this rerecord) and the mock_mismatch_* fields are suppressed (irrelevant in rerecord context).
* authored_assertions / authored_response_body — the SUITE's authored contract for the failing step (the assert array and response.body as defined when the suite was created/updated). Surface
Parameters (3)
app_idstringrequired
Keploy app ID
test_run_idstringrequired
Test run ID. Extracted from data.test_run_id on the phase=done NDJSON event written to the progress_file by the headless sandbox CLI.
verboseboolean
Force detailed per-step diagnostics even when all steps passed. Default false. Set true for explain/analyse intents so the dev sees the full picture, not just a one-line summary.
get_setup_instructions
Return step-by-step instructions for connecting this MCP server to Cursor, Windsurf, VS Code, Claude Desktop, Claude Code, or Trae. Safe to call without authentication — use this when the user asks 'how do I set up Keploy MCP?' or when a tool call fails with an auth error.
No parameters.
importRecording
POST /apps/{appId}/recordings/{testSetId}/import — Import test case changes into a recording — Bulk import test case changes: update existing test cases (by ID), insert new ones (without ID), and delete specified test cases. Pass `branch_id` (query or body) to scope the import to a branch overlay. Requires scope: `write`.
Parameters (5)
appIdstringrequired
Path parameter: appId
branch_idstring
Optional branch overlay id (body alternative to the query param). Either form is accepted; query wins.
delete_test_case_idsarray
IDs of test cases to delete
testSetIdstringrequired
Path parameter: testSetId
test_casesarray
listAPIKeys
GET /api-keys — List API keys — Requires scope: `admin`.
No parameters.
listApps
GET /apps — List apps — Returns the tenant's apps. Use the optional `q` query parameter to name-filter (case-insensitive substring, e.g. `?q=orderflow` → apps whose name contains 'orderflow'); without it the full paginated list is returned. Callers that know the app's folder / repo name should pass it as `q` to avoid paginating through hundreds of apps. Requires scope: `read`.
Parameters (3)
limitnumber
Page size. Defaults to 20 when omitted; `limit=0` is also treated
as "use default" (so existing clients sending an explicit zero
keep the prior behaviour). Capped at 100 — the spec rejects
higher values with 400 so callers fail loudly instead of being
silently clamped, and the handler enforces the same cap as a
defence-in-depth fallback.
offsetnumber
Zero-based pagination offset. Negative values are rejected — the
handler also clamps to 0 as a defence-in-depth fallback.
qstring
Case-insensitive substring to filter app names by. Omit to list all apps.
listAppsByCluster
GET /apps/by-cluster/{clusterId} — List apps in a cluster — Returns apps belonging to a specific cluster. More efficient than iterating all apps. Requires scope: `read`.
Parameters (1)
clusterIdstringrequired
Path parameter: clusterId
listAppsWithRecordings
GET /apps/with-recordings — List proxy apps with network recordings — Returns all k8s-proxy apps (origin.type=PROXY). These apps are auto-created by the Keploy k8s-proxy agent on first recording and contain network recordings of ingress HTTP traffic (as Keploy test cases) and egress dependency calls — database queries, external API calls, message queues — captured as Keploy mocks. Use listRecordings and getRecording to access the recorded request/response pairs and dependency mocks from live environments. Requires scope: `read`.
No parameters.
listBranches
GET /apps/{appId}/branches — List Keploy branches on an app — Returns every Keploy branch on an app, optionally filtered by status. Used by MCP write tools and CI integrations to enumerate branches without shelling out to the CLI. Requires scope: `read`.
Parameters (2)
appIdstringrequired
Path parameter: appId
statusstring
Optional status filter. Omit to return all branches. (allowed values: open|review_requested|approved|changes_requested|merged|closed|conflict — CASE-SENSITIVE)
listClusters
GET /clusters — List clusters — Returns all clusters for the authenticated company. Requires scope: `read`.
No parameters.
listGenerationHistory
GET /apps/{appId}/generation-history — List generation history — Requires scope: `read`. Returns all entries (no pagination).
Parameters (1)
appIdstringrequired
Path parameter: appId
listJobs
GET /jobs — List jobs — Requires scope: `read`.
Parameters (2)
limitnumber
Page size. Defaults to 20 when omitted; `limit=0` is also treated
as "use default" (so existing clients sending an explicit zero
keep the prior behaviour). Capped at 100 — the spec rejects
higher values with 400 so callers fail loudly instead of being
silently clamped, and the handler enforces the same cap as a
defence-in-depth fallback.
offsetnumber
Zero-based pagination offset. Negative values are rejected — the
handler also clamps to 0 as a defence-in-depth fallback.
listLoadTestRuns
GET /apps/{appId}/load-tests — List load test runs — Requires scope: `read`. Returns all runs (no pagination).
Parameters (1)
appIdstringrequired
Path parameter: appId
listMocks
GET /apps/{appId}/recordings/{testSetId}/mocks — List mocks for a recording — Returns mock reference metadata and optionally parsed mock specs for a test set. Use ?include_specs=true to download and parse the actual mock YAML from object storage. Pass `branch_id` to scope the read to a branch overlay (bundle-uploaded recordings on a branch are invisible to main reads). Requires scope: `read`.
Parameters (4)
appIdstringrequired
Path parameter: appId
branch_idstring
Optional Keploy branch UUID. When set, scopes the read/write to that branch's overlay
(copy-on-write — see /apps/{appId}/branches). When absent or empty, operations target
the main branch (the historical default). Required for writes against a branch (the
api-server's branch gate rejects 400 otherwise); reads are tolerant of absence.
include_specsboolean
When true, download and parse the actual mock YAML specs from object storage.
testSetIdstringrequired
Path parameter: testSetId
listRecordings
GET /apps/{appId}/recordings — List recording sessions — Returns test sets (recording sessions) for an app. Requires scope: `read`.
Parameters (3)
appIdstringrequired
Path parameter: appId
limitnumber
Page size. Defaults to 20 when omitted; `limit=0` is also treated
as "use default" (so existing clients sending an explicit zero
keep the prior behaviour). Capped at 100 — the spec rejects
higher values with 400 so callers fail loudly instead of being
silently clamped, and the handler enforces the same cap as a
defence-in-depth fallback.
offsetnumber
Zero-based pagination offset. Negative values are rejected — the
handler also clamps to 0 as a defence-in-depth fallback.
listSmartTestCases
GET /apps/{appId}/smart-set/cases — List smart test cases — Returns the app's smart test set — the deduped, content-addressed set of
API contracts maintained across recording sessions. Pass `branch_id` to
get a branch's curated view (main cases ⊕ that branch's edits); absent →
the main view. `include_obsolete=true` includes cases a user marked
skip-in-replay. Requires scope: `read`.
Parameters (3)
appIdstringrequired
Path parameter: appId
branch_idstring
Optional Keploy branch UUID. When set, scopes the read/write to that branch's overlay
(copy-on-write — see /apps/{appId}/branches). When absent or empty, operations target
the main branch (the historical default). Required for writes against a branch (the
api-server's branch gate rejects 400 otherwise); reads are tolerant of absence.
include_obsoleteboolean
Include cases marked obsolete (skip-in-replay).
listSuiteReports
GET /apps/{appId}/test-runs/{runId}/suite-reports — List suite reports for a test run — Requires scope: `read`. Supports cursor-based pagination.
Parameters (5)
afterstring
Cursor for forward pagination
appIdstringrequired
Path parameter: appId
beforestring
Cursor for backward pagination
page_sizenumber
Number of items per page
runIdstringrequired
Path parameter: runId
listTestCaseReports
GET /apps/{appId}/test-reports/{reportId}/test-set-reports/{testSetReportId}/test-cases — List test case reports — Returns individual test case results with expected/actual diffs within a test set report. Requires scope: `read`.
Parameters (5)
appIdstringrequired
Path parameter: appId
limitnumber
Page size. Defaults to 20 when omitted; `limit=0` is also treated
as "use default" (so existing clients sending an explicit zero
keep the prior behaviour). Capped at 100 — the spec rejects
higher values with 400 so callers fail loudly instead of being
silently clamped, and the handler enforces the same cap as a
defence-in-depth fallback.
offsetnumber
Zero-based pagination offset. Negative values are rejected — the
handler also clamps to 0 as a defence-in-depth fallback.
reportIdstringrequired
Path parameter: reportId
testSetReportIdstringrequired
Path parameter: testSetReportId
listTestReports
GET /apps/{appId}/test-reports — List test run reports — Browse legacy /tr test runs for an app. Combine `status` +
`source` to scope to specific run kinds — e.g. `status=FAILED` +
`source=ci` shows broken CI runs only. Use `branch_id` to scope
to a Keploy branch overlay. Use `getTestReportFull` for the
inflated single-call view with per-test-case diffs + mock
mismatches.
CANONICAL ONE-SHOT CALL for "find the latest failed local cloud
replay" (Phase A1 of the skill):
listTestReports({appId, branch_id, status: "FAILED", limit: 5})
Note: `status` is CASE-SENSITIVE. Use the exact enum values
(`FAILED`, `PASSED`, `RUNNING`, `PENDING`, `IGNORED`, `OBSOLETE`).
Results are sorted newest-first by `created_at` — the most
recent run is `data[0]`. ONE call is enough; if `data` is empty
DO NOT retry with different status / source / branch_id
permutations — the run genuinely doesn't exist on this branch
and the dev needs to re-run `keploy cloud replay` first.
Requires scope: `read`.
Parameters (8)
appIdstringrequired
Path parameter: appId
branch_idstring
Query parameter: branch_id
limitnumber
Page size. Defaults to 20 when omitted; `limit=0` is also treated
as "use default" (so existing clients sending an explicit zero
keep the prior behaviour). Capped at 100 — the spec rejects
higher values with 400 so callers fail loudly instead of being
silently clamped, and the handler enforces the same cap as a
defence-in-depth fallback.
offsetnumber
Zero-based pagination offset. Negative values are rejected — the
handler also clamps to 0 as a defence-in-depth fallback.
Query parameter: status (allowed values: PASSED|FAILED|RUNNING|PENDING|IGNORED|OBSOLETE — CASE-SENSITIVE)
untilnumber
Query parameter: until
listTestRuns
GET /apps/{appId}/test-runs — List test runs — List test runs for an app. Optional `kind` query param filters by run kind: `rerecord` (record_sandbox_test runs), `sandbox_run` (replay_sandbox_test runs), or `test_suite_run` (replay_test_suite live runs). Omit to return runs of every kind. Requires scope: `read`.
Parameters (4)
appIdstringrequired
Path parameter: appId
kindstring
Filter by run kind. Omit to return runs of every kind. (allowed values: rerecord|sandbox_run|test_suite_run — CASE-SENSITIVE)
limitnumber
Page size. Defaults to 20 when omitted; `limit=0` is also treated
as "use default" (so existing clients sending an explicit zero
keep the prior behaviour). Capped at 100 — the spec rejects
higher values with 400 so callers fail loudly instead of being
silently clamped, and the handler enforces the same cap as a
defence-in-depth fallback.
offsetnumber
Zero-based pagination offset. Negative values are rejected — the
handler also clamps to 0 as a defence-in-depth fallback.
listTestSetReports
GET /apps/{appId}/test-reports/{reportId}/test-set-reports — List test set reports within a run — Returns per-test-set results within a test run report. Requires scope: `read`.
Parameters (4)
appIdstringrequired
Path parameter: appId
limitnumber
Page size. Defaults to 20 when omitted; `limit=0` is also treated
as "use default" (so existing clients sending an explicit zero
keep the prior behaviour). Capped at 100 — the spec rejects
higher values with 400 so callers fail loudly instead of being
silently clamped, and the handler enforces the same cap as a
defence-in-depth fallback.
offsetnumber
Zero-based pagination offset. Negative values are rejected — the
handler also clamps to 0 as a defence-in-depth fallback.
reportIdstringrequired
Path parameter: reportId
listTestSuites
GET /apps/{appId}/test-suites — List test suites — List test suites for an app. Optional `has_sandbox_test` query param filters by sandbox-test linkage: `true` returns only suites that have a sandbox test (linked=true / test_set_id populated); `false` returns only suites without one. Omit to return every suite. Requires scope: `read`. Supports cursor-based pagination.
Parameters (7)
afterstring
Cursor for forward pagination (mutually exclusive with `before`)
appIdstringrequired
Path parameter: appId
beforestring
Cursor for backward pagination (mutually exclusive with `after`)
branch_idstring
Optional Keploy branch UUID. When set, scopes the read/write to that branch's overlay
(copy-on-write — see /apps/{appId}/branches). When absent or empty, operations target
the main branch (the historical default). Required for writes against a branch (the
api-server's branch gate rejects 400 otherwise); reads are tolerant of absence.
has_sandbox_teststring
Filter by sandbox-test linkage. Omit to return every suite. (allowed values: true|false — CASE-SENSITIVE)
page_sizenumber
Number of items per page
qstring
Substring / regex match on suite name (server-side regex filter). Use for bounded duplicate-checks on large apps so MCP doesn't have to paginate the whole list.
list_branches
List Keploy branches on an app. Use this BEFORE any write tool (create_test_suite, update_test_suite, sandbox flows) to see if a branch already exists for the dev's current work. If one matches the dev's git branch, pass its branch_id (or branch_name) to the write tool. Otherwise call create_branch.
Output: JSON array of {id, name, status, created_at}. Status one of: open, review_requested, approved, changes_requested, merged, closed, conflict.
Optional status filter narrows the list (e.g., "open" hides merged/closed branches).
Output: {"branches": [{id, name, status, created_at, updated_at}, ...]}. Synchronous — no playbook to walk.
Parameters (2)
app_idstringrequired
Keploy app ID
statusstring
optional status filter — open / review_requested / approved / changes_requested / merged / closed / conflict
normalizeSuiteReport
POST /apps/{appId}/test-runs/{runId}/suite-reports/{reportId}/normalize — Normalize a suite report — Requires scope: `write`.
Parameters (3)
appIdstringrequired
Path parameter: appId
reportIdstringrequired
Path parameter: reportId
runIdstringrequired
Path parameter: runId
normalizeTestRun
POST /apps/{appId}/test-runs/{runId}/normalize — Normalize a test run — Requires scope: `write`.
Parameters (2)
appIdstringrequired
Path parameter: appId
runIdstringrequired
Path parameter: runId
record_sandbox_test
Record (or refresh) the sandbox test for one or more existing test suites — captures the request/response per step plus the outbound mocks (DB, downstream HTTP, etc.) against the dev's locally-running app, then links the captures onto the suite. Use this when the dev says "record", "rerecord", "re-record", "refresh the recordings", "capture mocks", or as the RECORD step in FROM-SCRATCH (after create_test_suite).
This tool resolves the app (if only a hint is given), resolves ONE OR MORE suites to record (by exact ids OR case-insensitive name substring match), and delegates to a headless playbook. Output produces a RERECORD REPORT — it answers "did the sandbox test get created and linked successfully?".
╔═══ PRE-CHECK — DID YOU ARRIVE HERE FROM A FAILED REPLAY? ═══╗
This tool refreshes the CAPTURED BASELINE (mocks + recorded request/response per step). It does NOT modify the suite's authored assert array or response.body — those are the contract as defined when the suite was created/updated. If the contract changed and you re-record without updating the suite first, the new rerecord fires the suite's stale assertions against the live app, gate-1-fails on the same diff, and the suite comes back unlinked.
Before calling THIS tool in response to a failed replay_sandbox_test or replay_test_suite, walk these checks:
1. Read failed_steps[].authored_assertions and authored_response_body in the most recent get_session_report (kind=sandbox_run / test_suite_run). The fields are inlined — no second tool call needed unless the report predates the inlined fields.
2. For each failing step: does any authored assertion pin the diverging value? (e.g. assert {path: "$.order.status", expected: "created order"} where the diff says "expected 'created order', got 'created'".)
* YES → call update_test_suite FIRST to update that assertion + the response.body field, THEN call this tool.
* NO → safe to call this tool directly; the captured baseline drifts but no authored assertion blocks the rerecord.
3. If you can't find authored_assertions in the report (older format) AND don't already know the suite's shape, call getTestSuite({app_id, suite_id, branch_id}) to inspect the assert array before deciding. Don't guess.
REFUSE-RULE: if the dev confirms a contract change is intentional and the failing step has a pinned authored assertion on the diverging value, you MUST run update_test_suite before this tool. Calling record_sandbox_test FIRST in that case is the bug this pre-check exists to prevent — don't justify it as "let's just refresh the baseline first". The order is update → record → replay; never record → update.
╚═══════════════════════════════════════════════════════════════╝
===== BEFORE CALLING — one-time setup =====
(a) APP_ID RESOLUTION (skip if app_id is already known):
* Derive a likely app name from the cwd's basename (e.g. cwd=/home/dev/orderflow → "orderflow"). Lowercase it.
* Call listApps({q: "<cwd-basename>"}) — the server does a case-insensitive server-side substring match, so you don't paginate the full tenant list (can be hundreds of apps on shared accounts).
* Exactly one match → use its id. Multiple → list them and ASK the dev which one (a wrong app_id silently routes traffic + suite creates into the wrong app). Exception: if the compose file / repo layout unambiguously pins one candidate (e.g. compose has service "producer" and one candidate is "<folder>.producer" while others are unrelated siblings), you may pick it AND tell the dev up-front so they can correct.
* Zero matches → ASK permission to create a new Keploy app with the derived name; on yes, call createApp({name, endpoint}) and use the returned id.
* Alternatively pass app_name_hint to THIS tool and the server resolves it (same rules; multiple/zero → typed error).
(b) KEPLOY BINARY VERIFICATION:
* Bash: "keploy --version" (or "~/.keploy/bin/keploy --version"). If it exits non-zero the binary is missing.
* If missing OR older than this MCP server was built against, install/upgrade: curl --silent -O -L https://ke
Parameters (11)
app_commandstringrequired
Shell command that starts the user's app
app_dirstring
Absolute path to the repo root
app_idstring
Keploy app ID. Provide this OR app_name_hint.
app_name_hintstring
Case-insensitive substring of the app name (typically cwd basename, e.g. "orderflow"). Used when app_id isn't known.
app_urlstringrequired
Base URL the app listens on
branch_idstringrequired
REQUIRED. Keploy branch ID (uuid). Resolve BEFORE calling: (1) `git rev-parse --abbrev-ref HEAD` in app_dir; (2) call create_branch tool with {app_id, name: <git branch>} → use returned branch_id. Direct writes to main are blocked.
container_namestring
REQUIRED when app_command is docker-compose. EXACT container_name from compose file.
network_namestring
Optional docker network name for compose setups.
suite_idsstring
Comma-separated exact suite IDs to record. Provide this OR suite_name_hint.
suite_name_hintstring
Case-insensitive substring of suite name (e.g. "happy-path" or "deterministic"). Every matching suite is recorded in one playbook run.
timeout_secondsnumber
Per-run timeout, default 300
replay_sandbox_test
Replay the sandbox test for one or more suites against captured mocks — re-runs the suite's steps against the dev's locally-running app while keploy serves outbound calls (DB, downstream HTTP, etc.) from the captured mocks. Use this when the dev says "replay", "run my sandbox tests", "integration-test", "check if mocks still match" — keywords "sandbox" / "replay" / "mocks" / "integration-test" all map here. Also the REPLAY STEP of FROM-SCRATCH: call this LAST (after create_test_suite + record_sandbox_test) to give the dev the whole-app regression picture against the freshly captured mocks. Output produces a SANDBOX RUN REPORT — it answers "does the suite still hold up against its captured baseline?".
═══════════════════════════════════════════════════════════════════
DISAMBIGUATION — pick this tool vs. replay_test_suite:
═══════════════════════════════════════════════════════════════════
USE replay_sandbox_test (THIS TOOL) when the dev says:
* "run my sandbox tests" / "replay my sandbox tests"
* "integration-test my app" / "run the integration tests"
* "check if my mocks still match" / "replay against the captured mocks"
* "rerun my sandbox suite" (with the word "sandbox")
Trigger keyword: an explicit "sandbox" / "replay" / "mocks" / "integration-test" — silent signal that the dev wants captured-mock replay, NOT live-app execution.
USE replay_test_suite INSTEAD when the dev says:
* "run the test suite" / "run my test suites" (bare — no "sandbox")
* "execute test suite X" / "run suite 810d3ebe…"
* "test the suite again" / "smoke test against the live app"
Bare verbs ("run / test / execute") applied to "the suite" without the word "sandbox" mean LIVE-APP execution, NOT captured-mock replay. replay_test_suite hits the dev's running localhost app directly via HTTP — no docker spin-up, no mocks.
After a record_sandbox_test run, the natural next step is THIS tool (replay against the just-captured mocks). After create_test_suite / update_test_suite, the natural next step is replay_test_suite (validate against the live app). When the dev's verb is bare and the prior turn doesn't make the intent obvious, ASK rather than picking sandbox-replay silently — code-change regressions can hide under "mock didn't match" failures.
═══════════════════════════════════════════════════════════════════
DISCOVERY — when the dev hands you a bare suite_id with no app_id / branch_id:
═══════════════════════════════════════════════════════════════════
Suites live on a (app_id, branch_id) tuple. A bare suite_id has NO on-disk hint about which app or branch holds it; you have to RESOLVE both before calling this tool. Walk these steps in order — STOP as soon as getTestSuite returns 200:
1. Detect the dev's git branch: Bash `git rev-parse --abbrev-ref HEAD` in app_dir.
If exit non-zero / output is "HEAD" → not a git repo / detached HEAD; ASK the dev for the Keploy branch name.
2. Resolve candidate apps via the cwd basename: Bash `basename $(pwd)` → call listApps with q=<basename>. Usually 1–2 candidates. If 0 → ASK; if >1 → walk every candidate in step 4.
3. For each candidate app, call list_branches({app_id}) and find the branch whose `name` matches the git branch from step 1. That gives you {branch_id}. If no match → not this app, try next.
4. Verify with getTestSuite({app_id, suite_id, branch_id=<from step 3>}). 200 → resolved; 404 → wrong app/branch, try next.
5. If steps 2–4 exhaust, walk every OPEN branch on each candidate app via list_branches → getTestSuite. Then try main (branch_id omitted). If still nothing → ASK the dev for the {app_id, branch_id} pair.
After resolving once in a session, REUSE the {app_id, branch_id} for subsequent suite-targeted calls; don't re-walk discovery for every action.
SCOPE — whole-app vs single-suite:
* Default: LEAVE suite_ids UNSET → the tool resolves "every suite for the app that has a sandbox test (test_set_id populated)" and replays them all. Use this for "run my sandbox tests" / "check if my tests still pass" — whole-app regression. New suites auto-pick up.
Parameters (11)
app_commandstringrequired
Shell command that starts the user's app
app_dirstring
Absolute path to the repo root
app_idstring
Keploy app ID. Provide this OR app_name_hint.
app_name_hintstring
Case-insensitive substring of the app name (typically the cwd basename, e.g. "orderflow"). Used when app_id isn't known. Server does listApps({q: hint}) and requires exactly one match; 0 or >1 matches return an error.
app_urlstringrequired
Base URL the app listens on
branch_idstringrequired
REQUIRED. Keploy branch ID (uuid). Resolve BEFORE calling: (1) `git rev-parse --abbrev-ref HEAD` in app_dir; (2) call create_branch tool with {app_id, name: <git branch>} → use returned branch_id. Direct writes to main are blocked.
container_namestring
REQUIRED when app_command is docker-compose. EXACT container_name from compose file.
network_namestring
Optional docker network name for compose setups.
suite_idstring
OPTIONAL legacy single-suite alias for suite_ids. Prefer suite_ids when passing one or more — keeps the input shape uniform across record_sandbox_test / replay_sandbox_test.
suite_idsstring
OPTIONAL comma-separated suite IDs to narrow the run to a specific subset. LEAVE UNSET when the dev says "run my sandbox tests" / "replay everything" — the CLI then resolves "every suite with a sandbox test for the app" itself, which is the right default for whole-app regression and means new suites auto-pick up. Only pass an explicit list when the dev names suites ("replay just the auth suite", "run only suite 810d3ebe…").
timeout_secondsnumber
Per-run timeout, default 300
replay_test_suite
Replay an existing test suite live against the dev's LOCAL APP (no mocks, no docker spin-up). Returns a playbook that delegates to the enterprise CLI `keploy test-suite`, which walks each suite's steps, fires HTTP requests at base_path, evaluates assertions, and uploads per-suite results to api-server. The CLI prints a final pass/fail summary table plus a "Report:" URL to stdout. Output produces a TEST SUITE REPORT — it answers "does the suite hold up against the actual current system?".
═══════════════════════════════════════════════════════════════════
DISAMBIGUATION — pick this tool vs. replay_sandbox_test:
═══════════════════════════════════════════════════════════════════
USE replay_test_suite (THIS TOOL) when the dev says:
* "run the test suite" / "run my test suites"
* "execute test suite X" / "run suite 810d3ebe…"
* "test the suite again" / "rerun the suite"
* "validate the suite changes" (after editing a suite)
* "smoke test against the live app"
Default reading: bare verbs "run" / "execute" / "test" applied to "the suite" mean LIVE-APP execution, NOT replay against captured mocks.
USE replay_sandbox_test INSTEAD when the dev says:
* "run my sandbox tests" / "replay my sandbox tests"
* "integration-test my app" / "check if my mocks still match"
* "replay the captured tests" / "run against the recorded mocks"
Trigger keyword: "sandbox" / "replay" / "mocks" / "integration-test" — explicit signal that the dev wants captured-mock replay, not live-app.
After a record_sandbox_test run, the natural next step is replay_sandbox_test (replay against the freshly captured mocks). After create_test_suite / update_test_suite, the natural next step is replay_test_suite (validate the new/edited suite against the live app). When the dev's verb is bare ("run the suite") and the prior turn was create/update, prefer THIS tool. When the prior turn was record, ASK the dev if unsure — the verbs overlap and silently picking sandbox-replay can mask code-change failures with mock-replay noise.
USE THIS for: re-running previously-created suites against a running local app — verifying a regression after a code change, smoke-testing a branch, re-validating after editing a suite.
DO NOT USE this for: validating a NEW suite that hasn't been inserted yet (use create_test_suite — it runs the suite twice as part of validation), or for running suites against the captured-mock copy of the app (use replay_sandbox_test — captured-mock replay flow).
═══════════════════════════════════════════════════════════════════
DISCOVERY — when the dev hands you a bare suite_id with no app_id / branch_id:
═══════════════════════════════════════════════════════════════════
Suites live on a (app_id, branch_id) tuple. A bare suite_id has no on-disk hint about which app or branch holds it; you have to RESOLVE both before calling this tool. Walk these steps in order — STOP as soon as getTestSuite returns 200:
1. Detect the dev's git branch: Bash `git rev-parse --abbrev-ref HEAD` in app_dir.
If exit non-zero / output is "HEAD" → not a git repo / detached HEAD; ASK the dev for the Keploy branch name (don't invent one).
2. Resolve candidate apps via the cwd basename: Bash `basename $(pwd)` → call listApps with q=<basename> (case-insensitive substring match). Usually 1–2 candidates (e.g. "orderflow" → matches "orderflow" and "orderflow.producer"). If 0 → ASK the dev for the app_id; if >1 → walk every candidate in step 4.
3. For each candidate app, call list_branches({app_id}) and find the branch whose `name` matches the git branch from step 1. That gives you {branch_id, status}. If no match → that app's not the owner; try the next candidate. If status is closed/merged → ask the dev whether to use this branch anyway.
4. Verify with getTestSuite({app_id, suite_id, branch_id=<from step 3>}). 200 → resolved; 404 → wrong app, try next candidate.
5. If steps 2–4 exhaust without a hit, the suite is on a branch whose name doesn't match the git branch (the dev created it with a custom name, or it's on main). Then: call list_branch
Parameters (6)
app_dirstring
Absolute path to the dev's repo root. Defaults to '.' (cwd). The CLI cd's here.
app_idstringrequired
Keploy app ID
base_pathstringrequired
Base URL of the dev's local app, e.g. http://localhost:8080
branch_idstringrequired
REQUIRED. Keploy branch UUID. Resolve via two-step flow: (1) `git rev-parse --abbrev-ref HEAD` in app_dir; (2) call create_branch tool. Direct main writes are blocked.
headerstring
Optional single request header, e.g. "Cookie: session=…". Injected on every step.
suite_idsstring
Optional comma-separated suite IDs to run. Omit to run every suite registered for app_id on the branch.
revokeAPIKey
DELETE /api-keys/{keyId} — Revoke an API key — Requires scope: `admin`.
Parameters (1)
keyIdstringrequired
Path parameter: keyId
runTestSuites
POST /apps/{appId}/test-suites/run — Run test suites — Run test suites against a PUBLIC target URL. DO NOT use for local-app / localhost runs — base_url must be reachable from the SaaS backend (rejects loopback / private IPs as 400 'invalid baseURL'). For localhost runs use the MCP tool record_sandbox_test (keploy agent). Optional sandbox_mode field: ""|"rerecord"|"integration_test" — the sandbox modes are primarily used through MCP's record_sandbox_test / replay_sandbox_test tools. Requires scope: `write`.
Parameters (7)
appIdstringrequired
Path parameter: appId
authobject
Optional auth bundle the runner injects into every step.
Carries an `authtype` discriminator (BearerToken /
BasicAuth / APIKeyAuth / CookieAuth / LoginCurl / None)
plus the matching variant block. See models.Auth in
pkg/models/e2e.go for the full shape.
base_urlstringrequired
PUBLIC target URL the SaaS backend will hit. Loopback /
private IPs are rejected with 400. For localhost runs use
the MCP record_sandbox_test tool, not this endpoint.
rate_limitnumber
Per-second cap on outgoing requests; 0 means unbounded.
sandbox_modestring
Empty for normal in-backend runs. `rerecord` and
`integration_test` switch to sandbox flow where the local
keploy agent or k8s-proxy drives the run. Surfaced for
completeness; MCP tools (record_sandbox_test /
replay_sandbox_test) are the supported entry points.
test_suite_idsarray
Suite IDs to include in the run. Empty/omitted means "run
all suites for the app" — same default the GraphQL surface
applies.
timeoutnumber
Per-request timeout in seconds; 0 means use the runner default.
run_and_report
Run test suites and return results with failures and coverage.
!! DO NOT USE for local-app "tests for my changes" flows !!
This tool sends the run to the SaaS backend which REJECTS private/localhost URLs ("IPv6 address is private / reserved"). It only works when base_url points at a PUBLIC, non-loopback address (a staging/prod deployment).
For local-app testing, use record_sandbox_test / replay_sandbox_test instead — they drive the keploy local agent which happily records against http://localhost.
Parameters (3)
app_idstringrequired
Keploy app ID
base_urlstringrequired
PUBLIC target API base URL (not localhost). For localhost, use record_sandbox_test.
timeoutnumber
Per-request timeout in seconds
scaffold_pipeline_workflow
Generate the exact CI workflow YAML to add keploy sandbox tests to a pull-request pipeline, and tell you where to write it. Use this when the dev asks to "add keploy sandbox tests to my pipeline" / "wire keploy into CI" / "run keploy on PR" / "add a CI job for keploy" — the server emits the file contents verbatim so you don't have to compose the flag list yourself.
===== GOAL =====
Write a CI workflow file that runs `keploy test sandbox --cloud-app-id <uuid> --app-url <url>` on pull requests and gates the PR on the result. NEVER kick off an actual test run in this flow — it is pure file authoring, ends with the file on disk. DO NOT fire replay_sandbox_test, record_sandbox_test, replay_test_suite, or any other run-starting MCP tool here.
===== HOW (absolute) =====
Call this tool. It returns { file_path, content, summary }. Write the "content" to "file_path" VERBATIM via your Write tool — NO flag renames, NO flag removals, NO step reordering, NO synthesis. The server owns the YAML template; your job is only to (1) resolve the inputs from the repo and api-server and (2) Write the returned content. Do NOT compose the YAML yourself from general knowledge — flag drift (missing --cloud-app-id, inventing --app) is the most common bug when Claude improvises.
DO NOT ASK the dev for confirmation before writing. Resolve everything from the repo + api-server, pick the GitHub Actions default, call this tool, Write the file. The dev's prompt is already the go-ahead.
===== STEPS =====
1. DETECT THE CI SYSTEM:
* Default = GitHub Actions (biggest share). File = .github/workflows/keploy-sandbox.yml.
* If .gitlab-ci.yml exists → GitLab (not yet supported by this tool; tell the dev and stop).
* If .circleci/config.yml exists → Circle (not yet supported; tell the dev and stop).
* Otherwise → GitHub Actions.
2. RESOLVE VALUES by calling MCP tools + reading the repo:
* app_id: call listApps({q: "<cwd basename>"}). Exactly one → use its id. Multiple → pick the one whose name most specifically matches the repo's primary service (e.g. "orderflow.producer" wins over "orderflow" when there's a ./producer directory); mention which you picked in the final message. Zero → stop and tell the dev to create the app + rerecord first.
* suite_ids: DO NOT pass this arg by default. An empty suite_ids means the CLI resolves "every linked sandbox suite for the app" at CI run time — which is what you want (new suites auto-pick up without workflow edits). The tool still verifies there's ≥1 linked suite at scaffold time so the first PR run doesn't fail empty-handed. Only pass suite_ids when the dev explicitly narrows ("run only the auth suite in CI"); don't pin "all current suites" — that's staleness waiting to happen.
* compose_file: READ THE REPO. Default is docker-compose.yml. AVOID passing a docker-compose-keploy.yaml variant that has `networks: default: external: true` — those variants only work locally, where another compose run has already created the external network. In CI the runner starts clean and `external: true` fails with "network not found". If the primary docker-compose.yml brings up the full app (deps + app service), use it end-to-end.
* app_service, container_name, app_port: read from the SAME compose_file you picked above. app_service = the service key (e.g. "producer"); container_name = that service's container_name: field in that same compose file (e.g. "orderflow-producer" if compose_file=docker-compose.yml, but "producer" if compose_file=docker-compose-keploy.yaml — THESE DIFFER, pick consistently); app_port = the host-side of its ports: mapping.
* app_url = http://localhost:<app_port>. The tool derives this; you don't pass it separately.
3. CALL THIS TOOL with app_id, app_service, container_name, app_port, compose_file (and suite_ids only if the dev explicitly narrowed scope). It returns { file_path, content, summary }. Write the "content" to the "file_path" VERBATIM.
===== FLAG NAME RULES (absolute, do not drift when reviewing the output) =====
* `--cloud-app-id` ← NOT `--app-id`. The OSS config has
Parameters (9)
app_idstring
Keploy app UUID. Provide this OR app_name_hint.
app_name_hintstring
Case-insensitive substring of the app name (typically cwd basename, e.g. "orderflow").
app_portnumber
HTTP port the service exposes (default 8080)
app_servicestringrequired
Docker-compose service name for the user's app (e.g. 'producer'). Must be a service KEY in the compose_file you pass.
branch_namestring
OPTIONAL Keploy branch NAME (the human-readable string the dev knows — e.g. "update-order-response-fields" or whatever the dev's git branch is) to scope the "are there any linked suites yet?" pre-flight count. Resolved to a branch UUID internally via list_branches. The generated YAML still uses --create-branch ${{ github.head_ref }} at CI run time (per-PR branch), so this arg ONLY influences the pre-flight smoke count + the summary line — it doesn't get baked into the workflow. Pass when your linked suites live on a branch (not main) so the scaffold's count reflects them. If unset, the scaffold counts main-view suites only.
ci_systemstring
CI system. Currently only "github-actions" supported (default).
compose_filestring
Path to the docker-compose file CI should use (default "docker-compose.yml"). Avoid pointing this at a variant with `networks: default: external: true` — those only work locally where the external network already exists; in CI (clean runner) they fail with "network not found".
container_namestringrequired
Container name from the compose_file's container_name: field for app_service. Different compose files can set different container names for the same service — e.g. docker-compose.yml might say 'orderflow-producer' while docker-compose-keploy.yaml says 'producer'. Must match whichever file you pass as compose_file.
suite_idsstring
Optional comma-separated suite IDs to pin in the workflow. LEAVE BLANK by default — the CLI resolves every linked suite for the app at run time, so new suites auto-pick up in CI without editing the YAML. Only pass an explicit list when the dev narrows scope.
setSmartTestCaseObsolete
POST /apps/{appId}/smart-set/cases/{name}/obsolete — Mark a smart test case obsolete / restore it (branch-only) — Toggle a smart case's obsolete flag (skip-in-replay) on a branch — an
overlay edit. `branch_id` is REQUIRED: edits, including obsolete, are
branch-only. Requires scope: `write`.
Parameters (4)
appIdstringrequired
Path parameter: appId
branch_idstringrequired
Branch UUID — required (obsolete is branch-only).
namestringrequired
Smart-set-local case name (e.g. "test-3").
obsoletebooleanrequired
startLoadTest
POST /apps/{appId}/load-tests — Start a load test — Requires scope: `write`.
Parameters (1)
appIdstringrequired
Path parameter: appId
stopJob
POST /jobs/{jobId}/stop — Stop a running job — Requires scope: `write`.
Parameters (1)
jobIdstringrequired
Path parameter: jobId
stopLoadTest
POST /apps/{appId}/load-tests/{runId}/stop — Stop a running load test — Requires scope: `write`.
Parameters (2)
appIdstringrequired
Path parameter: appId
runIdstringrequired
Path parameter: runId
streamJobEvents
GET /jobs/{jobId}/events — Stream job events (SSE) — Requires scope: `read`. Returns a `text/event-stream`.
Parameters (1)
jobIdstringrequired
Path parameter: jobId
streamLoadTestEvents
GET /apps/{appId}/load-tests/{runId}/events — Stream load test events (SSE) — Requires scope: `read`. Returns a `text/event-stream`.
Parameters (2)
appIdstringrequired
Path parameter: appId
runIdstringrequired
Path parameter: runId
updateApp
PUT /apps/{appId} — Update an app — Requires scope: `write`.
Parameters (24)
api_examplesstring
Sample request/response pairs the AI consults when authoring suites.
appIdstringrequired
Path parameter: appId
app_level_custom_functionobject
Register a JS function devs can reference from suite step templates. Key uniquely identifies the function; CustomFunction is the JS source.
app_level_custom_variablesobject
Add / update / delete a SINGLE global variable. The Action enum on the embedded ExtractInput controls the operation. To set multiple variables, call updateApp once per variable.
authobject
Authentication configuration for test execution. The runner injects the matching headers on every step request.
brdstring
Business requirements document content the AI uses for context.
code_snippetstring
Server code snippet the AI uses for endpoint context.
countrystring
Two-letter country code controlling data-residency-affected behavior. Rarely set.
disable_schema_assertionboolean
docsstring
Free-form developer docs. Used by AI as additional context when authoring suites.
enable_post_hookboolean
Run the post-step hook after each test step.
enable_pre_hookboolean
Run the pre-step hook before each test step.
endpointstring
graphql_schemastring
GraphQL schema (SDL) the AI uses when generating GraphQL suites.
ignore_endpointsarray
Endpoint patterns the runner skips when generating / running suites.
labelsarray
Add or update labels on the app. New labels (no `id`)
require both `name` and `color`; updates to existing
labels (with `id`) require at least one of `name`/`color`.
main_curlstring
Reference curl that drives generation when no schema is available.
max_test_suitesnumber
Cap on how many suites generate-tests will mint at once. Server default applies if omitted.
postmanstring
Postman collection JSON the AI parses for endpoint shapes / examples.
prdstring
Product requirements document content the AI uses for context.
private_modeboolean
Restrict app visibility to the authenticated user only.
rate_limitnumber
Requests-per-second cap the runner applies to outbound calls during runs.
schemastring
webhook_urlstring
updateSmartTestCase
PATCH /apps/{appId}/smart-set/cases/{name} — Edit a smart test case (branch-only) — Edit a smart case on a branch. `branch_id` is REQUIRED — recording-driven
main is never hand-edited; edits live on a branch, are reviewed via the
branch diff, then merged. Value edits (noise/assertions/golden response
body/description/mock links) write in place; SHAPE edits (endpoint/method/
status via request/response) recompute the contract identity (schema_ref)
and, on collision with another case, return a conflict in the body rather
than silently merging. Patch fields are JSON strings so the wire stays
unambiguous; only provided fields are applied. Requires scope: `write`.
Parameters (10)
appIdstringrequired
Path parameter: appId
assertionsJsonstring
JSON assertions map.
branch_idstringrequired
Branch UUID — required (edits are branch-only).
descriptionstring
mockReferencesJsonstring
JSON array of {name,kind} — relink which mocks this case uses.
namestringrequired
Smart-set-local case name (e.g. "test-3").
noiseJsonstring
JSON map<string,[]string> of fields to ignore on compare.
requestJsonstring
Full HTTPReq JSON (SHAPE edit — recomputes schema_ref).
respBodystring
Golden response body override (value edit).
responseJsonstring
Full HTTPResp JSON (SHAPE edit).
updateTestCase
PUT /apps/{appId}/recordings/{testSetId}/test-cases/{testCaseId} — Update a test case — Update mutable fields of a recorded test case identified by its friendly **name** (e.g. `test-4` — the name in the recording yaml) within `(testSetId, branchId)`. The body can carry `name` / `http_req` / `http_resp` (response-edit sub-action) AND/OR `noise` (noise sub-action — a replace-style `path → match-substrings` map for non-deterministic fields). Both Case-2a sub-actions documented in the LLM workflow are handled by this single endpoint. Pass `branch_id` to scope the edit to a branch overlay (bundle-uploaded test cases on a branch are invisible to main writes). Requires scope: `write`.
Parameters (8)
appIdstringrequired
Path parameter: appId
branch_idstring
Optional branch overlay id (body alternative to the query param). Either form is accepted; query wins when both are set.
http_reqobject
http_respobject
namestring
noiseobject
Replace-style noise map. Keys are JSON paths (`body.foo.bar` / `header.X-Trace`), values are arrays of match-substrings (empty array means "ignore this field entirely on diff"). To merge with existing noise, do GET → modify → PUT. Nullability semantics: **omit the field** (or send JSON `null`) to preserve the existing noise unchanged; **send `{}`** to clear all noise; **send a populated map** to replace. Sending `null` and omitting are equivalent at the server.
testCaseIdstringrequired
Path parameter: testCaseId
testSetIdstringrequired
Path parameter: testSetId
updateTestSuite
PUT /apps/{appId}/test-suites/{suiteId} — Update a test suite — Requires scope: `write`.
Parameters (3)
appIdstringrequired
Path parameter: appId
branch_idstring
Optional Keploy branch UUID. When set, scopes the read/write to that branch's overlay
(copy-on-write — see /apps/{appId}/branches). When absent or empty, operations target
the main branch (the historical default). Required for writes against a branch (the
api-server's branch gate rejects 400 otherwise); reads are tolerant of absence.
suiteIdstringrequired
Path parameter: suiteId
update_app_custom_variables
Add or update an app-level custom variable. App-level variables are shared across ALL test suites for this app (vs step-level extracts which are per-suite). Use this to declare reusable random-value generators ONCE per app, instead of redeclaring them in a prelude step of every suite.
═══════════════════════════════════════════════════════════════════
WHEN TO USE
═══════════════════════════════════════════════════════════════════
You're drafting a POST / PUT / PATCH suite. The validator's R9 (REST) / G24 (GraphQL) rules require every mutation body to reference at least one {{var}} backed by a dynamic generator. Two ways to satisfy them:
1. App-level fn-generator (preferred) — declared ONCE here, reused by every suite. No prelude step needed.
2. Step-level extract via a prelude step (legacy) — declared per-suite. Adds a no-op step.
ALWAYS prefer (1). Workflow:
a) Call get_app_testing_context first — read app.appLevelCustomVariables.
b) If a suitable gen* var already covers your need (e.g., genEmail for an email field) → reference {{genEmail}} in the body and skip this tool. Don't recreate.
c) If no existing var fits → call this tool to create one. Future suites on this app will reuse it.
═══════════════════════════════════════════════════════════════════
NAMING CONVENTION (recommended for readability — not enforced by the validator)
═══════════════════════════════════════════════════════════════════
* Dynamic generators: by convention use gen-prefix + camelCase — genEmail, genOrderId,
genTenantId, genUUID. The recommended_substitutions hints in get_app_testing_context
parse these prefixes to surface "use {{genEmail}} for email fields" suggestions, so
following the convention makes the agent's reuse path smoother. It is NOT enforced:
the validator decides whether a {{key}} is a dynamic generator by looking at the
value SHAPE alone — specifically whether the stored value parses as a JavaScript
function literal. A var named "randomEmail" with a function value works fine; a var
named "genEmail" with a plain string is just a static fixture.
* Static fixtures: plain camelCase — seedTenantId, defaultRegion.
Static vars are fine for headers / query params / assertion expected values.
Static vars are REJECTED by R9/G24 if used inside POST/PUT/PATCH bodies — they'd
produce identical writes across runs and break idempotency.
═══════════════════════════════════════════════════════════════════
VALUE FORMAT
═══════════════════════════════════════════════════════════════════
* type=Dynamic: a NAMED JavaScript function declaration whose body returns
a value per call. The runtime registers the function in a JS VM by name
and calls it on every substitution — so the form MUST be parseable as a
named declaration: "function <name>(){...}" or "async function <name>(){...}".
Anonymous functions ("function(){...}") and arrow functions
("() => ...", "x => x*2") are REJECTED at the MCP layer — the runtime
can't execute them and would either error out or silently substitute the
source text as a literal string.
Example value:
function genEmail(){ return 'u_' + Date.now() + '_' + Math.random().toString(36).slice(2,8) + '@example.com'; }
* type=Static: a plain string.
Example value: us-east-1
═══════════════════════════════════════════════════════════════════
SEMANTICS
═══════════════════════════════════════════════════════════════════
* Upsert by key — same key called twice with different value replaces. Idempotent on retries.
* Delete is NOT exposed via MCP. To remove a variable, the dev uses Settings → Global Custom Variables in the Keploy UI.
* Vars created here surface in that same UI page so the dev can see and edit them.
* Branching: pass branch_id to scope the var to a Keploy branch overlay. Omit to write to the app's main config.
═══════════════════════════════════════════════════════════════════
DON'T
═══════════════════════════════════════════════════════════════════
* Don't create a Dynamic var with a value that isn'
Parameters (5)
app_idstringrequired
Keploy app ID
branch_idstring
Optional Keploy branch UUID. When set, writes to the branch overlay instead of the app's main config.
keystringrequired
Variable name. Use gen-prefix camelCase for dynamic generators (genEmail), plain camelCase for static fixtures.
typestringrequired
Dynamic or Static. Use Dynamic for fn-generators (reusable in POST/PUT/PATCH bodies); Static for fixtures (headers, query params, assertion expected values only).
valuestringrequired
For type=Dynamic, a JavaScript function literal (e.g., function(){ return ... }). For type=Static, a plain string.
update_test_suite
Edit an existing test suite — change one or more step bodies, assertions, headers, or remove/add steps. Returns a playbook that delegates to `keploy update-test-suite`, which validates the new state (static structural checks + 2 live runs for idempotency + GET-coupling check) and snapshot-replaces the suite via api-server.
POST-EDIT BEHAVIOUR: any structural change here (step method/url/body/headers/extract/assert, or add/delete steps) AUTOMATICALLY clears the suite's sandbox test server-side — the suite comes back as linked=false. Call record_sandbox_test on the updated suite before any sandbox replay; otherwise replay_sandbox_test will 400 with "no sandboxed tests". Cosmetic-only edits (name, description, labels) preserve the sandbox test.
═══════════════════════════════════════════════════════════════════
FETCH-FIRST RULE — required for the edit to be accepted:
═══════════════════════════════════════════════════════════════════
The api-server's replace handler rejects updates that preserve ZERO step IDs from the existing suite ("full rewrite, not an edit"). To make a real edit:
1. Call getTestSuite first (or use download_recording / get_app_testing_context if you already have the suite). Capture each existing step's "id" field.
2. Compose your new steps_json INCLUDING the existing "id" on every step you want to KEEP or EDIT. Omit "id" only on steps you're ADDING. Drop a step entirely from steps_json to DELETE it.
3. Call this tool with that merged steps_json.
If you author a fresh JSON without the existing step IDs, the server rejects it with "preserves no steps from the existing suite". When that happens, your two options are: (a) re-author with IDs preserved (preferred — keeps history), or (b) call delete_test_suite then create_test_suite (loses history, fresh suite_id).
═══════════════════════════════════════════════════════════════════
DISCOVERY — when the dev hands you a bare suite_id with no app_id / branch_id:
═══════════════════════════════════════════════════════════════════
Suites live on a (app_id, branch_id) tuple. A bare suite_id has no on-disk hint about which app or branch holds it; you have to RESOLVE both before calling this tool. Walk these steps in order — STOP as soon as getTestSuite returns 200:
1. Detect the dev's git branch: Bash `git rev-parse --abbrev-ref HEAD` in app_dir.
If exit non-zero / output is "HEAD" → not a git repo / detached HEAD; ASK the dev for the Keploy branch name.
2. Resolve candidate apps via the cwd basename: Bash `basename $(pwd)` → call listApps with q=<basename>. Usually 1–2 candidates. If 0 → ASK; if >1 → walk every candidate in step 4.
3. For each candidate app, call list_branches({app_id}) and find the branch whose `name` matches the git branch from step 1. That gives you {branch_id}. If no match → not this app, try next.
4. Verify with getTestSuite({app_id, suite_id, branch_id=<from step 3>}). 200 → resolved; 404 → wrong app/branch, try next.
5. If steps 2–4 exhaust, walk every OPEN branch on each candidate app, then try main (branch_id omitted). If still nothing → ASK the dev for the {app_id, branch_id} pair.
The getTestSuite call in step 4 is the one whose response you also use to capture every step's existing "id" for the FETCH-FIRST RULE above — so step 4 is actually a 2-for-1: discovery AND fetch-first happen on the same call.
After resolving once in a session, REUSE the {app_id, branch_id} for subsequent suite-targeted calls; don't re-walk discovery for every action.
═══════════════════════════════════════════════════════════════════
INPUTS
═══════════════════════════════════════════════════════════════════
* app_id (required) — Keploy app id
* suite_id (required) — UUID of the suite to update
* branch_id (required) — Keploy branch UUID (resolve via the two-step flow before calling)
* steps_json (required) — JSON array of the FULL desired step list. Each kept step MUST carry the existing "id". Same step shape as create_test_suite (response, extract, assert, etc — all static structural checks apply).
* name / descr
Parameters (9)
app_dirstring
Absolute path to the dev's repo root. Defaults to '.' (cwd).
app_idstringrequired
Keploy app ID
app_urlstringrequired
Base URL of the dev's local running app, e.g. http://localhost:8080. The CLI fires the new state twice against this for the idempotency + GET-coupling checks.
branch_idstringrequired
REQUIRED. Keploy branch UUID.
descriptionstring
Optional override for the suite's description.
labelsstring
Optional comma-separated labels to set (replaces the existing list).
namestring
Optional override for the suite's name. Defaults to the existing name if omitted.
steps_jsonstringrequired
JSON array of the full desired step list. Each step you want to KEEP must carry the existing "id" — omit "id" only on new steps. Drop steps entirely to delete them.
suite_idstringrequired
UUID of the test suite to update
uploadRecordingBundle
POST /apps/{appId}/recordings/bundle — Atomic test set + cases + mocks + mappings ingest — Bundle ingest — creates the test set, every test case, every
mock, and the mapping doc in a single call. Each step is its
own DB write; partial failure leaves earlier rows in place,
callers can replay safely.
Branch-aware via optional `branch_id` — when set, every row
lands on the overlay until merge.
Use this when authoring a recording from scratch (LLM
workflows, CLI imports). For incremental edits, prefer the
per-resource endpoints (`createMock`, `createTestCase`, etc.).
Requires scope: `write`.
Parameters (8)
appIdstringrequired
Path parameter: appId
branch_idstring
image_namestring
image_tagstring
mocksarray
Per-mock authoring entries. For each entry, prefer
`mock_yaml` (canonical envelope as it lives in
mocks.yaml on disk) over `spec`. The typed `spec`
object hits the same lowercase-key-drop bug
documented on createMock when the input came from
on-disk YAML. When both are set on an entry,
`mock_yaml` wins.
namestringrequired
Test set name (unique within the app/branch).
smart_setboolean
Route the bundle into the smart test set as a branch import (new schema_refs only) instead of creating a legacy test set. Requires branch_id. The CLI sets this only when --smartTestSet is passed and the app has EnableSmartTestSet.
test_casesarray
upload_recording
Atomic test set + cases + mocks + mappings ingest. Creates the test set row, every test case, every mock, and the mapping doc in one call.
PREFER THE CLI FOR ON-DISK RECORDINGS. When the dev has a recorded test-set on disk (e.g. `./keploy/test-set-0/` produced by `keploy record`), invoke this via Bash instead — it streams bytes from disk to server in one HTTP round-trip:
```
keploy upload test-set \
--app <namespace.deployment> # or --cloud-app-id <uuid>
--branch <uuid|name> # optional, find-or-create on name
--test-set <path|name> # e.g. keploy/test-set-0
[--name <override>] # rename on the server
```
The CLI path runs in ~3 seconds for a typical recording; calling this MCP tool directly with the same bundle inlined as args takes minutes because Claude has to serialize ~10K+ tokens of YAML/JSON through tool_use. Reserve this MCP tool for cases where the data is already in conversation context (e.g. you just generated test cases programmatically and don't want to round-trip to disk).
Each step is its own DB write; partial failure leaves earlier rows in place — callers can replay safely. `branch_id` is REQUIRED — direct writes to main via MCP are blocked. Every row lands on the branch overlay until merge.
`test_cases[].mock_names` lists the mocks each case consumes; the server folds these into the mapping doc on upload.
Returns { test_set, test_case_ids, mock_ids }.
Parameters (7)
app_idstringrequired
Keploy app ID
branch_idstringrequired
REQUIRED. Keploy branch UUID. Direct writes to main are blocked.
image_namestring
Optional. Used for smart-set release grouping.
image_tagstring
Optional. Used with image_name.
mocksarray
Array of { name, kind, spec }.
namestringrequired
Test set name (unique within the app/branch).
test_casesarray
Array of { name, http_req, http_resp, noise?, mock_names? }.
upsertSmartMock
PUT /apps/{appId}/smart-set/mocks/{name} — Create or replace a smart-set mock's content (branch-only) — Write/replace a smart-set mock's content (a single NetworkTrafficDoc
YAML) on a branch. `branch_id` is REQUIRED — main mocks come from
recording and refresh via re-record; hand edits live on a branch, are
reviewed via the branch diff, then merged. Requires scope: `write`.
Parameters (4)
appIdstringrequired
Path parameter: appId
branch_idstringrequired
Branch UUID — required (edits are branch-only).
mockYamlstringrequired
The mock as a NetworkTrafficDoc YAML document.
namestringrequired
Smart-set mock name (e.g. "mock-2").
validateTestSuite
POST /apps/{appId}/test-suites/{suiteId}/validate — Validate a test suite — Run the suite against a public, non-loopback base URL to capture responses and run assertions. DO NOT use for local-app / localhost validation — the SaaS backend rejects private IPs with 500. For local apps, curl endpoints yourself (Bash) and pass the captured responses into create_test_suite directly. Requires scope: `write`.
<p align="center">
<img src="https://docs.keploy.io/img/keploy-logo-dark.svg?s=200&v=4" height="80" alt="Keploy Logo" />
</p>
<p align="center">
<a href="https://trendshift.io/repositories/3262" target="_blank">
<img src="https://trendshift.io/api/badge/repositories/3262" alt="keploy%2Fkeploy | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/>
</a>
</p>
<h3 align="center"><b>⚡️ API tests faster than unit tests, from user traffic ⚡️</b></h3>
<p align="center">🌟 The must-have tool for developers in the AI-Gen era for 90% test coverage 🌟</p>
<p align="center">
<a href="https://join.slack.com/t/keploy/shared_invite/zt-3zcnuqfgl-WYK1NMhslVHsCtNcA1ULwA"><img src="https://img.shields.io/badge/Slack-4A154B?style=flat&logo=slack&logoColor=white" alt="Slack" /></a>
<a href="https://www.linkedin.com/company/keploy/"><img src="https://img.shields.io/badge/LinkedIn-%230077B5.svg?style=flat&logo=linkedin&logoColor=white" alt="LinkedIn" /></a>
<a href="https://www.youtube.com/channel/UC6OTg7F4o0WkmNtSoob34lg"><img src="https://img.shields.io/badge/YouTube-%23FF0000.svg?style=flat&logo=YouTube&logoColor=white" alt="YouTube" /></a>
<a href="https://x.com/Keployio"><img src="https://img.shields.io/badge/X-%231DA1F2.svg?style=flat&logo=X&logoColor=white" alt="X" /></a>
</p>
<p align="center">
<a href="https://landscape.cncf.io/?item=app-definition-and-development--continuous-integration-delivery--keploy">
<img src="https://img.shields.io/badge/CNCF%20Landscape-5699C6?logo=cncf&style=social" alt="Keploy CNCF Landscape" />
</a>
<a href="https://github.com/Keploy/Keploy/stargazers"><img src="https://img.shields.io/github/stars/keploy/keploy?color=%23EAC54F&logo=github" alt="GitHub Stars" /></a>
<a href="https://github.com/Keploy/Keploy/">
<img src="https://img.shields.io/github/stars/keploy/keploy?color=%23EAC54F&logo=github&label=Help%20us%20reach%2020K%20stars!%20Now%20at:" alt="Help us reach 20k stars!" />
</a>
</p>
Keploy is a developer‑centric API and integration testing tool that auto‑generates tests and data‑mocks faster than unit tests.
It records API calls, database queries, and streaming events — then replays them as tests. Under the hood, Keploy uses eBPF to capture traffic at the network layer, but for you it’s completely code‑less and language‑agnostic.
<img align="center" src="https://raw.githubusercontent.com/keploy/docs/main/static/gif/record-replay.gif" width="100%" alt="Convert API calls to API tests test cases and Data Mocks using AI"/>
🐰 Fun fact: Keploy uses itself for testing! Check out our swanky coverage badge:
Key Highlights
🎯 No code changes
Just run your app with keploy record. Real API + integration flows are automatically captured as tests and mocks. (Keploy uses eBPF under the hood to capture traffic, so you don’t need to add any SDKs or modify code.)
📹 Record and Replay complex Flows
Keploy can record and replay complex, distributed API flows as mocks and stubs. It's like having a very light-weight time machine for your tests—saving you tons of time!
Unlike tools that only mock HTTP endpoints, Keploy records databases (Postgres, MySQL, MongoDB), streaming/queues (Kafka, RabbitMQ), external APIs, and more.
It replays them deterministically so you can run tests without re‑provisioning infra.
<img src="https://keploy-devrel.s3.us-west-2.amazonaws.com/keploy+ai+test+gen+for+api+statement+schema+and+branch+coverage.jpg" width="100%" alt="ai test gen for api statement schema and branch coverage"/>
<img src="https://keploy-devrel.s3.us-west-2.amazonaws.com/ai+test+case+generation+that+works.png" width="100%" alt="ai test gen for api statement schema and branch coverage"/>
Other Capabilities
🌐 CI/CD Integration: Run tests with mocks anywhere you like—locally on the CLI, in your CI pipeline (Jenkins, Github Actions..) , or even across a Kubernetes cluster. Read more
🎭 Multi-Purpose Mocks: You can also use Keploy-generated Mocks, as server Tests!
📊 Reporting: Unified reports for API, integration, unit, and e2e coverage with insights directly in your CI or PRs.
🖥️ Console: A developer-friendly console to view, manage, and debug recorded tests and mocks.
⏱️ Time Freezing: Deterministically replay tests by freezing system time during execution. Read more
📚 Mock Registry: Centralized registry to manage, reuse, and version mocks across teams and environments. Read more
Because Keploy intercepts at the network layer (eBPF), it works with any language, framework, or runtime—no SDK required.
Note: Some of the dependencies are not open-source by nature because their protocols and parsings are not open-sourced. It's not supported in Keploy enterprise.