# MockServer

> MockServer is an open-source HTTP(S) mock server, recording proxy, and **MCP (Model Context Protocol) server for AI coding assistants**. It enables easy mocking of any system you integrate with via HTTP or HTTPS, supports request matching with JSON, XPath, regex, and JSON Schema, acts as a recording proxy for introspection of traffic between services, and exposes a built-in MCP endpoint at `/mockserver/mcp` so AI assistants like Claude Code, Cursor, Windsurf, Cline, and OpenCode can create expectations, verify requests, retrieve recorded traffic, and debug mismatches through natural language. Available as a Docker container, Maven dependency, npm module, or standalone JAR. Clients are available in Java, JavaScript/TypeScript, Python, and Ruby. Licensed under Apache 2.0 — AI training, retrieval, and citation are explicitly welcomed; see https://www.mock-server.com/ai.txt. Website: https://www.mock-server.com | GitHub: https://github.com/mock-server/mockserver

## Quick Start

Start MockServer with Docker:

    docker run -d --rm -p 1080:1080 mockserver/mockserver

Create a simple expectation:

    curl -X PUT http://localhost:1080/mockserver/expectation -d '{
      "httpRequest": {
        "method": "GET",
        "path": "/api/users"
      },
      "httpResponse": {
        "statusCode": 200,
        "headers": {"Content-Type": ["application/json"]},
        "body": {"type": "JSON", "json": "[{\"id\": 1, \"name\": \"Alice\"}]"}
      }
    }'

Test it:

    curl http://localhost:1080/api/users

Verify the request was received:

    curl -X PUT http://localhost:1080/mockserver/verify -d '{
      "httpRequest": {"method": "GET", "path": "/api/users"},
      "times": {"atLeast": 1}
    }'

Reset all state when done:

    curl -X PUT http://localhost:1080/mockserver/reset

## Core Concepts

MockServer works through **expectations**. Each expectation has:
- **httpRequest** (matcher): defines which incoming requests to match
- **action** (one of): httpResponse, httpForward, httpError, httpOverrideForwardedRequest, httpResponseTemplate, httpForwardTemplate, httpResponseClassCallback, httpForwardClassCallback, httpResponseObjectCallback, httpForwardObjectCallback
- **times** (optional): how many times the expectation can be matched (default: unlimited)
- **timeToLive** (optional): how long the expectation remains active
- **id** (optional): unique identifier for updates; if an expectation with the same id exists, it is replaced
- **priority** (optional): integer; higher-priority expectations are matched first

When a request arrives, MockServer evaluates expectations in priority order (highest first), then insertion order. The first matching expectation's action is executed. If no match is found and proxy mode is enabled, the request is forwarded.

## REST API Reference

Most control plane endpoints use the PUT method. Some (e.g. clock status, configuration, OpenAPI spec) also support GET. The base path is `/mockserver`.

### PUT /mockserver/expectation

Create or update expectations. If the body contains an expectation with an `id` that matches an existing expectation, that expectation is replaced (upsert).

Request body: a single Expectation object or an array of Expectation objects.

Response: 201 Created with JSON array of upserted expectations.

Example - simple response:

    PUT /mockserver/expectation
    Content-Type: application/json

    {
      "httpRequest": {
        "method": "POST",
        "path": "/api/login"
      },
      "httpResponse": {
        "statusCode": 200,
        "body": "{\"token\": \"abc123\"}"
      },
      "times": {"remainingTimes": 1, "unlimited": false}
    }

Example - with id and priority for upsert:

    {
      "id": "login-mock",
      "priority": 10,
      "httpRequest": {"path": "/api/login"},
      "httpResponse": {"statusCode": 200, "body": "{\"token\": \"xyz\"}"}
    }

Example - unlimited times with TTL:

    {
      "httpRequest": {"path": "/health"},
      "httpResponse": {"statusCode": 200, "body": "OK"},
      "times": {"unlimited": true},
      "timeToLive": {"timeUnit": "HOURS", "timeToLive": 1, "unlimited": false}
    }

### PUT /mockserver/openapi

Create expectations from an OpenAPI or Swagger specification.

Request body:

    {
      "specUrlOrPayload": "https://example.com/api/openapi.yaml",
      "operationsAndResponses": {
        "listUsers": "200",
        "getUser": "200",
        "createUser": "201"
      }
    }

The `specUrlOrPayload` field can be a URL string or an inline OpenAPI spec object. The `operationsAndResponses` field is optional; if omitted, expectations are created for all operations with their first defined response.

Response: 201 Created with the generated expectations.

### PUT /mockserver/clear

Clear expectations and/or recorded requests that match a request matcher.

Query parameter `type` (optional, default "all"):
- `all` - clear both expectations and log (recorded requests)
- `log` - clear only recorded request log
- `expectations` - clear only expectations

Request body (optional): a request matcher to select what to clear. If empty, clears everything.

Example - clear all state matching a path:

    PUT /mockserver/clear
    Content-Type: application/json

    {"path": "/api/users"}

Example - clear only expectations:

    PUT /mockserver/clear?type=expectations
    Content-Type: application/json

    {"path": "/api/users"}

Example - clear by expectation id:

    PUT /mockserver/clear
    Content-Type: application/json

    {"id": "login-mock"}

Response: 200 OK.

### PUT /mockserver/reset

Reset all expectations, recorded requests, and logs. No request body required.

Response: 200 OK.

### PUT /mockserver/retrieve

Retrieve recorded requests, active expectations, recorded expectations, request-response pairs, or log messages.

Query parameters:
- `type` (optional, default "requests"):
  - `requests` - recorded requests
  - `request_responses` - recorded request-response pairs (includes timestamp)
  - `recorded_expectations` - expectations recorded from proxied traffic
  - `active_expectations` - currently active expectations
  - `logs` - log messages
- `format` (optional, default "json"):
  - `json` - JSON format
  - `java` - Java code format
  - `log_entries` - raw log entry format (for logs type)

Request body (optional): a request matcher to filter results. If empty, returns all.

Example - retrieve all recorded requests:

    PUT /mockserver/retrieve?type=requests
    Content-Type: application/json

    {}

Response (200 OK):

    [
      {
        "method": "GET",
        "path": "/api/users",
        "headers": {"Host": ["localhost:1080"]},
        "keepAlive": true,
        "secure": false
      }
    ]

Example - retrieve active expectations:

    PUT /mockserver/retrieve?type=active_expectations

Example - retrieve request-response pairs for a specific path:

    PUT /mockserver/retrieve?type=request_responses
    Content-Type: application/json

    {"path": "/api/.*"}

Response:

    [
      {
        "httpRequest": {"method": "GET", "path": "/api/users"},
        "httpResponse": {"statusCode": 200, "body": "[...]"},
        "timestamp": "2024-01-15T10:30:00.000Z"
      }
    ]

Example - retrieve logs:

    PUT /mockserver/retrieve?type=logs
    Content-Type: application/json

    {"path": "/api/users"}

### PUT /mockserver/verify

Verify that a request was received a specific number of times.

Request body:

    {
      "httpRequest": {
        "method": "GET",
        "path": "/api/users"
      },
      "times": {
        "atLeast": 1,
        "atMost": 3
      }
    }

Either `httpRequest` or `expectationId` must be provided. The `times` field can specify `atLeast`, `atMost`, or both. If `times` is omitted, defaults to `atLeast: 1`.

Optional field: `maximumNumberOfRequestToReturnInVerificationFailure` (integer) - limits how many non-matching requests are listed in the failure message.

Response:
- 202 Accepted: verification passed
- 406 Not Acceptable: verification failed, body contains failure description in text/plain format

Example - verify exact count:

    {
      "httpRequest": {"path": "/api/login"},
      "times": {"atLeast": 2, "atMost": 2}
    }

Example - verify by expectation id:

    {
      "expectationId": {"id": "login-mock"},
      "times": {"atLeast": 1}
    }

### PUT /mockserver/verifySequence

Verify that requests were received in a specific order.

Request body using httpRequests:

    {
      "httpRequests": [
        {"path": "/api/login"},
        {"path": "/api/users"},
        {"path": "/api/logout"}
      ]
    }

Or using expectation IDs:

    {
      "expectationIds": [
        {"id": "login-mock"},
        {"id": "users-mock"}
      ]
    }

Optional: `maximumNumberOfRequestToReturnInVerificationFailure` (integer).

Response:
- 202 Accepted: sequence was received in specified order
- 406 Not Acceptable: sequence was not received in order, body contains failure description

### PUT /mockserver/status

Returns the ports MockServer is listening on.

No request body required.

Response (200 OK):

    {"ports": [1080]}

### PUT /mockserver/stop

Stop the running MockServer process (only supported on Netty version).

No request body required.

Response: 200 OK.

### PUT /mockserver/bind

Bind additional listening ports (only supported on Netty version).

Request body:

    {"ports": [1081, 1082]}

Use `0` for dynamically allocated ports.

Response (200 OK): the ports that were actually bound.

### PUT /mockserver/clock

Control the server clock for deterministic time-based testing. Freeze the clock at a specific instant, advance it by a duration, or reset it to real wall-clock time. The controllable clock affects response template date/time helpers (`now_iso_8601`, `now_epoch`, `now_rfc_1123`, `dates.*`) and expectation TimeToLive expiry. Event-log timestamps and JWT issuance are not affected.

Request body:

    {"action": "freeze", "instant": "2025-01-15T09:30:00Z"}
    {"action": "advance", "durationMillis": 3600000}
    {"action": "reset"}

- `action` (required): one of `freeze`, `advance`, `reset`
- `instant` (optional, freeze only): ISO-8601 instant to freeze at; omit to freeze at current time
- `durationMillis` (required for advance): positive number of milliseconds to advance by

Response (200 OK):

    {"status": "freeze", "currentInstant": "2025-01-15T09:30:00Z", "currentEpochMillis": 1736933400000}

Response (400 Bad Request): if action is missing, instant is malformed, or durationMillis is non-positive.

### GET /mockserver/clock

Returns the current server clock status.

No request body required.

Response (200 OK):

    {"currentInstant": "2025-01-15T09:30:00Z", "currentEpochMillis": 1736933400000, "frozen": true}

## Request Matching

### Match Fields

All fields are optional. If a field is omitted, it matches any value.

**method** - HTTP method. Can be a plain string or a matcher object:

    "method": "GET"
    "method": {"value": "P.*", "not": false}

**path** - URL path. Supports exact match, regex, or JSON Schema:

    "path": "/api/users"
    "path": "/api/users/[0-9]+"

**pathParameters** - path parameters (for parameterized paths):

    "pathParameters": {"userId": ["123"]}

**queryStringParameters** - URL query parameters:

    "queryStringParameters": {"page": ["1"], "limit": ["10"]}

Or using the array format:

    "queryStringParameters": [
      {"name": "page", "values": ["1"]},
      {"name": "limit", "values": ["10"]}
    ]

**headers** - HTTP request headers:

    "headers": {"Authorization": ["Bearer .*"], "Content-Type": ["application/json"]}

**cookies** - HTTP cookies:

    "cookies": {"session": "abc123"}

Or array format:

    "cookies": [{"name": "session", "value": "abc123"}]

**body** - request body matcher (see Body Matchers section)

**secure** - boolean, if true matches only HTTPS requests

**keepAlive** - boolean, matches connection keep-alive setting

**protocol** - matches HTTP protocol version: `HTTP_1_1` or `HTTP_2`

**socketAddress** - matches the remote socket address:

    "socketAddress": {"host": "example.com", "port": 443, "scheme": "HTTPS"}

### Key Match Style

For queryStringParameters and headers, the `keyMatchStyle` controls matching behavior:
- `SUB_SET` (default): the request must contain at least the specified parameters (may have others)
- `MATCHING_KEY`: the request must contain exactly the specified parameters and no others

Example:

    "queryStringParameters": {
      "keyMatchStyle": "MATCHING_KEY",
      "page": ["1"]
    }

### String Matchers

String fields (method, path, header values, query parameter values, cookie values) support several matcher types:

Plain string (exact match):

    "path": "/api/users"

Regex (Java regex syntax, automatically detected when string contains regex characters):

    "path": "/api/users/[0-9]+"

Negation (matches when the value does NOT match):

    "path": {"not": true, "value": "/api/admin.*"}

Optional fields (field may or may not be present):

    "headers": {"Authorization": {"optional": true, "value": "Bearer .*"}}

JSON Schema for string values:

    "path": {"schema": {"type": "string", "pattern": "^/api/.*"}}

### Body Matchers

The body field in httpRequest supports multiple matcher types:

**Plain string** (substring match):

    "body": "hello"

**STRING type** (exact or substring match):

    "body": {"type": "STRING", "string": "hello world", "subString": true}

With content type:

    "body": {"type": "STRING", "string": "hello", "contentType": "text/plain; charset=utf-8"}

**REGEX type**:

    "body": {"type": "REGEX", "regex": ".*user.*name.*"}

**JSON type** (partial or strict match):

    "body": {
      "type": "JSON",
      "json": "{\"name\": \"Alice\"}",
      "matchType": "ONLY_MATCHING_FIELDS"
    }

The `matchType` can be:
- `ONLY_MATCHING_FIELDS` (default): the request body must contain at least the specified fields
- `STRICT`: the request body must match exactly (same fields, same order for arrays)

**JSON shorthand** (object body without type field is treated as JSON with ONLY_MATCHING_FIELDS):

    "body": {"name": "Alice", "age": 30}

**JSON_SCHEMA type**:

    "body": {
      "type": "JSON_SCHEMA",
      "jsonSchema": {
        "type": "object",
        "required": ["name"],
        "properties": {
          "name": {"type": "string"},
          "age": {"type": "integer", "minimum": 0}
        }
      }
    }

**JSON_PATH type**:

    "body": {"type": "JSON_PATH", "jsonPath": "$.users[?(@.name == 'Alice')]"}

**XML type**:

    "body": {"type": "XML", "xml": "<user><name>Alice</name></user>"}

**XML_SCHEMA type**:

    "body": {
      "type": "XML_SCHEMA",
      "xmlSchema": "<?xml version=\"1.0\"?>..."
    }

**XPATH type**:

    "body": {"type": "XPATH", "xpath": "/user/name[text()='Alice']"}

**BINARY type** (base64 encoded):

    "body": {"type": "BINARY", "base64Bytes": "aGVsbG8="}

**PARAMETERS type** (for form-encoded bodies):

    "body": {
      "type": "PARAMETERS",
      "parameters": {"username": ["alice"], "password": ["secret"]}
    }

**Negation** works with all body types:

    "body": {"not": true, "type": "JSON_PATH", "jsonPath": "$.error"}

### OpenAPI Request Matcher

Instead of specifying individual fields, match against an OpenAPI operation:

    "httpRequest": {
      "specUrlOrPayload": "https://example.com/openapi.yaml",
      "operationId": "getUser"
    }

## Response Actions

### Literal Response (httpResponse)

Return a fixed response:

    {
      "httpRequest": {"path": "/api/hello"},
      "httpResponse": {
        "statusCode": 200,
        "reasonPhrase": "OK",
        "headers": {
          "Content-Type": ["application/json"],
          "Cache-Control": ["no-cache"]
        },
        "cookies": {"session": "abc123"},
        "body": "{\"message\": \"hello\"}",
        "delay": {"timeUnit": "MILLISECONDS", "value": 500},
        "connectionOptions": {
          "closeSocket": false,
          "keepAliveOverride": true,
          "suppressContentLengthHeader": false
        }
      }
    }

Response body types are similar to request body types. Common formats:

Plain string:

    "body": "hello world"

JSON (explicit type):

    "body": {"type": "JSON", "json": "{\"key\": \"value\"}"}

JSON (shorthand - objects without type field are serialized as JSON):

    "body": {"key": "value"}

XML:

    "body": {"type": "XML", "xml": "<response><status>ok</status></response>"}

Binary:

    "body": {"type": "BINARY", "base64Bytes": "aGVsbG8=", "contentType": "application/octet-stream"}

Connection options:
- `closeSocket` (boolean): close socket after response
- `closeSocketDelay` (Delay): delay before closing socket
- `keepAliveOverride` (boolean): override connection keep-alive
- `suppressContentLengthHeader` (boolean): don't send content-length
- `contentLengthHeaderOverride` (integer): override content-length value
- `suppressConnectionHeader` (boolean): don't send connection header
- `chunkSize` (integer): chunk response body

### Forward (httpForward)

Forward the request to another server:

    {
      "httpRequest": {"path": "/api/.*"},
      "httpForward": {
        "host": "backend.example.com",
        "port": 8080,
        "scheme": "HTTP",
        "delay": {"timeUnit": "MILLISECONDS", "value": 100}
      }
    }

### Override Forward (httpOverrideForwardedRequest)

Forward with modifications to the request and/or response:

    {
      "httpRequest": {"path": "/api/users"},
      "httpOverrideForwardedRequest": {
        "requestOverride": {
          "headers": {"X-Forwarded-For": ["10.0.0.1"]}
        },
        "requestModifier": {
          "path": {"regex": "^/api", "substitution": "/v2/api"},
          "headers": {
            "add": {"X-Request-Id": ["abc123"]},
            "remove": ["Cookie"]
          },
          "queryStringParameters": {
            "add": {"source": ["proxy"]},
            "replace": {"version": ["2"]},
            "remove": ["debug"]
          },
          "cookies": {
            "add": {"tracking": "enabled"},
            "remove": ["session"]
          }
        },
        "responseOverride": {
          "headers": {"X-Proxy": ["MockServer"]}
        },
        "responseModifier": {
          "headers": {
            "add": {"X-Served-By": ["mockserver"]},
            "remove": ["Server"]
          },
          "cookies": {
            "add": {"proxy-session": "xyz"},
            "remove": ["internal-cookie"]
          }
        },
        "delay": {"timeUnit": "MILLISECONDS", "value": 50}
      }
    }

### Response Template (httpResponseTemplate)

Generate dynamic responses using templates. Template types: VELOCITY, JAVASCRIPT, MUSTACHE.

Velocity example:

    {
      "httpRequest": {"path": "/api/users/{userId}"},
      "httpResponseTemplate": {
        "templateType": "VELOCITY",
        "template": "{\"statusCode\":200,\"body\":\"{\\\"id\\\": \\\"$!request.pathParameters['userId'][0]\\\", \\\"name\\\": \\\"User $!request.pathParameters['userId'][0]\\\"}\"}",
        "delay": {"timeUnit": "MILLISECONDS", "value": 0}
      }
    }

JavaScript example:

    {
      "httpRequest": {"path": "/api/echo"},
      "httpResponseTemplate": {
        "templateType": "JAVASCRIPT",
        "template": "return { statusCode: 200, body: JSON.stringify(request) };"
      }
    }

Mustache example:

    {
      "httpRequest": {"path": "/api/greeting"},
      "httpResponseTemplate": {
        "templateType": "MUSTACHE",
        "template": "{\"statusCode\": 200, \"body\": \"Hello {{request.queryStringParameters.name.0}}\"}"
      }
    }

Available template variables: `request` (the incoming HttpRequest object), with sub-fields: method, path, pathParameters, queryStringParameters, headers, cookies, body, secure, keepAlive.

### Error (httpError)

Return a simulated error (drop connection, send random bytes):

    {
      "httpRequest": {"path": "/api/error"},
      "httpError": {
        "dropConnection": true,
        "responseBytes": "cmFuZG9tIGJ5dGVz"
      }
    }

### Class Callback (httpResponseClassCallback / httpForwardClassCallback)

Invoke a Java class on the server classpath:

    {
      "httpRequest": {"path": "/api/callback"},
      "httpResponseClassCallback": {
        "callbackClass": "com.example.MyCallback"
      }
    }

The class must implement `org.mockserver.mock.action.ExpectationResponseCallback` (for response) or `ExpectationForwardCallback` (for forward).

### Object Callback (httpResponseObjectCallback / httpForwardObjectCallback)

Invoke a callback via WebSocket (used by client libraries for closures/lambdas):

    {
      "httpRequest": {"path": "/api/dynamic"},
      "httpResponseObjectCallback": {
        "clientId": "websocket-client-id",
        "responseCallback": true
      }
    }

This is typically not created manually but through client libraries (Java, JavaScript, Python, Ruby) that register callback functions over WebSocket connections.

## Times and TimeToLive

### Times

Controls how many times an expectation can be matched:

    "times": {"remainingTimes": 5, "unlimited": false}

Unlimited (default):

    "times": {"unlimited": true}

Once:

    "times": {"remainingTimes": 1, "unlimited": false}

### TimeToLive

Controls how long an expectation remains active:

    "timeToLive": {
      "timeUnit": "MINUTES",
      "timeToLive": 30,
      "unlimited": false
    }

Supported timeUnit values: DAYS, HOURS, MINUTES, SECONDS, MILLISECONDS, MICROSECONDS, NANOSECONDS.

Unlimited (default):

    "timeToLive": {"unlimited": true}

## Configuration Properties

Properties can be set via (in order of precedence):
1. Java code (highest)
2. System property (-Dmockserver.propertyName=value)
3. Property file (mockserver.properties)
4. Environment variable (MOCKSERVER_PROPERTY_NAME) (lowest)

### Memory Configuration

**maxExpectations**
Maximum expectations held in the in-memory ring buffer. Oldest/lowest-priority expectations are overwritten when limit is reached.
- Default: min(free heap KB / 10, 15000) — automatically calculated from available JVM heap
- System property: mockserver.maxExpectations
- Environment variable: MOCKSERVER_MAX_EXPECTATIONS
- Each expectation typically uses 4-10 KB of heap

**maxLogEntries**
Maximum log entries in the in-memory ring buffer. Includes recorded requests, match failures, and other log entries.
- Default: min(free heap KB / 8, 100000) — automatically calculated from available JVM heap
- System property: mockserver.maxLogEntries
- Environment variable: MOCKSERVER_MAX_LOG_ENTRIES
- Each HTTP request generates 2-3 log entries; each entry uses 4-10 KB

**maxWebSocketExpectations**
Maximum remote method callbacks (web sockets) registered for expectations.
- Default: 1500
- Environment variable: MOCKSERVER_MAX_WEB_SOCKET_EXPECTATIONS

**outputMemoryUsageCsv**
Output JVM memory usage metrics to CSV file periodically.
- Default: false
- Environment variable: MOCKSERVER_OUTPUT_MEMORY_USAGE_CSV

**memoryUsageCsvDirectory**
Directory to output memory usage CSV files.
- Default: "."
- Environment variable: MOCKSERVER_MEMORY_USAGE_CSV_DIRECTORY

### Logging Configuration

**logLevel**
Minimum log level to record and output. Values: TRACE, DEBUG, INFO, WARN, ERROR, OFF
- Default: INFO
- System property: mockserver.logLevel
- Environment variable: MOCKSERVER_LOG_LEVEL
- Lower levels capture more entries, particularly TRACE

**disableSystemOut**
Disable logging to standard output.
- Default: false
- Environment variable: MOCKSERVER_DISABLE_SYSTEM_OUT

**disableLogging**
Disable all logging and processing of log events.
- Default: false
- Environment variable: MOCKSERVER_DISABLE_LOGGING

**logLevelOverrides**
Override the log level for specific categories of log events or individual log message types.
Keys can be category group names (MATCHING, REQUEST_LIFECYCLE, EXPECTATION_MANAGEMENT, VERIFICATION, SERVER, GENERAL) or individual LogMessageType names (e.g., EXPECTATION_NOT_MATCHED).
Values are SLF4J log level names (TRACE, DEBUG, INFO, WARN, ERROR).
Resolution order: individual type override > category group override > global logLevel.
Only affects stdout/SLF4J output and dashboard UI; event log storage for verification is unaffected.
Note: overrides can only suppress events already captured at the global logLevel. They cannot increase verbosity beyond what the global logLevel generates. Set the global logLevel low enough first, then use overrides to suppress noisy categories.
- Default: {} (empty, global logLevel applies to all)
- System property: mockserver.logLevelOverrides
- Environment variable: MOCKSERVER_LOG_LEVEL_OVERRIDES
- Format: JSON map, e.g., {"MATCHING":"WARN","EXPECTATION_MATCHED":"INFO"}
- Category groups:
  - MATCHING: EXPECTATION_MATCHED, EXPECTATION_NOT_MATCHED, NO_MATCH_RESPONSE
  - REQUEST_LIFECYCLE: RECEIVED_REQUEST, FORWARDED_REQUEST, EXPECTATION_RESPONSE, TEMPLATE_GENERATED
  - EXPECTATION_MANAGEMENT: CREATED_EXPECTATION, UPDATED_EXPECTATION, REMOVED_EXPECTATION, CLEARED
  - VERIFICATION: VERIFICATION, VERIFICATION_FAILED, VERIFICATION_PASSED, RETRIEVED
  - SERVER: SERVER_CONFIGURATION, AUTHENTICATION_FAILED, OPENAPI_RESPONSE_VALIDATION_FAILED
  - GENERAL: TRACE, DEBUG, INFO, WARN, ERROR, EXCEPTION

**detailedMatchFailures**
Include detailed reasons why each non-matching field did not match in log entries.
- Default: true
- Environment variable: MOCKSERVER_DETAILED_MATCH_FAILURES

**metricsEnabled**
Enable recording of metrics exposed via /mockserver/metrics in Prometheus format.
- Default: false
- Environment variable: MOCKSERVER_METRICS_ENABLED

### Scalability Configuration

**nioEventLoopThreadCount**
Number of threads for the main event loop (reading requests, writing control plane responses, managing expectations).
- Default: 5
- Environment variable: MOCKSERVER_NIO_EVENT_LOOP_THREAD_COUNT
- Read at startup only

**actionHandlerThreadCount**
Number of threads for the action handler pool (serializing responses, handling delays, executing callbacks).
- Default: max(5, available processors)
- Environment variable: MOCKSERVER_ACTION_HANDLER_THREAD_COUNT

**clientNioEventLoopThreadCount**
Number of threads for client event loop when calling downstream.
- Default: 5
- Environment variable: MOCKSERVER_CLIENT_NIO_EVENT_LOOP_THREAD_COUNT

**webSocketClientEventLoopThreadCount**
Number of threads per WebSocket callback client.
- Default: 5
- Environment variable: MOCKSERVER_WEB_SOCKET_CLIENT_EVENT_LOOP_THREAD_COUNT

**maxFutureTimeout**
Maximum wait time (ms) for any future (e.g., WebSocket callback responses).
- Default: 90000
- Environment variable: MOCKSERVER_MAX_FUTURE_TIMEOUT

**matchersFailFast**
If true, request matchers fail on the first non-matching field. If false, all fields are compared.
- Default: true
- Environment variable: MOCKSERVER_MATCHERS_FAIL_FAST

### Socket Configuration

**maxSocketTimeout**
Maximum response time (ms) from a socket.
- Default: 20000
- Environment variable: MOCKSERVER_MAX_SOCKET_TIMEOUT

**socketConnectionTimeout**
Maximum socket connection time (ms).
- Default: 20000
- Environment variable: MOCKSERVER_SOCKET_CONNECTION_TIMEOUT

**alwaysCloseSocketConnections**
Always close connections after response (ignore keep-alive).
- Default: false
- Environment variable: MOCKSERVER_ALWAYS_CLOSE_SOCKET_CONNECTIONS

**localBoundIP**
Local IP address to bind for accepting socket connections.
- Default: 0.0.0.0
- Environment variable: MOCKSERVER_LOCAL_BOUND_IP

### HTTP Request Parsing Configuration

**maxInitialLineLength**
Maximum size of the first line of an HTTP request.
- Default: Integer.MAX_VALUE
- Environment variable: MOCKSERVER_MAX_INITIAL_LINE_LENGTH

**maxHeaderSize**
Maximum size of HTTP request headers.
- Default: Integer.MAX_VALUE
- Environment variable: MOCKSERVER_MAX_HEADER_SIZE

**maxChunkSize**
Maximum HTTP chunk size.
- Default: Integer.MAX_VALUE
- Environment variable: MOCKSERVER_MAX_CHUNK_SIZE

**useSemicolonAsQueryParameterSeparator**
Treat semicolons as query parameter separators.
- Default: true
- Environment variable: MOCKSERVER_USE_SEMICOLON_AS_QUERY_PARAMETER_SEPARATOR

**assumeAllRequestsAreHttp**
If false, requests with non-standard methods are assumed binary.
- Default: false
- Environment variable: MOCKSERVER_ASSUME_ALL_REQUESTS_ARE_HTTP

### CORS Configuration

**enableCORSForAPI**
Enable CORS for MockServer REST API (for browser-based JavaScript).
- Default: false
- Environment variable: MOCKSERVER_ENABLE_CORS_FOR_API

**enableCORSForAllResponses**
Enable CORS for all responses (REST API and expectation responses).
- Default: false
- Environment variable: MOCKSERVER_ENABLE_CORS_FOR_ALL_RESPONSES

**corsAllowOrigin**
Value for access-control-allow-origin header.
- Default: ""
- Environment variable: MOCKSERVER_CORS_ALLOW_ORIGIN

**corsAllowMethods**
Value for access-control-allow-methods header.
- Default: ""
- Environment variable: MOCKSERVER_CORS_ALLOW_METHODS

**corsAllowHeaders**
Value for access-control-allow-headers and access-control-expose-headers.
- Default: ""
- Environment variable: MOCKSERVER_CORS_ALLOW_HEADERS

**corsAllowCredentials**
Value for access-control-allow-credentials. When true, access-control-allow-origin uses the request's origin header.
- Default: false
- Environment variable: MOCKSERVER_CORS_ALLOW_CREDENTIALS

**corsMaxAgeInSeconds**
Value for access-control-max-age header.
- Default: 0
- Environment variable: MOCKSERVER_CORS_MAX_AGE_IN_SECONDS

### Initialization and Persistence Configuration

**initializationClass**
Java class used to initialize expectations at startup.
- Default: null
- Environment variable: MOCKSERVER_INITIALIZATION_CLASS

**initializationJsonPath**
Path to JSON file used to initialize expectations at startup. The file should contain a JSON array of Expectation objects.
- Default: null
- Environment variable: MOCKSERVER_INITIALIZATION_JSON_PATH

**initializationOpenAPIPath**
Path to OpenAPI spec file (YAML or JSON) used to initialize expectations at startup. MockServer generates expectations for each operation in the spec with example responses derived from the schema. Supports file glob patterns for watching multiple files.
- Default: null
- Environment variable: MOCKSERVER_INITIALIZATION_OPENAPI_PATH

**watchInitializationJson**
Watch initialization JSON and OpenAPI files for changes. When a change is detected, expectations are created, removed, or updated by matching against their id/key.
- Default: false
- Environment variable: MOCKSERVER_WATCH_INITIALIZATION_JSON

**persistExpectations**
Persist expectations as JSON, updated whenever expectation state changes.
- Default: false
- Environment variable: MOCKSERVER_PERSIST_EXPECTATIONS

**persistedExpectationsPath**
File path for persisted expectations JSON file.
- Default: persistedExpectations.json
- Environment variable: MOCKSERVER_PERSISTED_EXPECTATIONS_PATH

### Verification Configuration

**maximumNumberOfRequestToReturnInVerificationFailure**
Maximum number of requests included in verification failure results.
- Default: 10
- Environment variable: MOCKSERVER_MAXIMUM_NUMBER_OF_REQUESTS_TO_RETURN_IN_VERIFICATION_FAILURE

### Proxying Configuration

**attemptToProxyIfNoMatchingExpectation**
When no matching expectation is found and the host header does not match MockServer, attempt to proxy the request.
- Default: true
- Environment variable: MOCKSERVER_ATTEMPT_TO_PROXY_IF_NO_MATCHING_EXPECTATION

**forwardHttpProxy**
Use HTTP proxy for all outbound/forwarded requests.
- Default: null
- Environment variable: MOCKSERVER_FORWARD_HTTP_PROXY
- Example: "127.0.0.1:1090"

**forwardHttpsProxy**
Use HTTPS proxy (HTTP CONNECT) for all outbound/forwarded requests.
- Default: null
- Environment variable: MOCKSERVER_FORWARD_HTTPS_PROXY

**forwardSocksProxy**
Use SOCKS proxy for all outbound/forwarded requests.
- Default: null
- Environment variable: MOCKSERVER_FORWARD_SOCKS_PROXY

**forwardProxyAuthenticationUsername** / **forwardProxyAuthenticationPassword**
Credentials for proxy authentication when using HTTPS proxy.
- Default: null
- Environment variables: MOCKSERVER_FORWARD_PROXY_AUTHENTICATION_USERNAME, MOCKSERVER_FORWARD_PROXY_AUTHENTICATION_PASSWORD

**proxyAuthenticationRealm**
Authentication realm for proxy authentication to MockServer.
- Default: "MockServer HTTP Proxy"
- Environment variable: MOCKSERVER_PROXY_SERVER_REALM

**proxyAuthenticationUsername** / **proxyAuthenticationPassword**
Required credentials for proxy authentication to MockServer.
- Default: "" (no authentication required)
- Environment variables: MOCKSERVER_PROXY_AUTHENTICATION_USERNAME, MOCKSERVER_PROXY_AUTHENTICATION_PASSWORD

### Liveness Configuration

**livenessHttpGetPath**
Path for HTTP GET liveness/healthcheck probes. When set, GET requests to this path return 200 OK with MockServer version and ports.
- Default: null (disabled; use PUT /mockserver/status instead)
- Environment variable: MOCKSERVER_LIVENESS_HTTP_GET_PATH
- Example: "/liveness/probe"

### TLS Configuration

**dynamicallyCreateCertificateAuthorityCertificate**
Enable dynamic creation of Certificate Authority X.509 Certificate and Private Key for increased security.
- Default: false
- Environment variable: MOCKSERVER_DYNAMICALLY_CREATE_CERTIFICATE_AUTHORITY_CERTIFICATE

**directoryToSaveDynamicSSLCertificate**
Directory for saving dynamically generated CA key pair.
- Default: null
- Environment variable: MOCKSERVER_CERTIFICATE_DIRECTORY_TO_SAVE_DYNAMIC_SSL_CERTIFICATE

**proactivelyInitialiseTLS**
Initialize TLS during startup (instead of on first TLS connection).
- Default: false
- Environment variable: MOCKSERVER_PROACTIVELY_INITIALISE_TLS

**preventCertificateDynamicUpdate**
Disable automatic SAN updates from request Host headers.
- Default: false
- Environment variable: MOCKSERVER_PREVENT_CERTIFICATE_DYNAMIC_UPDATE

**sslCertificateDomainName**
Domain name for auto-generated TLS certificates.
- Default: localhost
- Environment variable: MOCKSERVER_SSL_CERTIFICATE_DOMAIN_NAME

**sslSubjectAlternativeNameDomains**
SAN domain names for auto-generated TLS certificates (comma-separated).
- Default: localhost
- Environment variable: MOCKSERVER_SSL_SUBJECT_ALTERNATIVE_NAME_DOMAINS

**sslSubjectAlternativeNameIps**
SAN IP addresses for auto-generated TLS certificates (comma-separated).
- Default: 127.0.0.1,0.0.0.0
- Environment variable: MOCKSERVER_SSL_SUBJECT_ALTERNATIVE_NAME_IPS

**certificateAuthorityPrivateKey**
Location of custom CA private key PEM file (PKCS#8 or PKCS#1).
- Default: null (uses built-in CA)
- Environment variable: MOCKSERVER_CERTIFICATE_AUTHORITY_PRIVATE_KEY

**certificateAuthorityCertificate**
Location of custom CA X.509 certificate PEM file.
- Default: null (uses built-in CA)
- Environment variable: MOCKSERVER_CERTIFICATE_AUTHORITY_X509_CERTIFICATE

### Template Restriction Configuration

**javascriptDisallowedClasses**
Comma-separated list of Java classes not allowed in JavaScript templates.
- Default: ""
- Environment variable: MOCKSERVER_JAVASCRIPT_DISALLOWED_CLASSES

**javascriptDisallowedText**
Comma-separated list of text strings not allowed in JavaScript templates.
- Default: ""
- Environment variable: MOCKSERVER_JAVASCRIPT_DISALLOWED_TEXT

**velocityDisallowClassLoading**
Prevent class loading in Velocity templates.
- Default: false
- Environment variable: MOCKSERVER_VELOCITY_DISALLOW_CLASS_LOADING

**velocityDisallowedText**
Comma-separated list of text not allowed in Velocity templates.
- Default: ""
- Environment variable: MOCKSERVER_VELOCITY_DISALLOWED_TEXT

**mustacheDisallowedText**
Comma-separated list of text not allowed in Mustache templates.
- Default: ""
- Environment variable: MOCKSERVER_MUSTACHE_DISALLOWED_TEXT

## Client Libraries

### Java Client

Maven dependency:

    <dependency>
      <groupId>org.mock-server</groupId>
      <artifactId>mockserver-client-java</artifactId>
      <version>6.1.0</version>
    </dependency>

Basic usage:

    import org.mockserver.client.MockServerClient;
    import org.mockserver.model.HttpRequest;
    import org.mockserver.model.HttpResponse;
    import static org.mockserver.model.HttpRequest.request;
    import static org.mockserver.model.HttpResponse.response;

    MockServerClient client = new MockServerClient("localhost", 1080);

    // Create expectation
    client.when(
        request().withMethod("GET").withPath("/api/users")
    ).respond(
        response()
            .withStatusCode(200)
            .withHeader("Content-Type", "application/json")
            .withBody("[{\"id\": 1, \"name\": \"Alice\"}]")
    );

    // Verify request was received
    client.verify(
        request().withPath("/api/users"),
        org.mockserver.verify.VerificationTimes.atLeast(1)
    );

    // Verify request sequence
    client.verify(
        request().withPath("/api/login"),
        request().withPath("/api/users")
    );

    // Retrieve recorded requests
    HttpRequest[] requests = client.retrieveRecordedRequests(
        request().withPath("/api/.*")
    );

    // Clear specific expectations
    client.clear(request().withPath("/api/users"));

    // Reset all
    client.reset();

Using ClientAndServer (starts embedded MockServer):

    import org.mockserver.integration.ClientAndServer;

    ClientAndServer mockServer = ClientAndServer.startClientAndServer(1080);
    // ... set up expectations, run tests ...
    mockServer.stop();

JUnit 5 extension:

    import org.mockserver.junit.jupiter.MockServerExtension;
    import org.mockserver.junit.jupiter.MockServerSettings;

    @ExtendWith(MockServerExtension.class)
    @MockServerSettings(ports = {1080})
    class MyTest {
        @Test
        void testSomething(MockServerClient client) {
            client.when(request().withPath("/test"))
                  .respond(response().withBody("ok"));
            // ... test code ...
        }
    }

### JavaScript/TypeScript Client

Install:

    npm install mockserver-client

Usage:

    var mockServerClient = require('mockserver-client').mockServerClient;

    // Create expectation
    mockServerClient("localhost", 1080)
      .mockAnyResponse({
        httpRequest: {
          method: "GET",
          path: "/api/users"
        },
        httpResponse: {
          statusCode: 200,
          body: JSON.stringify([{id: 1, name: "Alice"}])
        }
      })
      .then(function () {
        console.log("expectation created");
      });

    // Verify request
    mockServerClient("localhost", 1080)
      .verify({
        path: "/api/users"
      }, 1)  // at least 1 time
      .then(function () {
        console.log("request verified");
      });

    // Clear expectations
    mockServerClient("localhost", 1080)
      .clear({path: "/api/users"})
      .then(function () {
        console.log("cleared");
      });

    // Reset all
    mockServerClient("localhost", 1080)
      .reset()
      .then(function () {
        console.log("reset");
      });

    // Callback-based response
    mockServerClient("localhost", 1080)
      .mockWithCallback({
        path: "/api/dynamic"
      }, function (request) {
        return {
          statusCode: 200,
          body: JSON.stringify({echo: request.body})
        };
      });

TypeScript types are included: `import { mockServerClient } from 'mockserver-client';`

### Python Client

Install:

    pip install mockserver-client

Usage:

    from mockserver import (
        MockServerClient, HttpRequest, HttpResponse,
        Expectation, Times, TimeToLive, VerificationTimes
    )

    client = MockServerClient("localhost", 1080)

    # Create expectation using fluent API
    client.when(
        HttpRequest(method="GET", path="/api/users")
    ).respond(
        HttpResponse(
            status_code=200,
            headers={"Content-Type": ["application/json"]},
            body='[{"id": 1, "name": "Alice"}]'
        )
    )

    # Create expectation using upsert
    client.upsert(Expectation(
        http_request=HttpRequest(method="POST", path="/api/login"),
        http_response=HttpResponse(
            status_code=200,
            body='{"token": "abc123"}'
        ),
        times=Times(remaining_times=1, unlimited=False)
    ))

    # Verify request was received
    client.verify(
        HttpRequest(path="/api/users"),
        times=VerificationTimes(at_least=1)
    )

    # Verify request sequence
    client.verify_sequence(
        HttpRequest(path="/api/login"),
        HttpRequest(path="/api/users")
    )

    # Retrieve recorded requests
    requests = client.retrieve_recorded_requests(
        HttpRequest(path="/api/.*")
    )

    # Clear and reset
    client.clear(HttpRequest(path="/api/users"))
    client.reset()

    # Context manager support
    with MockServerClient("localhost", 1080) as client:
        client.when(
            HttpRequest(path="/test")
        ).respond(
            HttpResponse(body="hello")
        )

    # Async client
    from mockserver import AsyncMockServerClient

    async def setup():
        client = AsyncMockServerClient("localhost", 1080)
        await client.when(
            HttpRequest(path="/async")
        ).respond(
            HttpResponse(body="async response")
        )
        await client.close()

### Ruby Client

Install:

    gem install mockserver-client

Usage:

    require 'mockserver-client'
    include MockServer

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

    # Create expectation using fluent API
    client.when(
      HttpRequest.request(method: 'GET', path: '/api/users')
    ).respond(
      HttpResponse.response(
        status_code: 200,
        headers: { 'Content-Type' => ['application/json'] },
        body: '[{"id": 1, "name": "Alice"}]'
      )
    )

    # Create expectation using upsert
    client.upsert(Expectation.new(
      http_request: HttpRequest.new(path: '/api/login'),
      http_response: HttpResponse.new(
        status_code: 200,
        body: '{"token": "abc123"}'
      )
    ))

    # Verify request
    client.verify(
      HttpRequest.new(path: '/api/users'),
      times: VerificationTimes.new(at_least: 1)
    )

    # Verify sequence
    client.verify_sequence(
      HttpRequest.new(path: '/api/login'),
      HttpRequest.new(path: '/api/users')
    )

    # Retrieve recorded requests
    requests = client.retrieve_recorded_requests(request: HttpRequest.new(path: '/api/.*'))

    # Clear and reset
    client.clear(HttpRequest.new(path: '/api/users'))
    client.reset

    # Block form (auto-close)
    Client.new('localhost', 1080) do |c|
      c.when(HttpRequest.request(path: '/hello'))
       .respond(HttpResponse.response(body: 'world'))
    end

    client.close

## Docker Deployment

### Basic Docker Run

    docker run -d --rm -p 1080:1080 mockserver/mockserver

### With Custom Port

    docker run -d --rm -p 8080:1090 \
      --env MOCKSERVER_SERVER_PORT=1090 \
      mockserver/mockserver

### With Logging

    docker run -d --rm -p 1080:1080 \
      --env MOCKSERVER_LOG_LEVEL=DEBUG \
      mockserver/mockserver

### With Configuration File

Mount a directory containing mockserver.properties and/or initialization files:

    docker run -d --rm -p 1080:1080 \
      -v $(pwd)/config:/config \
      --env MOCKSERVER_PROPERTY_FILE=/config/mockserver.properties \
      mockserver/mockserver

Note: The container runs as `nonroot` user. Ensure mounted files are readable:

    chmod o+r config/mockserver.properties

### With Initialization JSON

Pre-load expectations from a JSON file at startup:

    docker run -d --rm -p 1080:1080 \
      -v $(pwd):/config \
      --env MOCKSERVER_INITIALIZATION_JSON_PATH=/config/expectations.json \
      mockserver/mockserver

### With OpenAPI Initialization

Pre-load expectations from an OpenAPI spec file at startup:

    docker run -d --rm -p 1080:1080 \
      -v $(pwd):/config \
      --env MOCKSERVER_INITIALIZATION_OPENAPI_PATH=/config/petstore.yaml \
      mockserver/mockserver

### With Persistence

Enable persistence so expectations survive container restarts:

    docker run -d -p 1080:1080 \
      -v $(pwd)/data:/config \
      --env MOCKSERVER_PERSIST_EXPECTATIONS=true \
      --env MOCKSERVER_PERSISTED_EXPECTATIONS_PATH=/config/expectations.json \
      --env MOCKSERVER_INITIALIZATION_JSON_PATH=/config/expectations.json \
      mockserver/mockserver

### With Custom TLS Certificates

    docker run -d --rm -p 1080:1080 \
      -v $(pwd)/certs:/config \
      --env MOCKSERVER_CERTIFICATE_AUTHORITY_PRIVATE_KEY=/config/ca-key.pem \
      --env MOCKSERVER_CERTIFICATE_AUTHORITY_X509_CERTIFICATE=/config/ca-cert.pem \
      mockserver/mockserver

### With Class Callbacks

Add custom JARs to the classpath by mounting them to /libs:

    docker run -d --rm -p 1080:1080 \
      -v $(pwd)/libs:/libs \
      mockserver/mockserver

### Using Command Line Arguments

    docker run -d --rm -p 1080:1090 \
      mockserver/mockserver \
      -logLevel INFO -serverPort 1090

### With Liveness Probe

    docker run -d --rm -p 1080:1080 \
      --env MOCKSERVER_LIVENESS_HTTP_GET_PATH=/liveness/probe \
      mockserver/mockserver

Test: `curl http://localhost:1080/liveness/probe`

### Docker Compose

Basic:

    services:
      mockserver:
        image: mockserver/mockserver:6.1.0
        ports:
          - "1080:1080"
        environment:
          MOCKSERVER_LOG_LEVEL: INFO

With configuration and initialization:

    services:
      mockserver:
        image: mockserver/mockserver:6.1.0
        ports:
          - "1080:1080"
        environment:
          MOCKSERVER_PROPERTY_FILE: /config/mockserver.properties
          MOCKSERVER_INITIALIZATION_JSON_PATH: /config/initializerJson.json
        volumes:
          - type: bind
            source: .
            target: /config

With OpenAPI initialization and file watching:

    services:
      mockserver:
        image: mockserver/mockserver:6.1.0
        ports:
          - "1080:1080"
        environment:
          MOCKSERVER_INITIALIZATION_OPENAPI_PATH: /config/petstore.yaml
          MOCKSERVER_WATCH_INITIALIZATION_JSON: "true"
        volumes:
          - type: bind
            source: .
            target: /config

Using command line arguments:

    services:
      mockserver:
        image: mockserver/mockserver:6.1.0
        command: -logLevel DEBUG -serverPort 1090
        ports:
          - "1080:1090"

### Performance-Optimized Docker

    docker run -d --rm -p 1080:1080 \
      --env MOCKSERVER_LOG_LEVEL=WARN \
      --env MOCKSERVER_DISABLE_SYSTEM_OUT=true \
      --env MOCKSERVER_NIO_EVENT_LOOP_THREAD_COUNT=10 \
      --env MOCKSERVER_ACTION_HANDLER_THREAD_COUNT=10 \
      --env MOCKSERVER_MATCHERS_FAIL_FAST=true \
      mockserver/mockserver

### Docker Image Notes

- Based on `gcr.io/distroless/java17:nonroot` — no shell, no system utilities
- Exports port 1080 by default
- Health checks must be performed externally (no curl/wget inside container)
- Configuration files are loaded from /config if present
- Additional JARs for classpath are loaded from /libs

## Common Patterns

### Match Any Request

    {
      "httpRequest": {},
      "httpResponse": {"statusCode": 200, "body": "catch all"}
    }

### Match by JSON Body (Partial Match)

Match requests that contain at least the specified JSON fields:

    {
      "httpRequest": {
        "method": "POST",
        "path": "/api/users",
        "body": {
          "type": "JSON",
          "json": "{\"role\": \"admin\"}",
          "matchType": "ONLY_MATCHING_FIELDS"
        }
      },
      "httpResponse": {"statusCode": 201}
    }

### Match by JSON Body (Shorthand)

JSON objects as body are automatically treated as ONLY_MATCHING_FIELDS:

    {
      "httpRequest": {
        "method": "POST",
        "path": "/api/users",
        "body": {"role": "admin"}
      },
      "httpResponse": {"statusCode": 201}
    }

### Match by JSON Schema

    {
      "httpRequest": {
        "path": "/api/users",
        "body": {
          "type": "JSON_SCHEMA",
          "jsonSchema": {
            "type": "object",
            "required": ["email"],
            "properties": {
              "email": {"type": "string", "format": "email"},
              "name": {"type": "string", "minLength": 1}
            }
          }
        }
      },
      "httpResponse": {"statusCode": 201}
    }

### Match by JSON Path

    {
      "httpRequest": {
        "path": "/api/orders",
        "body": {"type": "JSON_PATH", "jsonPath": "$.items[?(@.price > 100)]"}
      },
      "httpResponse": {"statusCode": 200, "body": "expensive items found"}
    }

### Match by XPath

    {
      "httpRequest": {
        "path": "/api/xml",
        "body": {"type": "XPATH", "xpath": "/order/item[price>100]"}
      },
      "httpResponse": {"statusCode": 200}
    }

### Match with Regex Path

    {
      "httpRequest": {
        "path": "/api/users/[0-9]+/orders"
      },
      "httpResponse": {"statusCode": 200, "body": "[]"}
    }

### Match with Negation

Match requests that do NOT have a specific header:

    {
      "httpRequest": {
        "path": "/api/protected",
        "headers": {
          "Authorization": {"not": true, "value": ".*"}
        }
      },
      "httpResponse": {"statusCode": 401, "body": "Unauthorized"}
    }

### Simulate Delays

    {
      "httpRequest": {"path": "/api/slow"},
      "httpResponse": {
        "statusCode": 200,
        "body": "finally!",
        "delay": {"timeUnit": "SECONDS", "value": 5}
      }
    }

### Forward to Real Service (Proxy)

    {
      "httpRequest": {"path": "/api/.*"},
      "httpForward": {
        "host": "real-api.example.com",
        "port": 443,
        "scheme": "HTTPS"
      }
    }

### Forward with Request Modification

    {
      "httpRequest": {"path": "/api/.*"},
      "httpOverrideForwardedRequest": {
        "requestModifier": {
          "headers": {
            "add": {"X-Api-Key": ["my-secret-key"]},
            "remove": ["Cookie"]
          },
          "path": {
            "regex": "^/api",
            "substitution": "/v2/api"
          }
        }
      }
    }

### One-Time Response

Return a response only once, then the expectation is removed:

    {
      "httpRequest": {"path": "/api/token"},
      "httpResponse": {"statusCode": 200, "body": "{\"token\": \"one-time\"}"},
      "times": {"remainingTimes": 1, "unlimited": false}
    }

### Expiring Expectation

    {
      "httpRequest": {"path": "/api/temp"},
      "httpResponse": {"statusCode": 200, "body": "temporary"},
      "timeToLive": {"timeUnit": "MINUTES", "timeToLive": 5, "unlimited": false}
    }

### Priority-Based Response (Multiple Expectations for Same Path)

Higher priority expectations are matched first:

    // Default response (low priority)
    {
      "priority": 0,
      "httpRequest": {"path": "/api/users"},
      "httpResponse": {"statusCode": 200, "body": "[]"}
    }

    // Specific response (high priority, one-time)
    {
      "priority": 10,
      "httpRequest": {"path": "/api/users"},
      "httpResponse": {"statusCode": 500, "body": "error"},
      "times": {"remainingTimes": 1, "unlimited": false}
    }

First request to /api/users returns 500, subsequent requests return 200.

### Record and Replay

Step 1: Use MockServer as a proxy to record traffic:

    docker run -d --rm -p 1080:1080 \
      --env MOCKSERVER_PERSIST_EXPECTATIONS=true \
      --env MOCKSERVER_PERSISTED_EXPECTATIONS_PATH=/config/recorded.json \
      mockserver/mockserver

Configure your application to use localhost:1080 as a proxy.

Step 2: Retrieve recorded expectations:

    curl -X PUT http://localhost:1080/mockserver/retrieve?type=recorded_expectations

Step 3: Replay by loading the recorded expectations:

    docker run -d --rm -p 1080:1080 \
      -v $(pwd):/config \
      --env MOCKSERVER_INITIALIZATION_JSON_PATH=/config/recorded.json \
      mockserver/mockserver

### Parallel Tests with Unique Expectations

Use expectation IDs for isolation between parallel tests:

    // Test A
    curl -X PUT http://localhost:1080/mockserver/expectation -d '{
      "id": "test-a-users",
      "httpRequest": {"path": "/test-a/users"},
      "httpResponse": {"statusCode": 200, "body": "test-a-data"}
    }'

    // Test B
    curl -X PUT http://localhost:1080/mockserver/expectation -d '{
      "id": "test-b-users",
      "httpRequest": {"path": "/test-b/users"},
      "httpResponse": {"statusCode": 200, "body": "test-b-data"}
    }'

    // Clean up Test A only
    curl -X PUT http://localhost:1080/mockserver/clear -d '{"id": "test-a-users"}'

### Simulating Errors

Drop connection:

    {
      "httpRequest": {"path": "/api/fail"},
      "httpError": {"dropConnection": true}
    }

Random bytes response:

    {
      "httpRequest": {"path": "/api/corrupt"},
      "httpError": {
        "responseBytes": "cmFuZG9tZ2FyYmFnZQ=="
      }
    }

### Matching Form Data (URL Encoded Body)

    {
      "httpRequest": {
        "method": "POST",
        "path": "/login",
        "body": {
          "type": "PARAMETERS",
          "parameters": {
            "username": ["admin"],
            "password": ["secret"]
          }
        }
      },
      "httpResponse": {"statusCode": 302, "headers": {"Location": ["/dashboard"]}}
    }

### Retrieve and Inspect Traffic

Get all recorded requests:

    curl -X PUT http://localhost:1080/mockserver/retrieve?type=requests

Get request-response pairs:

    curl -X PUT http://localhost:1080/mockserver/retrieve?type=request_responses

Get active expectations:

    curl -X PUT http://localhost:1080/mockserver/retrieve?type=active_expectations

Get logs (for debugging):

    curl -X PUT http://localhost:1080/mockserver/retrieve?type=logs

Filter by request matcher:

    curl -X PUT http://localhost:1080/mockserver/retrieve?type=requests -d '{"path": "/api/.*"}'

### Dynamic Response with Velocity Template

    {
      "httpRequest": {
        "method": "GET",
        "path": "/api/users/{userId}"
      },
      "httpResponseTemplate": {
        "templateType": "VELOCITY",
        "template": "{\"statusCode\": 200, \"headers\": {\"Content-Type\": [\"application/json\"]}, \"body\": \"{\\\"id\\\": $!request.pathParameters['userId'][0], \\\"name\\\": \\\"User $!request.pathParameters['userId'][0]\\\"}\"}"
      }
    }

### Dynamic Response with JavaScript Template

    {
      "httpRequest": {"path": "/api/echo"},
      "httpResponseTemplate": {
        "templateType": "JAVASCRIPT",
        "template": "return { statusCode: 200, headers: {'Content-Type': ['application/json']}, body: JSON.stringify({method: request.method, path: request.path, body: request.body}) };"
      }
    }

### OpenAPI-Based Expectations

Create expectations from an OpenAPI specification with specific operation-response mappings:

    curl -X PUT http://localhost:1080/mockserver/openapi -d '{
      "specUrlOrPayload": "https://petstore.swagger.io/v2/swagger.json",
      "operationsAndResponses": {
        "findPetsByStatus": "200",
        "getPetById": "200",
        "addPet": "200"
      }
    }'

## MCP Integration (Model Context Protocol)

MockServer exposes an MCP endpoint for AI coding assistants to interact with the server programmatically.

### MCP Endpoint

    POST http://localhost:1080/mockserver/mcp

The MCP endpoint follows the Model Context Protocol specification and provides tools and resources for AI assistants.

### Available MCP Tools

**create_expectation** - Create an expectation with request matcher and response action.

**verify_request** - Verify that a request was received a specific number of times.

**verify_request_sequence** - Verify that requests were received in a specific order.

**retrieve_recorded_requests** - Retrieve requests recorded by MockServer.

**retrieve_request_responses** - Retrieve request-response pairs recorded by MockServer.

**clear_expectations** - Clear expectations and/or logs matching a request.

**reset** - Reset all state (expectations, logs, recorded requests).

**get_status** - Get MockServer status (running ports).

**create_forward_expectation** - Create an expectation that forwards requests to another host.

**debug_request_mismatch** - Diagnose why a request didn't match any expectation by retrieving detailed match failure logs.

**stop_server** - Stop the MockServer instance.

**create_expectation_from_openapi** - Create expectations from an OpenAPI specification.

**raw_expectation** - Send a raw expectation JSON body directly to PUT /mockserver/expectation.

**raw_retrieve** - Send a raw retrieve request with custom type and format parameters.

**raw_verify** - Send a raw verification JSON body directly to PUT /mockserver/verify.

### Available MCP Resources

**mockserver://expectations** - List all active expectations.

**mockserver://requests** - List all recorded requests.

**mockserver://logs** - Retrieve recent log messages.

**mockserver://configuration** - Show current MockServer configuration.

### MCP Client Configuration

For Cursor (.cursor/mcp.json):

    {
      "mcpServers": {
        "mockserver": {
          "url": "http://localhost:1080/mockserver/mcp"
        }
      }
    }

For Claude Code (.claude/mcp.json):

    {
      "mcpServers": {
        "mockserver": {
          "type": "url",
          "url": "http://localhost:1080/mockserver/mcp"
        }
      }
    }

For OpenCode (.opencode/config.json):

    {
      "mcpServers": {
        "mockserver": {
          "type": "remote",
          "url": "http://localhost:1080/mockserver/mcp"
        }
      }
    }

For Windsurf, Cline, and other MCP-compatible tools, configure the MCP server URL as: http://localhost:1080/mockserver/mcp

### MCP Usage Examples

Example tool invocation (create_expectation):

    Tool: create_expectation
    Arguments: {
      "method": "GET",
      "path": "/api/users",
      "statusCode": 200,
      "responseBody": "[{\"id\": 1, \"name\": \"Alice\"}]"
    }

Example tool invocation (verify_request):

    Tool: verify_request
    Arguments: {
      "method": "GET",
      "path": "/api/users",
      "atLeast": 1
    }

Example tool invocation (debug_request_mismatch):

    Tool: debug_request_mismatch
    Arguments: {
      "method": "POST",
      "path": "/api/login"
    }

This retrieves detailed logs showing why the request didn't match any active expectation, including field-by-field comparison results.

## Running MockServer

### Docker

    docker run -d --rm -p 1080:1080 mockserver/mockserver

### Maven Plugin

    <plugin>
      <groupId>org.mock-server</groupId>
      <artifactId>mockserver-maven-plugin</artifactId>
      <version>6.1.0</version>
      <configuration>
        <serverPort>1080</serverPort>
        <logLevel>WARN</logLevel>
      </configuration>
      <executions>
        <execution>
          <id>process-test-classes</id>
          <phase>process-test-classes</phase>
          <goals><goal>start</goal></goals>
        </execution>
        <execution>
          <id>verify</id>
          <phase>verify</phase>
          <goals><goal>stop</goal></goals>
        </execution>
      </executions>
    </plugin>

### Standalone JAR

    java -jar mockserver-netty-no-dependencies-6.1.0.jar -serverPort 1080 -logLevel INFO

### npm Module

    npm install -g mockserver-node
    mockserver-node --serverPort 1080 --verbose true

### Programmatic (Java)

    import org.mockserver.integration.ClientAndServer;

    ClientAndServer mockServer = ClientAndServer.startClientAndServer(1080);
    // ... use mockServer ...
    mockServer.stop();

## Chaos Testing & Fault Injection

MockServer supports declarative HTTP chaos/fault injection via a `chaos` profile on any expectation. Attach an `HttpChaosProfile` to inject probabilistic error statuses (e.g. 500, 503, 429 with optional `Retry-After`) and/or artificial latency into responses. Works on both mocked responses and forwarded/proxied upstream responses, making MockServer usable as a chaos proxy for resilience testing.

### Chaos Profile Fields

- `errorStatus` (integer, 100-599): HTTP status to inject
- `errorProbability` (number, 0.0-1.0): probability of error injection (0.0=never, 1.0=always)
- `retryAfter` (string): Retry-After header value on injected errors
- `latency` (Delay object): artificial delay added to every response
- `seed` (integer): fixed seed for reproducible probabilistic decisions

### Example: Inject 503 errors 30% of the time

    curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
        "httpRequest": { "path": "/api/service" },
        "httpResponse": { "statusCode": 200, "body": "{\"status\":\"ok\"}" },
        "chaos": { "errorStatus": 503, "errorProbability": 0.3, "retryAfter": "30" }
    }'

### Example: Inject faults into proxied upstream

    curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
        "httpRequest": { "path": "/api/upstream" },
        "httpForward": { "host": "upstream.example.com", "port": 443, "scheme": "HTTPS" },
        "chaos": { "errorStatus": 503, "errorProbability": 0.5, "latency": { "timeUnit": "MILLISECONDS", "value": 1000 } }
    }'

See: https://www.mock-server.com/mock_server/chaos_testing.html

## Proxying

MockServer can act as an HTTP/HTTPS/SOCKS proxy:

### Port Forwarding Proxy

    docker run -d --rm -p 1080:1080 \
      mockserver/mockserver \
      -serverPort 1080 \
      -proxyRemotePort 443 \
      -proxyRemoteHost api.example.com

All requests to localhost:1080 are forwarded to api.example.com:443.

### HTTP Proxy

Configure your application to use MockServer as its HTTP proxy (localhost:1080). All traffic flows through MockServer and can be:
- Recorded for later verification
- Matched against expectations for mocking
- Forwarded to the real server (default behavior)

### HTTPS Tunneling Proxy

MockServer supports CONNECT tunneling. Configure your HTTPS proxy to localhost:1080. MockServer dynamically generates TLS certificates to intercept and inspect HTTPS traffic.

## Troubleshooting

### Why didn't my request match?

1. Retrieve logs to see match failure details:

       curl -X PUT http://localhost:1080/mockserver/retrieve?type=logs

2. Check active expectations:

       curl -X PUT http://localhost:1080/mockserver/retrieve?type=active_expectations

3. Retrieve recorded requests to see what was actually received:

       curl -X PUT http://localhost:1080/mockserver/retrieve?type=requests

4. Enable detailed match failures and set log level to DEBUG:

       docker run -d --rm -p 1080:1080 \
         --env MOCKSERVER_LOG_LEVEL=DEBUG \
         --env MOCKSERVER_DETAILED_MATCH_FAILURES=true \
         mockserver/mockserver

### Common Issues

- **Request body mismatch**: JSON body matchers default to `ONLY_MATCHING_FIELDS`. Ensure the specified fields exist in the request. For exact matching, use `"matchType": "STRICT"`.
- **Header case sensitivity**: Header names are case-insensitive in HTTP. MockServer matches headers case-insensitively.
- **Regex path matching**: Regex is automatically applied to path strings containing regex metacharacters. For exact matching of paths with special characters, use `{"not": false, "value": "/exact/path"}`.
- **Expectation ordering**: When multiple expectations match, the highest-priority one wins. If priorities are equal, the most recently added expectation is checked first.
- **Memory limits**: If expectations or logs are disappearing, check `maxExpectations` and `maxLogEntries`. Both use circular buffers that overwrite oldest entries when full.

## Full JSON Schema Reference

### Expectation Object

    {
      "id": "string (optional, unique identifier for upsert)",
      "priority": "integer (optional, higher = matched first)",
      "httpRequest": { /* RequestDefinition */ },
      "httpResponse": { /* HttpResponse (action - pick one) */ },
      "httpResponseTemplate": { /* HttpTemplate */ },
      "httpResponseClassCallback": { /* HttpClassCallback */ },
      "httpResponseObjectCallback": { /* HttpObjectCallback */ },
      "httpForward": { /* HttpForward (action - pick one) */ },
      "httpForwardTemplate": { /* HttpTemplate */ },
      "httpForwardClassCallback": { /* HttpClassCallback */ },
      "httpForwardObjectCallback": { /* HttpObjectCallback */ },
      "httpOverrideForwardedRequest": { /* HttpOverrideForwardedRequest */ },
      "httpError": { /* HttpError (action - pick one) */ },
      "times": {
        "remainingTimes": "integer",
        "unlimited": "boolean (default true)"
      },
      "timeToLive": {
        "timeUnit": "DAYS|HOURS|MINUTES|SECONDS|MILLISECONDS|MICROSECONDS|NANOSECONDS",
        "timeToLive": "integer",
        "unlimited": "boolean (default true)"
      }
    }

### HttpRequest Object (Request Matcher)

    {
      "method": "string or matcher object",
      "path": "string or matcher object",
      "pathParameters": { /* KeyToMultiValue */ },
      "queryStringParameters": { /* KeyToMultiValue */ },
      "headers": { /* KeyToMultiValue */ },
      "cookies": { /* KeyToValue */ },
      "body": { /* Body matcher */ },
      "secure": "boolean",
      "keepAlive": "boolean",
      "socketAddress": {
        "host": "string",
        "port": "integer",
        "scheme": "HTTP|HTTPS"
      },
      "protocol": "HTTP_1_1|HTTP_2"
    }

### HttpResponse Object

    {
      "statusCode": "integer (default 200)",
      "reasonPhrase": "string",
      "headers": { /* KeyToMultiValue */ },
      "cookies": { /* KeyToValue */ },
      "body": "string or body object",
      "delay": {
        "timeUnit": "string (e.g. SECONDS, MILLISECONDS)",
        "value": "integer"
      },
      "connectionOptions": {
        "closeSocket": "boolean",
        "closeSocketDelay": { /* Delay */ },
        "keepAliveOverride": "boolean",
        "suppressContentLengthHeader": "boolean",
        "contentLengthHeaderOverride": "integer",
        "suppressConnectionHeader": "boolean",
        "chunkSize": "integer"
      }
    }

### KeyToMultiValue Formats

Object format (shorthand):

    {"headerName": ["value1", "value2"], "keyMatchStyle": "SUB_SET"}

Array format:

    [{"name": "headerName", "values": ["value1", "value2"]}]

### KeyToValue Formats

Object format:

    {"cookieName": "cookieValue"}

Array format:

    [{"name": "cookieName", "value": "cookieValue"}]

### Verification Object

    {
      "httpRequest": { /* RequestDefinition */ },
      "times": {
        "atLeast": "integer",
        "atMost": "integer"
      },
      "maximumNumberOfRequestToReturnInVerificationFailure": "integer"
    }

Or by expectation ID:

    {
      "expectationId": {"id": "string"},
      "times": {"atLeast": 1}
    }

### VerificationSequence Object

    {
      "httpRequests": [
        { /* RequestDefinition */ },
        { /* RequestDefinition */ }
      ],
      "maximumNumberOfRequestToReturnInVerificationFailure": "integer"
    }

Or by expectation IDs:

    {
      "expectationIds": [
        {"id": "string"},
        {"id": "string"}
      ]
    }

## Environment Variable Quick Reference

| Environment Variable | System Property | Default |
|---|---|---|
| MOCKSERVER_SERVER_PORT | mockserver.serverPort | 1080 |
| MOCKSERVER_LOG_LEVEL | mockserver.logLevel | INFO |
| MOCKSERVER_DISABLE_SYSTEM_OUT | mockserver.disableSystemOut | false |
| MOCKSERVER_DISABLE_LOGGING | mockserver.disableLogging | false |
| MOCKSERVER_LOG_LEVEL_OVERRIDES | mockserver.logLevelOverrides | {} |
| MOCKSERVER_MAX_EXPECTATIONS | mockserver.maxExpectations | auto |
| MOCKSERVER_MAX_LOG_ENTRIES | mockserver.maxLogEntries | auto |
| MOCKSERVER_MAX_WEB_SOCKET_EXPECTATIONS | mockserver.maxWebSocketExpectations | 1500 |
| MOCKSERVER_NIO_EVENT_LOOP_THREAD_COUNT | mockserver.nioEventLoopThreadCount | 5 |
| MOCKSERVER_ACTION_HANDLER_THREAD_COUNT | mockserver.actionHandlerThreadCount | max(5, cpus) |
| MOCKSERVER_MAX_SOCKET_TIMEOUT | mockserver.maxSocketTimeout | 20000 |
| MOCKSERVER_SOCKET_CONNECTION_TIMEOUT | mockserver.socketConnectionTimeout | 20000 |
| MOCKSERVER_MAX_FUTURE_TIMEOUT | mockserver.maxFutureTimeout | 90000 |
| MOCKSERVER_DETAILED_MATCH_FAILURES | mockserver.detailedMatchFailures | true |
| MOCKSERVER_MATCHERS_FAIL_FAST | mockserver.matchersFailFast | true |
| MOCKSERVER_ENABLE_CORS_FOR_API | mockserver.enableCORSForAPI | false |
| MOCKSERVER_ENABLE_CORS_FOR_ALL_RESPONSES | mockserver.enableCORSForAllResponses | false |
| MOCKSERVER_INITIALIZATION_JSON_PATH | mockserver.initializationJsonPath | null |
| MOCKSERVER_INITIALIZATION_OPENAPI_PATH | mockserver.initializationOpenAPIPath | null |
| MOCKSERVER_WATCH_INITIALIZATION_JSON | mockserver.watchInitializationJson | false |
| MOCKSERVER_PERSIST_EXPECTATIONS | mockserver.persistExpectations | false |
| MOCKSERVER_PERSISTED_EXPECTATIONS_PATH | mockserver.persistedExpectationsPath | persistedExpectations.json |
| MOCKSERVER_ATTEMPT_TO_PROXY_IF_NO_MATCHING_EXPECTATION | mockserver.attemptToProxyIfNoMatchingExpectation | true |
| MOCKSERVER_FORWARD_HTTP_PROXY | mockserver.forwardHttpProxy | null |
| MOCKSERVER_FORWARD_HTTPS_PROXY | mockserver.forwardHttpsProxy | null |
| MOCKSERVER_FORWARD_SOCKS_PROXY | mockserver.forwardSocksProxy | null |
| MOCKSERVER_LIVENESS_HTTP_GET_PATH | mockserver.livenessHttpGetPath | null |
| MOCKSERVER_DYNAMICALLY_CREATE_CERTIFICATE_AUTHORITY_CERTIFICATE | mockserver.dynamicallyCreateCertificateAuthorityCertificate | false |
| MOCKSERVER_SSL_CERTIFICATE_DOMAIN_NAME | mockserver.sslCertificateDomainName | localhost |
| MOCKSERVER_SSL_SUBJECT_ALTERNATIVE_NAME_DOMAINS | mockserver.sslSubjectAlternativeNameDomains | localhost |
| MOCKSERVER_SSL_SUBJECT_ALTERNATIVE_NAME_IPS | mockserver.sslSubjectAlternativeNameIps | 127.0.0.1,0.0.0.0 |
| MOCKSERVER_PROPERTY_FILE | mockserver.propertyFile | mockserver.properties |
| MOCKSERVER_METRICS_ENABLED | mockserver.metricsEnabled | false |
| MOCKSERVER_ALWAYS_CLOSE_SOCKET_CONNECTIONS | mockserver.alwaysCloseSocketConnections | false |
| MOCKSERVER_LOCAL_BOUND_IP | mockserver.localBoundIP | 0.0.0.0 |
| MOCKSERVER_ASSUME_ALL_REQUESTS_ARE_HTTP | mockserver.assumeAllRequestsAreHttp | false |
| MOCKSERVER_USE_SEMICOLON_AS_QUERY_PARAMETER_SEPARATOR | mockserver.useSemicolonAsQueryParameterSeparator | true |
| MOCKSERVER_JAVASCRIPT_DISALLOWED_CLASSES | mockserver.javascriptDisallowedClasses | "" |
| MOCKSERVER_VELOCITY_DISALLOW_CLASS_LOADING | mockserver.velocityDisallowClassLoading | false |
