MCP HubMCP Hub
스킬 목록으로 돌아가기

test-a2a-interop

pjt222
업데이트됨 2 days ago
4 조회
17
2
17
GitHub에서 보기
테스팅aitestingautomation

정보

이 스킬은 에이전트 카드 발견, 작업 생명주기 상태, 스트리밍을 포함한 프로토콜 준수를 검증하여 A2A(에이전트 간) 상호 운용성을 테스트합니다. 새로운 A2A 서버 구현을 확인하고, CI/CD에서 적합성 테스트를 실행하며, 배포 전 다중 에이전트 워크플로우를 디버깅하는 데 사용하세요. 이는 개발자가 에이전트가 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/test-a2a-interop

Claude Code에서 이 명령을 복사하여 붙여넣어 스킬을 설치하세요

문서

Test A2A Interop

Validate A2A agent conforms to spec: Agent Card discovery, task lifecycle, SSE streaming, err handling, multi-agent comm patterns.

Use When

  • Verify new A2A server impl before deploy
  • Validate interop between ≥2 A2A agents
  • Conformance tests in CI/CD for A2A services
  • Debug fails in multi-agent A2A workflows
  • Certify agent meets A2A protocol for registry

In

  • Required: Base URL of agent under test
  • Required: Auth creds (if needed)
  • Optional: 2nd agent URL for bidirectional interop
  • Optional: Specific skills to test (default: all in Card)
  • Optional: Test timeout per task (default 60s)
  • Optional: Output format (json, markdown, junit)

Do

Step 1: Fetch + Validate Agent Cards

1.1. Retrieve Card from well-known endpoint:

curl -s https://agent.example.com/.well-known/agent.json -o agent-card.json

1.2. Validate top-level required:

const requiredFields = ["name", "description", "url", "skills"];
for (const field of requiredFields) {
  assert(agentCard[field] !== undefined, `Missing required field: ${field}`);
}

1.3. Validate each skill entry:

for (const skill of agentCard.skills) {
  assert(skill.id, "Skill missing id");
  assert(skill.name, "Skill missing name");
  assert(skill.description, "Skill missing description");
  assert(
    Array.isArray(skill.inputModes) && skill.inputModes.length > 0,
    `Skill ${skill.id} missing inputModes`
  );
  assert(
    Array.isArray(skill.outputModes) && skill.outputModes.length > 0,
    `Skill ${skill.id} missing outputModes`
  );
}

1.4. Validate auth config:

  • authentication.schemes includes oauth2 → verify credentials.oauth2 has tokenUrl
  • includes apiKey → verify credentials.apiKey has headerName

1.5. Validate capability flags = boolean.

1.6. Record validation results in conformance report:

interface ConformanceResult {
  test: string;
  category: "agent-card" | "lifecycle" | "streaming" | "error-handling" | "interop";
  status: "pass" | "fail" | "skip";
  message?: string;
  duration_ms?: number;
}

Got: Card passes all structural validation.

If err: Record each fail w/ specific field + reason. Don't abort; continue. Invalid Card = test result.

Step 2: Test Tasks Covering Lifecycle States

2.1. Submission (submitted → working → completed)

Send task agent should handle per declared skills:

const submitResult = await sendJsonRpc(agentUrl, {
  jsonrpc: "2.0",
  id: 1,
  method: "tasks/send",
  params: {
    id: `test-${uuid()}`,
    sessionId: `session-${uuid()}`,
    message: {
      role: "user",
      parts: [{ type: "text", text: skillExamples[0] }],
    },
  },
});

assert(submitResult.result, "tasks/send should return a result");
assert(submitResult.result.id, "Result should include task ID");
assert(
  ["submitted", "working", "completed"].includes(submitResult.result.status.state),
  `Unexpected initial state: ${submitResult.result.status.state}`
);

2.2. Polling (tasks/get)

Poll until terminal:

let task = submitResult.result;
const startTime = Date.now();
while (!["completed", "failed", "canceled"].includes(task.status.state)) {
  if (Date.now() - startTime > TEST_TIMEOUT_MS) {
    fail(`Task ${task.id} did not complete within ${TEST_TIMEOUT_MS}ms`);
    break;
  }
  await sleep(1000);
  const getResult = await sendJsonRpc(agentUrl, {
    jsonrpc: "2.0",
    id: 2,
    method: "tasks/get",
    params: { id: task.id },
  });
  task = getResult.result;
}

assert(task.status.state === "completed", `Task should complete, got: ${task.status.state}`);

2.3. Cancellation

Submit + immediately cancel:

const cancelTask = await sendJsonRpc(agentUrl, {
  jsonrpc: "2.0",
  id: 3,
  method: "tasks/send",
  params: { id: `test-cancel-${uuid()}`, sessionId: `session-${uuid()}`, message: { ... } },
});

const cancelResult = await sendJsonRpc(agentUrl, {
  jsonrpc: "2.0",
  id: 4,
  method: "tasks/cancel",
  params: { id: cancelTask.result.id },
});

assert(
  cancelResult.result.status.state === "canceled",
  "Canceled task should be in canceled state"
);

2.4. Input-required (multi-turn)

Skill supports multi-turn → ambiguous req → triggers input-required, then follow-up:

// Send ambiguous request
const multiTurnTask = await sendJsonRpc(agentUrl, { ... });

// Poll until input-required or completed
// If input-required, send follow-up
if (task.status.state === "input-required") {
  const followUp = await sendJsonRpc(agentUrl, {
    jsonrpc: "2.0",
    id: 6,
    method: "tasks/send",
    params: {
      id: task.id,
      sessionId: task.sessionId,
      message: { role: "user", parts: [{ type: "text", text: "Column A and Column B" }] },
    },
  });
  assert(
    ["working", "completed"].includes(followUp.result.status.state),
    "Follow-up should resume task"
  );
}

2.5. State transition history

Card declares stateTransitionHistory: true:

const getWithHistory = await sendJsonRpc(agentUrl, {
  jsonrpc: "2.0",
  id: 7,
  method: "tasks/get",
  params: { id: completedTaskId, historyLength: 100 },
});

assert(
  Array.isArray(getWithHistory.result.history),
  "Task should include history array"
);
assert(
  getWithHistory.result.history.length >= 2,
  "History should have at least 2 entries (submitted and completed)"
);

Got: All lifecycle transitions work. Tasks complete, cancel cleanly, multi-turn fns when supported.

If err: Record specific transition fail, expected vs actual. Include full JSON-RPC res in report.

Step 3: Validate SSE Streaming

3.1. Skip if streaming: false.

3.2. Send tasks/sendSubscribe + validate SSE stream:

const response = await fetch(`${agentUrl}/subscribe`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    jsonrpc: "2.0",
    id: 10,
    method: "tasks/sendSubscribe",
    params: {
      id: `test-stream-${uuid()}`,
      sessionId: `session-${uuid()}`,
      message: { role: "user", parts: [{ type: "text", text: "Stream test task" }] },
    },
  }),
});

assert(
  response.headers.get("content-type")?.includes("text/event-stream"),
  "Response must be text/event-stream"
);

3.3. Parse SSE events + validate structure:

const events: SSEEvent[] = [];
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";

while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  buffer += decoder.decode(value, { stream: true });

  // Parse SSE events from buffer
  const lines = buffer.split("\n");
  for (const line of lines) {
    if (line.startsWith("event: ")) {
      currentEvent.type = line.slice(7);
    } else if (line.startsWith("data: ")) {
      currentEvent.data = JSON.parse(line.slice(6));
      events.push(currentEvent);
    }
  }
}

3.4. Validate event sequence:

  • 1st = status event w/ submitted | working
  • Intermediate may include status updates + artifact deliveries
  • Final has final: true + terminal state
  • No events after final

3.5. Validate cleanup:

  • Close connection mid-stream
  • Verify task retrievable via tasks/get
  • Verify no server errs from premature disconnect

Got: SSE delivers correctly formatted events in right sequence, ending w/ final terminal event.

If err: SSE advertised but endpoint returns non-SSE → conformance fail. Events out of order → record sequence. Stream never terminates → record timeout.

Step 4: Test Err Handling + Edge Cases

4.1. Unknown method

const unknownMethod = await sendJsonRpc(agentUrl, {
  jsonrpc: "2.0",
  id: 20,
  method: "tasks/nonexistent",
  params: {},
});
assert(unknownMethod.error?.code === -32601, "Should return method not found");

4.2. Malformed JSON-RPC

const malformed = await fetch(agentUrl, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: '{"not": "valid jsonrpc"}',
});
const response = await malformed.json();
assert(response.error?.code === -32600, "Should return invalid request");

4.3. Get nonexistent task

const notFound = await sendJsonRpc(agentUrl, {
  jsonrpc: "2.0",
  id: 22,
  method: "tasks/get",
  params: { id: "nonexistent-task-id" },
});
assert(notFound.error, "Should return error for nonexistent task");

4.4. Cancel completed task

const cancelCompleted = await sendJsonRpc(agentUrl, {
  jsonrpc: "2.0",
  id: 23,
  method: "tasks/cancel",
  params: { id: completedTaskId },
});
assert(cancelCompleted.error, "Should error when canceling completed task");

4.5. Auth enforcement

Auth configured → req w/o creds:

const unauthResponse = await fetch(agentUrl, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ jsonrpc: "2.0", id: 24, method: "tasks/get", params: { id: "x" } }),
});
assert(unauthResponse.status === 401, "Should reject unauthenticated requests");

4.6. Card publicly accessible w/o auth

const publicCard = await fetch(`${agentUrl}/.well-known/agent.json`);
assert(publicCard.status === 200, "Agent Card should be publicly accessible");

Got: All err conditions return appropriate JSON-RPC err codes w/o crashing.

If err: Record each err handling test fail. Server crashes during err testing = critical, must fix before deploy.

Step 5: Generate Conformance Report

5.1. Aggregate test results → structured report:

interface ConformanceReport {
  agentUrl: string;
  agentName: string;
  agentVersion: string;
  testDate: string;
  summary: {
    total: number;
    passed: number;
    failed: number;
    skipped: number;
  };
  categories: {
    agentCard: ConformanceResult[];
    lifecycle: ConformanceResult[];
    streaming: ConformanceResult[];
    errorHandling: ConformanceResult[];
    interop: ConformanceResult[];
  };
  conformanceLevel: "full" | "partial" | "minimal" | "non-conformant";
}

5.2. Calculate conformance level:

  • full: All pass, including streaming + push notifications
  • partial: Core lifecycle pass, some optional fail
  • minimal: Card valid + basic send/get works
  • non-conformant: Card invalid | basic lifecycle broken

5.3. Generate report in requested format:

  • json: Machine-readable for CI/CD
  • markdown: Human-readable w/ pass/fail tables
  • junit: XML for test framework integration

5.4. Include recommendations:

## Failed Tests

| Test | Category | Message | Recommendation |
|------|----------|---------|----------------|
| cancel-completed-task | error-handling | Server returned 500 | Add guard for terminal state transitions |
| sse-final-event | streaming | No final event received | Ensure SSE sends event with final:true |

5.5. Bidirectional testing requested (2 agents):

  • A can discover B's Card
  • A can send task to B
  • B can send task to A
  • Both handle concurrent tasks w/o interference

Got: Complete conformance report w/ pass/fail, level, actionable recommendations.

If err: Report gen fails → output raw test results to stdout fallback. Test data never lost due to reporting err.

Check

  • Card fetched + structurally validated
  • ≥1 task completes full lifecycle (submitted → working → completed)
  • Cancellation works
  • Err responses use correct JSON-RPC codes
  • SSE tested if advertised
  • Auth enforced on task endpoints, NOT on Card
  • Conformance report generated in requested format
  • Fails include actionable remediation
  • Suite runnable in CI/CD w/o manual

Traps

  • Cold server: Some agents take init time. Add health check | warmup before tests.
  • Hardcoded test data: Use dynamic task + session IDs (UUIDs). Never assume specific task ID avail.
  • Ignore timing: Transitions async. Always poll w/ backoff vs immediate state assertion.
  • SSE parsing complexity: Events may span multiple chunks. Buffer incoming + parse complete events.
  • Only happy path: Err handling tests as important as success. Malformed reqs, invalid transitions, auth fails all covered.
  • Network dependency: Runnable vs localhost (dev) + remote (prod). Parameterize URL.
  • Assume skill behavior: Suite validates protocol conformance, not skill correctness. Use example phrases from Card to trigger, don't assert specific output.

  • design-a2a-agent-card — design Card being tested
  • implement-a2a-server — implement server being tested
  • build-ci-cd-pipeline — integrate into CI/CD
  • troubleshoot-mcp-connection — debugging patterns for A2A connectivity
  • review-software-architecture — arch review for multi-agent systems

GitHub 저장소

pjt222/agent-almanac
경로: i18n/caveman-ultra/skills/test-a2a-interop
0
agentsagentskillsai-assisted-developmentclaude-codeskillsteams

연관 스킬

evaluating-llms-harness

테스팅

이 Claude Skill은 MMLU, GSM8K를 포함한 60개 이상의 표준화된 학술 과제에서 LLM 성능을 벤치마크하기 위해 lm-evaluation-harness를 실행합니다. 개발자들이 모델 품질을 비교하고, 학습 진행 상황을 추적하거나 학술 결과를 보고할 수 있도록 설계되었습니다. 이 도구는 HuggingFace와 vLLM 모델을 포함한 다양한 백엔드를 지원합니다.

스킬 보기

cloudflare-cron-triggers

테스팅

이 스킬은 cron 표현식을 사용하여 Worker를 스케줄링하기 위한 Cloudflare Cron Triggers 구현에 관한 포괄적인 지식을 제공합니다. 주기적 작업, 유지보수 작업, 자동화된 워크플로우 설정 방법을 다루며, 잘못된 cron 표현식이나 시간대 문제 같은 일반적인 이슈들을 해결하는 방법을 포함합니다. 개발자들은 이를 통해 스케줄된 핸들러 구성, cron 트리거 테스트, Workflows 및 Green Compute와의 연동 작업을 수행할 수 있습니다.

스킬 보기

webapp-testing

테스팅

이 Claude Skill은 Python 스크립트를 통해 로컬 웹 애플리케이션을 테스트하기 위한 Playwright 기반 툴킷을 제공합니다. 프론트엔드 검증, UI 디버깅, 스크린샷 캡처, 로그 확인 기능을 지원하며 서버 라이프사이클을 관리합니다. 브라우저 자동화 작업에 사용하되 컨텍스트 오염을 방지하기 위해 소스 코드를 읽지 않고 스크립트를 직접 실행하세요.

스킬 보기

finishing-a-development-branch

테스팅

이 스킬은 테스트 통과를 확인한 후 체계적인 통합 옵션을 제시하여 개발자가 완성된 작업을 마무리하도록 돕습니다. 구현이 완료된 후 머지, PR 생성, 브랜치 정리와 같은 워크플로우를 안내합니다. 코드가 준비되고 테스트가 완료되었을 때 개발 프로세스를 체계적으로 마무리하기 위해 사용하세요.

스킬 보기