Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
2d09e6b
feat: add update mechanism config structure
greysonlalonde Jan 5, 2026
7589e52
feat: add shared task helpers and error types
greysonlalonde Jan 5, 2026
12d60a4
feat: add polling and streaming handlers
greysonlalonde Jan 5, 2026
f478004
chore: use TaskStateResult and TaskState enum
greysonlalonde Jan 5, 2026
c75391d
feat: activate polling events
greysonlalonde Jan 5, 2026
33caeeb
chore: refactor handlers to unified protocol
greysonlalonde Jan 6, 2026
8c08963
chore: use python version compat types
greysonlalonde Jan 6, 2026
174c61b
feat: add push notification protocol and config
greysonlalonde Jan 6, 2026
3607993
feat: add push notification events
greysonlalonde Jan 6, 2026
33d73c0
refactor: extract shared message sending logic
greysonlalonde Jan 6, 2026
f9977a5
feat: implement push notification handler
greysonlalonde Jan 6, 2026
1f9fe0f
chore: remove excess docs note
greysonlalonde Jan 6, 2026
a2e6901
chore: remove unused authentication field from PushNotificationConfig
greysonlalonde Jan 6, 2026
e9fafd4
feat: add authentication field to PushNotificationConfig
greysonlalonde Jan 6, 2026
7c95553
Merge branch 'main' into gl/feat/a2a-async-updates
greysonlalonde Jan 6, 2026
edbe8a8
chore: update typing import for python version compat
greysonlalonde Jan 6, 2026
d6c7f33
chore: add a2a integration tests with cassettes
greysonlalonde Jan 6, 2026
5629a70
fix: pass push notification config in initial request
greysonlalonde Jan 6, 2026
fa19a57
chore: add a2a async updates docs
greysonlalonde Jan 6, 2026
57d9143
chore: update test assumption, docs
greysonlalonde Jan 6, 2026
b754dc1
fix: ensure response models checked before parsing attempt
greysonlalonde Jan 6, 2026
514df45
Merge branch 'main' into gl/feat/a2a-async-updates
greysonlalonde Jan 6, 2026
f53b875
fix: ensure failed states are handled for push, poll
greysonlalonde Jan 6, 2026
c639455
fix: ensure stream is closed on exit
greysonlalonde Jan 6, 2026
8c9b1fb
fix: add error catch for polling timeout
greysonlalonde Jan 7, 2026
cbd394e
Merge branch 'main' into gl/feat/a2a-async-updates
greysonlalonde Jan 7, 2026
0230cb6
feat: add agent_card to conditional branch fallback
greysonlalonde Jan 7, 2026
1da060c
fix: ensure artifacts are not duplicated
greysonlalonde Jan 7, 2026
556f82a
feat: move handler types to types.py and add native async a2a functions
greysonlalonde Jan 7, 2026
9efb973
feat: add server agent card generation and organize a2a utils into su…
greysonlalonde Jan 7, 2026
480ac98
feat: add server and client configs, deprecate unified
greysonlalonde Jan 7, 2026
ad52420
Merge branch 'main' into gl/feat/a2a-native-async
greysonlalonde Jan 7, 2026
c5a639b
Merge branch 'gl/feat/a2a-native-async' into gl/feat/a2a-agent-cards
greysonlalonde Jan 7, 2026
4fb99c0
Merge branch 'main' into gl/feat/a2a-agent-cards
greysonlalonde Jan 7, 2026
2de21a0
feat: generate agent card from server config or agent
greysonlalonde Jan 7, 2026
422b35c
feat: add additional a2a fields, deprecate old config
greysonlalonde Jan 14, 2026
f806cf5
Merge branch 'main' into gl/feat/a2a-agent-cards
greysonlalonde Jan 14, 2026
68df061
feat: inject server methods
greysonlalonde Jan 14, 2026
372ba9a
fix: reorganize type declarations
greysonlalonde Jan 14, 2026
34b6137
chore: add agent card structure tests
greysonlalonde Jan 14, 2026
f059dfa
fix: only inject method on agent
greysonlalonde Jan 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 196 additions & 19 deletions lib/crewai/src/crewai/a2a/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,22 @@

from __future__ import annotations

from typing import Annotated, Any, ClassVar, Literal

from pydantic import (
BaseModel,
BeforeValidator,
ConfigDict,
Field,
HttpUrl,
TypeAdapter,
from importlib.metadata import version
from typing import Any, ClassVar, Literal

from a2a.types import (
AgentCapabilities,
AgentCardSignature,
AgentInterface,
AgentProvider,
AgentSkill,
SecurityScheme,
)
from pydantic import BaseModel, ConfigDict, Field
from typing_extensions import deprecated

from crewai.a2a.auth.schemas import AuthScheme
from crewai.a2a.types import TransportType, Url


try:
Expand All @@ -25,25 +29,25 @@
UpdateConfig = Any # type: ignore[misc,assignment]


http_url_adapter = TypeAdapter(HttpUrl)

Url = Annotated[
str,
BeforeValidator(
lambda value: str(http_url_adapter.validate_python(value, strict=True))
),
]


def _get_default_update_config() -> UpdateConfig:
from crewai.a2a.updates import StreamingConfig

return StreamingConfig()


@deprecated(
"""
`crewai.a2a.config.A2AConfig` is deprecated and will be removed in v2.0.0,
use `crewai.a2a.config.A2AClientConfig` or `crewai.a2a.config.A2AServerConfig` instead.
""",
category=FutureWarning,
)
class A2AConfig(BaseModel):
"""Configuration for A2A protocol integration.

Deprecated:
Use A2AClientConfig instead. This class will be removed in a future version.

Attributes:
endpoint: A2A agent endpoint URL.
auth: Authentication scheme.
Expand Down Expand Up @@ -87,3 +91,176 @@ class A2AConfig(BaseModel):
default="JSONRPC",
description="Specified mode of A2A transport protocol",
)


class A2AClientConfig(BaseModel):
"""Configuration for connecting to remote A2A agents.

Attributes:
endpoint: A2A agent endpoint URL.
auth: Authentication scheme.
timeout: Request timeout in seconds.
max_turns: Maximum conversation turns with A2A agent.
response_model: Optional Pydantic model for structured A2A agent responses.
fail_fast: If True, raise error when agent unreachable; if False, skip and continue.
trust_remote_completion_status: If True, return A2A agent's result directly when completed.
updates: Update mechanism config.
accepted_output_modes: Media types the client can accept in responses.
supported_transports: Ordered list of transport protocols the client supports.
use_client_preference: Whether to prioritize client transport preferences over server.
extensions: Extension URIs the client supports.
"""

model_config: ClassVar[ConfigDict] = ConfigDict(extra="forbid")

endpoint: Url = Field(description="A2A agent endpoint URL")
auth: AuthScheme | None = Field(
default=None,
description="Authentication scheme",
)
timeout: int = Field(default=120, description="Request timeout in seconds")
max_turns: int = Field(
default=10, description="Maximum conversation turns with A2A agent"
)
response_model: type[BaseModel] | None = Field(
default=None,
description="Optional Pydantic model for structured A2A agent responses",
)
fail_fast: bool = Field(
default=True,
description="If True, raise error when agent unreachable; if False, skip",
)
trust_remote_completion_status: bool = Field(
default=False,
description="If True, return A2A result directly when completed",
)
updates: UpdateConfig = Field(
default_factory=_get_default_update_config,
description="Update mechanism config",
)
accepted_output_modes: list[str] = Field(
default_factory=lambda: ["application/json"],
description="Media types the client can accept in responses",
)
supported_transports: list[str] = Field(
default_factory=lambda: ["JSONRPC"],
description="Ordered list of transport protocols the client supports",
)
use_client_preference: bool = Field(
default=False,
description="Whether to prioritize client transport preferences over server",
)
extensions: list[str] = Field(
default_factory=list,
description="Extension URIs the client supports",
)
transport_protocol: Literal["JSONRPC", "GRPC", "HTTP+JSON"] = Field(
default="JSONRPC",
description="Specified mode of A2A transport protocol",
)


class A2AServerConfig(BaseModel):
"""Configuration for exposing a Crew or Agent as an A2A server.

All fields correspond to A2A AgentCard fields. Fields like name, description,
and skills can be auto-derived from the Crew/Agent if not provided.

Attributes:
name: Human-readable name for the agent.
description: Human-readable description of the agent.
version: Version string for the agent card.
skills: List of agent skills/capabilities.
default_input_modes: Default supported input MIME types.
default_output_modes: Default supported output MIME types.
capabilities: Declaration of optional capabilities.
preferred_transport: Transport protocol for the preferred endpoint.
protocol_version: A2A protocol version this agent supports.
provider: Information about the agent's service provider.
documentation_url: URL to the agent's documentation.
icon_url: URL to an icon for the agent.
additional_interfaces: Additional supported interfaces.
security: Security requirement objects for all interactions.
security_schemes: Security schemes available to authorize requests.
supports_authenticated_extended_card: Whether agent provides extended card to authenticated users.
url: Preferred endpoint URL for the agent.
signatures: JSON Web Signatures for the AgentCard.
"""

model_config: ClassVar[ConfigDict] = ConfigDict(extra="forbid")

name: str | None = Field(
default=None,
description="Human-readable name for the agent. Auto-derived from Crew/Agent if not provided.",
)
description: str | None = Field(
default=None,
description="Human-readable description of the agent. Auto-derived from Crew/Agent if not provided.",
)
version: str = Field(
default="1.0.0",
description="Version string for the agent card",
)
skills: list[AgentSkill] = Field(
default_factory=list,
description="List of agent skills. Auto-derived from tasks/tools if not provided.",
)
default_input_modes: list[str] = Field(
default_factory=lambda: ["text/plain", "application/json"],
description="Default supported input MIME types",
)
default_output_modes: list[str] = Field(
default_factory=lambda: ["text/plain", "application/json"],
description="Default supported output MIME types",
)
capabilities: AgentCapabilities = Field(
default_factory=lambda: AgentCapabilities(
streaming=True,
push_notifications=False,
),
description="Declaration of optional capabilities supported by the agent",
)
preferred_transport: TransportType = Field(
default="JSONRPC",
description="Transport protocol for the preferred endpoint",
)
protocol_version: str = Field(
default_factory=lambda: version("a2a-sdk"),
description="A2A protocol version this agent supports",
)
provider: AgentProvider | None = Field(
default=None,
description="Information about the agent's service provider",
)
documentation_url: Url | None = Field(
default=None,
description="URL to the agent's documentation",
)
icon_url: Url | None = Field(
default=None,
description="URL to an icon for the agent",
)
additional_interfaces: list[AgentInterface] = Field(
default_factory=list,
description="Additional supported interfaces (transport and URL combinations)",
)
security: list[dict[str, list[str]]] = Field(
default_factory=list,
description="Security requirement objects for all agent interactions",
)
security_schemes: dict[str, SecurityScheme] = Field(
default_factory=dict,
description="Security schemes available to authorize requests",
)
supports_authenticated_extended_card: bool = Field(
default=False,
description="Whether agent provides extended card to authenticated users",
)
url: Url | None = Field(
default=None,
description="Preferred endpoint URL for the agent. Set at runtime if not provided.",
)
signatures: list[AgentCardSignature] = Field(
default_factory=list,
description="JSON Web Signatures for the AgentCard",
)
24 changes: 23 additions & 1 deletion lib/crewai/src/crewai/a2a/types.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
"""Type definitions for A2A protocol message parts."""

from typing import Any, Literal, Protocol, TypedDict, runtime_checkable
from __future__ import annotations

from typing import (
Annotated,
Any,
Literal,
Protocol,
TypedDict,
runtime_checkable,
)

from pydantic import BeforeValidator, HttpUrl, TypeAdapter
from typing_extensions import NotRequired

from crewai.a2a.updates import (
Expand All @@ -15,6 +25,18 @@
)


TransportType = Literal["JSONRPC", "GRPC", "HTTP+JSON"]

http_url_adapter: TypeAdapter[HttpUrl] = TypeAdapter(HttpUrl)

Url = Annotated[
str,
BeforeValidator(
lambda value: str(http_url_adapter.validate_python(value, strict=True))
),
]


@runtime_checkable
class AgentResponseProtocol(Protocol):
"""Protocol for the dynamically created AgentResponse model."""
Expand Down
1 change: 1 addition & 0 deletions lib/crewai/src/crewai/a2a/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""A2A utility modules for client operations."""
Loading
Loading