Skip to content

MCP adapter should fall back to parsing text-JSON when structuredContent is absent #193

Description

@bokelley

Problem

The MCP adapter in `src/adcp/protocols/mcp.py` raises an error on successful responses that lack `structuredContent`:

```python
if not hasattr(result, "structuredContent") or result.structuredContent is None:
raise ValueError(
f"MCP tool {tool_name} did not return structuredContent. "
f"This SDK requires MCP tools to provide structured responses for successful calls."
)
```

The AdCP reference training agent (the source-of-truth implementation we validate against) does NOT return `structuredContent`. It returns JSON encoded as `text` inside a `TextContent` item:

```python

Typical response from test-agent.adcontextprotocol.org/mcp/ get_adcp_capabilities:

TextContent(type='text', text='{"adcp":{"major_versions":[3],"idempotency":{"replay_ttl_seconds":86400}}, ...}')

structuredContent is None

```

Every successful MCP call against the training agent fails in our SDK today.

Evidence

From a live probe (2026-04-18) — see [upstream issue adcontextprotocol/adcp#XXXX] for the full trace:

```
MCP tool get_adcp_capabilities did not return structuredContent.
Got content: [TextContent(type='text', text='{"adcp":{...}}', ...)]
```

MCP spec permits servers to return structured data either as a dedicated `structuredContent` field (newer) or as JSON inside a `TextContent` (older, still valid). Our adapter is stricter than the spec.

Suggested fix

In `src/adcp/protocols/mcp.py` where we raise for missing `structuredContent`, fall back to parsing the first `TextContent` item as JSON:

```python

After extracting message_text / before the raise:

if not hasattr(result, "structuredContent") or result.structuredContent is None:
# Fall back to text-JSON for servers that don't emit structuredContent
for item in serialized_content:
text = item.get("text") if isinstance(item, dict) else None
if text:
try:
parsed = json.loads(text)
if isinstance(parsed, dict):
data_to_return = parsed
break
except json.JSONDecodeError:
pass
else:
raise ValueError(f"MCP tool {tool_name} returned neither structuredContent nor JSON text")
else:
data_to_return = result.structuredContent
```

Impact

Without this fix, the Python SDK cannot talk to the AdCP reference implementation (training agent) end-to-end. Integration tests against the hosted test-agent fail at every successful call.

Workaround (used in live idempotency test today)

Monkey-patch the session at test time to inject a synthetic `structuredContent` built from text content — see `.context/live_idempotency_test.py` on branch `bokelley/issue-181-idempotency`.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions