ExMCP Configuration Guide
View SourceThis guide covers the supported configuration surfaces for ExMCP 1.0.
Dependency
def deps do
[
{:ex_mcp, "~> 1.0.0-rc.1"}
]
endProtocol Version
ExMCP supports:
2024-11-052025-03-262025-06-182025-11-25
The latest supported version is returned by ExMCP.protocol_version/0.
config :ex_mcp,
protocol_version: "2025-11-25"Validate versions with the public negotiator:
ExMCP.Protocol.VersionNegotiator.valid_version?("2025-11-25")Client Configuration
You can pass options directly to ExMCP.Client.start_link/1:
{:ok, client} =
ExMCP.Client.start_link(
transport: :http,
url: "https://api.example.com/mcp",
use_sse: true,
request_timeout: 30_000
)Or build a reusable config with ExMCP.ClientConfig:
config =
ExMCP.ClientConfig.new(:production)
|> ExMCP.ClientConfig.put_transport(:http, url: "https://api.example.com/mcp")
|> ExMCP.ClientConfig.put_auth(:bearer, token: System.fetch_env!("MCP_TOKEN"))
|> ExMCP.ClientConfig.put_retry_policy(max_attempts: 3, base_interval: 500)
{:ok, client} = ExMCP.connect(config)stdio
{:ok, client} =
ExMCP.Client.start_link(
transport: :stdio,
command: ["node", "server.js"],
cd: "/path/to/project",
env: [{"NODE_ENV", "production"}],
timeout: 30_000
)Supported options:
:command:cd:env:timeout
HTTP/SSE
{:ok, client} =
ExMCP.Client.start_link(
transport: :http,
url: "https://api.example.com/mcp",
use_sse: true,
headers: [{"Authorization", "Bearer #{token}"}],
request_timeout: 30_000,
stream_handshake_timeout: 15_000,
stream_idle_timeout: 60_000,
max_retry_delay: 60_000
)Supported options include:
:url:endpoint:headers:use_sse:session_id:protocol_version:timeout:request_timeout:stream_handshake_timeout:stream_idle_timeout:max_retry_delay:security:auth:auth_provider
BEAM-Local
{:ok, server} = MyServer.start_link(transport: :beam) # works when using DSL; otherwise use HandlerServer.start_link(handler: MyServer, ...)
{:ok, client} =
ExMCP.Client.start_link(
transport: :beam,
server: server,
timeout: 5_000
)transport: :beam is local to the current VM and requires a server PID. Keep any
pooling, service discovery, or process selection in your application layer.
Server Configuration
Servers (DSL or raw handlers) can be started with:
MyServer.start_link(transport: :beam) # DSL modules get this
MyServer.start_link(transport: :stdio)
MyServer.start_link(transport: :http, port: 4000, sse_enabled: true)
# For a raw handler module (no DSL):
ExMCP.Server.HandlerServer.start_link(handler: MyHandler, transport: :beam)
# or the convenience:
ExMCP.start_server(handler: MyHandler, transport: :stdio)Phoenix/Plug applications usually mount ExMCP.HttpPlug:
forward "/mcp", ExMCP.HttpPlug,
handler: MyApp.MCPServer,
server_info: %{name: "my-app", version: "1.0.0"},
sse_enabled: true,
cors_enabled: trueResilience
Retries:
ExMCP.Client.start_link(
transport: :http,
url: "https://api.example.com/mcp",
retry_policy: [max_attempts: 3, initial_delay: 100, max_delay: 2_000]
)Circuit breaker and health checks:
ExMCP.Client.start_link(
transport: :http,
url: "https://api.example.com/mcp",
reliability: [
circuit_breaker: [failure_threshold: 5, reset_timeout: 30_000],
health_check: [check_interval: 60_000]
]
)Logging
For stdio servers, stdout must contain only JSON-RPC messages. ExMCP configures stdio logging when stdio mode starts. Send ad hoc diagnostics to stderr:
IO.puts(:stderr, "debug")For HTTP and BEAM-local development:
Logger.configure(level: :debug)Security
HTTP clients can use headers:
ExMCP.Client.start_link(
transport: :http,
url: "https://api.example.com/mcp",
headers: [{"Authorization", "Bearer #{token}"}]
)For server-side HTTP concerns, compose Plug/Phoenix pipelines before
ExMCP.HttpPlug.