Authors Mountebank imposters (multi-protocol mock servers - HTTP, HTTPS, TCP, SMTP, LDAP, gRPC, WebSockets, GraphQL, and more) by POSTing JSON definitions to the Mountebank control API on port 2525, configures stubs with predicates and responses, and uses record-playback proxy mode to capture upstream traffic. Use when the project needs a multi-protocol mock server beyond HTTP-only tools like WireMock or MSW.
74
93%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Mountebank is a multi-protocol service-virtualization (mock server) tool: stand up on-demand mocks across many protocols by POSTing JSON imposter definitions to its control API on port 2525, so tests run against deterministic stubs instead of live dependencies.
Per mountebank-readme, supported protocols include:
Docs-domain note (verified 2026-05-04): the canonical
mbtest.orgdomain was hijacked (redirects to an unrelated site), so this skill cites the GitHub repo bbyars/mountebank;mbtest.devis the project's alternate docs domain. Verify both URLs before linking from authored content.
If the team is HTTP-only on the JVM, wiremock-stubs
is the lighter fit. For Node / browser HTTP-only, use
msw-handlers. Mountebank's strength
is multi-protocol breadth; pay the operational cost (a separate
process, port 2525) only when you need it.
mb start or the Docker image); the control API listens on port 2525./imposters with a port, a protocol, and one or more stubs.predicates (path / method / header / body matchers) and responses (the reply to send).GET http://localhost:2525/imposters/<port> and assert HTTP 200 with your stubs listed before pointing tests at it. If it 404s or the stub is missing, the POST body was malformed - fix the JSON and re-POST.proxyOnce proxy response to capture real traffic, then replay offline.DELETE /imposters/<port> in teardown (or restart Mountebank) so stale stubs don't leak between runs.npm install -g @mbtest/mountebank(Per mountebank-readme.)
For Docker-based CI (preferred for runner cleanliness):
docker run --rm -p 2525:2525 -p 4545:4545 bbyars/mountebank:latest startThe control API listens on port 2525; imposter ports (4545
in the example) are configured per imposter.
Mountebank's data model uses these layers:
| Layer | Purpose |
|---|---|
| Imposter | One mock server bound to a port and protocol. |
| Stub | A request matcher attached to an imposter - the response triggered when matched. |
| Predicate | A condition on the incoming request (path, method, header, body, JSON path). |
| Response | The reply Mountebank sends when a stub's predicates match. |
POST to the control API:
curl -X POST http://localhost:2525/imposters \
-H 'Content-Type: application/json' \
-d '{
"port": 4545,
"protocol": "http",
"stubs": [{
"predicates": [{
"and": [
{ "equals": { "method": "GET", "path": "/orders/42" } }
]
}],
"responses": [{
"is": {
"statusCode": 200,
"headers": { "Content-Type": "application/json" },
"body": "{\"order_id\": 42, \"status\": \"shipped\"}"
}
}]
}]
}'After this POST, GET http://localhost:4545/orders/42 returns the
stubbed response.
Beyond equals, Mountebank offers deepEquals, contains,
startsWith / endsWith, matches (regex), exists, the boolean
not / or / and, and inject for a custom JavaScript predicate.
Full operator table:
references/predicates-and-proxying.md.
If a stub has multiple responses, Mountebank cycles through them in order on subsequent matching requests:
{
"stubs": [{
"predicates": [{ "equals": { "method": "GET", "path": "/poll" } }],
"responses": [
{ "is": { "statusCode": 202 } },
{ "is": { "statusCode": 202 } },
{ "is": { "statusCode": 200, "body": "DONE" } }
]
}]
}Three calls: 202, 202, 200, then it cycles back. Useful for modeling polling endpoints.
Point an imposter's response at a real upstream with a proxy block.
proxyOnce records the first response as a stub then replays it,
proxyAlways records every call, and proxyTransparent passes
through without recording. The record-playback config and the full
mode table are in
references/predicates-and-proxying.md.
For Node.js test suites, use the mountebank npm package
programmatically:
import mb from 'mountebank';
const mbServer = await mb.create({ port: 2525, allowInjection: true });
// POST imposter via fetch / axios / the mb client lib
await fetch('http://localhost:2525/imposters', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ port: 4545, protocol: 'http', stubs: [...] }),
});
// Run tests against http://localhost:4545
// Tear down
await fetch('http://localhost:2525/imposters/4545', { method: 'DELETE' });
await mbServer.close();# .github/workflows/integration.yml
- name: Start Mountebank
run: |
npx -p @mbtest/mountebank mb start &
npx wait-on http://localhost:2525
- name: Seed imposters
run: bash scripts/seed-mountebank.sh
- run: npm test
- name: Stop Mountebank
if: always()
run: pkill -f 'mountebank' || trueFor a more robust pattern, run Mountebank in Docker as a sidecar service rather than a background process - kills + cleanup are cleaner.
A test suite depends on a flaky third-party payments API. Record it
once, then run offline. Create a proxy imposter that forwards every
path to the real upstream in proxyOnce mode:
{
"port": 4545,
"protocol": "http",
"stubs": [{
"predicates": [{ "matches": { "path": ".*" } }],
"responses": [{ "proxy": {
"to": "https://payments.example.com",
"mode": "proxyOnce"
}}]
}]
}Run the suite once with the real upstream reachable: each distinct
request hits payments.example.com and Mountebank stores the response
as a stub. On every later run the stored stubs answer and the real API
is never called - the suite is deterministic and offline.
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Hard-coded imposter ports across many tests | Port collisions under parallel CI execution. | Use dynamic ports; capture them from the control API's response. |
| Predicates with regex that match unintended paths | Test passes because the wrong stub responded. | Anchor regexes (^/$); prefer equals over matches when possible. |
allowInjection: true in production-adjacent envs | JS injection is powerful; allows arbitrary code execution. | Only enable for local / CI; never on a shared mock server. |
| Forgetting to delete imposters between test runs | Stale imposters persist across runs; tests interfere. | DELETE /imposters/<port> in test teardown OR restart Mountebank. |
Recording in proxyAlways mode and committing the captures | Captures may include real PII / tokens. | Use proxyOnce; review captured stubs before committing; scrub PII via JSON Schema or jq pre-commit. |
stubFor.mbtest.dev.wiremock-stubs - HTTP-only
alternative on the JVM.msw-handlers - HTTP-only
alternative for browser + Node.