Sinto que estamos nun punto no que o Protocolo de Contexto do Modelo (MCP) se sente case sinónimo de enxeñaría GenAI. Anthropic introduciu o MCP en novembro de 2024, revolucionando a enxeñaría GenAI. Chegou ao punto onde, como enxeñeiros, podemos implementar varias ferramentas baseadas no noso caso de uso e dirixir o LLM da nosa elección para usar esas ferramentas desde o confort dos nosos clientes de escritorio favoritos como Claude. Desde a súa aparición, houbo numerosos servidores MCP desenvolvidos por enxeñeiros entusiastas. Isto foi posible debido ao rápido desenvolvemento de SDKs de MCP en varios idiomas, incluíndo , e máis recentemente, Aínda que eu o vexa , e eu sabía que tiña que tomalo por un spin! Python Tipografía Golán Esta proposta oficial para un Golang SDK Que é o MCP, pregúntaste? Comecemos cunha introdución rápida ao MCP, que é breve para Ata recentemente, a enxeñaría de IA requiriu unha avaliación coidadosa das capacidades de varios LLMs para seleccionar o correcto. Con MCP, pode seleccionar o LLM da súa elección e ampliar as súas capacidades mediante a implementación de ferramentas personalizadas e conectarse a fontes de datos externas! Model Context Protocol Cliente MCP Servidores MCP Capacidades (ferramentas, recursos, prompts) O LLM Un servidor MCP ten certas capacidades determinadas por: Ferramentas - As funcións que pode executar, como listar todos os correos electrónicos dun remitente, buscar un PR en Github Recursos - Fontes de datos externos, como unha lista de documentos aos que desexa que o LLM se refira Prompts - Un conxunto de modelos que axudan aos usuarios a obter as respostas desexadas rapidamente Hai moitos servidores MCP xa dispoñibles que podes comezar a usar para as túas aplicacións. https://github.com/punkpeye/awesome-mcp-servers BYOM (Levar o seu propio servidor MCP) Neste post, quero ir sobre como podemos desenvolver o noso propio servidor MCP usando Golang. Algúns días atrás, por calquera razón,n quería ir sobre todos os repositorios no . Páxina de Kubernetes GitHub Agora podería ter usado o servidor oficial GitHub MCP, pero aínda que ofrece un gran conxunto de ferramentas para repositorios e organizacións, non tiña unha ferramenta directa para listar todos os repositorios nunha organización. Desenvolvemento dun servidor MCP en Golang Este é o SDK oficial de Golang MCP: O README e exemplos / carpetas conteñen bos exemplos sobre o desenvolvemento dun servidor e cliente MCP. https://github.com/modelcontextprotocol/go-sdk Seguindo estes exemplos, decidín crear un servidor MCP do seguinte xeito: server := mcp.NewServer(&mcp.Implementation{ Name: "demo-github-mcp", Title: "A demo github mcp server", Version: "0.0.1", }, nil) O seguinte paso foi proporcionar ao servidor a capacidade de listar todos os repositorios nunha org de GitHub. type ToolHandlerFor[In, Out any] func(context.Context, *ServerSession, *CallToolParamsFor[In]) (*CallToolResultFor[Out], error) Despois disto, creei unha ferramenta, que acepta os seguintes argumentos relacionados co Github org como entrada: ListRepositories // User can pass in either the name of the org (example: kubernetes), or its URL (example: https://github.com/kubernetes) type GithubOrgArgs struct { Name string URL string } func ListRepositories(ctx context.Context, ss *mcp.ServerSession, params *mcp.CallToolParamsFor[GithubOrgArgs]) (*mcp.CallToolResultFor[struct{}], error) { Now let's go over the body of ListRepositories step-by-step: Verificación de entrada para garantir que só aceptemos argumentos válidos if params == nil { return nil, fmt.Errorf("empty params") } args := params.Arguments if args.Name == "" && args.URL == "" { return nil, fmt.Errorf("empty args") } Formar a URL da API de GitHub a partir da entrada baseada nos documentos da API de GitHub var apiURL string var organization string if args.URL != "" { // If URL is provided, extract org name and build API URL url := strings.TrimPrefix(args.URL, "https://") url = strings.TrimPrefix(url, "http://") url = strings.TrimPrefix(url, "github.com/") url = strings.TrimSuffix(url, "/") orgName := strings.Split(url, "/")[0] apiURL = fmt.Sprintf("https://api.github.com/orgs/%s/repos", orgName) organization = orgName } else { // Use the provided organization name apiURL = fmt.Sprintf("https://api.github.com/orgs/%s/repos", args.Name) organization = args.Name } apiURL = apiURL + "?per_page=100" Envía a túa solicitude e recibe unha resposta client := &http.Client{Timeout: 10 * time.Second} req, err := http.NewRequestWithContext(ctx, "GET", apiURL, nil) if err != nil { return nil, err } req.Header.Add("Accept", "application/vnd.github.v3+json") resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) return nil, fmt.Errorf("GitHub API error (status %d): %s", resp.StatusCode, string(body)) } Comprobar a resposta da API de GitHub type repository struct { Name string `json:"name"` FullName string `json:"full_name"` HTMLURL string `json:"html_url"` Private bool `json:"private"` } // Parse the JSON response var repositories []repository if err := json.NewDecoder(resp.Body).Decode(&repositories); err != nil { return nil, fmt.Errorf("failed to parse response: %w", err) } Devolve a resposta como un contexto textual var result strings.Builder result.WriteString(fmt.Sprintf("Repositories for organization %s:", organization)) for _, repo := range repositories { result.WriteString(fmt.Sprintf("Name: %s, URL: %s", repo.Name, repo.HTMLURL)) } return &mcp.CallToolResultFor[struct{}]{ Content: []mcp.Content{ &mcp.TextContent{Text: result.String()}, }, }, nil Despois de definir a ferramenta, o seguinte paso é rexistrala co servidor MCP: mcp.AddTool(server, &mcp.Tool{ Name: "list-repositories", Description: "A tool to list all repositories in a Github org", InputSchema: &jsonschema.Schema{ Type: "object", Properties: map[string]*jsonschema.Schema{ "name": { Type: "string", Description: "GitHub organization name (e.g., kubernetes)", }, "url": { Type: "string", Description: "GitHub organization URL (e.g., https://github.com/kubernetes)", }, }, }, }, ListRepositories) A continuación, é hora de comezar o servidor! Para este servidor MCP demo, estou a usar o transporte de estadio, que permite ao servidor comunicarse a través de STDIN e STDOUT. Este é o enfoque estándar para integracións locais de MCP con clientes como Claude Desktop ou VSCode. t := mcp.NewLoggingTransport(mcp.NewStdioTransport(), os.Stderr) log.Println("🚀 MCP server starting up...") if err := server.Run(context.Background(), t); err != nil { log.Printf("Server failed: %v", err) } log.Println("🚀 MCP server shutting down...") Así é como se ve o código con todo xuntado: func main() { server := mcp.NewServer(&mcp.Implementation{ Name: "demo-github-mcp", Title: "A demo github mcp server", Version: "0.0.1", }, nil) mcp.AddTool(server, &mcp.Tool{ Name: "list-repositories", Description: "A tool to list all repositories in a Github org", InputSchema: &jsonschema.Schema{ Type: "object", Properties: map[string]*jsonschema.Schema{ "name": { Type: "string", Description: "GitHub organization name (e.g., kubernetes)", }, "url": { Type: "string", Description: "GitHub organization URL (e.g., https://github.com/kubernetes)", }, }, }, }, ListRepositories) t := mcp.NewLoggingTransport(mcp.NewStdioTransport(), os.Stderr) log.Println("🚀 MCP server starting up...") if err := server.Run(context.Background(), t); err != nil { log.Printf("Server failed: %v", err) } log.Println("🚀 MCP server shutting down...") } type GithubOrgArgs struct { Name string URL string } func ListRepositories(ctx context.Context, ss *mcp.ServerSession, params *mcp.CallToolParamsFor[GithubOrgArgs]) (*mcp.CallToolResultFor[struct{}], error) { if params == nil { return nil, fmt.Errorf("empty params") } args := params.Arguments if args.Name == "" && args.URL == "" { return nil, fmt.Errorf("empty args") } var apiURL string var organization string if args.URL != "" { // If URL is provided, extract org name and build API URL url := strings.TrimPrefix(args.URL, "https://") url = strings.TrimPrefix(url, "http://") url = strings.TrimPrefix(url, "github.com/") url = strings.TrimSuffix(url, "/") orgName := strings.Split(url, "/")[0] apiURL = fmt.Sprintf("https://api.github.com/orgs/%s/repos", orgName) organization = orgName } else { // Use the provided organization name apiURL = fmt.Sprintf("https://api.github.com/orgs/%s/repos", args.Name) organization = args.Name } apiURL = apiURL + "?per_page=100" client := &http.Client{Timeout: 10 * time.Second} req, err := http.NewRequestWithContext(ctx, "GET", apiURL, nil) if err != nil { return nil, err } req.Header.Add("Accept", "application/vnd.github.v3+json") resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) return nil, fmt.Errorf("GitHub API error (status %d): %s", resp.StatusCode, string(body)) } type repository struct { Name string `json:"name"` FullName string `json:"full_name"` HTMLURL string `json:"html_url"` Private bool `json:"private"` } // Parse the JSON response var repositories []repository if err := json.NewDecoder(resp.Body).Decode(&repositories); err != nil { return nil, fmt.Errorf("failed to parse response: %w", err) } var result strings.Builder result.WriteString(fmt.Sprintf("Repositories for organization %s:", organization)) for _, repo := range repositories { result.WriteString(fmt.Sprintf("Name: %s, URL: %s", repo.Name, repo.HTMLURL)) } return &mcp.CallToolResultFor[struct{}]{ Content: []mcp.Content{ &mcp.TextContent{Text: result.String()}, }, }, nil } Agora o último paso é compilar isto e xerar o executable con: go build Usar o servidor MCP cun cliente como Claude Vexamos como engadir este servidor ao escritorio de Claude. Abre a aplicación de escritorio Claude e vai a Claude -> Configuración -> Desenvolvedor. Aquí verás unha sección chamada Servidores MCP locais. Faga clic no botón de abaixo chamado Edit Config. Isto abrirá o ficheiro Claude config. Editar o ficheiro para engadir o servidor MCP do seguinte xeito: { "mcpServers": { "demo-github-mcp": { "command": "/path/to/executable/generated/from/go build", "args": [] } } } Reiniciar a aplicación Claude (Actualmente, esta é a única forma de actualizar os servidores MCP dentro de Claude) E agora é hora de probar isto!Este é o prompt que dei: List all repositories in the Kubernetes github org Claude recoñeceu o servidor demo-github-mcp executado localmente e preguntou se podía usar iso! Axiña que o aprobei, Claude listou os repositorios nas org e mesmo mostrou a ferramenta usada (list-repositorios) ao comezo da resposta: Vimos como podemos desenvolver un servidor MCP sinxelo usando Golang, e usalo a través de clientes MCP como Claude! O momento do “ah” Mentres estaba a desenvolver este servidor, seguín preguntándome: "Se estou a escribir o código para listar repositorios, que vai facer o LLM?", e entón de súpeto clicou - o LLM permite a comunicación a este servidor a través da linguaxe natural! Cando Claude Sonnet 4 leu a miña solicitude - "Lista de todos os repositorios no Kubernetes GitHub org" - e por si só recoñeceu que o servidor MCP executado localmente funcionaría mellor para a solicitude, a miña mente foi soprada 😀. Naquel momento, non tiven que proporcionarlle ningunha información sobre o nome da ferramenta ou os parámetros que aceptaba. Consideracións de produción Aínda que o noso servidor MCP funciona moi ben para fins de demostración, hai varias características importantes que faltan para o uso de produción: Xestión de páxinas: A API de GitHub devolve resultados paginados, sendo por defecto 30 entradas por páxina. No código anterior, aumentou a 100 co parámetro de consulta por páxina. Para o código de produción, procesaría o encabezado de resposta Link para obter información sobre a seguinte páxina e o número total de páxinas. (Refírese a isto) Limitación de tarifas: O código anterior non ten en conta os límites de tarifas da API de GitHub Autenticación: As solicitudes autenticadas teñen un límite de taxa máis alto que as solicitudes non autenticadas. Que é o seguinte? Esta demostración do servidor MCP de GitHub foi un bo comezo. Pero a principal razón pola que estou a escribir isto é para demostrar o fácil que pode ser para personalizar a súa ferramenta GenAI favorita ao seu gusto! Tamén é sorprendentemente sinxelo comezar cun servidor MCP en Golang para os seus propios proxectos! Non podo esperar a ver os poderosos servizos creados pola combinación da flexibilidade de MCP e a natureza de alto rendemento de Golang!