adcp 1.4.0__tar.gz → 1.4.1__tar.gz
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-1.4.0/src/adcp.egg-info → adcp-1.4.1}/PKG-INFO +1 -1
- {adcp-1.4.0 → adcp-1.4.1}/pyproject.toml +1 -1
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp/__init__.py +1 -1
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp/protocols/mcp.py +38 -16
- {adcp-1.4.0 → adcp-1.4.1/src/adcp.egg-info}/PKG-INFO +1 -1
- {adcp-1.4.0 → adcp-1.4.1}/tests/test_protocols.py +27 -3
- {adcp-1.4.0 → adcp-1.4.1}/LICENSE +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/README.md +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/setup.cfg +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp/__main__.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp/client.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp/config.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp/exceptions.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp/protocols/__init__.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp/protocols/a2a.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp/protocols/base.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp/simple.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp/testing/__init__.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp/testing/test_helpers.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp/types/__init__.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp/types/core.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp/types/generated.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp/types/tasks.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp/utils/__init__.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp/utils/operation_id.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp/utils/preview_cache.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp/utils/response_parser.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp.egg-info/SOURCES.txt +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp.egg-info/dependency_links.txt +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp.egg-info/entry_points.txt +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp.egg-info/requires.txt +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/src/adcp.egg-info/top_level.txt +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/tests/test_cli.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/tests/test_client.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/tests/test_code_generation.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/tests/test_discriminated_unions.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/tests/test_format_id_validation.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/tests/test_helpers.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/tests/test_preview_html.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/tests/test_response_parser.py +0 -0
- {adcp-1.4.0 → adcp-1.4.1}/tests/test_simple_api.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "adcp"
|
|
7
|
-
version = "1.4.
|
|
7
|
+
version = "1.4.1"
|
|
8
8
|
description = "Official Python client for the Ad Context Protocol (AdCP)"
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "AdCP Community", email = "maintainers@adcontextprotocol.org"}
|
|
@@ -245,24 +245,12 @@ class MCPAdapter(ProtocolAdapter):
|
|
|
245
245
|
# Call the tool using MCP client session
|
|
246
246
|
result = await session.call_tool(tool_name, params)
|
|
247
247
|
|
|
248
|
-
#
|
|
249
|
-
|
|
250
|
-
# response data must be in structuredContent
|
|
251
|
-
if not hasattr(result, "structuredContent") or result.structuredContent is None:
|
|
252
|
-
raise ValueError(
|
|
253
|
-
f"MCP tool {tool_name} did not return structuredContent. "
|
|
254
|
-
f"This SDK requires MCP tools to provide structured responses. "
|
|
255
|
-
f"Got content: {result.content if hasattr(result, 'content') else 'none'}"
|
|
256
|
-
)
|
|
248
|
+
# Check if this is an error response
|
|
249
|
+
is_error = hasattr(result, "isError") and result.isError
|
|
257
250
|
|
|
258
|
-
# Extract
|
|
259
|
-
data_to_return = result.structuredContent
|
|
260
|
-
|
|
261
|
-
# Extract human-readable message from content (optional)
|
|
262
|
-
# This is typically a status message like "Found 42 creative formats"
|
|
251
|
+
# Extract human-readable message from content
|
|
263
252
|
message_text = None
|
|
264
253
|
if hasattr(result, "content") and result.content:
|
|
265
|
-
# Serialize content using the same method used for backward compatibility
|
|
266
254
|
serialized_content = self._serialize_mcp_content(result.content)
|
|
267
255
|
if isinstance(serialized_content, list):
|
|
268
256
|
for item in serialized_content:
|
|
@@ -271,6 +259,40 @@ class MCPAdapter(ProtocolAdapter):
|
|
|
271
259
|
message_text = item["text"]
|
|
272
260
|
break
|
|
273
261
|
|
|
262
|
+
# Handle error responses
|
|
263
|
+
if is_error:
|
|
264
|
+
# For error responses, structuredContent is optional
|
|
265
|
+
# Use the error message from content as the error
|
|
266
|
+
error_message = message_text or "Tool execution failed"
|
|
267
|
+
if self.agent_config.debug and start_time:
|
|
268
|
+
duration_ms = (time.time() - start_time) * 1000
|
|
269
|
+
debug_info = DebugInfo(
|
|
270
|
+
request=debug_request,
|
|
271
|
+
response={
|
|
272
|
+
"error": error_message,
|
|
273
|
+
"is_error": True,
|
|
274
|
+
},
|
|
275
|
+
duration_ms=duration_ms,
|
|
276
|
+
)
|
|
277
|
+
return TaskResult[Any](
|
|
278
|
+
status=TaskStatus.FAILED,
|
|
279
|
+
error=error_message,
|
|
280
|
+
success=False,
|
|
281
|
+
debug_info=debug_info,
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
# For successful responses, structuredContent is required
|
|
285
|
+
if not hasattr(result, "structuredContent") or result.structuredContent is None:
|
|
286
|
+
raise ValueError(
|
|
287
|
+
f"MCP tool {tool_name} did not return structuredContent. "
|
|
288
|
+
f"This SDK requires MCP tools to provide structured responses "
|
|
289
|
+
f"for successful calls. "
|
|
290
|
+
f"Got content: {result.content if hasattr(result, 'content') else 'none'}"
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
# Extract the structured data (required for success)
|
|
294
|
+
data_to_return = result.structuredContent
|
|
295
|
+
|
|
274
296
|
if self.agent_config.debug and start_time:
|
|
275
297
|
duration_ms = (time.time() - start_time) * 1000
|
|
276
298
|
debug_info = DebugInfo(
|
|
@@ -278,7 +300,7 @@ class MCPAdapter(ProtocolAdapter):
|
|
|
278
300
|
response={
|
|
279
301
|
"data": data_to_return,
|
|
280
302
|
"message": message_text,
|
|
281
|
-
"is_error":
|
|
303
|
+
"is_error": False,
|
|
282
304
|
},
|
|
283
305
|
duration_ms=duration_ms,
|
|
284
306
|
)
|
|
@@ -164,6 +164,7 @@ class TestMCPAdapter:
|
|
|
164
164
|
# Mock MCP result with structuredContent (required for AdCP)
|
|
165
165
|
mock_result.content = [{"type": "text", "text": "Success"}]
|
|
166
166
|
mock_result.structuredContent = {"products": [{"id": "prod1"}]}
|
|
167
|
+
mock_result.isError = False
|
|
167
168
|
mock_session.call_tool.return_value = mock_result
|
|
168
169
|
|
|
169
170
|
with patch.object(adapter, "_get_session", return_value=mock_session):
|
|
@@ -193,6 +194,7 @@ class TestMCPAdapter:
|
|
|
193
194
|
# Mock MCP result with structuredContent (preferred over content)
|
|
194
195
|
mock_result.content = [{"type": "text", "text": "Found 42 creative formats"}]
|
|
195
196
|
mock_result.structuredContent = {"formats": [{"id": "format1"}, {"id": "format2"}]}
|
|
197
|
+
mock_result.isError = False
|
|
196
198
|
mock_session.call_tool.return_value = mock_result
|
|
197
199
|
|
|
198
200
|
with patch.object(adapter, "_get_session", return_value=mock_session):
|
|
@@ -207,24 +209,46 @@ class TestMCPAdapter:
|
|
|
207
209
|
|
|
208
210
|
@pytest.mark.asyncio
|
|
209
211
|
async def test_call_tool_missing_structured_content(self, mcp_config):
|
|
210
|
-
"""Test tool call fails when structuredContent is missing."""
|
|
212
|
+
"""Test tool call fails when structuredContent is missing on successful response."""
|
|
211
213
|
adapter = MCPAdapter(mcp_config)
|
|
212
214
|
|
|
213
215
|
mock_session = AsyncMock()
|
|
214
216
|
mock_result = MagicMock()
|
|
215
|
-
# Mock MCP result WITHOUT structuredContent (invalid
|
|
217
|
+
# Mock MCP result WITHOUT structuredContent and isError=False (invalid)
|
|
216
218
|
mock_result.content = [{"type": "text", "text": "Success"}]
|
|
217
219
|
mock_result.structuredContent = None
|
|
220
|
+
mock_result.isError = False
|
|
218
221
|
mock_session.call_tool.return_value = mock_result
|
|
219
222
|
|
|
220
223
|
with patch.object(adapter, "_get_session", return_value=mock_session):
|
|
221
224
|
result = await adapter._call_mcp_tool("get_products", {"brief": "test"})
|
|
222
225
|
|
|
223
|
-
# Verify error handling for missing structuredContent
|
|
226
|
+
# Verify error handling for missing structuredContent on success
|
|
224
227
|
assert result.success is False
|
|
225
228
|
assert result.status == TaskStatus.FAILED
|
|
226
229
|
assert "did not return structuredContent" in result.error
|
|
227
230
|
|
|
231
|
+
@pytest.mark.asyncio
|
|
232
|
+
async def test_call_tool_error_without_structured_content(self, mcp_config):
|
|
233
|
+
"""Test tool call handles error responses without structuredContent gracefully."""
|
|
234
|
+
adapter = MCPAdapter(mcp_config)
|
|
235
|
+
|
|
236
|
+
mock_session = AsyncMock()
|
|
237
|
+
mock_result = MagicMock()
|
|
238
|
+
# Mock MCP error response WITHOUT structuredContent (valid for errors)
|
|
239
|
+
mock_result.content = [{"type": "text", "text": "brand_manifest must provide brand information"}]
|
|
240
|
+
mock_result.structuredContent = None
|
|
241
|
+
mock_result.isError = True
|
|
242
|
+
mock_session.call_tool.return_value = mock_result
|
|
243
|
+
|
|
244
|
+
with patch.object(adapter, "_get_session", return_value=mock_session):
|
|
245
|
+
result = await adapter._call_mcp_tool("get_products", {"brief": "test"})
|
|
246
|
+
|
|
247
|
+
# Verify error is handled gracefully
|
|
248
|
+
assert result.success is False
|
|
249
|
+
assert result.status == TaskStatus.FAILED
|
|
250
|
+
assert result.error == "brand_manifest must provide brand information"
|
|
251
|
+
|
|
228
252
|
@pytest.mark.asyncio
|
|
229
253
|
async def test_call_tool_error(self, mcp_config):
|
|
230
254
|
"""Test tool call error via MCP."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|