GoでMCP(Model Context Protocol)サーバーを立ててみた

## はじめに

MCP(Model Context Protocol)は、AIモデルが外部ツールやデータソースと安全かつシームレスに連携するためのオープンスタンダードのことです。  
MCPを活用すると、AIモデルとデータソースの間で統一されたプロトコルに基づく通信が可能になり、汎用性の高いアーキテクチャを実現できます。

本記事では、Goを用いてMCPサーバーを構築し、JSON-RPC 2.0を通じてクライアントと通信する方法を解説します。

**対象読者**
- MCPの基本概念を学びたい方
- GoでJSON-RPC 2.0サーバーを実装したい方
- AIモデルと外部ツールを連携させる仕組みを作りたい方

**目次**
1. MCP(Model Context Protocol)とは？
   - MCPの役割と仕組み
   - なぜMCPを使うのか？
2. GoでMCPサーバーを実装する
   - JSON-RPC 2.0の基本
   - MCPサーバーのエンドポイント定義
   - GoでのJSON-RPC 2.0サーバーの実装
3. MCPクライアントを実装し、動作確認
   - JSON-RPC 2.0クライアントの実装
   - 実際にデータの送受信を行う
4. まとめ

---

## 1. MCP(Model Context Protocol)とは？

#### **1.1 MCPの役割と仕組み**

MCPは、AIモデルが外部データソースやツールと安全にやり取りするための通信プロトコルです。  
以下のような特徴を持っています。

| 特徴 | 説明 |
|------|------|
| **標準化** | 外部システムと統一されたインターフェースで通信可能 |
| **セキュア** | データの送受信時に認証・暗号化を実装可能 |
| **スケーラブル** | AIモデルのスケールアウトが容易になる |

#### **1.2 なぜMCPを使うのか？**

MCPを導入することで、AIモデルのデータ連携がより柔軟かつ安全になります。

例えば、以下のようなユースケースがあります。
- AIモデルがデータベースからリアルタイムで情報を取得
- 外部のフィードバックシステムと統合し、モデルを自動調整
- 異なるツールとAPIを統一的に管理

#### **1.3 MCP は JSON-RPC 2.0 をベースにしている**

MCPは**JSON-RPC 2.0 を基盤としたプロトコル**であり、リクエストとレスポンスの形式はJSONで統一されています。  
このため、**gRPCではなく、JSON-RPC 2.0に基づいたサーバーを実装する必要があります**。(誤りがあったため修正しました。)

---

## 2. GoでMCPサーバーを実装する

#### **2.1 JSON-RPC 2.0の基本**

JSON-RPC 2.0は、**JSON形式のリクエストとレスポンスを用いた軽量なRPC（Remote Procedure Call）プロトコル**です。

基本的な JSON-RPC 2.0 のリクエスト・レスポンスのフォーマットは以下の通りです。

**リクエストの例（AI モデルへの入力）**
```json
{
  "jsonrpc": "2.0",
  "method": "processInput",
  "params": { "model_id": "AI-001", "input_data": "Hello" },
  "id": 1
}
```

**レスポンスの例（処理結果）**
```json
{
  "jsonrpc": "2.0",
  "result": { "output_data": "Processed: Hello", "success": true },
  "id": 1
}
```

#### **2.2 GoでJSON-RPC 2.0サーバーを実装**

**MCP サーバー（`server.go`）**
```go
package main

import (
    "encoding/json"
    "log"
    "net/http"
)

type Request struct {
    JSONRPC string `json:"jsonrpc"`
    Method  string `json:"method"`
    Params  map[string]string `json:"params"`
    ID      int `json:"id"`
}

type Response struct {
    JSONRPC string `json:"jsonrpc"`
    Result  map[string]interface{} `json:"result"`
    ID      int `json:"id"`
}

func handleMCPRequest(w http.ResponseWriter, r *http.Request) {
    var req Request
    json.NewDecoder(r.Body).Decode(&req)
    log.Printf("Received request: %s", req.Method)
    
    response := Response{
        JSONRPC: "2.0",
        Result: map[string]interface{}{
            "output_data": "Processed: " + req.Params["input_data"],
            "success": true,
        },
        ID: req.ID,
    }
    json.NewEncoder(w).Encode(response)
}

func main() {
    http.HandleFunc("/mcp", handleMCPRequest)
    log.Println("MCP Server is running on port 8080")
    http.ListenAndServe(":8080", nil)
}
```

- `/mcp` エンドポイントを作成し、**JSON-RPC 2.0 のリクエストを処理**
- `handleMCPRequest()` でリクエストをパースし、レスポンスを生成

---

## 3. MCPクライアントを実装し、動作確認

#### **3.1 JSON-RPC 2.0クライアントの実装**

**MCPクライアント（`client.go`）**
```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
)

func main() {
    requestBody := map[string]interface{}{
        "jsonrpc": "2.0",
        "method": "processInput",
        "params": map[string]string{"model_id": "AI-001", "input_data": "Hello"},
        "id": 1,
    }

    jsonBody, _ := json.Marshal(requestBody)
    resp, _ := http.Post("http://localhost:8080/mcp", "application/json", bytes.NewBuffer(jsonBody))
    var response map[string]interface{}
    json.NewDecoder(resp.Body).Decode(&response)
    fmt.Println(response)
}
```

- クライアントがJSON-RPC 2.0のリクエストを送信
- `/mcp` エンドポイントにHTTP POSTで送信し、レスポンスを受信

---

## 4. まとめ

| 項目 | 説明 |
|------|------|
| **MCP** | AIモデルと外部ツールを連携するプロトコル |
| **JSON-RPC 2.0** | 軽量なRPCプロトコルで、MCPの基盤 |
| **MCP サーバー** | JSON-RPC 2.0を用いてGoで実装 |

少し高度な内容になりますが、話題のMCPサーバーに触れて見ました。
MCPの場合は、MCPサーバーとMCPクライアントが存在し、一定のプロトコルに沿ってやりとりするため、特定のAIモデルに依存せずいい感じにやり取りができます！