From 857290bf7eda233625fe7b4def9ede565dea01d5 Mon Sep 17 00:00:00 2001 From: boushi1111 <95118141+boushi1111@users.noreply.github.com> Date: Wed, 4 Feb 2026 18:38:40 +0900 Subject: [PATCH 1/3] Fix TypeError when MCP schema type is a list Fixes crash in Gemini native tools with VRChat MCP. --- astrbot/core/agent/tool.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/astrbot/core/agent/tool.py b/astrbot/core/agent/tool.py index 2ffbd40ca4..aa9ce3a3a9 100644 --- a/astrbot/core/agent/tool.py +++ b/astrbot/core/agent/tool.py @@ -246,6 +246,11 @@ def convert_schema(schema: dict) -> dict: result = {} + # 修复:如果 type 是列表 (e.g. ["string", "null"]),取第一个有效类型 + if "type" in schema and isinstance(schema["type"], list): + # 优先取不是 null 的类型,如果没有则默认为 string + schema["type"] = next((t for t in schema["type"] if t != "null"), "string") + if "type" in schema and schema["type"] in supported_types: result["type"] = schema["type"] if "format" in schema and schema["format"] in supported_formats.get( From a7bd954b70d57c7043188be9204292e88ac6b482 Mon Sep 17 00:00:00 2001 From: boushi1111 <95118141+boushi1111@users.noreply.github.com> Date: Wed, 4 Feb 2026 19:01:31 +0900 Subject: [PATCH 2/3] Refactor: avoid modifying schema in place per feedback --- astrbot/core/agent/tool.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/astrbot/core/agent/tool.py b/astrbot/core/agent/tool.py index aa9ce3a3a9..15c864f4f0 100644 --- a/astrbot/core/agent/tool.py +++ b/astrbot/core/agent/tool.py @@ -246,21 +246,31 @@ def convert_schema(schema: dict) -> dict: result = {} - # 修复:如果 type 是列表 (e.g. ["string", "null"]),取第一个有效类型 - if "type" in schema and isinstance(schema["type"], list): - # 优先取不是 null 的类型,如果没有则默认为 string - schema["type"] = next((t for t in schema["type"] if t != "null"), "string") - - if "type" in schema and schema["type"] in supported_types: - result["type"] = schema["type"] + # Logic refined based on PR feedback (No side effects) + # Get the type value without modifying the original schema dictionary + origin_type = schema.get("type") + target_type = origin_type + + # Handle list types (e.g., ["string", "null"] from VRChat MCP) + if isinstance(origin_type, list): + # Pick the first non-null type. + # If the list contains only unsupported types, it will be handled by the check below. + target_type = next((t for t in origin_type if t != "null"), "string") + + # Check if the resolved type is supported + if target_type in supported_types: + result["type"] = target_type if "format" in schema and schema["format"] in supported_formats.get( result["type"], set(), ): result["format"] = schema["format"] else: + # Fallback for unsupported types (or if target_type was invalid) result["type"] = "null" + # ============================================================ + support_fields = { "title", "description", @@ -289,7 +299,7 @@ def convert_schema(schema: dict) -> dict: result["items"] = convert_schema(schema["items"]) return result - + tools = [] for tool in self.tools: d: dict[str, Any] = {"name": tool.name} From dfe072cf038610838db89e424b86ed12a97cf801 Mon Sep 17 00:00:00 2001 From: boushi1111 <95118141+boushi1111@users.noreply.github.com> Date: Thu, 5 Feb 2026 12:20:27 +0900 Subject: [PATCH 3/3] Fix formatting and cleanup comments --- astrbot/core/agent/tool.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/astrbot/core/agent/tool.py b/astrbot/core/agent/tool.py index 15c864f4f0..50899ff80b 100644 --- a/astrbot/core/agent/tool.py +++ b/astrbot/core/agent/tool.py @@ -246,18 +246,16 @@ def convert_schema(schema: dict) -> dict: result = {} - # Logic refined based on PR feedback (No side effects) - # Get the type value without modifying the original schema dictionary + # Avoid side effects by not modifying the original schema origin_type = schema.get("type") target_type = origin_type - # Handle list types (e.g., ["string", "null"] from VRChat MCP) + # Compatibility fix: Gemini API expects 'type' to be a string (enum), + # but standard JSON Schema (MCP) allows lists (e.g. ["string", "null"]). + # We fallback to the first non-null type. if isinstance(origin_type, list): - # Pick the first non-null type. - # If the list contains only unsupported types, it will be handled by the check below. target_type = next((t for t in origin_type if t != "null"), "string") - # Check if the resolved type is supported if target_type in supported_types: result["type"] = target_type if "format" in schema and schema["format"] in supported_formats.get( @@ -266,11 +264,8 @@ def convert_schema(schema: dict) -> dict: ): result["format"] = schema["format"] else: - # Fallback for unsupported types (or if target_type was invalid) result["type"] = "null" - # ============================================================ - support_fields = { "title", "description", @@ -299,7 +294,7 @@ def convert_schema(schema: dict) -> dict: result["items"] = convert_schema(schema["items"]) return result - + tools = [] for tool in self.tools: d: dict[str, Any] = {"name": tool.name}