From 43785a18ec70a2c61b0df00299e00d36adf8b68f Mon Sep 17 00:00:00 2001 From: ygd58 Date: Sun, 29 Mar 2026 11:16:23 +0200 Subject: [PATCH 1/2] fix(mcp): coerce JSON-encoded string params to native types before call_tool --- tools/mcp_tool.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tools/mcp_tool.py b/tools/mcp_tool.py index 2b68ff4bfc..eafb546696 100644 --- a/tools/mcp_tool.py +++ b/tools/mcp_tool.py @@ -1009,6 +1009,26 @@ def _make_tool_handler(server_name: str, tool_name: str, tool_timeout: float): ``handler(args_dict, **kwargs) -> str`` """ + def _coerce_json_strings(args: dict) -> dict: + """Coerce string values that are valid JSON into native types. + + LLMs sometimes emit array/bool/number params as JSON-encoded strings. + MCP servers validate against inputSchema and reject string where array/bool/int expected. + """ + coerced = {} + for k, v in args.items(): + if isinstance(v, str): + try: + parsed = json.loads(v) + # Only coerce if result is a non-string type (list, dict, bool, int, float) + if not isinstance(parsed, str): + coerced[k] = parsed + continue + except (json.JSONDecodeError, ValueError): + pass + coerced[k] = v + return coerced + def _handler(args: dict, **kwargs) -> str: with _lock: server = _servers.get(server_name) @@ -1018,7 +1038,7 @@ def _handler(args: dict, **kwargs) -> str: }) async def _call(): - result = await server.session.call_tool(tool_name, arguments=args) + result = await server.session.call_tool(tool_name, arguments=_coerce_json_strings(args)) # MCP CallToolResult has .content (list of content blocks) and .isError if result.isError: error_text = "" From e55fbabe3d30930a7d13cda304d8aee00352b020 Mon Sep 17 00:00:00 2001 From: ygd58 Date: Sun, 29 Mar 2026 11:19:46 +0200 Subject: [PATCH 2/2] fix(mcp): skip utility tools when server capabilities don't advertise them --- tools/mcp_tool.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tools/mcp_tool.py b/tools/mcp_tool.py index eafb546696..f7792d3b8d 100644 --- a/tools/mcp_tool.py +++ b/tools/mcp_tool.py @@ -1489,6 +1489,19 @@ def _select_utility_schemas(server_name: str, server: MCPServerTask, config: dic required_method, ) continue + # Some servers (e.g. gitmcp) have the method on the session object but + # return "Method not found" at the transport layer. Only register if + # the server advertises the capability in its server_info. + server_info = getattr(server.session, "server_info", None) or getattr(server, "_server_info", None) + if server_info is not None: + capabilities = getattr(server_info, "capabilities", None) or {} + if isinstance(capabilities, dict): + if handler_key in {"list_resources", "read_resource"} and not capabilities.get("resources"): + logger.debug("MCP server '%s': skipping '%s' (server capabilities missing 'resources')", server_name, handler_key) + continue + if handler_key in {"list_prompts", "get_prompt"} and not capabilities.get("prompts"): + logger.debug("MCP server '%s': skipping '%s' (server capabilities missing 'prompts')", server_name, handler_key) + continue selected.append(entry) return selected