Как я переписал Model Context Protocol на Go и получили 100K ops/sec (может и больше)))
- понедельник, 2 февраля 2026 г. в 00:00:09
Всем привет! Меня зовут Дима, некоторые меня тут уже знают, и сегодня я расскажу о том, как я создал GoMCP — production-grade альтернативу официальному MCP SDK от Anthropic. Спойлер: получилось в 10 раз быстрее, с multi-tenancy и enterprise-фичами из коробки.
100K+ tool calls/sec (vs ~10K у Python SDK)
Security hardening: input validation, audit logging, rate limiting
Multi-tenancy: изоляция namespace + квоты
3 адаптера: stdio (MCP v1), gRPC, HTTP REST
213 тестов, 430+ Full Ralph итераций
Почему не официальный SDK?
В конце 2024 года Anthropic представил Model Context Protocol — стандарт для подключения LLM к внешним инструментам. Идея отличная, но реализация... скажем так, не для production:
Проблемы официального SDK
# Типичный MCP server на Python
@server.tool()
async def my_tool(args: dict) -> str:
# Где валидация? Где rate limiting?
# Где audit logging?
return do_dangerous_stuff(args) # 🔥
Проблема | Python SDK | GoMCP |
|---|---|---|
Input validation | ❌ | ✅ Regex patterns, depth limits |
Audit logging | ❌ | ✅ Structured, ring buffer |
Rate limiting | ❌ | ✅ Per-client, configurable |
Multi-tenancy | ❌ | ✅ Quotas, tool ACL |
Hot-reload | ❌ | ✅ Zero-downtime |
┌─────────────────────────────────────────────────────────┐
│ gomcp-server │
├─────────────┬─────────────┬─────────────┬──────────────┤
│ Stdio │ gRPC │ HTTP │ Health │
│ Adapter │ Server │ Mode │ Endpoints │
├─────────────┴─────────────┴─────────────┴──────────────┤
│ Supervisor │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌───────────┐ │
│ │ Security │ │ Tenant │ │ Batching │ │ HotReload │ │
│ └──────────┘ └──────────┘ └──────────┘ └───────────┘ │
└─────────────────────────────────────────────────────────┘
1. Supervisor pattern — централизованное управление workers:
sup := supervisor.New(supervisor.Config{
DefaultTimeout: 30 * time.Second,
MaxWorkers: 100,
HeartbeatPeriod: 5 * time.Second,
})
// Graceful shutdown
sup.Shutdown() // Ждёт завершения всех calls
2. Security-first — валидация на входе:
validator := security.DefaultValidator()
// Проверяет:
// - Max string length: 100KB
// - Max array length: 10K items
// - Max nesting depth: 20
// - XSS patterns: <script>, javascript:
// - SQL injection: DROP, TRUNCATE
// - Template injection: ${}, {{}}
result := validator.ValidateJSON(userInput)
if !result.Valid {
return errors.New(result.Errors[0].Error())
}
3. Multi-tenancy — изоляция клиентов:
tm := tenant.NewManager()
t, _ := tm.CreateTenant("company-a", "Company A", tenant.Quotas{
MaxToolCalls: 1000,
MaxBatchSize: 50,
MaxConcurrent: 10,
})
// Ограничение доступа к инструментам
t.SetAllowedTools([]string{"read_file", "list_dir"})
// company-a НЕ сможет вызвать "delete_file"
Тестировали на AMD Ryzen 9 5900X, 32GB RAM:
Операция | ops/sec | Latency p99 |
|---|---|---|
Supervisor.CallTool | 100,000 | 10ms |
Security.ValidateJSON | 500,000 | 2ms |
AuditLogger.Log | 1,000,000 | 1ms |
Tenant.CheckQuota | 2,000,000 | 0.5ms |
batch := batching.NewBuilder().
Add("r1", "tool1", args1).
Add("r2", "tool2", args2).
Add("r3", "tool3", args3).
Parallel(5). // До 5 параллельных вызовов
Build()
result := processor.Process(ctx, batch)
// Вместо 3 sequential calls — 1 parallel batch
gomcp-server -mode=stdio
JSON-RPC 2.0 через stdin/stdout. Полностью совместим с Claude Desktop:
{"jsonrpc":"2.0","id":"1","method":"tools/list"}
gomcp-server -mode=grpc -addr=:50051
Для микросервисной архитектуры. Поддерживает streaming, TLS.
gomcp-server -mode=http -addr=:8080
# List tools
curl http://localhost:8080/v1/tools
# Call tool
curl -X POST http://localhost:8080/v1/tools/call \
-d '{"tool":"echo","arguments":{"msg":"hello"}}'
# Batch
curl -X POST http://localhost:8080/v1/tools/batch \
-d '{"requests":[...], "parallel": true}'
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 go build -o /gomcp-server ./cmd/gomcp-server
FROM alpine:3.19
RUN adduser -D gomcp
USER gomcp
COPY --from=builder /gomcp-server /app/gomcp-server
HEALTHCHECK CMD wget --spider http://localhost:8080/healthz
ENTRYPOINT ["/app/gomcp-server"]
# docker-compose.yml
services:
gomcp-server:
build: .
ports: ["8080:8080"]
deploy:
resources:
limits:
memory: 512M
import { GoMCPClient } from '@gomcp/sdk';
const client = new GoMCPClient({
baseUrl: 'http://localhost:8080'
});
// List tools
const tools = await client.listTools();
// Call tool
const result = await client.callTool('echo', { msg: 'hello' });
// Batch
const batch = await client.batchCall([
{ tool: 't1', arguments: {} },
{ tool: 't2', arguments: {} }
], { parallel: true });
Я использовал методологию Full Ralph — многократное выполнение тестов для выявления flaky tests и race conditions:
Go packages: 12
Total tests: 174
Full Ralph iterations: 430+
TypeScript SDK: 39 tests
Каждый пакет прошёл минимум 10 итераций полного тестового набора.