rootly-mcp-server 2.0.12__py3-none-any.whl → 2.0.14__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.
@@ -391,9 +391,10 @@ def create_rootly_mcp_server(
391
391
  # Single page mode
392
392
  if page_number > 0:
393
393
  params = {
394
- "page[size]": min(page_size, 5), # Keep responses very small to avoid errors
394
+ "page[size]": page_size, # Use requested page size (already limited to max 20)
395
395
  "page[number]": page_number,
396
396
  "include": "",
397
+ "fields[incidents]": "id,title,summary,status,severity,created_at,updated_at,url,started_at",
397
398
  }
398
399
  if query:
399
400
  params["filter[search]"] = query
@@ -409,7 +410,7 @@ def create_rootly_mcp_server(
409
410
  # Multi-page mode (page_number = 0)
410
411
  all_incidents = []
411
412
  current_page = 1
412
- effective_page_size = min(page_size, 5) # Keep responses very small to avoid errors
413
+ effective_page_size = page_size # Use requested page size (already limited to max 20)
413
414
  max_pages = 10 # Safety limit to prevent infinite loops
414
415
 
415
416
  try:
@@ -418,6 +419,7 @@ def create_rootly_mcp_server(
418
419
  "page[size]": effective_page_size,
419
420
  "page[number]": current_page,
420
421
  "include": "",
422
+ "fields[incidents]": "id,title,summary,status,severity,created_at,updated_at,url,started_at",
421
423
  }
422
424
  if query:
423
425
  params["filter[search]"] = query
@@ -922,6 +924,133 @@ def _filter_openapi_spec(spec: Dict[str, Any], allowed_paths: List[str]) -> Dict
922
924
  "description": param.get("description", "Parameter value")
923
925
  }
924
926
 
927
+ # Add/modify pagination limits to alerts and incident-related endpoints to prevent infinite loops
928
+ if method.lower() == "get" and ("alerts" in path.lower() or "incident" in path.lower()):
929
+ if "parameters" not in operation:
930
+ operation["parameters"] = []
931
+
932
+ # Find existing pagination parameters and update them with limits
933
+ page_size_param = None
934
+ page_number_param = None
935
+
936
+ for param in operation["parameters"]:
937
+ if param.get("name") == "page[size]":
938
+ page_size_param = param
939
+ elif param.get("name") == "page[number]":
940
+ page_number_param = param
941
+
942
+ # Update or add page[size] parameter with limits
943
+ if page_size_param:
944
+ # Update existing parameter with limits
945
+ if "schema" not in page_size_param:
946
+ page_size_param["schema"] = {}
947
+ page_size_param["schema"].update({
948
+ "type": "integer",
949
+ "default": 10,
950
+ "minimum": 1,
951
+ "maximum": 20,
952
+ "description": "Number of results per page (max: 20)"
953
+ })
954
+ else:
955
+ # Add new parameter
956
+ operation["parameters"].append({
957
+ "name": "page[size]",
958
+ "in": "query",
959
+ "required": False,
960
+ "schema": {
961
+ "type": "integer",
962
+ "default": 10,
963
+ "minimum": 1,
964
+ "maximum": 20,
965
+ "description": "Number of results per page (max: 20)"
966
+ }
967
+ })
968
+
969
+ # Update or add page[number] parameter with defaults
970
+ if page_number_param:
971
+ # Update existing parameter
972
+ if "schema" not in page_number_param:
973
+ page_number_param["schema"] = {}
974
+ page_number_param["schema"].update({
975
+ "type": "integer",
976
+ "default": 1,
977
+ "minimum": 1,
978
+ "description": "Page number to retrieve"
979
+ })
980
+ else:
981
+ # Add new parameter
982
+ operation["parameters"].append({
983
+ "name": "page[number]",
984
+ "in": "query",
985
+ "required": False,
986
+ "schema": {
987
+ "type": "integer",
988
+ "default": 1,
989
+ "minimum": 1,
990
+ "description": "Page number to retrieve"
991
+ }
992
+ })
993
+
994
+ # Add sparse fieldsets for alerts endpoints to reduce payload size
995
+ if "alert" in path.lower():
996
+ # Add fields[alerts] parameter with essential fields only - make it required with default
997
+ operation["parameters"].append({
998
+ "name": "fields[alerts]",
999
+ "in": "query",
1000
+ "required": True,
1001
+ "schema": {
1002
+ "type": "string",
1003
+ "default": "id,summary,status,started_at,ended_at,short_id,alert_urgency_id,source,noise",
1004
+ "description": "Comma-separated list of alert fields to include (reduces payload size)"
1005
+ }
1006
+ })
1007
+
1008
+ # Add include parameter for alerts endpoints to minimize relationships
1009
+ if "alert" in path.lower():
1010
+ # Check if include parameter already exists
1011
+ include_param_exists = any(param.get("name") == "include" for param in operation["parameters"])
1012
+ if not include_param_exists:
1013
+ operation["parameters"].append({
1014
+ "name": "include",
1015
+ "in": "query",
1016
+ "required": True,
1017
+ "schema": {
1018
+ "type": "string",
1019
+ "default": "",
1020
+ "description": "Related resources to include (empty for minimal payload)"
1021
+ }
1022
+ })
1023
+
1024
+ # Add sparse fieldsets for incidents endpoints to reduce payload size
1025
+ if "incident" in path.lower():
1026
+ # Add fields[incidents] parameter with essential fields only - make it required with default
1027
+ operation["parameters"].append({
1028
+ "name": "fields[incidents]",
1029
+ "in": "query",
1030
+ "required": True,
1031
+ "schema": {
1032
+ "type": "string",
1033
+ "default": "id,title,summary,status,severity,created_at,updated_at,url,started_at",
1034
+ "description": "Comma-separated list of incident fields to include (reduces payload size)"
1035
+ }
1036
+ })
1037
+
1038
+ # Add include parameter for incidents endpoints to minimize relationships
1039
+ if "incident" in path.lower():
1040
+ # Check if include parameter already exists
1041
+ include_param_exists = any(param.get("name") == "include" for param in operation["parameters"])
1042
+ if not include_param_exists:
1043
+ operation["parameters"].append({
1044
+ "name": "include",
1045
+ "in": "query",
1046
+ "required": True,
1047
+ "schema": {
1048
+ "type": "string",
1049
+ "default": "",
1050
+ "description": "Related resources to include (empty for minimal payload)"
1051
+ }
1052
+ })
1053
+
925
1054
  # Also clean up any remaining broken references in components
926
1055
  if "components" in filtered_spec and "schemas" in filtered_spec["components"]:
927
1056
  schemas = filtered_spec["components"]["schemas"]
@@ -935,6 +1064,52 @@ def _filter_openapi_spec(spec: Dict[str, Any], allowed_paths: List[str]) -> Dict
935
1064
  logger.warning(f"Removing schema with broken references: {schema_name}")
936
1065
  del schemas[schema_name]
937
1066
 
1067
+ # Clean up any operation-level references to removed schemas
1068
+ removed_schemas = set()
1069
+ if "components" in filtered_spec and "schemas" in filtered_spec["components"]:
1070
+ removed_schemas = {"new_workflow", "update_workflow", "workflow", "workflow_task",
1071
+ "workflow_response", "workflow_list", "new_workflow_task",
1072
+ "update_workflow_task", "workflow_task_response", "workflow_task_list"}
1073
+
1074
+ for path, path_item in filtered_spec.get("paths", {}).items():
1075
+ for method, operation in path_item.items():
1076
+ if method.lower() not in ["get", "post", "put", "delete", "patch"]:
1077
+ continue
1078
+
1079
+ # Clean request body references
1080
+ if "requestBody" in operation:
1081
+ request_body = operation["requestBody"]
1082
+ if "content" in request_body:
1083
+ for content_type, content_info in request_body["content"].items():
1084
+ if "schema" in content_info and "$ref" in content_info["schema"]:
1085
+ ref_path = content_info["schema"]["$ref"]
1086
+ schema_name = ref_path.split("/")[-1]
1087
+ if schema_name in removed_schemas:
1088
+ # Replace with generic object schema
1089
+ content_info["schema"] = {
1090
+ "type": "object",
1091
+ "description": "Request data for this endpoint",
1092
+ "additionalProperties": True
1093
+ }
1094
+ logger.debug(f"Cleaned broken reference in {method.upper()} {path} request body: {ref_path}")
1095
+
1096
+ # Clean response references
1097
+ if "responses" in operation:
1098
+ for status_code, response in operation["responses"].items():
1099
+ if "content" in response:
1100
+ for content_type, content_info in response["content"].items():
1101
+ if "schema" in content_info and "$ref" in content_info["schema"]:
1102
+ ref_path = content_info["schema"]["$ref"]
1103
+ schema_name = ref_path.split("/")[-1]
1104
+ if schema_name in removed_schemas:
1105
+ # Replace with generic object schema
1106
+ content_info["schema"] = {
1107
+ "type": "object",
1108
+ "description": "Response data from this endpoint",
1109
+ "additionalProperties": True
1110
+ }
1111
+ logger.debug(f"Cleaned broken reference in {method.upper()} {path} response: {ref_path}")
1112
+
938
1113
  return filtered_spec
939
1114
 
940
1115
 
@@ -946,8 +1121,23 @@ def _has_broken_references(schema_def: Dict[str, Any]) -> bool:
946
1121
  broken_refs = [
947
1122
  "incident_trigger_params",
948
1123
  "new_workflow",
949
- "update_workflow",
950
- "workflow"
1124
+ "update_workflow",
1125
+ "workflow",
1126
+ "new_workflow_task",
1127
+ "update_workflow_task",
1128
+ "workflow_task",
1129
+ "workflow_task_response",
1130
+ "workflow_task_list",
1131
+ "workflow_response",
1132
+ "workflow_list",
1133
+ "workflow_custom_field_selection_response",
1134
+ "workflow_custom_field_selection_list",
1135
+ "workflow_form_field_condition_response",
1136
+ "workflow_form_field_condition_list",
1137
+ "workflow_group_response",
1138
+ "workflow_group_list",
1139
+ "workflow_run_response",
1140
+ "workflow_runs_list"
951
1141
  ]
952
1142
  if any(broken_ref in ref_path for broken_ref in broken_refs):
953
1143
  return True
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rootly-mcp-server
3
- Version: 2.0.12
3
+ Version: 2.0.14
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
@@ -28,6 +28,10 @@ Description-Content-Type: text/markdown
28
28
 
29
29
  # Rootly MCP Server
30
30
 
31
+ [![PyPI version](https://badge.fury.io/py/rootly-mcp-server.svg)](https://pypi.org/project/rootly-mcp-server/)
32
+ [![PyPI - Downloads](https://img.shields.io/pypi/dm/rootly-mcp-server)](https://pypi.org/project/rootly-mcp-server/)
33
+ [![Python Version](https://img.shields.io/pypi/pyversions/rootly-mcp-server.svg)](https://pypi.org/project/rootly-mcp-server/)
34
+
31
35
  An MCP server for the [Rootly API](https://docs.rootly.com/api-reference/overview) that integrates seamlessly with MCP-compatible editors like Cursor, Windsurf, and Claude. Resolve production incidents in under a minute without leaving your IDE.
32
36
 
33
37
  [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/install-mcp?name=rootly&config=eyJjb21tYW5kIjoibnB4IC15IG1jcC1yZW1vdGUgaHR0cHM6Ly9tY3Aucm9vdGx5LmNvbS9zc2UgLS1oZWFkZXIgQXV0aG9yaXphdGlvbjoke1JPT1RMWV9BVVRIX0hFQURFUn0iLCJlbnYiOnsiUk9PVExZX0FVVEhfSEVBREVSIjoiQmVhcmVyIDxZT1VSX1JPT1RMWV9BUElfVE9LRU4%2BIn19)
@@ -1,12 +1,12 @@
1
1
  rootly_mcp_server/__init__.py,sha256=rvIuqIyuzgC7b9qSnylrdDP2zPO-7Ou9AoblR6re1co,629
2
2
  rootly_mcp_server/__main__.py,sha256=_F4p65_VjnN84RtmEdESVLLH0tO5tL9qBfb2Xdvbj2E,6480
3
3
  rootly_mcp_server/client.py,sha256=uit-YijR7OAJtysBoclqnublEDVkFfcb29wSzhpBv44,4686
4
- rootly_mcp_server/server.py,sha256=5NyGWUOjz1C1kFbAbu2iMNfuKo53_Sq254vF0cEUSHE,41358
4
+ rootly_mcp_server/server.py,sha256=dlqsLnD3QzRtSlUYzK_PUe5zR9rbQRvySRSjmKbCy5w,51476
5
5
  rootly_mcp_server/smart_utils.py,sha256=lvGN9ITyJjBkm7ejpYagd8VWodLKnC6FmwECfCOcGwM,22973
6
6
  rootly_mcp_server/utils.py,sha256=NyxdcDiFGlV2a8eBO4lKgZg0D7Gxr6xUIB0YyJGgpPA,4165
7
7
  rootly_mcp_server/data/__init__.py,sha256=fO8a0bQnRVEoRMHKvhFzj10bhoaw7VsI51czc2MsUm4,143
8
- rootly_mcp_server-2.0.12.dist-info/METADATA,sha256=UJznyguav4QbdflENbArVS2rGL5f8KZKuA0RLDuN70M,8722
9
- rootly_mcp_server-2.0.12.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
- rootly_mcp_server-2.0.12.dist-info/entry_points.txt,sha256=NE33b8VgigVPGBkboyo6pvN1Vz35HZtLybxMO4Q03PI,70
11
- rootly_mcp_server-2.0.12.dist-info/licenses/LICENSE,sha256=c9w9ZZGl14r54tsP40oaq5adTVX_HMNHozPIH2ymzmw,11341
12
- rootly_mcp_server-2.0.12.dist-info/RECORD,,
8
+ rootly_mcp_server-2.0.14.dist-info/METADATA,sha256=XvbzBwNb-XkGeedNz2oZDRebwvBA1cUYSYSrPftB9L4,9078
9
+ rootly_mcp_server-2.0.14.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
+ rootly_mcp_server-2.0.14.dist-info/entry_points.txt,sha256=NE33b8VgigVPGBkboyo6pvN1Vz35HZtLybxMO4Q03PI,70
11
+ rootly_mcp_server-2.0.14.dist-info/licenses/LICENSE,sha256=c9w9ZZGl14r54tsP40oaq5adTVX_HMNHozPIH2ymzmw,11341
12
+ rootly_mcp_server-2.0.14.dist-info/RECORD,,