adcp 1.0.3__py3-none-any.whl → 1.0.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- adcp/__init__.py +1 -1
- adcp/__main__.py +22 -21
- adcp/protocols/base.py +3 -0
- adcp/protocols/mcp.py +65 -4
- adcp/types/core.py +1 -0
- adcp/utils/response_parser.py +4 -1
- {adcp-1.0.3.dist-info → adcp-1.0.5.dist-info}/METADATA +1 -1
- {adcp-1.0.3.dist-info → adcp-1.0.5.dist-info}/RECORD +12 -12
- {adcp-1.0.3.dist-info → adcp-1.0.5.dist-info}/WHEEL +0 -0
- {adcp-1.0.3.dist-info → adcp-1.0.5.dist-info}/entry_points.txt +0 -0
- {adcp-1.0.3.dist-info → adcp-1.0.5.dist-info}/licenses/LICENSE +0 -0
- {adcp-1.0.3.dist-info → adcp-1.0.5.dist-info}/top_level.txt +0 -0
adcp/__init__.py
CHANGED
adcp/__main__.py
CHANGED
|
@@ -23,37 +23,38 @@ from adcp.types.core import AgentConfig, Protocol
|
|
|
23
23
|
|
|
24
24
|
def print_json(data: Any) -> None:
|
|
25
25
|
"""Print data as JSON."""
|
|
26
|
-
|
|
26
|
+
from pydantic import BaseModel
|
|
27
|
+
|
|
28
|
+
# Handle Pydantic models
|
|
29
|
+
if isinstance(data, BaseModel):
|
|
30
|
+
print(data.model_dump_json(indent=2, exclude_none=True))
|
|
31
|
+
else:
|
|
32
|
+
print(json.dumps(data, indent=2, default=str))
|
|
27
33
|
|
|
28
34
|
|
|
29
35
|
def print_result(result: Any, json_output: bool = False) -> None:
|
|
30
36
|
"""Print result in formatted or JSON mode."""
|
|
31
37
|
if json_output:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"metadata": result.metadata,
|
|
39
|
-
"debug_info": (
|
|
40
|
-
{
|
|
41
|
-
"request": result.debug_info.request,
|
|
42
|
-
"response": result.debug_info.response,
|
|
43
|
-
"duration_ms": result.debug_info.duration_ms,
|
|
44
|
-
}
|
|
45
|
-
if result.debug_info
|
|
46
|
-
else None
|
|
47
|
-
),
|
|
48
|
-
}
|
|
49
|
-
)
|
|
38
|
+
# Match JavaScript client: output just the data for scripting
|
|
39
|
+
if result.success and result.data:
|
|
40
|
+
print_json(result.data)
|
|
41
|
+
else:
|
|
42
|
+
# On error, output error info
|
|
43
|
+
print_json({"error": result.error, "success": False})
|
|
50
44
|
else:
|
|
51
|
-
|
|
45
|
+
# Pretty output with message and data (like JavaScript client)
|
|
52
46
|
if result.success:
|
|
47
|
+
print("\nSUCCESS\n")
|
|
48
|
+
# Show protocol message if available
|
|
49
|
+
if hasattr(result, "message") and result.message:
|
|
50
|
+
print("Protocol Message:")
|
|
51
|
+
print(result.message)
|
|
52
|
+
print()
|
|
53
53
|
if result.data:
|
|
54
|
-
print("
|
|
54
|
+
print("Response:")
|
|
55
55
|
print_json(result.data)
|
|
56
56
|
else:
|
|
57
|
+
print("\nFAILED\n")
|
|
57
58
|
print(f"Error: {result.error}")
|
|
58
59
|
|
|
59
60
|
|
adcp/protocols/base.py
CHANGED
|
@@ -49,6 +49,7 @@ class ProtocolAdapter(ABC):
|
|
|
49
49
|
return TaskResult[T](
|
|
50
50
|
status=raw_result.status,
|
|
51
51
|
data=None,
|
|
52
|
+
message=raw_result.message,
|
|
52
53
|
success=False,
|
|
53
54
|
error=raw_result.error or "No data returned from adapter",
|
|
54
55
|
metadata=raw_result.metadata,
|
|
@@ -66,6 +67,7 @@ class ProtocolAdapter(ABC):
|
|
|
66
67
|
return TaskResult[T](
|
|
67
68
|
status=raw_result.status,
|
|
68
69
|
data=parsed_data,
|
|
70
|
+
message=raw_result.message, # Preserve human-readable message from protocol
|
|
69
71
|
success=raw_result.success,
|
|
70
72
|
error=raw_result.error,
|
|
71
73
|
metadata=raw_result.metadata,
|
|
@@ -76,6 +78,7 @@ class ProtocolAdapter(ABC):
|
|
|
76
78
|
return TaskResult[T](
|
|
77
79
|
status=TaskStatus.FAILED,
|
|
78
80
|
error=f"Failed to parse response: {e}",
|
|
81
|
+
message=raw_result.message,
|
|
79
82
|
success=False,
|
|
80
83
|
debug_info=raw_result.debug_info,
|
|
81
84
|
)
|
adcp/protocols/mcp.py
CHANGED
|
@@ -186,6 +186,40 @@ class MCPAdapter(ProtocolAdapter):
|
|
|
186
186
|
else:
|
|
187
187
|
raise ValueError(f"Unsupported transport scheme: {parsed.scheme}")
|
|
188
188
|
|
|
189
|
+
def _serialize_mcp_content(self, content: list[Any]) -> list[dict[str, Any]]:
|
|
190
|
+
"""
|
|
191
|
+
Convert MCP SDK content objects to plain dicts.
|
|
192
|
+
|
|
193
|
+
The MCP SDK returns Pydantic objects (TextContent, ImageContent, etc.)
|
|
194
|
+
but the rest of the ADCP client expects protocol-agnostic dicts.
|
|
195
|
+
This method handles the translation at the protocol boundary.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
content: List of MCP content items (may be dicts or Pydantic objects)
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
List of plain dicts representing the content
|
|
202
|
+
"""
|
|
203
|
+
result = []
|
|
204
|
+
for item in content:
|
|
205
|
+
# Already a dict, pass through
|
|
206
|
+
if isinstance(item, dict):
|
|
207
|
+
result.append(item)
|
|
208
|
+
# Pydantic v2 model with model_dump()
|
|
209
|
+
elif hasattr(item, "model_dump"):
|
|
210
|
+
result.append(item.model_dump())
|
|
211
|
+
# Pydantic v1 model with dict()
|
|
212
|
+
elif hasattr(item, "dict") and callable(item.dict):
|
|
213
|
+
result.append(item.dict())
|
|
214
|
+
# Fallback: try to access __dict__
|
|
215
|
+
elif hasattr(item, "__dict__"):
|
|
216
|
+
result.append(dict(item.__dict__))
|
|
217
|
+
# Last resort: serialize as unknown type
|
|
218
|
+
else:
|
|
219
|
+
logger.warning(f"Unknown MCP content type: {type(item)}, serializing as string")
|
|
220
|
+
result.append({"type": "unknown", "data": str(item)})
|
|
221
|
+
return result
|
|
222
|
+
|
|
189
223
|
async def _call_mcp_tool(self, tool_name: str, params: dict[str, Any]) -> TaskResult[Any]:
|
|
190
224
|
"""Call a tool using MCP protocol."""
|
|
191
225
|
start_time = time.time() if self.agent_config.debug else None
|
|
@@ -205,22 +239,49 @@ class MCPAdapter(ProtocolAdapter):
|
|
|
205
239
|
# Call the tool using MCP client session
|
|
206
240
|
result = await session.call_tool(tool_name, params)
|
|
207
241
|
|
|
242
|
+
# This SDK requires MCP tools to return structuredContent
|
|
243
|
+
# The content field may contain human-readable messages but the actual
|
|
244
|
+
# response data must be in structuredContent
|
|
245
|
+
if not hasattr(result, "structuredContent") or result.structuredContent is None:
|
|
246
|
+
raise ValueError(
|
|
247
|
+
f"MCP tool {tool_name} did not return structuredContent. "
|
|
248
|
+
f"This SDK requires MCP tools to provide structured responses. "
|
|
249
|
+
f"Got content: {result.content if hasattr(result, 'content') else 'none'}"
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
# Extract the structured data (required)
|
|
253
|
+
data_to_return = result.structuredContent
|
|
254
|
+
|
|
255
|
+
# Extract human-readable message from content (optional)
|
|
256
|
+
# This is typically a status message like "Found 42 creative formats"
|
|
257
|
+
message_text = None
|
|
258
|
+
if hasattr(result, "content") and result.content:
|
|
259
|
+
# Serialize content using the same method used for backward compatibility
|
|
260
|
+
serialized_content = self._serialize_mcp_content(result.content)
|
|
261
|
+
if isinstance(serialized_content, list):
|
|
262
|
+
for item in serialized_content:
|
|
263
|
+
is_text = isinstance(item, dict) and item.get("type") == "text"
|
|
264
|
+
if is_text and item.get("text"):
|
|
265
|
+
message_text = item["text"]
|
|
266
|
+
break
|
|
267
|
+
|
|
208
268
|
if self.agent_config.debug and start_time:
|
|
209
269
|
duration_ms = (time.time() - start_time) * 1000
|
|
210
270
|
debug_info = DebugInfo(
|
|
211
271
|
request=debug_request,
|
|
212
272
|
response={
|
|
213
|
-
"
|
|
273
|
+
"data": data_to_return,
|
|
274
|
+
"message": message_text,
|
|
214
275
|
"is_error": result.isError if hasattr(result, "isError") else False,
|
|
215
276
|
},
|
|
216
277
|
duration_ms=duration_ms,
|
|
217
278
|
)
|
|
218
279
|
|
|
219
|
-
#
|
|
220
|
-
# For AdCP, we expect the data in the content
|
|
280
|
+
# Return both the structured data and the human-readable message
|
|
221
281
|
return TaskResult[Any](
|
|
222
282
|
status=TaskStatus.COMPLETED,
|
|
223
|
-
data=
|
|
283
|
+
data=data_to_return,
|
|
284
|
+
message=message_text,
|
|
224
285
|
success=True,
|
|
225
286
|
debug_info=debug_info,
|
|
226
287
|
)
|
adcp/types/core.py
CHANGED
|
@@ -127,6 +127,7 @@ class TaskResult(BaseModel, Generic[T]):
|
|
|
127
127
|
|
|
128
128
|
status: TaskStatus
|
|
129
129
|
data: T | None = None
|
|
130
|
+
message: str | None = None # Human-readable message from agent (e.g., MCP content text)
|
|
130
131
|
submitted: SubmittedInfo | None = None
|
|
131
132
|
needs_input: NeedsInputInfo | None = None
|
|
132
133
|
error: str | None = None
|
adcp/utils/response_parser.py
CHANGED
|
@@ -20,10 +20,13 @@ def parse_mcp_content(content: list[dict[str, Any]], response_type: type[T]) ->
|
|
|
20
20
|
MCP tools return content as a list of content items:
|
|
21
21
|
[{"type": "text", "text": "..."}, {"type": "resource", ...}]
|
|
22
22
|
|
|
23
|
+
The MCP adapter is responsible for serializing MCP SDK Pydantic objects
|
|
24
|
+
to plain dicts before calling this function.
|
|
25
|
+
|
|
23
26
|
For AdCP, we expect JSON data in text content items.
|
|
24
27
|
|
|
25
28
|
Args:
|
|
26
|
-
content: MCP content array
|
|
29
|
+
content: MCP content array (list of plain dicts)
|
|
27
30
|
response_type: Expected Pydantic model type
|
|
28
31
|
|
|
29
32
|
Returns:
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
adcp/__init__.py,sha256=
|
|
2
|
-
adcp/__main__.py,sha256=
|
|
1
|
+
adcp/__init__.py,sha256=e7jeHMFSk_DxjaIWgRGqCR3A0fTOKtE-sMgulASLCWM,2512
|
|
2
|
+
adcp/__main__.py,sha256=Avy_C71rruh2lOuojvuXDj09tkFOaek74nJ-dbx25Sw,12838
|
|
3
3
|
adcp/client.py,sha256=iIZUy5j25H48gGYlR_VuVsB9zP6SF1HtRssdPs2VJkc,24232
|
|
4
4
|
adcp/config.py,sha256=Vsy7ZPOI8G3fB_i5Nk-CHbC7wdasCUWuKlos0fwA0kY,2017
|
|
5
5
|
adcp/exceptions.py,sha256=dNRMKV23DlkGKyB9Xmt6MtlhvDu1crjzD_en4nAEwDY,4399
|
|
6
6
|
adcp/protocols/__init__.py,sha256=6UFwACQ0QadBUzy17wUROHqsJDp8ztPW2jzyl53Zh_g,262
|
|
7
7
|
adcp/protocols/a2a.py,sha256=TN26ac98h2NUZTTs39Tyd6pVoS3k-sASuLKhLpdYV-A,12255
|
|
8
|
-
adcp/protocols/base.py,sha256=
|
|
9
|
-
adcp/protocols/mcp.py,sha256=
|
|
8
|
+
adcp/protocols/base.py,sha256=CGqUilQv_ymhnfdowBV_HJhIxYUDM3sRO7ahW-kRB0M,5087
|
|
9
|
+
adcp/protocols/mcp.py,sha256=iQTr5QkZbUj42cc_ca7esvcV4EgcOpI8IT6akut9-UA,16028
|
|
10
10
|
adcp/types/__init__.py,sha256=3E_TJUXqQQFcjmSZZSPLwqBP3s_ijsH2LDeuOU-MP30,402
|
|
11
|
-
adcp/types/core.py,sha256=
|
|
11
|
+
adcp/types/core.py,sha256=BO6188PI8lIiVjpiSR0pmsCuXq3Tg9KlkHC5N2fXFxw,4824
|
|
12
12
|
adcp/types/generated.py,sha256=KoILEa5Gg0tsjMYoqDEWulKvPzS0333L3qj75AHr4yI,50855
|
|
13
13
|
adcp/types/tasks.py,sha256=Ae9TSwG2F7oWXTcl4TvLhAzinbQkHNGF1Pc0q8RMNNM,23424
|
|
14
14
|
adcp/utils/__init__.py,sha256=uetvSJB19CjQbtwEYZiTnumJG11GsafQmXm5eR3hL7E,153
|
|
15
15
|
adcp/utils/operation_id.py,sha256=wQX9Bb5epXzRq23xoeYPTqzu5yLuhshg7lKJZihcM2k,294
|
|
16
|
-
adcp/utils/response_parser.py,sha256=
|
|
17
|
-
adcp-1.0.
|
|
18
|
-
adcp-1.0.
|
|
19
|
-
adcp-1.0.
|
|
20
|
-
adcp-1.0.
|
|
21
|
-
adcp-1.0.
|
|
22
|
-
adcp-1.0.
|
|
16
|
+
adcp/utils/response_parser.py,sha256=NQTLlbvmnM_tE4B5w3oB1Wshny1p-Uh8IWbghlwoNJc,4057
|
|
17
|
+
adcp-1.0.5.dist-info/licenses/LICENSE,sha256=PF39NR3Ae8PLgBhg3Uxw6ju7iGVIf8hfv9LRWQdii_U,629
|
|
18
|
+
adcp-1.0.5.dist-info/METADATA,sha256=pmJWpMYNFVbyME-LpWm_Q9xFe3adBpivni17xNE2yaY,12724
|
|
19
|
+
adcp-1.0.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
20
|
+
adcp-1.0.5.dist-info/entry_points.txt,sha256=DQKpcGsJX8DtVI_SGApQ7tNvqUB4zkTLaTAEpFgmi3U,44
|
|
21
|
+
adcp-1.0.5.dist-info/top_level.txt,sha256=T1_NF0GefncFU9v_k56oDwKSJREyCqIM8lAwNZf0EOs,5
|
|
22
|
+
adcp-1.0.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|