Skip to content

Commit 31881cb

Browse files
bokelleyclaude
andcommitted
fix: use explicit skill invocation for A2A tool calls
Implement explicit skill invocation (deterministic) by default for A2A protocol, as recommended by the A2A specification. This uses DataPart with skill name and parameters instead of natural language interpretation. **Changes:** - Add `use_explicit_skill` parameter to `_call_a2a_tool` (default: True) - Explicit mode: Send DataPart with `{"skill": "...", "parameters": {...}}` - Natural language mode: Send TextPart with formatted text (legacy) - Import DataPart from a2a.types **Benefits:** - Works without authentication for discovery endpoints (test agent) - Predictable, repeatable behavior - Direct skill execution without AI interpretation overhead - Aligns with A2A best practices **Reference:** https://docs.adcontextprotocol.org/docs/protocols/a2a-guide **Testing:** - ✅ list_authorized_properties works without auth - ✅ list_creative_formats works without auth - ✅ get_products validates parameters correctly 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 5cd7e0e commit 31881cb

File tree

1 file changed

+49
-9
lines changed

1 file changed

+49
-9
lines changed

src/adcp/protocols/a2a.py

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,16 @@
99

1010
import httpx
1111
from a2a.client import A2ACardResolver, A2AClient
12-
from a2a.types import Message, MessageSendParams, Part, Role, SendMessageRequest, Task, TextPart
12+
from a2a.types import (
13+
DataPart,
14+
Message,
15+
MessageSendParams,
16+
Part,
17+
Role,
18+
SendMessageRequest,
19+
Task,
20+
TextPart,
21+
)
1322

1423
from adcp.exceptions import (
1524
ADCPAuthenticationError,
@@ -115,19 +124,50 @@ async def close(self) -> None:
115124
self._httpx_client = None
116125
self._a2a_client = None
117126

118-
async def _call_a2a_tool(self, tool_name: str, params: dict[str, Any]) -> TaskResult[Any]:
119-
"""Call a tool using A2A protocol via official a2a-sdk client."""
127+
async def _call_a2a_tool(
128+
self, tool_name: str, params: dict[str, Any], use_explicit_skill: bool = True
129+
) -> TaskResult[Any]:
130+
"""
131+
Call a tool using A2A protocol via official a2a-sdk client.
132+
133+
Args:
134+
tool_name: Name of the skill/tool to invoke
135+
params: Parameters to pass to the skill
136+
use_explicit_skill: If True, use explicit skill invocation (deterministic).
137+
If False, use natural language (flexible).
138+
139+
The default is explicit skill invocation for predictable, repeatable behavior.
140+
See: https://docs.adcontextprotocol.org/docs/protocols/a2a-guide
141+
"""
120142
start_time = time.time() if self.agent_config.debug else None
121143
a2a_client = await self._get_a2a_client()
122144

123145
# Build A2A message
124146
message_id = str(uuid4())
125-
text_part = TextPart(text=self._format_tool_request(tool_name, params))
126-
message = Message(
127-
message_id=message_id,
128-
role=Role.user,
129-
parts=[Part(root=text_part)],
130-
)
147+
148+
if use_explicit_skill:
149+
# Explicit skill invocation (deterministic)
150+
# Use DataPart with skill name and parameters
151+
data_part = DataPart(
152+
data={
153+
"skill": tool_name,
154+
"parameters": params,
155+
}
156+
)
157+
message = Message(
158+
message_id=message_id,
159+
role=Role.user,
160+
parts=[Part(root=data_part)],
161+
)
162+
else:
163+
# Natural language invocation (flexible)
164+
# Agent interprets intent from text
165+
text_part = TextPart(text=self._format_tool_request(tool_name, params))
166+
message = Message(
167+
message_id=message_id,
168+
role=Role.user,
169+
parts=[Part(root=text_part)],
170+
)
131171

132172
# Build request params
133173
params_obj = MessageSendParams(message=message)

0 commit comments

Comments
 (0)