Skip to content

Read the SDK's field names, not only the wire spellings - #96

Merged
jhd3197 merged 1 commit into
mainfrom
dev
Aug 7, 2026
Merged

Read the SDK's field names, not only the wire spellings#96
jhd3197 merged 1 commit into
mainfrom
dev

Conversation

@jhd3197

@jhd3197 jhd3197 commented Aug 6, 2026

Copy link
Copy Markdown
Owner

The bridge read tool.inputSchema and result.isError. Those are MCP's JSON names, but the mcp Python SDK exposes input_schema / is_error as the attributes and keeps the camelCase spellings as pydantic aliases. Against mcp 2.x both reads therefore returned the default, silently:

  • every registered MCP tool was advertised to the model with an empty parameter schema, so a tool that needs arguments looked like it took none — verified against a real stdio server, where echo(message) came through as {"type": "object", "properties": {}}
  • every failed call looked successful, handing the model a failure message formatted as ordinary output

Neither failed loudly, and the tests couldn't catch either: their mocks used the same spelling as the code. _mcp_field() now reads the first present of several names (attribute, then key), and the tests gained a _sdk_tool factory shaped like the SDK's own model. Both new tests fail against the previous code.

The bridge read `tool.inputSchema` and `result.isError`. Those are MCP's
JSON names, but the `mcp` Python SDK exposes `input_schema` / `is_error`
as the attributes and keeps the camelCase spellings as pydantic aliases.
Against mcp 2.x both reads therefore returned the default, silently:

- every registered MCP tool was advertised to the model with an empty
  parameter schema, so a tool that needs arguments looked like it took
  none — verified against a real stdio server, where `echo(message)`
  came through as `{"type": "object", "properties": {}}`
- every failed call looked successful, handing the model a failure
  message formatted as ordinary output

Neither failed loudly, and the tests couldn't catch either: their mocks
used the same spelling as the code. `_mcp_field()` now reads the first
present of several names (attribute, then key), and the tests gained a
`_sdk_tool` factory shaped like the SDK's own model. Both new tests fail
against the previous code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 6, 2026 15:55

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR hardens the MCP bridge against SDK field-name/alias differences (camelCase wire JSON vs snake_case SDK attributes) and adds regression tests to prevent silent schema/error-loss.

Changes:

  • Added _mcp_field helper to read MCP fields via multiple spellings (attribute-first, then dict key).
  • Updated tool registration and tool-call error detection to use _mcp_field for inputSchema/input_schema and isError/is_error.
  • Added tests covering SDK-shaped tool objects and call results.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
tests/test_mcp_bridge.py Adds regression tests and SDK-shaped mocks to validate schema passthrough and error detection under snake_case attributes.
prompture/integrations/mcp_bridge.py Introduces _mcp_field and uses it to support both camelCase and snake_case MCP field spellings.

Comment on lines +79 to +83
if isinstance(obj, dict):
for name in names:
if obj.get(name) is not None:
return obj[name]
return default
@jhd3197
jhd3197 merged commit 46bf9d9 into main Aug 7, 2026
5 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants