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 an `appId` uint64 field that viper maps `--app-id` into; passing a UUID there fails with "invalid syntax" before RunE runs.
* `keploy test sandbox --cloud-app-id <uuid> --app-url <url>` ← the CI form. NOT `keploy test --cloud-app-id` (must be `test sandbox` — the headless flags live on the sandbox subcommand only), NOT `keploy test-suite run` (that command doesn't exist). There is NO `--pipeline` flag.
* Install URL = `https://keploy.io/ent/install.sh` ← NOT `https://keploy.io/install.sh` (OSS; no sandbox subcommand at all), NOT a github.com/keploy/keploy release tarball.
If the server-emitted content ever disagrees with these rules, trust the server output and file a bug — don't edit the YAML.
===== RESOLUTION ARGS =====
* Pass either app_id (explicit UUID) or app_name_hint (substring; server does listApps and requires exactly one match).
* Pass app_service (docker-compose service name), container_name (from compose container_name: field read from the SAME compose_file arg), and app_port (HTTP port the service exposes).
* compose_file is optional, defaults to "docker-compose.yml". If the repo has a -keploy.yaml variant with `external: true` networks, do NOT point compose_file at it — it won't work in CI.
* suite_ids is optional and should be LEFT BLANK by default — the CLI resolves every linked suite at run time. Only pin an explicit list when the dev narrows scope.
===== FINAL RESPONSE — three short sections, no questions =====
### Created
| File | Lines |
| --- | --- |
| .github/workflows/keploy-sandbox.yml | N |
### Summary
- App: <name> (<app_id>), <N> linked suites replayed on every PR
- Trigger: pull_request → main, + manual workflow_dispatch
- Failure on any suite gates the PR (non-zero exit from the CLI)
### Before the first run, add this GitHub secret
- `KEPLOY_API_KEY` — at https://github.com/<owner>/<repo>/settings/secrets/actions/new
(self-hosted users — point at your own api-server by building the
enterprise binary with -X main.api_server_uri=<url>; there is no
runtime env override on the released binary.)
This tool does NOT run anything. It only generates file contents.
Connector