GoMCP
Uma biblioteca Go para construir clientes e servidores usando o Model Context Protocol (MCP).
Documentação
GoMCP - Biblioteca Go do Model Context Protocol
Conformidade com a Especificação MCP
✅ Conformidade total em todas as versões da especificação MCP - Consulte COMPLIANCE.md para verificação detalhada.
GoMCP é uma implementação completa em Go do Model Context Protocol (MCP), projetada para facilitar a interação perfeita entre aplicações e Modelos de Linguagem de Grande Porte (LLMs). A biblioteca suporta todas as versões da especificação com negociação automática e fornece uma API limpa e idiomática para clientes e servidores.
Sumário
- Visão Geral
- Principais Recursos
- Estabilidade da API
- Instalação
- Início Rápido
- Conceitos Principais
- Exemplos
- Documentação
- Contribuição
- Licença
Visão Geral
O Model Context Protocol (MCP) padroniza a comunicação entre aplicações e LLMs, permitindo:
- Chamada de Ferramentas: Executar ações e funções por meio de LLMs
- Acesso a Recursos: Fornecer dados estruturados a LLMs com contexto de espaço de trabalho
- Renderização de Prompts: Criar modelos reutilizáveis para interações com LLMs
- Amostragem: Gerar texto a partir de LLMs com controle sobre parâmetros
- Gerenciamento de Sessão: Contexto rico e acesso à raiz do espaço de trabalho para capacidades aprimoradas de ferramentas
GoMCP fornece uma implementação idiomática em Go que lida com todos os detalhes do protocolo, oferecendo uma API limpa e amigável ao desenvolvedor.
Principais Recursos
- Implementação Completa do Protocolo: Suporte total a todas as versões da especificação MCP
- Negociação Automática de Versão: Compatibilidade perfeita entre clientes e servidores
- Múltiplas Opções de Transporte: Suporte a stdio, HTTP, WebSocket e Server-Sent Events
- API Type-Safe: Aproveita o sistema de tipos do Go para segurança e expressividade
- Gerenciamento de Processos do Servidor: Iniciar, gerenciar e parar automaticamente servidores MCP externos
- Configuração do Servidor: Carregar definições de servidor a partir de arquivos de configuração
- Arquitetura de Sessão MCP: Gerenciamento abrangente de sessão com extração de dados ciente do transporte
- Busca Automática de Raiz: Descoberta automática da raiz do espaço de trabalho seguindo o protocolo MCP
- Arquitetura Flexível: Design modular para fácil extensão e personalização
Estabilidade da API
GoMCP v1.5.0 representa uma versão estável e pronta para produção com APIs bloqueadas. A biblioteca atingiu maturidade total com um conjunto abrangente de recursos e implementações testadas em batalha.
🔒 Garantia de Bloqueio de API (v1.5.0+)
- API do Cliente: Todos os métodos do cliente (
CallTool,GetResource,GetPrompt, etc.) estão bloqueados e estáveis - API do Servidor: Métodos de registro do servidor (
Tool,Resource,Prompt) e padrões de manipulador estão finalizados - Camada de Transporte: Todas as implementações de transporte seguem interfaces estáveis e bloqueadas
- Sistema de Eventos: Tipos de eventos e padrões de assinatura estão padronizados e bloqueados
- Gerenciamento de Servidor: APIs de ciclo de vida do processo e gerenciamento de configuração estão estáveis
✅ Conformidade Total com o Protocolo
- Suporte Completo à Especificação MCP: Implementação total de todas as versões do protocolo (2024-11-05, 2025-03-26, rascunho)
- Negociação Automática de Versão: Tratamento de compatibilidade perfeita entre diferentes versões da especificação
- Conformidade de Transporte: Todas as camadas de transporte implementam corretamente suas respectivas especificações MCP
- Segurança de Tipos: Tipagem forte em todo o código garante que os contratos de API sejam mantidos
✅ Pronto para Produção
- Testes Abrangentes: Cobertura extensa de testes em todos os componentes principais
- Tratamento de Erros: Tratamento robusto de erros com códigos e mensagens de erro MCP adequados
- Desempenho: Otimizado para cargas de trabalho de produção com gerenciamento eficiente de recursos
- Documentação Completa: Documentação completa da API e exemplos de uso
🚀 Desenvolvimento Futuro
Com o bloqueio de API do v1.5.0, as futuras versões se concentrarão em:
- Recursos Aditivos: Novas funcionalidades que estendem, mas não quebram, as APIs existentes
- Otimizações de Desempenho: Melhorias internas que mantêm a compatibilidade da API
- Documentação Aprimorada: Exemplos expandidos e guias de integração
- Novas Opções de Transporte: Implementações adicionais de transporte usando a interface de transporte estável
Compromisso: As APIs do v1.5.0 estão bloqueadas e não mudarão. Quaisquer melhorias futuras serão aditivas e manterão total compatibilidade retroativa. GoMCP está pronto para implantações de produção empresarial.
Instalação
go get github.com/localrivet/gomcp
Início Rápido
Exemplo de Cliente
package main
import (
"log"
"github.com/localrivet/gomcp/client"
)
func main() {
// Create a new client with stdio transport
c, err := client.NewClient("stdio:///",
client.WithStdio(),
client.WithProtocolVersion("2025-03-26"),
client.WithProtocolNegotiation(true),
)
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
defer c.Close()
// Call a tool on the MCP server
result, err := c.CallTool("say_hello", map[string]interface{}{
"name": "World",
})
if err != nil {
log.Fatalf("Tool call failed: %v", err)
}
log.Printf("Result: %v", result)
}
Nota: Este exemplo usa transporte stdio, o que significa que o cliente espera se comunicar com um servidor MCP via stdin/stdout. Para um exemplo completo que gerencia automaticamente o processo do servidor, consulte a seção "Cliente com Gerenciamento Automático de Servidor" abaixo.
Cliente com Gerenciamento Automático de Servidor
O que isso faz: Este exemplo demonstra o poderoso recurso de gerenciamento automático de servidor do GoMCP. Em vez de iniciar e parar manualmente os processos do servidor MCP, o cliente pode automaticamente:
- Iniciar processos de servidor sob demanda usando comandos do sistema
- Estabelecer conexões com esses servidores via stdio/pipes
- Injeção de variáveis de ambiente para configuração (chaves de API, etc.)
- Limpeza automática - os processos do servidor são encerrados quando o cliente fecha
- Gerenciamento do ciclo de vida do processo - lida com inicialização, verificações de saúde e desligamento do servidor
Por que isso importa: Esse padrão elimina a complexidade operacional de gerenciar servidores MCP. Você pode distribuir um único binário que inicia automaticamente os servidores MCP necessários, tornando a implantação e a integração muito mais simples. É especialmente útil para:
- Ambientes de desenvolvimento - iniciar automaticamente serviços dependentes
- Pipelines de CI/CD - iniciar servidores para testes sem configuração manual
- Aplicações de desktop - incorporar servidores MCP sem exigir instalação separada
- Arquiteturas de microsserviços - gerenciar dependências de servidor declarativamente
package main
import (
"log"
"github.com/localrivet/gomcp/client"
)
func main() {
// Define server configuration
config := client.ServerConfig{
MCPServers: map[string]client.ServerDefinition{
"govibe": {
Command: "govibe",
Args: []string{},
Env: map[string]string{
"ANTHROPIC_API_KEY": "${ANTHROPIC_API_KEY}",
},
},
},
}
// Create a client with automatic server management
c, err := client.NewClient("my-client",
client.WithServers(config, "govibe"),
)
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
defer c.Close() // Automatically stops the server process
// Call a tool on the managed server
result, err := c.CallTool("add_task", map[string]interface{}{
"prompt": "Create a login page with authentication",
})
if err != nil {
log.Fatalf("Tool call failed: %v", err)
}
log.Printf("Task created: %v", result)
// Add project roots for the server to access
err = c.AddRoot("/path/to/project", "project-root")
if err != nil {
log.Fatalf("Failed to add root: %v", err)
}
// Get a resource that might use the project context
resource, err := c.GetResource("/project/files/src/main.go")
if err != nil {
log.Fatalf("Resource request failed: %v", err)
}
log.Printf("Resource content: %v", resource)
}
Detalhes-chave de Implementação:
- A sintaxe
${ANTHROPIC_API_KEY}injeta automaticamente variáveis de ambiente do processo atual - Os processos do servidor se comunicam via pipes stdio para IPC seguro e de alto desempenho
- O cliente aguarda a inicialização do servidor antes de aceitar solicitações
- Desligamento gracioso garante que os servidores sejam encerrados corretamente, evitando processos órfãos
- Vários servidores podem ser gerenciados simultaneamente com diferentes configurações
Exemplo de Servidor
package main
import (
"fmt"
"log/slog"
"os"
"github.com/localrivet/gomcp/server"
)
func main() {
// Create a logger
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
}))
// Create a new server
srv := server.NewServer("example-server",
server.WithLogger(logger),
).AsStdio()
// Register a tool with inline struct
srv.Tool("say_hello", "Greet someone", func(ctx *server.Context, args struct {
Name string `json:"name"`
}) (interface{}, error) {
return map[string]interface{}{
"message": fmt.Sprintf("Hello, %s!", args.Name),
}, nil
})
// Start the server
if err := srv.Run(); err != nil {
log.Fatalf("Failed to run server: %v", err)
}
}
Exemplo Avançado de Servidor
package main
import (
"fmt"
"log/slog"
"os"
"path/filepath"
"strings"
"github.com/localrivet/gomcp/server"
"github.com/localrivet/gomcp/transport/sse"
)
func main() {
// Create a logger
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelInfo,
}))
// Create a new server with comprehensive functionality
srv := server.NewServer("advanced-server",
server.WithLogger(logger),
).AsStdio()
// Register multiple tools with different parameter types
srv.Tool("calculator", "Perform mathematical calculations", func(ctx *server.Context, args struct {
Operation string `json:"operation"`
A float64 `json:"a"`
B float64 `json:"b"`
}) (interface{}, error) {
switch args.Operation {
case "add":
return map[string]interface{}{"result": args.A + args.B}, nil
case "multiply":
return map[string]interface{}{"result": args.A * args.B}, nil
case "divide":
if args.B == 0 {
return nil, fmt.Errorf("division by zero")
}
return map[string]interface{}{"result": args.A / args.B}, nil
default:
return nil, fmt.Errorf("unsupported operation: %s", args.Operation)
}
})
srv.Tool("create_file", "Create a file with content", func(ctx *server.Context, args struct {
Path string `json:"path"`
Content string `json:"content"`
}) (interface{}, error) {
// In a real implementation, you'd validate paths and permissions
return map[string]interface{}{
"message": fmt.Sprintf("File created at %s with %d bytes", args.Path, len(args.Content)),
"path": args.Path,
"size": len(args.Content),
}, nil
})
// Register resources with different patterns
srv.Resource("/config", "Get server configuration", func(ctx *server.Context, args *struct{}) (interface{}, error) {
return map[string]interface{}{
"version": "1.0.0",
"environment": "development",
"features": []string{"tools", "resources", "prompts"},
}, nil
})
// Templated resource for file access
srv.Resource("/files/{path*}", "Access file system resources", func(ctx *server.Context, args *struct {
Path string `path:"path"`
}) (interface{}, error) {
// Extract file extension for content type detection
ext := strings.ToLower(filepath.Ext(args.Path))
return map[string]interface{}{
"path": args.Path,
"extension": ext,
"type": getFileType(ext),
"content": fmt.Sprintf("Mock content for file: %s", args.Path),
}, nil
})
// User profile resource with parameters
srv.Resource("/users/{id}", "Get user profile information", func(ctx *server.Context, args *struct {
ID string `path:"id"`
IncludePosts bool `json:"include_posts"`
}) (interface{}, error) {
user := map[string]interface{}{
"id": args.ID,
"name": fmt.Sprintf("User %s", args.ID),
"email": fmt.Sprintf("user%s@example.com", args.ID),
"active": true,
}
if args.IncludePosts {
user["posts"] = []map[string]interface{}{
{"id": 1, "title": "Hello World", "content": "First post"},
{"id": 2, "title": "Second Post", "content": "Another post"},
}
}
return user, nil
})
// Register prompts for different use cases
srv.Prompt("code_review", "Provide code review assistance",
server.User("Please review this {{language}} code for best practices, potential bugs, and improvements:\n\n```{{language}}\n{{code}}\n```"),
server.Assistant("I'll analyze your {{language}} code and provide detailed feedback on best practices, potential issues, and suggested improvements."),
)
srv.Prompt("email_template", "Generate professional email content",
server.Assistant("I'll help you create a professional email."),
server.User("Write a {{tone}} email to {{recipient}} about {{subject}}. Include these key points: {{key_points}}"),
)
srv.Prompt("documentation", "Generate technical documentation",
server.User("Create documentation for this {{type}} with the following details:\n\nName: {{name}}\nPurpose: {{purpose}}\nParameters: {{parameters}}\nExample: {{example}}"),
server.Assistant("I'll create comprehensive technical documentation following best practices for clarity and completeness."),
)
// Start the server
if err := srv.Run(); err != nil {
logger.Error("Failed to run server", "error", err)
os.Exit(1)
}
}
// Helper function to determine file type from extension
func getFileType(ext string) string {
switch ext {
case ".go":
return "go_source"
case ".js", ".ts":
return "javascript"
case ".py":
return "python"
case ".md":
return "markdown"
case ".json":
return "json"
case ".yaml", ".yml":
return "yaml"
default:
return "text"
}
}
Conceitos Principais
Clientes e Servidores
client.Client: Interface para comunicação com servidores MCP. Lida com negociação de protocolo, gerenciamento de solicitação/resposta e ciclo de vida do servidor.server.Server: Componente principal para implementar servidores MCP. Fornece métodos de registro para ferramentas, recursos e prompts com geração automática de esquema.
Ferramentas
Ferramentas expõem funções que LLMs podem chamar para executar ações. GoMCP suporta:
- Parâmetros type-safe usando definições de struct inline
- Geração automática de esquema a partir de tags de struct do Go
- Tratamento de erros com respostas de erro MCP adequadas
- Suporte a cancelamento para operações de longa duração
Lado do Servidor (Implementação):
// Simple tool with inline struct
srv.Tool("say_hello", "Greet someone", func(ctx *server.Context, args struct {
Name string `json:"name"`
}) (interface{}, error) {
return map[string]interface{}{
"message": fmt.Sprintf("Hello, %s!", args.Name),
}, nil
})
// Tool with complex parameters
srv.Tool("calculate", "Perform calculations", func(ctx *server.Context, args struct {
Operation string `json:"operation"`
A float64 `json:"a"`
B float64 `json:"b"`
}) (interface{}, error) {
switch args.Operation {
case "add":
return map[string]interface{}{"result": args.A + args.B}, nil
case "multiply":
return map[string]interface{}{"result": args.A * args.B}, nil
default:
return nil, fmt.Errorf("unsupported operation: %s", args.Operation)
}
})
Lado do Cliente (Uso):
// Call a simple tool
result, err := client.CallTool("say_hello", map[string]interface{}{
"name": "Alice",
})
if err != nil {
log.Fatalf("Tool call failed: %v", err)
}
fmt.Printf("Result: %v\n", result)
// Call a tool with complex parameters
calcResult, err := client.CallTool("calculate", map[string]interface{}{
"operation": "add",
"a": 10.5,
"b": 20.3,
})
if err != nil {
log.Fatalf("Calculation failed: %v", err)
}
fmt.Printf("Calculation result: %v\n", calcResult)
Recursos
Recursos fornecem dados estruturados a LLMs em vários formatos:
- Recursos estáticos com URIs fixas (por exemplo,
/config,/status) - Recursos modelados com parâmetros de caminho (por exemplo,
/files/{path*},/users/{id}) - Recursos dinâmicos que podem aceitar parâmetros adicionais dos corpos das solicitações
- Múltiplos tipos de conteúdo incluindo texto, imagens, links e dados binários
Lado do Servidor (Implementação):
// Static resource
srv.Resource("/config", "Get configuration", func(ctx *server.Context, args *struct{}) (interface{}, error) {
return map[string]interface{}{
"version": "1.0.0",
"environment": "production",
}, nil
})
// Templated resource with path parameters
srv.Resource("/files/{path*}", "Access files", func(ctx *server.Context, args *struct {
Path string `path:"path"`
}) (interface{}, error) {
return map[string]interface{}{
"path": args.Path,
"content": fmt.Sprintf("Content of %s", args.Path),
}, nil
})
// Resource with additional parameters
srv.Resource("/users/{id}", "Get user info", func(ctx *server.Context, args *struct {
ID string `path:"id"`
IncludePosts bool `json:"include_posts"`
}) (interface{}, error) {
user := map[string]interface{}{
"id": args.ID,
"name": fmt.Sprintf("User %s", args.ID),
}
if args.IncludePosts {
user["posts"] = []string{"Post 1", "Post 2"}
}
return user, nil
})
Lado do Cliente (Uso):
// Get a static resource
config, err := client.GetResource("/config")
if err != nil {
log.Fatalf("Failed to get config: %v", err)
}
fmt.Printf("Config: %v\n", config)
// Get a templated resource
fileContent, err := client.GetResource("/files/src/main.go")
if err != nil {
log.Fatalf("Failed to get file: %v", err)
}
fmt.Printf("File content: %v\n", fileContent)
// Get a resource with additional parameters
user, err := client.GetResource("/users/123", client.WithResourceParams(map[string]interface{}{
"include_posts": true,
}))
if err != nil {
log.Fatalf("Failed to get user: %v", err)
}
fmt.Printf("User: %v\n", user)
Prompts
Prompts definem modelos de mensagem reutilizáveis para interações com LLMs:
- Variáveis de modelo usando a sintaxe
{{variable}} - Múltiplos tipos de mensagem (Usuário, Assistente, Sistema)
- Conversas baseadas em papéis para padrões de interação complexos
- Validação de argumentos para parâmetros de modelo
Lado do Servidor (Implementação):
// Simple prompt template
srv.Prompt("email_template", "Generate emails",
server.User("Write a {{tone}} email to {{recipient}} about {{subject}}"),
)
// Multi-message conversation prompt
srv.Prompt("code_review", "Code review assistant",
server.Assistant("I'll help you review code for best practices and bugs."),
server.User("Please review this {{language}} code:\n\n```{{language}}\n{{code}}\n```"),
)
// Complex prompt with multiple variables
srv.Prompt("documentation", "Generate docs",
server.User("Create {{type}} documentation for:\nName: {{name}}\nPurpose: {{purpose}}\nExample: {{example}}"),
server.Assistant("I'll create comprehensive {{type}} documentation."),
)
Lado do Cliente (Uso):
// Get a simple prompt
emailPrompt, err := client.GetPrompt("email_template", map[string]interface{}{
"tone": "professional",
"recipient": "team",
"subject": "project update",
})
if err != nil {
log.Fatalf("Failed to get prompt: %v", err)
}
fmt.Printf("Email prompt: %v\n", emailPrompt)
// Get a complex prompt with multiple variables
docPrompt, err := client.GetPrompt("documentation", map[string]interface{}{
"type": "API",
"name": "UserService",
"purpose": "Manage user accounts",
"example": "userService.CreateUser()",
})
if err != nil {
log.Fatalf("Failed to get doc prompt: %v", err)
}
fmt.Printf("Documentation prompt: %v\n", docPrompt)
Operações em Lote
GoMCP suporta operações em lote JSON-RPC para melhor desempenho:
- Redução de idas e voltas de rede enviando várias solicitações de uma vez
- Ordenação de solicitações mantida nas respostas
- Tipos de solicitação mistos (ferramentas, recursos, prompts) em um único lote
- Tratamento de falhas parciais com erros individuais de resposta
- Interface fluente de construção para fácil montagem de lotes
Lado do Cliente (Uso):
// Create a batch request
batch := client.NewBatch().
CallTool("say_hello", map[string]interface{}{"name": "Alice"}).
GetResource("/config").
GetPrompt("email_template", map[string]interface{}{
"tone": "professional",
"recipient": "team",
"subject": "project update",
})
// Execute the batch
results, err := c.ExecuteBatch(batch)
if err != nil {
log.Fatalf("Batch execution failed: %v", err)
}
// Process results
for i, result := range results {
if result.Error != nil {
log.Printf("Request %d failed: %v", i, result.Error)
} else {
log.Printf("Request %d result: %v", i, result.Result)
}
}
Sistema de Eventos
GoMCP fornece um sistema de eventos abrangente que permite monitorar e reagir a várias atividades dentro do seu servidor ou cliente MCP. O sistema de eventos usa uma arquitetura type-safe baseada em canais para máximo desempenho e confiabilidade.
Principais Benefícios:
- Monitoramento em tempo real de operações do servidor e interações do cliente
- Tratamento de eventos type-safe com structs de eventos fortemente tipados
- Arquitetura baseada em canais para processamento de eventos de alto desempenho e não bloqueante
- Cobertura abrangente de todos os eventos de ciclo de vida e operação do servidor
- Integração fácil com sistemas de registro, métricas e monitoramento
Tipos de Eventos Disponíveis
GoMCP emite eventos para todas as principais operações e mudanças de ciclo de vida:
Eventos de Ciclo de Vida do Servidor:
server.initialized- O servidor foi iniciado e está pronto para aceitar solicitaçõesserver.shutdown- O servidor está sendo desligado
Eventos de Conexão do Cliente:
client.connected- Um cliente conectou-se ao servidorclient.disconnected- Um cliente desconectou-se do servidorclient.initializing- O cliente está começando a conectar (lado do cliente)client.initialized- O cliente conectou-se com sucesso (lado do cliente)client.error- A operação do cliente falhou (lado do cliente)
Eventos de Registro:
tool.registered- Uma ferramenta foi registrada no servidorresource.registered- Um recurso foi registrado no servidor
Eventos de Operação:
tool.executed- Uma ferramenta foi executada (operação bem-sucedida)resource.accessed- Um recurso foi acessadoprompt.executed- Um prompt foi executadorequest.failed- Qualquer solicitação MCP falhou
Uso de Eventos no Lado do Servidor
Configurando Assinaturas de Eventos:
package main
import (
"context"
"log/slog"
"os"
"github.com/localrivet/gomcp/events"
"github.com/localrivet/gomcp/server"
)
func main() {
// Create server
srv := server.NewServer("my-server")
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
// Subscribe to server lifecycle events
events.Subscribe[events.ServerInitializedEvent](srv.Events(), events.TopicServerInitialized,
func(ctx context.Context, evt events.ServerInitializedEvent) error {
logger.Info("🚀 Server initialized",
"name", evt.ServerName,
"version", evt.ProtocolVersion,
"tools", evt.ToolCount,
"resources", evt.ResourceCount)
return nil
})
events.Subscribe[events.ServerShutdownEvent](srv.Events(), events.TopicServerShutdown,
func(ctx context.Context, evt events.ServerShutdownEvent) error {
logger.Info("🛑 Server shutting down",
"name", evt.ServerName,
"graceful", evt.GracefulExit,
"reason", evt.Reason)
return nil
})
// Subscribe to client connection events
events.Subscribe[events.ClientConnectedEvent](srv.Events(), events.TopicClientConnected,
func(ctx context.Context, evt events.ClientConnectedEvent) error {
logger.Info("🔌 Client connected",
"sessionId", evt.SessionID,
"clientName", evt.ClientInfo.Name,
"clientVersion", evt.ClientInfo.Version,
"protocolVersion", evt.ProtocolVersion)
return nil
})
events.Subscribe[events.ClientDisconnectedEvent](srv.Events(), events.TopicClientDisconnected,
func(ctx context.Context, evt events.ClientDisconnectedEvent) error {
logger.Info("🔌 Client disconnected",
"sessionId", evt.SessionID,
"duration", time.Since(evt.ConnectedAt))
return nil
})
// Subscribe to tool events
events.Subscribe[events.ToolRegisteredEvent](srv.Events(), events.TopicToolRegistered,
func(ctx context.Context, evt events.ToolRegisteredEvent) error {
logger.Info("🔧 Tool registered",
"name", evt.ToolName,
"description", evt.Description)
return nil
})
events.Subscribe[events.ToolExecutedEvent](srv.Events(), events.TopicToolExecuted,
func(ctx context.Context, evt events.ToolExecutedEvent) error {
logger.Info("⚡ Tool executed",
"method", evt.Method,
"success", len(evt.ResponseJSON) > 0)
return nil
})
// Subscribe to resource events
events.Subscribe[events.ResourceRegisteredEvent](srv.Events(), events.TopicResourceRegistered,
func(ctx context.Context, evt events.ResourceRegisteredEvent) error {
logger.Info("📄 Resource registered",
"uri", evt.URI,
"name", evt.Name,
"mimeType", evt.MimeType)
return nil
})
events.Subscribe[events.ResourceAccessedEvent](srv.Events(), events.TopicResourceAccessed,
func(ctx context.Context, evt events.ResourceAccessedEvent) error {
logger.Info("📖 Resource accessed",
"uri", evt.URI,
"success", evt.Success,
"responseSize", evt.ResponseSize)
return nil
})
// Subscribe to error events
events.Subscribe[events.RequestFailedEvent](srv.Events(), events.TopicRequestFailed,
func(ctx context.Context, evt events.RequestFailedEvent) error {
logger.Error("❌ Request failed",
"method", evt.Method,
"error", evt.Error)
return nil
})
// Register tools and resources
srv.Tool("greet", "Say hello", func(ctx *server.Context, args struct {
Name string `json:"name"`
}) (interface{}, error) {
return fmt.Sprintf("Hello, %s!", args.Name), nil
})
srv.Resource("/status", "Server status", func(ctx *server.Context, args *struct{}) (interface{}, error) {
return map[string]interface{}{
"status": "healthy",
"uptime": time.Since(startTime),
}, nil
})
// Start server
srv.AsStdio().Run()
}
Uso de Eventos no Lado do Cliente
Monitorando Operações do Cliente:
package main
import (
"context"
"log/slog"
"github.com/localrivet/gomcp/client"
"github.com/localrivet/gomcp/events"
)
func main() {
// Create client
c, err := client.NewClient("my-client")
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
defer c.Close()
logger := slog.Default()
// Subscribe to client lifecycle events
events.Subscribe[events.ClientInitializingEvent](c.Events(), events.TopicClientInitializing,
func(ctx context.Context, evt events.ClientInitializingEvent) error {
logger.Info("🔄 Client connecting", "url", evt.URL)
return nil
})
events.Subscribe[events.ClientInitializedEvent](c.Events(), events.TopicClientInitialized,
func(ctx context.Context, evt events.ClientInitializedEvent) error {
logger.Info("✅ Client connected", "url", evt.URL)
return nil
})
events.Subscribe[events.ClientErrorEvent](c.Events(), events.TopicClientError,
func(ctx context.Context, evt events.ClientErrorEvent) error {
logger.Error("❌ Client error", "error", evt.Error)
return nil
})
events.Subscribe[events.ClientDisconnectedEvent](c.Events(), events.TopicClientDisconnected,
func(ctx context.Context, evt events.ClientDisconnectedEvent) error {
logger.Info("🔌 Client disconnected", "url", evt.URL)
return nil
})
// Subscribe to operation events
events.Subscribe[events.ToolExecutedEvent](c.Events(), events.TopicToolExecuted,
func(ctx context.Context, evt events.ToolExecutedEvent) error {
logger.Info("⚡ Tool called", "method", evt.Method)
return nil
})
events.Subscribe[events.RequestFailedEvent](c.Events(), events.TopicRequestFailed,
func(ctx context.Context, evt events.RequestFailedEvent) error {
logger.Error("❌ Request failed", "method", evt.Method, "error", evt.Error)
return nil
})
// Connect to server and perform operations
err = c.ConnectStdio("./my-mcp-server")
if err != nil {
log.Fatalf("Failed to connect: %v", err)
}
// Tool calls and other operations will now emit events
result, err := c.CallTool("greet", map[string]interface{}{"name": "World"})
if err != nil {
log.Fatalf("Tool call failed: %v", err)
}
fmt.Printf("Result: %v\n", result)
}
Padrões de Integração do Sistema de Eventos
Coleta de Métricas:
type MetricsCollector struct {
toolCallCount int64
errorCount int64
connectedClients int64
}
func (m *MetricsCollector) SetupEventSubscriptions(srv server.Server) {
// Track tool executions
events.Subscribe[events.ToolExecutedEvent](srv.Events(), events.TopicToolExecuted,
func(ctx context.Context, evt events.ToolExecutedEvent) error {
atomic.AddInt64(&m.toolCallCount, 1)
return nil
})
// Track errors
events.Subscribe[events.RequestFailedEvent](srv.Events(), events.TopicRequestFailed,
func(ctx context.Context, evt events.RequestFailedEvent) error {
atomic.AddInt64(&m.errorCount, 1)
return nil
})
// Track connections
events.Subscribe[events.ClientConnectedEvent](srv.Events(), events.TopicClientConnected,
func(ctx context.Context, evt events.ClientConnectedEvent) error {
atomic.AddInt64(&m.connectedClients, 1)
return nil
})
events.Subscribe[events.ClientDisconnectedEvent](srv.Events(), events.TopicClientDisconnected,
func(ctx context.Context, evt events.ClientDisconnectedEvent) error {
atomic.AddInt64(&m.connectedClients, -1)
return nil
})
}
Registro de Auditoria:
type AuditLogger struct {
logger *slog.Logger
}
func (a *AuditLogger) SetupAuditSubscriptions(srv server.Server) {
// Log all tool executions for security audit
events.Subscribe[events.ToolExecutedEvent](srv.Events(), events.TopicToolExecuted,
func(ctx context.Context, evt events.ToolExecutedEvent) error {
a.logger.Info("AUDIT: Tool executed",
"method", evt.Method,
"requestJSON", evt.RequestJSON,
"responseJSON", evt.ResponseJSON,
"timestamp", time.Now())
return nil
})
// Log all failed requests
events.Subscribe[events.RequestFailedEvent](srv.Events(), events.TopicRequestFailed,
func(ctx context.Context, evt events.RequestFailedEvent) error {
a.logger.Warn("AUDIT: Request failed",
"method", evt.Method,
"error", evt.Error,
"requestJSON", evt.RequestJSON,
"timestamp", time.Now())
return nil
})
}
Monitoramento de Saúde:
type HealthMonitor struct {
lastToolExecution time.Time
errorRate float64
isHealthy bool
}
func (h *HealthMonitor) SetupHealthSubscriptions(srv server.Server) {
events.Subscribe[events.ToolExecutedEvent](srv.Events(), events.TopicToolExecuted,
func(ctx context.Context, evt events.ToolExecutedEvent) error {
h.lastToolExecution = time.Now()
h.updateHealthStatus()
return nil
})
events.Subscribe[events.RequestFailedEvent](srv.Events(), events.TopicRequestFailed,
func(ctx context.Context, evt events.RequestFailedEvent) error {
h.calculateErrorRate()
h.updateHealthStatus()
return nil
})
}
func (h *HealthMonitor) updateHealthStatus() {
h.isHealthy = time.Since(h.lastToolExecution) < 5*time.Minute && h.errorRate < 0.1
}
Referência da Estrutura de Eventos
Todos os eventos incluem metadados abrangentes e seguem padrões consistentes:
Eventos de Servidor incluem nome do servidor, carimbos de data/hora e métricas operacionais Eventos de Cliente incluem IDs de sessão, versões de protocolo e detalhes de conexão Eventos de Operação incluem nomes de métodos, cargas JSON reais e resultados de execução Eventos de Erro incluem informações detalhadas de erro e contexto para depuração
Para um exemplo completo com todos os tipos de eventos, consulte examples/events_integration/main.go.
Transportes
GoMCP suporta múltiplas camadas de transporte para diferentes casos de uso:
- stdio: Ferramentas de linha de comando e integração direta com LLM
- WebSocket: Comunicação bidirecional para aplicações web
- Server-Sent Events (SSE): Padrão híbrido com SSE para servidor-para-cliente e HTTP POST para cliente-para-servidor
- HTTP: Interfaces RESTful simples
- Unix Socket: Comunicação interprocessos de alta performance
- UDP: Comunicação de baixa sobrecarga e alta vazão
- MQTT: Mensageria publish/subscribe para aplicações IoT
- NATS: Mensageria nativa para nuvem, de alta performance
- gRPC: Comunicação serviço-a-serviço com tipagem forte
Lado do Servidor (Implementação):
import (
"fmt"
"log/slog"
"os"
"path/filepath"
"strings"
"github.com/localrivet/gomcp/server"
"github.com/localrivet/gomcp/transport/sse"
)
// Stdio transport (for CLI tools and LLM integration)
srv := server.NewServer("my-server").AsStdio()
// HTTP transport
srv := server.NewServer("my-server").AsHTTP(":8080")
// WebSocket transport
srv := server.NewServer("my-server").AsWebSocket(":8080", "/mcp")
// SSE transport with single MCP endpoint (Streamable HTTP per 2025-03-26 spec)
// WARNING: Current implementation uses deprecated pattern - needs update
srv := server.NewServer("my-server").AsSSE(":8080")
// Custom MCP endpoint path
srv := server.NewServer("my-server").AsSSE(":8080",
sse.SSE.WithMCPEndpoint("/mcp"),
)
// Unix socket transport
srv := server.NewServer("my-server").AsUnix("/tmp/mcp.sock")
// UDP transport
srv := server.NewServer("my-server").AsUDP(":8080")
// MQTT transport
srv := server.NewServer("my-server").AsMQTT("mqtt://localhost:1883", "mcp/requests", "mcp/responses")
// NATS transport
srv := server.NewServer("my-server").AsNATS("nats://localhost:4222", "mcp.requests", "mcp.responses")
// gRPC transport
srv := server.NewServer("my-server").AsGRPC(":9090")
Lado do Cliente (Uso):
// Connect via stdio (for connecting to CLI tools)
client, err := client.NewClient("my-client",
client.WithStdioTransport("./my-mcp-server"),
)
// Connect via HTTP
client, err := client.NewClient("my-client",
client.WithHTTPTransport("http://localhost:8080"),
)
// Connect via WebSocket
client, err := client.NewClient("my-client",
client.WithWebSocketTransport("ws://localhost:8080/mcp"),
)
// Connect via SSE (hybrid: SSE for receiving + HTTP POST for sending)
// The client connects to the base URL; endpoints are discovered automatically
client, err := client.NewClient("my-client",
client.WithSSE("http://localhost:8080"),
)
// Connect via Unix socket
client, err := client.NewClient("my-client",
client.WithUnixTransport("/tmp/mcp.sock"),
)
// Connect via UDP
client, err := client.NewClient("my-client",
client.WithUDPTransport("localhost:8080"),
)
// Connect via MQTT
client, err := client.NewClient("my-client",
client.WithMQTTTransport("mqtt://localhost:1883", "mcp/responses", "mcp/requests"),
)
// Connect via NATS
client, err := client.NewClient("my-client",
client.WithNATSTransport("nats://localhost:4222", "mcp.responses", "mcp.requests"),
)
// Connect via gRPC
client, err := client.NewClient("my-client",
client.WithGRPCTransport("localhost:9090"),
)
Nota sobre Transporte SSE: O transporte SSE implementa corretamente a especificação MCP "Streamable HTTP" (2025-03-26):
- Endpoint MCP Único: Usa um único endpoint que lida tanto com GET (para SSE) quanto com POST (para mensagens)
- Requisições do Cliente: Enviadas via HTTP POST para o endpoint MCP
- Respostas do Servidor: Podem ser respostas JSON imediatas ou streams SSE
- Mensagens Iniciadas pelo Servidor: Enviadas via streams SSE iniciados por GET
- Gerenciamento de Sessão: IDs de sessão opcionais via cabeçalho
Mcp-Session-Id - Compatibilidade Retroativa: Suporta o padrão legado 2024-11-05 com fallback automático
Gerenciamento de Servidores
GoMCP fornece gerenciamento automático de processos de servidores MCP externos:
- Inicialização automática de processos a partir de arquivos de configuração ou definições programáticas
- Injeção de variáveis de ambiente com sintaxe
${VAR} - Desligamento gracioso e limpeza quando clientes desconectam
- Suporte a múltiplos servidores para arquiteturas complexas
- Monitoramento de saúde e gerenciamento de conexões
Lado do Cliente (Uso):
// Define server configuration
config := client.ServerConfig{
MCPServers: map[string]client.ServerDefinition{
"file-server": {
Command: "python",
Args: []string{"-m", "mcp_server_files"},
Env: map[string]string{
"FILES_ROOT": "/workspace",
"LOG_LEVEL": "info",
},
},
"database-server": {
Command: "./db-mcp-server",
Args: []string{"--config", "config.json"},
Env: map[string]string{
"DATABASE_URL": "${DATABASE_URL}",
"API_KEY": "${DB_API_KEY}",
},
WorkingDirectory: "/opt/db-server",
},
"ai-tools": {
Command: "ai-mcp-tools",
Args: []string{"--model", "gpt-4"},
Env: map[string]string{
"OPENAI_API_KEY": "${OPENAI_API_KEY}",
"ANTHROPIC_API_KEY": "${ANTHROPIC_API_KEY}",
},
},
},
}
// Create client with automatic server management
client, err := client.NewClient("orchestrator",
client.WithServers(config, "file-server"), // Start file-server
)
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
defer client.Close() // Automatically stops managed servers
// Start additional servers on demand
err = client.StartServer("database-server")
if err != nil {
log.Fatalf("Failed to start database server: %v", err)
}
// Use multiple servers
fileResult, err := client.CallTool("list_files", map[string]interface{}{
"path": "/workspace/src",
})
// Switch to database server context or use a different client instance
dbClient, err := client.NewClient("db-client",
client.WithServers(config, "database-server"),
)
defer dbClient.Close()
queryResult, err := dbClient.CallTool("execute_query", map[string]interface{}{
"sql": "SELECT * FROM users LIMIT 10",
})
// Load configuration from file
configFromFile, err := client.LoadServerConfig("mcp-servers.json")
if err != nil {
log.Fatalf("Failed to load config: %v", err)
}
// Create client with all servers from config
multiClient, err := client.NewClient("multi-server",
client.WithServersFromConfig(configFromFile, "file-server", "ai-tools"),
)
defer multiClient.Close()
// Health monitoring
status := client.GetServerStatus("file-server")
if !status.Running {
log.Printf("File server is not running: %v", status.Error)
err := client.RestartServer("file-server")
if err != nil {
log.Fatalf("Failed to restart server: %v", err)
}
}
Exemplo de Arquivo de Configuração (mcp-servers.json):
{
"mcpServers": {
"file-server": {
"command": "python",
"args": ["-m", "mcp_server_files"],
"env": {
"FILES_ROOT": "/workspace",
"LOG_LEVEL": "info"
}
},
"database-server": {
"command": "./db-mcp-server",
"args": ["--config", "config.json"],
"env": {
"DATABASE_URL": "${DATABASE_URL}",
"API_KEY": "${DB_API_KEY}"
},
"workingDirectory": "/opt/db-server"
}
}
}
Padrões Adequados de Limpeza
Ao usar registros de servidores com múltiplos servidores MCP, é importante seguir padrões adequados de limpeza para evitar condições de corrida:
✅ Padrão Correto: Use registry.StopAll()
registry := client.NewServerRegistry(
client.WithRegistryLogger(logger),
)
// IMPORTANT: Use defer registry.StopAll() for proper cleanup
defer func() {
if err := registry.StopAll(); err != nil {
log.Printf("Warning: Error during server shutdown: %v", err)
}
}()
err := registry.LoadConfig(configFile)
// ... use the servers ...
❌ Padrão Incorreto: client.Close() manual antes de registry.StopAll()
// DON'T DO THIS - creates race condition
defer func() {
client.Close() // ❌ Kills connection/process immediately
registry.StopAll() // ❌ Tries to wait for already-killed process
}()
Por que isso cria uma condição de corrida:
client.Close()termina imediatamente a conexão MCP e pode matar o processo do servidorregistry.StopAll()então tenta desligar graciosamente processos que já estão mortos- Isso resulta em erros "Failed to wait for server process error='signal: killed'"
Melhores Práticas
- Sempre use
registry.StopAll()- ele lida com toda a limpeza de clientes internamente - Use blocos defer para garantir a limpeza na saída do programa
- Não misture client.Close() manual com registry.StopAll()
- Lide com erros de limpeza graciosamente - os processos podem já ter sido encerrados
Gerenciamento de Sessão
GoMCP v1.5.5 introduz gerenciamento abrangente de sessão com a Arquitetura de Sessão MCP, fornecendo contexto rico e descoberta automatizada de workspaces:
Acesso à Sessão no Lado do Servidor
// Tool handlers receive session context automatically
srv.Tool("analyze_project", "Analyze project structure", func(ctx *server.Context, args struct {
AnalysisType string `json:"analysis_type"`
}) (interface{}, error) {
// Access session environment (from transport headers/process env)
env := ctx.Session.Env()
apiKey := env["ANTHROPIC_API_KEY"]
// Access workspace roots (from clientInfo + automated roots/list)
roots := ctx.Session.Roots()
primaryRoot := ""
if len(roots) > 0 {
primaryRoot = roots[0]
}
// Access client capabilities
caps := ctx.Session.Capabilities()
return map[string]interface{}{
"primary_workspace": primaryRoot,
"all_roots": roots,
"has_api_access": apiKey != "",
"supports_sampling": caps.Sampling.Supported,
"analysis_type": args.AnalysisType,
}, nil
})
Dados de Sessão Cientes do Transporte
GoMCP extrai automaticamente dados de sessão da camada de transporte conforme a especificação MCP:
- stdio: Ambiente das variáveis de ambiente do processo
- HTTP: Ambiente dos cabeçalhos da requisição (padrão
X-Env-*) - WebSocket: Ambiente dos cabeçalhos da conexão
- SSE: Ambiente dos cabeçalhos da requisição inicial
Descoberta Automatizada de Raiz de Workspace
O servidor detecta automaticamente quando os clientes suportam a capacidade roots e:
- Extração Inicial: Extrai raízes de workspace de
clientInfo.rootsdurante a inicialização - Detecção de Capacidade: Detecta se o cliente anuncia a capacidade
roots - Busca Automatizada: Envia requisições
roots/listapósnotifications/initialized - Tratamento de Respostas: Processa respostas
roots/listcom rastreamento adequado de requisições - Integração de Contexto: Disponibiliza as raízes via
ctx.Session.Roots()
Conformidade com o Protocolo MCP
Conformidade total com as três versões do protocolo MCP:
- 2024-11-05: Extração básica de raízes e detecção de capacidade
- 2025-03-26: Gerenciamento aprimorado de sessão com detecção de suporte a áudio
- draft: Recursos mais recentes com arquitetura completa de sessão
Estrutura ClientInfo
ClientInfo aprimorado fornece dados abrangentes de sessão:
type ClientInfo struct {
Name string `json:"name"`
Version string `json:"version"`
SamplingSupported bool `json:"sampling_supported,omitempty"`
SamplingCaps *SamplingCapabilities `json:"sampling_caps,omitempty"`
ProtocolVersion string `json:"protocol_version,omitempty"`
Env map[string]string `json:"env,omitempty"` // NEW: Environment data from transport
Roots []string `json:"roots,omitempty"` // NEW: Workspace roots from init + roots/list
}
Métodos de Conveniência de Sessão
A interface ClientSession fornece acesso fácil aos dados de sessão:
// In tool handlers
func MyTool(ctx *server.Context, args MyArgs) (interface{}, error) {
session := ctx.Session
// Get environment variables (from transport)
env := session.Env()
dbUrl := env["DATABASE_URL"]
// Get workspace roots (from init + automated roots/list)
roots := session.Roots()
// Get client capabilities
caps := session.Capabilities()
supportsSampling := caps.Sampling.Supported
supportsAudio := caps.Audio.Supported
// Use session data in tool logic...
}
Benefícios
- Zero Configuração: Extração automática de dados de sessão sem configuração manual
- Conformidade com MCP: Segue a especificação oficial MCP para tratamento de sessão
- Agnóstico de Transporte: Funciona consistentemente em todos os tipos de transporte
- Compatibilidade Retroativa: Sem mudanças que quebrem os manipuladores de ferramentas existentes
Exemplos
O diretório examples/ contém exemplos completos demonstrando vários recursos:
examples/minimal/: Exemplos básicos de cliente e servidorexamples/sampling/: Exemplos de geração de texto via API de amostragemexamples/server_config/: Exemplos de gerenciamento e configuração de servidoresexamples/server/: Vários padrões de implementação de servidores
Documentação
- GoDoc: Documentação de referência da API
docs/: Documentação e guias adicionaisdocs/examples/: Guias detalhados de recursosdocs/getting-started/: Guias de introduçãodocs/api-reference/: Documentação detalhada da API
Contribuindo
Contribuições são bem-vindas! Sinta-se à vontade para enviar um Pull Request.
Licença
Este projeto é licenciado sob a Licença MIT - veja o arquivo LICENSE para detalhes.