Toolnexus.Mcp.load (ctx-aware)
Elixir · package toolnexus · SPEC §2 · elixir/lib/toolnexus/mcp.ex
@spec load(String.t() | map(), keyword()) :: Toolnexus.Mcp.Source.t()def load(input, opts \\ [])
# opts:# :deadline (alias :timeout) — ms, default :infinity# :wait_for — (Toolnexus.Request.t() -> Toolnexus.Answer.t())There is no separate function here — Elixir’s load/2 is always the ctx-aware form; the plain
call on the load page is this same function with the options omitted.
This page is scoped to the two options that make it cancellation-aware: :deadline (bound the
whole connect phase) and :wait_for (bridge inbound elicitation).
When to use it
Section titled “When to use it”- A hung or slow stdio server must not stall your agent’s boot. Without
:deadline, a server that starts but never completes itsinitializehandshake blocksload/2forever. With it, the whole load aborts at the deadline — in-flight connects are torn down (brutal_kill), no child process is left running. - A server wants to ask the human something mid-connect (a form, an OAuth URL) via MCP
elicitation. Pass
:wait_forand the client advertises theelicitationcapability and routes the server’selicitation/createthrough your handler as a §10Toolnexus.Request/Toolnexus.Answer— seeToolnexus.Mcp.Protocol.elicitation_to_requestfor the exact mapping. - Both together — a
:wait_forthat itself might hang waiting on a human is exactly the case:deadlineexists to bound.
Why this and not the alternative
Section titled “Why this and not the alternative”Examples
Section titled “Examples”1. Deadline exceeded aborts the whole load
Section titled “1. Deadline exceeded aborts the whole load”["sleep", "5"] is a local process that never speaks MCP — it stands in for a hung server without
touching the network. A :deadline shorter than that raises, and no server ends up connected.
alias Toolnexus.Mcp
config = %{ "mcpServers" => %{ "slow" => %{"type" => "local", "command" => ["sleep", "5"], "timeout" => 30_000} }}
{message, raised?} = try do Mcp.load(config, deadline: 100) {nil, false} rescue e -> {Exception.message(e), true} end
true = raised?true = String.contains?(message, "deadline")
IO.puts("ok: #{message}")2. :timeout is accepted as the same option
Section titled “2. :timeout is accepted as the same option”alias Toolnexus.Mcp
config = %{ "mcpServers" => %{ "slow" => %{"type" => "local", "command" => ["sleep", "5"], "timeout" => 30_000} }}
raised? = try do # :timeout is read as a fallback for :deadline — same effect, same message. Mcp.load(config, timeout: 100) false rescue _ -> true end
true = raised?
IO.puts("ok: :timeout aborted the load")3. :wait_for alongside a bounded deadline, with server isolation intact
Section titled “3. :wait_for alongside a bounded deadline, with server isolation intact”Passing :wait_for never changes how disabled or failing servers are handled — it only affects
servers that actually elicit. Here nothing elicits, so this demonstrates the option is safe to
always pass, combined with a real deadline.
alias Toolnexus.{Answer, Mcp, Request}
# A wait_for you would actually wire up: auto-decline every elicitation.wait_for = fn %Request{id: id} -> %Answer{id: id, ok: false, reason: "declined"} end
config = %{ "mcpServers" => %{ "off" => %{"type" => "local", "command" => ["true"], "enabled" => false} }}
source = Mcp.load(config, wait_for: wait_for, deadline: 5_000)
true = source.status == %{"off" => "disabled"}[] = source.tools
Mcp.close(source)
IO.puts("ok: wait_for + deadline together, disabled server unaffected")Options
Section titled “Options”| Option | Aliases | Default | Effect |
|---|---|---|---|
:deadline |
:timeout |
:infinity |
Overall connect-phase bound in ms. On exceed: every in-flight connect task is killed (Task.shutdown/2 with :brutal_kill), the supervisor is stopped, and load/2 raises "toolnexus: MCP load deadline exceeded". |
:wait_for |
— | nil |
(Request.t() -> Answer.t()). Advertises the elicitation MCP capability; an inbound elicitation/create is mapped through elicitation_to_request/1, your function is called, and the answer is mapped back. Without it, the capability is not advertised and a compliant server will not elicit. |
See also
Section titled “See also”Toolnexus.Mcp.load— the base call this page’s options extendToolnexus.Mcp.list_tools— inventory without leaving anything connectedToolnexus.Mcp.parse_config— parse and validate config without connectingToolnexus.Mcp.Protocol.elicitation_to_request— what:wait_forreceives and returns