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.
Files changed (28) hide show
  1. {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/.github/workflows/pypi-release.yml +3 -2
  2. {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/.gitignore +5 -1
  3. {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/Dockerfile +3 -1
  4. {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/PKG-INFO +28 -26
  5. {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/README.md +25 -24
  6. {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/pyproject.toml +3 -4
  7. rootly_mcp_server-2.0.9/rootly_fastmcp_server_routemap.py +210 -0
  8. rootly_mcp_server-2.0.9/rootly_openapi.json +97254 -0
  9. {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/src/rootly_mcp_server/__init__.py +1 -1
  10. {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/src/rootly_mcp_server/__main__.py +6 -6
  11. {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/src/rootly_mcp_server/client.py +2 -2
  12. rootly_mcp_server-2.0.9/src/rootly_mcp_server/routemap_server.py +206 -0
  13. {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/src/rootly_mcp_server/server.py +318 -184
  14. {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/src/rootly_mcp_server/test_client.py +11 -9
  15. rootly_mcp_server-2.0.9/src/rootly_mcp_server/utils.py +105 -0
  16. {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/uv.lock +354 -401
  17. rootly_mcp_server-2.0.6/CLAUDE.md +0 -68
  18. rootly_mcp_server-2.0.6/rootly_fastmcp_server_routemap.py +0 -285
  19. rootly_mcp_server-2.0.6/src/rootly_mcp_server/rootly_openapi_loader.py +0 -109
  20. rootly_mcp_server-2.0.6/src/rootly_mcp_server/routemap_server.py +0 -291
  21. {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/.semaphore/deploy.yml +0 -0
  22. {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/.semaphore/semaphore.yml +0 -0
  23. {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/.semaphore/update-task-definition.sh +0 -0
  24. {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/LICENSE +0 -0
  25. {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/rootly-mcp-server-demo.gif +0 -0
  26. {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/rootly_fastmcp_server.py +0 -0
  27. {rootly_mcp_server-2.0.6 → rootly_mcp_server-2.0.9}/rootly_openapi_loader.py +0 -0
  28. {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 build twine
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/*
@@ -187,4 +187,8 @@ swagger.json
187
187
 
188
188
  # Test outputs and temporary files
189
189
  test_output/
190
- *.tmp
190
+ *.tmp
191
+
192
+ # Markdown files (exclude all except README)
193
+ *.md
194
+ !README.md
@@ -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", "--host", "0.0.0.0", "--port", "8000", "--hosted"]
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.6
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: fastmcp==2.10.5
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 test_mcp_client.py
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 test_mcp_client.py
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.6"
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
- # Pinned to 2.10.5 to avoid unexpected breaking changes - to upgrade, just bump the pinned version here
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()