AIエージェント・プロトコル実装ガイド ― MCP / A2A / AG-UI / x402 / DID をGoで書く
結論を先に言う。 エージェントプロトコル群は、概念図で眺めるとレイヤが綺麗に分かれていることが分かるが、実装に降ろした瞬間に「結局なにを書けばいいのか」が見えなくなる。本稿は、2026年6月末時点で実用性のある主要プロトコル ―― MCP / A2A / AG-UI / x402 / DID ―― を、Goでの具体的なサーバ/クライアント実装まで踏み込んで解説する。SSEのクライアントだけはiOS/Swiftで補う。
全体マップ ― 8つのレイヤ
最初に俯瞰しておく。乱立しているように見えるエージェントプロトコルは、「誰と何を交換するか」という軸でおおむね8レイヤに収まる。
| レイヤ | 役割 | 代表的なプロトコル | 本稿でGo実装 |
|---|---|---|---|
| 1. Agent ↔ Tool / Data | ツール・データへの接続 | MCP, WebMCP | ◯ |
| 2. Agent ↔ Agent | エージェント間の水平協調 | A2A, ACP, ANP, AConP, SLIM | ◯ |
| 3. Agent ↔ UI / User | フロントエンド連携 | AG-UI, A2UI | ◯ + Swift |
| 4. Discovery / Identity | 発見・識別・命名 | OASF, ADS, Agent Card, DID | ◯ |
| 5. Payment / Commerce | 決済・商取引 | x402, AP2, ACP(商取引), UCP, MPP, Trusted Agent Protocol | ◯ |
| 6. 通信プロトコル(研究系) | 適応的・分散的な対話 | Agora, AITP, Coral, LOKA, PXP | ― |
| 7. インターフェース宣言 / 基盤 | API/エージェント宣言 | agents.json, Agent Protocol (AIEF), LMOS | ― |
| 8. AGNTCY スタック | 統合スタックのコンポーネント | OASF / SLIM / AConP / ADS / Observability | ― |
ガバナンスでは MCP (Anthropic→Linux Foundation, 2025年12月寄贈)、A2A (Google Cloud, 2025年4月発表→2025年6月Linux Foundation移管)、AGNTCY (Cisco主導, Linux Foundation配下) と、いずれもLinux Foundation配下に集まる流れになっている。実装で投資する対象としても安全性が高い。
それでは順に手を動かしていく。
1. MCP ― Go SDKでツールサーバを書く
MCP(Model Context Protocol)は、エージェントがツールやデータソースを呼ぶための標準プロトコルだ。トランスポートは stdio と Streamable HTTP の2系統、メッセージは JSON-RPC 2.0 で、tools/list tools/call resources/read prompts/get などのメソッドが定義されている。
Goでは公式SDK github.com/modelcontextprotocol/go-sdk が提供されている。「指定された都市の天気を返す」だけのツールサーバを書いてみる。
// cmd/mcp-weather/main.go
package main
import (
"context"
"fmt"
"log"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
type GetWeatherInput struct {
City string `json:"city" jsonschema:"description=City name in English"`
}
type GetWeatherOutput struct {
City string `json:"city"`
TemperatureC float64 `json:"temperature_c"`
Condition string `json:"condition"`
}
func getWeather(ctx context.Context, req *mcp.CallToolRequest, in GetWeatherInput) (
*mcp.CallToolResult, GetWeatherOutput, error,
) {
// 実運用では外部APIを叩く。ここではダミー応答。
out := GetWeatherOutput{
City: in.City,
TemperatureC: 22.5,
Condition: "Sunny",
}
text := fmt.Sprintf("%s is %.1f°C and %s.", out.City, out.TemperatureC, out.Condition)
return &mcp.CallToolResult{
Content: []mcp.Content{&mcp.TextContent{Text: text}},
}, out, nil
}
func main() {
srv := mcp.NewServer(
&mcp.Implementation{Name: "weather", Version: "0.1.0"},
nil,
)
mcp.AddTool(srv, &mcp.Tool{
Name: "get_weather",
Description: "Get current weather for a city.",
}, getWeather)
// stdio トランスポート(Claude Desktop / Codex CLI などが想定)
if err := srv.Run(context.Background(), &mcp.StdioTransport{}); err != nil {
log.Fatal(err)
}
}
ポイントは AddTool がジェネリクスで型を取り、入力スキーマを jsonschema タグから自動生成する点だ。クライアント(Claude Desktop など)が tools/list を投げると、SDKがこの構造体定義からJSON Schemaを組み立てて返す。手書きでスキーマを書く必要がない。
クライアント側で同じSDKを使い、上のサーバを呼ぶコードはこうなる。
// cmd/mcp-call/main.go
package main
import (
"context"
"fmt"
"log"
"os/exec"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
func main() {
ctx := context.Background()
client := mcp.NewClient(&mcp.Implementation{Name: "demo", Version: "0.1.0"}, nil)
// 子プロセスとしてサーバを起動し、stdio で接続する
cmd := exec.Command("./mcp-weather")
session, err := client.Connect(ctx, &mcp.CommandTransport{Command: cmd}, nil)
if err != nil {
log.Fatal(err)
}
defer session.Close()
res, err := session.CallTool(ctx, &mcp.CallToolParams{
Name: "get_weather",
Arguments: map[string]any{"city": "Tokyo"},
})
if err != nil {
log.Fatal(err)
}
for _, c := range res.Content {
if t, ok := c.(*mcp.TextContent); ok {
fmt.Println(t.Text) // → "Tokyo is 22.5°C and Sunny."
}
}
}
実運用では Streamable HTTP トランスポート(mcp.NewStreamableHTTPHandler)でリモートサーバとして公開し、認証は OAuth 2.1 + PKCE に乗せるのが定石だ。仕様としても2025年3月版から認可ベースラインがOAuth 2.1に統一されている。
派生として、ブラウザ/Web側からツールを露出する WebMCP がある。位置づけはW3CのDraft Community Group Reportで、2026年6月時点ではまだ標準化前。Webサイト自身が、訪れたエージェントに対して機能をMCPツールとして提供する、というクライアントサイドのMCPだ。
2. A2A ― Agent CardをGoで配信し、タスクを委譲する
A2A(Agent2Agent)はGoogle Cloud発(2025年4月)→ Linux Foundation配下(2025年6月)のプロトコルで、エージェント間のタスク委譲を担う。MCPがツール接続なら、A2Aはエージェントへの委譲、と切り分けるのが分かりやすい。
A2Aの心臓は Agent Card ―― エージェントが自身の能力を機械可読JSONで宣言するカードだ。クライアントエージェントは /.well-known/agent-card.json を取得し、相手の能力を把握してからタスクを投げる。
Agent Card を Go で配信する
// cmd/a2a-agent/main.go
package main
import (
"encoding/json"
"log"
"net/http"
)
type Skill struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Tags []string `json:"tags"`
InputModes []string `json:"inputModes"`
OutputModes []string `json:"outputModes"`
}
type AgentCard struct {
ProtocolVersion string `json:"protocolVersion"`
Name string `json:"name"`
Description string `json:"description"`
URL string `json:"url"`
Version string `json:"version"`
Capabilities map[string]any `json:"capabilities"`
DefaultInputModes []string `json:"defaultInputModes"`
DefaultOutputModes []string `json:"defaultOutputModes"`
Skills []Skill `json:"skills"`
}
var card = AgentCard{
ProtocolVersion: "0.3.0",
Name: "translation-agent",
Description: "Translates plain text between Japanese and English.",
URL: "https://example.com/a2a",
Version: "1.0.0",
Capabilities: map[string]any{
"streaming": true,
"pushNotifications": false,
},
DefaultInputModes: []string{"text/plain"},
DefaultOutputModes: []string{"text/plain"},
Skills: []Skill{{
ID: "translate",
Name: "Translate JA<->EN",
Description: "Translate text between Japanese and English.",
Tags: []string{"translation", "nlp"},
InputModes: []string{"text/plain"},
OutputModes: []string{"text/plain"},
}},
}
func main() {
http.HandleFunc("/.well-known/agent-card.json", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(card)
})
http.HandleFunc("POST /a2a", handleTaskRPC) // 次節で実装
log.Fatal(http.ListenAndServe(":8080", nil))
}
タスク委譲を JSON-RPC で受ける
A2A の通信本体は JSON-RPC 2.0 over HTTPS で、メソッドは message/send(同期)と message/stream(SSE)が中心だ。同期版の最小実装はこうなる。
func handleTaskRPC(w http.ResponseWriter, r *http.Request) {
var req struct {
JSONRPC string `json:"jsonrpc"`
ID json.RawMessage `json:"id"`
Method string `json:"method"`
Params struct {
Message struct {
Role string `json:"role"`
Parts []struct {
Kind string `json:"kind"`
Text string `json:"text"`
} `json:"parts"`
} `json:"message"`
} `json:"params"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "bad request", http.StatusBadRequest)
return
}
if req.Method != "message/send" {
writeRPCError(w, req.ID, -32601, "method not found")
return
}
// ここで実翻訳。サンプルはエコー。
input := req.Params.Message.Parts[0].Text
output := "[ja->en] " + input
resp := map[string]any{
"jsonrpc": "2.0",
"id": req.ID,
"result": map[string]any{
"kind": "message",
"role": "agent",
"parts": []map[string]any{
{"kind": "text", "text": output},
},
},
}
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(resp)
}
func writeRPCError(w http.ResponseWriter, id json.RawMessage, code int, msg string) {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{
"jsonrpc": "2.0",
"id": id,
"error": map[string]any{"code": code, "message": msg},
})
}
呼び出し側のエージェントはまずカードを取り、/a2a に JSON-RPC を投げる。これだけで「翻訳エージェント」へのタスク委譲が成立する。
流派の整理 ― ACP / ANP / AConP / SLIM
A2A 以外の水平協調プロトコルも併せて押さえておく。
- ACP(Agent Communication Protocol):IBM BeeAI 発の、SDK不要・REST中心の軽量メッセージング。後述する決済の「ACP(Agentic Commerce Protocol)」とは別物。同名衝突に注意。
- ANP(Agent Network Protocol):HTTPS/DNS の上で P2P 直接通信する分散志向。三層構成(アイデンティティ/メタプロトコル交渉/アプリケーション)が特徴で、DID を採用する。
- AConP / SLIM:AGNTCY スタックのコンポーネント。AConP が接続確立、SLIM (Secure Low-latency Interactive Messaging) がメッセージング層を担う。
実装観点では、A2A の Agent Card + JSON-RPC が今いちばん書ける選択肢だ。
3. AG-UI ― GoでSSEを配信し、iOS/Swiftで受ける
エージェントからユーザーへ向けた進捗・中間結果のストリーミングを標準化するのが AG-UI(CopilotKit主導)だ。ライフサイクル、テキスト、ツール呼び出し、状態同期、特殊イベントの5カテゴリ・約16イベントが定義されている。2026年3月には AWS Bedrock AgentCore Runtime がAG-UI対応を追加した。
ここではGoのSSEサーバと、それを受けるiOS/Swiftクライアントを書く。
Go側のSSE配信
// cmd/agui/main.go
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"time"
)
type event struct {
Type string
Data map[string]any
}
func writeSSE(w http.ResponseWriter, ev event) error {
b, err := json.Marshal(ev.Data)
if err != nil {
return err
}
if _, err := fmt.Fprintf(w, "event: %s\ndata: %s\n\n", ev.Type, b); err != nil {
return err
}
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
return nil
}
func runHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
threadID, runID := "t_1", "r_1"
msgID := "m_1"
events := []event{
{"RUN_STARTED", map[string]any{"threadId": threadID, "runId": runID}},
{"TEXT_MESSAGE_START", map[string]any{"messageId": msgID, "role": "assistant"}},
{"TEXT_MESSAGE_CONTENT", map[string]any{"messageId": msgID, "delta": "今週の売上を集計します"}},
{"TOOL_CALL_START", map[string]any{"toolCallId": "tc_1", "toolCallName": "query_sales"}},
{"TOOL_CALL_END", map[string]any{"toolCallId": "tc_1"}},
{"STATE_DELTA", map[string]any{
"delta": []map[string]any{
{"op": "add", "path": "/sales/mon", "value": 1_200_000},
},
}},
{"TEXT_MESSAGE_END", map[string]any{"messageId": msgID}},
{"RUN_FINISHED", map[string]any{"threadId": threadID, "runId": runID}},
}
for _, ev := range events {
if err := writeSSE(w, ev); err != nil {
log.Printf("write: %v", err)
return
}
select {
case <-time.After(150 * time.Millisecond):
case <-r.Context().Done():
return
}
}
}
func main() {
http.HandleFunc("POST /agui/run", runHandler)
log.Fatal(http.ListenAndServe(":8081", nil))
}
ポイントを2つ。
http.Flusherを必ず使う。Goのnet/httpはデフォルトでレスポンスをバッファするため、Flushしないとクライアントに届かない。r.Context().Done()を監視して早期切断に対応する。SSEは長時間接続なのでクライアントが先に閉じることが多い。
iOS / Swiftクライアント(URLSessionでSSEを受ける)
ブラウザなら EventSource 一発だが、iOSにはネイティブSSEがない。URLSession のストリームを行単位で読み、自前で event: / data: をパースする。
// AGUIClient.swift
import Foundation
enum AGUIEvent {
case runStarted(threadId: String, runId: String)
case textContent(messageId: String, delta: String)
case toolCallStart(toolCallId: String, name: String)
case stateDelta(patches: [[String: Any]])
case runFinished(threadId: String, runId: String)
case other(name: String, payload: [String: Any])
}
actor AGUIClient {
let endpoint: URL
init(endpoint: URL) {
self.endpoint = endpoint
}
func run() -> AsyncThrowingStream<AGUIEvent, Error> {
AsyncThrowingStream { continuation in
Task {
do {
var req = URLRequest(url: endpoint)
req.httpMethod = "POST"
req.setValue("text/event-stream", forHTTPHeaderField: "Accept")
let (bytes, _) = try await URLSession.shared.bytes(for: req)
var eventName = ""
var dataBuffer = ""
for try await line in bytes.lines {
if line.isEmpty {
if let event = Self.parse(name: eventName, data: dataBuffer) {
continuation.yield(event)
}
eventName = ""
dataBuffer = ""
continue
}
if line.hasPrefix("event:") {
eventName = line.dropFirst("event:".count)
.trimmingCharacters(in: .whitespaces)
} else if line.hasPrefix("data:") {
dataBuffer += line.dropFirst("data:".count)
.trimmingCharacters(in: .whitespaces)
}
}
continuation.finish()
} catch {
continuation.finish(throwing: error)
}
}
}
}
private static func parse(name: String, data: String) -> AGUIEvent? {
guard !name.isEmpty, !data.isEmpty,
let raw = data.data(using: .utf8),
let json = try? JSONSerialization.jsonObject(with: raw) as? [String: Any]
else { return nil }
switch name {
case "RUN_STARTED":
return .runStarted(
threadId: json["threadId"] as? String ?? "",
runId: json["runId"] as? String ?? ""
)
case "TEXT_MESSAGE_CONTENT":
return .textContent(
messageId: json["messageId"] as? String ?? "",
delta: json["delta"] as? String ?? ""
)
case "TOOL_CALL_START":
return .toolCallStart(
toolCallId: json["toolCallId"] as? String ?? "",
name: json["toolCallName"] as? String ?? ""
)
case "STATE_DELTA":
return .stateDelta(patches: json["delta"] as? [[String: Any]] ?? [])
case "RUN_FINISHED":
return .runFinished(
threadId: json["threadId"] as? String ?? "",
runId: json["runId"] as? String ?? ""
)
default:
return .other(name: name, payload: json)
}
}
}
SwiftUI側はこのストリームをイベントごとに @Observable なViewModelへ流し込み、TEXT_MESSAGE_CONTENT を逐次追記、STATE_DELTA をJSON Patch(RFC 6902)として適用すれば、ブラウザ実装と同等のUI更新ができる。
A2UI との関係
AG-UIがイベントストリーム寄りなのに対し、A2UIはUIの宣言的記述そのものを扱う。両者は競合ではなく組み合わせるレイヤで、AG-UIのSTATE_DELTAの中身としてA2UIスニペットを流すといった統合が想定される。詳細は別記事「Generative UIとは何か ― AG-UIとA2UIで読み解くエージェント時代のUI」を参照。
4. Discovery / Identity ― Go で DID Web を解決する
エージェントの世界では、「相手は本物か」を検証する識別レイヤが必要になる。中心的な標準はW3Cの DID(Decentralized Identifiers) で、ANPはこれを採用している。
DIDのうち、Web上で運用しやすいのが did:web メソッドだ。did:web:example.com:agents:alice は https://example.com/agents/alice/did.json を取って解決する、というシンプルなルールになっている。Goでは標準ライブラリだけで書ける。
// internal/didweb/resolver.go
package didweb
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"time"
)
type DIDDocument struct {
Context []string `json:"@context"`
ID string `json:"id"`
VerificationMethod []struct {
ID string `json:"id"`
Type string `json:"type"`
Controller string `json:"controller"`
PublicKeyJwk map[string]any `json:"publicKeyJwk,omitempty"`
} `json:"verificationMethod"`
Service []struct {
ID string `json:"id"`
Type string `json:"type"`
ServiceEndpoint string `json:"serviceEndpoint"`
} `json:"service,omitempty"`
}
func Resolve(ctx context.Context, did string) (*DIDDocument, error) {
if !strings.HasPrefix(did, "did:web:") {
return nil, errors.New("not a did:web identifier")
}
parts := strings.Split(strings.TrimPrefix(did, "did:web:"), ":")
if len(parts) == 0 {
return nil, errors.New("empty did:web identifier")
}
// did:web:example.com → https://example.com/.well-known/did.json
// did:web:example.com:agents:a → https://example.com/agents/a/did.json
host, err := url.PathUnescape(parts[0])
if err != nil {
return nil, fmt.Errorf("decode host: %w", err)
}
u := &url.URL{Scheme: "https", Host: host}
if len(parts) == 1 {
u.Path = "/.well-known/did.json"
} else {
segs := make([]string, 0, len(parts)-1)
for _, p := range parts[1:] {
seg, err := url.PathUnescape(p)
if err != nil {
return nil, fmt.Errorf("decode path: %w", err)
}
segs = append(segs, seg)
}
u.Path = "/" + strings.Join(segs, "/") + "/did.json"
}
cctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
req, _ := http.NewRequestWithContext(cctx, http.MethodGet, u.String(), nil)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("status %d for %s", resp.StatusCode, u.String())
}
var doc DIDDocument
if err := json.NewDecoder(resp.Body).Decode(&doc); err != nil {
return nil, err
}
if doc.ID != did {
return nil, fmt.Errorf("did mismatch: doc=%q req=%q", doc.ID, did)
}
return &doc, nil
}
注意点:
did:web:の:はURLのパス区切り/に変換する。ただしポート番号を含むホストはlocalhost%3A8080のようにパーセントエンコードされる仕様なので、ホスト部分もPathUnescapeを通している。- 取得したドキュメントの
idが要求した DID と一致するかを必ず検証する。これは仕様の MUST 要件で、外しがちなところだ。 - HTTPSは必須。TLSの検証を切るオプションは絶対に提供しない。
エージェント側はこのドキュメント中の verificationMethod.publicKeyJwk を使い、相手が発行した JWS の署名を検証する流れになる。Agent Card と DID document を組み合わせれば「能力(card)」と「身元(did)」がプロトコルとして揃う。
発見・識別レイヤの周辺には、他に OASF(AGNTCY内では「エージェントのDNS」と呼ばれる能力スキーマ)、ADS(AGNTCYのディレクトリ)、Agent Card(A2A)、ANS / AgentDNS / NANDA といった名前解決系がある。階層で見れば「DID=身分証」「Agent Card / OASF=能力記述」「ADS / ANS等=ディレクトリ」と守備範囲が分かれる。
5. x402 ― Goで HTTP 402 ミドルウェアを書く
x402 はCoinbase主導の、HTTP 402 Payment Required を使ったステーブルコインネイティブ決済レールだ。2026年5月時点で1.65億件超のエージェント取引を処理しており、実運用トラクションでは現時点トップ。フローはシンプル。
- クライアントが保護されたエンドポイントにリクエスト
- サーバが
402 Payment Requiredと支払い要件(receiver、amount、network 等)を返す - クライアントが署名済みの payment payload を
X-PAYMENTヘッダに乗せて再送 - サーバ(または facilitator)が検証 → 決済処理 → 元のレスポンスを返す
Goで最小ミドルウェアを書くとこうなる。実際の検証・決済は facilitator に委ねる構成にしている。
// internal/x402/middleware.go
package x402
import (
"encoding/base64"
"encoding/json"
"net/http"
)
type Requirement struct {
Scheme string `json:"scheme"` // "exact"
Network string `json:"network"` // "base", "base-sepolia"
MaxAmount string `json:"maxAmountRequired"` // atomic units in string
Asset string `json:"asset"` // token contract address
PayTo string `json:"payTo"` // receiver address
Resource string `json:"resource"` // URL of the protected resource
MimeType string `json:"mimeType"`
MaxTimeout int `json:"maxTimeoutSeconds"`
}
type PaymentRequiredBody struct {
X402Version int `json:"x402Version"`
Accepts []Requirement `json:"accepts"`
Error string `json:"error,omitempty"`
}
type Facilitator interface {
Verify(payload []byte, req Requirement) error
Settle(payload []byte, req Requirement) error
}
func Middleware(req Requirement, f Facilitator, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
header := r.Header.Get("X-PAYMENT")
if header == "" {
writeRequired(w, req, "missing X-PAYMENT")
return
}
payload, err := base64.StdEncoding.DecodeString(header)
if err != nil {
writeRequired(w, req, "X-PAYMENT must be base64")
return
}
if err := f.Verify(payload, req); err != nil {
writeRequired(w, req, "verify failed: "+err.Error())
return
}
// 検証OK → 本来のレスポンスを返してから決済(または先に決済→応答)
next.ServeHTTP(w, r)
_ = f.Settle(payload, req)
})
}
func writeRequired(w http.ResponseWriter, req Requirement, msg string) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusPaymentRequired)
_ = json.NewEncoder(w).Encode(PaymentRequiredBody{
X402Version: 1,
Accepts: []Requirement{req},
Error: msg,
})
}
実装上の注意。
- 検証(Verify)と決済(Settle)を分ける。x402は「先に検証してから応答→後で決済」と「先に決済してから応答」の両モードがある。レスポンス本体が高価な計算なら前者、軽い読み出しなら後者を選ぶ。
- 同じ
X-PAYMENTを二度受け取らないよう、nonce 管理は facilitator 側の責務にすることが多い。
決済レイヤの全体観だけ整理しておく。
- 運ぶ層:x402(Coinbase)、MPP (Stripe×Tempo、2026年3月18日メインネット開始、セッション上限承認+マイクロペイメントストリーム)
- 認可する層:AP2(Google、ユーザーが「いくらまで」を署名する限定的デジタル契約)
- 商取引フロー層:ACP(Agentic Commerce Protocol)(OpenAI + Stripe、2026年2月 ChatGPT Instant Checkout で開始)、UCP(Google + Shopify、2026年1月発表)
- 認証する層:Trusted Agent Protocol(Visa、attestation)
x402 と AP2 は直交軸として組み合わさり得る ―― x402で「決済を運び」、AP2で「決済の権限を限定する」、というのが現実的なスタックだ。
6〜8. 研究系・基盤系 ― 実装の選択肢としての位置づけ
実装の主戦場は1〜5レイヤなので残りは要点だけ触れる。
研究/コミュニティ系の通信プロトコル:
- Agora:固定スキーマでなく、エージェント同士が通信規約そのものを交渉・進化させる適応的アプローチ。ANPの「メタプロトコル交渉」と思想的に近い。
- AITP:NEAR発。経済的トランザクション軸。
- Coral Protocol:分散的な相互運用性、共有オントロジー、セマンティックグラウンディング。
- LOKA:アイデンティティ・信頼に加え倫理をプロトコル層に埋め込む。
- PXP:タスク指向対話における相互理解可能性にフォーカスした研究プロトタイプ。
インターフェース宣言/プラットフォーム:
- agents.json:
robots.txt的にWebサイトがAI連携機能を宣言する機械可読フォーマット。 - Agent Protocol(AI Engineer Foundation):OpenAPI v3上にエージェントのライフサイクル操作(起動・停止・監視)を定義。コントロールコンソール↔エージェントの標準。
- LMOS(Language Model Operating System):Eclipseのオープンソースプロジェクト。マルチエージェントシステムのフルスタック基盤。
AGNTCY スタック:Cisco主導・Linux Foundation配下で、OASF / SLIM / AConP / ADS / Observability といった複数レイヤのコンポーネントをまとめて提供する。AGNTCY を「単一プロトコル」だと誤解すると話が噛み合わない ―― 実態は複数レイヤのオープンスタックだ。
採用指針 ― 「何を書くか」
最後に、Goでエージェント基盤を作るときの現実的な指針を整理する。
| やりたいこと | 採用すべきプロトコル | Goでの主な道具 |
|---|---|---|
| ツールサーバを作る | MCP | modelcontextprotocol/go-sdk |
| 別エージェントへ仕事を委譲 | A2A | net/http + JSON-RPC + Agent Card |
| 進捗をUIに出す | AG-UI (+ Swift クライアント) | net/http SSE + http.Flusher |
| エージェントの身元保証 | DID(W3C)+ Agent Card | net/http で did:web 解決 |
| 自律決済を許す | x402 (+ AP2) | HTTP 402 ミドルウェア + facilitator |
ポイントは、1つのGoサービスがMCPサーバ・A2Aエンドポイント・AG-UI配信・x402ゲートを兼ねられることだ。各プロトコルは独立した別ポート/別パスで共存できるよう設計されている。Linux Foundation 配下に集まった主要3本(MCP / A2A / AGNTCY)の中立性も担保された今、Goで足場を組む投資対効果は十分高い。
あとは 「同じACP」が2種類あるような小さな罠を避けながら、レイヤごとに実装を積み上げていけばいい。
参照(一次ソース/公式サイト)
- Model Context Protocol / Go SDK
- A2A Protocol / Agent Card 仕様
- AG-UI Documentation
- x402
- AP2 Protocol
- AGNTCY
- W3C DID Core / did:web Method
本稿のサンプルコードは2026年6月30日時点で確認できる仕様をもとにしています。SDKやプロトコル仕様は急速に更新されているため、実装にあたっては各公式ドキュメントの最新版を参照してください。