Skip to content

Toolnexus.Mcp.resource_tools

Elixir · package toolnexus · SPEC §2

@spec resource_tools(Toolnexus.Mcp.Source.t() | nil) :: [Toolnexus.Tool.t()]
def resource_tools(source)
# => [list_mcp_resources, read_mcp_resource]

Two tools bound to the source’s connected servers: list_mcp_resources %{server?} prints one line per resource (server uri — name (mimeType): description) and read_mcp_resource %{server, uri} returns its text. Both carry %Toolnexus.ToolHints{read_only: true}. They are opt-in — Mcp.load/2 does not add them; register them yourself — and they stop working when the source is closed (a closed server reports an error line, not a crash). nil, or a source with no connected server, still returns both tools; listing then says No resources.

  • MCP servers that expose data as resources (files, schemas, docs) rather than tools.
  • A read-only agent mode — both tools are read-only, so a plan-mode before_tool gate lets them through.
alias Toolnexus.{Context, Mcp, ToolHints}
[list, read] = Mcp.resource_tools(nil)
true = list.name == "list_mcp_resources" and read.name == "read_mcp_resource"
true = list.hints == %ToolHints{read_only: true}
%{output: "No resources.", metadata: %{count: 0}} = list.execute.(%{}, %Context{})
IO.puts("ok: #{list.name}, #{read.name}")
alias Toolnexus.{Mcp, Toolkit}
source = Mcp.load(%{})
tk = Toolkit.register(Toolnexus.create_toolkit!(builtins: false), Mcp.resource_tools(source))
r = Toolkit.execute(tk, "list_mcp_resources", %{})
true = r.output == "No resources."
Mcp.close(source)
IO.puts("ok: resource tools on the toolkit")

The shared fixture examples/mcp-progress-server.mjs serves one resource, fixture://hello. Skipped when node is not installed.

alias Toolnexus.{Context, Mcp}
repo = System.get_env("TOOLNEXUS_REPO") || File.cwd!()
fixture = Path.join(repo, "examples/mcp-progress-server.mjs")
if System.find_executable("node") && File.exists?(fixture) do
src = Mcp.load(%{"progress" => %{"command" => ["node", fixture], "timeout" => 5000}})
[list, read] = Mcp.resource_tools(src)
"progress fixture://hello — hello (text/plain): A fixed text resource." = list.execute.(%{}, %Context{}).output
%{output: "hello resource", is_error: false} =
read.execute.(%{"server" => "progress", "uri" => "fixture://hello"}, %Context{})
Mcp.close(src)
IO.puts("ok: read fixture://hello")
else
IO.puts("ok: skipped (no node)")
end
  • Toolnexus.Mcp.load — Read an mcp.json, connect every local stdio and remote streamable-HTTP server, expose each server tool as a Tool.
  • Toolnexus.Mcp.load — The ctx-aware load: bound connection time and cancel a slow or hung server without leaking a child process.
  • Toolnexus.Mcp.list_tools — List what each configured server would expose, plus per-server status, without wiring it into a toolkit.
  • Toolnexus.Mcp.parse_config — Parse and validate config without connecting — the fast fail for a malformed or misspelled server block.