MCPEx.Transport.TestServer (MCPEx v0.1.0)

Test server implementation to pair with MCPEx.Transport.Test.

This module simulates an MCP server for integration testing, allowing tests to verify client behavior without requiring an actual MCP server.

Features

  • Simulate server responses and notifications
  • Control message timing and failures
  • Test specific scenarios and protocol flows
  • Verify client requests match expectations
  • Support all MCP protocol features (resources, tools, prompts, sampling)

Usage

# Create a test server with capabilities
server = MCPEx.Transport.TestServer.new(
  capabilities: [:resources, :tools]
)

# Start a test transport connected to this server
{:ok, transport} = MCPEx.Transport.Test.start_link(server: server)

# Trigger server events
MCPEx.Transport.TestServer.send_notification(server, %{
  jsonrpc: "2.0",
  method: "notifications/resources/updated"
})

# Verify client messages
assert MCPEx.Transport.TestServer.last_message(server).method == "initialize"

Summary

Functions

Adds a custom handler for a specific method or response.

Adds a prompt to the server.

Adds a resource to the server.

Adds a tool to the server.

Creates a standard error response for use in handlers.

Gets the current recording for later replay.

Gets the last message received by the server.

Creates a new test server instance.

Processes a message from a client.

Checks if the server received a message matching the given criteria.

Gets all messages received by the server.

Registers a client with the server.

Sends a notification message to a client.

Sets a custom response handler for a specific method.

Unregisters a client from the server.

Types

client_ref()

@type client_ref() :: reference()

t()

@type t() :: %MCPEx.Transport.TestServer{
  capabilities: [atom()],
  clients: [{client_ref(), pid()}],
  delay: non_neg_integer(),
  error_rate: float(),
  log: list(),
  prompts: [map()],
  received_messages: [map()],
  recording: list() | nil,
  resources: [map()],
  responses: map(),
  scenario: atom() | nil,
  tools: [map()]
}

Functions

add_handler(server, method, handler_fn)

@spec add_handler(t(), String.t(), function()) :: t()

Adds a custom handler for a specific method or response.

Parameters

  • server - The server struct
  • method - The method name to handle
  • handler_fn - A function that takes the params and returns the result or an error

Returns

  • Updated server struct

add_prompt(server, prompt)

@spec add_prompt(t(), map() | keyword()) :: t()

Adds a prompt to the server.

Parameters

  • server - The server struct
  • prompt - The prompt to add

Returns

  • Updated server struct

add_resource(server, resource)

@spec add_resource(t(), map() | keyword()) :: t()

Adds a resource to the server.

Parameters

  • server - The server struct
  • resource - The resource to add

Returns

  • Updated server struct

add_tool(server, tool)

@spec add_tool(t(), map() | keyword()) :: t()

Adds a tool to the server.

Parameters

  • server - The server struct
  • tool - The tool to add

Returns

  • Updated server struct

error(error_type, message, data \\ nil)

@spec error(atom(), String.t(), map() | nil) :: {:error, integer(), String.t()}

Creates a standard error response for use in handlers.

Parameters

  • error_type - The type of error (atom like :invalid_params, :parse_error)
  • message - The error message
  • data - Optional additional data about the error

Returns

  • {:error, code, message} - Error tuple that can be returned from handlers

get_recording(server)

@spec get_recording(t()) :: list() | nil

Gets the current recording for later replay.

Parameters

  • server - The server struct

Returns

  • The recording data or nil if not in recording mode

last_message(server)

@spec last_message(t()) :: map() | nil

Gets the last message received by the server.

Parameters

  • server - The server struct

Returns

  • The last message or nil if none

new(options \\ [])

@spec new(keyword()) :: t()

Creates a new test server instance.

Options

  • :capabilities - List of server capabilities (default: [])
  • :scenario - Name of the test scenario to run (default: nil)
  • :delay - Artificial delay in milliseconds for responses (default: 0)
  • :error_rate - Probability of simulated errors (0.0-1.0, default: 0.0)
  • :resources - Initial resources to serve (default: [])
  • :tools - Initial tools to serve (default: [])
  • :prompts - Initial prompts to serve (default: [])

Returns

  • A new TestServer struct

process_message(server, client_ref, message)

@spec process_message(t(), client_ref(), map() | String.t()) ::
  {map() | {:error, term()}, t()}

Processes a message from a client.

Parameters

  • server - The server struct
  • client_ref - Reference for the client sending the message
  • message - The message received from the client

Returns

  • {response, updated_server} - Response to send back and updated server

received_message?(server, criteria)

@spec received_message?(
  t(),
  keyword()
) :: boolean()

Checks if the server received a message matching the given criteria.

Parameters

  • server - The server struct
  • criteria - Criteria to match (method, id, etc.)

Returns

  • true or false

received_messages(server)

@spec received_messages(t()) :: [map()]

Gets all messages received by the server.

Parameters

  • server - The server struct

Returns

  • List of messages in reverse chronological order

register_client(server, client_pid)

@spec register_client(t(), pid()) :: {client_ref(), t()}

Registers a client with the server.

Parameters

  • server - The server struct
  • client_pid - PID of the client transport process

Returns

  • {ref, updated_server} - The client reference and updated server

request_sampling(server, options, client_ref \\ nil)

@spec request_sampling(t(), keyword(), client_ref() | nil) :: t()

Triggers a sampling request.

Parameters

  • server - The server struct
  • options - Options for the sampling request
  • client_ref - Optional client reference (first client if nil)

Returns

  • Updated server struct

send_notification(server, notification, client_ref \\ nil)

@spec send_notification(t(), map(), client_ref() | nil) :: t()

Sends a notification message to a client.

Parameters

  • server - The server struct
  • notification - The notification message to send
  • client_ref - Optional client reference (sends to all if nil)

Returns

  • Updated server struct

set_response_handler(server, method, handler)

@spec set_response_handler(t(), String.t(), function()) :: t()

Sets a custom response handler for a specific method.

Parameters

  • server - The server struct
  • method - The method to handle
  • handler - Function that takes (server, message) and returns response

Returns

  • Updated server struct

unregister_client(server, client_ref)

@spec unregister_client(t(), client_ref()) :: t()

Unregisters a client from the server.

Parameters

  • server - The server struct
  • client_ref - Reference for the client to unregister

Returns

  • Updated server struct