frog-runner: Remote and Local Usage¶
frog-runner is a FHIR Frog service that orchestrates test execution across one or more FHIR servers. You can run it locally in Docker (managed by the MCP) or connect to a remote instance.
When to use frog-runner vs. the local engine¶
| Scenario | Tool | Pros |
|---|---|---|
| Quick local test during development | frog_run_local (MCP's in-process engine) |
Fast, no Docker setup |
| Run a Test IG package | frog_runner_start_local + frog_run_remote |
Test IGs are preloaded in the runner image |
| Shared CI/remote testing | Remote runner URL | No Docker on your machine; CI infrastructure manages it |
| Complex multi-server tests | frog_runner (v0.2) |
Matrix runs, bulk operations |
Local runner setup¶
Start the runner and SUT¶
# Start a local HAPI FHIR server (the system under test)
# Note: starts plain HAPI without any IG preloaded
frog_server_start_local(confirm: true)
# Start the runner
frog_runner_start_local(confirm: true)
The MCP will:
1. Create a Docker network named frog-net (if it does not exist)
2. Start a HAPI FHIR container (frog-sut) on loopback port 8081
3. Start the runner container (frog-runner) on loopback port 8080
4. Poll readiness endpoints until both are ready
Typical startup time: - SUT (HAPI FHIR): 40–60 seconds for first start; subsequent starts are faster - Runner: 3–12 seconds
Check status¶
Polls the runner to check if a run is still in progress. The runner reports COMPLETED, IN_PROGRESS, or FAILED.
Stop the runner and SUT¶
Removes the containers (but keeps the frog-net network for reuse).
Running tests with the local runner¶
Basic run¶
frog_run_remote(
testPackage: "fhirfrog.au-core-tests#0.1.0",
server: "test-sut",
runnerUrl: "http://127.0.0.1:8080"
)
Or, simpler (if the MCP knows your runner):
Run a Test IG package¶
Test IG packages (e.g. fhirfrog.au-core-tests#0.1.0) are preloaded in the runner image. Install a concrete version to your local cache, then reference it:
frog_package_install(
packageId: "fhirfrog.au-core-tests",
version: "0.1.0",
confirm: true
)
frog_run_remote(
testPackage: "fhirfrog.au-core-tests#0.1.0",
server: "test-sut"
)
Remote runner setup¶
If you have a runner URL (e.g., from your CI infrastructure or a shared environment), point the MCP to it:
Then use frog_run_remote without runnerUrl:
frog-runner HTTP API reference¶
The runner exposes a REST API at /api/v1/. All requests are JSON.
Version (GET /api/v1/version)¶
Response:
Create a run (POST /api/v1/runs)¶
Start a new test run.
Request body:
{
"scriptPath": "patient-search.json",
"serverUrl": "http://fhir-server:8080",
"fixturesPath": "./fixtures",
"igPackage": "fhirfrog.au-core-tests#0.1.0"
}
Response (HTTP 202):
Poll run status (GET /api/v1/runs/)¶
Response:
{
"runId": "run-abc123-def456",
"status": "IN_PROGRESS",
"progress": {
"current": 3,
"total": 10
},
"createdAt": "2026-10-21T12:34:56Z"
}
Status values: QUEUED, IN_PROGRESS, COMPLETED, FAILED.
Get run result (GET /api/v1/runs//result)¶
Response (HTTP 200 if COMPLETED, HTTP 202 if still running):
{
"runId": "run-abc123-def456",
"status": "COMPLETED",
"testScriptUrl": "http://example.org/TestScript/patient-search",
"serverUrl": "http://fhir-server:8080",
"result": {
"resourceType": "TestReport",
"id": "...",
"status": "completed",
"testScriptReference": "...",
"result": "passed",
"score": 1.0,
"tally": {
"passed": 3,
"failed": 0,
"skipped": 0
}
}
}
Known limit: The runner returns HTTP 200 even when tests fail. Check result.result for pass/fail status.
Export run (GET /api/v1/runs//export?format=)¶
Export the run result in a different format.
GET /api/v1/runs/run-abc123-def456/export?format=junit-xml
GET /api/v1/runs/run-abc123-def456/export?format=testscript-json
GET /api/v1/runs/run-abc123-def456/export?format=excel
Supported formats (v0.1):
- testscript-json — TestReport as JSON
- testscript-xml — TestReport as XML
- junit-xml — JUnit XML format for CI systems
Response: file download (HTTP 200, with appropriate Content-Type).
Docker setup (local runner and SUT)¶
Network creation¶
The MCP creates the network automatically on first use:
Once created, the network persists until you explicitly remove it:
If you remove the network while containers are running, you must restart them.
Port binding¶
By default:
- SUT: 127.0.0.1:8081 → container port 8080
- Runner: 127.0.0.1:8080 → container port 8080
Both are bound to localhost only (no remote access). Override the host ports with environment variables:
export FHIR_SERVER_PORT=9000
export FROG_RUNNER_PORT=9001
frog_server_start_local(confirm: true)
frog_runner_start_local(confirm: true)
Container-to-container communication¶
On the frog-net network, containers refer to each other by hostname:
- Runner refers to SUT as frog-sut:8080
- The SUT does not know about the runner
This means you cannot test by hitting 127.0.0.1:8081 from inside the runner. The runner's tests must target the SUT by name on the internal network.
macOS Docker Desktop notes¶
On macOS, Docker Desktop runs a lightweight Linux VM. Some caveats:
- File mounting: Paths are mounted from the macOS host into the Linux VM, then into the container. Home directory (
~) works by default; other paths may need sharing in Docker Desktop preferences. - Socket mounting: The Docker socket mount (
/var/run/docker.sock) works through a proxy that Docker Desktop provides. - Memory: Give Docker Desktop at least 4 GB when running a SUT + runner together.
- FROG_WORKSPACE_HOST: When the MCP itself runs in a container, set this variable to the macOS host path (not the path inside the MCP container).
Remote server registration¶
Register a server so you can refer to it by name:
frog_server_register(
name: "production",
url: "https://fhir.example.org/fhir",
conformsTo: "http://example.org/ImplementationGuide/au-core",
confirm: true
)
Then use it in runs:
Troubleshooting¶
Docker not available¶
If the MCP cannot contact Docker, frog_doctor will report docker: FAIL with a hint. Common causes:
- Docker daemon is not running
- Socket is not mounted (if MCP runs in a container)
- DOCKER_HOST is not set (for remote Docker)
Runner unreachable¶
Check:
- Is the runner running? (docker ps | grep frog-runner)
- Is the port correct? (check $FROG_RUNNER_PORT)
- Is the URL correct? (check $FROG_RUNNER_URL)
Run completes but says FAILED to start¶
If the runner returns HTTP 500 with an error like "could not find package", the Test IG package is missing or has the wrong version:
# List available packages
frog_package_list(includeRunner: true)
# Install the missing package
frog_package_install(
packageId: "fhirfrog.au-core-tests",
version: "0.1.0",
confirm: true
)
SUT not ready¶
If HAPI takes longer than 60 seconds to start, the MCP times out. Retry:
Or check the container logs: