rootly-mcp-server 0.0.1__tar.gz → 0.0.2__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rootly-mcp-server
3
- Version: 0.0.1
3
+ Version: 0.0.2
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
+ - Automatic fetching of the latest Swagger spec if not found locally
32
33
  - Authentication via Rootly API token
33
34
  - Default pagination (10 items) for incidents endpoints to prevent context window overflow
34
35
  - Easy integration with Claude and other MCP-compatible LLMs
@@ -77,6 +78,16 @@ Start the server:
77
78
  rootly-mcp-server
78
79
  ```
79
80
 
81
+ The server will automatically:
82
+ 1. Look for a local `swagger.json` file in the current and parent directories
83
+ 2. If not found, download the latest Swagger spec from Rootly's servers
84
+ 3. Cache the downloaded spec to `swagger.json` in the current directory for future use
85
+
86
+ You can also specify a custom Swagger file path:
87
+ ```bash
88
+ rootly-mcp-server --swagger-path=/path/to/your/swagger.json
89
+ ```
90
+
80
91
  ## MCP Configuration
81
92
 
82
93
  The server configuration is defined in `mcp.json`. To use this server with Claude or other MCP clients, add the following configuration to your MCP configuration file:
@@ -5,6 +5,7 @@ A Model Context Protocol (MCP) server for Rootly API. This server dynamically ge
5
5
  ## Features
6
6
 
7
7
  - Dynamically generated MCP tools based on Rootly's OpenAPI specification
8
+ - Automatic fetching of the latest Swagger spec if not found locally
8
9
  - Authentication via Rootly API token
9
10
  - Default pagination (10 items) for incidents endpoints to prevent context window overflow
10
11
  - Easy integration with Claude and other MCP-compatible LLMs
@@ -53,6 +54,16 @@ Start the server:
53
54
  rootly-mcp-server
54
55
  ```
55
56
 
57
+ The server will automatically:
58
+ 1. Look for a local `swagger.json` file in the current and parent directories
59
+ 2. If not found, download the latest Swagger spec from Rootly's servers
60
+ 3. Cache the downloaded spec to `swagger.json` in the current directory for future use
61
+
62
+ You can also specify a custom Swagger file path:
63
+ ```bash
64
+ rootly-mcp-server --swagger-path=/path/to/your/swagger.json
65
+ ```
66
+
56
67
  ## MCP Configuration
57
68
 
58
69
  The server configuration is defined in `mcp.json`. To use this server with Claude or other MCP clients, add the following configuration to your MCP configuration file:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "rootly-mcp-server"
3
- version = "0.0.1"
3
+ version = "0.0.2"
4
4
  description = "A Model Context Protocol server for Rootly APIs using OpenAPI spec"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -10,6 +10,7 @@ import os
10
10
  import re
11
11
  import logging
12
12
  from pathlib import Path
13
+ import requests
13
14
  from typing import Any, Dict, List, Optional, Tuple, Union, Callable
14
15
 
15
16
  import mcp
@@ -21,6 +22,9 @@ from .client import RootlyClient
21
22
  # Set up logger
22
23
  logger = logging.getLogger(__name__)
23
24
 
25
+ # Default Swagger URL
26
+ SWAGGER_URL = "https://rootly-heroku.s3.amazonaws.com/swagger/v1/swagger.json"
27
+
24
28
 
25
29
  class RootlyMCPServer(FastMCP):
26
30
  """
@@ -60,6 +64,31 @@ class RootlyMCPServer(FastMCP):
60
64
  logger.info("Registering tools based on Swagger spec")
61
65
  self._register_tools()
62
66
 
67
+ def _fetch_swagger_from_url(self, url: str = SWAGGER_URL) -> Dict[str, Any]:
68
+ """
69
+ Fetch the Swagger specification from the specified URL.
70
+
71
+ Args:
72
+ url: URL of the Swagger JSON file.
73
+
74
+ Returns:
75
+ The Swagger specification as a dictionary.
76
+
77
+ Raises:
78
+ Exception: If the request fails or the response is not valid JSON.
79
+ """
80
+ logger.info(f"Fetching Swagger specification from {url}")
81
+ try:
82
+ response = requests.get(url)
83
+ response.raise_for_status() # Raise an exception for bad status codes
84
+ return response.json()
85
+ except requests.RequestException as e:
86
+ logger.error(f"Failed to fetch Swagger spec: {e}")
87
+ raise Exception(f"Failed to fetch Swagger specification: {e}")
88
+ except json.JSONDecodeError as e:
89
+ logger.error(f"Failed to parse Swagger spec: {e}")
90
+ raise Exception(f"Failed to parse Swagger specification: {e}")
91
+
63
92
  def _load_swagger_spec(self, swagger_path: Optional[str] = None) -> Dict[str, Any]:
64
93
  """
65
94
  Load the Swagger specification from a file.
@@ -98,8 +127,21 @@ class RootlyMCPServer(FastMCP):
98
127
  with open(swagger_path, "r") as f:
99
128
  return json.load(f)
100
129
 
101
- # If we get here, we didn't find the file
102
- raise FileNotFoundError("Could not find swagger.json in current directory or parent directories")
130
+ # If the file wasn't found, fetch it from the URL and save it
131
+ logger.info("Swagger file not found locally, fetching from URL")
132
+ swagger_spec = self._fetch_swagger_from_url()
133
+
134
+ # Save the fetched spec to the current directory
135
+ swagger_path = current_dir / "swagger.json"
136
+ logger.info(f"Saving Swagger file to {swagger_path}")
137
+ try:
138
+ with open(swagger_path, "w") as f:
139
+ json.dump(swagger_spec, f)
140
+ logger.info(f"Saved Swagger file to {swagger_path}")
141
+ except Exception as e:
142
+ logger.warning(f"Failed to save Swagger file: {e}")
143
+
144
+ return swagger_spec
103
145
 
104
146
  def _register_tools(self) -> None:
105
147
  """