Osjećam se kao da smo na točki u kojoj se Model Kontekstni Protokol (MCP) oseća gotovo sinonim za GenAI inženjering. Anthropic je uveo MCP u novembru 2024. godine, revolucionarno GenAI inženjering. To nas je dovelo do točke u kojoj, kao inženjeri, možemo implementirati različite alate na osnovu našeg slučaja upotrebe, i usmjeriti LLM našeg izbora da koristi te alate iz udobnosti naših omiljenih IDEs ili desktop klijenata kao što je Claude. Od svog dolaska, bilo je mnogo MCP servera razvijenih od entuzijazma inženjera. To je bilo moguće zbog brzog razvoja MCP SDK-ova na različitim jezicima, uključujući U pitanju je i u poslednje vrijeme, (Yay ) Video sam , i znao sam da moram uzeti za spin! Python rešenja Tipografija Golan ovaj zvanični prijedlog za Golang SDK Što je MCP, pitate se? Počnimo s brzim uvodom u MCP, koji je kratak za Do nedavno, AI inženjering zahtijeva pažljivu procjenu mogućnosti različitih LLM-ova da biste odabrali pravu. Sa MCP-om, možete odabrati LLM po svom izboru i proširiti njegove mogućnosti implementacijom prilagođenih alata i povezivanjem sa vanjskim izvorima podataka sami! Glavne konstrukcije protokola su: Model Context Protocol MCP klijent MCP poslužitelj Sposobnosti (Tools, Resources, Prompts) Međunarodni LLM MCP server ima određene mogućnosti koje određuju: Alatke - Funkcije koje može izvršiti, kao što su popis svih e-pošte od pošiljatelja, pretraživanje PR-a na Githubu resursi - spoljni izvor podataka, kao što je spisak dokumenata na koje želite da se LLM upućuje Prompts - Set predloška koji pomažu korisnicima da brzo dobiju željene odgovore Postoji mnogo MCP servera već dostupnih koje možete početi koristiti za svoje aplikacije. Možete se obratiti ovoj kompilaciji sjajnih MCP servera: https://github.com/punkpeye/awesome-mcp-servers BYOM (Provedite svoj MCP server) U ovom postu, želim da prođem kroz kako možemo razviti naš vlastiti MCP server koristeći Golang. Prije nekoliko dana, bez obzira na razlog,n Želeo sam da idem preko svih repozitorija u . Kubernetes GitHub org Sada sam mogao koristiti zvanični GitHub MCP server, ali iako nudi odličan alat za repozitorije i organizacije, nije imao direktan alat za popis svih repozitorija u organizaciji. Razvoj MCP servera u Golangu Ovo je zvanični Golang MCP SDK: README i primjeri / fascikli sadrže odlične primjere razvoja MCP servera i klijenta. https://github.com/modelcontextprotocol/go-sdk Slijedom tih primjera, odlučio sam da kreiram MCP server kako slijedi: server := mcp.NewServer(&mcp.Implementation{ Name: "demo-github-mcp", Title: "A demo github mcp server", Version: "0.0.1", }, nil) Sljedeći korak bio je da se serveru pruži mogućnost popisa svih repozitorija u GitHub org. type ToolHandlerFor[In, Out any] func(context.Context, *ServerSession, *CallToolParamsFor[In]) (*CallToolResultFor[Out], error) Nakon toga, stvorio sam a alat, koji prihvaća sljedeće argumente vezane za Github org kao ulaz: 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: Provjera unosa kako bi se osiguralo da prihvatimo samo valjane argumente if params == nil { return nil, fmt.Errorf("empty params") } args := params.Arguments if args.Name == "" && args.URL == "" { return nil, fmt.Errorf("empty args") } Oblikovanje GitHub API URL-a iz ulaza zasnovanog na GitHub API dokumentima 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" Pošaljite zahtjev i dobijte odgovor 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)) } Parse odgovor iz GitHub API-ja 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) } Vratite odgovor kao tekstualni kontekst 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 Nakon definicije alata, sledeći korak je da ga registrujete sa MCP serverom: 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) Za ovaj demo MCP server koristim stdio transport, koji omogućava serveru da komunicira preko STDIN i STDOUT. Ovo je standardni pristup za lokalne MCP integracije sa klijentima kao što su Claude Desktop ili 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...") Evo kako kod izgleda sa svim zajedno: 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 } Sada je završni korak kompilirati ovo i generisati izvršivo sa: go build Korištenje MCP servera sa klijentima kao što je Claude Pogledajmo kako dodati ovaj server na Claude's desktop. Otvorite Claude desktop aplikaciju i idite na Claude -> Settings -> Developer. Ovde ćete vidjeti odjeljak pod nazivom Lokalni MCP serveri. Kliknite na dugme ispod pod nazivom Edit Config. Ovo će otvoriti Claude config datoteku. Uredi datoteku da biste dodali MCP server na sledeći način: { "mcpServers": { "demo-github-mcp": { "command": "/path/to/executable/generated/from/go build", "args": [] } } } Ponovo pokrenite Claude aplikaciju (trenutno je to jedini način za osvežavanje MCP servera unutar Claude) A sada je vreme da ovo isprobate! Ovo je poziv koji sam dao: List all repositories in the Kubernetes github org Claude je prepoznao lokalno pokrenut demo-github-mcp server i pitao može li to koristiti! Čim sam ga odobrio, Claude je naveo repozitorije u org i čak je prikazivao alat koji se koristi (list-repozitorije) na početku odgovora: Vidjeli smo kako možemo razviti jednostavan MCP server koristeći Golang, i koristiti ga preko MCP klijenata kao što je Claude! “Aha” trenutak Dok sam razvijao ovaj server, stalno sam se pitao: "Ako pišem kod za popis repozitorija, šta će LLM učiniti?" i onda odjednom kliknuo - LLM omogućava komunikaciju s ovim serverom putem prirodnog jezika! Kada je Claude Sonnet 4 pročitao moj upitnik - "Popis svih repozitorija u Kubernetes GitHub org-u" - i sam prepoznao da će lokalno pokrenuti MCP server najbolje raditi za upitnik, moj um je bio izbačen 😀 U tom trenutku, nisam morao pružiti nikakve informacije o imenu alata ili parametara koji je prihvatio. Razmatranja proizvodnje Iako naš MCP server radi odlično za demo svrhe, postoji nekoliko važnih značajki koje nedostaju za upotrebu u proizvodnji: Upravljanje paginiranjem: GitHubov API vraća paginirane rezultate, podrazumevano 30 stavki po stranici. U gornjem kodu sam to povećao na 100 s parametrom upita po stranici. Za proizvodni kod, obradićete naslov odgovora Link da biste dobili informacije o sledećoj stranici i ukupnom broju stranica. (Referirajte se na ovo) Ograničenje stope: gornji kod ne uzima u obzir GitHub API ograničenja stope Autentifikacija: Autentificirani zahtjevi imaju veću granicu stope od neautentificiranih zahtjeva. Šta je sledeće? Ovaj demo GitHub MCP server bio je dobar početak. Ali glavni razlog zbog kojeg ovo pišem je da pokažem koliko lako može biti prilagoditi vaš omiljeni GenAI alat prema vašem ukusu! Također je iznenađujuće jednostavno započeti s MCP serverom u Golangu za svoje projekte! Jedva čekam da vidim moćne usluge stvorene kombinacijom MCP fleksibilnosti i Golangove visoke performanse!