gnumanth commited on
Commit
a4b9277
·
verified ·
1 Parent(s): c6d3e78

feat: xkcd mcp server

Browse files
Files changed (2) hide show
  1. app.py +132 -0
  2. requirements.txt +2 -0
app.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import requests
3
+ import json
4
+ from typing import Dict, Any, Optional
5
+
6
+ def get_xkcd_comic(comic_id: str = "") -> str:
7
+ """
8
+ Fetch XKCD comic information by ID or get the latest comic.
9
+
10
+ Args:
11
+ comic_id (str): Comic ID number (leave empty for latest comic)
12
+
13
+ Returns:
14
+ str: JSON string containing comic information including title, alt text, and image URL
15
+ """
16
+ try:
17
+ if comic_id.strip():
18
+ # Get specific comic by ID
19
+ url = f"https://xkcd.com/{comic_id.strip()}/info.0.json"
20
+ else:
21
+ # Get latest comic
22
+ url = "https://xkcd.com/info.0.json"
23
+
24
+ response = requests.get(url, timeout=10)
25
+ response.raise_for_status()
26
+
27
+ comic_data = response.json()
28
+
29
+ # Format the response nicely
30
+ formatted_response = {
31
+ "num": comic_data["num"],
32
+ "title": comic_data["title"],
33
+ "alt": comic_data["alt"],
34
+ "img": comic_data["img"],
35
+ "year": comic_data["year"],
36
+ "month": comic_data["month"],
37
+ "day": comic_data["day"],
38
+ "transcript": comic_data.get("transcript", ""),
39
+ "safe_title": comic_data["safe_title"]
40
+ }
41
+
42
+ return json.dumps(formatted_response, indent=2)
43
+
44
+ except requests.exceptions.RequestException as e:
45
+ return f"Error fetching comic: {str(e)}"
46
+ except KeyError as e:
47
+ return f"Error parsing comic data: Missing field {str(e)}"
48
+ except Exception as e:
49
+ return f"Unexpected error: {str(e)}"
50
+
51
+ def search_xkcd_transcript(search_term: str) -> str:
52
+ """
53
+ Search for XKCD comics by searching their transcripts and titles.
54
+ Note: This is a simple demonstration - in a real implementation you'd want a proper search index.
55
+
56
+ Args:
57
+ search_term (str): Term to search for in comic transcripts and titles
58
+
59
+ Returns:
60
+ str: JSON string containing matching comics information
61
+ """
62
+ try:
63
+ # Get latest comic number first
64
+ latest_response = requests.get("https://xkcd.com/info.0.json", timeout=10)
65
+ latest_response.raise_for_status()
66
+ latest_num = latest_response.json()["num"]
67
+
68
+ matches = []
69
+ search_term_lower = search_term.lower()
70
+
71
+ # Search through recent comics (last 50 for demo purposes)
72
+ start_num = max(1, latest_num - 49)
73
+
74
+ for comic_num in range(start_num, latest_num + 1):
75
+ try:
76
+ url = f"https://xkcd.com/{comic_num}/info.0.json"
77
+ response = requests.get(url, timeout=5)
78
+ response.raise_for_status()
79
+ comic_data = response.json()
80
+
81
+ # Check if search term is in title, alt text, or transcript
82
+ if (search_term_lower in comic_data["title"].lower() or
83
+ search_term_lower in comic_data["alt"].lower() or
84
+ search_term_lower in comic_data.get("transcript", "").lower()):
85
+
86
+ matches.append({
87
+ "num": comic_data["num"],
88
+ "title": comic_data["title"],
89
+ "alt": comic_data["alt"][:100] + "..." if len(comic_data["alt"]) > 100 else comic_data["alt"],
90
+ "img": comic_data["img"]
91
+ })
92
+
93
+ except:
94
+ continue # Skip comics that can't be fetched
95
+
96
+ return json.dumps({"search_term": search_term, "matches": matches}, indent=2)
97
+
98
+ except Exception as e:
99
+ return f"Search error: {str(e)}"
100
+
101
+ # Create Gradio interface
102
+ with gr.Blocks(title="XKCD MCP Server") as demo:
103
+ gr.Markdown("# XKCD MCP Server")
104
+ gr.Markdown("This server provides tools to fetch and search XKCD comics via MCP protocol.")
105
+
106
+ with gr.Tab("Get Comic"):
107
+ comic_input = gr.Textbox(
108
+ label="Comic ID",
109
+ placeholder="Leave empty for latest comic",
110
+ value=""
111
+ )
112
+ comic_output = gr.Textbox(
113
+ label="Comic Data (JSON)",
114
+ lines=15
115
+ )
116
+ comic_btn = gr.Button("Get Comic")
117
+ comic_btn.click(get_xkcd_comic, inputs=[comic_input], outputs=[comic_output])
118
+
119
+ with gr.Tab("Search Comics"):
120
+ search_input = gr.Textbox(
121
+ label="Search Term",
122
+ placeholder="Enter term to search in titles, alt text, and transcripts"
123
+ )
124
+ search_output = gr.Textbox(
125
+ label="Search Results (JSON)",
126
+ lines=15
127
+ )
128
+ search_btn = gr.Button("Search")
129
+ search_btn.click(search_xkcd_transcript, inputs=[search_input], outputs=[search_output])
130
+
131
+ if __name__ == "__main__":
132
+ demo.launch(mcp_server=True, share=True)
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ gradio[mcp]
2
+ requests