Search Topic Images
search_topic_imagesFind educational diagrams and illustrations for CAIE topics to get visual explanations with image URLs, titles, and source domains.
Instructions
Find educational diagrams and illustrations for a CAIE topic.
Returns external web images (Wikipedia, GFG diagrams) — NOT past paper images. For actual question/mark scheme images, use get_questions with include_images=True.
Use this when:
Student needs a visual explanation of a concept (e.g., "show me a binary tree diagram")
Adding supplementary illustrations beyond what exam papers show
Returns: image URL, title, source domain.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | ||
| subject | No | ||
| num_images | No |
Implementation Reference
- mcp_server.py:1365-1434 (handler)The main handler function for the 'search_topic_images' tool. Executes the image search logic: calls the upstream API at /search/images, filters results (skipping Wikipedia thumbnails and SVGs), cleans image titles, and returns curated image URLs with metadata.
def search_topic_images( query: str, subject: Optional[str] = DEFAULT_SUBJECT, num_images: int = 3, ) -> ToolResult: """Find educational diagrams and illustrations for a CAIE topic. Returns external web images (Wikipedia, GFG diagrams) — NOT past paper images. For actual question/mark scheme images, use get_questions with include_images=True. Use this when: - Student needs a visual explanation of a concept (e.g., "show me a binary tree diagram") - Adding supplementary illustrations beyond what exam papers show Returns: image URL, title, source domain. """ params: dict[str, Any] = { "q": query, "num_images": max(1, min(num_images, 10)), } if subject: params["subject"] = subject try: data = _api_get("/search/images", params) except Exception as exc: logger.error("search_topic_images failed: %s", exc) error_payload = _error_from_exception(exc, "/search/images") raise ToolError(error_payload.get("error", {}).get("message", "Image search failed.")) raw_images = data.get("images", []) if isinstance(data, dict) else [] # Clean up image data and filter problematic URLs curated_images: list[dict[str, Any]] = [] for img in raw_images: if not isinstance(img, dict): continue url = img.get("url", "") # Skip Wikipedia thumbnails as they often throw 429 or have CORS issues if "wikimedia.org/wikipedia/commons/thumb" in url or "wikipedia.org" in url: continue # Skip SVGs as they don't render well in some markdown/chat clients if url.lower().endswith(".svg"): continue curated_images.append({ "url": url, "title": _clean_image_title(img.get("title", "")), "source": img.get("source_domain", ""), }) if len(curated_images) >= num_images: break # Build concise text summary content_lines = [f"Found {len(curated_images)} images for '{query}':"] for i, img in enumerate(curated_images, 1): content_lines.append(f"[{i}] {img['title']} — {img['source']}") content_lines.append(f" URL: {img['url']}") if not curated_images: content_lines.append("No images found for this query.") payload = { "ok": True, "query": query, "returned": len(curated_images), "images": curated_images, } return ToolResult(content="\n".join(content_lines), structured_content=payload) - mcp_server.py:1360-1364 (registration)The @mcp.tool decorator registering 'search_topic_images' with title 'Search Topic Images', tags 'search' and 'enhanced', and annotations for read-only/idempotent hints.
@mcp.tool( title="Search Topic Images", tags={"search", "enhanced"}, annotations={"readOnlyHint": True, "idempotentHint": True}, ) - mcp_server.py:1192-1194 (helper)The _clean_image_title helper function used by search_topic_images to clean Wikipedia/GFG image titles by removing 'File:' prefix, file extensions, and site suffixes.
def _clean_image_title(title: str) -> str: - mcp_server.py:188-210 (helper)The _to_image_url helper function used by search_topic_images (indirectly via get_questions) to convert local filesystem image paths to public URLs.
def _to_image_url(path: Any) -> Optional[str]: """Convert a local filesystem image path to a public URL.""" if not path: return None path_str = str(path) # Extract just the filename from any path format filename = path_str.replace("\\", "/").rsplit("/", 1)[-1] if not filename: return None return f"{IMAGE_BASE_URL}/{filename}" def _short_session(session_name: Optional[str]) -> str: s = (session_name or "").strip().lower() if "may" in s: return "MJ" if "oct" in s: return "ON" if "feb" in s: return "FM" return session_name or ""