スキル一覧に戻る

implement-a2a-server

pjt222
更新日 6 days ago
15 閲覧
17
2
17
GitHubで表示
メタaiautomationdesign

について

このスキルは、A2Aプロトコル用のJSON-RPC 2.0サーバーを実装し、完全なタスクライフサイクル管理とSSEストリーミングを提供します。マルチエージェントワークフローで相互運用する必要があるエージェントを構築する場合や、既存サービスにA2Aサポートを追加する際にご利用ください。エージェントカードのバックエンド作成や、他のA2Aエージェントとの互換性確保に最適です。

クイックインストール

Claude Code

推奨
メイン
npx skills add pjt222/agent-almanac -a claude-code
プラグインコマンド代替
/plugin add https://github.com/pjt222/agent-almanac
Git クローン代替
git clone https://github.com/pjt222/agent-almanac.git ~/.claude/skills/implement-a2a-server

このコマンドをClaude Codeにコピー&ペーストしてスキルをインストールします

ドキュメント

建 A2A 服務器

建全合之 A2A 服務器,處 JSON-RPC 2.0 請,管任務生命週期態,支 SSE 流即時更新,並供 Agent Card 以發現。

  • 建參與多代理 A2A 工作流之代理
  • 建由 design-a2a-agent-card 所設 Agent Card 之後端
  • 加 A2A 協議支援於既有代理或服務
  • 造參考 A2A 服務器施行供試
  • 部需與他 A2A 合之代理互操作者

  • :Agent Card(JSON),定代理技能與能
  • :施行語言(TypeScript/Node.js 或 Python)
  • :Agent Card 所定各技能之任務行邏輯
  • :推送通知鉤支援(truefalse
  • :持久任務存(內存、Redis、PostgreSQL)
  • :合 Agent Card 認證方案之認證中介
  • :最大並發任務限

一:立項目並備 JSON-RPC 2.0 處理器

1.1. 以 HTTP 服務器與 JSON-RPC 解初化:

TypeScript:

mkdir -p $PROJECT_NAME && cd $PROJECT_NAME
npm init -y
npm install express uuid
npm install -D typescript @types/node @types/express tsx

Python:

mkdir -p $PROJECT_NAME && cd $PROJECT_NAME
python -m venv .venv && source .venv/bin/activate
pip install fastapi uvicorn uuid6

1.2. 造 JSON-RPC 2.0 請求處理器:

interface JsonRpcRequest {
  jsonrpc: "2.0";
  id: string | number;
  method: string;
  params?: Record<string, unknown>;
}

interface JsonRpcResponse {
  jsonrpc: "2.0";
  id: string | number;
  result?: unknown;
  error?: { code: number; message: string; data?: unknown };
}

function handleJsonRpc(request: JsonRpcRequest): JsonRpcResponse {
  switch (request.method) {
    case "tasks/send":
      return handleTaskSend(request);
    case "tasks/get":
      return handleTaskGet(request);
    case "tasks/cancel":
      return handleTaskCancel(request);
    case "tasks/sendSubscribe":
      // Handled separately via SSE
      throw new Error("Use SSE endpoint for sendSubscribe");
    default:
      return {
        jsonrpc: "2.0",
        id: request.id,
        error: { code: -32601, message: `Method not found: ${request.method}` },
      };
  }
}

1.3. 掛 JSON-RPC 處理器於 POST 端點(常 /):

app.post("/", (req, res) => {
  const response = handleJsonRpc(req.body);
  res.json(response);
});

1.4. 於 /.well-known/agent.json 供 Agent Card:

app.get("/.well-known/agent.json", (req, res) => {
  res.json(agentCard);
});

得:接 JSON-RPC 2.0 請求並供 Agent Card 之 HTTP 服務器。

敗:JSON-RPC 解敗→驗請求體有 jsonrpcmethodid。畸 JSON 返 -32700(解誤),缺必欄返 -32600(無效請求)。

二:實任務態機

2.1. 定任務模型含諸 A2A 生命週期態:

type TaskState =
  | "submitted"
  | "working"
  | "input-required"
  | "completed"
  | "failed"
  | "canceled";

interface Task {
  id: string;
  sessionId: string;
  status: {
    state: TaskState;
    message?: Message;
    timestamp: string;
  };
  history?: TaskStatus[];
  artifacts?: Artifact[];
  metadata?: Record<string, unknown>;
}

interface Message {
  role: "user" | "agent";
  parts: Part[];
}

type Part =
  | { type: "text"; text: string }
  | { type: "file"; file: { name: string; mimeType: string; bytes?: string; uri?: string } }
  | { type: "data"; data: Record<string, unknown> };

2.2. 施態轉則:

submitted  -> working | failed | canceled
working    -> completed | failed | canceled | input-required
input-required -> working | failed | canceled
completed  -> (terminal)
failed     -> (terminal)
canceled   -> (terminal)

2.3. 造任務存含 CRUD 操作:

class TaskStore {
  private tasks: Map<string, Task> = new Map();

  create(sessionId: string, message: Message): Task { ... }
  get(taskId: string): Task | undefined { ... }
  updateStatus(taskId: string, state: TaskState, message?: Message): Task { ... }
  addArtifact(taskId: string, artifact: Artifact): void { ... }
  cancel(taskId: string): Task { ... }
}

2.4. Agent Card 啟 stateTransitionHistory 則各態變附於任務 history 含時戳。

得:強有效態轉並維史之任務存。

敗:試無效態轉(如 completedworking)→返 JSON-RPC 誤碼 -32002 並明訊。絕勿默忽無效轉。

三:加 tasks/send 與 tasks/get 法

3.1. 施 tasks/send——提交任務之主法:

function handleTaskSend(request: JsonRpcRequest): JsonRpcResponse {
  const { id: taskId, sessionId, message } = request.params as TaskSendParams;

  // Create or resume task
  let task = taskStore.get(taskId);
  if (!task) {
    task = taskStore.create(sessionId, message);
  } else if (task.status.state === "input-required") {
    taskStore.updateStatus(task.id, "working");
  }

  // Route to skill handler based on message content
  const skill = matchSkill(message);
  if (!skill) {
    taskStore.updateStatus(task.id, "failed", {
      role: "agent",
      parts: [{ type: "text", text: "No matching skill for this request." }],
    });
    return { jsonrpc: "2.0", id: request.id, result: taskStore.get(task.id) };
  }

  // Execute skill (async — task will transition to working, then completed/failed)
  executeSkill(skill, task, message).catch((error) => {
    taskStore.updateStatus(task.id, "failed", {
      role: "agent",
      parts: [{ type: "text", text: error.message }],
    });
  });

  return { jsonrpc: "2.0", id: request.id, result: taskStore.get(task.id) };
}

3.2. 施 tasks/get——取任務態與物件:

function handleTaskGet(request: JsonRpcRequest): JsonRpcResponse {
  const { id: taskId, historyLength } = request.params as TaskGetParams;
  const task = taskStore.get(taskId);

  if (!task) {
    return {
      jsonrpc: "2.0",
      id: request.id,
      error: { code: -32001, message: `Task not found: ${taskId}` },
    };
  }

  // Optionally trim history to requested length
  const result = historyLength !== undefined
    ? { ...task, history: task.history?.slice(-historyLength) }
    : task;

  return { jsonrpc: "2.0", id: request.id, result };
}

3.3. 施 tasks/cancel

function handleTaskCancel(request: JsonRpcRequest): JsonRpcResponse {
  const { id: taskId } = request.params as TaskCancelParams;
  try {
    const task = taskStore.cancel(taskId);
    return { jsonrpc: "2.0", id: request.id, result: task };
  } catch (error) {
    return {
      jsonrpc: "2.0",
      id: request.id,
      error: { code: -32002, message: (error as Error).message },
    };
  }
}

得:正管任務生命週期之 tasks/sendtasks/gettasks/cancel 諸法。

敗:技能匹配敗→返 failed 態任務含明訊。任務存滿→返 -32003(資源耗)。

四:實 SSE 流供 tasks/sendSubscribe

4.1. 造 SSE 端點流任務更新:

app.post("/subscribe", (req, res) => {
  const request = req.body as JsonRpcRequest;
  if (request.method !== "tasks/sendSubscribe") {
    res.status(400).json({ error: "Only tasks/sendSubscribe supported" });
    return;
  }

  // Set SSE headers
  res.setHeader("Content-Type", "text/event-stream");
  res.setHeader("Cache-Control", "no-cache");
  res.setHeader("Connection", "keep-alive");

  const { id: taskId, sessionId, message } = request.params as TaskSendParams;
  let task = taskStore.get(taskId) ?? taskStore.create(sessionId, message);

  // Send initial status
  sendSSEEvent(res, "status", {
    id: request.id,
    result: { id: task.id, status: task.status },
  });

  // Subscribe to task updates
  const unsubscribe = taskStore.onUpdate(task.id, (updatedTask) => {
    if (updatedTask.status.state === "working") {
      sendSSEEvent(res, "status", {
        id: request.id,
        result: { id: updatedTask.id, status: updatedTask.status },
      });
    }

    if (updatedTask.artifacts?.length) {
      sendSSEEvent(res, "artifact", {
        id: request.id,
        result: { id: updatedTask.id, artifact: updatedTask.artifacts.at(-1) },
      });
    }

    // Close stream on terminal states
    if (["completed", "failed", "canceled"].includes(updatedTask.status.state)) {
      sendSSEEvent(res, "status", {
        id: request.id,
        result: { id: updatedTask.id, status: updatedTask.status, final: true },
      });
      unsubscribe();
      res.end();
    }
  });

  // Handle client disconnect
  req.on("close", () => {
    unsubscribe();
  });
});

function sendSSEEvent(res: Response, event: string, data: unknown): void {
  res.write(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`);
}

4.2. 加事件發或 pub/sub 機制於任務存:

class TaskStore {
  private listeners: Map<string, Set<(task: Task) => void>> = new Map();

  onUpdate(taskId: string, callback: (task: Task) => void): () => void {
    if (!this.listeners.has(taskId)) {
      this.listeners.set(taskId, new Set());
    }
    this.listeners.get(taskId)!.add(callback);
    return () => this.listeners.get(taskId)?.delete(callback);
  }

  private notifyListeners(taskId: string): void {
    const task = this.get(taskId);
    if (task) {
      this.listeners.get(taskId)?.forEach((cb) => cb(task));
    }
  }
}

4.3. 於諸任務態轉與物件加發事件。

得:隨任務進流即時態與物件事件之 SSE 流。

敗:SSE 連斷→客應可重連並用 tasks/get 取當前態。確任務存不倚活 SSE 連。

五:加推送通知鉤支援

5.1. Agent Card 啟 pushNotifications→經 tasks/pushNotification/set 施鉤註:

  • PushNotificationConfigurl(必 HTTPS)、選 tokenevents 列(["status", "artifact"]
  • 驗鉤 URL 用 HTTPS;否則返誤碼 -32004
  • 存配於任務存,以任務 ID 為鍵

5.2. 於任務態變發鉤回調:

  • 每態轉或加物件時查有註之推送配
  • POST JSON 載體(taskIdeventTypestatustimestamp)至鉤 URL
  • 若有予 token 則含 Authorization: Bearer <token>

5.3. 為鉤敗實重試邏輯(指數退避,最多 3 試)。

5.4. 加 tasks/pushNotification/get 供取任務當前推送配。

得:鉤註與遞送含重試邏輯。

敗:推送通知敗絕不得影響任務行。記誤續之。鉤 URL 持不可達→耗試後除訂閱。

六:集 Agent Card 以發現

6.1. 啟時載並供 Agent Card:

  • agent-card.json 並驗能符施行
  • 啟時若卡聲 streaming: true 而 SSE 未啟→擲
  • 啟時若卡聲 pushNotifications: true 而鉤未啟→擲

6.2. 為跨源 Agent Card 發現加 CORS 頭:

  • /.well-known/agent.jsonAccess-Control-Allow-Origin: *
  • GETOPTIONS

6.3. 加合 Agent Card 方案之認證中介:

  • /.well-known/agent.json 略認證(Agent Card 恆公)
  • 他諸端點→驗 Authorization 頭或 API 鍵
  • 未授返 HTTP 401 含 JSON-RPC 誤碼 -32000

6.4. 啟服務器並端對端驗:

# Start server
npm run dev

# Fetch Agent Card
curl -s http://localhost:3000/.well-known/agent.json | python3 -m json.tool

# Send a task
curl -X POST http://localhost:3000/ \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tasks/send","params":{"id":"task-1","sessionId":"session-1","message":{"role":"user","parts":[{"type":"text","text":"Analyze my dataset"}]}}}'

得:運 A2A 服務器,供其 Agent Card,接任務,管全生命週期。

敗:Agent Card 能不符施行→6.1 之啟驗將捕失配。修施行或更卡以符。

  • 服務器啟並於 /.well-known/agent.json 供 Agent Card
  • tasks/send 造任務並轉之經生命週期
  • tasks/get 取任務態與物件
  • tasks/cancel 將任務移至 canceled 態
  • SSE 流發即時態與物件事件(若啟)
  • 推送通知於態變遞鉤(若啟)
  • 無效態轉返合適 JSON-RPC 誤
  • 認證拒未授請求(若配)
  • Agent Card 能精反映服務器施行
  • 諸 JSON-RPC 返含 jsonrpc: "2.0" 與正確 id

  • 漏 JSON-RPC 誤碼:A2A 協議定特定誤碼。用 -32700(解誤)、-32600(無效請求)、-32601(法未找)及自定碼供域誤
  • 任務 ID 撞:任務 ID 用 UUID。若客予 ID→造任務前驗唯一
  • SSE 連漏:客斷時必清 SSE 訂。用 req.on("close") 偵斷
  • 阻塞技能行:長行技能必異步行。立返 submittedworking 態任務,後經事件更
  • Agent Card 漂移:服務器施行變而卡未更→客有誤期。啟時驗
  • 忽終態:任務至 completedfailedcanceled 後,無他態轉可。於態機中防此

  • design-a2a-agent-card
  • test-a2a-interop
  • build-custom-mcp-server
  • scaffold-mcp-server
  • configure-ingress-networking

GitHub リポジトリ

pjt222/agent-almanac
パス: i18n/wenyan-ultra/skills/implement-a2a-server
0
agentsagentskillsai-assisted-developmentclaude-codeskillsteams

関連スキル

content-collections

メタ

このスキルは、Content Collections(Markdown/MDXファイルを型安全なデータコレクションに変換するTypeScriptファーストのツール)の本番環境でテストされた設定を提供します。Zodバリデーションによる型安全性を実現し、ブログ、ドキュメントサイト、コンテンツ重視のVite + Reactアプリケーション構築時にご利用ください。Viteプラグインの設定、MDXコンパイルから、デプロイ最適化、スキーマバリデーションまで、すべてを網羅しています。

スキルを見る

polymarket

メタ

このスキルは、開発者がPolymarket予測市場プラットフォームを活用したアプリケーション構築を可能にします。API統合による取引や市場データの取得に加え、WebSocketを介したリアルタイムデータストリーミングにより、ライブ取引や市場活動を監視できます。取引戦略の実装や、ライブ市場更新を処理するツールの作成にご利用ください。

スキルを見る

creating-opencode-plugins

メタ

このスキルは、開発者がコマンド、ファイル、LSP操作など25種類以上のイベントタイプにフックするOpenCodeプラグインを作成することを支援します。JavaScript/TypeScriptモジュール向けに、プラグイン構造、イベントAPI仕様、および実装パターンを提供します。カスタムイベント駆動ロジックでOpenCode AIアシスタントのライフサイクルをインターセプト、監視、または拡張する必要がある場合にご利用ください。

スキルを見る

sglang

メタ

SGLangは、高性能なLLMサービングフレームワークであり、RadixAttentionプレフィックスキャッシュを活用したJSON、正規表現、エージェントワークフロー向けの高速で構造化された生成を特長とします。特にプレフィックスが繰り返されるタスクにおいて、大幅に高速な推論を実現し、複雑な構造化出力やマルチターン対話に最適です。制約付きデコードが必要な場合や、広範なプレフィックス共有を伴うアプリケーションを構築する場合は、vLLMなどの代替案ではなくSGLangを選択してください。

スキルを見る