Spaces:
Running
Running
File size: 8,379 Bytes
a4b9277 69ba8d5 a4b9277 69ba8d5 a4b9277 69ba8d5 a4b9277 69ba8d5 a4b9277 69ba8d5 a4b9277 69ba8d5 a4b9277 69ba8d5 a4b9277 69ba8d5 a4b9277 ebcb262 a38d810 ebcb262 a4b9277 a38d810 a4b9277 ebcb262 a4b9277 37828a6 a4b9277 37828a6 a4b9277 a38d810 ebcb262 a38d810 a4b9277 69ba8d5 a4b9277 69ba8d5 a4b9277 69ba8d5 a4b9277 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
import gradio as gr
import requests
import json
import random
from typing import Dict, Any, Optional
def get_latest() -> str:
"""
Fetch the latest XKCD comic.
Returns:
str: JSON string containing latest comic information
"""
try:
url = "https://xkcd.com/info.0.json"
response = requests.get(url, timeout=10)
response.raise_for_status()
comic_data = response.json()
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 latest 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 get_comic(comic_id: str) -> str:
"""
Fetch a specific XKCD comic by ID.
Args:
comic_id (str): Comic ID number
Returns:
str: JSON string containing comic information
"""
try:
if not comic_id.strip():
return "Error: Comic ID is required"
url = f"https://xkcd.com/{comic_id.strip()}/info.0.json"
response = requests.get(url, timeout=10)
response.raise_for_status()
comic_data = response.json()
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 {comic_id}: {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 get_random() -> str:
"""
Fetch a random XKCD comic.
Returns:
str: JSON string containing random comic information
"""
try:
# First get the latest comic to know the range
latest_response = requests.get("https://xkcd.com/info.0.json", timeout=10)
latest_response.raise_for_status()
latest_num = latest_response.json()["num"]
# Generate random comic ID (excluding 404 which doesn't exist)
comic_id = random.randint(1, latest_num)
if comic_id == 404:
comic_id = 405
url = f"https://xkcd.com/{comic_id}/info.0.json"
response = requests.get(url, timeout=10)
response.raise_for_status()
comic_data = response.json()
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 random 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-500 range for faster results)
# Recent comics often don't have transcripts, so we search older ones first
max_search_range = min(500, 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=2)
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) >= 10:
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)}"
# Helper function for the Gradio interface
def get_xkcd_comic(comic_id: str = "") -> str:
"""Wrapper function for Gradio interface compatibility"""
if not comic_id.strip():
return get_latest()
else:
return get_comic(comic_id)
# 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"):
with gr.Row():
with gr.Column():
latest_btn = gr.Button("Get Latest Comic")
random_btn = gr.Button("Get Random Comic")
with gr.Column():
comic_input = gr.Textbox(
label="Comic ID",
placeholder="Enter comic ID number",
value=""
)
specific_btn = gr.Button("Get Specific Comic")
comic_output = gr.Textbox(
label="Comic Data (JSON)",
lines=15
)
latest_btn.click(get_latest, outputs=[comic_output])
random_btn.click(get_random, outputs=[comic_output])
specific_btn.click(get_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) |