Skip to content

Fix MCPToolWrapper._create_signature for schemas with optional-before-required properties#383

Open
OICWS wants to merge 2 commits into
andrewyng:mainfrom
OICWS:OICWS/fix-mcp-tool-signature-ordering
Open

Fix MCPToolWrapper._create_signature for schemas with optional-before-required properties#383
OICWS wants to merge 2 commits into
andrewyng:mainfrom
OICWS:OICWS/fix-mcp-tool-signature-ordering

Conversation

@OICWS

@OICWS OICWS commented Jul 24, 2026

Copy link
Copy Markdown

MCPToolWrapper._create_signature raises ValueError for tool schemas with an optional property listed before a required one

Summary

MCPToolWrapper._create_signature (in aisuite/mcp/tool_wrapper.py) builds
the wrapper's inspect.Signature by iterating the tool's JSON Schema
properties in their original declaration order, placing each as a
required (no-default) or optional (default=None) inspect.Parameter
depending on whether it's in the schema's required list — but without
first sorting required parameters ahead of optional ones.

Python's inspect.Signature/def syntax requires every non-default
parameter to precede any default parameter. JSON Schema has no such
ordering constraint — required is just a list, independent of
properties order — so any tool whose schema lists an optional property
before a required one causes construction to fail:

ValueError: non-default argument follows default argument

This is not a rare edge case. It's the pattern used by most interaction
tools in the official @playwright/mcp server (an optional
human-readable element description listed before the required target
element reference), including browser_click, browser_type,
browser_hover, browser_drag, browser_take_screenshot,
browser_evaluate, and browser_select_option.

Impact

MCPClient.get_callable_tools() calls create_mcp_tool_wrapper for every
tool the server reports; when this raises, the calling code (e.g. a
try/except around tool discovery) can end up silently skipping the tool
or the whole server, with no indication to the end user that the failure
is a library bug rather than a misconfigured server. In our case (a
downstream project extending aisuite), connecting to a real
@playwright/mcp server via MCPClient.get_callable_tools() returned
zero tools — every one of the schema-order-affected tools failed to
construct.

Minimal repro

from aisuite.mcp.tool_wrapper import MCPToolWrapper

schema = {
    "name": "browser_click",
    "description": "Click an element",
    "inputSchema": {
        "type": "object",
        "properties": {
            "element": {"type": "string", "description": "Human-readable element"},
            "target": {"type": "string", "description": "Element target ref"},
        },
        "required": ["target"],
    },
}

MCPToolWrapper(mcp_client=None, tool_name="browser_click", tool_schema=schema)
# ValueError: non-default argument follows default argument

Suggested fix

Sort parameters so required ones come first (stable otherwise) before
constructing the inspect.Signature, e.g. in _create_signature:

def _create_signature(self, input_schema: Dict[str, Any]) -> inspect.Signature:
    properties = input_schema.get("properties", {})
    required = input_schema.get("required", [])

    parameters = []
    for param_name, annotation in sorted(
        self.__annotations__.items(), key=lambda item: item[0] not in required
    ):
        if param_name in required:
            param = inspect.Parameter(
                param_name,
                inspect.Parameter.POSITIONAL_OR_KEYWORD,
                annotation=annotation,
            )
        else:
            param = inspect.Parameter(
                param_name,
                inspect.Parameter.POSITIONAL_OR_KEYWORD,
                default=None,
                annotation=annotation,
            )
        parameters.append(param)

    return inspect.Signature(parameters, return_annotation=Any)

This only changes construction order, not semantics -- MCP tool calls are
always made with keyword arguments, so parameter position doesn't affect
callers.

Environment

  • aisuite version: 0.1.14 (latest published release as of this report)
  • Reproduced against the official @playwright/mcp@latest server (npm),
    version 0.0.78 at time of testing

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.

1 participant