Toolnexus.Mcp.load
Elixir · package toolnexus · SPEC §2 · elixir/lib/toolnexus/mcp.ex
@spec load(String.t() | map(), keyword()) :: Toolnexus.Mcp.Source.t()def load(input, opts \\ [])
# %Toolnexus.Mcp.Source{# tools: [%Toolnexus.Tool{}],# status: %{String.t() => "connected" | "failed" | "disabled"},# sup: pid(),# connections: %{String.t() => pid()}# }Parses an mcp.json (path, raw JSON, or a map — see
Toolnexus.Mcp.parse_config/1), then connects to every
enabled server concurrently — local stdio and remote streamable-HTTP alike — and converts
each server’s tools into uniform Toolnexus.Tool structs named
sanitize(server)_sanitize(tool). One server failing to connect never takes the others down: it
is isolated to a "failed" status entry, and load/2 still returns.
When to use it
Section titled “When to use it”- Wiring MCP servers into an agent — this is the whole point of the MCP source: config in,
a flat list of callable
Toolstructs out. - You want per-server outcome, not just a tool list —
statustells you which servers connected, which were skipped as disabled, and which failed, so you can log or surface it. - You need the supervisor —
supis aDynamicSupervisorholding every connection; pass the wholeSourcetoclose/1when you are done to terminate stdio children cleanly.
Why this and not the alternative
Section titled “Why this and not the alternative”If you only want the tool definitions without leaving anything connected — for an inventory,
a doctor command, a CI check — use Toolnexus.Mcp.list_tools/2
instead; it connects, lists, and disconnects everything before returning.
Examples
Section titled “Examples”1. The smallest useful call — a disabled server
Section titled “1. The smallest useful call — a disabled server”enabled: false (or disabled: true) servers are skipped entirely — no connection attempt, no
process spawned — and show up in status as "disabled".
alias Toolnexus.Mcp
config = %{ "mcpServers" => %{ "off" => %{"type" => "local", "command" => ["true"], "enabled" => false} }}
source = Mcp.load(config)
[] = source.toolstrue = source.status == %{"off" => "disabled"}true = is_pid(source.sup)
Mcp.close(source)
IO.puts("ok: #{inspect(source.status)}")2. Per-server isolation — one disabled, one that fails to connect
Section titled “2. Per-server isolation — one disabled, one that fails to connect”A server whose command cannot even start (or whose handshake fails) does not stop the others —
it is recorded as "failed" and simply contributes no tools. This is what makes load/2 safe to
call against config you do not fully trust.
alias Toolnexus.Mcp
config = %{ "mcpServers" => %{ "off" => %{"type" => "local", "command" => ["true"], "enabled" => false}, "broken" => %{ "type" => "local", "command" => ["/nonexistent-toolnexus-docs-binary"], "timeout" => 500 } }}
source = Mcp.load(config)
true = source.status["off"] == "disabled"true = source.status["broken"] == "failed"# Neither server contributed a tool.[] = source.tools# Only isolation matters here — a failed server still leaves the supervisor alive# for the servers that DID connect.true = Process.alive?(source.sup)
Mcp.close(source)false = Process.alive?(source.sup)
IO.puts("ok: #{inspect(source.status)}")3. Malformed config raises before anything connects
Section titled “3. Malformed config raises before anything connects”load/2 calls parse_config/1 first — a JSON syntax error surfaces immediately, as a raise, not
as a per-server "failed" status. No process is spawned.
alias Toolnexus.Mcp
raised? = try do Mcp.load(~s({not valid json)) false rescue _ -> true end
true = raised?
IO.puts("ok: malformed config raised before connecting")Options
Section titled “Options”| Option | Default | What it does |
|---|---|---|
:wait_for |
nil |
(Request.t() -> Answer.t()). When set, the client advertises the MCP elicitation capability and bridges an inbound elicitation/create onto it (§10). See Toolnexus.Mcp.Protocol.elicitation_to_request. |
:deadline (alias :timeout) |
:infinity |
Overall load deadline in ms. See the ctx-aware form. |
What you get back
Section titled “What you get back”| Field | Type | What it is |
|---|---|---|
tools |
[%Toolnexus.Tool{}] |
Every tool from every connected server, concatenated. |
status |
%{name => status} |
"connected", "failed", or "disabled" per configured server. |
sup |
pid() |
The DynamicSupervisor holding all connections — pass to close/1. |
connections |
%{name => pid()} |
The connection process per connected server. |
See also
Section titled “See also”Toolnexus.Mcp.load(ctx-aware form) — the:deadlineand:wait_foroptionsToolnexus.Mcp.list_tools— inventory without leaving anything connectedToolnexus.Mcp.parse_config— parse and validate config without connectingToolnexus.Mcp.Protocol.elicitation_to_request— what:wait_forreceivesToolnexus.Mcp.close/1— disconnect every server and terminate the supervisor