私たちは、モデルコンテキスト・プロトコル(MCP)がGenAIエンジニアリングとほぼ同義であるという点にいるように感じます。Anthropicは2024年11月にMCPを導入し、GenAIエンジニアリングを革命させました。それは私たちをエンジニアとして、私たちの使用ケースに基づいてさまざまなツールを導入し、私たちの好きなIDEsやCloudeのようなデスクトップクライアントの快適さからこれらのツールを使用するために私たちの選択のLLMを指揮することができます。その登場以来、多くのMCPサーバーがエンジニアの熱心によって開発されています。これは、さまざまな言語でMCP SDKの急速な開発によって可能でした。 で、 そして、最近、 ☆☆☆☆☆☆☆☆☆ , and I knew I had to take it for a spin! Python TypeScript ゴラン この Golang SDK の公式提案 MCPってなに?と聞かれます。 まずは、MCPの簡単な紹介から始めましょう。 最近まで、AIエンジニアリングは、適切なものを選択するためにさまざまなLLMの能力を慎重に評価する必要がありました。MCPを使用して、あなたはあなたの選択のLLMを選択し、カスタムツールを実装し、外部データソースに接続することによってその能力を拡張することができます!プロトコルの主な構造は次のとおりです。 Model Context Protocol MCPクライアント MCPサーバー 能力(ツール、リソース、プロンプト) LLM MCP サーバーには、以下によって決定される特定の機能があります。 ツール - 送信者からのすべての電子メールのリスト、Github上のPR検索などの機能を実行できます。 リソース - LLMが参照したい文書のリストなど、外部のデータ源 Prompts - ユーザーが望む答えを迅速に得るのを助けるテンプレートのセット すでにあなたのアプリケーションで使用を開始できる多くのMCPサーバーがあります. You can refer to this compilation of awesome MCP servers: https://github.com/punkpeye/awesome-mcp-servers BYOM(Make Your Own MCP Server) この投稿では、私たちがゴランを使用して独自のMCPサーバを開発する方法について話したいと思います. 数日前に、どんな理由であれ、私は、ゴランのすべてのリポジトリを参照したいと思いました。 . Kubernetes GitHub 今では、公式のGitHub MCP サーバーを使用していたかもしれないが、リポジトリや組織のための素晴らしいツールセットを提供しているにもかかわらず、組織内のすべてのリポジトリをリストするための直接のツールを持っていなかった。 ゴランでMCPサーバーを開発する これはGolang MCP SDKです。 READMEと例/フォルダには、MCPサーバとクライアントの開発についての素晴らしい例が含まれています。 https://github.com/modelcontextprotocol/go-sdk これらの例に従って、私は次のようにMCPサーバを作成することを決めました。 server := mcp.NewServer(&mcp.Implementation{ Name: "demo-github-mcp", Title: "A demo github mcp server", Version: "0.0.1", }, nil) 次のステップは、サーバーにGitHub org内のすべてのリポジトリをリストする能力を提供することでした。 type ToolHandlerFor[In, Out any] func(context.Context, *ServerSession, *CallToolParamsFor[In]) (*CallToolResultFor[Out], error) その後、私はAを創り出しました。 入力として Github org に関連する次のアルゴリズムを受け入れるツール: 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: 入力検証で、我々は有効な議論のみを受け入れることを確認する if params == nil { return nil, fmt.Errorf("empty params") } args := params.Arguments if args.Name == "" && args.URL == "" { return nil, fmt.Errorf("empty args") } GitHub APIドキュメントに基づく入力からGitHub API URLを形成する 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)) } 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) } テキストの文脈として返す 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 ツールを定義した後、次のステップは、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) 次に、サーバーを起動する時間です! このデモ MCP サーバーでは、サーバーが STDIN と STDOUT を介して通信することを可能にするスタジオ トランスポートを使用しています。 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...") これがコードがすべてを組み合わせたように見えます: 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 } 最後のステップは、これをコンパイルし、以下で実行可能なものを生成することです。 go build Claude のようなクライアントで MCP サーバーを使用する このサーバーを Claude のデスクトップに追加する方法を見てみましょう。 Claude デスクトップ アプリを開いて、Claude -> Settings -> Developer を参照してください。 ここでは、ローカルMCPサーバーと呼ばれるセクションが表示されます。Edit Configと呼ばれるボタンをクリックすると、Cloude configファイルが開きます。 MCP サーバを追加するためのファイルを以下のように編集します。 { "mcpServers": { "demo-github-mcp": { "command": "/path/to/executable/generated/from/go build", "args": [] } } } Claude アプリを再起動する (現時点では、Claude 内の MCP サーバーをリフレッシュする唯一の方法) そして今はこれをテストする時です! これが私が与えたスンプトです: List all repositories in the Kubernetes github org Claude は、現地で実行されている demo-github-mcp サーバを認識し、それを使用できるかどうか尋ねました! 私が承認した直後、Claude は org にリポジトリをリストし、応答の開始時に使用したツール(リストリポジトリ)を表示しました。 私たちは、Golangを使用してシンプルなMCPサーバを開発し、ClaudeのようなMCPクライアントを通じてそれを使用する方法を見ました! 「AHA」の瞬間 このサーバーを開発していたとき、私は自分に「リポジトリのリストを作成するためのコードを書いているのなら、LLMは何をしますか?」と尋ね続けたが、突然、LLMは自然言語を介してこのサーバーとのコミュニケーションを可能にします! Claude Sonnet 4 が「Kubernetes GitHub org ですべてのリポジトリをリストする」と私のプロンプトを読んで、ローカルで実行されている MCP サーバがプロンプトのために最適であることを自覚したとき、私の心は吹き飛ばされました 😀 その時点で、私はそれを受け入れられたツール名やパラメータに関する情報を提供する必要はありませんでした。 生産観点 私たちのMCPサーバはデモ用に素晴らしい機能を提供していますが、生産用に欠けているいくつかの重要な機能があります。 Pagination handling: GitHub の API は Paginated 結果を返します、デフォルトはページあたり 30 個のエントリです。上記のコードでは、per_page クエリパラメータで 100 個まで増加しました。 料金制限:上記のコードは GitHub API 料金制限を考慮しない Authentication: Authenticated requests have a higher rate limit than unauthenticated requests. Authentication is also required for requests to private GitHub repositories/enterprise GitHub. 認証されたリクエストは、未認証されたリクエストよりも高い割合制限があります。 次は何? このデモ GitHub MCP サーバーは良いスタートだったが、私がこれを書いている主な理由は、お気に入りの GenAI ツールをあなたの好みに合わせてカスタマイズするのがどれほど簡単かを示すためだ! 自分のプロジェクトのために Golang で MCP サーバーを始めるのも驚くほど簡単だ! 私は MCP の柔軟性と Golang の高性能の性質の組み合わせから作られた強力なサービスを見るのを楽しみにしています!