# MCP Tools — Agent consuming MCP servers

> **Scope:** stacks=["dotnet"]
> **Layer:** 2 (on-keyword)
> **Keywords:** mcp, model context protocol, mcp client, consume mcp server, mcp tools, external tools, McpClient, HttpClientTransport, MRTR
> **Read by Claude in:** implement (quando um agente usa tools de um MCP server externo)

**Verified against:** ModelContextProtocol 2.2.0 + Microsoft.Agents.AI 1.20.0 + Microsoft.Extensions.AI 10.9.0 (ai-pin 2026-09-08) + revisão de spec MCP **2026-07-28**. **Verificação documental — o ai-kit não exercita MCP**, e por isso este standard NÃO carimba `provado por`. Todos os nomes de tipo abaixo foram **medidos por reflexão sobre `ModelContextProtocol.Core` 2.2.0** em 2026-09-08. Last-verified: 2026-09-08.

---

## O que é (no contexto de um agente MAF)

MCP (Model Context Protocol) é um protocolo aberto que padroniza como um agente expõe e consome tools. Um agente MAF pode se conectar a qualquer **MCP server externo** (GitHub, filesystem, Postgres, ferramentas internas de outro projeto) e usar as tools desse servidor exatamente como se fossem tools nativas — o MAF trata tudo como `AITool`.

Casos de uso típicos:

- Agente de repositório que usa o GitHub MCP server para listar PRs e issues
- Agente de dados que usa o PostgreSQL MCP server para rodar queries
- Agente de documentação que usa o filesystem MCP server para ler arquivos locais
- Agente corporativo que consome um MCP server interno de outro projeto

---

## A API

**Package:**

```xml
<!-- no ai-pin.json -->
<PackageReference Include="ModelContextProtocol" Version="2.2.0" />

<!-- SÓ se o servidor expuser tools long-running. Fora do pin; apurado no nuget.org em 2026-09-08. -->
<PackageReference Include="ModelContextProtocol.Extensions.Tasks" Version="2.2.0" />
```

> O SDK C# saiu do preview: **2.2.0**, publicado em 2026-08-13. A faixa `0.1.*` que este standard trazia é de duas majors atrás.

### Renomes da 2.x — o que não compila mais

| Nome antigo (0.1.x / 1.x) | Nome atual, **medido em 2.2.0** |
|---|---|
| `McpClientFactory.CreateAsync(transport)` | **`McpClient.CreateAsync(transport)`** — estático em `McpClient` |
| `SseClientTransport` | **`HttpClientTransport`** com `HttpClientTransportOptions.TransportMode = HttpTransportMode.Sse` |

`McpClientFactory` e `SseClientTransport` **não existem** em `ModelContextProtocol.Core` 2.2.0 — verificado por varredura dos tipos exportados. Não são alternativas; são nomes mortos.

**Fluxo de consumo:**

1. `McpClient.CreateAsync(transport)` — conecta ao MCP server e devolve um client descartável
2. `mcpClient.ListToolsAsync()` — descobre as tools disponíveis no servidor
3. Passa as tools ao agente via `AsAIAgent(..., tools: [.. mcpTools])`

**`McpClientTool` herda de `AIFunction`.** Medido: `ModelContextProtocol.Client.McpClientTool : Microsoft.Extensions.AI.AIFunction`, e `AIFunction` é um `AITool`. Ou seja, **o `.Cast<AITool>()` é desnecessário** em qualquer ponto que aceite `IEnumerable<AITool>` — a conversão é implícita por herança. Mantenha o cast apenas quando precisar de um `AITool[]` a partir de um `Concat` de coleções de tipos diferentes.

**Transports suportados (medidos):**

| Transport | Classe | Opções | Quando usar |
|-----------|--------|--------|------------|
| stdio | `StdioClientTransport` | `StdioClientTransportOptions`: `Name`, `Command`, `Arguments`, `WorkingDirectory`, `EnvironmentVariables`, `ShutdownTimeout` | MCP server local (processo filho — `npx`, executável) |
| Streamable HTTP | `HttpClientTransport` | `HttpClientTransportOptions`: `Endpoint`, `TransportMode` (`AutoDetect` \| `StreamableHttp` \| `Sse`), `AdditionalHeaders`, `OAuth`, `ConnectionTimeout` | MCP server remoto |

`HttpTransportMode.Sse` existe para falar com servidores da era HTTP+SSE, que a revisão `2026-07-28` marcou como **Deprecated**. Para servidor novo, `StreamableHttp` (ou `AutoDetect`).

**Regra importante:** use `await using` no `mcpClient` — o dispose fecha o transporte corretamente.

### Modo stateless quebra três métodos

A revisão `2026-07-28` removeu as sessões, e o servidor C# 2.2.0 nasce **stateless por default**. Consequência para quem escreve **cliente**: `ElicitAsync`, `SampleAsync` e `RequestRootsAsync` dependem de uma sessão persistente e de requests server→client, e **falham** contra um servidor stateless. Sampling e Roots estão, além disso, **deprecados** pela SEP-2577 — o próprio `McpServerOptions.MaxSamplingOutputTokens` carrega essa frase no `[Obsolete]`.

A via que funciona nas duas configurações é o **MRTR** (*More-Round-Trip Response*), medido em `ModelContextProtocol.Protocol`:

```csharp
// No SERVIDOR: em vez de chamar de volta o cliente, devolva o pedido de input.
throw new InputRequiredException(new InputRequiredResult
{
    RequestState = state,
    InputRequests = { ["confirm"] = InputRequest.ForElicitation(elicitParams) },
});

// No CLIENTE: responda os pedidos e siga.
await mcpClient.ResolveInputRequestsAsync(answers, ct);
```

`InputRequest` tem as três fábricas `ForElicitation`, `ForSampling` e `ForRootsList`.

### Tasks (long-running) mudaram de pacote E de wire

Long-running saiu do core para **`ModelContextProtocol.Extensions.Tasks`** (2.2.0, apurado em 2026-09-08). **Não há compatibilidade de wire** com a implementação experimental das 1.3/1.4: quem usava aquilo **migra**, não apenas sobe a versão. Sinal de que é preciso migrar: código que fala tasks e continua compilando depois do upgrade — o formato mudou no fio, não na API.

### Custo de definição de tool por turno

Toda tool descoberta entra na **definição** enviada ao modelo a cada turno — nome, descrição e JSON Schema dos parâmetros. Um servidor MCP com 60 tools cobra isso em todo request, mesmo nos turnos em que nenhuma é chamada. Pode-se podar com `mcpClient.RemoveKnownTools([...])` / `AddKnownTools([...])`, ou registrando só o recorte relevante no agente. O critério de quantas tools cabem num agente é de `ai-agents-sweet-spot` — não é duplicado aqui.

---

## Exemplo C#

Agente que consome o GitHub MCP server e responde perguntas sobre repositórios:

```csharp
using ModelContextProtocol;
using ModelContextProtocol.Client;
using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;

// 1. Conectar ao MCP server (GitHub via npx)
await using var mcpClient = await McpClient.CreateAsync(
    new StdioClientTransport(new()
    {
        Name = "GitHubServer",
        Command = "npx",
        Arguments = ["-y", "--verbose", "@modelcontextprotocol/server-github"],
    }));

// 2. Descobrir as tools do servidor
IList<McpClientTool> mcpTools = await mcpClient.ListToolsAsync();

// 3. Criar o agente MAF com as MCP tools registradas
// McpClientTool herda de AIFunction (que é AITool) — sem cast.
// _modelRegistry vem de ModelRegistry (ai-agents-providers-model-registry)
AIAgent agent = _modelRegistry.GetChatClient("text-default")
    .AsAIAgent(
        instructions: "You answer questions about GitHub repositories.",
        tools: [.. mcpTools]);

// 4. Usar o agente normalmente
var response = await agent.RunAsync(
    "Summarize the last 4 commits to microsoft/agent-framework");
Console.WriteLine(response.Text);
```

**Variante com MCP server remoto (Streamable HTTP):**

```csharp
using ModelContextProtocol.Client;

await using var mcpClient = await McpClient.CreateAsync(
    new HttpClientTransport(new HttpClientTransportOptions
    {
        Name = "RemoteToolsServer",
        Endpoint = new Uri("https://tools.example.com/mcp"),
        TransportMode = HttpTransportMode.StreamableHttp,   // .Sse só para servidor legado
    }));

var mcpTools = await mcpClient.ListToolsAsync();
// Mesmo padrão para criar o agente...
```

**Múltiplos MCP servers:**

```csharp
// Conectar a dois servidores e combinar as tools
await using var githubClient = await McpClient.CreateAsync(
    new StdioClientTransport(new()
    {
        Name = "GitHub",
        Command = "npx",
        Arguments = ["-y", "@modelcontextprotocol/server-github"],
    }));

await using var fsClient = await McpClient.CreateAsync(
    new StdioClientTransport(new()
    {
        Name = "Filesystem",
        Command = "npx",
        Arguments = ["-y", "@modelcontextprotocol/server-filesystem", "/repo"],
    }));

AITool[] allTools = [
    .. await githubClient.ListToolsAsync(),
    .. await fsClient.ListToolsAsync(),
];   // McpClientTool : AIFunction : AITool — a conversão é por herança

AIAgent agent = _modelRegistry.GetChatClient("text-default")
    .AsAIAgent(
        instructions: "You help navigate the codebase.",
        tools: allTools);
```

---

## Popular MCP Servers

| Server npm | Propósito |
|-----------|-----------|
| `@modelcontextprotocol/server-github` | Repos, issues, PRs do GitHub |
| `@modelcontextprotocol/server-filesystem` | Operações no sistema de arquivos |
| `@modelcontextprotocol/server-sqlite` | Acesso a banco de dados SQLite |
| `@modelcontextprotocol/server-postgres` | Acesso a banco de dados PostgreSQL |

---

## MCP server vs tool nativa

| Cenário | Decisão | Motivo |
|---------|---------|--------|
| O servidor existe e é mantido por terceiros (GitHub, Postgres...) | Consumir MCP server | Reutilização imediata; sem esforço de manutenção |
| A lógica é específica deste projeto (ex.: buscar pedido no banco) | Escrever tool nativa `[Description]` | Mais simples, sem overhead de protocolo, testável diretamente |
| O servidor interno já existe e está publicado como MCP | Consumir MCP server | Evita duplicar lógica |
| Precisa de controle total de retries, logging, validação | Tool nativa + middleware MAF | Middleware `ResilientToolMiddleware` dá controle fino |

**Regra**: prefira MCP server quando o servidor já existe e é mantido por terceiros. Prefira tool nativa quando a lógica é do próprio projeto.

---

## Anti-patterns

| Anti-pattern | Problema | Solução |
|-------------|---------|---------|
| Criar um MCP server próprio só para o agente do mesmo projeto chamar | Overhead desnecessário de protocolo e deploy | Escrever tool nativa com `[Description]` + `AIFunctionFactory.Create()` |
| Não tratar indisponibilidade do MCP server | O MCP server pode estar offline ou lento; o agente quebra | Usar `ResilientToolMiddleware` para capturar `HttpRequestException` e `TimeoutException` (ver `ai-agents-production`) |
| Não usar `await using` no `mcpClient` | Processo filho (npx) fica zumbi; leak de recursos | Sempre `await using var mcpClient = await McpClient.CreateAsync(...)` |
| Usar `McpClientFactory` ou `SseClientTransport` | Não existem na 2.x — o código não compila | `McpClient.CreateAsync` e `HttpClientTransport` |
| Contar com `ElicitAsync` / `SampleAsync` / `RequestRootsAsync` | Exigem sessão persistente; o servidor 2.2.0 é stateless por default, e Sampling/Roots estão deprecados (SEP-2577) | MRTR: `InputRequiredException` + `InputRequest.ForElicitation(...)` |
| Só subir a versão do pacote de Tasks vindo das 1.3/1.4 | Não há compatibilidade de **wire**; compila e falha no fio | Migrar para `ModelContextProtocol.Extensions.Tasks` conscientemente |
| Registrar as 60 tools de um servidor "porque estavam lá" | A definição de toda tool é reenviada a cada turno | Podar (`RemoveKnownTools`) ou registrar só o recorte; critério em `ai-agents-sweet-spot` |
| Hardcodar o model diretamente (`new ChatClient("gpt-4o", key)`) | Viola provider-agnostic; difícil de trocar | Sempre `_modelRegistry.GetChatClient("alias")` (ver `ai-agents-providers-model-registry`) |


---

## Checklist (verifiable by morph-eval)

- [ ] `ModelContextProtocol` presente no `.csproj` com versão exata (2.2.0 no pin) — sem faixa flutuante
- [ ] `await using var mcpClient` — dispose correto do transporte
- [ ] `McpClient.CreateAsync(...)` — nenhuma ocorrência de `McpClientFactory` ou `SseClientTransport`
- [ ] `ListToolsAsync()` chamado antes de criar o agente
- [ ] Nenhum `.Cast<AITool>()` supérfluo (`McpClientTool` já é `AIFunction`)
- [ ] Nenhum `ElicitAsync`/`SampleAsync`/`RequestRootsAsync` contra servidor stateless
- [ ] Agente criado via `_modelRegistry.GetChatClient("alias").AsAIAgent(...)` — provider-agnostic
- [ ] `ResilientToolMiddleware` ou equivalente registrado (o MCP server pode ficar indisponível)
- [ ] Nenhum `Guid.NewGuid()` — usar `Guid.CreateVersion7()` se IDs são necessários

---

## References

- `ai-agents-setup` — pacotes base MAF, DI, quick start
- `ai-agents-production` — panorama de MCP Integration, `ResilientToolMiddleware`, middleware pipeline
- `ai-agents-mcp-server` — expor capacidades do próprio projeto como MCP server
- `integration/mcp/mcp-tools.md` — MCPs no workflow do morph-spec (Playwright, Context7, Neon) — contexto complementar, escopo diferente
- [ModelContextProtocol C# SDK](https://github.com/modelcontextprotocol/csharp-sdk) — `docs/concepts/**` (lido 2026-09-08)
- [MCP — versionamento da especificação](https://modelcontextprotocol.io/specification/versioning) — revisão corrente `2026-07-28` (lido 2026-09-08)
- [NuGet — ModelContextProtocol 2.2.0](https://www.nuget.org/packages/ModelContextProtocol/2.2.0) (publicado 2026-08-13)
- [Using MCP Tools — MAF docs](https://learn.microsoft.com/agent-framework/user-guide/model-context-protocol/using-mcp-tools)

---

*MORPH-SPEC by Polymorphism Tech — ai-agents/mcp-tools.md v2.0 (2026-09-08)*
