ExMCP Troubleshooting Guide
View Sourcestdio
Unexpected end of JSON input
The stdio transport requires stdout to contain only newline-delimited JSON-RPC.
Avoid IO.puts/1, normal Logger output, or noisy startup scripts on stdout.
Use stderr for diagnostics:
IO.puts(:stderr, "debug")For scripts with Mix.install/2, configure logging before installing deps:
Application.put_env(:ex_mcp, :stdio_mode, true)
Application.put_env(:logger, :level, :emergency)
Mix.install([{:ex_mcp, "~> 1.0.0-rc.1"}], verbose: false)Server hangs after starting
Start stdio servers with:
MyServer.start_link(transport: :stdio)For clients, command must be a list:
ExMCP.Client.start_link(transport: :stdio, command: ["node", "server.js"])HTTP
Connection refused
Check the URL and endpoint path. If the path is included in url, ExMCP uses
that as the default endpoint:
ExMCP.Client.start_link(transport: :http, url: "http://localhost:4000/mcp")Or provide it explicitly:
ExMCP.Client.start_link(
transport: :http,
url: "http://localhost:4000",
endpoint: "/mcp"
)CORS errors
For Phoenix/Plug servers, configure CORS in your Plug pipeline or pass
cors_enabled: true to ExMCP.HttpPlug.
SSE stream does not start
Confirm the server has SSE enabled and the client uses use_sse: true. Increase
stream_handshake_timeout for slow deployments.
BEAM-Local
Client cannot connect
transport: :beam requires a live server PID:
{:ok, server} = MyServer.start_link(transport: :beam) # DSL provides start_link; raw handlers use HandlerServer
Process.alive?(server)
{:ok, client} = ExMCP.Client.start_link(transport: :beam, server: server)Do not use transport: :native; it was removed in the 1.0 API cleanup.
DSL
Tools do not appear
Use ExMCP.Server.Handler and ExMCP.Server.DSL together, and make sure the
server starts through a supported transport:
defmodule MyServer do
use ExMCP.Server.Handler
use ExMCP.Server.DSL
tool "ping", "Health check" do
run fn _args, state ->
{:ok, %{content: [%{type: "text", text: "pong"}]}, state}
end
end
endDebugging
Enable debug logging for non-stdio transports:
Logger.configure(level: :debug)Inspect local server state when using BEAM-local tests:
:sys.get_state(server)Run focused tests:
mix test test/ex_mcp/client_beam_transport_test.exs
mix test test/ex_mcp/server/transport_test.exs