"""
Riksarkivet Historical Guide MCP Server.
Provides MCP resources for accessing historical documentation about Swedish archives.
"""
from pathlib import Path
from fastmcp import FastMCP
from ra_mcp_common.utils.formatting import format_error_message
guide_mcp = FastMCP(
name="ra-guide-mcp",
instructions="""
📚 Riksarkivet Historical Guide MCP Server
This server provides access to historical documentation about Swedish archives,
including guides about court records, prisons, and other archival materials.
AVAILABLE RESOURCES:
1. 📑 riksarkivet://contents/table_of_contents
- Returns the complete guide index (Innehållsförteckning)
- Lists all available historical guide sections
2. 📄 riksarkivet://guide/{filename}
- Access detailed historical documentation by filename
- Examples: '01_Domstolar.md', '02_Fangelse.md'
- Use table_of_contents to see available sections
TYPICAL WORKFLOW:
1. Access table_of_contents to see all available guide sections
2. Load specific sections using guide/{filename}
3. Use guide content to understand historical context for search results
All resources return markdown-formatted historical documentation.
""",
)
@guide_mcp.resource("riksarkivet://contents/table_of_contents")
def get_table_of_contents() -> str:
"""
Get the table of contents (Innehållsförteckning) for the Riksarkivet historical guide.
"""
try:
content = _load_markdown_file("00_Innehallsforteckning.md")
return content
except FileNotFoundError:
return format_error_message(
"Table of contents file not found",
error_suggestions=[
"Check if the markdown/00_Innehallsforteckning.md file exists",
"Verify the file path is correct",
],
)
except Exception as e:
return format_error_message(
f"Failed to load table of contents: {e!s}",
error_suggestions=[
"Check file permissions",
"Verify file encoding is UTF-8",
],
)
@guide_mcp.resource("riksarkivet://guide/{filename}")
def get_guide_content(filename: str) -> str:
"""
Load content from specific sections of the Riksarkivet historical guide.
Args:
filename: Markdown filename to load (e.g., '01_Domstolar.md', '02_Fangelse.md')
Returns:
The content of the requested guide section
"""
try:
if not _validate_markdown_filename(filename):
return _generate_invalid_filename_message()
if not _check_file_exists(filename):
return _generate_file_not_found_message(filename)
content = _load_markdown_file(filename)
return content
except Exception as e:
return format_error_message(
f"Failed to load guide content '{filename}': {e!s}",
error_suggestions=[
"Check file permissions",
"Verify file encoding is UTF-8",
"Ensure the filename is valid",
],
)
def _validate_markdown_filename(filename) -> bool:
"""Validate that the filename has .md extension."""
return filename.endswith(".md")
def _generate_invalid_filename_message() -> str:
"""Generate error message for invalid filename format."""
return format_error_message(
"Invalid filename format",
error_suggestions=["Filename must end with .md extension"],
)
_CURRENT_DIR = Path(__file__).parent
def _check_file_exists(filename) -> bool:
"""Check if the markdown file exists."""
safe_name = Path(filename).name
markdown_path = _CURRENT_DIR / ".." / ".." / ".." / ".." / "resources" / safe_name
if not markdown_path.exists():
# Try alternative paths for different package layouts
alt_paths = [
_CURRENT_DIR / ".." / ".." / ".." / ".." / ".." / "resources" / safe_name,
_CURRENT_DIR / ".." / ".." / ".." / ".." / ".." / "markdown" / safe_name,
]
for alt_path in alt_paths:
if alt_path.exists():
return True
return markdown_path.exists()
def _generate_file_not_found_message(filename) -> str:
"""Generate error message when file is not found."""
return format_error_message(
f"Guide section '{filename}' not found",
error_suggestions=[
"Check the filename spelling",
"Use get_table_of_contents resource to see available sections",
"Ensure the filename includes .md extension",
],
)
def _load_markdown_file(filename) -> str:
"""Load content from a markdown file."""
safe_name = Path(filename).name
# Try multiple possible paths
possible_paths = [
_CURRENT_DIR / ".." / ".." / ".." / ".." / "resources" / safe_name,
_CURRENT_DIR / ".." / ".." / ".." / ".." / ".." / "resources" / safe_name,
_CURRENT_DIR / ".." / ".." / ".." / ".." / ".." / "markdown" / safe_name,
]
for markdown_path in possible_paths:
if markdown_path.exists():
return markdown_path.read_text(encoding="utf-8")
# Default path for error message
raise FileNotFoundError(f"Could not find {safe_name} in any expected location")