GoMCP

Uma biblioteca Go para construir clientes e servidores usando o Model Context Protocol (MCP).

Documentação

GoMCP - Biblioteca Go do Model Context Protocol

Go Reference Go Report Card

Conformidade com a Especificação MCP

Draft Spec: 100% 2024-11-05 Spec: 100% 2025-03-26 Spec: 100%

✅ 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

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ções
  • server.shutdown - O servidor está sendo desligado

Eventos de Conexão do Cliente:

  • client.connected - Um cliente conectou-se ao servidor
  • client.disconnected - Um cliente desconectou-se do servidor
  • client.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 servidor
  • resource.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 acessado
  • prompt.executed - Um prompt foi executado
  • request.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):

  1. Endpoint MCP Único: Usa um único endpoint que lida tanto com GET (para SSE) quanto com POST (para mensagens)
  2. Requisições do Cliente: Enviadas via HTTP POST para o endpoint MCP
  3. Respostas do Servidor: Podem ser respostas JSON imediatas ou streams SSE
  4. Mensagens Iniciadas pelo Servidor: Enviadas via streams SSE iniciados por GET
  5. Gerenciamento de Sessão: IDs de sessão opcionais via cabeçalho Mcp-Session-Id
  6. 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:

  1. client.Close() termina imediatamente a conexão MCP e pode matar o processo do servidor
  2. registry.StopAll() então tenta desligar graciosamente processos que já estão mortos
  3. 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:

  1. Extração Inicial: Extrai raízes de workspace de clientInfo.roots durante a inicialização
  2. Detecção de Capacidade: Detecta se o cliente anuncia a capacidade roots
  3. Busca Automatizada: Envia requisições roots/list após notifications/initialized
  4. Tratamento de Respostas: Processa respostas roots/list com rastreamento adequado de requisições
  5. 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 servidor
  • examples/sampling/: Exemplos de geração de texto via API de amostragem
  • examples/server_config/: Exemplos de gerenciamento e configuração de servidores
  • examples/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 adicionais
    • docs/examples/: Guias detalhados de recursos
    • docs/getting-started/: Guias de introdução
    • docs/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.