adcp 1.0.1__py3-none-any.whl → 1.0.3__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.
@@ -0,0 +1,119 @@
1
+ from __future__ import annotations
2
+
3
+ """Utilities for parsing protocol responses into structured types."""
4
+
5
+ import json
6
+ import logging
7
+ from typing import Any, TypeVar
8
+
9
+ from pydantic import BaseModel, ValidationError
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ T = TypeVar("T", bound=BaseModel)
14
+
15
+
16
+ def parse_mcp_content(content: list[dict[str, Any]], response_type: type[T]) -> T:
17
+ """
18
+ Parse MCP content array into structured response type.
19
+
20
+ MCP tools return content as a list of content items:
21
+ [{"type": "text", "text": "..."}, {"type": "resource", ...}]
22
+
23
+ For AdCP, we expect JSON data in text content items.
24
+
25
+ Args:
26
+ content: MCP content array
27
+ response_type: Expected Pydantic model type
28
+
29
+ Returns:
30
+ Parsed and validated response object
31
+
32
+ Raises:
33
+ ValueError: If content cannot be parsed into expected type
34
+ """
35
+ if not content:
36
+ raise ValueError("Empty MCP content array")
37
+
38
+ # Look for text content items that might contain JSON
39
+ for item in content:
40
+ if item.get("type") == "text":
41
+ text = item.get("text", "")
42
+ if not text:
43
+ continue
44
+
45
+ try:
46
+ # Try parsing as JSON
47
+ data = json.loads(text)
48
+ # Validate against expected schema
49
+ return response_type.model_validate(data)
50
+ except json.JSONDecodeError:
51
+ # Not JSON, try next item
52
+ continue
53
+ except ValidationError as e:
54
+ logger.warning(
55
+ f"MCP content doesn't match expected schema {response_type.__name__}: {e}"
56
+ )
57
+ raise ValueError(f"MCP response doesn't match expected schema: {e}") from e
58
+ elif item.get("type") == "resource":
59
+ # Resource content might have structured data
60
+ try:
61
+ return response_type.model_validate(item)
62
+ except ValidationError:
63
+ # Try next item
64
+ continue
65
+
66
+ # If we get here, no content item could be parsed
67
+ # Include content preview for debugging (first 2 items, max 500 chars each)
68
+ content_preview = json.dumps(content[:2], indent=2, default=str)
69
+ if len(content_preview) > 500:
70
+ content_preview = content_preview[:500] + "..."
71
+
72
+ raise ValueError(
73
+ f"No valid {response_type.__name__} data found in MCP content. "
74
+ f"Content types: {[item.get('type') for item in content]}. "
75
+ f"Content preview:\n{content_preview}"
76
+ )
77
+
78
+
79
+ def parse_json_or_text(data: Any, response_type: type[T]) -> T:
80
+ """
81
+ Parse data that might be JSON string, dict, or other format.
82
+
83
+ Used by A2A adapter for flexible response parsing.
84
+
85
+ Args:
86
+ data: Response data (string, dict, or other)
87
+ response_type: Expected Pydantic model type
88
+
89
+ Returns:
90
+ Parsed and validated response object
91
+
92
+ Raises:
93
+ ValueError: If data cannot be parsed into expected type
94
+ """
95
+ # If already a dict, try direct validation
96
+ if isinstance(data, dict):
97
+ try:
98
+ return response_type.model_validate(data)
99
+ except ValidationError as e:
100
+ raise ValueError(
101
+ f"Response doesn't match expected schema {response_type.__name__}: {e}"
102
+ ) from e
103
+
104
+ # If string, try JSON parsing
105
+ if isinstance(data, str):
106
+ try:
107
+ parsed = json.loads(data)
108
+ return response_type.model_validate(parsed)
109
+ except json.JSONDecodeError as e:
110
+ raise ValueError(f"Response is not valid JSON: {e}") from e
111
+ except ValidationError as e:
112
+ raise ValueError(
113
+ f"Response doesn't match expected schema {response_type.__name__}: {e}"
114
+ ) from e
115
+
116
+ # Unsupported type
117
+ raise ValueError(
118
+ f"Cannot parse response of type {type(data).__name__} into {response_type.__name__}"
119
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: adcp
3
- Version: 1.0.1
3
+ Version: 1.0.3
4
4
  Summary: Official Python client for the Ad Context Protocol (AdCP)
5
5
  Author-email: AdCP Community <maintainers@adcontextprotocol.org>
6
6
  License: Apache-2.0
@@ -0,0 +1,22 @@
1
+ adcp/__init__.py,sha256=GtTKciZQ6kfkQlsM1s87zUIpkgRAU6bLfbEp2pSdBds,2512
2
+ adcp/__main__.py,sha256=4rjwH3i52Dsy-Pb0hw5hapQuEG8-bW2tTKKB0HsVsSs,12716
3
+ adcp/client.py,sha256=iIZUy5j25H48gGYlR_VuVsB9zP6SF1HtRssdPs2VJkc,24232
4
+ adcp/config.py,sha256=Vsy7ZPOI8G3fB_i5Nk-CHbC7wdasCUWuKlos0fwA0kY,2017
5
+ adcp/exceptions.py,sha256=dNRMKV23DlkGKyB9Xmt6MtlhvDu1crjzD_en4nAEwDY,4399
6
+ adcp/protocols/__init__.py,sha256=6UFwACQ0QadBUzy17wUROHqsJDp8ztPW2jzyl53Zh_g,262
7
+ adcp/protocols/a2a.py,sha256=TN26ac98h2NUZTTs39Tyd6pVoS3k-sASuLKhLpdYV-A,12255
8
+ adcp/protocols/base.py,sha256=Tdxg1hefWVDvRP7q3yYbtPlIfUr01luPG0oT773qi30,4906
9
+ adcp/protocols/mcp.py,sha256=zdnW3RxjEUwWm7jbTzs73dfVywCq3f7fyhwND3w-f54,13081
10
+ adcp/types/__init__.py,sha256=3E_TJUXqQQFcjmSZZSPLwqBP3s_ijsH2LDeuOU-MP30,402
11
+ adcp/types/core.py,sha256=w6CLD2K0riAHUnrktYmQV7qnkO-6Ab4-CN67YSagAE4,4731
12
+ adcp/types/generated.py,sha256=KoILEa5Gg0tsjMYoqDEWulKvPzS0333L3qj75AHr4yI,50855
13
+ adcp/types/tasks.py,sha256=Ae9TSwG2F7oWXTcl4TvLhAzinbQkHNGF1Pc0q8RMNNM,23424
14
+ adcp/utils/__init__.py,sha256=uetvSJB19CjQbtwEYZiTnumJG11GsafQmXm5eR3hL7E,153
15
+ adcp/utils/operation_id.py,sha256=wQX9Bb5epXzRq23xoeYPTqzu5yLuhshg7lKJZihcM2k,294
16
+ adcp/utils/response_parser.py,sha256=s7gEfXwPbvYTSoGqkx0Lp3v9uJ9HlWC2BSic3nO4ZnM,3909
17
+ adcp-1.0.3.dist-info/licenses/LICENSE,sha256=PF39NR3Ae8PLgBhg3Uxw6ju7iGVIf8hfv9LRWQdii_U,629
18
+ adcp-1.0.3.dist-info/METADATA,sha256=nqAcBHeBflsdVV2QNlqIeq93B53PZqQ9DNCKBhJOdGw,12724
19
+ adcp-1.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
20
+ adcp-1.0.3.dist-info/entry_points.txt,sha256=DQKpcGsJX8DtVI_SGApQ7tNvqUB4zkTLaTAEpFgmi3U,44
21
+ adcp-1.0.3.dist-info/top_level.txt,sha256=T1_NF0GefncFU9v_k56oDwKSJREyCqIM8lAwNZf0EOs,5
22
+ adcp-1.0.3.dist-info/RECORD,,
@@ -1,21 +0,0 @@
1
- adcp/__init__.py,sha256=8vcP5vvkGKAn6ESlbFDzTZczzyZmaJ__DjVVx_TlBBY,2512
2
- adcp/__main__.py,sha256=MR7hiJu0cM7wkBAMpJ9wq9IXWFjWeD5gEOvJOJawnW8,8900
3
- adcp/client.py,sha256=S1Qe_4wYt_A7T6ltK_6Cnx_9gr_I23brjSSHC00bcVM,20885
4
- adcp/config.py,sha256=Vsy7ZPOI8G3fB_i5Nk-CHbC7wdasCUWuKlos0fwA0kY,2017
5
- adcp/exceptions.py,sha256=dNRMKV23DlkGKyB9Xmt6MtlhvDu1crjzD_en4nAEwDY,4399
6
- adcp/protocols/__init__.py,sha256=6UFwACQ0QadBUzy17wUROHqsJDp8ztPW2jzyl53Zh_g,262
7
- adcp/protocols/a2a.py,sha256=c5PZ1SlZe2TPzx-eFu-RH9P-h98CW4F5PBdgsU5k8HE,10281
8
- adcp/protocols/base.py,sha256=fG4tk4UMVSkBE3zv5b7bPEOSEXOCTI2oFyy9V6H406Y,1183
9
- adcp/protocols/mcp.py,sha256=Oo0i7jyTnmQPoLeKuP6YBCmCzi1WazUDf3SzxMAU6lA,11163
10
- adcp/types/__init__.py,sha256=3E_TJUXqQQFcjmSZZSPLwqBP3s_ijsH2LDeuOU-MP30,402
11
- adcp/types/core.py,sha256=w6CLD2K0riAHUnrktYmQV7qnkO-6Ab4-CN67YSagAE4,4731
12
- adcp/types/generated.py,sha256=5vbr113-rhNJSMePAEvQSPtqqEqiloVw1F8BeT4tk3o,49973
13
- adcp/types/tasks.py,sha256=iwNoV-1_BxH3PZxmfg2rgZHDUN-o_Ob7zgPNbKtyNPM,21553
14
- adcp/utils/__init__.py,sha256=uetvSJB19CjQbtwEYZiTnumJG11GsafQmXm5eR3hL7E,153
15
- adcp/utils/operation_id.py,sha256=wQX9Bb5epXzRq23xoeYPTqzu5yLuhshg7lKJZihcM2k,294
16
- adcp-1.0.1.dist-info/licenses/LICENSE,sha256=PF39NR3Ae8PLgBhg3Uxw6ju7iGVIf8hfv9LRWQdii_U,629
17
- adcp-1.0.1.dist-info/METADATA,sha256=Kc-phbnBzt8fV4aQIze_VRNXogyBbnwanrKIwJD8iFE,12724
18
- adcp-1.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
19
- adcp-1.0.1.dist-info/entry_points.txt,sha256=DQKpcGsJX8DtVI_SGApQ7tNvqUB4zkTLaTAEpFgmi3U,44
20
- adcp-1.0.1.dist-info/top_level.txt,sha256=T1_NF0GefncFU9v_k56oDwKSJREyCqIM8lAwNZf0EOs,5
21
- adcp-1.0.1.dist-info/RECORD,,
File without changes