MockServer Ruby Client

Hand-written Ruby client for MockServer with full REST API, fluent builder DSL, and WebSocket callback support.

Installation

Add to your Gemfile:

gem 'mockserver-client', '~> 5.16'

Or install directly:

gem install mockserver-client

Quick Start

require 'mockserver-client'

# Create a client
client = MockServer::Client.new('localhost', 1080)

# Set up an expectation using the fluent API
client.when(
  MockServer::HttpRequest.request(path: '/hello')
    .with_method('GET')
).respond(
  MockServer::HttpResponse.response(body: 'world', status_code: 200)
)

# Verify a request was received
client.verify(
  MockServer::HttpRequest.request(path: '/hello'),
  times: MockServer::VerificationTimes.at_least(1)
)

# Clean up
client.reset
client.close

Block Form

MockServer::Client.new('localhost', 1080) do |client|
  client.when(
    MockServer::HttpRequest.request(path: '/api/test')
  ).respond(
    MockServer::HttpResponse.response(body: '{"status":"ok"}', status_code: 200)
  )
end
# Client is automatically closed when the block exits

WebSocket Callbacks

client = MockServer::Client.new('localhost', 1080)

# Response callback - dynamically generate responses
client.mock_with_callback(
  MockServer::HttpRequest.request(path: '/dynamic'),
  ->(request) {
    MockServer::HttpResponse.new(
      status_code: 200,
      body: "Echo: #{request.path}"
    )
  }
)

# Forward callback - modify requests before forwarding
client.mock_with_forward_callback(
  MockServer::HttpRequest.request(path: '/proxy'),
  ->(request) {
    request.with_header('X-Proxied', 'true')
  }
)

client.close

Models

All 25 domain model classes are available under the MockServer module:

  • Delay, Times, TimeToLive
  • KeyToMultiValue, Body, SocketAddress
  • HttpRequest, HttpResponse, HttpForward, HttpTemplate
  • HttpClassCallback, HttpObjectCallback, HttpError
  • HttpOverrideForwardedRequest, HttpRequestAndHttpResponse
  • ConnectionOptions
  • Expectation, ExpectationId
  • OpenAPIDefinition, OpenAPIExpectation
  • Verification, VerificationSequence, VerificationTimes
  • Ports
  • RequestDefinition (alias for HttpRequest)

LLM Mocking

Fluent builders under MockServer::LLM mock LLM provider endpoints. They produce expectations whose action is carried in the httpLlmResponse field; MockServer re-encodes the provider-agnostic completion into the configured provider's wire shape (OpenAI / Anthropic / Gemini / Bedrock / ...) when serving the request.

require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)

# A single completion mock
MockServer::LLM.llm_mock('/v1/chat/completions')
               .with_provider(MockServer::LLM::Provider::OPENAI)
               .with_model('gpt-4o')
               .responding_with(
                 MockServer::LLM.completion
                                .with_text('Hello!')
                                .with_usage(MockServer::LLM.usage.with_input_tokens(10).with_output_tokens(5))
               )
               .apply_to(client)

Multi-turn conversations use MockServer scenario-state advancement so each turn is served once, in order. Add per-turn predicates with when_* methods and optionally isolate per session with isolate_by:

MockServer::LLM.conversation
               .with_path('/v1/chat/completions')
               .with_provider(MockServer::LLM::Provider::OPENAI)
               .isolate_by(MockServer::LLM.header('x-session-id'))
               .turn.when_latest_message_contains('weather')
                    .responding_with(MockServer::LLM.completion.with_text('It is sunny.'))
               .turn.responding_with(MockServer::LLM.completion.with_text('Anything else?'))
               .apply_to(client)

Failover scenarios script N upstream failures (consecutive identical failures are coalesced) followed by a success; default JSON error bodies are generated per status code unless a custom body is supplied:

MockServer::LLM.llm_failover
               .with_path('/v1/chat/completions')
               .with_provider(MockServer::LLM::Provider::OPENAI)
               .fail_with(429, 2)          # two rate-limit failures
               .fail_with(503)             # then one service-unavailable
               .then_respond_with(MockServer::LLM.completion.with_text('Recovered'))
               .apply_to(client)

Each builder also exposes build to obtain the raw expectation Hash(es) without registering them.

MCP Mocking

MockServer::MCP.mcp_mock builds the set of expectations needed to emulate a Streamable-HTTP MCP (Model Context Protocol) server speaking JSON-RPC 2.0. It generates initialize, ping, notifications/initialized, and per-capability tools, resources, and prompts handlers.

MockServer::MCP.mcp_mock('/mcp')
               .with_server_name('MyServer')
               .with_tool('get_weather')
                 .with_description('Get the weather for a city')
                 .with_input_schema('{"type":"object","properties":{"city":{"type":"string"}}}')
                 .responding_with('72F and sunny')
               .and_then
               .with_resource('file:///config.json')
                 .with_name('config')
                 .with_content('{"debug":true}')
               .and_then
               .with_prompt('greet')
                 .with_argument('name', 'who to greet', true)
                 .responding_with('user', 'Hello there')
               .and_then
               .apply_to(client)

build returns the raw expectation Hashes if you prefer to register them yourself.

Interactive Breakpoints

The client supports matcher-driven interactive breakpoints over the callback WebSocket. Register a breakpoint matcher to pause forwarded/proxied exchanges at specific phases and inspect/modify/continue them via callback handlers.

Register a breakpoint

client = MockServer::Client.new('localhost', 1080)

# REQUEST phase only
bp_id = client.add_request_breakpoint(
  MockServer::HttpRequest.new(path: '/api/.*'),
  ->(request) { request }  # continue unchanged (or return HttpResponse to abort)
)

# REQUEST + RESPONSE
bp_id = client.add_request_and_response_breakpoint(
  MockServer::HttpRequest.new(path: '/api/.*'),
  ->(request) { request },                      # REQUEST handler
  ->(request, response) { response }            # RESPONSE handler
)

# All phases with stream frame handler
bp_id = client.add_breakpoint(
  MockServer::HttpRequest.new(path: '/stream/.*'),
  %w[REQUEST RESPONSE RESPONSE_STREAM INBOUND_STREAM],
  request_handler: ->(request) { request },
  response_handler: ->(request, response) { response },
  stream_frame_handler: ->(frame) { { 'action' => 'CONTINUE' } }
  # Other actions: MODIFY (with body), DROP, INJECT (with body), CLOSE
)

Manage breakpoints

# List all matchers
matchers = client.list_breakpoint_matchers  # {"matchers" => [...]}

# Remove a specific matcher
client.remove_breakpoint_matcher(bp_id)

# Clear all matchers
client.clear_breakpoint_matchers

Start / Launch MockServer

The Ruby client can download and launch a local MockServer instance directly -- no Java installation and no Docker required. The launcher downloads a self-contained platform bundle (mockserver-<version>-<os>-<arch>) from the GitHub Release, verifies its SHA-256, caches it per-user, and starts it.

Quick start

require 'mockserver-client'

# Download (first run) and start MockServer on port 1080
handle = MockServer::BinaryLauncher.start(port: 1080)
puts "MockServer running on port #{handle.port}, PID #{handle.pid}"

# ... use MockServer ...

handle.stop

Just ensure the binary is present

launcher_path = MockServer::BinaryLauncher.ensure_launcher

Specify a version

handle = MockServer::BinaryLauncher.start(port: 1080, version: '7.2.0')

API reference

Method / Class Description
MockServer::BinaryLauncher.ensure_launcher(version:, log:) Download, verify, cache, and return the launcher path. Defaults to MockServer::VERSION.
MockServer::BinaryLauncher.start(port:, version:, extra_args:, log:) Ensure the binary and start MockServer. Returns a ServerHandle.
MockServer::BinaryLauncher::ServerHandle Handle to the running process. Methods: stop(timeout:), running?. Attributes: pid, port, launcher.

Supported platforms

OS Architecture
Linux x86_64, aarch64
macOS (darwin) x86_64, aarch64
Windows x86_64, aarch64

Environment variables

Variable Purpose
MOCKSERVER_BINARY_BASE_URL Mirror host for the release assets (corporate / air-gapped networks)
MOCKSERVER_BINARY_CACHE Override the cache directory (default: ~/.cache/mockserver/binaries on Unix)
MOCKSERVER_SKIP_BINARY_DOWNLOAD Fail instead of downloading (use with a pre-seeded cache in CI)

Version

By default the launcher downloads the MockServer version matching this client gem (currently MockServer::VERSION from lib/mockserver/version.rb). Pass an explicit version: keyword to override.

Using in tests (RSpec)

Require mockserver/rspec to get a shared context that provides a fresh MockServer::Client per example and resets the server before and after each example, so recorded requests, expectations and logs never leak between examples:

# spec_helper.rb
require 'mockserver/rspec'

# any spec tagged :mockserver gets a reset `mockserver` client
RSpec.describe 'my integration', :mockserver do
  it 'records the request' do
    # `mockserver` is the shared, reset client
    mockserver.when(
      MockServer::HttpRequest.request(path: '/hello')
    ).respond(
      MockServer::HttpResponse.response(body: 'world', status_code: 200)
    )
  end
end

Host and port default to 127.0.0.1:1080 and can be overridden with the MOCKSERVER_HOST / MOCKSERVER_PORT environment variables, or by defining a mockserver_host / mockserver_port let in your example group.

License

Apache-2.0

AI Assistant Integration

MockServer includes a built-in MCP (Model Context Protocol) server that enables AI coding assistants to create expectations, verify requests, and debug HTTP traffic programmatically.