ExMCP.Server.StdioServer (ex_mcp v0.10.0)

View Source

STDIO transport server for MCP protocol.

This server reads from stdin and writes to stdout, making it suitable for command-line tools and scripting environments.

Important: STDIO Transport Requirements

The MCP STDIO transport requires that ONLY JSON-RPC messages appear on stdout. All other output (logs, debug messages, etc.) MUST go to stderr to avoid contaminating the protocol stream.

Handling Startup Output

This module implements several strategies to handle startup output from Mix.install and other sources:

  1. Automatic logging suppression - Configures all loggers to emergency level
  2. Startup delay - Waits before reading stdin (configurable via :stdio_startup_delay)
  3. Graceful non-JSON handling - Ignores non-JSON lines instead of sending errors

Configuration

For scripts using Mix.install, add this before calling Mix.install:

# Configure STDIO mode and startup delay
Application.put_env(:ex_mcp, :stdio_mode, true)
Application.put_env(:ex_mcp, :stdio_startup_delay, 500)  # ms

# Suppress all logging for clean STDIO JSON-RPC
System.put_env("ELIXIR_LOG_LEVEL", "emergency")

Usage

defmodule MyStdioServer do
  use ExMCP.Server

  deftool "hello" do
    meta do
      description "Says hello"
    end

    input_schema %{
      type: "object",
      properties: %{name: %{type: "string"}},
      required: ["name"]
    }
  end

  @impl true
  def handle_tool_call("hello", %{"name" => name}, state) do
    {:ok, %{content: [text("Hello, #{name}!")]}, state}
  end
end

# Start with STDIO transport
MyStdioServer.start_link(transport: :stdio)

Summary

Functions

Returns a specification to start this module under a supervisor.

Starts the STDIO server.

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

start_link(opts)

Starts the STDIO server.

Options

  • :module - The handler module implementing server callbacks
  • Other options are passed to GenServer.start_link