PinkRooster.ToolCollections.Mcp
0.4.0-preview.1
dotnet add package PinkRooster.ToolCollections.Mcp --version 0.4.0-preview.1
NuGet\Install-Package PinkRooster.ToolCollections.Mcp -Version 0.4.0-preview.1
<PackageReference Include="PinkRooster.ToolCollections.Mcp" Version="0.4.0-preview.1" />
<PackageVersion Include="PinkRooster.ToolCollections.Mcp" Version="0.4.0-preview.1" />
<PackageReference Include="PinkRooster.ToolCollections.Mcp" />
paket add PinkRooster.ToolCollections.Mcp --version 0.4.0-preview.1
#r "nuget: PinkRooster.ToolCollections.Mcp, 0.4.0-preview.1"
#:package PinkRooster.ToolCollections.Mcp@0.4.0-preview.1
#addin nuget:?package=PinkRooster.ToolCollections.Mcp&version=0.4.0-preview.1&prerelease
#tool nuget:?package=PinkRooster.ToolCollections.Mcp&version=0.4.0-preview.1&prerelease
PinkRooster.ToolCollections.Mcp
Turns a connected Model Context Protocol (MCP) server into a tool collection, so an agent gets the server's tools with the same instructions, constraints, approvals and context as any other collection.
dotnet add package PinkRooster.ToolCollections.Mcp --prerelease
Needs the .NET 10 SDK and an MCP server to connect to; the example below also needs internet access (Context7 works without a key; CONTEXT7_API_KEY is optional), and dotnet add package PinkRooster.Agents --prerelease to build the agent, with chatClient any IChatClient (how to make one). A server started with npx needs Node.js. It depends on PinkRooster.ToolCollections and ModelContextProtocol.Core, and is the only package here that references MCP.
Use it with PinkRooster.Agents or any Microsoft Agent Framework (MAF) agent.
McpToolCollection carries the server's tools and the instructions the server sends for the model. Connect in one line, and keep the collection alive while
the agent runs:
using PinkRooster.ToolCollections.Mcp;
await using var docs = await McpToolCollection.ConnectHttpAsync("Context7", new Uri("https://mcp.context7.com/mcp"));
using PinkRooster.Agents;
var agent = chatClient
.CreateAgent()
.WithRole("You answer questions about libraries from their current documentation.")
.WithTools(docs.WithConstraint("Name the library version you looked up."))
.Build();
var response = await agent.RunAsync("How do I stream a chat response?");
Console.WriteLine(response.Text);
- Connecting.
ConnectHttpAsyncreaches a remote server. Itsheadersargument leaves out a header whose value is null or blank, so an optional key can come straight from the environment:new Dictionary<string, string?> { ["CONTEXT7_API_KEY"] = Environment.GetEnvironmentVariable("CONTEXT7_API_KEY") }. It also takes your ownhttpClient, andconfigurefor the rest of the transport options (mode, timeout, OAuth). - On an agent class.
[AgentMcpHttpTools("Context7", "https://mcp.context7.com/mcp")]gives aDeclaredAgentthe server's tools. The endpoint is a string, because an attribute cannot hold aUri. The agent connects on first use, once per instance: the first run awaits the connection and passes its cancellation token to it, andawait agent.InitializeAsync()connects at start-up instead, so a server that cannot be reached fails there. Either way it connects once, andawait usingorDisposeAsyncon the agent closes the connection.ApiKeyEnvironmentVariable(sent asBearer <key>inHeaderName, defaultAuthorization) andRequireApprovalForDestructiveToolscover the common cases; for more, callConnectHttpAsyncinConfigure.[AgentMcpStdioTools("Files", "npx", "-y", "@modelcontextprotocol/server-filesystem", ".")]does the same for a local server started as a child process: name, command, then its arguments.ConnectStdioAsyncstarts a local server, as inConnectStdioAsync("Files", "npx", ["-y", "@modelcontextprotocol/server-filesystem", "."]). The server process gets this process's whole environment, your model key included; for a server you do not trust passinheritEnvironment: false, and give it what it needs throughenvironment. PassstandardErrorto see why a server failed to start. AndConnectAsynctakes anyIClientTransport. Each takesclientOptionsfor what the client offers the server (sampling, elicitation, its name) and aloggerFactoryto see what the client and transport do. A connect that fails throwsMcpException, naming the server and what to check; catch it to run without that server. - Who owns the client. A collection from a
Connectmethod owns its client: disposing the collection disconnects it. To share one client you manage yourself, pass it toMcpToolCollection.CreateAsync("Name", client); that collection leaves the client connected. Either way,docs.Clientreaches the server's resources and prompts. - Server instructions become the collection's first instruction. They are the server's text in
your agent's system prompt, so pass
includeServerInstructions: falsefor a server you do not trust. - Approval applies to the tools you name with
RequireApproval(...), and, when you callRequireApprovalForDestructiveTools(), to every tool the server does not mark read-only or non-destructive. The hints can only add approval, never remove it: a server you do not trust can mislabel a tool, so name its dangerous tools too. - Kind. A tool the server marks read-only has the kind
Read; every other tool has none, so a host that lets edits run unasked never runs a server's write that way.WithKind(ToolKind.Edit, "write_note")gives a tool you know a kind yourself. A read-only hint is the server's word: for a server you do not trust, take the kind away withWithKind(ToolKind.None, ...). - Choosing and renaming. The
selectargument sees each tool and returns it, a renamed copy (tool.WithName("docs_search")), or null to leave it out. Rename there when two servers share a tool name. A kept name that model providers reject, such asfiles.read, or two kept tools with one name, throws with the rename to write. - The list is fixed. The tools are the ones the server listed when the collection was created. When the server changes them, create a new collection and a new agent.
- A server that goes away is not reconnected. A call to its tools can then wait instead of failing,
so give agent runs a cancellation token with a deadline. Once you dispose a collection from a
Connectmethod, its tools fail at once, calls still waiting included.
For functions that do not come from an MCP server, see ExternalToolCollection in PinkRooster.ToolCollections.
Context from an MCP resource. A server that keeps state in a resource can feed the collection's
context. Here gitServer is an MCP client of your own, already connected:
using ModelContextProtocol.Protocol;
using PinkRooster.ToolCollections.Mcp;
var git = await McpToolCollection.CreateAsync("Git", gitServer);
git.WithContext(async ct =>
{
var branch = await gitServer.ReadResourceAsync("git://current-branch", cancellationToken: ct);
return $"## Git\nBranch: {branch.Contents.OfType<TextResourceContents>().FirstOrDefault()?.Text}";
});
That reads the resource before every model call, tool loop included, so each call costs one extra round trip to the server.
Feedback and license
Report a bug or ask a question on GitHub Issues. The package is released under the MIT license.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- ModelContextProtocol.Core (>= 2.2.0)
- PinkRooster.ToolCollections (= 0.4.0-preview.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.4.0-preview.1 | 52 | 10/9/2026 |