Return type standardization

This commit is contained in:
2026-07-27 00:35:20 -07:00
parent baa48d7cec
commit f0e5267828
8 changed files with 47 additions and 48 deletions
+3 -13
View File
@@ -42,23 +42,13 @@ class MCPServer:
) )
try: try:
result_data = await tool.handler(args) content = await tool.handler(args)
except ToolError as e: except ToolError as e:
# Convert tool errors into a text response so the LLM can understand and potentially fix the input # Convert tool errors into a text response so the LLM can understand and potentially fix the input
result_data = {"text": str(e)} content = [{"type": "text", "text": str(e)}]
except Exception as e: except Exception as e:
# Catch-all for unexpected crashes to prevent server death # Catch-all for unexpected crashes to prevent server death
result_data = {"text": f"Unexpected internal error: {str(e)}"} content = [{"type": "text", "text": f"Unexpected internal error: {str(e)}"}]
# Handle results that are already lists of content (for multimodal/multi-part responses)
if isinstance(result_data, list):
content = result_data
elif isinstance(result_data, dict) and "text" in result_data:
content = [{"type": "text", "text": result_data["text"]}]
elif isinstance(result_data, dict) and result_data.get("type") == "image":
content = [result_data]
else:
content = [result_data]
return {"content": content} return {"content": content}
+29 -20
View File
@@ -3,7 +3,7 @@ from typing import Any, Dict, List
import config import config
from tools.utils import ToolError, load_toml from tools.utils import ToolError, load_toml
async def handle(args: Dict[str, Any]) -> Dict[str, Any]: async def handle(args: Dict[str, Any]) -> List[Dict[str, Any]]:
""" """
Returns information about available models or detailed guides for a specific model. Returns information about available models or detailed guides for a specific model.
""" """
@@ -20,10 +20,13 @@ async def handle(args: Dict[str, Any]) -> Dict[str, Any]:
"name": name, "name": name,
"description": data.get("description", "No description provided.") "description": data.get("description", "No description provided.")
}) })
return { return [
"text": "Available models:\n\n" + "\n".join([f"- {m['name']}: {m['description']}" for m in catalog]) + {
"\n\nTo get a detailed prompting guide and available resolutions for a specific model, call this tool again with the 'model_name' argument." "type": "text",
} "text": "Available models:\n\n" + "\n".join([f"- {m['name']}: {m['description']}" for m in catalog]) +
"\n\nTo get a detailed prompting guide and available resolutions for a specific model, call this tool again with the 'model_name' argument."
}
]
if model_name not in models: if model_name not in models:
# Return the full catalog if the specific model isn't found # Return the full catalog if the specific model isn't found
@@ -33,11 +36,14 @@ async def handle(args: Dict[str, Any]) -> Dict[str, Any]:
"name": name, "name": name,
"description": data.get("description", "No description provided.") "description": data.get("description", "No description provided.")
}) })
return { return [
"text": f"Model '{model_name}' not found.\n\nAvailable models:\n\n" + {
"\n".join([f"- {m['name']}: {m['description']}" for m in catalog]) + "type": "text",
"\n\nTo get a detailed prompting guide and available resolutions for a specific model, call this tool again with the 'model_name' argument." "text": f"Model '{model_name}' not found.\n\nAvailable models:\n\n" +
} "\n".join([f"- {m['name']}: {m['description']}" for m in catalog]) +
"\n\nTo get a detailed prompting guide and available resolutions for a specific model, call this tool again with the 'model_name' argument."
}
]
model_data = models[model_name] model_data = models[model_name]
res_set_name = model_data.get("Resolution Set") res_set_name = model_data.get("Resolution Set")
@@ -58,13 +64,16 @@ async def handle(args: Dict[str, Any]) -> Dict[str, Any]:
preset_text = "\n".join([f"- {k}: {v}" for k, v in presets_to_show.items()]) preset_text = "\n".join([f"- {k}: {v}" for k, v in presets_to_show.items()])
res_text = ", ".join(res_options) res_text = ", ".join(res_options)
return { return [
"text": ( {
f"Model: {model_name}\n" "type": "text",
f"Description: {description}\n\n" "text": (
f"--- Prompting Guide ---\n{guide}\n\n" f"Model: {model_name}\n"
f"--- Active Presets ---\n{preset_text}\n\n" f"Description: {description}\n\n"
f"--- Available Resolution Presets ---\n{res_text}\n\n" f"--- Prompting Guide ---\n{guide}\n\n"
f"Use 'resolution_preset' in generate_image to choose one of these." f"--- Active Presets ---\n{preset_text}\n\n"
) f"--- Available Resolution Presets ---\n{res_text}\n\n"
} f"Use 'resolution_preset' in generate_image to choose one of these."
)
}
]
+3 -3
View File
@@ -35,7 +35,7 @@ async def handle(args: Dict[str, Any]):
# Case 1: No pattern and no lines provided -> Dump whole file # Case 1: No pattern and no lines provided -> Dump whole file
if not pattern and not lines_str: if not pattern and not lines_str:
output = [f"{i+1}: {line}" for i, line in enumerate(all_lines)] output = [f"{i+1}: {line}" for i, line in enumerate(all_lines)]
return {"text": "".join(output)} return [{"type": "text", "text": "".join(output)}]
# Determine target line numbers (1-based) # Determine target line numbers (1-based)
target_lines: Set[int] = set() target_lines: Set[int] = set()
@@ -61,7 +61,7 @@ async def handle(args: Dict[str, Any]):
raise ToolError(f"Error parsing line indices: {e}") raise ToolError(f"Error parsing line indices: {e}")
if not target_lines: if not target_lines:
return {"text": "No matching lines found."} return [{"type": "text", "text": "No matching lines found."}]
# Expand targets with context # Expand targets with context
lines_to_show: Set[int] = set() lines_to_show: Set[int] = set()
@@ -83,4 +83,4 @@ async def handle(args: Dict[str, Any]):
output.append(f"{ln}: {all_lines[ln-1]}") output.append(f"{ln}: {all_lines[ln-1]}")
last_line = ln last_line = ln
return {"text": "".join(output)} return [{"type": "text", "text": "".join(output)}]
+2 -2
View File
@@ -43,7 +43,7 @@ async def handle(args: Dict[str, Any]):
paged_items = all_items[start_idx:end_idx] paged_items = all_items[start_idx:end_idx]
if not paged_items: if not paged_items:
return {"text": f"No items found on page {page}."} return [{"type": "text", "text": f"No items found on page {page}."}]
output = [] output = []
for item in paged_items: for item in paged_items:
@@ -61,4 +61,4 @@ async def handle(args: Dict[str, Any]):
header = f"Listing of {path_str}\nPage {effective_page} of {total_pages} ({total_items} total items). Showing {start_idx+1}-{min(end_idx, total_items)}." header = f"Listing of {path_str}\nPage {effective_page} of {total_pages} ({total_items} total items). Showing {start_idx+1}-{min(end_idx, total_items)}."
return {"text": f"{header}\n\n" + "\n".join(output)} return [{"type": "text", "text": f"{header}\n\n" + "\n".join(output)}]
+1 -1
View File
@@ -25,6 +25,6 @@ async def handle(args: Dict[str, Any]):
try: try:
data = base64.b64encode(path.read_bytes()).decode("utf-8") data = base64.b64encode(path.read_bytes()).decode("utf-8")
return {"type": "image", "data": data, "mimeType": mime_type} return [{"type": "image", "data": data, "mimeType": mime_type}]
except Exception as e: except Exception as e:
raise ToolError(f"Error reading image: {str(e)}") raise ToolError(f"Error reading image: {str(e)}")
+2 -2
View File
@@ -26,11 +26,11 @@ async def handle(args: Dict[str, Any]):
# Pillow parses PNG text chunks into the .info dictionary # Pillow parses PNG text chunks into the .info dictionary
metadata = img.info metadata = img.info
if not metadata: if not metadata:
return {"text": "No metadata found in this PNG."} return [{"type": "text", "text": "No metadata found in this PNG."}]
# Format as a simple key: value list # Format as a simple key: value list
output = "\n".join([f"{k}: {v}" for k, v in metadata.items()]) output = "\n".join([f"{k}: {v}" for k, v in metadata.items()])
return {"text": f"PNG Metadata:\n{output}"} return [{"type": "text", "text": f"PNG Metadata:\n{output}"}]
except ToolError: except ToolError:
raise raise
except Exception as e: except Exception as e:
+4 -4
View File
@@ -41,7 +41,7 @@ def _load_tags() -> List[Dict[str, Any]]:
return tags return tags
async def handle(args: Dict[str, Any]) -> Dict[str, Any]: async def handle(args: Dict[str, Any]) -> List[Dict[str, Any]]:
""" """
Searches the Danbooru tag database for tags matching a query. Searches the Danbooru tag database for tags matching a query.
Returns the most popular tags including alias matches. Returns the most popular tags including alias matches.
@@ -93,7 +93,7 @@ async def handle(args: Dict[str, Any]) -> Dict[str, Any]:
suggestions_lower = difflib.get_close_matches(query, all_names_lower, n=10, cutoff=0.5) suggestions_lower = difflib.get_close_matches(query, all_names_lower, n=10, cutoff=0.5)
if not suggestions_lower: if not suggestions_lower:
return {"text": f"No tags found matching '{query}'."} return [{"type": "text", "text": f"No tags found matching '{query}'."}]
# Map lowercased suggestions back to original tag objects # Map lowercased suggestions back to original tag objects
suggestions = [] suggestions = []
@@ -105,6 +105,6 @@ async def handle(args: Dict[str, Any]) -> Dict[str, Any]:
type_sfx = get_type_suffix(tag["type"]) type_sfx = get_type_suffix(tag["type"])
suggestions.append(f"{tag['name']} ({count_fmt}){type_sfx}") suggestions.append(f"{tag['name']} ({count_fmt}){type_sfx}")
return {"text": f"No exact matches for '{query}'. Did you mean:\n" + "\n".join(suggestions)} return [{"type": "text", "text": f"No exact matches for '{query}'. Did you mean:\n" + "\n".join(suggestions)}]
return {"text": "Top matches:\n" + "\n".join(results)} return [{"type": "text", "text": "Top matches:\n" + "\n".join(results)}]
+3 -3
View File
@@ -3,7 +3,7 @@ import urllib.parse
import json import json
import re import re
import asyncio import asyncio
from typing import Any, Dict, Optional from typing import Any, Dict, List, Optional
from .utils import ToolError from .utils import ToolError
import config import config
@@ -130,7 +130,7 @@ async def _fetch_toc(title: str) -> Optional[str]:
return "\n".join(lines) return "\n".join(lines)
async def handle(args: Dict[str, Any]) -> Dict[str, Any]: async def handle(args: Dict[str, Any]) -> List[Dict[str, Any]]:
""" """
Main handler for the browse_wikipedia tool. Main handler for the browse_wikipedia tool.
Supports modes: summary, toc, section, and search. Supports modes: summary, toc, section, and search.
@@ -170,4 +170,4 @@ async def handle(args: Dict[str, Any]) -> Dict[str, Any]:
else: else:
raise ToolError(f"Invalid mode '{mode}'. Supported modes: summary, toc, section, search") raise ToolError(f"Invalid mode '{mode}'. Supported modes: summary, toc, section, search")
return {"text": result} return [{"type": "text", "text": result}]