rootly-mcp-server 2.0.6__tar.gz → 2.0.9__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.
- {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/.github/workflows/pypi-release.yml +3 -2
- {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/.gitignore +5 -1
- {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/Dockerfile +3 -1
- {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/PKG-INFO +28 -26
- {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/README.md +25 -24
- {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/pyproject.toml +3 -4
- rootly_mcp_server-2.0.9/rootly_fastmcp_server_routemap.py +210 -0
- rootly_mcp_server-2.0.9/rootly_openapi.json +97254 -0
- {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/src/rootly_mcp_server/__init__.py +1 -1
- {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/src/rootly_mcp_server/__main__.py +6 -6
- {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/src/rootly_mcp_server/client.py +2 -2
- rootly_mcp_server-2.0.9/src/rootly_mcp_server/routemap_server.py +206 -0
- {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/src/rootly_mcp_server/server.py +318 -184
- {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/src/rootly_mcp_server/test_client.py +11 -9
- rootly_mcp_server-2.0.9/src/rootly_mcp_server/utils.py +105 -0
- {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/uv.lock +354 -401
- rootly_mcp_server-2.0.6/CLAUDE.md +0 -68
- rootly_mcp_server-2.0.6/rootly_fastmcp_server_routemap.py +0 -285
- rootly_mcp_server-2.0.6/src/rootly_mcp_server/rootly_openapi_loader.py +0 -109
- rootly_mcp_server-2.0.6/src/rootly_mcp_server/routemap_server.py +0 -291
- {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/.semaphore/deploy.yml +0 -0
- {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/.semaphore/semaphore.yml +0 -0
- {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/.semaphore/update-task-definition.sh +0 -0
- {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/LICENSE +0 -0
- {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/rootly-mcp-server-demo.gif +0 -0
- {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/rootly_fastmcp_server.py +0 -0
- {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/rootly_openapi_loader.py +0 -0
- {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/src/rootly_mcp_server/data/__init__.py +0 -0
|
@@ -19,7 +19,8 @@ jobs:
|
|
|
19
19
|
|
|
20
20
|
- name: Install uv and build tools
|
|
21
21
|
run: |
|
|
22
|
-
pip install
|
|
22
|
+
pip install uv
|
|
23
|
+
uv pip install --system build twine
|
|
23
24
|
|
|
24
25
|
- name: Build package
|
|
25
26
|
run: |
|
|
@@ -30,4 +31,4 @@ jobs:
|
|
|
30
31
|
TWINE_USERNAME: __token__
|
|
31
32
|
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
|
|
32
33
|
run: |
|
|
33
|
-
twine upload dist/*
|
|
34
|
+
twine upload dist/*
|
|
@@ -23,6 +23,8 @@ EXPOSE 8000
|
|
|
23
23
|
|
|
24
24
|
# Set environment variables
|
|
25
25
|
ENV PYTHONUNBUFFERED=1
|
|
26
|
+
ENV FASTMCP_HOST=0.0.0.0
|
|
27
|
+
ENV FASTMCP_PORT=8000
|
|
26
28
|
|
|
27
29
|
# Run the application
|
|
28
|
-
CMD ["rootly-mcp-server", "--transport", "sse", "--log-level", "INFO", "--
|
|
30
|
+
CMD ["rootly-mcp-server", "--transport", "sse", "--log-level", "INFO", "--hosted"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rootly-mcp-server
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.9
|
|
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,7 +14,8 @@ 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:
|
|
17
|
+
Requires-Dist: brotli>=1.0.0
|
|
18
|
+
Requires-Dist: fastmcp>=2.9.0
|
|
18
19
|
Requires-Dist: httpx>=0.24.0
|
|
19
20
|
Requires-Dist: pydantic>=2.0.0
|
|
20
21
|
Requires-Dist: requests>=2.28.0
|
|
@@ -109,6 +110,30 @@ To customize `allowed_paths` and access additional Rootly API paths, clone the r
|
|
|
109
110
|
}
|
|
110
111
|
```
|
|
111
112
|
|
|
113
|
+
### Connect to Hosted MCP Server
|
|
114
|
+
|
|
115
|
+
Alternatively, connect directly to our hosted MCP server:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"mcpServers": {
|
|
120
|
+
"rootly": {
|
|
121
|
+
"command": "npx",
|
|
122
|
+
"args": [
|
|
123
|
+
"-y",
|
|
124
|
+
"mcp-remote",
|
|
125
|
+
"https://mcp.rootly.com/sse",
|
|
126
|
+
"--header",
|
|
127
|
+
"Authorization:${ROOTLY_AUTH_HEADER}"
|
|
128
|
+
],
|
|
129
|
+
"env": {
|
|
130
|
+
"ROOTLY_AUTH_HEADER": "Bearer <YOUR_ROOTLY_API_TOKEN>"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
112
137
|
## Features
|
|
113
138
|
|
|
114
139
|
- **Dynamic Tool Generation**: Automatically creates MCP resources from Rootly's OpenAPI (Swagger) specification
|
|
@@ -197,29 +222,6 @@ uv pip install <package>
|
|
|
197
222
|
Run the test client to ensure everything is configured correctly:
|
|
198
223
|
|
|
199
224
|
```bash
|
|
200
|
-
python
|
|
225
|
+
python src/rootly_mcp_server/test_client.py
|
|
201
226
|
```
|
|
202
227
|
|
|
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
|
-
```
|
|
@@ -84,6 +84,30 @@ To customize `allowed_paths` and access additional Rootly API paths, clone the r
|
|
|
84
84
|
}
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
+
### Connect to Hosted MCP Server
|
|
88
|
+
|
|
89
|
+
Alternatively, connect directly to our hosted MCP server:
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"mcpServers": {
|
|
94
|
+
"rootly": {
|
|
95
|
+
"command": "npx",
|
|
96
|
+
"args": [
|
|
97
|
+
"-y",
|
|
98
|
+
"mcp-remote",
|
|
99
|
+
"https://mcp.rootly.com/sse",
|
|
100
|
+
"--header",
|
|
101
|
+
"Authorization:${ROOTLY_AUTH_HEADER}"
|
|
102
|
+
],
|
|
103
|
+
"env": {
|
|
104
|
+
"ROOTLY_AUTH_HEADER": "Bearer <YOUR_ROOTLY_API_TOKEN>"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
87
111
|
## Features
|
|
88
112
|
|
|
89
113
|
- **Dynamic Tool Generation**: Automatically creates MCP resources from Rootly's OpenAPI (Swagger) specification
|
|
@@ -172,29 +196,6 @@ uv pip install <package>
|
|
|
172
196
|
Run the test client to ensure everything is configured correctly:
|
|
173
197
|
|
|
174
198
|
```bash
|
|
175
|
-
python
|
|
199
|
+
python src/rootly_mcp_server/test_client.py
|
|
176
200
|
```
|
|
177
201
|
|
|
178
|
-
### Connect to Hosted MCP Server
|
|
179
|
-
|
|
180
|
-
Alternatively, connect directly to our hosted MCP server:
|
|
181
|
-
|
|
182
|
-
```json
|
|
183
|
-
{
|
|
184
|
-
"mcpServers": {
|
|
185
|
-
"rootly": {
|
|
186
|
-
"command": "npx",
|
|
187
|
-
"args": [
|
|
188
|
-
"-y",
|
|
189
|
-
"mcp-remote",
|
|
190
|
-
"https://mcp.rootly.com/sse",
|
|
191
|
-
"--header",
|
|
192
|
-
"Authorization:${ROOTLY_AUTH_HEADER}"
|
|
193
|
-
],
|
|
194
|
-
"env": {
|
|
195
|
-
"ROOTLY_AUTH_HEADER": "Bearer <YOUR_ROOTLY_API_TOKEN>"
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "rootly-mcp-server"
|
|
3
|
-
version = "2.0.
|
|
3
|
+
version = "2.0.9"
|
|
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"
|
|
@@ -16,12 +16,11 @@ classifiers = [
|
|
|
16
16
|
"Programming Language :: Python :: 3.12",
|
|
17
17
|
]
|
|
18
18
|
dependencies = [
|
|
19
|
-
|
|
20
|
-
# and run `uv sync` to update the lockfile.
|
|
21
|
-
"fastmcp==2.10.5",
|
|
19
|
+
"fastmcp>=2.9.0",
|
|
22
20
|
"requests>=2.28.0", # For API calls
|
|
23
21
|
"httpx>=0.24.0", # For async HTTP client
|
|
24
22
|
"pydantic>=2.0.0", # For data validation
|
|
23
|
+
"brotli>=1.0.0", # For Brotli compression support in httpx
|
|
25
24
|
]
|
|
26
25
|
|
|
27
26
|
[project.urls]
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Rootly FastMCP Server (RouteMap Version)
|
|
4
|
+
|
|
5
|
+
Alternative implementation using FastMCP's RouteMap system for filtering
|
|
6
|
+
instead of pre-filtering the OpenAPI spec.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import httpx
|
|
10
|
+
from fastmcp import FastMCP
|
|
11
|
+
from fastmcp.server.openapi import RouteMap, MCPType
|
|
12
|
+
import os
|
|
13
|
+
import logging
|
|
14
|
+
from rootly_openapi_loader import load_rootly_openapi_spec
|
|
15
|
+
|
|
16
|
+
# Configure logging
|
|
17
|
+
logging.basicConfig(level=logging.INFO)
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
def create_rootly_mcp_server():
|
|
21
|
+
"""Create and configure the Rootly MCP server using RouteMap filtering."""
|
|
22
|
+
|
|
23
|
+
# Get Rootly API token from environment
|
|
24
|
+
ROOTLY_API_TOKEN = os.getenv("ROOTLY_API_TOKEN")
|
|
25
|
+
if not ROOTLY_API_TOKEN:
|
|
26
|
+
raise ValueError("ROOTLY_API_TOKEN environment variable is required")
|
|
27
|
+
|
|
28
|
+
logger.info("Creating authenticated HTTP client...")
|
|
29
|
+
# Create authenticated HTTP client
|
|
30
|
+
client = httpx.AsyncClient(
|
|
31
|
+
base_url="https://api.rootly.com",
|
|
32
|
+
headers={
|
|
33
|
+
"Authorization": f"Bearer {ROOTLY_API_TOKEN}",
|
|
34
|
+
"Content-Type": "application/vnd.api+json",
|
|
35
|
+
"User-Agent": "Rootly-FastMCP-Server/1.0"
|
|
36
|
+
},
|
|
37
|
+
timeout=30.0
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
logger.info("Loading OpenAPI specification...")
|
|
41
|
+
# Load OpenAPI spec with smart fallback logic
|
|
42
|
+
openapi_spec = load_rootly_openapi_spec()
|
|
43
|
+
logger.info("✅ Successfully loaded OpenAPI specification")
|
|
44
|
+
|
|
45
|
+
logger.info("Fixing OpenAPI spec for FastMCP compatibility...")
|
|
46
|
+
# Fix array types for FastMCP compatibility
|
|
47
|
+
def fix_array_types(obj):
|
|
48
|
+
if isinstance(obj, dict):
|
|
49
|
+
keys_to_process = list(obj.keys())
|
|
50
|
+
for key in keys_to_process:
|
|
51
|
+
value = obj[key]
|
|
52
|
+
if key == 'type' and isinstance(value, list):
|
|
53
|
+
non_null_types = [t for t in value if t != 'null']
|
|
54
|
+
if len(non_null_types) >= 1:
|
|
55
|
+
obj[key] = non_null_types[0]
|
|
56
|
+
obj['nullable'] = True
|
|
57
|
+
else:
|
|
58
|
+
fix_array_types(value)
|
|
59
|
+
elif isinstance(obj, list):
|
|
60
|
+
for item in obj:
|
|
61
|
+
fix_array_types(item)
|
|
62
|
+
|
|
63
|
+
fix_array_types(openapi_spec)
|
|
64
|
+
logger.info("✅ Fixed OpenAPI spec compatibility issues")
|
|
65
|
+
|
|
66
|
+
logger.info("Creating FastMCP server with RouteMap filtering...")
|
|
67
|
+
|
|
68
|
+
# Define custom route maps for filtering specific endpoints
|
|
69
|
+
route_maps = [
|
|
70
|
+
# Core incident management
|
|
71
|
+
RouteMap(
|
|
72
|
+
pattern=r"^/v1/incidents$",
|
|
73
|
+
mcp_type=MCPType.TOOL,
|
|
74
|
+
mcp_tags={"incidents", "core"}
|
|
75
|
+
),
|
|
76
|
+
RouteMap(
|
|
77
|
+
pattern=r"^/v1/incidents/\{incident_id\}/alerts$",
|
|
78
|
+
mcp_type=MCPType.TOOL,
|
|
79
|
+
mcp_tags={"incidents", "alerts", "relationship"}
|
|
80
|
+
),
|
|
81
|
+
RouteMap(
|
|
82
|
+
pattern=r"^/v1/incidents/\{incident_id\}/action_items$",
|
|
83
|
+
mcp_type=MCPType.TOOL,
|
|
84
|
+
mcp_tags={"incidents", "action-items", "relationship"}
|
|
85
|
+
),
|
|
86
|
+
|
|
87
|
+
# Alert management
|
|
88
|
+
RouteMap(
|
|
89
|
+
pattern=r"^/v1/alerts$",
|
|
90
|
+
mcp_type=MCPType.TOOL,
|
|
91
|
+
mcp_tags={"alerts", "core"}
|
|
92
|
+
),
|
|
93
|
+
RouteMap(
|
|
94
|
+
pattern=r"^/v1/alerts/\{alert_id\}$",
|
|
95
|
+
mcp_type=MCPType.TOOL,
|
|
96
|
+
mcp_tags={"alerts", "detail"}
|
|
97
|
+
),
|
|
98
|
+
|
|
99
|
+
# Configuration entities
|
|
100
|
+
RouteMap(
|
|
101
|
+
pattern=r"^/v1/severities(\{severity_id\})?$",
|
|
102
|
+
mcp_type=MCPType.TOOL,
|
|
103
|
+
mcp_tags={"configuration", "severities"}
|
|
104
|
+
),
|
|
105
|
+
RouteMap(
|
|
106
|
+
pattern=r"^/v1/incident_types(\{incident_type_id\})?$",
|
|
107
|
+
mcp_type=MCPType.TOOL,
|
|
108
|
+
mcp_tags={"configuration", "incident-types"}
|
|
109
|
+
),
|
|
110
|
+
RouteMap(
|
|
111
|
+
pattern=r"^/v1/functionalities(\{functionality_id\})?$",
|
|
112
|
+
mcp_type=MCPType.TOOL,
|
|
113
|
+
mcp_tags={"configuration", "functionalities"}
|
|
114
|
+
),
|
|
115
|
+
|
|
116
|
+
# Organization
|
|
117
|
+
RouteMap(
|
|
118
|
+
pattern=r"^/v1/teams(\{team_id\})?$",
|
|
119
|
+
mcp_type=MCPType.TOOL,
|
|
120
|
+
mcp_tags={"organization", "teams"}
|
|
121
|
+
),
|
|
122
|
+
RouteMap(
|
|
123
|
+
pattern=r"^/v1/users(\{user_id\}|/me)?$",
|
|
124
|
+
mcp_type=MCPType.TOOL,
|
|
125
|
+
mcp_tags={"organization", "users"}
|
|
126
|
+
),
|
|
127
|
+
|
|
128
|
+
# Infrastructure
|
|
129
|
+
RouteMap(
|
|
130
|
+
pattern=r"^/v1/services(\{service_id\})?$",
|
|
131
|
+
mcp_type=MCPType.TOOL,
|
|
132
|
+
mcp_tags={"infrastructure", "services"}
|
|
133
|
+
),
|
|
134
|
+
RouteMap(
|
|
135
|
+
pattern=r"^/v1/environments(\{environment_id\})?$",
|
|
136
|
+
mcp_type=MCPType.TOOL,
|
|
137
|
+
mcp_tags={"infrastructure", "environments"}
|
|
138
|
+
),
|
|
139
|
+
|
|
140
|
+
# Action items
|
|
141
|
+
RouteMap(
|
|
142
|
+
pattern=r"^/v1/incident_action_items(\{incident_action_item_id\})?$",
|
|
143
|
+
mcp_type=MCPType.TOOL,
|
|
144
|
+
mcp_tags={"action-items", "management"}
|
|
145
|
+
),
|
|
146
|
+
|
|
147
|
+
# Workflows
|
|
148
|
+
RouteMap(
|
|
149
|
+
pattern=r"^/v1/workflows(\{workflow_id\})?$",
|
|
150
|
+
mcp_type=MCPType.TOOL,
|
|
151
|
+
mcp_tags={"automation", "workflows"}
|
|
152
|
+
),
|
|
153
|
+
RouteMap(
|
|
154
|
+
pattern=r"^/v1/workflow_runs(\{workflow_run_id\})?$",
|
|
155
|
+
mcp_type=MCPType.TOOL,
|
|
156
|
+
mcp_tags={"automation", "workflow-runs"}
|
|
157
|
+
),
|
|
158
|
+
|
|
159
|
+
# Status pages
|
|
160
|
+
RouteMap(
|
|
161
|
+
pattern=r"^/v1/status_pages(\{status_page_id\})?$",
|
|
162
|
+
mcp_type=MCPType.TOOL,
|
|
163
|
+
mcp_tags={"communication", "status-pages"}
|
|
164
|
+
),
|
|
165
|
+
|
|
166
|
+
# Exclude everything else
|
|
167
|
+
RouteMap(
|
|
168
|
+
pattern=r".*",
|
|
169
|
+
mcp_type=MCPType.EXCLUDE
|
|
170
|
+
)
|
|
171
|
+
]
|
|
172
|
+
|
|
173
|
+
# Create MCP server with custom route maps
|
|
174
|
+
mcp = FastMCP.from_openapi(
|
|
175
|
+
openapi_spec=openapi_spec,
|
|
176
|
+
client=client,
|
|
177
|
+
name="Rootly API Server (RouteMap Filtered)",
|
|
178
|
+
timeout=30.0,
|
|
179
|
+
tags={"rootly", "incident-management", "evaluation"},
|
|
180
|
+
route_maps=route_maps
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
logger.info(f"✅ Created MCP server with RouteMap filtering successfully")
|
|
184
|
+
logger.info("🚀 Selected Rootly API endpoints are now available as MCP tools")
|
|
185
|
+
|
|
186
|
+
return mcp
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def main():
|
|
192
|
+
"""Main entry point."""
|
|
193
|
+
try:
|
|
194
|
+
logger.info("🚀 Starting Rootly FastMCP Server (RouteMap Version)...")
|
|
195
|
+
mcp = create_rootly_mcp_server()
|
|
196
|
+
|
|
197
|
+
logger.info("🌐 Server starting on stdio transport...")
|
|
198
|
+
logger.info("Ready for MCP client connections!")
|
|
199
|
+
|
|
200
|
+
# Run the MCP server
|
|
201
|
+
mcp.run()
|
|
202
|
+
|
|
203
|
+
except KeyboardInterrupt:
|
|
204
|
+
logger.info("🛑 Server stopped by user")
|
|
205
|
+
except Exception as e:
|
|
206
|
+
logger.error(f"❌ Server error: {e}")
|
|
207
|
+
raise
|
|
208
|
+
|
|
209
|
+
if __name__ == "__main__":
|
|
210
|
+
main()
|