rootly-mcp-server 2.0.10__py3-none-any.whl → 2.0.11__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,206 +0,0 @@
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
- import sys
15
- from pathlib import Path
16
- from typing import Optional, List
17
-
18
- # Import the shared OpenAPI loader
19
- sys.path.append(str(Path(__file__).parent.parent.parent))
20
- from rootly_openapi_loader import load_rootly_openapi_spec
21
-
22
- # Configure logging
23
- logging.basicConfig(level=logging.INFO)
24
- logger = logging.getLogger(__name__)
25
-
26
- def create_rootly_mcp_server(
27
- swagger_path: Optional[str] = None,
28
- name: str = "Rootly API Server (RouteMap Filtered)",
29
- allowed_paths: Optional[List[str]] = None,
30
- hosted: bool = False,
31
- base_url: Optional[str] = None,
32
- ):
33
- """Create and configure the Rootly MCP server using RouteMap filtering."""
34
-
35
- # Get Rootly API token from environment
36
- ROOTLY_API_TOKEN = os.getenv("ROOTLY_API_TOKEN")
37
- if not ROOTLY_API_TOKEN:
38
- raise ValueError("ROOTLY_API_TOKEN environment variable is required")
39
-
40
- logger.info("Creating authenticated HTTP client...")
41
- # Create authenticated HTTP client
42
- client = httpx.AsyncClient(
43
- base_url=base_url or "https://api.rootly.com",
44
- headers={
45
- "Authorization": f"Bearer {ROOTLY_API_TOKEN}",
46
- "Content-Type": "application/vnd.api+json",
47
- "User-Agent": "Rootly-FastMCP-Server/1.0"
48
- },
49
- timeout=30.0
50
- )
51
-
52
- logger.info("Loading OpenAPI specification...")
53
- # Load OpenAPI spec with smart fallback logic
54
- openapi_spec = load_rootly_openapi_spec()
55
- logger.info("✅ Successfully loaded OpenAPI specification")
56
-
57
- logger.info("Fixing OpenAPI spec for FastMCP compatibility...")
58
- # Fix array types for FastMCP compatibility
59
- def fix_array_types(obj):
60
- if isinstance(obj, dict):
61
- keys_to_process = list(obj.keys())
62
- for key in keys_to_process:
63
- value = obj[key]
64
- if key == 'type' and isinstance(value, list):
65
- non_null_types = [t for t in value if t != 'null']
66
- if len(non_null_types) >= 1:
67
- obj[key] = non_null_types[0]
68
- obj['nullable'] = True
69
- else:
70
- fix_array_types(value)
71
- elif isinstance(obj, list):
72
- for item in obj:
73
- fix_array_types(item)
74
-
75
- fix_array_types(openapi_spec)
76
- logger.info("✅ Fixed OpenAPI spec compatibility issues")
77
-
78
- logger.info("Creating FastMCP server with RouteMap filtering...")
79
-
80
- # Define custom route maps for filtering specific endpoints
81
- route_maps = [
82
- # Core incident management
83
- RouteMap(
84
- pattern=r"^/v1/incidents$",
85
- mcp_type=MCPType.TOOL
86
- ),
87
- RouteMap(
88
- pattern=r"^/v1/incidents/\{incident_id\}/alerts$",
89
- mcp_type=MCPType.TOOL
90
- ),
91
- RouteMap(
92
- pattern=r"^/v1/incidents/\{incident_id\}/action_items$",
93
- mcp_type=MCPType.TOOL
94
- ),
95
-
96
- # Alert management
97
- RouteMap(
98
- pattern=r"^/v1/alerts$",
99
- mcp_type=MCPType.TOOL
100
- ),
101
- RouteMap(
102
- pattern=r"^/v1/alerts/\{id\}$",
103
- mcp_type=MCPType.TOOL
104
- ),
105
-
106
- # Configuration entities
107
- RouteMap(
108
- pattern=r"^/v1/severities(\{id\})?$",
109
- mcp_type=MCPType.TOOL
110
- ),
111
- RouteMap(
112
- pattern=r"^/v1/incident_types(\{id\})?$",
113
- mcp_type=MCPType.TOOL
114
- ),
115
- RouteMap(
116
- pattern=r"^/v1/functionalities(\{id\})?$",
117
- mcp_type=MCPType.TOOL
118
- ),
119
-
120
- # Organization
121
- RouteMap(
122
- pattern=r"^/v1/teams(\{id\})?$",
123
- mcp_type=MCPType.TOOL
124
- ),
125
- RouteMap(
126
- pattern=r"^/v1/users(\{id\}|/me)?$",
127
- mcp_type=MCPType.TOOL
128
- ),
129
-
130
- # Infrastructure
131
- RouteMap(
132
- pattern=r"^/v1/services(\{id\})?$",
133
- mcp_type=MCPType.TOOL
134
- ),
135
- RouteMap(
136
- pattern=r"^/v1/environments(\{id\})?$",
137
- mcp_type=MCPType.TOOL
138
- ),
139
-
140
- # Action items
141
- RouteMap(
142
- pattern=r"^/v1/incident_action_items(\{id\})?$",
143
- mcp_type=MCPType.TOOL
144
- ),
145
-
146
- # Workflows
147
- RouteMap(
148
- pattern=r"^/v1/workflows(\{id\})?$",
149
- mcp_type=MCPType.TOOL
150
- ),
151
- RouteMap(
152
- pattern=r"^/v1/workflow_runs(\{id\})?$",
153
- mcp_type=MCPType.TOOL
154
- ),
155
-
156
- # Status pages
157
- RouteMap(
158
- pattern=r"^/v1/status_pages(\{id\})?$",
159
- mcp_type=MCPType.TOOL
160
- ),
161
-
162
- # Exclude everything else
163
- RouteMap(
164
- pattern=r".*",
165
- mcp_type=MCPType.EXCLUDE
166
- )
167
- ]
168
-
169
- # Create MCP server with custom route maps
170
- mcp = FastMCP.from_openapi(
171
- openapi_spec=openapi_spec,
172
- client=client,
173
- name=name,
174
- timeout=30.0,
175
- tags={"rootly", "incident-management", "evaluation"},
176
- route_maps=route_maps
177
- )
178
-
179
- logger.info("✅ Created MCP server with RouteMap filtering successfully")
180
- logger.info("🚀 Selected Rootly API endpoints are now available as MCP tools")
181
-
182
- return mcp
183
-
184
-
185
-
186
-
187
- def main():
188
- """Main entry point."""
189
- try:
190
- logger.info("🚀 Starting Rootly FastMCP Server (RouteMap Version)...")
191
- mcp = create_rootly_mcp_server()
192
-
193
- logger.info("🌐 Server starting on stdio transport...")
194
- logger.info("Ready for MCP client connections!")
195
-
196
- # Run the MCP server
197
- mcp.run()
198
-
199
- except KeyboardInterrupt:
200
- logger.info("🛑 Server stopped by user")
201
- except Exception as e:
202
- logger.error(f"❌ Server error: {e}")
203
- raise
204
-
205
- if __name__ == "__main__":
206
- main()
@@ -1,150 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Test client for the Rootly MCP Server
4
-
5
- This script demonstrates how to use the Rootly MCP Server.
6
- """
7
-
8
- import asyncio
9
- import os
10
- import sys
11
- from pathlib import Path
12
-
13
- # Add the src directory to the Python path
14
- sys.path.insert(0, str(Path(__file__).parent.parent))
15
-
16
- from rootly_mcp_server.server import create_rootly_mcp_server
17
-
18
-
19
- async def test_server():
20
- """Test the Rootly MCP server."""
21
- print("Creating Rootly MCP Server...")
22
-
23
- try:
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
- )
35
-
36
- print("✅ 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")
60
-
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())}")
80
- else:
81
- print("\n⚠️ No tools found")
82
-
83
- # Test accessing a specific tool
84
- if tool_count > 0:
85
- print("\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 tools and find the specific tool
97
- try:
98
- all_tools = await server.get_tools()
99
- if first_tool_name in all_tools:
100
- retrieved_tool = all_tools[first_tool_name]
101
- print(f" ✅ Successfully retrieved tool by name: {first_tool_name}")
102
- print(f" Retrieved tool type: {type(retrieved_tool)}")
103
- else:
104
- print(f" ❌ Could not find tool by name: {first_tool_name}")
105
- except Exception as e:
106
- print(f" ❌ Error retrieving tools: {e}")
107
-
108
- except Exception as e:
109
- print(f"❌ Error accessing tools: {e}")
110
- import traceback
111
- traceback.print_exc()
112
- tool_count = 0
113
-
114
- print("\n🎉 Test completed successfully!")
115
- print(f"Total tools found: {tool_count}")
116
-
117
- except Exception as e:
118
- print(f"❌ Error creating server: {e}")
119
- import traceback
120
- traceback.print_exc()
121
- return False
122
-
123
- return True
124
-
125
-
126
- def main():
127
- """Main function."""
128
- print("Rootly MCP Server Test")
129
- print("=" * 50)
130
-
131
- # Check for API token
132
- api_token = os.getenv("ROOTLY_API_TOKEN")
133
- if not api_token:
134
- print("⚠️ Warning: ROOTLY_API_TOKEN not set. Server will use mock client.")
135
- else:
136
- print(f"✅ API token found: {api_token[:10]}...")
137
-
138
- # Run the test
139
- success = asyncio.run(test_server())
140
-
141
- if success:
142
- print("\n🎉 All tests passed!")
143
- sys.exit(0)
144
- else:
145
- print("\n❌ Tests failed!")
146
- sys.exit(1)
147
-
148
-
149
- if __name__ == "__main__":
150
- main()
@@ -1,13 +0,0 @@
1
- rootly_mcp_server/__init__.py,sha256=6pLh19IFyqE-Cve9zergkD-X_yApEkInREKmRa73T6s,628
2
- rootly_mcp_server/__main__.py,sha256=_F4p65_VjnN84RtmEdESVLLH0tO5tL9qBfb2Xdvbj2E,6480
3
- rootly_mcp_server/client.py,sha256=diIBINJP_z4nnQIAC1b70vQSiHaNojEfUDARC2nrKHU,4681
4
- rootly_mcp_server/routemap_server.py,sha256=0LfK2EzwkFQF9SpHNvGcca5ZaxkBC80gIdDojE0aUcs,6100
5
- rootly_mcp_server/server.py,sha256=O31ByNNfKjBmmYY1y_ooPKL12b-PXaM5eNrekXzpURE,24346
6
- rootly_mcp_server/test_client.py,sha256=Ytd5ZP7vImm12CT97k3p9tlkY_JNcXHSzcGGnHCBqv0,5275
7
- rootly_mcp_server/utils.py,sha256=NyxdcDiFGlV2a8eBO4lKgZg0D7Gxr6xUIB0YyJGgpPA,4165
8
- rootly_mcp_server/data/__init__.py,sha256=fO8a0bQnRVEoRMHKvhFzj10bhoaw7VsI51czc2MsUm4,143
9
- rootly_mcp_server-2.0.10.dist-info/METADATA,sha256=SyvYvw80MbIJVhxXvAG_ai1F9FV9N3qPx9Ls4bj3mbg,6189
10
- rootly_mcp_server-2.0.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
11
- rootly_mcp_server-2.0.10.dist-info/entry_points.txt,sha256=NE33b8VgigVPGBkboyo6pvN1Vz35HZtLybxMO4Q03PI,70
12
- rootly_mcp_server-2.0.10.dist-info/licenses/LICENSE,sha256=c9w9ZZGl14r54tsP40oaq5adTVX_HMNHozPIH2ymzmw,11341
13
- rootly_mcp_server-2.0.10.dist-info/RECORD,,