import wikipedia
import asyncio
import sys
from datetime import datetime
from typing import Dict, Any
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("WikipediaSearch")
class WikipediaServer:
"""
Wikipedia Server class for testing and programmatic access.
This class provides the same functionality as the MCP tools but in a
class-based interface that's easier to test and use programmatically.
"""
def __init__(self):
self.name = "WikipediaServer"
self.version = "1.0.0"
async def fetch_wikipedia_info(self, query: str) -> Dict[str, Any]:
"""
Search Wikipedia for a topic and return comprehensive information.
Args:
query: Search query string
Returns:
Dictionary containing success status, data, and metadata
"""
try:
# Input validation
if not query or not isinstance(query, str):
return {
"success": False,
"error": "Query must be a non-empty string",
"metadata": {
"query": str(query) if query else "",
"timestamp": datetime.now().isoformat()
}
}
query = query.strip()
if not query:
return {
"success": False,
"error": "Query cannot be empty or whitespace only",
"metadata": {
"query": query,
"timestamp": datetime.now().isoformat()
}
}
# Search Wikipedia
search_results = wikipedia.search(query, results=10)
if not search_results:
return {
"success": False,
"error": "No Wikipedia articles found for this query",
"metadata": {
"query": query,
"timestamp": datetime.now().isoformat()
}
}
# Get the best match
best_match = search_results[0]
page = wikipedia.page(best_match)
return {
"success": True,
"data": {
"title": page.title,
"summary": page.summary,
"url": page.url,
"page_id": page.pageid,
"length": len(page.content),
"categories": page.categories[:10], # Limit to first 10
"links": page.links[:20], # Limit to first 20
"last_modified": datetime.now().isoformat() # Placeholder
},
"metadata": {
"query": query,
"search_time_ms": 0, # Placeholder
"timestamp": datetime.now().isoformat()
}
}
except wikipedia.DisambiguationError as e:
return {
"success": False,
"error": f"Multiple articles found. Please be more specific.",
"error_type": "DisambiguationError",
"suggestions": list(e.options[:10]),
"metadata": {
"query": query,
"timestamp": datetime.now().isoformat()
}
}
except wikipedia.PageError:
return {
"success": False,
"error": "No Wikipedia page could be loaded for this query",
"error_type": "PageError",
"metadata": {
"query": query,
"timestamp": datetime.now().isoformat()
}
}
except Exception as e:
return {
"success": False,
"error": f"An error occurred: {str(e)}",
"error_type": type(e).__name__,
"metadata": {
"query": query,
"timestamp": datetime.now().isoformat()
}
}
async def list_wikipedia_sections(self, topic: str) -> Dict[str, Any]:
"""
Return a list of section titles from the Wikipedia page of a given topic.
Args:
topic: Wikipedia article title or search query
Returns:
Dictionary containing success status, sections data, and metadata
"""
try:
# Input validation
if not topic or not isinstance(topic, str):
return {
"success": False,
"error": "Topic must be a non-empty string",
"metadata": {
"topic": str(topic) if topic else "",
"timestamp": datetime.now().isoformat()
}
}
topic = topic.strip()
if not topic:
return {
"success": False,
"error": "Topic cannot be empty or whitespace only",
"metadata": {
"topic": topic,
"timestamp": datetime.now().isoformat()
}
}
# Get Wikipedia page
page = wikipedia.page(topic)
sections = page.sections
# Structure sections with additional metadata
structured_sections = []
for i, section in enumerate(sections):
structured_sections.append({
"title": section,
"level": 2, # Default level, could be enhanced
"index": i + 1
})
return {
"success": True,
"data": {
"article_title": page.title,
"sections": structured_sections,
"total_sections": len(sections)
},
"metadata": {
"topic": topic,
"page_id": page.pageid,
"timestamp": datetime.now().isoformat()
}
}
except wikipedia.PageError:
return {
"success": False,
"error": f"Wikipedia article not found for topic: {topic}",
"error_type": "PageError",
"metadata": {
"topic": topic,
"timestamp": datetime.now().isoformat()
}
}
except Exception as e:
return {
"success": False,
"error": f"An error occurred: {str(e)}",
"error_type": type(e).__name__,
"metadata": {
"topic": topic,
"timestamp": datetime.now().isoformat()
}
}
async def get_section_content(self, topic: str, section_title: str) -> Dict[str, Any]:
"""
Return the content of a specific section in a Wikipedia article.
Args:
topic: Wikipedia article title or search query
section_title: Name of the section to retrieve
Returns:
Dictionary containing success status, section content, and metadata
"""
try:
# Input validation
if not topic or not isinstance(topic, str):
return {
"success": False,
"error": "Topic must be a non-empty string",
"metadata": {
"topic": str(topic) if topic else "",
"section_title": str(section_title) if section_title else "",
"timestamp": datetime.now().isoformat()
}
}
if not section_title or not isinstance(section_title, str):
return {
"success": False,
"error": "Section title must be a non-empty string",
"metadata": {
"topic": topic,
"section_title": str(section_title) if section_title else "",
"timestamp": datetime.now().isoformat()
}
}
topic = topic.strip()
section_title = section_title.strip()
if not topic or not section_title:
return {
"success": False,
"error": "Topic and section title cannot be empty or whitespace only",
"metadata": {
"topic": topic,
"section_title": section_title,
"timestamp": datetime.now().isoformat()
}
}
# Get Wikipedia page
page = wikipedia.page(topic)
content = page.section(section_title)
if content is None:
# Try to find similar sections
available_sections = page.sections
return {
"success": False,
"error": f"Section '{section_title}' not found in article '{page.title}'",
"error_type": "SectionNotFound",
"suggestions": available_sections[:10],
"metadata": {
"topic": topic,
"section_title": section_title,
"page_id": page.pageid,
"timestamp": datetime.now().isoformat()
}
}
# Handle empty content
if not content:
content = ""
return {
"success": True,
"data": {
"article_title": page.title,
"section_title": section_title,
"content": content,
"content_length": len(content),
"subsections": [], # Could be enhanced to find subsections
"section_level": 2 # Default level
},
"metadata": {
"topic": topic,
"section_title": section_title,
"page_id": page.pageid,
"timestamp": datetime.now().isoformat()
}
}
except wikipedia.PageError:
return {
"success": False,
"error": f"Wikipedia article not found for topic: {topic}",
"error_type": "PageError",
"metadata": {
"topic": topic,
"section_title": section_title,
"timestamp": datetime.now().isoformat()
}
}
except Exception as e:
return {
"success": False,
"error": f"An error occurred: {str(e)}",
"error_type": type(e).__name__,
"metadata": {
"topic": topic,
"section_title": section_title,
"timestamp": datetime.now().isoformat()
}
}
@mcp.tool()
def fetch_wikipedia_info(query: str) -> dict:
"""
Search Wikipedia for a topic and return title, summary, and URL of the best match.
"""
try:
search_results = wikipedia.search(query)
if not search_results:
return {"error": "No results found for your query."}
best_match = search_results[0]
page = wikipedia.page(best_match)
return {
"title": page.title,
"summary": page.summary,
"url": page.url
}
except wikipedia.DisambiguationError as e:
return {
"error": f"Ambiguous topic. Try one of these: {', '.join(e.options[:5])}"
}
except wikipedia.PageError:
return {
"error": "No Wikipedia page could be loaded for this query."
}
@mcp.tool()
def list_wikipedia_sections(topic: str) -> dict:
"""
Return a list of section titles from the Wikipedia page of a given topic.
"""
try:
page = wikipedia.page(topic)
sections = page.sections
return {"sections": sections}
except Exception as e:
return {"error": str(e)}
@mcp.tool()
def get_section_content(topic: str, section_title: str) -> dict:
"""
Return the content of a specific section in a Wikipedia article.
"""
try:
page = wikipedia.page(topic)
content = page.section(section_title)
if content:
return {"content": content}
else:
return {"error": f"Section '{section_title}' not found in article '{topic}'."}
except Exception as e:
return {"error": str(e)}
if __name__ == "__main__":
print("Starting MCP Wikipedia Server with multiple tools...")
mcp.run(transport="stdio")