DEV Community

Cover image for 🧩 Minha Primeira Comunicação com MCP e .NET – Parte 2
Danilo O. Pinheiro, dopme.io
Danilo O. Pinheiro, dopme.io

Posted on

🧩 Minha Primeira Comunicação com MCP e .NET – Parte 2

Integração Completa com gRPC

Nesta segunda parte da série "Minha Primeira Comunicação com MCP e .NET", exploramos como realizar uma integração completa com gRPC, permitindo que o MCP (Model Context Protocol) comunique-se com aplicações .NET de maneira eficiente, tipada e de alta performance.


🚀 Introdução

O MCP (Model Context Protocol) surge como uma camada de interoperabilidade entre modelos de linguagem, agentes e aplicações corporativas. No ecossistema .NET, a integração com gRPC é uma escolha natural, combinando tipagem forte, baixa latência e eficiência binária via Protocol Buffers — características ideais para comunicação entre processos e serviços distribuídos.

Este artigo demonstra, de forma arquitetural e prática, como criar uma ponte robusta entre MCP e aplicações .NET via gRPC, garantindo integração segura e escalável para sistemas modernos.


⚙️ O que é gRPC e por que usá-lo?

O gRPC (Google Remote Procedure Call) é um framework de comunicação que utiliza Protocol Buffers (protobuf) como formato de serialização binária. Ele substitui o tradicional REST em cenários que exigem alta performance, streaming bidirecional e comunicação eficiente entre microsserviços.

Benefícios principais:

  • 🚀 Performance superior: comunicação até 7x mais rápida que JSON/REST
  • 🔒 Contrato tipado: validação em tempo de compilação entre cliente e servidor
  • ⚙️ Streaming nativo: suporte a comunicação unidirecional e bidirecional
  • 🌐 Multiplataforma: compatibilidade com C#, Python, Go, Java, Node.js e mais
  • 📦 Payload compacto: serialização binária reduz tráfego de rede

🧠 Arquitetura da Integração MCP + gRPC + .NET

A integração segue uma arquitetura de orquestração bidirecional onde o MCP atua como cliente inteligente:

┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐ │ │ │ │ │ │ │ MCP Agent │ ◄────► │ gRPC Server │ ◄────► │ Domain Layer │ │ (Cliente) │ │ (.NET 8) │ │ Business Logic │ │ │ │ │ │ │ └─────────────┘ └──────────────────┘ └─────────────────┘ ▲ ▲ ▲ │ │ │ Contexto Validação Persistência Semântico & Roteamento & Regras 
Enter fullscreen mode Exit fullscreen mode

Fluxo de comunicação:

  1. O MCP Agent envia comandos contextualizados ou dados estruturados
  2. O Servidor gRPC valida, autentica e roteia as mensagens
  3. A camada de domínio aplica regras de negócio e retorna resultados
  4. Respostas retornam via stream ou chamadas unitárias

🏗️ Implementação Passo a Passo

1️⃣ Criar o Projeto Base

dotnet new grpc -n MCPPipeline.GrpcServer cd MCPPipeline.GrpcServer 
Enter fullscreen mode Exit fullscreen mode

O template gera automaticamente:

  • Estrutura de Protos/ para definição de contratos
  • Pasta Services/ para implementações
  • Configuração Kestrel otimizada para HTTP/2

2️⃣ Definir o Contrato Protobuf

Crie o arquivo Protos/mcp.proto:

syntax = "proto3"; option csharp_namespace = "MCPPipeline.Grpc"; package mcp; // Serviço principal de comunicação MCP service MCPService { // Comando único com resposta direta rpc SendCommand (MCPRequest) returns (MCPResponse); // Streaming bidirecional para múltiplas operações rpc StreamUpdates (stream MCPRequest) returns (stream MCPResponse); // Health check para monitoramento rpc HealthCheck (HealthRequest) returns (HealthResponse); } message MCPRequest { string command = 1; // Comando a ser executado string payload = 2; // Dados contextuais (JSON) map<string, string> metadata = 3; // Metadados adicionais int64 timestamp = 4; // Timestamp da requisição } message MCPResponse { string result = 1; // Resultado da operação string status = 2; // Status: OK, ERROR, PROCESSING string error_message = 3; // Mensagem de erro (se houver) int64 processing_time_ms = 4; // Tempo de processamento } message HealthRequest {} message HealthResponse { string status = 1; string version = 2; } 
Enter fullscreen mode Exit fullscreen mode

Configure o .csproj para incluir o arquivo proto:

<ItemGroup> <Protobuf Include="Protos\mcp.proto" GrpcServices="Server" /> </ItemGroup> 
Enter fullscreen mode Exit fullscreen mode

3️⃣ Implementar o Serviço gRPC

using Grpc.Core; using MCPPipeline.Grpc; using System.Diagnostics; namespace MCPPipeline.GrpcServer.Services; public class MCPServiceImpl : MCPService.MCPServiceBase { private readonly ILogger<MCPServiceImpl> _logger; private readonly IMCPCommandHandler _commandHandler; public MCPServiceImpl( ILogger<MCPServiceImpl> logger, IMCPCommandHandler commandHandler) { _logger = logger; _commandHandler = commandHandler; } public override async Task<MCPResponse> SendCommand( MCPRequest request, ServerCallContext context) { var sw = Stopwatch.StartNew(); try { _logger.LogInformation( "Comando recebido: {Command} | Payload: {Payload}", request.Command, request.Payload); // Processar comando via handler de domínio var result = await _commandHandler.ExecuteAsync( request.Command, request.Payload, context.CancellationToken); sw.Stop(); return new MCPResponse { Result = result, Status = "OK", ProcessingTimeMs = sw.ElapsedMilliseconds }; } catch (Exception ex) { _logger.LogError(ex, "Erro ao processar comando: {Command}", request.Command); return new MCPResponse { Status = "ERROR", ErrorMessage = ex.Message, ProcessingTimeMs = sw.ElapsedMilliseconds }; } } public override async Task StreamUpdates( IAsyncStreamReader<MCPRequest> requestStream, IServerStreamWriter<MCPResponse> responseStream, ServerCallContext context) { await foreach (var request in requestStream.ReadAllAsync(context.CancellationToken)) { _logger.LogInformation("Stream recebido: {Command}", request.Command); var response = new MCPResponse { Result = $"Processando → {request.Command}", Status = "PROCESSING" }; await responseStream.WriteAsync(response); } } public override Task<HealthResponse> HealthCheck( HealthRequest request, ServerCallContext context) { return Task.FromResult(new HealthResponse { Status = "Healthy", Version = "1.0.0" }); } } 
Enter fullscreen mode Exit fullscreen mode

4️⃣ Implementar o Command Handler (Domain Layer)

public interface IMCPCommandHandler { Task<string> ExecuteAsync(string command, string payload, CancellationToken ct); } public class MCPCommandHandler : IMCPCommandHandler { private readonly ILogger<MCPCommandHandler> _logger; public MCPCommandHandler(ILogger<MCPCommandHandler> logger) { _logger = logger; } public async Task<string> ExecuteAsync( string command, string payload, CancellationToken ct) { return command switch { "GetStatus" => await GetSystemStatusAsync(ct), "ProcessData" => await ProcessDataAsync(payload, ct), "AnalyzeContext" => await AnalyzeContextAsync(payload, ct), _ => throw new InvalidOperationException($"Comando desconhecido: {command}") }; } private async Task<string> GetSystemStatusAsync(CancellationToken ct) { await Task.Delay(10, ct); // Simula processamento return "Sistema operacional | Uptime: 99.9%"; } private async Task<string> ProcessDataAsync(string payload, CancellationToken ct) { _logger.LogInformation("Processando dados: {Payload}", payload); await Task.Delay(50, ct); return $"Dados processados com sucesso: {payload.Length} caracteres"; } private async Task<string> AnalyzeContextAsync(string payload, CancellationToken ct) { await Task.Delay(100, ct); return $"Análise contextual concluída | Tokens: {payload.Split(' ').Length}"; } } 
Enter fullscreen mode Exit fullscreen mode

5️⃣ Configurar o Servidor no Program.cs

using MCPPipeline.GrpcServer.Services; var builder = WebApplication.CreateBuilder(args); // Adicionar serviços gRPC builder.Services.AddGrpc(options => { options.EnableDetailedErrors = true; options.MaxReceiveMessageSize = 4 * 1024 * 1024; // 4MB }); // Registrar handlers de domínio builder.Services.AddScoped<IMCPCommandHandler, MCPCommandHandler>(); var app = builder.Build(); // Mapear serviços gRPC app.MapGrpcService<MCPServiceImpl>(); // Endpoint HTTP para verificação app.MapGet("/", () => Results.Ok(new { Service = "MCP gRPC Server", Status = "Running", Version = "1.0.0" })); app.Run(); 
Enter fullscreen mode Exit fullscreen mode

Configuração do appsettings.json:

{ "Kestrel": { "EndpointDefaults": { "Protocols": "Http2" } }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning", "Grpc": "Debug" } } } 
Enter fullscreen mode Exit fullscreen mode

6️⃣ Criar Cliente MCP (Simulação)

using Grpc.Net.Client; using MCPPipeline.Grpc; // Configurar canal gRPC using var channel = GrpcChannel.ForAddress("https://localhost:5001"); var client = new MCPService.MCPServiceClient(channel); // Exemplo 1: Comando único var response = await client.SendCommandAsync(new MCPRequest { Command = "GetStatus", Payload = "Kernel v2.0", Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() }); Console.WriteLine($"✅ Resposta: {response.Result}"); Console.WriteLine($"⏱️ Tempo: {response.ProcessingTimeMs}ms"); // Exemplo 2: Streaming bidirecional using var streamingCall = client.StreamUpdates(); // Enviar múltiplos comandos await streamingCall.RequestStream.WriteAsync(new MCPRequest { Command = "ProcessData", Payload = "Sample data batch 1" }); await streamingCall.RequestStream.WriteAsync(new MCPRequest { Command = "AnalyzeContext", Payload = "Context information" }); await streamingCall.RequestStream.CompleteAsync(); // Receber respostas await foreach (var streamResponse in streamingCall.ResponseStream.ReadAllAsync()) { Console.WriteLine($"📥 Stream: {streamResponse.Result}"); } 
Enter fullscreen mode Exit fullscreen mode

🔗 Integração com MCP Kernel

Para integrar com o Semantic Kernel ou outro framework MCP:

public class MCPGrpcPlugin { private readonly MCPService.MCPServiceClient _grpcClient; public MCPGrpcPlugin(MCPService.MCPServiceClient grpcClient) { _grpcClient = grpcClient; } [KernelFunction, Description("Executa comando no servidor MCP via gRPC")] public async Task<string> ExecuteCommandAsync( [Description("Comando a executar")] string command, [Description("Dados contextuais")] string payload) { var response = await _grpcClient.SendCommandAsync(new MCPRequest { Command = command, Payload = payload, Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() }); return response.Status == "OK" ? response.Result : $"Erro: {response.ErrorMessage}"; } } 
Enter fullscreen mode Exit fullscreen mode

Registro no Kernel:

var kernel = Kernel.CreateBuilder() .AddOpenAIChatCompletion("gpt-4", apiKey) .Build(); var grpcChannel = GrpcChannel.ForAddress("https://localhost:5001"); var grpcClient = new MCPService.MCPServiceClient(grpcChannel); kernel.Plugins.AddFromObject(new MCPGrpcPlugin(grpcClient), "MCPCommands"); 
Enter fullscreen mode Exit fullscreen mode

🔒 Boas Práticas e Segurança

Autenticação e Segurança

// Adicionar autenticação JWT builder.Services.AddAuthentication("Bearer") .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true }; }); builder.Services.AddAuthorization(); 
Enter fullscreen mode Exit fullscreen mode

Observabilidade

// OpenTelemetry para tracing distribuído builder.Services.AddOpenTelemetry() .WithTracing(tracing => tracing .AddAspNetCoreInstrumentation() .AddGrpcClientInstrumentation() .AddOtlpExporter()); 
Enter fullscreen mode Exit fullscreen mode

Resiliência

// Polly para retry e circuit breaker services.AddGrpcClient<MCPService.MCPServiceClient>(o => { o.Address = new Uri("https://localhost:5001"); }) .AddPolicyHandler(Policy .HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode) .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)))); 
Enter fullscreen mode Exit fullscreen mode

📊 Quando Usar gRPC com MCP

Microsserviços internos com alta frequência de comunicação

Streaming de dados em tempo real (logs, métricas, eventos)

Pipelines de IA que exigem baixa latência (<10ms)

Comunicação entre agentes distribuídos em Kubernetes

APIs internas onde tipagem forte previne erros em produção

Evite usar quando:

  • Precisar de suporte em navegadores web (sem HTTP/2 full)
  • Integração com sistemas legados sem suporte a Protobuf
  • APIs públicas onde REST/JSON é mais apropriado

🎯 Conclusão

A integração entre MCP e gRPC no .NET cria uma base sólida para sistemas inteligentes e distribuídos, unindo a velocidade e confiabilidade do gRPC com a inteligência contextual do Model Context Protocol. Essa combinação é ideal para pipelines de IA corporativos onde precisão, performance e interoperabilidade são essenciais.

Próximos passos:

  • Implementar autenticação mTLS para produção
  • Adicionar cache distribuído (Redis) para respostas frequentes
  • Configurar balanceamento de carga com health checks gRPC
  • Instrumentar com OpenTelemetry para observabilidade completa

Na Parte 3 desta série, exploraremos observabilidade e tracing distribuído entre MCP e .NET usando OpenTelemetry e Datadog.


🤝 Conecte-se Comigo

Se você trabalha com .NET moderno e quer dominar arquitetura, C#, observabilidade, DevOps ou interoperabilidade:

💼 LinkedIn
✍️ Medium

📬 contato@dopme.io

📬 devsfree@devsfree.com.br


📚 Referências:


² Porque eis que teus inimigos fazem tumulto, e os que te odeiam levantaram a cabeça. ³ Tomaram astuto conselho contra o teu povo, e consultaram contra os teus escondidos. Salmos 83:2,3

Top comments (0)