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
Fluxo de comunicação:
- O MCP Agent envia comandos contextualizados ou dados estruturados
- O Servidor gRPC valida, autentica e roteia as mensagens
- A camada de domínio aplica regras de negócio e retorna resultados
- 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
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;
}
Configure o .csproj
para incluir o arquivo proto:
<ItemGroup>
<Protobuf Include="Protos\mcp.proto" GrpcServices="Server" />
</ItemGroup>
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"
});
}
}
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}";
}
}
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();
Configuração do appsettings.json
:
{
"Kestrel": {
"EndpointDefaults": {
"Protocols": "Http2"
}
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Grpc": "Debug"
}
}
}
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}");
}
🔗 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}";
}
}
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");
🔒 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();
Observabilidade
// OpenTelemetry para tracing distribuído
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddAspNetCoreInstrumentation()
.AddGrpcClientInstrumentation()
.AddOtlpExporter());
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))));
📊 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)