McpServer.Test (HTTP MCP Server v0.6.0)

View Source

Test utilities for MCP Server routers.

Provides two testing approaches:

Approach 1: Direct Function Calls (Fast)

Direct calls to router functions, bypassing HTTP/JSON-RPC layer. Best for unit testing individual tools, prompts, and resources.

defmodule MyApp.McpRouterTest do
  use ExUnit.Case
  use McpServer.Test, router: MyApp.McpRouter

  test "search tool returns results" do
    result = call_tool("search", %{query: "test"})
    assert {:ok, contents} = result
    assert [%McpServer.Tool.Content.Text{text: text}] = contents
    assert text =~ "found"
  end

  test "code_review prompt generates messages" do
    {:ok, messages} = get_prompt("code_review", %{code: "def foo, do: :bar"})
    assert length(messages) == 2
  end

  test "list all tools" do
    {:ok, tools} = list_tools()
    assert length(tools) > 0
  end
end

Approach 2: Full Request Simulation (Comprehensive)

Simulates complete JSON-RPC request lifecycle through the HTTP plug. Tests serialization, protocol compliance, and error handling.

defmodule MyApp.McpRouterIntegrationTest do
  use ExUnit.Case
  use McpServer.Test, router: MyApp.McpRouter

  test "search via JSON-RPC" do
    conn = init_session()

    {:ok, result} = request(conn, "tools/call", %{
      name: "search",
      arguments: %{query: "test"}
    })

    assert result["content"]
    assert [%{"type" => "text", "text" => text}] = result["content"]
  end

  test "handles invalid tool name" do
    conn = init_session()

    {:error, error} = request(conn, "tools/call", %{
      name: "nonexistent",
      arguments: %{}
    })

    assert error["code"] == -32602
  end

  test "full workflow" do
    conn = init_session()

    # List tools
    {:ok, tools_result} = request(conn, "tools/list")
    assert is_list(tools_result["tools"])

    # Call a tool
    {:ok, call_result} = request(conn, "tools/call", %{
      name: "search",
      arguments: %{query: "test"}
    })
    assert call_result["content"]
  end
end

Custom Connection State

You can customize the MCP connection for testing:

test "with custom session" do
  conn = mock_conn(session_id: "custom-session-123")
  result = call_tool("search", %{query: "test"}, conn)
  assert {:ok, _} = result
end

test "with private data" do
  conn = mock_conn()
         |> McpServer.Conn.put_private(:user_id, 42)
  result = call_tool("auth_tool", %{}, conn)
  assert {:ok, _} = result
end

Summary

Functions

Imports test utilities for the given router.

Creates a mock MCP connection for testing.

Functions

__using__(opts)

(macro)

Imports test utilities for the given router.

Options

  • :router - Required. The router module to test.

Example

use McpServer.Test, router: MyApp.McpRouter

mock_conn(opts \\ [])

Creates a mock MCP connection for testing.

Options

  • :session_id - Session ID (default: "test-session-123")
  • :private - Private data map (default: %{})

Examples

conn = mock_conn()
conn = mock_conn(session_id: "custom-session")
conn = mock_conn(private: %{user_id: 42})