rootly-mcp-server 2.0.8__py3-none-any.whl → 2.0.10__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.
@@ -69,7 +69,7 @@ class AuthenticatedHTTPXClient:
69
69
  """An HTTPX client wrapper that handles Rootly API authentication and parameter transformation."""
70
70
 
71
71
  def __init__(self, base_url: str = "https://api.rootly.com", hosted: bool = False, parameter_mapping: Optional[Dict[str, str]] = None):
72
- self.base_url = base_url
72
+ self._base_url = base_url
73
73
  self.hosted = hosted
74
74
  self._api_token = None
75
75
  self.parameter_mapping = parameter_mapping or {}
@@ -77,15 +77,22 @@ class AuthenticatedHTTPXClient:
77
77
  if not self.hosted:
78
78
  self._api_token = self._get_api_token()
79
79
 
80
- # Create the HTTPX client
81
- headers = {"Content-Type": "application/vnd.api+json", "Accept": "application/vnd.api+json"}
80
+ # Create the HTTPX client
81
+ headers = {
82
+ "Content-Type": "application/vnd.api+json",
83
+ "Accept": "application/vnd.api+json"
84
+ # Let httpx handle Accept-Encoding automatically with all supported formats
85
+ }
82
86
  if self._api_token:
83
87
  headers["Authorization"] = f"Bearer {self._api_token}"
84
88
 
85
89
  self.client = httpx.AsyncClient(
86
90
  base_url=base_url,
87
91
  headers=headers,
88
- timeout=30.0
92
+ timeout=30.0,
93
+ follow_redirects=True,
94
+ # Ensure proper handling of compressed responses
95
+ limits=httpx.Limits(max_keepalive_connections=5, max_connections=10)
89
96
  )
90
97
 
91
98
  def _get_api_token(self) -> Optional[str]:
@@ -116,7 +123,7 @@ class AuthenticatedHTTPXClient:
116
123
  if 'params' in kwargs:
117
124
  kwargs['params'] = self._transform_params(kwargs['params'])
118
125
 
119
- # Call the underlying client's request method
126
+ # Call the underlying client's request method and let it handle everything
120
127
  return await self.client.request(method, url, **kwargs)
121
128
 
122
129
  async def get(self, url: str, **kwargs):
@@ -146,8 +153,19 @@ class AuthenticatedHTTPXClient:
146
153
  pass
147
154
 
148
155
  def __getattr__(self, name):
149
- # Delegate all other attributes to the underlying client
156
+ # Delegate all other attributes to the underlying client, except for request methods
157
+ if name in ['request', 'get', 'post', 'put', 'patch', 'delete']:
158
+ # Use our overridden methods instead
159
+ return getattr(self, name)
150
160
  return getattr(self.client, name)
161
+
162
+ @property
163
+ def base_url(self):
164
+ return self._base_url
165
+
166
+ @property
167
+ def headers(self):
168
+ return self.client.headers
151
169
 
152
170
 
153
171
  def create_rootly_mcp_server(
@@ -225,6 +243,7 @@ def create_rootly_mcp_server(
225
243
  return PlainTextResponse("OK")
226
244
 
227
245
  # Add some custom tools for enhanced functionality
246
+
228
247
  @mcp.tool()
229
248
  def list_endpoints() -> list:
230
249
  """List all available Rootly API endpoints with their descriptions."""
@@ -262,8 +281,8 @@ def create_rootly_mcp_server(
262
281
  except Exception:
263
282
  pass # Fallback to default client behavior
264
283
 
265
- # Make the request using the underlying httpx client
266
- return await http_client.client.request(method, url, **kwargs)
284
+ # Use our custom client with proper error handling instead of bypassing it
285
+ return await http_client.request(method, url, **kwargs)
267
286
 
268
287
  @mcp.tool()
269
288
  async def search_incidents(
@@ -281,7 +300,7 @@ def create_rootly_mcp_server(
281
300
  # Single page mode
282
301
  if page_number > 0:
283
302
  params = {
284
- "page[size]": min(page_size, 20),
303
+ "page[size]": min(page_size, 5), # Keep responses very small to avoid errors
285
304
  "page[number]": page_number,
286
305
  "include": "",
287
306
  }
@@ -298,10 +317,11 @@ def create_rootly_mcp_server(
298
317
  # Multi-page mode (page_number = 0)
299
318
  all_incidents = []
300
319
  current_page = 1
301
- effective_page_size = min(page_size, 10)
320
+ effective_page_size = min(page_size, 5) # Keep responses very small to avoid errors
321
+ max_pages = 10 # Safety limit to prevent infinite loops
302
322
 
303
323
  try:
304
- while len(all_incidents) < max_results:
324
+ while len(all_incidents) < max_results and current_page <= max_pages:
305
325
  params = {
306
326
  "page[size]": effective_page_size,
307
327
  "page[number]": current_page,
@@ -318,16 +338,23 @@ def create_rootly_mcp_server(
318
338
  if "data" in response_data:
319
339
  incidents = response_data["data"]
320
340
  if not incidents:
341
+ # No more incidents available
342
+ break
343
+
344
+ # Check if we got fewer incidents than requested (last page)
345
+ if len(incidents) < effective_page_size:
346
+ all_incidents.extend(incidents)
321
347
  break
322
348
 
323
349
  all_incidents.extend(incidents)
324
350
 
325
- # Check if we have more pages
351
+ # Check metadata if available
326
352
  meta = response_data.get("meta", {})
327
353
  current_page_meta = meta.get("current_page", current_page)
328
- total_pages = meta.get("total_pages", 1)
329
-
330
- if current_page_meta >= total_pages:
354
+ total_pages = meta.get("total_pages")
355
+
356
+ # If we have reliable metadata, use it
357
+ if total_pages and current_page_meta >= total_pages:
331
358
  break
332
359
 
333
360
  current_page += 1
@@ -377,7 +404,7 @@ def _load_swagger_spec(swagger_path: Optional[str] = None) -> Dict[str, Any]:
377
404
  logger.info(f"Using provided Swagger path: {swagger_path}")
378
405
  if not os.path.isfile(swagger_path):
379
406
  raise FileNotFoundError(f"Swagger file not found at {swagger_path}")
380
- with open(swagger_path, "r") as f:
407
+ with open(swagger_path, "r", encoding="utf-8") as f:
381
408
  return json.load(f)
382
409
  else:
383
410
  # First, check in the package data directory
@@ -385,7 +412,7 @@ def _load_swagger_spec(swagger_path: Optional[str] = None) -> Dict[str, Any]:
385
412
  package_data_path = Path(__file__).parent / "data" / "swagger.json"
386
413
  if package_data_path.is_file():
387
414
  logger.info(f"Found Swagger file in package data: {package_data_path}")
388
- with open(package_data_path, "r") as f:
415
+ with open(package_data_path, "r", encoding="utf-8") as f:
389
416
  return json.load(f)
390
417
  except Exception as e:
391
418
  logger.debug(f"Could not load Swagger file from package data: {e}")
@@ -398,7 +425,7 @@ def _load_swagger_spec(swagger_path: Optional[str] = None) -> Dict[str, Any]:
398
425
  swagger_path = current_dir / "swagger.json"
399
426
  if swagger_path.is_file():
400
427
  logger.info(f"Found Swagger file at {swagger_path}")
401
- with open(swagger_path, "r") as f:
428
+ with open(swagger_path, "r", encoding="utf-8") as f:
402
429
  return json.load(f)
403
430
 
404
431
  # Check parent directories
@@ -406,7 +433,7 @@ def _load_swagger_spec(swagger_path: Optional[str] = None) -> Dict[str, Any]:
406
433
  swagger_path = parent / "swagger.json"
407
434
  if swagger_path.is_file():
408
435
  logger.info(f"Found Swagger file at {swagger_path}")
409
- with open(swagger_path, "r") as f:
436
+ with open(swagger_path, "r", encoding="utf-8") as f:
410
437
  return json.load(f)
411
438
 
412
439
  # If the file wasn't found, fetch it from the URL and save it
@@ -417,7 +444,7 @@ def _load_swagger_spec(swagger_path: Optional[str] = None) -> Dict[str, Any]:
417
444
  swagger_path = current_dir / "swagger.json"
418
445
  logger.info(f"Saving Swagger file to {swagger_path}")
419
446
  try:
420
- with open(swagger_path, "w") as f:
447
+ with open(swagger_path, "w", encoding="utf-8") as f:
421
448
  json.dump(swagger_spec, f)
422
449
  logger.info(f"Saved Swagger file to {swagger_path}")
423
450
  except Exception as e:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rootly-mcp-server
3
- Version: 2.0.8
3
+ Version: 2.0.10
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
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3
14
14
  Classifier: Programming Language :: Python :: 3.12
15
15
  Classifier: Topic :: Software Development :: Build Tools
16
16
  Requires-Python: >=3.12
17
+ Requires-Dist: brotli>=1.0.0
17
18
  Requires-Dist: fastmcp>=2.9.0
18
19
  Requires-Dist: httpx>=0.24.0
19
20
  Requires-Dist: pydantic>=2.0.0
@@ -2,12 +2,12 @@ rootly_mcp_server/__init__.py,sha256=6pLh19IFyqE-Cve9zergkD-X_yApEkInREKmRa73T6s
2
2
  rootly_mcp_server/__main__.py,sha256=_F4p65_VjnN84RtmEdESVLLH0tO5tL9qBfb2Xdvbj2E,6480
3
3
  rootly_mcp_server/client.py,sha256=diIBINJP_z4nnQIAC1b70vQSiHaNojEfUDARC2nrKHU,4681
4
4
  rootly_mcp_server/routemap_server.py,sha256=0LfK2EzwkFQF9SpHNvGcca5ZaxkBC80gIdDojE0aUcs,6100
5
- rootly_mcp_server/server.py,sha256=os6uxXUOGeaPrPlq3r2GNLAM-iJYVIe2W5loASMkaBc,22953
5
+ rootly_mcp_server/server.py,sha256=O31ByNNfKjBmmYY1y_ooPKL12b-PXaM5eNrekXzpURE,24346
6
6
  rootly_mcp_server/test_client.py,sha256=Ytd5ZP7vImm12CT97k3p9tlkY_JNcXHSzcGGnHCBqv0,5275
7
7
  rootly_mcp_server/utils.py,sha256=NyxdcDiFGlV2a8eBO4lKgZg0D7Gxr6xUIB0YyJGgpPA,4165
8
8
  rootly_mcp_server/data/__init__.py,sha256=fO8a0bQnRVEoRMHKvhFzj10bhoaw7VsI51czc2MsUm4,143
9
- rootly_mcp_server-2.0.8.dist-info/METADATA,sha256=F1BHBKiIdJmlblKhzWDX55KLNFPXn4FfomeisilOlrw,6159
10
- rootly_mcp_server-2.0.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
- rootly_mcp_server-2.0.8.dist-info/entry_points.txt,sha256=NE33b8VgigVPGBkboyo6pvN1Vz35HZtLybxMO4Q03PI,70
12
- rootly_mcp_server-2.0.8.dist-info/licenses/LICENSE,sha256=c9w9ZZGl14r54tsP40oaq5adTVX_HMNHozPIH2ymzmw,11341
13
- rootly_mcp_server-2.0.8.dist-info/RECORD,,
9
+ rootly_mcp_server-2.0.10.dist-info/METADATA,sha256=SyvYvw80MbIJVhxXvAG_ai1F9FV9N3qPx9Ls4bj3mbg,6189
10
+ rootly_mcp_server-2.0.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
+ rootly_mcp_server-2.0.10.dist-info/entry_points.txt,sha256=NE33b8VgigVPGBkboyo6pvN1Vz35HZtLybxMO4Q03PI,70
12
+ rootly_mcp_server-2.0.10.dist-info/licenses/LICENSE,sha256=c9w9ZZGl14r54tsP40oaq5adTVX_HMNHozPIH2ymzmw,11341
13
+ rootly_mcp_server-2.0.10.dist-info/RECORD,,