McpSource.ResourceTools
C# · package Toolnexus · SPEC §2 · McpResources.cs
public List<ITool> McpSource.ResourceTools();// [0] list_mcp_resources { server? } → "server uri — name (mimeType): description" per line// [1] read_mcp_resource { server, uri } → the resource's textTwo read-only tools (Hints.ReadOnly = true) bound to the source’s connected servers. Opt-in: they
are not added to a toolkit on their own; register them with ExtraTools. They stop working once
the McpSource is disposed. A source with no connected server still returns both; they report
that no server is connected. An unknown server or uri is an error result, not an exception.
When to use it
Section titled “When to use it”An MCP server exposes data as resources (files, records, docs) rather than tools, and you want the model to browse and read them itself.
Why this and not the alternative
Section titled “Why this and not the alternative”Examples
Section titled “Examples”1. The two tools, even with no server
Section titled “1. The two tools, even with no server”using Toolnexus;
await using var src = await McpSource.LoadAsync("""{"mcp":{}}""");var tools = src.ResourceTools();
if (string.Join(",", tools.Select(t => t.Name)) != "list_mcp_resources,read_mcp_resource") throw new Exception("names");if (!tools.All(t => t.Hints!.ReadOnly)) throw new Exception("expected read-only hints");Console.WriteLine($"ok: {string.Join(", ", tools.Select(t => t.Name))} (read-only)");2. List and read a server’s resource
Section titled “2. List and read a server’s resource”using Toolnexus;
// TOOLNEXUS_REPO is set by the docs test runner; in your own code use your server's path.var repo = Environment.GetEnvironmentVariable("TOOLNEXUS_REPO") ?? ".";var server = Path.Combine(repo, "examples", "mcp-progress-server.mjs");var cfg = "{\"mcp\":{\"progress\":{\"command\":\"node\",\"args\":[" + System.Text.Json.JsonSerializer.Serialize(server) + "]}}}";
await using var src = await McpSource.LoadAsync(cfg);var (list, read) = (src.ResourceTools()[0], src.ResourceTools()[1]);
var listed = await list.ExecuteAsync(new Dictionary<string, object?>());var text = await read.ExecuteAsync(new Dictionary<string, object?> { ["server"] = "progress", ["uri"] = "fixture://hello" });var missing = await read.ExecuteAsync(new Dictionary<string, object?> { ["server"] = "progress", ["uri"] = "fixture://nope" });
if (text.Output != "hello resource" || !missing.IsError) throw new Exception(text.Output);Console.WriteLine($"ok: {listed.Output} → \"{text.Output}\"");3. Hand them to the model through a toolkit
Section titled “3. Hand them to the model through a toolkit”using Toolnexus;
var repo = Environment.GetEnvironmentVariable("TOOLNEXUS_REPO") ?? ".";var server = Path.Combine(repo, "examples", "mcp-progress-server.mjs");var cfg = "{\"mcp\":{\"progress\":{\"command\":\"node\",\"args\":[" + System.Text.Json.JsonSerializer.Serialize(server) + "]}}}";
await using var src = await McpSource.LoadAsync(cfg);await using var tk = await Toolkit.CreateAsync(new Toolkit.Options { ExtraTools = src.ResourceTools() });
var r = await tk.ExecuteAsync("read_mcp_resource", new Dictionary<string, object?> { ["server"] = "progress", ["uri"] = "fixture://hello" });if (r.Output != "hello resource") throw new Exception(r.Output);Console.WriteLine($"ok: the toolkit offers {string.Join(", ", tk.Tools().Select(t => t.Name))}");See also
Section titled “See also”McpSource.LoadAsync— Read an mcp.json, connect every local stdio and remote streamable-HTTP server, expose each server tool as a Tool.McpSource.LoadAsync— The ctx-aware load: bound connection time and cancel a slow or hung server without leaking a child process.McpSource.ListMcpToolsAsync— 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.