File size: 5,246 Bytes
a4b9277
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ebcb262
a38d810
ebcb262
a4b9277
a38d810
a4b9277
 
ebcb262
a4b9277
 
 
37828a6
a4b9277
 
37828a6
a4b9277
 
 
 
 
 
 
 
 
a38d810
ebcb262
a38d810
 
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
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-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)}"

# 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)