MockServer can mock AI protocol servers — including MCP (Model Context Protocol) servers and A2A (Agent-to-Agent Protocol) agents — enabling you to test AI clients against predictable mock services without running real AI infrastructure.

This is distinct from MockServer's built-in MCP control plane (which lets AI assistants control MockServer). These features let you mock other people's MCP and A2A servers.

SSE Streaming Responses

Server-Sent Events (SSE) responses stream events to clients over a long-lived HTTP connection. This is the transport used by MCP's Streamable HTTP protocol and many other AI APIs.

curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "GET",
        "path": "/events"
    },
    "httpSseResponse": {
        "statusCode": 200,
        "events": [
            {
                "event": "message",
                "data": "hello world",
                "id": "1"
            },
            {
                "event": "update",
                "data": "second event",
                "id": "2"
            }
        ],
        "closeConnection": true
    }
}'
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "GET",
        "path": "/events"
    },
    "httpSseResponse": {
        "statusCode": 200,
        "events": [
            {
                "event": "message",
                "data": "first event",
                "id": "1"
            },
            {
                "event": "message",
                "data": "delayed event",
                "id": "2",
                "delay": {
                    "timeUnit": "MILLISECONDS",
                    "value": 500
                }
            }
        ],
        "closeConnection": true
    }
}'

Each event is sent after the specified delay, enabling realistic simulation of streaming APIs.

curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "GET",
        "path": "/events"
    },
    "httpSseResponse": {
        "statusCode": 200,
        "events": [
            {
                "event": "message",
                "data": "line 1\nline 2\nline 3",
                "id": "1"
            }
        ],
        "closeConnection": true
    }
}'

Multi-line data is automatically split into multiple data: lines per the SSE specification.

WebSocket Mocking

Mock WebSocket endpoints that perform the upgrade handshake and send a sequence of text or binary messages. Useful for testing clients that connect to WebSocket APIs (GraphQL subscriptions, real-time feeds, etc.).

new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("GET")
            .withPath("/ws")
    )
    .respondWithWebSocket(
        webSocketResponse()
            .withMessage(webSocketMessage("hello"))
            .withMessage(webSocketMessage("world"))
            .withCloseConnection(true)
    );
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "GET",
        "path": "/ws"
    },
    "httpWebSocketResponse": {
        "messages": [
            {"text": "hello"},
            {"text": "world"}
        ],
        "closeConnection": true
    }
}'
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "GET",
        "path": "/ws"
    },
    "httpWebSocketResponse": {
        "messages": [
            {"text": "immediate"},
            {"text": "delayed", "delay": {"timeUnit": "MILLISECONDS", "value": 500}}
        ],
        "closeConnection": true
    }
}'
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "GET",
        "path": "/graphql"
    },
    "httpWebSocketResponse": {
        "subprotocol": "graphql-ws",
        "messages": [
            {"text": "{\"type\": \"connection_ack\"}"},
            {"text": "{\"type\": \"next\", \"payload\": {\"data\": {\"newMessage\": \"hello\"}}}"}
        ],
        "closeConnection": true
    }
}'

The subprotocol field is included in the WebSocket handshake response, allowing clients that require specific subprotocols (like graphql-ws or graphql-transport-ws) to connect successfully.

JSON-RPC Body Matching

Match HTTP requests containing JSON-RPC 2.0 bodies by method name. This is the foundation for mocking any JSON-RPC based protocol (MCP, A2A, language servers, etc.).

new MockServerClient("localhost", 1080)
    .when(
        request()
            .withMethod("POST")
            .withPath("/rpc")
            .withBody(jsonRpc("tools/list"))
    )
    .respond(
        response()
            .withStatusCode(200)
            .withBody("{\"jsonrpc\": \"2.0\", \"result\": {\"tools\": []}, \"id\": 1}")
    );
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "POST",
        "path": "/rpc",
        "body": {
            "type": "JSON_RPC",
            "method": "tools/list"
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "body": "{\"jsonrpc\": \"2.0\", \"result\": {\"tools\": []}, \"id\": 1}"
    }
}'
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "method": "POST",
        "path": "/rpc",
        "body": {
            "type": "JSON_RPC",
            "method": "tools/call",
            "paramsSchema": "{\"type\": \"object\", \"properties\": {\"name\": {\"type\": \"string\"}}, \"required\": [\"name\"]}"
        }
    },
    "httpResponse": {
        "statusCode": 200,
        "body": "{\"jsonrpc\": \"2.0\", \"result\": {\"content\": [{\"type\": \"text\", \"text\": \"result\"}]}, \"id\": 1}"
    }
}'

The paramsSchema field is a JSON Schema that validates the params field of the JSON-RPC request. The request only matches if the params are valid according to the schema.

MCP Server Mocking

The McpMockBuilder generates a complete set of expectations to make MockServer behave as a mock MCP (Model Context Protocol) server. This enables testing MCP clients without running a real MCP server.

The builder handles all protocol details: initialize, ping, tools/list, tools/call, resources/list, resources/read, prompts/list, prompts/get, and notifications/initialized. JSON-RPC response IDs are automatically echoed from the request.

import static org.mockserver.client.McpMockBuilder.mcpMock;

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

mcpMock("/mcp")
    .withServerName("TestMCP")
    .withServerVersion("1.0.0")
    .withTool("get_weather")
        .withDescription("Get weather for a city")
        .withInputSchema("{\"type\": \"object\", \"properties\": {\"city\": {\"type\": \"string\"}}, \"required\": [\"city\"]}")
        .respondingWith("72F and sunny")
        .and()
    .withTool("search")
        .withDescription("Search the knowledge base")
        .respondingWith("No results found")
        .and()
    .applyTo(mockServerClient);
MockServerClient mockServerClient = new MockServerClient("localhost", 1080);

mcpMock("/mcp")
    .withServerName("ConfigServer")
    .withResource("config://app")
        .withName("App Configuration")
        .withDescription("Application configuration")
        .withMimeType("application/json")
        .withContent("{\"debug\": true, \"logLevel\": \"info\"}")
        .and()
    .withResource("docs://readme")
        .withName("README")
        .withMimeType("text/plain")
        .withContent("Welcome to the application")
        .and()
    .applyTo(mockServerClient);
MockServerClient mockServerClient = new MockServerClient("localhost", 1080);

mcpMock("/mcp")
    .withServerName("PromptServer")
    .withPrompt("code_review")
        .withDescription("Review code changes for issues")
        .withArgument("language", "Programming language", true)
        .withArgument("style", "Review style (brief/detailed)", false)
        .respondingWith("user", "Please review the following code for bugs and style issues")
        .and()
    .applyTo(mockServerClient);
MockServerClient mockServerClient = new MockServerClient("localhost", 1080);

mcpMock("/mcp")
    .withServerName("FullMCPServer")
    .withServerVersion("2.0.0")
    .withProtocolVersion("2025-03-26")
    .withTool("calculator")
        .withDescription("Basic arithmetic calculator")
        .withInputSchema("{\"type\": \"object\", \"properties\": {\"expression\": {\"type\": \"string\"}}, \"required\": [\"expression\"]}")
        .respondingWith("42")
        .and()
    .withTool("failing_tool")
        .withDescription("A tool that always fails")
        .respondingWith("Something went wrong", true)
        .and()
    .withResource("config://app")
        .withName("App Config")
        .withContent("{\"version\": \"1.0\"}")
        .and()
    .withPrompt("summarize")
        .withDescription("Summarize text")
        .respondingWith("user", "Please summarize the following text concisely")
        .and()
    .applyTo(mockServerClient);

This creates expectations for all MCP methods: initialize, ping, notifications/initialized, tools/list, tools/call (per tool), resources/list, resources/read (per resource), prompts/list, and prompts/get (per prompt).

A2A Agent Mocking

The A2aMockBuilder generates expectations to make MockServer behave as a mock A2A (Agent-to-Agent Protocol) agent. This enables testing A2A clients that discover and communicate with agents.

The builder handles: Agent Card discovery (GET /.well-known/agent.json), tasks/send, tasks/get, and tasks/cancel.

import static org.mockserver.client.A2aMockBuilder.a2aMock;

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

a2aMock("/agent")
    .withAgentName("TranslationAgent")
    .withAgentDescription("Translates text between languages")
    .withAgentVersion("1.0.0")
    .withSkill("translate")
        .withName("Translation")
        .withDescription("Translates text between any two languages")
        .withTag("translation")
        .withTag("language")
        .withExample("Translate 'hello' to Spanish")
        .and()
    .withDefaultTaskResponse("Translation complete: Hola")
    .applyTo(mockServerClient);
MockServerClient mockServerClient = new MockServerClient("localhost", 1080);

a2aMock("/agent")
    .withAgentName("SmartAgent")
    .withSkill("translate")
        .withName("Translation")
        .and()
    .withSkill("summarize")
        .withName("Summarization")
        .and()
    .onTaskSend()
        .matchingMessage("translate.*")
        .respondingWith("Translated: Bonjour le monde")
        .and()
    .onTaskSend()
        .matchingMessage("summarize.*")
        .respondingWith("Summary: Brief overview of the content")
        .and()
    .onTaskSend()
        .matchingMessage("fail.*")
        .respondingWith("Unable to process request", true)
        .and()
    .applyTo(mockServerClient);

Custom task handlers use regex pattern matching on the message text. Handlers with more specific patterns should be added first. The default tasks/send handler matches any message that doesn't match a custom handler.

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

// Create the mock A2A agent
a2aMock("/agent")
    .withAgentName("TestAgent")
    .withAgentVersion("1.0.0")
    .withAgentUrl("http://localhost:1080/agent")
    .withAgentCardPath("/.well-known/agent.json")
    .withSkill("process")
        .withName("Data Processing")
        .withDescription("Process data")
        .and()
    .withDefaultTaskResponse("Processing complete")
    .applyTo(mockServerClient);

// Client workflow:
// 1. GET /.well-known/agent.json   → discovers agent card with skills
// 2. POST /agent (tasks/send)      → sends message, gets completed task
// 3. POST /agent (tasks/get)       → retrieves task status
// 4. POST /agent (tasks/cancel)    → cancels a running task