Spaces:
Running
Running
| import gradio as gr | |
| import requests | |
| import json | |
| from typing import Dict, Any, Optional | |
| def get_xkcd_comic(comic_id: str = "") -> str: | |
| """ | |
| Fetch XKCD comic information by ID or get the latest comic. | |
| Args: | |
| comic_id (str): Comic ID number (leave empty for latest comic) | |
| Returns: | |
| str: JSON string containing comic information including title, alt text, and image URL | |
| """ | |
| try: | |
| if comic_id.strip(): | |
| # Get specific comic by ID | |
| url = f"https://xkcd.com/{comic_id.strip()}/info.0.json" | |
| else: | |
| # Get latest comic | |
| url = "https://xkcd.com/info.0.json" | |
| response = requests.get(url, timeout=10) | |
| response.raise_for_status() | |
| comic_data = response.json() | |
| # Format the response nicely | |
| formatted_response = { | |
| "num": comic_data["num"], | |
| "title": comic_data["title"], | |
| "alt": comic_data["alt"], | |
| "img": comic_data["img"], | |
| "year": comic_data["year"], | |
| "month": comic_data["month"], | |
| "day": comic_data["day"], | |
| "transcript": comic_data.get("transcript", ""), | |
| "safe_title": comic_data["safe_title"] | |
| } | |
| return json.dumps(formatted_response, indent=2) | |
| except requests.exceptions.RequestException as e: | |
| return f"Error fetching comic: {str(e)}" | |
| except KeyError as e: | |
| return f"Error parsing comic data: Missing field {str(e)}" | |
| except Exception as e: | |
| return f"Unexpected error: {str(e)}" | |
| def search_xkcd_transcript(search_term: str) -> str: | |
| """ | |
| Search for XKCD comics by searching their transcripts and titles. | |
| Note: This is a simple demonstration - in a real implementation you'd want a proper search index. | |
| Args: | |
| search_term (str): Term to search for in comic transcripts and titles | |
| Returns: | |
| str: JSON string containing matching comics information | |
| """ | |
| try: | |
| # Get latest comic number first | |
| latest_response = requests.get("https://xkcd.com/info.0.json", timeout=10) | |
| latest_response.raise_for_status() | |
| latest_num = latest_response.json()["num"] | |
| matches = [] | |
| search_term_lower = search_term.lower() | |
| # Search through comics that are more likely to have transcripts (1-1600 range) | |
| # Recent comics often don't have transcripts, so we search older ones first | |
| max_search_range = min(1600, latest_num) | |
| for comic_num in range(1, max_search_range + 1): | |
| try: | |
| url = f"https://xkcd.com/{comic_num}/info.0.json" | |
| response = requests.get(url, timeout=5) | |
| response.raise_for_status() | |
| comic_data = response.json() | |
| # Check if search term is in title, alt text, safe_title, or transcript | |
| if (search_term_lower in comic_data["title"].lower() or | |
| search_term_lower in comic_data["alt"].lower() or | |
| search_term_lower in comic_data.get("safe_title", "").lower() or | |
| search_term_lower in comic_data.get("transcript", "").lower()): | |
| matches.append({ | |
| "num": comic_data["num"], | |
| "title": comic_data["title"], | |
| "alt": comic_data["alt"][:100] + "..." if len(comic_data["alt"]) > 100 else comic_data["alt"], | |
| "img": comic_data["img"] | |
| }) | |
| # Limit results to prevent long search times | |
| if len(matches) >= 20: | |
| break | |
| except: | |
| continue # Skip comics that can't be fetched | |
| return json.dumps({"search_term": search_term, "matches": matches}, indent=2) | |
| except Exception as e: | |
| return f"Search error: {str(e)}" | |
| # Create Gradio interface | |
| with gr.Blocks(title="XKCD MCP Server") as demo: | |
| gr.Markdown("# XKCD MCP Server") | |
| gr.Markdown("This server provides tools to fetch and search XKCD comics via MCP protocol.") | |
| with gr.Tab("Get Comic"): | |
| comic_input = gr.Textbox( | |
| label="Comic ID", | |
| placeholder="Leave empty for latest comic", | |
| value="" | |
| ) | |
| comic_output = gr.Textbox( | |
| label="Comic Data (JSON)", | |
| lines=15 | |
| ) | |
| comic_btn = gr.Button("Get Comic") | |
| comic_btn.click(get_xkcd_comic, inputs=[comic_input], outputs=[comic_output]) | |
| with gr.Tab("Search Comics"): | |
| search_input = gr.Textbox( | |
| label="Search Term", | |
| placeholder="Enter term to search in titles, alt text, and transcripts" | |
| ) | |
| search_output = gr.Textbox( | |
| label="Search Results (JSON)", | |
| lines=15 | |
| ) | |
| search_btn = gr.Button("Search") | |
| search_btn.click(search_xkcd_transcript, inputs=[search_input], outputs=[search_output]) | |
| if __name__ == "__main__": | |
| demo.launch(mcp_server=True, share=True) |