MCP servers
An agent can offer an MCP server's tools next to its toolsets. Typeshift takes no MCP dependency, so you pick the client.
Servers your app connects
UseMcpServer names a server in the agent's constructor. The name is a key you choose, and your app decides which server it stands for:
UseMcpServer("status_page")
.ReadOnly();Your app connects the server by implementing IMcpToolProvider. Its GetToolsAsync receives the name and returns that server's tools. This provider uses the official ModelContextProtocol.Core client and builds the server's URL from the name:
internal sealed class McpToolProvider : IMcpToolProvider, IAsyncDisposable
{
private readonly Dictionary<string, McpClient> _clients = [];
public async ValueTask<IReadOnlyList<AITool>> GetToolsAsync(string server, CancellationToken ct)
{
if (!_clients.TryGetValue(server, out var client))
{
var transport = new HttpClientTransport(new() { Endpoint = new Uri($"https://mcp.example.com/{server}") });
_clients[server] = client = await McpClient.CreateAsync(transport, cancellationToken: ct);
}
return [.. await client.ListToolsAsync(cancellationToken: ct)];
}
// The scope that resolved the provider disposes it, and its connections with it
public async ValueTask DisposeAsync()
{
foreach (var client in _clients.Values)
{
await client.DisposeAsync();
}
}
}Registering the provider
Register the provider with WithMcpToolProvider<T>():
services.AddTypeshift()
.WithMcpToolProvider<McpToolProvider>();The provider is scoped, and Typeshift resolves it from the run's services, so each scope, such as a web request, gets its own. That lets it connect each customer with their own credentials. When the scope ends, it disposes the provider along with its connections, so the provider above opens one connection per server for each scope.
How the agent offers the tools
In the tool list the model receives, the servers' tools come after the agent's own. UseMcpServer(...).RequiresApproval() makes every call of the server's tools need approval, as Approval and modes describes. ReadOnly() marks every tool of the server as reading only, so plan mode offers them all. Use it only for a server whose tools change nothing.
The run fails when a server's tool is named like one of the agent's own tools or like a tool of another server it uses, because the model would otherwise see only one of them. The error names the server and the tool. An agent that uses servers also fails its run when no provider is registered.
A server's tools are untrusted
Treat a server's tool descriptions and results as untrusted text, never as instructions. The model reads both, so a server you do not control puts its words into the model's context.
Servers the model provider connects
Some model providers call MCP servers themselves. For those, put Microsoft.Extensions.AI's HostedMcpServerTool in the chat options:
WithChatOptions()
.WithModel("anthropic", "claude-sonnet-5-5")
.WithTools(new HostedMcpServerTool("status_page", new Uri("https://mcp.example.com/status")));The model provider then connects to the server itself, and your IMcpToolProvider takes no part. A hosted tool carries no ReadOnly() mark, so plan mode hides it.
Related
- Approval and modes: asking before a server's tools run.
- Plan mode: which tools plan mode offers.
- Agents: offering toolsets next to servers.
- Builders:
UseMcpServeramong the other calls of an agent's constructor.