rootly-mcp-server 1.0.0__py3-none-any.whl → 2.0.1__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.
@@ -1,67 +1,148 @@
1
+ #!/usr/bin/env python3
1
2
  """
2
- Test script for Rootly MCP Server.
3
+ Test client for the Rootly MCP Server
3
4
 
4
- This script tests the default pagination for incidents endpoints.
5
+ This script demonstrates how to use the Rootly MCP Server.
5
6
  """
6
7
 
7
- import json
8
- import logging
8
+ import asyncio
9
9
  import os
10
10
  import sys
11
+ from pathlib import Path
11
12
 
12
- # Configure logging
13
- logging.basicConfig(
14
- level=logging.INFO,
15
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
16
- )
13
+ # Add the src directory to the Python path
14
+ sys.path.insert(0, str(Path(__file__).parent.parent))
17
15
 
18
- # Add the parent directory to the path so we can import the package
19
- sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')))
16
+ from rootly_mcp_server.server import create_rootly_mcp_server
20
17
 
21
- from rootly_mcp_server import RootlyMCPServer
22
18
 
23
- def test_incidents_pagination():
24
- """Test that incidents endpoints have default pagination."""
19
+ async def test_server():
20
+ """Test the Rootly MCP server."""
21
+ print("Creating Rootly MCP Server...")
25
22
 
26
- # Create a server instance
27
- server = RootlyMCPServer(default_page_size=5) # Use a smaller page size for testing
28
-
29
- # Find an incidents endpoint tool
30
- incidents_tool = None
31
- for tool_name in server.list_tools():
32
- if "incidents" in tool_name and tool_name.endswith("_get"):
33
- incidents_tool = tool_name
34
- break
35
-
36
- if not incidents_tool:
37
- logging.error("No incidents GET endpoint found")
38
- return
39
-
40
- logging.info(f"Testing pagination with tool: {incidents_tool}")
41
-
42
- # Call the tool
43
23
  try:
44
- result = server.invoke_tool(incidents_tool, {})
45
- result_json = json.loads(result)
24
+ # Create the server with a subset of endpoints for testing
25
+ server = create_rootly_mcp_server(
26
+ name="TestRootly",
27
+ allowed_paths=[
28
+ "/incidents",
29
+ "/alerts",
30
+ "/teams",
31
+ "/services"
32
+ ],
33
+ hosted=False # Use local API token
34
+ )
46
35
 
47
- # Check if the result has pagination info
48
- if "meta" in result_json and "pagination" in result_json["meta"]:
49
- pagination = result_json["meta"]["pagination"]
50
- logging.info(f"Pagination info: {pagination}")
36
+ print(f"✅ Server created successfully")
37
+ print(f"Server type: {type(server)}")
38
+
39
+ # Use the get_tools method to access tools
40
+ try:
41
+ tools = await server.get_tools()
42
+ print(f"Tools type: {type(tools)}")
43
+ print(f"Tools: {tools}")
44
+
45
+ # Handle both dict and list cases
46
+ if isinstance(tools, dict):
47
+ tools_list = list(tools.values())
48
+ tools_names = list(tools.keys())
49
+ tool_count = len(tools)
50
+ elif isinstance(tools, list):
51
+ tools_list = tools
52
+ tools_names = [getattr(tool, 'name', f'tool_{i}') for i, tool in enumerate(tools)]
53
+ tool_count = len(tools)
54
+ else:
55
+ tools_list = []
56
+ tools_names = []
57
+ tool_count = 0
58
+
59
+ print(f"Found {tool_count} tools via get_tools() method")
51
60
 
52
- if pagination.get("per_page") == 5:
53
- logging.info("✅ Default pagination applied successfully!")
61
+ # List the registered tools
62
+ if tool_count > 0:
63
+ print(f"\n📋 Registered tools ({tool_count}):")
64
+ for i, tool in enumerate(tools_list):
65
+ if isinstance(tools, dict):
66
+ tool_name = tools_names[i]
67
+ else:
68
+ tool_name = getattr(tool, 'name', f'tool_{i}')
69
+
70
+ description = getattr(tool, 'description', 'No description')
71
+ print(f" • {tool_name}: {description[:100]}...")
72
+ print(f" Tool type: {type(tool)}")
73
+ print(f" Tool attributes: {[attr for attr in dir(tool) if not attr.startswith('_')][:10]}")
74
+
75
+ # Show parameter schema if available
76
+ if hasattr(tool, 'inputSchema') and tool.inputSchema:
77
+ props = tool.inputSchema.get('properties', {})
78
+ if props:
79
+ print(f" Parameters: {', '.join(props.keys())}")
54
80
  else:
55
- logging.warning(f" Default pagination not applied. Per page: {pagination.get('per_page')}")
56
- else:
57
- logging.warning("❌ No pagination info found in response")
81
+ print(f"\n⚠️ No tools found")
82
+
83
+ # Test accessing a specific tool
84
+ if tool_count > 0:
85
+ print(f"\n🔍 Testing tool access...")
86
+ if isinstance(tools, dict):
87
+ first_tool_name = tools_names[0]
88
+ first_tool = tools[first_tool_name]
89
+ else:
90
+ first_tool = tools_list[0]
91
+ first_tool_name = getattr(first_tool, 'name', 'unknown')
92
+
93
+ print(f" ✅ First tool: {first_tool_name}")
94
+ print(f" Tool details: {first_tool}")
95
+
96
+ # Try to get tool by name
97
+ try:
98
+ retrieved_tool = await server.get_tool(first_tool_name)
99
+ if retrieved_tool:
100
+ print(f" ✅ Successfully retrieved tool by name: {first_tool_name}")
101
+ else:
102
+ print(f" ❌ Could not retrieve tool by name: {first_tool_name}")
103
+ except Exception as e:
104
+ print(f" ❌ Error retrieving tool: {e}")
58
105
 
59
- # Log the number of items returned
60
- if "data" in result_json:
61
- logging.info(f"Number of items returned: {len(result_json['data'])}")
106
+ except Exception as e:
107
+ print(f" Error accessing tools: {e}")
108
+ import traceback
109
+ traceback.print_exc()
110
+ tool_count = 0
111
+
112
+ print(f"\n🎉 Test completed successfully!")
113
+ print(f"Total tools found: {tool_count}")
62
114
 
63
115
  except Exception as e:
64
- logging.error(f"Error testing pagination: {e}")
116
+ print(f"Error creating server: {e}")
117
+ import traceback
118
+ traceback.print_exc()
119
+ return False
120
+
121
+ return True
122
+
123
+
124
+ def main():
125
+ """Main function."""
126
+ print("Rootly MCP Server Test")
127
+ print("=" * 50)
128
+
129
+ # Check for API token
130
+ api_token = os.getenv("ROOTLY_API_TOKEN")
131
+ if not api_token:
132
+ print("⚠️ Warning: ROOTLY_API_TOKEN not set. Server will use mock client.")
133
+ else:
134
+ print(f"✅ API token found: {api_token[:10]}...")
135
+
136
+ # Run the test
137
+ success = asyncio.run(test_server())
138
+
139
+ if success:
140
+ print("\n🎉 All tests passed!")
141
+ sys.exit(0)
142
+ else:
143
+ print("\n❌ Tests failed!")
144
+ sys.exit(1)
145
+
65
146
 
66
147
  if __name__ == "__main__":
67
- test_incidents_pagination()
148
+ main()
@@ -0,0 +1,225 @@
1
+ Metadata-Version: 2.4
2
+ Name: rootly-mcp-server
3
+ Version: 2.0.1
4
+ Summary: A Model Context Protocol server for Rootly APIs using OpenAPI spec
5
+ Project-URL: Homepage, https://github.com/Rootly-AI-Labs/Rootly-MCP-server
6
+ Project-URL: Issues, https://github.com/Rootly-AI-Labs/Rootly-MCP-server/issues
7
+ Author-email: Rootly AI Labs <support@rootly.com>
8
+ License-Expression: Apache-2.0
9
+ License-File: LICENSE
10
+ Keywords: automation,incidents,llm,mcp,rootly
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Software Development :: Build Tools
16
+ Requires-Python: >=3.12
17
+ Requires-Dist: fastmcp==2.4.0
18
+ Requires-Dist: httpx>=0.24.0
19
+ Requires-Dist: pydantic>=2.0.0
20
+ Requires-Dist: requests>=2.28.0
21
+ Provides-Extra: dev
22
+ Requires-Dist: black>=23.0.0; extra == 'dev'
23
+ Requires-Dist: isort>=5.0.0; extra == 'dev'
24
+ Description-Content-Type: text/markdown
25
+
26
+ # Rootly MCP Server
27
+
28
+ 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.
29
+
30
+ [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/install-mcp?name=rootly&config=eyJjb21tYW5kIjoibnB4IC15IG1jcC1yZW1vdGUgaHR0cHM6Ly9tY3Aucm9vdGx5LmNvbS9zc2UgLS1oZWFkZXIgQXV0aG9yaXphdGlvbjoke1JPT1RMWV9BVVRIX0hFQURFUn0iLCJlbnYiOnsiUk9PVExZX0FVVEhfSEVBREVSIjoiQmVhcmVyIDxZT1VSX1JPT1RMWV9BUElfVE9LRU4%2BIn19)
31
+
32
+ ![Demo GIF](rootly-mcp-server-demo.gif)
33
+
34
+ ## Prerequisites
35
+
36
+ - Python 3.12 or higher
37
+ - `uv` package manager
38
+ ```bash
39
+ curl -LsSf https://astral.sh/uv/install.sh | sh
40
+ ```
41
+ - [Rootly API token](https://docs.rootly.com/api-reference/overview#how-to-generate-an-api-key%3F)
42
+
43
+ ## Installation
44
+
45
+ Install via our [PyPi package](https://pypi.org/project/rootly-mcp-server/) or by cloning this repository.
46
+
47
+ Configure your MCP-compatible editor (tested with Cursor and Windsurf) with the following:
48
+
49
+ ### With uv
50
+
51
+ ```json
52
+ {
53
+ "mcpServers": {
54
+ "rootly": {
55
+ "command": "uv",
56
+ "args": [
57
+ "tool",
58
+ "run",
59
+ "--from",
60
+ "rootly-mcp-server",
61
+ "rootly-mcp-server",
62
+ ],
63
+ "env": {
64
+ "ROOTLY_API_TOKEN": "<YOUR_ROOTLY_API_TOKEN>"
65
+ }
66
+ }
67
+ }
68
+ }
69
+ ```
70
+
71
+ ### With uv-tool-uvx
72
+
73
+ ```json
74
+ {
75
+ "mcpServers": {
76
+ "rootly": {
77
+ "command": "uvx",
78
+ "args": [
79
+ "--from",
80
+ "rootly-mcp-server",
81
+ "rootly-mcp-server",
82
+ ],
83
+ "env": {
84
+ "ROOTLY_API_TOKEN": "<YOUR_ROOTLY_API_TOKEN>"
85
+ }
86
+ }
87
+ }
88
+ }
89
+ ```
90
+
91
+ To customize `allowed_paths` and access additional Rootly API paths, clone the repository and use this configuration:
92
+
93
+ ```json
94
+ {
95
+ "mcpServers": {
96
+ "rootly": {
97
+ "command": "uv",
98
+ "args": [
99
+ "run",
100
+ "--directory",
101
+ "/path/to/rootly-mcp-server",
102
+ "rootly-mcp-server"
103
+ ],
104
+ "env": {
105
+ "ROOTLY_API_TOKEN": "<YOUR_ROOTLY_API_TOKEN>"
106
+ }
107
+ }
108
+ }
109
+ }
110
+ ```
111
+
112
+ ## Features
113
+
114
+ - **Dynamic Tool Generation**: Automatically creates MCP resources from Rootly's OpenAPI (Swagger) specification
115
+ - **Smart Pagination**: Defaults to 10 items per request for incident endpoints to prevent context window overflow
116
+ - **API Filtering**: Limits exposed API endpoints for security and performance
117
+
118
+ ### Whitelisted Endpoints
119
+
120
+ By default, the following Rootly API endpoints are exposed to the AI agent (see `allowed_paths` in `src/rootly_mcp_server/server.py`):
121
+
122
+ ```
123
+ /v1/incidents
124
+ /v1/incidents/{incident_id}/alerts
125
+ /v1/alerts
126
+ /v1/alerts/{alert_id}
127
+ /v1/severities
128
+ /v1/severities/{severity_id}
129
+ /v1/teams
130
+ /v1/teams/{team_id}
131
+ /v1/services
132
+ /v1/services/{service_id}
133
+ /v1/functionalities
134
+ /v1/functionalities/{functionality_id}
135
+ /v1/incident_types
136
+ /v1/incident_types/{incident_type_id}
137
+ /v1/incident_action_items
138
+ /v1/incident_action_items/{incident_action_item_id}
139
+ /v1/incidents/{incident_id}/action_items
140
+ /v1/workflows
141
+ /v1/workflows/{workflow_id}
142
+ /v1/workflow_runs
143
+ /v1/workflow_runs/{workflow_run_id}
144
+ /v1/environments
145
+ /v1/environments/{environment_id}
146
+ /v1/users
147
+ /v1/users/{user_id}
148
+ /v1/users/me
149
+ /v1/status_pages
150
+ /v1/status_pages/{status_page_id}
151
+ ```
152
+
153
+ ### Why Path Limiting?
154
+
155
+ We limit exposed API paths for two key reasons:
156
+
157
+ 1. **Context Management**: Rootly's comprehensive API can overwhelm AI agents, affecting their ability to perform simple tasks effectively
158
+ 2. **Security**: Control which information and actions are accessible through the MCP server
159
+
160
+ To expose additional paths, modify the `allowed_paths` variable in `src/rootly_mcp_server/server.py`.
161
+
162
+ ## About Rootly AI Labs
163
+
164
+ This project was developed by [Rootly AI Labs](https://labs.rootly.ai/), where we're building the future of system reliability and operational excellence. As an open-source incubator, we share ideas, experiment, and rapidly prototype solutions that benefit the entire community.
165
+ ![Rootly AI logo](https://github.com/Rootly-AI-Labs/EventOrOutage/raw/main/rootly-ai.png)
166
+
167
+ ## Developer Setup & Troubleshooting
168
+
169
+ ### Prerequisites
170
+ - Python 3.12 or higher
171
+ - [`uv`](https://github.com/astral-sh/uv) for dependency management
172
+
173
+ ### 1. Set Up Virtual Environment
174
+
175
+ Create and activate a virtual environment:
176
+
177
+ ```bash
178
+ uv venv .venv
179
+ source .venv/bin/activate # Always activate before running scripts
180
+ ```
181
+
182
+ ### 2. Install Dependencies
183
+
184
+ Install all project dependencies:
185
+
186
+ ```bash
187
+ uv pip install .
188
+ ```
189
+
190
+ To add new dependencies during development:
191
+ ```bash
192
+ uv pip install <package>
193
+ ```
194
+
195
+ ### 3. Verify Installation
196
+
197
+ Run the test client to ensure everything is configured correctly:
198
+
199
+ ```bash
200
+ python test_mcp_client.py
201
+ ```
202
+
203
+ ### Connect to Hosted MCP Server
204
+
205
+ Alternatively, connect directly to our hosted MCP server:
206
+
207
+ ```json
208
+ {
209
+ "mcpServers": {
210
+ "rootly": {
211
+ "command": "npx",
212
+ "args": [
213
+ "-y",
214
+ "mcp-remote",
215
+ "https://mcp.rootly.com/sse",
216
+ "--header",
217
+ "Authorization:${ROOTLY_AUTH_HEADER}"
218
+ ],
219
+ "env": {
220
+ "ROOTLY_AUTH_HEADER": "Bearer <YOUR_ROOTLY_API_TOKEN>"
221
+ }
222
+ }
223
+ }
224
+ }
225
+ ```
@@ -0,0 +1,12 @@
1
+ rootly_mcp_server/__init__.py,sha256=H3Dd4Od-PEff8VdbMgnblS6cCEBCb9XqYdI_-wmtQqk,628
2
+ rootly_mcp_server/__main__.py,sha256=yOBEaQWEMZvsC7zT5no7yo1ISIvfeq7NlIPflBMoAFI,6447
3
+ rootly_mcp_server/client.py,sha256=05TsHVJ3WtLH0k4R19Yzwwx4xcmjCKH6hS3uKcTMwRA,4678
4
+ rootly_mcp_server/routemap_server.py,sha256=SloHu4ZTaFBusoIlhM2h7jfB8kFtMQrT3M_n7JAekug,6101
5
+ rootly_mcp_server/server.py,sha256=2paE39caYRVuQG4yWEEOqZewbvpFREmf-J01lPQ5elw,19756
6
+ rootly_mcp_server/test_client.py,sha256=8p1aJHrEt_Tj2NuJzTnTHw-ZeW816P99fJi5bhPidyc,5119
7
+ rootly_mcp_server/data/__init__.py,sha256=fO8a0bQnRVEoRMHKvhFzj10bhoaw7VsI51czc2MsUm4,143
8
+ rootly_mcp_server-2.0.1.dist-info/METADATA,sha256=SNQJLqa2_tBEV3ZRycB8NgnDt6jaw5UvuW1zMbI_R-A,6140
9
+ rootly_mcp_server-2.0.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
+ rootly_mcp_server-2.0.1.dist-info/entry_points.txt,sha256=NE33b8VgigVPGBkboyo6pvN1Vz35HZtLybxMO4Q03PI,70
11
+ rootly_mcp_server-2.0.1.dist-info/licenses/LICENSE,sha256=c9w9ZZGl14r54tsP40oaq5adTVX_HMNHozPIH2ymzmw,11341
12
+ rootly_mcp_server-2.0.1.dist-info/RECORD,,