-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathtools.py
More file actions
165 lines (124 loc) · 5.16 KB
/
tools.py
File metadata and controls
165 lines (124 loc) · 5.16 KB
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
"""
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.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"],
)
def _find_resources_dir() -> Path:
"""Locate the resources directory.
Checks two locations:
1. Inside the guide-mcp package (development / editable install)
2. Working directory ``resources/`` (Docker production with --no-editable)
"""
# Development: resources/ lives next to src/ in the guide-mcp package
# tools.py -> ra_mcp_guide_mcp/ -> src/ -> guide-mcp/ -> resources/
pkg_resources = Path(__file__).resolve().parent.parent.parent / "resources"
if pkg_resources.is_dir():
return pkg_resources
# Production (Docker): resources/ is copied to the working directory
cwd_resources = Path("resources")
if cwd_resources.is_dir():
return cwd_resources.resolve()
return pkg_resources # fall back so error messages reference a sensible path
_RESOURCES_DIR = _find_resources_dir()
def _check_file_exists(filename) -> bool:
"""Check if the markdown file exists."""
safe_name = Path(filename).name
return (_RESOURCES_DIR / safe_name).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
path = _RESOURCES_DIR / safe_name
if not path.exists():
raise FileNotFoundError(f"Could not find {safe_name} in {_RESOURCES_DIR}")
return path.read_text(encoding="utf-8")