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.
- rootly_mcp_server/__init__.py +1 -1
- rootly_mcp_server/__main__.py +78 -10
- rootly_mcp_server/client.py +40 -26
- rootly_mcp_server/routemap_server.py +206 -0
- rootly_mcp_server/server.py +439 -365
- rootly_mcp_server/test_client.py +128 -47
- rootly_mcp_server-2.0.1.dist-info/METADATA +225 -0
- rootly_mcp_server-2.0.1.dist-info/RECORD +12 -0
- rootly_mcp_server/data/swagger.json +0 -1
- rootly_mcp_server-1.0.0.dist-info/METADATA +0 -128
- rootly_mcp_server-1.0.0.dist-info/RECORD +0 -12
- {rootly_mcp_server-1.0.0.dist-info → rootly_mcp_server-2.0.1.dist-info}/WHEEL +0 -0
- {rootly_mcp_server-1.0.0.dist-info → rootly_mcp_server-2.0.1.dist-info}/entry_points.txt +0 -0
- {rootly_mcp_server-1.0.0.dist-info → rootly_mcp_server-2.0.1.dist-info}/licenses/LICENSE +0 -0
rootly_mcp_server/test_client.py
CHANGED
|
@@ -1,67 +1,148 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
1
2
|
"""
|
|
2
|
-
Test
|
|
3
|
+
Test client for the Rootly MCP Server
|
|
3
4
|
|
|
4
|
-
This script
|
|
5
|
+
This script demonstrates how to use the Rootly MCP Server.
|
|
5
6
|
"""
|
|
6
7
|
|
|
7
|
-
import
|
|
8
|
-
import logging
|
|
8
|
+
import asyncio
|
|
9
9
|
import os
|
|
10
10
|
import sys
|
|
11
|
+
from pathlib import Path
|
|
11
12
|
|
|
12
|
-
#
|
|
13
|
-
|
|
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
|
-
|
|
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
|
|
24
|
-
"""Test
|
|
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
|
-
|
|
45
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
53
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
[](https://cursor.com/install-mcp?name=rootly&config=eyJjb21tYW5kIjoibnB4IC15IG1jcC1yZW1vdGUgaHR0cHM6Ly9tY3Aucm9vdGx5LmNvbS9zc2UgLS1oZWFkZXIgQXV0aG9yaXphdGlvbjoke1JPT1RMWV9BVVRIX0hFQURFUn0iLCJlbnYiOnsiUk9PVExZX0FVVEhfSEVBREVSIjoiQmVhcmVyIDxZT1VSX1JPT1RMWV9BUElfVE9LRU4%2BIn19)
|
|
31
|
+
|
|
32
|
+

|
|
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
|
+

|
|
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,,
|