-
Notifications
You must be signed in to change notification settings - Fork 593
Description
🔌 Epic: A2A Protocol v0.3.0 Full Compliance Implementation
Goal
Achieve 100% compliance with the official A2A (Agent-to-Agent) Protocol specification v0.3.0 by implementing all three transport bindings (JSON-RPC 2.0, HTTP+JSON/REST, gRPC), correct message schemas with kind discriminator fields, proper Agent Card discovery, streaming support, and task lifecycle management. This ensures ContextForge can interoperate with any A2A-compliant agent ecosystem including Google's A2A SDK implementations.
Why Now?
The A2A protocol is gaining adoption as the standard for agent-to-agent communication:
- Interoperability: Current implementation uses
typeinstead ofkindfor message parts andmessage/sendmethod naming, causing compatibility issues with strict A2A v0.3.0 implementations - Ecosystem Growth: Google's A2A SDK and other implementations expect spec-compliant behavior
- User Confusion: Issue [BUG]: A2A Agent "Test Agent" returns HTTP 500 error message #2544 demonstrates users struggle with format mismatches between "Generic" agents and simple REST endpoints
- Missing Features: No support for tasks, streaming (
SendStreamingMessage), or Agent Card discovery (.well-known/agent-card.json) - Transport Gaps: Only JSON-RPC supported; no REST binding (
/v1/message:send) or gRPC binding - Production Readiness: Enterprise customers need certified A2A compliance for vendor evaluation
📖 User Stories
US-1: Integration Developer - JSON-RPC 2.0 Compliance
As an Integration Developer
I want ContextForge to send spec-compliant JSON-RPC 2.0 requests to A2A agents
So that my agents built with Google's A2A SDK work without modification
Acceptance Criteria:
Given an A2A agent registered with agent_type="a2a" or "jsonrpc"
When ContextForge invokes the agent with a message
Then the request should use JSON-RPC 2.0 format:
{
"jsonrpc": "2.0",
"id": "<unique-request-id>",
"method": "message/send",
"params": {
"message": {
"messageId": "<unique-message-id>",
"role": "user",
"parts": [{"kind": "text", "text": "Hello!"}]
}
}
}
And message parts should use "kind" field (not "type")
And the "id" field should be a unique string (not integer 1)
And the response should be parsed correctly:
- Success: {"jsonrpc": "2.0", "result": {...}, "id": "..."}
- Error: {"jsonrpc": "2.0", "error": {"code": ..., "message": ...}, "id": "..."}Technical Requirements:
- Update
a2a_service.pyto usekindinstead oftypefor message parts - Generate unique request IDs (UUID) instead of hardcoded
1 - Support all A2A error codes (-32001 to -32005)
- Parse JSONRPC error responses correctly
US-2: Platform Admin - REST Transport Binding
As a Platform Administrator
I want ContextForge to support the A2A REST transport binding
So that I can integrate with A2A agents that expose REST endpoints instead of JSON-RPC
Acceptance Criteria:
Given an A2A agent registered with agent_type="a2a-rest"
And endpoint_url="https://agent.example.com/v1"
When ContextForge sends a message
Then the request should use REST format:
POST https://agent.example.com/v1/message:send
Content-Type: application/json
{
"message": {
"messageId": "<unique-message-id>",
"role": "user",
"parts": [{"kind": "text", "text": "Hello!"}]
}
}
And the A2A-Version header should be included: "A2A-Version: 0.3"
And task operations should map correctly:
- GetTask → GET /v1/tasks/{id}
- ListTasks → GET /v1/tasks
- CancelTask → POST /v1/tasks/{id}:cancelTechnical Requirements:
- Add
a2a-restagent type - Implement REST endpoint mapping per A2A spec
- Add
A2A-Versionheader to requests - Support all REST operations (message, tasks, push notifications)
US-3: Enterprise Developer - gRPC Transport Binding
As an Enterprise Developer
I want ContextForge to support the A2A gRPC transport binding
So that I can achieve high-performance communication with A2A agents using Protocol Buffers
Acceptance Criteria:
Given an A2A agent registered with agent_type="a2a-grpc"
And endpoint_url="grpc://agent.example.com:50051"
When ContextForge sends a message
Then the request should use gRPC with Protocol Buffers:
- Service: A2AService
- Method: SendMessage
- Request: SendMessageRequest protobuf
And the connection should support:
- Unary RPCs for SendMessage, GetTask, etc.
- Server streaming for SendStreamingMessage, SubscribeToTask
- TLS encryption (grpcs://)Technical Requirements:
- Generate Python gRPC stubs from
a2a.proto - Implement gRPC client in A2A service
- Support streaming responses
- Handle gRPC status codes mapped to A2A errors
US-4: AI Engineer - Agent Card Discovery
As an AI Engineer
I want ContextForge to discover A2A agent capabilities via Agent Cards
So that I can auto-configure agents and understand their supported features
Acceptance Criteria:
Given an A2A agent endpoint at https://agent.example.com
When I register the agent with only the base URL
Then ContextForge should:
- Fetch /.well-known/agent-card.json
- Parse the Agent Card JSON:
{
"name": "MyAgent",
"description": "...",
"url": "https://agent.example.com",
"protocolVersions": ["0.3"],
"capabilities": {
"streaming": true,
"pushNotifications": false
},
"skills": [...],
"securitySchemes": {...}
}
- Auto-detect transport binding (JSON-RPC, REST, gRPC)
- Store capabilities in database
- Display capabilities in Admin UI
And if the Agent Card has security requirements
Then ContextForge should prompt for credentialsTechnical Requirements:
- Implement Agent Card fetching and parsing
- Auto-detect transport from Agent Card
- Store agent capabilities in database
- Display Agent Card info in Admin UI
- Support extended Agent Card (
/extendedAgentCardwith auth)
US-5: Platform Admin - Streaming Message Support
As a Platform Administrator
I want ContextForge to support A2A streaming messages
So that users can receive real-time responses from AI agents
Acceptance Criteria:
Given an A2A agent with capabilities.streaming=true
When a user invokes the agent via the streaming endpoint
Then ContextForge should:
- Call SendStreamingMessage (JSON-RPC) or POST /v1/message:stream (REST)
- Receive Server-Sent Events (SSE) stream
- Parse streaming events:
- MessageStart: {"type": "message_start", ...}
- ContentDelta: {"type": "content_delta", "delta": {"text": "..."}}
- MessageEnd: {"type": "message_end", ...}
- Forward events to client via SSE
- Handle stream errors gracefully
And metrics should track streaming message duration and throughputTechnical Requirements:
- Implement streaming invocation endpoint
- Parse A2A SSE event format
- Forward SSE to MCP clients
- Handle stream interruptions and reconnection
- Track streaming metrics
US-6: Integration Developer - Task Lifecycle Management
As an Integration Developer
I want ContextForge to support the full A2A task lifecycle
So that I can track long-running agent operations and handle multi-turn conversations
Acceptance Criteria:
Given an A2A agent returns a Task object instead of immediate result:
{
"kind": "task",
"id": "task-123",
"contextId": "ctx-456",
"status": {"state": "working"},
"history": [...]
}
When the task is in "working" state
Then ContextForge should:
- Store the task reference
- Allow polling via GetTask
- Allow subscription via SubscribeToTask
- Handle status transitions:
- PENDING → WORKING → COMPLETED
- PENDING → WORKING → INPUT_REQUIRED → WORKING → COMPLETED
- PENDING → FAILED/CANCELLED/REJECTED
- Support task cancellation via CancelTask
And multi-turn conversations should use contextId for continuityTechnical Requirements:
- Create Task database model for tracking A2A tasks
- Implement GetTask, ListTasks, CancelTask operations
- Support SubscribeToTask streaming
- Handle INPUT_REQUIRED state with user prompts
- Link tasks via contextId for conversations
US-7: DevOps Engineer - Push Notifications
As a DevOps Engineer
I want ContextForge to support A2A push notifications
So that long-running tasks can notify webhooks instead of requiring polling
Acceptance Criteria:
Given an A2A agent with capabilities.pushNotifications=true
When I configure a push notification for a task:
POST /tasks/{taskId}/pushNotificationConfigs
{
"url": "https://my-webhook.example.com/notify",
"authentication": {"type": "bearer", "token": "..."}
}
Then the agent should POST updates to my webhook
And ContextForge should:
- Allow registering push notification configs
- Provide webhook receiver endpoint for incoming notifications
- Verify notification authenticity
- Update task status based on notificationsTechnical Requirements:
- Implement push notification config CRUD
- Create webhook receiver endpoint
- Verify notification signatures/tokens
- Update task status from notifications
- Handle notification delivery failures
US-8: Platform Admin - Simple REST Passthrough Mode
As a Platform Administrator
I want a passthrough mode for simple REST endpoints
So that I can integrate AWS Lambda and other non-A2A endpoints without format wrapping
Acceptance Criteria:
Given an agent registered with agent_type="rest-passthrough"
When I invoke the agent with parameters:
{"query": "Hello", "custom_field": "value"}
Then ContextForge should:
- Send the parameters directly as the request body (no wrapping)
- NOT wrap in {interaction_type, parameters, protocol_version}
- NOT wrap in JSON-RPC format
- Pass through the raw JSON body:
{"query": "Hello", "custom_field": "value"}
And the response should be returned without parsing/wrappingTechnical Requirements:
- Add
rest-passthroughagent type - Skip all format wrapping for passthrough mode
- Pass raw request body to endpoint
- Return raw response to client
- Document clearly in UI with warnings about compatibility
US-9: Security Engineer - A2A Authentication Schemes
As a Security Engineer
I want ContextForge to support all A2A security schemes
So that I can securely authenticate with any A2A agent
Acceptance Criteria:
Given an A2A agent with securitySchemes in its Agent Card:
{
"securitySchemes": {
"bearerAuth": {"type": "http", "scheme": "bearer"},
"apiKey": {"type": "apiKey", "in": "header", "name": "X-API-Key"},
"oauth2": {"type": "oauth2", "flows": {...}}
},
"security": [{"bearerAuth": []}, {"apiKey": []}]
}
When registering the agent
Then ContextForge should:
- Parse security requirements from Agent Card
- Prompt for required credentials
- Support HTTP Basic, Bearer, API Key authentication
- Support OAuth 2.0 flows (client credentials, authorization code)
- Support OpenID Connect
- Include credentials in requests per scheme specificationTechnical Requirements:
- Parse Agent Card security schemes
- Implement all HTTP auth schemes
- Implement OAuth 2.0 token management
- Encrypt and store credentials securely
- Refresh tokens automatically
US-10: Platform Admin - Clear Agent Type Labeling
As a Platform Administrator
I want clear, accurate labels for agent types in the Admin UI
So that I can correctly configure agents without confusion
Acceptance Criteria:
Given the Admin UI agent type dropdown
Then the options should be clearly labeled:
| Value | Display Label | Description |
|------------------|-----------------------------------|--------------------------------------------------|
| a2a-jsonrpc | A2A Protocol (JSON-RPC 2.0) | Official A2A spec - JSON-RPC transport |
| a2a-rest | A2A Protocol (REST) | Official A2A spec - HTTP+JSON transport |
| a2a-grpc | A2A Protocol (gRPC) | Official A2A spec - gRPC transport |
| rest-passthrough | Simple REST (Passthrough) | Raw JSON passthrough - no format wrapping |
| custom | ContextForge Custom Format | Legacy wrapper format (interaction_type/params) |
And tooltips should explain each format with example payloads
And a "Preview Request" button should show the exact JSON to be sentTechnical Requirements:
- Rename agent types with clear labels
- Add tooltips with format examples
- Implement request preview modal
- Warn about trailing slash behavior
- Migration path for existing agents
🏗 Architecture
A2A Protocol Transport Bindings
graph TB
subgraph "ContextForge A2A Gateway"
Router[A2A Router]
Service[A2A Service]
subgraph "Transport Adapters"
JSONRPC[JSON-RPC 2.0 Adapter]
REST[REST Adapter]
GRPC[gRPC Adapter]
Passthrough[Passthrough Adapter]
end
subgraph "Protocol Handlers"
MessageHandler[Message Handler]
TaskHandler[Task Handler]
StreamHandler[Stream Handler]
CardHandler[Agent Card Handler]
end
end
subgraph "A2A Agents"
Agent1[JSON-RPC Agent]
Agent2[REST Agent]
Agent3[gRPC Agent]
Agent4[Simple REST API]
end
Router --> Service
Service --> JSONRPC --> Agent1
Service --> REST --> Agent2
Service --> GRPC --> Agent3
Service --> Passthrough --> Agent4
MessageHandler --> Service
TaskHandler --> Service
StreamHandler --> Service
CardHandler --> Service
Message Part Schema (A2A v0.3.0)
classDiagram
class Part {
<<union>>
}
class TextPart {
+kind: "text"
+text: string
+metadata?: object
}
class FilePart {
+kind: "file"
+file: FileContent
+metadata?: object
}
class DataPart {
+kind: "data"
+data: object
+metadata?: object
}
class FileContent {
+name?: string
+mimeType?: string
+bytes?: string (base64)
+uri?: string
}
Part <|-- TextPart
Part <|-- FilePart
Part <|-- DataPart
FilePart --> FileContent
Task State Machine
stateDiagram-v2
[*] --> PENDING: Task Created
PENDING --> WORKING: Agent Processing
WORKING --> COMPLETED: Success
WORKING --> FAILED: Error
WORKING --> CANCELLED: User Cancelled
WORKING --> INPUT_REQUIRED: Need User Input
INPUT_REQUIRED --> WORKING: User Provided Input
PENDING --> REJECTED: Agent Rejected
COMPLETED --> [*]
FAILED --> [*]
CANCELLED --> [*]
REJECTED --> [*]
Agent Card Discovery Flow
sequenceDiagram
participant Admin as Admin UI
participant GW as ContextForge
participant Agent as A2A Agent
Admin->>GW: Register agent (base URL only)
GW->>Agent: GET /.well-known/agent-card.json
Agent-->>GW: Agent Card JSON
GW->>GW: Parse capabilities
GW->>GW: Detect transport binding
GW->>GW: Parse security schemes
alt Security Required
GW-->>Admin: Prompt for credentials
Admin->>GW: Provide credentials
GW->>Agent: GET /extendedAgentCard (authenticated)
Agent-->>GW: Extended Agent Card
end
GW->>GW: Store agent with capabilities
GW-->>Admin: Agent registered successfully
📋 Implementation Tasks
Phase 1: Core Schema Updates ✅
-
Update Message Part Schema
- Change
typetokindin all message part creation - Update
admin.pyline 14890:{"type": "text"→{"kind": "text" - Update
a2a_service.pyto usekindfield - Add backward compatibility: accept both
typeandkindon input - Output always uses
kindper spec
- Change
-
Update Request ID Generation
- Change hardcoded
"id": 1to UUID string - Use
str(uuid.uuid4())for request IDs - Store request ID for response correlation
- Change hardcoded
-
Fix Trailing Slash Behavior
- Remove automatic JSON-RPC triggering on trailing slash
- Make format selection based solely on agent_type
- Add migration for existing agents with trailing slashes
- Add UI warning about URL format
Phase 2: Agent Type Refactoring ✅
-
Define New Agent Types
class A2AAgentType(str, Enum): A2A_JSONRPC = "a2a-jsonrpc" # A2A spec JSON-RPC 2.0 A2A_REST = "a2a-rest" # A2A spec REST binding A2A_GRPC = "a2a-grpc" # A2A spec gRPC binding REST_PASSTHROUGH = "rest-passthrough" # Raw passthrough CUSTOM = "custom" # Legacy ContextForge format # Deprecated aliases GENERIC = "generic" # → a2a-jsonrpc JSONRPC = "jsonrpc" # → a2a-jsonrpc
-
Migration for Existing Agents
- Create Alembic migration for agent_type values
- Map
generic→a2a-jsonrpc - Map
jsonrpc→a2a-jsonrpc - Keep
customas-is - Log migration changes
-
Update Format Selection Logic
def get_transport_adapter(agent_type: str, endpoint_url: str): if agent_type in ["a2a-jsonrpc", "generic", "jsonrpc"]: return JSONRPCAdapter() elif agent_type == "a2a-rest": return RESTAdapter() elif agent_type == "a2a-grpc": return GRPCAdapter() elif agent_type == "rest-passthrough": return PassthroughAdapter() else: # custom return CustomFormatAdapter()
Phase 3: JSON-RPC 2.0 Full Compliance ✅
-
Request Format
- Method:
message/send(correct per spec) - Parts use
kindfield - Request ID is unique string
- Include all required message fields
- Method:
-
Response Parsing
- Parse success:
{"result": {...}} - Parse error:
{"error": {"code": ..., "message": ..., "data": ...}} - Map A2A error codes:
-32001→ TaskNotFoundError-32002→ TaskNotCancelableError-32003→ PushNotificationNotSupportedError-32004→ UnsupportedOperationError-32005→ ContentTypeNotSupportedError
- Parse success:
-
Batch Requests (Optional)
- Support JSON-RPC batch requests
- Parse batch responses
Phase 4: REST Transport Binding ✅
-
Endpoint Mapping
Operation Method Endpoint SendMessage POST /v1/message:sendSendStreamingMessage POST /v1/message:streamGetTask GET /v1/tasks/{id}ListTasks GET /v1/tasksCancelTask POST /v1/tasks/{id}:cancelSubscribeToTask POST /v1/tasks/{id}:subscribe -
Headers
-
Content-Type: application/json -
A2A-Version: 0.3 -
A2A-Extensions: <comma-separated URIs>(if any) - Authentication headers per security scheme
-
-
REST Adapter Implementation
class RESTAdapter: async def send_message(self, endpoint: str, message: dict) -> dict: url = f"{endpoint}/v1/message:send" headers = {"A2A-Version": "0.3", "Content-Type": "application/json"} response = await client.post(url, json={"message": message}, headers=headers) return response.json()
Phase 5: gRPC Transport Binding ✅
-
Proto Generation
- Download official
a2a.protofrom A2A repo - Generate Python stubs with
grpcio-tools - Include generated files in package
- Download official
-
gRPC Client
class GRPCAdapter: def __init__(self, endpoint: str): self.channel = grpc.aio.insecure_channel(endpoint) self.stub = a2a_pb2_grpc.A2AServiceStub(self.channel) async def send_message(self, message: dict) -> dict: request = a2a_pb2.SendMessageRequest(message=to_proto(message)) response = await self.stub.SendMessage(request) return from_proto(response)
-
Streaming Support
- Implement
SendStreamingMessagewith async generator - Implement
SubscribeToTaskstreaming - Handle stream errors and reconnection
- Implement
-
TLS Support
- Support
grpcs://for TLS - Certificate verification options
- Support
Phase 6: Agent Card Discovery ✅
-
Fetch Agent Card
async def discover_agent_card(base_url: str) -> AgentCard: card_url = f"{base_url.rstrip('/')}/.well-known/agent-card.json" response = await client.get(card_url) return AgentCard.model_validate(response.json())
-
Agent Card Schema
class AgentCard(BaseModel): name: str description: Optional[str] url: str version: Optional[str] protocol_versions: List[str] = Field(alias="protocolVersions") capabilities: AgentCapabilities skills: Optional[List[AgentSkill]] security_schemes: Optional[Dict[str, SecurityScheme]] = Field(alias="securitySchemes") security: Optional[List[Dict[str, List[str]]]] extensions: Optional[List[Extension]]
-
Auto-Configuration
- Detect transport from Agent Card
- Store capabilities in database
- Parse and store security requirements
- Display in Admin UI
-
Extended Agent Card
- Support authenticated
/extendedAgentCardendpoint - Parse additional protected capabilities
- Support authenticated
Phase 7: Task Management ✅
-
Task Database Model
class A2ATask(Base): __tablename__ = "a2a_tasks" id: Mapped[str] = mapped_column(String(100), primary_key=True) context_id: Mapped[Optional[str]] = mapped_column(String(100)) agent_id: Mapped[str] = mapped_column(ForeignKey("a2a_agents.id")) status_state: Mapped[str] = mapped_column(String(50)) # PENDING, WORKING, etc. status_message: Mapped[Optional[str]] history: Mapped[Optional[dict]] = mapped_column(JSON) artifacts: Mapped[Optional[list]] = mapped_column(JSON) created_at: Mapped[datetime] updated_at: Mapped[datetime]
-
Task Operations
-
get_task(task_id)→ GetTask -
list_tasks(context_id, status)→ ListTasks -
cancel_task(task_id)→ CancelTask -
subscribe_to_task(task_id)→ SubscribeToTask (streaming)
-
-
Task State Handling
- Handle INPUT_REQUIRED state
- Support multi-turn conversations via contextId
- Clean up completed/failed tasks
Phase 8: Streaming Support ✅
-
SSE Client
async def stream_message(self, endpoint: str, message: dict): async with client.stream("POST", f"{endpoint}/v1/message:stream", json={"message": message}) as response: async for line in response.aiter_lines(): if line.startswith("data: "): event = json.loads(line[6:]) yield event
-
Event Types
-
message_start- New message started -
content_delta- Incremental content -
message_end- Message complete -
error- Stream error
-
-
SSE Forwarding
- Forward A2A SSE events to MCP clients
- Map A2A events to MCP streaming format
- Handle client disconnection
Phase 9: Passthrough Mode ✅
-
Passthrough Adapter
class PassthroughAdapter: async def invoke(self, endpoint: str, body: dict, headers: dict) -> dict: # No wrapping - send body directly response = await client.post(endpoint, json=body, headers=headers) return response.json()
-
No Format Wrapping
- Skip interaction_type/parameters wrapper
- Skip JSON-RPC wrapper
- Pass raw request body
- Return raw response
-
UI Warnings
- Warn that passthrough has no format validation
- Explain this is for simple REST APIs only
- Document expected request/response format
Phase 10: Admin UI Updates ✅
-
Agent Type Dropdown
- Clear labels with descriptions
- Tooltips explaining each format
- Group by protocol family
-
Request Preview
- "Preview Request" button
- Show exact JSON that will be sent
- Syntax highlighting
-
Agent Card Display
- Show discovered capabilities
- Display supported skills
- Show security requirements
-
Trailing Slash Warning
- Detect trailing slash in URL
- Show warning banner
- Suggest correction
Phase 11: Testing ✅
-
Unit Tests
- Test
kindvstypefield handling - Test all transport adapters
- Test Agent Card parsing
- Test task state machine
- Test streaming event parsing
- Test error code mapping
- Test
-
Integration Tests
- Test against A2A SDK HelloWorld sample
- Test against mock A2A servers
- Test all three transports
- Test task lifecycle
- Test streaming
-
Compatibility Tests
- Test backward compatibility with existing agents
- Test migration of existing agent_type values
- Test both
typeandkindinput acceptance
Phase 12: Documentation ✅
-
A2A Integration Guide
- Transport binding selection guide
- Agent Card discovery documentation
- Task management guide
- Streaming setup guide
-
Migration Guide
- Upgrading from old agent types
- Updating endpoint URLs
- Testing after migration
-
API Documentation
- OpenAPI spec for A2A endpoints
- Agent Card schema reference
- Error code reference
⚙️ Configuration Example
Agent Registration with Auto-Discovery
# Register agent with just base URL - auto-discovers capabilities
curl -X POST "http://localhost:4444/a2a" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"agent": {
"name": "smart-assistant",
"endpoint_url": "https://agent.example.com",
"auto_discover": true
}
}'
# Response includes discovered capabilities
{
"id": "...",
"name": "smart-assistant",
"agentType": "a2a-jsonrpc", # Auto-detected
"capabilities": {
"streaming": true,
"pushNotifications": false,
"tasks": true
},
"skills": [
{"name": "search", "description": "Web search"},
{"name": "calculate", "description": "Math operations"}
]
}Manual Configuration
# JSON-RPC A2A Agent
curl -X POST "http://localhost:4444/a2a" \
-d '{
"agent": {
"name": "jsonrpc-agent",
"endpoint_url": "https://agent.example.com/jsonrpc",
"agent_type": "a2a-jsonrpc"
}
}'
# REST A2A Agent
curl -X POST "http://localhost:4444/a2a" \
-d '{
"agent": {
"name": "rest-agent",
"endpoint_url": "https://agent.example.com",
"agent_type": "a2a-rest"
}
}'
# Simple REST Passthrough (AWS Lambda, etc.)
curl -X POST "http://localhost:4444/a2a" \
-d '{
"agent": {
"name": "my-lambda",
"endpoint_url": "https://lambda.amazonaws.com/prod/invoke",
"agent_type": "rest-passthrough"
}
}'✅ Success Criteria
- JSON-RPC Compliance: Parts use
kindfield; unique request IDs; correct error handling - REST Binding: Full
/v1/message:sendendpoint support with proper headers - gRPC Binding: Working proto-based communication with streaming
- Agent Card: Auto-discovery from
.well-known/agent-card.json - Tasks: Full lifecycle support (create, poll, cancel, subscribe)
- Streaming: SSE message streaming works end-to-end
- Passthrough: Raw JSON passthrough for simple REST endpoints
- UI Clarity: Clear agent type labels with request preview
- Backward Compatibility: Existing agents continue to work
- Testing: 80%+ coverage; passes A2A SDK sample tests
- Documentation: Complete integration guide
🏁 Definition of Done
- Message parts use
kindfield (nottype) - Request IDs are unique UUIDs (not hardcoded
1) - Trailing slash no longer forces JSON-RPC
- All three transports implemented (JSON-RPC, REST, gRPC)
- Agent Card discovery working
- Task management implemented
- Streaming support working
- Passthrough mode available
- Agent type labels clear in UI
- Request preview feature working
- Existing agents migrated
- Unit tests with 80%+ coverage
- Integration tests passing
- A2A SDK samples working
- Documentation complete
- Code passes
make verify
📝 Additional Notes
🔹 A2A Spec Versions:
- v0.1.0: Used
typefor parts (legacy) - v0.3.0: Uses
kindfor parts (current) - ContextForge should support both on input, output
kind
🔹 Transport Selection:
- JSON-RPC: Best for simple request/response
- REST: Best for HTTP-native environments
- gRPC: Best for high-performance, streaming-heavy use cases
- Passthrough: For non-A2A REST endpoints
🔹 Breaking Changes:
genericagent type renamed toa2a-jsonrpc- Parts output changes from
typetokind - Trailing slash behavior changes
- Migration required for existing agents
🔹 Performance Considerations:
- Agent Card caching (5 min TTL)
- gRPC connection pooling
- Streaming buffer management
- Task cleanup for completed tasks
🔗 Related Issues
- [BUG]: A2A Agent "Test Agent" returns HTTP 500 error message #2544 - A2A Agent "Test Agent" returns HTTP 500 error (user confusion)
- [BUG]: A2A Agent test not working #840 - A2A agent query input improvements