MCP HubMCP Hub
Retour aux compétences

implement-a2a-server

pjt222
Mis à jour Yesterday
2 vues
17
2
17
Voir sur GitHub
Métaaiautomationdesign

À propos

Cette compétence implémente un serveur A2A JSON-RPC 2.0 avec une gestion complète du cycle de vie des tâches (soumise/en cours/terminée/échouée/annulée/entrée requise). Elle fournit un streaming SSE et des notifications push pour des mises à jour en temps réel, permettant aux agents de participer à des workflows multi-agents. Utilisez-la pour ajouter la prise en charge du protocole A2A à un agent existant ou pour construire un backend pour une Carte Agent qui doit interagir avec d'autres agents compatibles A2A.

Installation rapide

Claude Code

Recommandé
Principal
npx skills add pjt222/agent-almanac -a claude-code
Commande PluginAlternatif
/plugin add https://github.com/pjt222/agent-almanac
Git CloneAlternatif
git clone https://github.com/pjt222/agent-almanac.git ~/.claude/skills/implement-a2a-server

Copiez et collez cette commande dans Claude Code pour installer cette compétence

Documentation

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 所定每技能之任務執行邏輯
  • 可選:推送通知 webhook 支(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. 供 Agent Card 於 /.well-known/agent.json

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

得: HTTP 伺服器受 JSON-RPC 2.0 請求並供 Agent Card。

敗則: 若 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),返 -32002 JSON-RPC 誤附述。勿默忽無效轉換。

第三步:加 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(資源耗盡)。

第四步:為 tasks/sendSubscribe 實 SSE 流

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. 加事件發射或發布訂閱機制於任務存:

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 連。

第五步:加推送通知 webhook 支

5.1. 若 Agent Card 啟 pushNotifications,以 tasks/pushNotification/set 實 webhook 註冊:

  • PushNotificationConfigurl(須 HTTPS)、可選 tokenevents 陣(["status", "artifact"]
  • 驗 webhook URL 用 HTTPS;否返誤碼 -32004
  • 存配置於任務存,以任務 ID 索

5.2. 於任務狀態變送 webhook 回調:

  • 每狀態轉換或成品加察註冊推送配置
  • POST JSON 載荷含 taskIdeventTypestatustimestamp 至 webhook URL
  • 若有 token 則含 Authorization: Bearer <token>

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

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

得: webhook 註冊與交付附重試邏輯。

敗則: 推送通知敗勿影響任務執行。記誤續行。若 webhook URL 持不可達,最多重試後移訂閱。

第六步:與 Agent Card 整合為探索

6.1. 啟動時載並供 Agent Card:

  • 解析 agent-card.json 並驗能力合實現
  • 若卡稱 streaming: true 而 SSE 未啟,啟動拋
  • 若卡稱 pushNotifications: true 而 webhook 未啟,啟動拋

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 啟動驗將捕差。修實現或更新 Agent Card 以合。

  • 伺服器啟並於 /.well-known/agent.json 供 Agent Card
  • tasks/send 創任務並轉生命
  • tasks/get 取任務狀態與成品
  • tasks/cancel 將任務移至取消態
  • SSE 流送實時狀態與成品事件(若啟)
  • 推送通知於狀態變送 webhook(若啟)
  • 無效狀態轉換返合 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 偏移:若伺服器實現變而 Agent Card 未更,客將有誤期。啟動驗
  • 忽終態:任務達 completedfailed、或 canceled 後,勿許再轉。狀態機守此

  • design-a2a-agent-card — 設此伺服器所實之 Agent Card
  • test-a2a-interop — 對 A2A 合規測驗伺服器
  • build-custom-mcp-server — 告 A2A 實之 MCP 伺服器模
  • scaffold-mcp-server — 適於 A2A 伺服器設之搭模
  • configure-ingress-networking — 附 TLS 與路由之生產部署

Dépôt GitHub

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

Compétences associées

content-collections

Méta

Cette compétence propose une configuration éprouvée en production pour Content Collections, un outil axé sur TypeScript qui transforme des fichiers Markdown/MDX en collections de données typées de manière sûre avec une validation Zod. Utilisez-la lors de la création de blogs, de sites de documentation ou d'applications Vite + React riches en contenu pour garantir la sécurité de typage et la validation automatique du contenu. Elle couvre tout, de la configuration du plugin Vite et de la compilation MDX à l'optimisation des déploiements et la validation des schémas.

Voir la compétence

polymarket

Méta

Cette compétence permet aux développeurs de créer des applications avec la plateforme de marchés prédictifs Polymarket, incluant l'intégration d'API pour le trading et les données de marché. Elle fournit également une diffusion de données en temps réel via WebSocket pour surveiller les transactions en direct et l'activité du marché. Utilisez-la pour mettre en œuvre des stratégies de trading ou pour créer des outils traitant les mises à jour de marché en direct.

Voir la compétence

creating-opencode-plugins

Méta

Cette compétence aide les développeurs à créer des plugins OpenCode qui s'interconnectent avec plus de 25 types d'événements tels que les commandes, les fichiers et les opérations LSP. Elle fournit la structure du plugin, les spécifications de l'API événementielle et les modèles d'implémentation pour les modules JavaScript/TypeScript. Utilisez-la lorsque vous avez besoin d'intercepter, de surveiller ou d'étendre le cycle de vie de l'assistant IA OpenCode avec une logique personnalisée pilotée par les événements.

Voir la compétence

sglang

Méta

SGLang est un framework de service LLM haute performance spécialisé dans la génération rapide et structurée pour les workflows JSON, regex et agentiques grâce à son cache de préfixe RadixAttention. Il offre une inférence nettement plus rapide, particulièrement pour les tâches avec des préfixes répétés, ce qui le rend idéal pour les sorties complexes et structurées ainsi que les conversations multi-tours. Choisissez SGLang plutôt que des alternatives comme vLLM lorsque vous avez besoin d'un décodage contraint ou que vous construisez des applications avec un partage étendu de préfixes.

Voir la compétence