Ich fühle mich, als wären wir an einem Punkt, an dem das Modellkontextprotokoll (MCP) fast gleichbedeutend mit GenAI-Ingenieurwesen ist. Anthropic führte MCP im November 2024 ein, was GenAI-Ingenieurwesen revolutionierte. Es brachte uns zu einem Punkt, an dem wir als Ingenieure verschiedene Werkzeuge basierend auf unserem Anwendungsfall implementieren und den LLM unserer Wahl leiten können, um diese Werkzeuge aus dem Komfort unserer Lieblings-IDEs oder Desktop-Clients wie Claude zu verwenden. Seit seiner Ankunft gab es zahlreiche MCP-Server, die von begeisterten Ingenieuren entwickelt wurden. Dies war aufgrund der schnellen Entwicklung von MCP SDKs in verschiedenen Sprachen möglich, darunter , der Und in letzter Zeit, (Yay ) Ich habe gesehen Und ich wusste, dass ich es für einen Spin nehmen musste! Python Typschrift Golang Dieser offizielle Vorschlag für ein Golang SDK Was ist MCP, fragen Sie sich? Beginnen wir mit einer schnellen Einführung in MCP, die kurz für Bis vor kurzem erforderte KI-Ingenieurwesen eine sorgfältige Bewertung der Fähigkeiten verschiedener LLMs, um den richtigen zu wählen.Mit MCP können Sie den LLM Ihrer Wahl auswählen und seine Fähigkeiten erweitern, indem Sie benutzerdefinierte Tools implementieren und sich selbst mit externen Datenquellen verbinden!Die Hauptkonstrukte des Protokolls sind: Model Context Protocol MCP Client MCP Server Fähigkeiten (Tools, Ressourcen, Prompts) Das LLM Ein MCP-Server hat bestimmte Fähigkeiten, die durch: Tools - Die Funktionen, die es ausführen kann, wie die Liste aller E-Mails eines Absenders, die Suche nach einer PR auf Github Ressourcen - externe Datenquelle, z. B. eine Liste von Dokumenten, auf die sich der LLM beziehen möchte Prompts - Eine Reihe von Vorlagen, die Benutzern helfen, die gewünschten Antworten schnell zu erhalten Es gibt bereits viele MCP-Server, die Sie für Ihre Anwendungen verwenden können. Sie können sich auf diese Sammlung von fantastischen MCP-Server beziehen: https://github.com/punkpeye/awesome-mcp-servers BYOM (bringen Sie Ihren eigenen MCP-Server mit) In diesem Beitrag möchte ich darüber sprechen, wie wir unseren eigenen MCP-Server mithilfe von Golang entwickeln können. Vor ein paar Tagen, aus welchen Gründen auch immer, ich wollte über alle Repositories in der . Kubernetes auf GitHub Jetzt hätte ich den offiziellen GitHub MCP-Server benutzen können, aber obwohl es ein großartiges Toolkit für Repositorys und Organisationen bietet, hat es kein direktes Werkzeug für die Auflistung aller Repositorys in einer Organisation. Entwickeln eines MCP-Servers in Golang Hier ist das offizielle Golang MCP SDK: Die README und Beispiele / Ordner enthalten großartige Beispiele für die Entwicklung eines MCP-Servers und Clients. https://github.com/modelcontextprotocol/go-sdk Nach diesen Beispielen habe ich mir vorgenommen, einen MCP-Server wie folgt zu erstellen: server := mcp.NewServer(&mcp.Implementation{ Name: "demo-github-mcp", Title: "A demo github mcp server", Version: "0.0.1", }, nil) Der nächste Schritt war, dem Server die Möglichkeit zu geben, alle Repositories in einem GitHub org aufzulisten. type ToolHandlerFor[In, Out any] func(context.Context, *ServerSession, *CallToolParamsFor[In]) (*CallToolResultFor[Out], error) Danach habe ich ein Tool, das die folgenden Argumente in Bezug auf die Github org als Eingabe akzeptiert: 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: Eingabeprüfung, um sicherzustellen, dass wir nur gültige Argumente akzeptieren if params == nil { return nil, fmt.Errorf("empty params") } args := params.Arguments if args.Name == "" && args.URL == "" { return nil, fmt.Errorf("empty args") } Formulieren der GitHub API-URL aus der Eingabe basierend auf GitHub API-Docks 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" Senden Sie Ihre Anfrage und erhalten Sie eine Antwort 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)) } Reagieren Sie auf die GitHub API 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) } Geben Sie die Antwort als Textkontext zurück 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 Nachdem Sie das Tool definiert haben, ist der nächste Schritt, es mit dem MCP-Server zu registrieren: 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) Als nächstes ist es an der Zeit, den Server zu starten! Für diesen MCP-Server-Demo nutze ich den Stdio-Transport, der es dem Server ermöglicht, über STDIN und STDOUT zu kommunizieren. 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...") So sieht der Code mit allem zusammen: 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 } Jetzt ist der letzte Schritt, dies zu kompilieren und das Ausführbare mit zu generieren: go build MCP-Server mit einem Client wie Claude verwenden Schauen wir uns an, wie wir diesen Server dem Desktop von Claude hinzufügen. Öffnen Sie die Claude Desktop-App und gehen Sie zu Claude -> Einstellungen -> Entwickler. Hier sehen Sie einen Abschnitt namens Local MCP Servers. Klicken Sie auf den Button unten namens Edit Config. Dies öffnet die Datei Claude config. Bearbeiten Sie die Datei, um den MCP-Server wie folgt hinzuzufügen: { "mcpServers": { "demo-github-mcp": { "command": "/path/to/executable/generated/from/go build", "args": [] } } } Starten Sie die Claude-App neu (derzeit ist dies die einzige Möglichkeit, MCP-Server innerhalb von Claude zu aktualisieren) Und jetzt ist es an der Zeit, dies auszuprobieren!Dies ist die Anleitung, die ich gegeben habe: List all repositories in the Kubernetes github org Claude erkannte den lokal laufenden Demo-github-mcp-Server und fragte, ob er das nutzen könnte! Sobald ich es genehmigte, listete Claude die Repositories in den Org und zeigte sogar das verwendete Werkzeug (Listen-Repositories) zu Beginn der Antwort: Wir haben gesehen, wie wir mit Golang einen einfachen MCP-Server entwickeln und ihn über MCP-Clients wie Claude nutzen können! Der „Ah“ Moment Während ich diesen Server entwickelte, fragte ich mich ständig: "Wenn ich den Code für die Auflistung von Repositorys schreibe, was wird der LLM tun?" und dann klickte er plötzlich - der LLM ermöglicht die Kommunikation mit diesem Server über natürliche Sprache! Als Claude Sonnet 4 meine Aufforderung las - "Listen Sie alle Repositories in der Kubernetes GitHub org" - und selbst erkannte, dass der lokal laufende MCP-Server am besten für die Aufforderung funktionieren würde, wurde mein Geist weggeblasen 😀 An diesem Punkt musste ich ihm keine Informationen über den Toolname oder die Parameter geben, die er akzeptierte. Produktionsüberlegungen Während unser MCP-Server für Demo-Zwecke hervorragend funktioniert, fehlen für die Produktion mehrere wichtige Funktionen: Pagination Handling: Die API von GitHub gibt paginierte Ergebnisse zurück, die Standardeinstellung sind 30 Einträge pro Seite. Im obigen Code habe ich das mit dem Per_page-Abfrage-Parameter auf 100 erhöht. Tarifbegrenzung: Der obige Code berücksichtigt nicht die GitHub API-Tariflimits Authentifizierung: Authentifizierte Anfragen haben eine höhere Rate als nicht-authentifizierte Anfragen. Authentifizierung ist auch für Anfragen an private GitHub-Repositorys/Unternehmen GitHub erforderlich. Was ist als nächstes? Diese Demo von GitHub MCP-Server war ein guter Start. Aber der Hauptgrund, warum ich dies schreibe, ist zu demonstrieren, wie einfach es sein kann, Ihr Lieblings GenAI-Tool nach Ihren Wünschen anzupassen! Es ist auch überraschend einfach, mit einem MCP-Server in Golang für Ihre eigenen Projekte zu beginnen! Ich kann es kaum erwarten, die leistungsstarken Dienste zu sehen, die aus der Kombination von MCP-Flexibilität und Golangs hoher Leistung geschaffen wurden!