---
spec_doc_type: spec
spec_id: input/a2a-json-rpc-surface
status: 부분
source_evidence:
  - type: contract
    path: agent-contract/outer/a2a-json-rpc-api.md
    notes: A2A JSON-RPC 외부 HTTP 계약
  - type: code
    path: apps/edge/internal/input/a2a/server.go
    notes: A2A HTTP server, JSON-RPC method dispatch, auth, SubmitRun 연동
  - type: code
    path: apps/edge/internal/input/a2a/task_store.go
    notes: in-memory task store와 RunEvent drain
  - type: code
    path: apps/edge/internal/input/a2a/types.go
    notes: JSON-RPC envelope와 A2A task/message/artifact DTO
  - type: test
    path: apps/edge/internal/input/a2a/server_test.go
    notes: A2A handler 동작 검증
  - type: test
    path: apps/edge/internal/input/a2a/task_store_test.go
    notes: task store drain 상태 검증
---

# 스펙: A2A JSON-RPC 입력 표면

## 목적

Edge가 A2A JSON-RPC 요청을 받아 내부 `adapter + target` 실행으로 넘기는 현재 MVP 동작을 설명한다.

## 기능 목록

| 기능 | 설명 |
|------|------|
| A2A HTTP server | `a2a.enabled=true`이면 Edge input manager가 A2A HTTP server를 시작한다. 기본 RPC path는 `/a2a`다. |
| agent card | `GET /.well-known/agent.json`으로 현재 agent card를 제공한다. |
| bearer auth | `a2a.bearer_token`이 있으면 matching bearer authorization header를 요구한다. |
| JSON-RPC method 처리 | JSON-RPC 2.0 envelope를 검증하고 `message/send`, `tasks/get`, `tasks/cancel`만 처리한다. |
| message/send 실행 | text parts를 newline으로 이어 prompt를 만들고 Edge service `SubmitRun`을 호출한다. |
| blocking task | `configuration.blocking`이 true이거나 생략되면 HTTP 요청 안에서 run stream을 drain한 뒤 최종 task snapshot을 반환한다. |
| background task | non-blocking 요청은 working snapshot을 먼저 반환하고 background goroutine이 run stream을 drain한다. |
| task 조회 | `tasks/get`이 process-local task store의 task snapshot을 반환한다. |
| task 취소 | `tasks/cancel`은 working task에 대해 service `CancelRun`을 호출한다. |
| task artifact | completed task는 adapter output delta를 text artifact 하나에 누적한다. |

## 범위

- 포함: A2A HTTP listener, agent card, bearer auth, JSON-RPC envelope, message text extraction, blocking/background task drain, task get/cancel.
- 제외: A2A streaming, durable task persistence, OpenAI-compatible chat 대체 표면, provider-pool model catalog route, full A2A spec 호환.

## 주요 흐름

```mermaid
sequenceDiagram
  participant Caller
  participant A2A as A2A server
  participant Service as Edge service
  participant Store as TaskStore

  Caller->>A2A: message/send(text parts)
  A2A->>Service: SubmitRun(prompt)
  Service-->>A2A: run id + stream
  A2A->>Store: task working 저장
  alt blocking
    A2A->>A2A: run stream drain
    A2A-->>Caller: final task snapshot
  else non-blocking
    A2A-->>Caller: working task snapshot
    A2A->>A2A: background drain
  end
```

## 계약

- `iop.a2a-json-rpc-api`: `agent-contract/outer/a2a-json-rpc-api.md`
- 내부 실행 wire: `agent-contract/inner/edge-node-runtime-wire.md`

## 설정/데이터/이벤트

- `configs/edge.yaml`의 `a2a` 섹션이 listener, path, node ref, adapter, target, session id, timeout, bearer token을 제공한다.
- A2A run metadata에는 현재 `source=a2a`와 `blocking=true` 또는 `blocking=false`가 들어간다. 이 metadata는 OpenAI-compatible request metadata 계약과 별개인 내부 run metadata다.
- TaskStore는 run event `delta`, `complete`, `error`, `cancelled`와 node disconnect를 관찰해 task status를 갱신한다.

## 검증

- `go test ./apps/edge/internal/input/a2a`
- `go test ./apps/edge/internal/input`
- `go test ./apps/edge/internal/service`

## 한계와 주의사항

- task state는 메모리에만 있고 Edge 재시작 시 사라진다.
- agent card URL은 현재 listen/path 기반 단순 표현이다. reverse proxy/public URL 계약은 별도로 확정해야 한다.
- A2A 표면은 현재 configured adapter/target으로만 run을 보낸다. request별 provider-pool model route는 이 spec의 현재 동작이 아니다.
- streaming capability는 agent card에서 false다.
- OpenAI-compatible chat/completions 대체 표면으로 쓰지 않는다.

## 변경 기록

- 2026-07-07: 현재 코드와 A2A 계약 기준으로 bootstrap spec 작성.
- 2026-07-07: 기능 목록 중심으로 축소하고 주요 흐름을 Mermaid sequence diagram으로 정리.
