rootly-mcp-server 0.0.2__py3-none-any.whl → 0.0.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rootly_mcp_server/data/__init__.py +4 -0
- rootly_mcp_server/data/swagger.json +1 -0
- rootly_mcp_server/server.py +116 -82
- {rootly_mcp_server-0.0.2.dist-info → rootly_mcp_server-0.0.4.dist-info}/METADATA +6 -4
- rootly_mcp_server-0.0.4.dist-info/RECORD +12 -0
- rootly_mcp_server-0.0.2.dist-info/RECORD +0 -10
- {rootly_mcp_server-0.0.2.dist-info → rootly_mcp_server-0.0.4.dist-info}/WHEEL +0 -0
- {rootly_mcp_server-0.0.2.dist-info → rootly_mcp_server-0.0.4.dist-info}/entry_points.txt +0 -0
- {rootly_mcp_server-0.0.2.dist-info → rootly_mcp_server-0.0.4.dist-info}/licenses/LICENSE +0 -0
rootly_mcp_server/server.py
CHANGED
|
@@ -11,6 +11,7 @@ import re
|
|
|
11
11
|
import logging
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
import requests
|
|
14
|
+
import importlib.resources
|
|
14
15
|
from typing import Any, Dict, List, Optional, Tuple, Union, Callable
|
|
15
16
|
|
|
16
17
|
import mcp
|
|
@@ -29,51 +30,68 @@ SWAGGER_URL = "https://rootly-heroku.s3.amazonaws.com/swagger/v1/swagger.json"
|
|
|
29
30
|
class RootlyMCPServer(FastMCP):
|
|
30
31
|
"""
|
|
31
32
|
A Model Context Protocol server for Rootly API integration.
|
|
32
|
-
|
|
33
|
+
|
|
33
34
|
This server dynamically generates MCP tools based on the Rootly API's
|
|
34
35
|
OpenAPI (Swagger) specification.
|
|
35
36
|
"""
|
|
36
|
-
|
|
37
|
-
def __init__(self,
|
|
37
|
+
|
|
38
|
+
def __init__(self,
|
|
39
|
+
swagger_path: Optional[str] = None,
|
|
40
|
+
name: str = "Rootly",
|
|
41
|
+
default_page_size: int = 10,
|
|
42
|
+
allowed_paths: Optional[List[str]] = None):
|
|
38
43
|
"""
|
|
39
44
|
Initialize the Rootly MCP Server.
|
|
40
|
-
|
|
45
|
+
|
|
41
46
|
Args:
|
|
42
47
|
swagger_path: Path to the Swagger JSON file. If None, will look for
|
|
43
48
|
swagger.json in the current directory and parent directories.
|
|
44
49
|
name: Name of the MCP server.
|
|
45
50
|
default_page_size: Default number of items to return per page for paginated endpoints.
|
|
46
|
-
|
|
51
|
+
allowed_paths: List of API paths to load. If None, all paths will be loaded.
|
|
52
|
+
Paths should be specified without the /v1 prefix.
|
|
53
|
+
Example: ["/incidents", "/incidents/{incident_id}/alerts"]
|
|
47
54
|
"""
|
|
48
|
-
|
|
55
|
+
# Set default allowed paths if none provided
|
|
56
|
+
self.allowed_paths = allowed_paths or [
|
|
57
|
+
"/incidents",
|
|
58
|
+
"/incidents/{incident_id}/alerts"
|
|
59
|
+
]
|
|
60
|
+
# Add /v1 prefix to paths if not present
|
|
61
|
+
self.allowed_paths = [
|
|
62
|
+
f"/v1{path}" if not path.startswith("/v1") else path
|
|
63
|
+
for path in self.allowed_paths
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
logger.info(f"Initializing RootlyMCPServer with allowed paths: {self.allowed_paths}")
|
|
49
67
|
super().__init__(name)
|
|
50
|
-
|
|
68
|
+
|
|
51
69
|
# Initialize the Rootly API client
|
|
52
70
|
self.client = RootlyClient()
|
|
53
|
-
|
|
71
|
+
|
|
54
72
|
# Store default page size
|
|
55
73
|
self.default_page_size = default_page_size
|
|
56
74
|
logger.info(f"Using default page size: {default_page_size}")
|
|
57
|
-
|
|
75
|
+
|
|
58
76
|
# Load the Swagger specification
|
|
59
77
|
logger.info("Loading Swagger specification")
|
|
60
78
|
self.swagger_spec = self._load_swagger_spec(swagger_path)
|
|
61
|
-
logger.info(f"Loaded Swagger spec with {len(self.swagger_spec.get('paths', {}))} paths")
|
|
62
|
-
|
|
79
|
+
logger.info(f"Loaded Swagger spec with {len(self.swagger_spec.get('paths', {}))} total paths")
|
|
80
|
+
|
|
63
81
|
# Register tools based on the Swagger spec
|
|
64
82
|
logger.info("Registering tools based on Swagger spec")
|
|
65
83
|
self._register_tools()
|
|
66
|
-
|
|
84
|
+
|
|
67
85
|
def _fetch_swagger_from_url(self, url: str = SWAGGER_URL) -> Dict[str, Any]:
|
|
68
86
|
"""
|
|
69
87
|
Fetch the Swagger specification from the specified URL.
|
|
70
|
-
|
|
88
|
+
|
|
71
89
|
Args:
|
|
72
90
|
url: URL of the Swagger JSON file.
|
|
73
|
-
|
|
91
|
+
|
|
74
92
|
Returns:
|
|
75
93
|
The Swagger specification as a dictionary.
|
|
76
|
-
|
|
94
|
+
|
|
77
95
|
Raises:
|
|
78
96
|
Exception: If the request fails or the response is not valid JSON.
|
|
79
97
|
"""
|
|
@@ -88,15 +106,18 @@ class RootlyMCPServer(FastMCP):
|
|
|
88
106
|
except json.JSONDecodeError as e:
|
|
89
107
|
logger.error(f"Failed to parse Swagger spec: {e}")
|
|
90
108
|
raise Exception(f"Failed to parse Swagger specification: {e}")
|
|
91
|
-
|
|
109
|
+
|
|
92
110
|
def _load_swagger_spec(self, swagger_path: Optional[str] = None) -> Dict[str, Any]:
|
|
93
111
|
"""
|
|
94
112
|
Load the Swagger specification from a file.
|
|
95
|
-
|
|
113
|
+
|
|
96
114
|
Args:
|
|
97
115
|
swagger_path: Path to the Swagger JSON file. If None, will look for
|
|
98
|
-
swagger.json in the
|
|
99
|
-
|
|
116
|
+
swagger.json in the following locations (in order):
|
|
117
|
+
1. package data directory
|
|
118
|
+
2. current directory and parent directories
|
|
119
|
+
3. download from the URL
|
|
120
|
+
|
|
100
121
|
Returns:
|
|
101
122
|
The Swagger specification as a dictionary.
|
|
102
123
|
"""
|
|
@@ -108,17 +129,27 @@ class RootlyMCPServer(FastMCP):
|
|
|
108
129
|
with open(swagger_path, "r") as f:
|
|
109
130
|
return json.load(f)
|
|
110
131
|
else:
|
|
111
|
-
#
|
|
132
|
+
# First, check in the package data directory
|
|
133
|
+
try:
|
|
134
|
+
package_data_path = Path(__file__).parent / "data" / "swagger.json"
|
|
135
|
+
if package_data_path.is_file():
|
|
136
|
+
logger.info(f"Found Swagger file in package data: {package_data_path}")
|
|
137
|
+
with open(package_data_path, "r") as f:
|
|
138
|
+
return json.load(f)
|
|
139
|
+
except Exception as e:
|
|
140
|
+
logger.debug(f"Could not load Swagger file from package data: {e}")
|
|
141
|
+
|
|
142
|
+
# Then, look for swagger.json in the current directory and parent directories
|
|
112
143
|
logger.info("Looking for swagger.json in current directory and parent directories")
|
|
113
144
|
current_dir = Path.cwd()
|
|
114
|
-
|
|
145
|
+
|
|
115
146
|
# Check current directory first
|
|
116
147
|
swagger_path = current_dir / "swagger.json"
|
|
117
148
|
if swagger_path.is_file():
|
|
118
149
|
logger.info(f"Found Swagger file at {swagger_path}")
|
|
119
150
|
with open(swagger_path, "r") as f:
|
|
120
151
|
return json.load(f)
|
|
121
|
-
|
|
152
|
+
|
|
122
153
|
# Check parent directories
|
|
123
154
|
for parent in current_dir.parents:
|
|
124
155
|
swagger_path = parent / "swagger.json"
|
|
@@ -126,11 +157,11 @@ class RootlyMCPServer(FastMCP):
|
|
|
126
157
|
logger.info(f"Found Swagger file at {swagger_path}")
|
|
127
158
|
with open(swagger_path, "r") as f:
|
|
128
159
|
return json.load(f)
|
|
129
|
-
|
|
160
|
+
|
|
130
161
|
# If the file wasn't found, fetch it from the URL and save it
|
|
131
162
|
logger.info("Swagger file not found locally, fetching from URL")
|
|
132
163
|
swagger_spec = self._fetch_swagger_from_url()
|
|
133
|
-
|
|
164
|
+
|
|
134
165
|
# Save the fetched spec to the current directory
|
|
135
166
|
swagger_path = current_dir / "swagger.json"
|
|
136
167
|
logger.info(f"Saving Swagger file to {swagger_path}")
|
|
@@ -140,32 +171,38 @@ class RootlyMCPServer(FastMCP):
|
|
|
140
171
|
logger.info(f"Saved Swagger file to {swagger_path}")
|
|
141
172
|
except Exception as e:
|
|
142
173
|
logger.warning(f"Failed to save Swagger file: {e}")
|
|
143
|
-
|
|
174
|
+
|
|
144
175
|
return swagger_spec
|
|
145
|
-
|
|
176
|
+
|
|
146
177
|
def _register_tools(self) -> None:
|
|
147
178
|
"""
|
|
148
179
|
Register MCP tools based on the Swagger specification.
|
|
149
|
-
|
|
150
|
-
This method iterates through the paths and operations in the Swagger spec
|
|
151
|
-
and creates corresponding MCP tools.
|
|
180
|
+
Only registers tools for paths specified in allowed_paths.
|
|
152
181
|
"""
|
|
153
182
|
paths = self.swagger_spec.get("paths", {})
|
|
154
|
-
|
|
155
|
-
|
|
183
|
+
|
|
184
|
+
# Filter paths based on allowed_paths
|
|
185
|
+
filtered_paths = {
|
|
186
|
+
path: path_item
|
|
187
|
+
for path, path_item in paths.items()
|
|
188
|
+
if path in self.allowed_paths
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
logger.info(f"Registering {len(filtered_paths)} paths out of {len(paths)} total paths")
|
|
192
|
+
|
|
156
193
|
# Register the list_endpoints tool
|
|
157
194
|
@self.tool()
|
|
158
195
|
def list_endpoints() -> str:
|
|
159
196
|
"""List all available Rootly API endpoints."""
|
|
160
197
|
endpoints = []
|
|
161
|
-
for path, path_item in
|
|
198
|
+
for path, path_item in filtered_paths.items():
|
|
162
199
|
for method, operation in path_item.items():
|
|
163
200
|
if method.lower() not in ["get", "post", "put", "delete", "patch"]:
|
|
164
201
|
continue
|
|
165
|
-
|
|
202
|
+
|
|
166
203
|
summary = operation.get("summary", "")
|
|
167
204
|
description = operation.get("description", "")
|
|
168
|
-
|
|
205
|
+
|
|
169
206
|
endpoints.append({
|
|
170
207
|
"path": path,
|
|
171
208
|
"method": method.upper(),
|
|
@@ -173,92 +210,89 @@ class RootlyMCPServer(FastMCP):
|
|
|
173
210
|
"description": description,
|
|
174
211
|
"tool_name": self._create_tool_name(path, method)
|
|
175
212
|
})
|
|
176
|
-
|
|
213
|
+
|
|
177
214
|
return json.dumps(endpoints, indent=2)
|
|
178
|
-
|
|
215
|
+
|
|
179
216
|
# Register a tool for each endpoint
|
|
180
217
|
tool_count = 0
|
|
181
|
-
|
|
182
|
-
for path, path_item in
|
|
218
|
+
|
|
219
|
+
for path, path_item in filtered_paths.items():
|
|
183
220
|
# Skip path parameters
|
|
184
221
|
if "parameters" in path_item:
|
|
185
222
|
path_item = {k: v for k, v in path_item.items() if k != "parameters"}
|
|
186
|
-
|
|
223
|
+
|
|
187
224
|
for method, operation in path_item.items():
|
|
188
225
|
if method.lower() not in ["get", "post", "put", "delete", "patch"]:
|
|
189
226
|
continue
|
|
190
|
-
|
|
227
|
+
|
|
191
228
|
# Create a tool name based on the path and method
|
|
192
229
|
tool_name = self._create_tool_name(path, method)
|
|
193
|
-
|
|
230
|
+
|
|
194
231
|
# Create a tool description
|
|
195
232
|
description = operation.get("summary", "") or operation.get("description", "")
|
|
196
233
|
if not description:
|
|
197
234
|
description = f"{method.upper()} {path}"
|
|
198
|
-
|
|
199
|
-
# Create the input schema
|
|
200
|
-
input_schema = self._create_input_schema(path, operation)
|
|
201
|
-
|
|
235
|
+
|
|
202
236
|
# Register the tool using the direct method
|
|
203
237
|
try:
|
|
204
238
|
# Define the tool function
|
|
205
239
|
def create_tool_fn(p=path, m=method, op=operation):
|
|
206
240
|
def tool_fn(**kwargs):
|
|
207
241
|
return self._handle_api_request(p, m, op, **kwargs)
|
|
208
|
-
|
|
242
|
+
|
|
209
243
|
# Set the function name and docstring
|
|
210
244
|
tool_fn.__name__ = tool_name
|
|
211
245
|
tool_fn.__doc__ = description
|
|
212
246
|
return tool_fn
|
|
213
|
-
|
|
247
|
+
|
|
214
248
|
# Create the tool function
|
|
215
249
|
tool_fn = create_tool_fn()
|
|
216
|
-
|
|
250
|
+
|
|
217
251
|
# Register the tool with FastMCP
|
|
218
252
|
self.add_tool(
|
|
219
253
|
name=tool_name,
|
|
220
254
|
description=description,
|
|
221
255
|
fn=tool_fn
|
|
222
256
|
)
|
|
223
|
-
|
|
257
|
+
|
|
224
258
|
tool_count += 1
|
|
225
259
|
logger.info(f"Registered tool: {tool_name}")
|
|
226
260
|
except Exception as e:
|
|
227
261
|
logger.error(f"Error registering tool {tool_name}: {e}")
|
|
228
|
-
|
|
229
|
-
logger.info(f"Registered {tool_count} tools in total")
|
|
230
|
-
|
|
262
|
+
|
|
263
|
+
logger.info(f"Registered {tool_count} tools in total. Modify allowed_paths to register more paths from the Rootly API.")
|
|
264
|
+
|
|
231
265
|
def _create_tool_name(self, path: str, method: str) -> str:
|
|
232
266
|
"""
|
|
233
267
|
Create a tool name based on the path and method.
|
|
234
|
-
|
|
268
|
+
|
|
235
269
|
Args:
|
|
236
270
|
path: The API path.
|
|
237
271
|
method: The HTTP method.
|
|
238
|
-
|
|
272
|
+
|
|
239
273
|
Returns:
|
|
240
274
|
A tool name string.
|
|
241
275
|
"""
|
|
242
276
|
# Remove the /v1 prefix if present
|
|
243
277
|
if path.startswith("/v1"):
|
|
244
278
|
path = path[3:]
|
|
245
|
-
|
|
279
|
+
|
|
246
280
|
# Replace path parameters with "by_id"
|
|
247
281
|
path = re.sub(r"\{([^}]+)\}", r"by_\1", path)
|
|
248
|
-
|
|
282
|
+
|
|
249
283
|
# Replace slashes with underscores and remove leading/trailing underscores
|
|
250
284
|
path = path.replace("/", "_").strip("_")
|
|
251
|
-
|
|
285
|
+
|
|
252
286
|
return f"{path}_{method.lower()}"
|
|
253
|
-
|
|
287
|
+
|
|
254
288
|
def _create_input_schema(self, path: str, operation: Dict[str, Any]) -> Dict[str, Any]:
|
|
255
289
|
"""
|
|
256
290
|
Create an input schema for the tool.
|
|
257
|
-
|
|
291
|
+
|
|
258
292
|
Args:
|
|
259
293
|
path: The API path.
|
|
260
294
|
operation: The Swagger operation object.
|
|
261
|
-
|
|
295
|
+
|
|
262
296
|
Returns:
|
|
263
297
|
An input schema dictionary.
|
|
264
298
|
"""
|
|
@@ -269,7 +303,7 @@ class RootlyMCPServer(FastMCP):
|
|
|
269
303
|
"required": [],
|
|
270
304
|
"additionalProperties": False
|
|
271
305
|
}
|
|
272
|
-
|
|
306
|
+
|
|
273
307
|
# Extract path parameters
|
|
274
308
|
path_params = re.findall(r"\{([^}]+)\}", path)
|
|
275
309
|
for param in path_params:
|
|
@@ -278,80 +312,80 @@ class RootlyMCPServer(FastMCP):
|
|
|
278
312
|
"description": f"Path parameter: {param}"
|
|
279
313
|
}
|
|
280
314
|
schema["required"].append(param)
|
|
281
|
-
|
|
315
|
+
|
|
282
316
|
# Add operation parameters
|
|
283
317
|
for param in operation.get("parameters", []):
|
|
284
318
|
param_name = param.get("name")
|
|
285
319
|
param_in = param.get("in")
|
|
286
|
-
|
|
320
|
+
|
|
287
321
|
if param_in in ["query", "header"]:
|
|
288
322
|
param_schema = param.get("schema", {})
|
|
289
323
|
param_type = param_schema.get("type", "string")
|
|
290
|
-
|
|
324
|
+
|
|
291
325
|
schema["properties"][param_name] = {
|
|
292
326
|
"type": param_type,
|
|
293
327
|
"description": param.get("description", f"{param_in} parameter: {param_name}")
|
|
294
328
|
}
|
|
295
|
-
|
|
329
|
+
|
|
296
330
|
if param.get("required", False):
|
|
297
331
|
schema["required"].append(param_name)
|
|
298
|
-
|
|
332
|
+
|
|
299
333
|
# Add request body for POST, PUT, PATCH methods
|
|
300
334
|
if "requestBody" in operation:
|
|
301
335
|
content = operation["requestBody"].get("content", {})
|
|
302
336
|
if "application/json" in content:
|
|
303
337
|
body_schema = content["application/json"].get("schema", {})
|
|
304
|
-
|
|
338
|
+
|
|
305
339
|
if "properties" in body_schema:
|
|
306
340
|
for prop_name, prop_schema in body_schema["properties"].items():
|
|
307
341
|
schema["properties"][prop_name] = {
|
|
308
342
|
"type": prop_schema.get("type", "string"),
|
|
309
343
|
"description": prop_schema.get("description", f"Body parameter: {prop_name}")
|
|
310
344
|
}
|
|
311
|
-
|
|
345
|
+
|
|
312
346
|
if "required" in body_schema:
|
|
313
347
|
schema["required"].extend(body_schema["required"])
|
|
314
|
-
|
|
348
|
+
|
|
315
349
|
return schema
|
|
316
|
-
|
|
350
|
+
|
|
317
351
|
def _handle_api_request(self, path: str, method: str, operation: Dict[str, Any], **kwargs) -> str:
|
|
318
352
|
"""
|
|
319
353
|
Handle an API request to the Rootly API.
|
|
320
|
-
|
|
354
|
+
|
|
321
355
|
Args:
|
|
322
356
|
path: The API path.
|
|
323
357
|
method: The HTTP method.
|
|
324
358
|
operation: The Swagger operation object.
|
|
325
359
|
**kwargs: The parameters for the request.
|
|
326
|
-
|
|
360
|
+
|
|
327
361
|
Returns:
|
|
328
362
|
The API response as a JSON string.
|
|
329
363
|
"""
|
|
330
364
|
logger.debug(f"Handling API request: {method} {path}")
|
|
331
365
|
logger.debug(f"Request parameters: {kwargs}")
|
|
332
|
-
|
|
366
|
+
|
|
333
367
|
# Extract path parameters
|
|
334
368
|
path_params = re.findall(r"\{([^}]+)\}", path)
|
|
335
369
|
actual_path = path
|
|
336
|
-
|
|
370
|
+
|
|
337
371
|
# Replace path parameters in the URL
|
|
338
372
|
for param in path_params:
|
|
339
373
|
if param in kwargs:
|
|
340
374
|
actual_path = actual_path.replace(f"{{{param}}}", str(kwargs.pop(param)))
|
|
341
|
-
|
|
375
|
+
|
|
342
376
|
# Separate query parameters and body parameters
|
|
343
377
|
query_params = {}
|
|
344
378
|
body_params = {}
|
|
345
|
-
|
|
379
|
+
|
|
346
380
|
if method.lower() == "get":
|
|
347
381
|
# For GET requests, all remaining parameters are query parameters
|
|
348
382
|
query_params = kwargs
|
|
349
|
-
|
|
383
|
+
|
|
350
384
|
# Add default pagination for incident-related endpoints
|
|
351
385
|
if "incidents" in path and method.lower() == "get":
|
|
352
386
|
# Check if pagination parameters are already specified
|
|
353
387
|
has_pagination = any(param.startswith("page[") for param in query_params.keys())
|
|
354
|
-
|
|
388
|
+
|
|
355
389
|
# If no pagination parameters are specified, add default page size
|
|
356
390
|
if not has_pagination:
|
|
357
391
|
query_params["page[size]"] = self.default_page_size
|
|
@@ -361,13 +395,13 @@ class RootlyMCPServer(FastMCP):
|
|
|
361
395
|
for param in operation.get("parameters", []):
|
|
362
396
|
param_name = param.get("name")
|
|
363
397
|
param_in = param.get("in")
|
|
364
|
-
|
|
398
|
+
|
|
365
399
|
if param_in == "query" and param_name in kwargs:
|
|
366
400
|
query_params[param_name] = kwargs.pop(param_name)
|
|
367
|
-
|
|
401
|
+
|
|
368
402
|
# All remaining parameters go in the request body
|
|
369
403
|
body_params = kwargs
|
|
370
|
-
|
|
404
|
+
|
|
371
405
|
# Make the API request
|
|
372
406
|
try:
|
|
373
407
|
response = self.client.make_request(
|
|
@@ -379,4 +413,4 @@ class RootlyMCPServer(FastMCP):
|
|
|
379
413
|
return response
|
|
380
414
|
except Exception as e:
|
|
381
415
|
logger.error(f"Error calling Rootly API: {e}")
|
|
382
|
-
return json.dumps({"error": str(e)})
|
|
416
|
+
return json.dumps({"error": str(e)})
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rootly-mcp-server
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: A Model Context Protocol server for Rootly APIs using OpenAPI spec
|
|
5
5
|
Project-URL: Homepage, https://github.com/Rootly-AI-Labs/Rootly-MCP-server
|
|
6
6
|
Project-URL: Issues, https://github.com/Rootly-AI-Labs/Rootly-MCP-server/issues
|
|
@@ -29,6 +29,7 @@ A Model Context Protocol (MCP) server for Rootly API. This server dynamically ge
|
|
|
29
29
|
## Features
|
|
30
30
|
|
|
31
31
|
- Dynamically generated MCP tools based on Rootly's OpenAPI specification
|
|
32
|
+
- Swagger specification is bundled with the package
|
|
32
33
|
- Automatic fetching of the latest Swagger spec if not found locally
|
|
33
34
|
- Authentication via Rootly API token
|
|
34
35
|
- Default pagination (10 items) for incidents endpoints to prevent context window overflow
|
|
@@ -79,9 +80,10 @@ rootly-mcp-server
|
|
|
79
80
|
```
|
|
80
81
|
|
|
81
82
|
The server will automatically:
|
|
82
|
-
1.
|
|
83
|
-
2. If not found
|
|
84
|
-
3.
|
|
83
|
+
1. Use the bundled Swagger specification that comes with the package
|
|
84
|
+
2. If not found in the package, look for a local `swagger.json` file in the current and parent directories
|
|
85
|
+
3. If still not found, download the latest Swagger spec from Rootly's servers
|
|
86
|
+
4. Cache the downloaded spec to `swagger.json` in the current directory for future use
|
|
85
87
|
|
|
86
88
|
You can also specify a custom Swagger file path:
|
|
87
89
|
```bash
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
rootly_mcp_server/__init__.py,sha256=n-YajkwxYg0eoVvtYfYTY6slaktHTYvQbasg15HwGKo,628
|
|
2
|
+
rootly_mcp_server/__main__.py,sha256=vRu8UuyhWnpHiC8vtgyv8G-WpFwumj393NtoT2lUSBk,4058
|
|
3
|
+
rootly_mcp_server/client.py,sha256=vvaY_UaYLobeHbJwgsmFNX-2ABpYoKxMTie8DRBo_xk,3865
|
|
4
|
+
rootly_mcp_server/server.py,sha256=unRG4xzFHdWX2ITcugEWz1XXhJJXNOTlc4nuwavnQL8,16181
|
|
5
|
+
rootly_mcp_server/test_client.py,sha256=xFQ4cfUpD6qs-aLidy56B3nnV38EFvUe_eBHOqZOC9o,2191
|
|
6
|
+
rootly_mcp_server/data/__init__.py,sha256=fO8a0bQnRVEoRMHKvhFzj10bhoaw7VsI51czc2MsUm4,143
|
|
7
|
+
rootly_mcp_server/data/swagger.json,sha256=8Ag4COTnS3WSC6vBaa2Q7hq3RxIQ8fGrmwsnNSnPheA,2046619
|
|
8
|
+
rootly_mcp_server-0.0.4.dist-info/METADATA,sha256=NoYBOwXWGIGTzgqkvFZrWL5xGjPaBrax9S2zPmD2EBE,3775
|
|
9
|
+
rootly_mcp_server-0.0.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
10
|
+
rootly_mcp_server-0.0.4.dist-info/entry_points.txt,sha256=NE33b8VgigVPGBkboyo6pvN1Vz35HZtLybxMO4Q03PI,70
|
|
11
|
+
rootly_mcp_server-0.0.4.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
12
|
+
rootly_mcp_server-0.0.4.dist-info/RECORD,,
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
rootly_mcp_server/__init__.py,sha256=n-YajkwxYg0eoVvtYfYTY6slaktHTYvQbasg15HwGKo,628
|
|
2
|
-
rootly_mcp_server/__main__.py,sha256=vRu8UuyhWnpHiC8vtgyv8G-WpFwumj393NtoT2lUSBk,4058
|
|
3
|
-
rootly_mcp_server/client.py,sha256=vvaY_UaYLobeHbJwgsmFNX-2ABpYoKxMTie8DRBo_xk,3865
|
|
4
|
-
rootly_mcp_server/server.py,sha256=C9VkrZEfkYy_MZwKw5gMOf__WhV7JMFE7BUqpax2nos,15330
|
|
5
|
-
rootly_mcp_server/test_client.py,sha256=xFQ4cfUpD6qs-aLidy56B3nnV38EFvUe_eBHOqZOC9o,2191
|
|
6
|
-
rootly_mcp_server-0.0.2.dist-info/METADATA,sha256=AXFB-cXLGYsS3E-GFeI2KQakclint98yY3z-2km6eFY,3619
|
|
7
|
-
rootly_mcp_server-0.0.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
8
|
-
rootly_mcp_server-0.0.2.dist-info/entry_points.txt,sha256=NE33b8VgigVPGBkboyo6pvN1Vz35HZtLybxMO4Q03PI,70
|
|
9
|
-
rootly_mcp_server-0.0.2.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
10
|
-
rootly_mcp_server-0.0.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|