Skip to content

McpSource.resourceTools

Java · package io.github.muthuishere:toolnexus · SPEC §2

// McpSource
public List<Tool> resourceTools() // [list_mcp_resources, read_mcp_resource]
// also reachable from a toolkit built with an MCP config
public McpSource Toolkit.mcpSource()

Two opt-in, read-only tools bound to the source’s connected servers:

  • list_mcp_resources {server?} — one line per resource: server uri — name (mimeType): description.
  • read_mcp_resource {server, uri} — the resource’s text. An unknown server or uri is an error result, not a throw.

Both carry ToolHints.READ_ONLY and stop working when the source is closed. Nothing registers them for you: add them to a toolkit with tk.register(...).

Your MCP servers expose resources (docs, schemas, files, records) as well as tools, and the model should be able to browse and read them on its own during a run.

1. The smallest useful call — list, then read

Section titled “1. The smallest useful call — list, then read”
import io.github.muthuishere.toolnexus.*;
import java.util.List;
import java.util.Map;
public class Example {
public static void main(String[] args) {
String cfg = "{\"mcpServers\":{\"progress\":{\"command\":[\"node\",\"examples/mcp-progress-server.mjs\"]}}}";
try (McpSource src = McpSource.load(cfg)) {
List<Tool> tools = src.resourceTools();
ToolResult list = tools.get(0).execute(Map.of(), null);
if (!list.output().contains("fixture://hello")) throw new AssertionError(list.output());
ToolResult read = tools.get(1).execute(Map.of("server", "progress", "uri", "fixture://hello"), null);
if (!"hello resource".equals(read.output())) throw new AssertionError(read.output());
System.out.println("ok: " + list.output());
}
}
}

2. The realistic case — register them on a toolkit

Section titled “2. The realistic case — register them on a toolkit”
import io.github.muthuishere.toolnexus.*;
public class Example {
public static void main(String[] args) {
String cfg = "{\"mcpServers\":{\"progress\":{\"command\":[\"node\",\"examples/mcp-progress-server.mjs\"]}}}";
try (Toolkit tk = Toolkit.create(new Toolkit.Options().builtins(false).mcpConfig(cfg))) {
tk.register(tk.mcpSource().resourceTools().toArray(new Tool[0]));
Tool read = tk.get("read_mcp_resource");
if (read == null || !read.hints().readOnly()) throw new AssertionError("not registered read-only");
System.out.println("ok: " + tk.tools().stream().map(Tool::name).filter(n -> n.contains("resource")).toList());
}
}
}

3. The full surface — errors are results

Section titled “3. The full surface — errors are results”
import io.github.muthuishere.toolnexus.*;
import java.util.Map;
public class Example {
public static void main(String[] args) {
String cfg = "{\"mcpServers\":{\"progress\":{\"command\":[\"node\",\"examples/mcp-progress-server.mjs\"]}}}";
try (McpSource src = McpSource.load(cfg)) {
Tool read = src.resourceTools().get(1);
ToolResult missing = read.execute(Map.of("server", "progress", "uri", "fixture://nope"), null);
ToolResult unknown = read.execute(Map.of("server", "nope", "uri", "x"), null);
if (!missing.isError() || !unknown.isError()) throw new AssertionError("expected error results");
System.out.println("ok: " + unknown.output());
}
}
}
  • McpSource.load — Read an mcp.json, connect every local stdio and remote streamable-HTTP server, expose each server tool as a Tool.
  • McpSource.loadWith — The ctx-aware load: bound connection time and cancel a slow or hung server without leaking a child process.
  • McpSource.listMcpTools — List what each configured server would expose, plus per-server status, without wiring it into a toolkit.
  • McpSource.parseConfig — Parse and validate config without connecting — the fast fail for a malformed or misspelled server block.