mcp-instana 0.1.1__py3-none-any.whl → 0.2.0__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.
Files changed (55) hide show
  1. {mcp_instana-0.1.1.dist-info → mcp_instana-0.2.0.dist-info}/METADATA +459 -138
  2. mcp_instana-0.2.0.dist-info/RECORD +59 -0
  3. src/application/application_analyze.py +373 -160
  4. src/application/application_catalog.py +3 -1
  5. src/application/application_global_alert_config.py +653 -0
  6. src/application/application_metrics.py +6 -2
  7. src/application/application_resources.py +3 -1
  8. src/application/application_settings.py +966 -370
  9. src/application/application_topology.py +6 -2
  10. src/automation/action_catalog.py +416 -0
  11. src/automation/action_history.py +338 -0
  12. src/core/server.py +159 -9
  13. src/core/utils.py +2 -2
  14. src/event/events_tools.py +602 -275
  15. src/infrastructure/infrastructure_analyze.py +7 -3
  16. src/infrastructure/infrastructure_catalog.py +3 -1
  17. src/infrastructure/infrastructure_metrics.py +6 -2
  18. src/infrastructure/infrastructure_resources.py +7 -5
  19. src/infrastructure/infrastructure_topology.py +5 -3
  20. src/prompts/__init__.py +16 -0
  21. src/prompts/application/__init__.py +1 -0
  22. src/prompts/application/application_alerts.py +54 -0
  23. src/prompts/application/application_catalog.py +26 -0
  24. src/prompts/application/application_metrics.py +57 -0
  25. src/prompts/application/application_resources.py +26 -0
  26. src/prompts/application/application_settings.py +75 -0
  27. src/prompts/application/application_topology.py +30 -0
  28. src/prompts/events/__init__.py +1 -0
  29. src/prompts/events/events_tools.py +161 -0
  30. src/prompts/infrastructure/infrastructure_analyze.py +72 -0
  31. src/prompts/infrastructure/infrastructure_catalog.py +53 -0
  32. src/prompts/infrastructure/infrastructure_metrics.py +45 -0
  33. src/prompts/infrastructure/infrastructure_resources.py +74 -0
  34. src/prompts/infrastructure/infrastructure_topology.py +38 -0
  35. src/prompts/settings/__init__.py +0 -0
  36. src/prompts/settings/custom_dashboard.py +157 -0
  37. src/prompts/website/__init__.py +1 -0
  38. src/prompts/website/website_analyze.py +35 -0
  39. src/prompts/website/website_catalog.py +40 -0
  40. src/prompts/website/website_configuration.py +105 -0
  41. src/prompts/website/website_metrics.py +34 -0
  42. src/settings/__init__.py +1 -0
  43. src/settings/custom_dashboard_tools.py +417 -0
  44. src/website/__init__.py +0 -0
  45. src/website/website_analyze.py +433 -0
  46. src/website/website_catalog.py +171 -0
  47. src/website/website_configuration.py +770 -0
  48. src/website/website_metrics.py +241 -0
  49. mcp_instana-0.1.1.dist-info/RECORD +0 -30
  50. src/prompts/mcp_prompts.py +0 -900
  51. src/prompts/prompt_loader.py +0 -29
  52. src/prompts/prompt_registry.json +0 -21
  53. {mcp_instana-0.1.1.dist-info → mcp_instana-0.2.0.dist-info}/WHEEL +0 -0
  54. {mcp_instana-0.1.1.dist-info → mcp_instana-0.2.0.dist-info}/entry_points.txt +0 -0
  55. {mcp_instana-0.1.1.dist-info → mcp_instana-0.2.0.dist-info}/licenses/LICENSE.md +0 -0
@@ -11,13 +11,17 @@ from typing import Any, Dict, Optional
11
11
  from src.core.utils import BaseInstanaClient, register_as_tool
12
12
 
13
13
  try:
14
- from instana_client.api.application_topology_api import ApplicationTopologyApi
14
+ from instana_client.api.application_topology_api import (
15
+ ApplicationTopologyApi,
16
+ )
15
17
  from instana_client.api_client import ApiClient
16
18
  from instana_client.configuration import Configuration
17
19
  except ImportError as e:
18
20
  import logging
21
+ import traceback
19
22
  logger = logging.getLogger(__name__)
20
- logger.error(f"Error importing Instana SDK: {e}", exc_info=True)
23
+ logger.error(f"Error importing Instana SDK: {e}")
24
+ traceback.print_exc()
21
25
  raise
22
26
 
23
27
  # Configure logger for this module
@@ -0,0 +1,416 @@
1
+ """
2
+ Automation Action CAtalog MCP Tools Module
3
+
4
+ This module provides automation action catalog tools for Instana Automation.
5
+ """
6
+
7
+ import logging
8
+ from typing import Any, Dict, List, Optional, Union
9
+
10
+ # Import the necessary classes from the SDK
11
+ try:
12
+ from instana_client.api.action_catalog_api import (
13
+ ActionCatalogApi,
14
+ )
15
+ except ImportError:
16
+ import logging
17
+ logger = logging.getLogger(__name__)
18
+ logger.error("Failed to import application alert configuration API", exc_info=True)
19
+ raise
20
+
21
+ from src.core.utils import BaseInstanaClient, register_as_tool, with_header_auth
22
+
23
+ # Configure logger for this module
24
+ logger = logging.getLogger(__name__)
25
+
26
+ class ActionCatalogMCPTools(BaseInstanaClient):
27
+ """Tools for application alerts in Instana MCP."""
28
+
29
+ def __init__(self, read_token: str, base_url: str):
30
+ """Initialize the Application Alert MCP tools client."""
31
+ super().__init__(read_token=read_token, base_url=base_url)
32
+
33
+ @register_as_tool
34
+ @with_header_auth(ActionCatalogApi)
35
+ async def get_action_matches(self,
36
+ payload: Union[Dict[str, Any], str],
37
+ target_snapshot_id: Optional[str] = None,
38
+ ctx=None,
39
+ api_client=None) -> Dict[str, Any]:
40
+ """
41
+ Get action matches for a given action search space and target snapshot ID.
42
+ Args:
43
+ Sample payload:
44
+ {
45
+ "name": "CPU spends significant time waiting for input/output",
46
+ "description": "Checks whether the system spends significant time waiting for input/output."
47
+ }
48
+ target_snapshot_id: Optional[str]: The target snapshot ID to get action matches for.
49
+ ctx: Optional[Dict[str, Any]]: The context to get action matches for.
50
+ api_client: Optional[ActionCatalogApi]: The API client to get action matches for.
51
+ Returns:
52
+ Dict[str, Any]: The action matches for the given payload and target snapshot ID.
53
+ """
54
+ try:
55
+
56
+ if not payload:
57
+ return {"error": "payload is required"}
58
+
59
+ # Parse the payload if it's a string
60
+ if isinstance(payload, str):
61
+ logger.debug("Payload is a string, attempting to parse")
62
+ try:
63
+ import json
64
+ try:
65
+ parsed_payload = json.loads(payload)
66
+ logger.debug("Successfully parsed payload as JSON")
67
+ request_body = parsed_payload
68
+ except json.JSONDecodeError as e:
69
+ logger.debug(f"JSON parsing failed: {e}, trying with quotes replaced")
70
+
71
+ # Try replacing single quotes with double quotes
72
+ fixed_payload = payload.replace("'", "\"")
73
+ try:
74
+ parsed_payload = json.loads(fixed_payload)
75
+ logger.debug("Successfully parsed fixed JSON")
76
+ request_body = parsed_payload
77
+ except json.JSONDecodeError:
78
+ # Try as Python literal
79
+ import ast
80
+ try:
81
+ parsed_payload = ast.literal_eval(payload)
82
+ logger.debug("Successfully parsed payload as Python literal")
83
+ request_body = parsed_payload
84
+ except (SyntaxError, ValueError) as e2:
85
+ logger.debug(f"Failed to parse payload string: {e2}")
86
+ return {"error": f"Invalid payload format: {e2}", "payload": payload}
87
+ except Exception as e:
88
+ logger.debug(f"Error parsing payload string: {e}")
89
+ return {"error": f"Failed to parse payload: {e}", "payload": payload}
90
+ else:
91
+ # If payload is already a dictionary, use it directly
92
+ logger.debug("Using provided payload dictionary")
93
+ request_body = payload
94
+
95
+ # Validate required fields in the payload
96
+ required_fields = ["name"]
97
+ for field in required_fields:
98
+ if field not in request_body:
99
+ logger.warning(f"Missing required field: {field}")
100
+ return {"error": f"Missing required field: {field}"}
101
+
102
+ # Import the ActionSearchSpace class
103
+ try:
104
+ from instana_client.models.action_search_space import (
105
+ ActionSearchSpace,
106
+ )
107
+ logger.debug("Successfully imported ActionSearchSpace")
108
+ except ImportError as e:
109
+ logger.debug(f"Error importing ActionSearchSpace: {e}")
110
+ return {"error": f"Failed to import ActionSearchSpace: {e!s}"}
111
+
112
+ # Create an ActionSearchSpace object from the request body
113
+ try:
114
+ logger.debug(f"Creating ActionSearchSpace with params: {request_body}")
115
+ config_object = ActionSearchSpace(**request_body)
116
+ logger.debug("Successfully created config object")
117
+ except Exception as e:
118
+ logger.debug(f"Error creating ActionSearchSpace: {e}")
119
+ return {"error": f"Failed to create config object: {e!s}"}
120
+
121
+ # Call the get_action_matches method from the SDK
122
+ logger.debug("Calling get_action_matches with config object")
123
+ result = api_client.get_action_matches(
124
+ action_search_space=config_object,
125
+ target_snapshot_id=target_snapshot_id,
126
+ )
127
+
128
+ # Convert the result to a dictionary
129
+ if isinstance(result, list):
130
+ # Convert list of ActionMatch objects to list of dictionaries
131
+ result_dict = []
132
+ for action_match in result:
133
+ try:
134
+ if hasattr(action_match, 'to_dict'):
135
+ result_dict.append(action_match.to_dict())
136
+ else:
137
+ result_dict.append(action_match)
138
+ except Exception as e:
139
+ logger.warning(f"Failed to convert action match to dict: {e}")
140
+ # Add a fallback representation
141
+ result_dict.append({
142
+ "error": f"Failed to serialize action match: {e}",
143
+ "raw_data": str(action_match)
144
+ })
145
+
146
+ logger.debug(f"Result from get_action_matches: {result_dict}")
147
+ return {
148
+ "success": True,
149
+ "message": "Action matches retrieved successfully",
150
+ "data": result_dict,
151
+ "count": len(result_dict)
152
+ }
153
+ elif hasattr(result, 'to_dict'):
154
+ try:
155
+ result_dict = result.to_dict()
156
+ logger.debug(f"Result from get_action_matches: {result_dict}")
157
+ return {
158
+ "success": True,
159
+ "message": "Action match retrieved successfully",
160
+ "data": result_dict
161
+ }
162
+ except Exception as e:
163
+ logger.warning(f"Failed to convert result to dict: {e}")
164
+ return {
165
+ "success": False,
166
+ "message": "Failed to serialize result",
167
+ "error": str(e),
168
+ "raw_data": str(result)
169
+ }
170
+ else:
171
+ # If it's already a dict or another format, use it as is
172
+ result_dict = result or {
173
+ "success": True,
174
+ "message": "Get action matches"
175
+ }
176
+ logger.debug(f"Result from get_action_matches: {result_dict}")
177
+ return result_dict
178
+ except Exception as e:
179
+ logger.error(f"Error in get_action_matches: {e}")
180
+ return {"error": f"Failed to get action matches: {e!s}"}
181
+
182
+ @register_as_tool
183
+ @with_header_auth(ActionCatalogApi)
184
+ async def get_actions(self,
185
+ page: Optional[int] = None,
186
+ page_size: Optional[int] = None,
187
+ search: Optional[str] = None,
188
+ types: Optional[List[str]] = None,
189
+ order_by: Optional[str] = None,
190
+ order_direction: Optional[str] = None,
191
+ ctx=None,
192
+ api_client=None) -> Dict[str, Any]:
193
+ """
194
+ Get a list of available automation actions from the action catalog.
195
+
196
+ Args:
197
+ page: Page number for pagination (optional)
198
+ page_size: Number of actions per page (optional)
199
+ search: Search term to filter actions by name or description (optional)
200
+ types: List of action types to filter by (optional)
201
+ order_by: Field to order results by (optional)
202
+ order_direction: Sort direction ('asc' or 'desc') (optional)
203
+ ctx: Optional[Dict[str, Any]]: The context for the action retrieval
204
+ api_client: Optional[ActionCatalogApi]: The API client for action catalog
205
+
206
+ Returns:
207
+ Dict[str, Any]: The list of available automation actions
208
+ """
209
+ try:
210
+ logger.debug("get_actions called")
211
+
212
+ # Call the get_actions method from the SDK
213
+ result = api_client.get_actions(
214
+ page=page,
215
+ page_size=page_size,
216
+ search=search,
217
+ types=types,
218
+ order_by=order_by,
219
+ order_direction=order_direction
220
+ )
221
+
222
+ # Convert the result to a dictionary
223
+ if hasattr(result, 'to_dict'):
224
+ result_dict = result.to_dict()
225
+ else:
226
+ # If it's already a dict or another format, use it as is
227
+ result_dict = result or {
228
+ "success": True,
229
+ "message": "Actions retrieved successfully"
230
+ }
231
+
232
+ logger.debug(f"Result from get_actions: {result_dict}")
233
+ return result_dict
234
+
235
+ except Exception as e:
236
+ logger.error(f"Error in get_actions: {e}")
237
+ return {"error": f"Failed to get actions: {e!s}"}
238
+
239
+ @register_as_tool
240
+ @with_header_auth(ActionCatalogApi)
241
+ async def get_action_details(self,
242
+ action_id: str,
243
+ ctx=None,
244
+ api_client=None) -> Dict[str, Any]:
245
+ """
246
+ Get detailed information about a specific automation action by ID.
247
+
248
+ Args:
249
+ action_id: The unique identifier of the action (required)
250
+ ctx: Optional[Dict[str, Any]]: The context for the action details retrieval
251
+ api_client: Optional[ActionCatalogApi]: The API client for action catalog
252
+
253
+ Returns:
254
+ Dict[str, Any]: The detailed information about the automation action
255
+ """
256
+ try:
257
+ if not action_id:
258
+ return {"error": "action_id is required"}
259
+
260
+ logger.debug(f"get_action_details called with action_id: {action_id}")
261
+
262
+ # Call the get_action method from the SDK
263
+ result = api_client.get_action(action_id=action_id)
264
+
265
+ # Convert the result to a dictionary
266
+ if hasattr(result, 'to_dict'):
267
+ result_dict = result.to_dict()
268
+ else:
269
+ # If it's already a dict or another format, use it as is
270
+ result_dict = result or {
271
+ "success": True,
272
+ "message": "Action details retrieved successfully"
273
+ }
274
+
275
+ logger.debug(f"Result from get_action: {result_dict}")
276
+ return result_dict
277
+
278
+ except Exception as e:
279
+ logger.error(f"Error in get_action_details: {e}")
280
+ return {"error": f"Failed to get action details: {e!s}"}
281
+
282
+ @register_as_tool
283
+ @with_header_auth(ActionCatalogApi)
284
+ async def search_actions(self,
285
+ search: str,
286
+ page: Optional[int] = None,
287
+ page_size: Optional[int] = None,
288
+ types: Optional[List[str]] = None,
289
+ order_by: Optional[str] = None,
290
+ order_direction: Optional[str] = None,
291
+ ctx=None,
292
+ api_client=None) -> Dict[str, Any]:
293
+ """
294
+ Search for automation actions in the action catalog.
295
+
296
+ Args:
297
+ search: Search term to find actions by name, description, or other attributes (required)
298
+ page: Page number for pagination (optional)
299
+ page_size: Number of actions per page (optional)
300
+ types: List of action types to filter by (optional)
301
+ order_by: Field to order results by (optional)
302
+ order_direction: Sort direction ('asc' or 'desc') (optional)
303
+ ctx: Optional[Dict[str, Any]]: The context for the action search
304
+ api_client: Optional[ActionCatalogApi]: The API client for action catalog
305
+
306
+ Returns:
307
+ Dict[str, Any]: The search results for automation actions
308
+ """
309
+ try:
310
+ if not search:
311
+ return {"error": "search parameter is required"}
312
+
313
+ logger.debug(f"search_actions called with search: {search}")
314
+
315
+ # Call the search_actions method from the SDK
316
+ result = api_client.search_actions(
317
+ search=search,
318
+ page=page,
319
+ page_size=page_size,
320
+ types=types,
321
+ order_by=order_by,
322
+ order_direction=order_direction
323
+ )
324
+
325
+ # Convert the result to a dictionary
326
+ if hasattr(result, 'to_dict'):
327
+ result_dict = result.to_dict()
328
+ else:
329
+ # If it's already a dict or another format, use it as is
330
+ result_dict = result or {
331
+ "success": True,
332
+ "message": "Action search completed successfully"
333
+ }
334
+
335
+ logger.debug(f"Result from search_actions: {result_dict}")
336
+ return result_dict
337
+
338
+ except Exception as e:
339
+ logger.error(f"Error in search_actions: {e}")
340
+ return {"error": f"Failed to search actions: {e!s}"}
341
+
342
+ @register_as_tool
343
+ @with_header_auth(ActionCatalogApi)
344
+ async def get_action_types(self,
345
+ ctx=None,
346
+ api_client=None) -> Dict[str, Any]:
347
+ """
348
+ Get a list of available action types in the action catalog.
349
+
350
+ Args:
351
+ ctx: Optional[Dict[str, Any]]: The context for the action types retrieval
352
+ api_client: Optional[ActionCatalogApi]: The API client for action catalog
353
+
354
+ Returns:
355
+ Dict[str, Any]: The list of available action types
356
+ """
357
+ try:
358
+ logger.debug("get_action_types called")
359
+
360
+ # Call the get_action_types method from the SDK
361
+ result = api_client.get_action_types()
362
+
363
+ # Convert the result to a dictionary
364
+ if hasattr(result, 'to_dict'):
365
+ result_dict = result.to_dict()
366
+ else:
367
+ # If it's already a dict or another format, use it as is
368
+ result_dict = result or {
369
+ "success": True,
370
+ "message": "Action types retrieved successfully"
371
+ }
372
+
373
+ logger.debug(f"Result from get_action_types: {result_dict}")
374
+ return result_dict
375
+
376
+ except Exception as e:
377
+ logger.error(f"Error in get_action_types: {e}")
378
+ return {"error": f"Failed to get action types: {e!s}"}
379
+
380
+ @register_as_tool
381
+ @with_header_auth(ActionCatalogApi)
382
+ async def get_action_categories(self,
383
+ ctx=None,
384
+ api_client=None) -> Dict[str, Any]:
385
+ """
386
+ Get a list of available action categories in the action catalog.
387
+
388
+ Args:
389
+ ctx: Optional[Dict[str, Any]]: The context for the action categories retrieval
390
+ api_client: Optional[ActionCatalogApi]: The API client for action catalog
391
+
392
+ Returns:
393
+ Dict[str, Any]: The list of available action categories
394
+ """
395
+ try:
396
+ logger.debug("get_action_categories called")
397
+
398
+ # Call the get_action_categories method from the SDK
399
+ result = api_client.get_action_categories()
400
+
401
+ # Convert the result to a dictionary
402
+ if hasattr(result, 'to_dict'):
403
+ result_dict = result.to_dict()
404
+ else:
405
+ # If it's already a dict or another format, use it as is
406
+ result_dict = result or {
407
+ "success": True,
408
+ "message": "Action categories retrieved successfully"
409
+ }
410
+
411
+ logger.debug(f"Result from get_action_categories: {result_dict}")
412
+ return result_dict
413
+
414
+ except Exception as e:
415
+ logger.error(f"Error in get_action_categories: {e}")
416
+ return {"error": f"Failed to get action categories: {e!s}"}