7 Protocols, 1 Binary, 0 Dependencies
HTTP, GraphQL, gRPC, WebSocket, MQTT, SSE, and SOAP from a single 43MB Go binary. No JVM, no Node, no Docker required. Here's why and how.
Last year I counted the mock tools running on my laptop. WireMock for HTTP (Java, 200MB Docker image). A custom Node script for WebSocket. Mosquitto for MQTT. A Go stub for gRPC. Four tools, four runtimes, four sets of config files, four ports to remember. I spent more time wiring up mock infrastructure than writing the code it was supposed to help me test.
So I built mockd. One binary:
mockd add http --path /api/orders --status 200
--body '{"orders": [{"id": "{{uuid}}", "total": {{faker.price}}}]}' Created mock: http_c9e4c315168bcfbe / Type: http / Method: GET / Path: /api/orders / Status: 200 curl http://localhost:4280/api/orders {"orders": [{"id": "7e53e8ca-...", "total": 405.87}]} HTTP done. Same binary, six more protocols to go.
All 7 protocols
Every protocol follows the same pattern: mockd add <type>, configure, hit the endpoint. No plugins. No extensions. No separate downloads.
GraphQL
mockd add graphql --path /graphql --operation GetUser
--response '{"id":"1","name":"{{faker.name}}","email":"{{faker.email}}"}' Created mock: graphql_... / Type: graphql / Path: /graphql / Operation: Query.GetUser Test it: curl -X POST http://localhost:4280/graphql -H 'Content-Type: application/json' -d '{"query":"{ GetUser }"}' returns {"data":{"GetUser":{"email":"alice684@mock.io","id":"1","name":"Charlie Brown"}}}.
gRPC
mockd add grpc --proto service.proto --service greeter.Greeter
--rpc-method SayHello --response '{"message": "Hello, {{faker.name}}!"}' Created mock: grpc_... / Type: grpc / Service: greeter.Greeter / Method: SayHello You provide the .proto file, mockd handles reflection and serialization. Port 50051 by default.
WebSocket
mockd add websocket --path /ws/chat
--message '{"event":"connected","ts":"{{now}}"}' Created mock: websocket_... / Type: websocket / Path: /ws/chat MQTT
mockd add mqtt --topic sensors/temp
--payload '{"temp":{{random.float 20.0 35.0}},"unit":"C","ts":"{{now}}"}' Created mock: mqtt_... / Type: mqtt / Topic: sensors/temp / QoS: 0 Built-in MQTT broker on port 1883. No Mosquitto required.
SSE (Server-Sent Events)
mockd add http --path /events --sse
--sse-event 'update:{"ts":"{{now}}","cpu":{{random.int 10 95}}}' Created mock: http_... / Type: http / Method: GET / Path: /events curl -N http://localhost:4280/events streams event:update / data:{"ts":"2026-03-07T18:26:03-06:00","cpu":70}.
SOAP
mockd add soap --path /service --operation GetUser --status 200
--response '<User><Id>1</Id><Name>{{faker.name}}</Name></User>' Created mock: soap_... / Type: soap / Path: /service / Operation: GetUser Send a SOAP envelope with SOAPAction: GetUser, get back a properly wrapped response.
All together
ID TYPE PATH METHOD STATUS ENABLED
list-users http /api/users GET 200 true
graphql_917e99253... graphql /graphql GQL - true
websocket_91720db... websocket /ws/chat WS - true
mqtt_4c28d4810103... mqtt :1883 MQTT - true
http_32897b6a3bd8... http /events GET - true
soap_edc045dfba00... soap /service SOAP - true
grpc_7a2ea7f56902... grpc :50051 gRPC - true Seven protocols. One process. One config file. One set of logs.
Why Go, why single binary
I get asked this a lot. Go was chosen for three reasons:
Static binary. CGO_ENABLED=0 go build produces a single statically-linked executable. No shared libraries, no runtime, no interpreter. Copy it to any Linux/macOS/Windows machine and it runs. Docker image is distroless/static — no shell, no libc.
Goroutines. Each protocol listener runs in its own goroutine. HTTP, gRPC, MQTT, WebSocket — same process, none blocking each other. In Node that’s clustering. In Java it’s thread pools. In Go it’s go serve().
Cross-compilation. GOOS=darwin GOARCH=arm64 go build from a Linux CI runner. Mockd ships six OS/arch binaries from a single GitHub Actions workflow.
The result is a 43MB binary containing all seven protocol servers, 34 faker functions, an admin API, and a request logger. No runtime to download. No plugins to install.
That matters in three places: CI/CD (curl -fsSL https://get.mockd.io | sh && mockd start — two seconds vs. pulling a 200MB Docker image), laptop onboarding (“install mockd” vs. “install Java 17 and Docker Desktop”), and air-gapped networks (a single binary can be SCP’d to a bastion host — try that with node_modules).
Shared internals
The seven protocols share core infrastructure:
- Template engine —
{{faker.name}},{{uuid}},{{now}},{{random.int 1 100}}— same 34 faker functions across every protocol - Config — one YAML or JSON file defines all mocks across all protocols
- Request logging — HTTP, GraphQL, gRPC, WebSocket, MQTT traffic in one unified log
- Import — OpenAPI, Postman, HAR, WireMock, cURL, Mockoon, and WSDL formats
- Chaos — 10 profiles and 12 fault types apply across HTTP, GraphQL, gRPC, and SOAP
- MCP — 18 tools give AI agents programmatic access to everything
How this compares
| Tool | Language | Protocols | Size | Runtime |
|---|---|---|---|---|
| mockd | Go | 7 (HTTP, GraphQL, gRPC, WS, MQTT, SSE, SOAP) | 43MB binary | None |
| WireMock | Java | HTTP (extensions for others) | ~200MB Docker | JVM |
| Mockoon | Electron/Node | HTTP, GraphQL | ~200MB app | Node |
| json-server | Node | REST | ~2MB (+ Node) | Node |
| Prism | Node | HTTP (OpenAPI validation) | ~50MB (+ Node) | Node |
| MockServer | Java | HTTP/HTTPS | ~200MB Docker | JVM |
WireMock is battle-tested with a huge plugin ecosystem. Mockoon has a desktop UI nothing else matches. json-server is dead simple for REST prototyping. Mockd’s angle: all seven protocols from one tool, zero runtime dependencies, sub-second startup.
The honest part
43MB is not small. It’s smaller than pulling a JVM or Node runtime, but it’s not a 5MB CLI tool. Every protocol adds weight — gRPC alone brings in protobuf libraries. I could strip features to shrink the binary, but that defeats the point.
gRPC requires a .proto file. There’s no way around this. gRPC is contract-driven — you can’t hand-wave the schema like you can with HTTP. No .proto file, no gRPC mock. That’s a protocol constraint, not a mockd constraint, but it still adds friction.
The MQTT broker is basic. QoS 0 and 1, topic wildcards, pub/sub. No clustering, no persistence, no QoS 2. For dev/test it’s fine. For benchmarking against a real broker, use Mosquitto or EMQX.
SOAP exists because it has to. Banks, insurance, healthcare — I didn’t add SOAP because I enjoy XML. I added it because developers at those companies deserve mock tooling that isn’t “install a 2008 Java web server.”
Install
brew install getmockd/tap/mockd
# or
curl -fsSL https://get.mockd.io | sh Start the server and start adding mocks. Every protocol. One binary. Zero dependencies.
Learn more
- Quickstart guide — create your first mock in under 60 seconds
- All 7 protocols — full documentation for each protocol
- Import from OpenAPI — convert existing API specs to mocks
- Chaos engineering — fault injection across all protocols
Links
- GitHub: github.com/getmockd/mockd (Apache 2.0)
- Docs: docs.mockd.io
- Install:
brew install getmockd/tap/mockdorcurl -fsSL https://get.mockd.io | sh
Try mockd
Multi-protocol API mock server. HTTP, gRPC, GraphQL, WebSocket, MQTT, SSE, SOAP.