GoMCP
Una biblioteca en Go para construir clientes y servidores utilizando el Protocolo de Contexto de Modelo (MCP).
Documentación
GoMCP - Biblioteca Go del Protocolo de Contexto de Modelo
Cumplimiento de la Especificación MCP
✅ Cumplimiento total en todas las versiones de la especificación MCP - Consulte COMPLIANCE.md para verificación detallada.
GoMCP es una implementación completa en Go del Protocolo de Contexto de Modelo (MCP), diseñada para facilitar la interacción fluida entre aplicaciones y Modelos de Lenguaje de Gran Escala (LLMs). La biblioteca admite todas las versiones de la especificación con negociación automática y proporciona una API limpia e idiomática tanto para clientes como para servidores.
Tabla de Contenidos
- Descripción General
- Características Clave
- Estabilidad de la API
- Instalación
- Inicio Rápido
- Conceptos Fundamentales
- Ejemplos
- Documentación
- Contribuciones
- Licencia
Descripción General
El Protocolo de Contexto de Modelo (MCP) estandariza la comunicación entre aplicaciones y LLMs, permitiendo:
- Llamada de Herramientas: Ejecutar acciones y funciones a través de LLMs
- Acceso a Recursos: Proporcionar datos estructurados a LLMs con contexto del espacio de trabajo
- Renderizado de Prompts: Crear plantillas reutilizables para interacciones con LLMs
- Muestreo: Generar texto desde LLMs con control sobre los parámetros
- Gestión de Sesiones: Acceso a contexto enriquecido y raíces del espacio de trabajo para capacidades mejoradas de herramientas
GoMCP proporciona una implementación idiomática en Go que maneja todos los detalles del protocolo mientras ofrece una API limpia y amigable para desarrolladores.
Características Clave
- Implementación Completa del Protocolo: Soporte total para todas las versiones de la especificación MCP
- Negociación Automática de Versiones: Compatibilidad fluida entre clientes y servidores
- Múltiples Opciones de Transporte: Soporte para stdio, HTTP, WebSocket y Eventos Enviados por el Servidor
- API Segura por Tipos: Aprovecha el sistema de tipos de Go para seguridad y expresividad
- Gestión de Procesos de Servidor: Iniciar, gestionar y detener automáticamente servidores MCP externos
- Configuración de Servidores: Cargar definiciones de servidores desde archivos de configuración
- Arquitectura de Sesiones MCP: Gestión integral de sesiones con extracción de datos consciente del transporte
- Obtención Automática de Raíces: Descubrimiento automático de raíces del espacio de trabajo siguiendo el protocolo MCP
- Arquitectura Flexible: Diseño modular para fácil extensión y personalización
Estabilidad de la API
GoMCP v1.5.0 representa una versión estable, lista para producción con APIs bloqueadas. La biblioteca ha alcanzado plena madurez con un conjunto integral de características e implementaciones probadas en batalla.
🔒 Garantía de Bloqueo de API (v1.5.0+)
- API de Cliente: Todos los métodos de cliente (
CallTool,GetResource,GetPrompt, etc.) están bloqueados y estables - API de Servidor: Los métodos de registro de servidor (
Tool,Resource,Prompt) y los patrones de manejadores están finalizados - Capa de Transporte: Todas las implementaciones de transporte siguen interfaces estables y bloqueadas
- Sistema de Eventos: Los tipos de eventos y patrones de suscripción están estandarizados y bloqueados
- Gestión de Servidores: Las APIs de ciclo de vida de procesos y gestión de configuración son estables
✅ Cumplimiento Total del Protocolo
- Soporte Completo de la Especificación MCP: Implementación total de todas las versiones del protocolo (2024-11-05, 2025-03-26, borrador)
- Negociación Automática de Versiones: Manejo fluido de compatibilidad entre diferentes versiones de la especificación
- Cumplimiento del Transporte: Todas las capas de transporte implementan correctamente sus respectivas especificaciones MCP
- Seguridad de Tipos: Tipado fuerte en todo el sistema asegura que los contratos de API se mantengan
✅ Listo para Producción
- Pruebas Integrales: Amplia cobertura de pruebas en todos los componentes principales
- Manejo de Errores: Manejo robusto de errores con códigos y mensajes de error MCP apropiados
- Rendimiento: Optimizado para cargas de trabajo de producción con gestión eficiente de recursos
- Documentación Completa: Documentación completa de API y ejemplos de uso
🚀 Desarrollo Futuro
Con el bloqueo de API de v1.5.0, las versiones futuras se centrarán en:
- Características Aditivas: Nueva funcionalidad que extiende pero no rompe las APIs existentes
- Optimizaciones de Rendimiento: Mejoras internas que mantienen la compatibilidad de API
- Documentación Mejorada: Ejemplos ampliados y guías de integración
- Nuevas Opciones de Transporte: Implementaciones adicionales de transporte usando la interfaz de transporte estable
Compromiso: Las APIs de v1.5.0 están bloqueadas y no cambiarán. Cualquier mejora futura será aditiva y mantendrá compatibilidad total hacia atrás. GoMCP está listo para despliegues empresariales de producción.
Instalación
go get github.com/localrivet/gomcp
Inicio Rápido
Ejemplo 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 ejemplo utiliza transporte stdio, lo que significa que el cliente espera comunicarse con un servidor MCP a través de stdin/stdout. Para un ejemplo completo que gestiona automáticamente el proceso del servidor, consulte la sección "Cliente con Gestión Automática de Servidores" a continuación.
Cliente con Gestión Automática de Servidores
Qué hace esto: Este ejemplo demuestra la potente característica de gestión automática de servidores de GoMCP. En lugar de iniciar y detener manualmente los procesos del servidor MCP, el cliente puede automáticamente:
- Iniciar procesos de servidor bajo demanda usando comandos del sistema
- Establecer conexiones a esos servidores a través de stdio/tuberías
- Inyección de variables de entorno para configuración (claves API, etc.)
- Limpieza automática - los procesos del servidor se terminan cuando el cliente se cierra
- Gestión del ciclo de vida del proceso - maneja el inicio del servidor, verificaciones de salud y apagado
Por qué esto importa: Este patrón elimina la complejidad operativa de gestionar servidores MCP. Puede distribuir un solo binario que automáticamente active los servidores MCP requeridos, haciendo el despliegue e integración mucho más simples. Es especialmente útil para:
- Entornos de desarrollo - iniciar automáticamente servicios dependientes
- Tuberías CI/CD - activar servidores para pruebas sin configuración manual
- Aplicaciones de escritorio - incrustar servidores MCP sin requerir instalación separada
- Arquitecturas de microservicios - gestionar dependencias de servidores 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)
}
Detalles Clave de Implementación:
- La sintaxis
${ANTHROPIC_API_KEY}inyecta automáticamente variables de entorno del proceso actual - Los procesos del servidor se comunican a través de tuberías stdio para IPC seguro y de alto rendimiento
- El cliente espera la inicialización del servidor antes de aceptar solicitudes
- Apagado elegante asegura que los servidores se terminen correctamente, previniendo procesos huérfanos
- Múltiples servidores pueden gestionarse simultáneamente con diferentes configuraciones
Ejemplo 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)
}
}
Ejemplo Avanzado 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"
}
}
Conceptos Fundamentales
Clientes y Servidores
client.Client: Interfaz para comunicarse con servidores MCP. Maneja la negociación del protocolo, la gestión de solicitudes/respuestas y el ciclo de vida del servidor.server.Server: Componente central para implementar servidores MCP. Proporciona métodos de registro para herramientas, recursos y prompts con generación automática de esquemas.
Herramientas
Las herramientas exponen funciones que los LLMs pueden llamar para realizar acciones. GoMCP soporta:
- Parámetros seguros por tipos usando definiciones de estructuras en línea
- Generación automática de esquemas a partir de etiquetas de estructuras Go
- Manejo de errores con respuestas de error MCP apropiadas
- Soporte de cancelación para operaciones de larga duración
Lado del Servidor (Implementación):
// 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 del 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
Los recursos proporcionan datos estructurados a los LLMs en varios formatos:
- Recursos estáticos con URIs fijos (por ejemplo,
/config,/status) - Recursos con plantillas con parámetros de ruta (por ejemplo,
/files/{path*},/users/{id}) - Recursos dinámicos que pueden aceptar parámetros adicionales de los cuerpos de solicitud
- Múltiples tipos de contenido incluyendo texto, imágenes, enlaces y datos binarios
Lado del Servidor (Implementación):
// 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 del 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
Los prompts definen plantillas de mensajes reutilizables para interacciones con LLMs:
- Variables de plantilla usando sintaxis
{{variable}} - Múltiples tipos de mensajes (Usuario, Asistente, Sistema)
- Conversaciones basadas en roles para patrones de interacción complejos
- Validación de argumentos para parámetros de plantilla
Lado del Servidor (Implementación):
// 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 del 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)
Operaciones por Lotes
GoMCP soporta operaciones por lotes JSON-RPC para rendimiento mejorado:
- Reducción de viajes de ida y vuelta de red enviando múltiples solicitudes a la vez
- Mantenimiento del orden de solicitudes en las respuestas
- Tipos de solicitudes mixtas (herramientas, recursos, prompts) en un solo lote
- Manejo de fallos parciales con errores de respuesta individuales
- Interfaz fluida de construcción para fácil construcción de lotes
Lado del 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 proporciona un sistema integral de eventos que le permite monitorear y reaccionar a diversas actividades dentro de su servidor o cliente MCP. El sistema de eventos utiliza una arquitectura segura por tipos basada en canales para máximo rendimiento y confiabilidad.
Beneficios Clave:
- Monitoreo en tiempo real de operaciones del servidor e interacciones del cliente
- Manejo de eventos seguro por tipos con estructuras de eventos fuertemente tipadas
- Arquitectura basada en canales para procesamiento de eventos de alto rendimiento y no bloqueante
- Cobertura integral de todos los eventos del ciclo de vida del servidor y operaciones
- Integración fácil con sistemas de registro, métricas y monitoreo
Tipos de Eventos Disponibles
GoMCP emite eventos para todas las operaciones principales y cambios de ciclo de vida:
Eventos del Ciclo de Vida del Servidor:
server.initialized- El servidor ha iniciado y está listo para aceptar solicitudesserver.shutdown- El servidor se está apagando
Eventos de Conexión del Cliente:
client.connected- Un cliente se conectó al servidorclient.disconnected- Un cliente se desconectó del servidorclient.initializing- El cliente está comenzando a conectarse (lado del cliente)client.initialized- El cliente se conectó exitosamente (lado del cliente)client.error- La operación del cliente falló (lado del cliente)
Eventos de Registro:
tool.registered- Una herramienta fue registrada con el servidorresource.registered- Un recurso fue registrado con el servidor
Eventos de Operación:
tool.executed- Una herramienta fue ejecutada (operación exitosa)resource.accessed- Un recurso fue accedidoprompt.executed- Un prompt fue ejecutadorequest.failed- Cualquier solicitud MCP falló
Uso de Eventos en el Lado del Servidor
Configuración de Suscripciones a 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 en el Lado del Cliente
Monitoreo de Operaciones del 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)
}
Patrones de Integración del Sistema de Eventos
Recopilación 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 Auditoría:
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
})
}
Monitoreo de Salud:
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
}
Referencia de Estructura de Eventos
Todos los eventos incluyen metadatos integrales y siguen patrones consistentes:
Eventos del Servidor incluyen nombre del servidor, marcas de tiempo y métricas operativas Eventos del Cliente incluyen IDs de sesión, versiones de protocolo y detalles de conexión Eventos de Operación incluyen nombres de métodos, cargas útiles JSON reales y resultados de ejecución Eventos de Error incluyen información detallada de errores y contexto para depuración
Para un ejemplo completo de trabajo con todos los tipos de eventos, consulte examples/events_integration/main.go.
Transportes
GoMCP admite múltiples capas de transporte para diferentes casos de uso:
- stdio: Herramientas CLI e integración directa con LLM
- WebSocket: Comunicación bidireccional para aplicaciones web
- Server-Sent Events (SSE): Patrón híbrido con SSE para servidor a cliente y HTTP POST para cliente a servidor
- HTTP: Interfaces RESTful simples
- Unix Socket: Comunicación interproceso de alto rendimiento
- UDP: Comunicación de baja sobrecarga y alto rendimiento
- MQTT: Mensajería de publicación/suscripción para aplicaciones IoT
- NATS: Mensajería nativa de la nube y de alto rendimiento
- gRPC: Comunicación servicio a servicio con tipado fuerte
Lado del servidor (Implementación):
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 del 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 el transporte SSE: El transporte SSE implementa correctamente la especificación MCP "Streamable HTTP" (2025-03-26):
- Endpoint MCP único: Utiliza un solo endpoint que maneja tanto GET (para SSE) como POST (para mensajes)
- Solicitudes del cliente: Se envían mediante HTTP POST al endpoint MCP
- Respuestas del servidor: Pueden ser respuestas JSON inmediatas o flujos SSE
- Mensajes iniciados por el servidor: Se envían mediante flujos SSE iniciados por GET
- Gestión de sesiones: IDs de sesión opcionales mediante el encabezado
Mcp-Session-Id - Compatibilidad hacia atrás: Admite el patrón heredado 2024-11-05 con respaldo automático
Gestión de servidores
GoMCP proporciona gestión automática de procesos de servidores MCP externos:
- Inicio automático de procesos desde archivos de configuración o definiciones programáticas
- Inyección de variables de entorno con sintaxis
${VAR} - Apagado ordenado y limpieza cuando los clientes se desconectan
- Soporte multi-servidor para arquitecturas complejas
- Monitoreo de salud y gestión de conexiones
Lado del 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)
}
}
Ejemplo de archivo de configuración (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"
}
}
}
Patrones de limpieza adecuados
Al usar registros de servidores con múltiples servidores MCP, es importante seguir patrones de limpieza adecuados para evitar condiciones de carrera:
✅ Patrón correcto: Usar 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 ...
❌ Patrón incorrecto: 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 qué esto crea una condición de carrera:
client.Close()termina inmediatamente la conexión MCP y puede matar el proceso del servidorregistry.StopAll()luego intenta apagar ordenadamente procesos que ya están muertos- Esto resulta en errores "Failed to wait for server process error='signal: killed'"
Mejores prácticas
- Siempre use
registry.StopAll()- maneja toda la limpieza de clientes internamente - Use bloques defer para garantizar la limpieza al salir del programa
- No mezcle client.Close() manual con registry.StopAll()
- Maneje los errores de limpieza con elegancia - los procesos pueden haber terminado ya
Gestión de sesiones
GoMCP v1.5.5 introduce una gestión integral de sesiones con la Arquitectura de Sesiones MCP, proporcionando contexto enriquecido y descubrimiento automatizado del espacio de trabajo:
Acceso a sesiones en el lado del 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
})
Datos de sesión conscientes del transporte
GoMCP extrae automáticamente los datos de sesión de la capa de transporte según la especificación MCP:
- stdio: Entorno de las variables de entorno del proceso
- HTTP: Entorno de los encabezados de solicitud (patrón
X-Env-*) - WebSocket: Entorno de los encabezados de conexión
- SSE: Entorno de los encabezados de solicitud inicial
Descubrimiento automatizado de raíces del espacio de trabajo
El servidor detecta automáticamente cuando los clientes admiten la capacidad roots y:
- Extracción inicial: Extrae las raíces del espacio de trabajo de
clientInfo.rootsdurante la inicialización - Detección de capacidades: Detecta si el cliente anuncia la capacidad
roots - Obtención automatizada: Envía solicitudes
roots/listdespués denotifications/initialized - Manejo de respuestas: Procesa las respuestas
roots/listcon seguimiento adecuado de solicitudes - Integración de contexto: Hace que las raíces estén disponibles mediante
ctx.Session.Roots()
Cumplimiento del protocolo MCP
Cumplimiento total en las tres versiones del protocolo MCP:
- 2024-11-05: Extracción básica de raíces y detección de capacidades
- 2025-03-26: Gestión de sesiones mejorada con detección de soporte de audio
- draft: Últimas características con arquitectura de sesiones completa
Estructura ClientInfo
El ClientInfo mejorado proporciona datos de sesión integrales:
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 conveniencia de sesión
La interfaz ClientSession proporciona acceso fácil a los datos de sesión:
// 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...
}
Beneficios
- Configuración cero: Extracción automática de datos de sesión sin configuración manual
- Cumplimiento MCP: Sigue la especificación oficial de MCP para el manejo de sesiones
- Independiente del transporte: Funciona de manera consistente en todos los tipos de transporte
- Compatibilidad hacia atrás: Sin cambios disruptivos para los manejadores de herramientas existentes
Ejemplos
El directorio examples/ contiene ejemplos completos que demuestran varias características:
examples/minimal/: Ejemplos básicos de cliente y servidorexamples/sampling/: Ejemplos de generación de texto mediante la API de muestreoexamples/server_config/: Ejemplos de gestión y configuración de servidoresexamples/server/: Varios patrones de implementación de servidores
Documentación
- GoDoc: Documentación de referencia de la API
docs/: Documentación y guías adicionalesdocs/examples/: Guías detalladas de característicasdocs/getting-started/: Guías de inicio rápidodocs/api-reference/: Documentación detallada de la API
Contribuciones
¡Las contribuciones son bienvenidas! No dude en enviar un Pull Request.
Licencia
Este proyecto está licenciado bajo la Licencia MIT - consulte el archivo LICENSE para más detalles.