mcp-instana 0.1.0__py3-none-any.whl → 0.1.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.
Files changed (37) hide show
  1. mcp_instana-0.1.1.dist-info/METADATA +908 -0
  2. mcp_instana-0.1.1.dist-info/RECORD +30 -0
  3. {mcp_instana-0.1.0.dist-info → mcp_instana-0.1.1.dist-info}/WHEEL +1 -1
  4. mcp_instana-0.1.1.dist-info/entry_points.txt +4 -0
  5. mcp_instana-0.1.0.dist-info/LICENSE → mcp_instana-0.1.1.dist-info/licenses/LICENSE.md +3 -3
  6. src/application/__init__.py +1 -0
  7. src/{client/application_alert_config_mcp_tools.py → application/application_alert_config.py} +251 -273
  8. src/application/application_analyze.py +415 -0
  9. src/application/application_catalog.py +153 -0
  10. src/{client/application_metrics_mcp_tools.py → application/application_metrics.py} +107 -129
  11. src/{client/application_resources_mcp_tools.py → application/application_resources.py} +128 -150
  12. src/application/application_settings.py +1135 -0
  13. src/application/application_topology.py +107 -0
  14. src/core/__init__.py +1 -0
  15. src/core/server.py +436 -0
  16. src/core/utils.py +213 -0
  17. src/event/__init__.py +1 -0
  18. src/{client/events_mcp_tools.py → event/events_tools.py} +128 -136
  19. src/infrastructure/__init__.py +1 -0
  20. src/{client/infrastructure_analyze_mcp_tools.py → infrastructure/infrastructure_analyze.py} +200 -203
  21. src/{client/infrastructure_catalog_mcp_tools.py → infrastructure/infrastructure_catalog.py} +194 -264
  22. src/infrastructure/infrastructure_metrics.py +167 -0
  23. src/{client/infrastructure_resources_mcp_tools.py → infrastructure/infrastructure_resources.py} +192 -223
  24. src/{client/infrastructure_topology_mcp_tools.py → infrastructure/infrastructure_topology.py} +105 -106
  25. src/log/__init__.py +1 -0
  26. src/log/log_alert_configuration.py +331 -0
  27. src/prompts/mcp_prompts.py +900 -0
  28. src/prompts/prompt_loader.py +29 -0
  29. src/prompts/prompt_registry.json +21 -0
  30. mcp_instana-0.1.0.dist-info/METADATA +0 -649
  31. mcp_instana-0.1.0.dist-info/RECORD +0 -19
  32. mcp_instana-0.1.0.dist-info/entry_points.txt +0 -3
  33. src/client/What is the sum of queue depth for all q +0 -55
  34. src/client/instana_client_base.py +0 -93
  35. src/client/log_alert_configuration_mcp_tools.py +0 -316
  36. src/client/show the top 5 services with the highest +0 -28
  37. src/mcp_server.py +0 -343
@@ -0,0 +1,1135 @@
1
+ """
2
+ Application Settings MCP Tools Module
3
+
4
+ This module provides application settings-specific MCP tools for Instana monitoring.
5
+
6
+ The API endpoints of this group provides a way to create, read, update, delete (CRUD) for various configuration settings.
7
+ """
8
+
9
+ import re
10
+ import sys
11
+ import traceback
12
+ from datetime import datetime
13
+ from typing import Any, Dict, List, Optional
14
+
15
+ from src.core.utils import BaseInstanaClient, register_as_tool, with_header_auth
16
+
17
+ # Import the necessary classes from the SDK
18
+ try:
19
+ from instana_client.api.application_settings_api import ApplicationSettingsApi
20
+ from instana_client.api_client import ApiClient
21
+ from instana_client.configuration import Configuration
22
+ from instana_client.models.application_config import ApplicationConfig
23
+ from instana_client.models.endpoint_config import EndpointConfig
24
+ from instana_client.models.manual_service_config import ManualServiceConfig
25
+ from instana_client.models.new_application_config import NewApplicationConfig
26
+ from instana_client.models.new_manual_service_config import NewManualServiceConfig
27
+ from instana_client.models.service_config import ServiceConfig
28
+ except ImportError as e:
29
+ print(f"Error importing Instana SDK: {e}", file=sys.stderr)
30
+ traceback.print_exc(file=sys.stderr)
31
+ raise
32
+
33
+
34
+ # Helper function for debug printing
35
+ def debug_print(*args, **kwargs):
36
+ """Print debug information to stderr instead of stdout"""
37
+ print(*args, file=sys.stderr, **kwargs)
38
+
39
+ class ApplicationSettingsMCPTools(BaseInstanaClient):
40
+ """Tools for application settings in Instana MCP."""
41
+
42
+ def __init__(self, read_token: str, base_url: str):
43
+ """Initialize the Application Settings MCP tools client."""
44
+ super().__init__(read_token=read_token, base_url=base_url)
45
+
46
+ try:
47
+
48
+ # Configure the API client with the correct base URL and authentication
49
+ configuration = Configuration()
50
+ configuration.host = base_url
51
+ configuration.api_key['ApiKeyAuth'] = read_token
52
+ configuration.api_key_prefix['ApiKeyAuth'] = 'apiToken'
53
+
54
+ # Create an API client with this configuration
55
+ api_client = ApiClient(configuration=configuration)
56
+
57
+ # Initialize the Instana SDK's ApplicationSettingsApi with our configured client
58
+ self.settings_api = ApplicationSettingsApi(api_client=api_client)
59
+ except Exception as e:
60
+ debug_print(f"Error initializing ApplicationSettingsApi: {e}")
61
+ traceback.print_exc(file=sys.stderr)
62
+ raise
63
+
64
+ @register_as_tool
65
+ @with_header_auth(ApplicationSettingsApi)
66
+ async def get_all_applications_configs(self,
67
+ ctx=None,
68
+ api_client=None) -> List[Dict[str, Any]]:
69
+ """
70
+ All Application Perspectives Configuration
71
+ Get a list of all Application Perspectives with their configuration settings.
72
+
73
+ Args:
74
+ ctx: The MCP context (optional)
75
+
76
+ Returns:
77
+ Dictionary containing endpoints data or error information
78
+ """
79
+ try:
80
+ debug_print("Fetching all applications and their settings")
81
+ result = api_client.get_application_configs()
82
+ # Convert the result to a list of dictionaries
83
+ if isinstance(result, list):
84
+ result_dict = [item.to_dict() if hasattr(item, 'to_dict') else item for item in result]
85
+ elif hasattr(result, 'to_dict'):
86
+ result_dict = result.to_dict()
87
+ else:
88
+ result_dict = result
89
+
90
+ debug_print(f"Result from get_application_configs: {result_dict}")
91
+ return result_dict
92
+
93
+ except Exception as e:
94
+ debug_print(f"Error in get_application_configs: {e}")
95
+ traceback.print_exc(file=sys.stderr)
96
+ return [{"error": f"Failed to get all applications: {e!s}"}]
97
+
98
+ @register_as_tool
99
+ @with_header_auth(ApplicationSettingsApi)
100
+ async def add_application_config(self,
101
+ access_rules: List[Dict[str, str]],
102
+ boundary_scope: str,
103
+ label: str,
104
+ scope: str,
105
+ tag_filter_expression: Optional[List[Dict[str, str]]] = None,
106
+ ctx=None,
107
+ api_client=None) -> Dict[str, Any]:
108
+ """
109
+ Add a new Application Perspective configuration.
110
+ This tool allows you to create a new Application Perspective with specified settings.
111
+ Args:
112
+ accessRules: List of access rules for the application perspective
113
+ boundaryScope: Boundary scope for the application perspective
114
+ label: Label for the application perspective
115
+ scope: Scope of the application perspective
116
+ tagFilterExpression: Tag filter expression for the application perspective (Optional)
117
+ ctx: The MCP context (optional)
118
+ Returns:
119
+ Dictionary containing the created application perspective configuration or error information
120
+ """
121
+ try:
122
+ debug_print("Adding new application perspective configuration")
123
+ if not (access_rules or boundary_scope or label or scope):
124
+ return {"error": "Required enitities are missing or invalid"}
125
+
126
+ # Create a NewApplicationConfig instance with the provided parameters
127
+ request_body = {
128
+ "access_rules": access_rules,
129
+ "boundary_scope": boundary_scope,
130
+ "label": label,
131
+ "scope": scope,
132
+ "tag_filter_expression": tag_filter_expression
133
+ }
134
+ new_application_config = NewApplicationConfig(**request_body)
135
+ debug_print(f"New Application Config: {new_application_config.to_dict()}")
136
+
137
+ # Call the add_application_config method from the SDK
138
+ result = api_client.add_application_config(
139
+ new_application_config=new_application_config
140
+ )
141
+
142
+ # Convert the result to a dictionary
143
+ if hasattr(result, 'to_dict'):
144
+ result_dict = result.to_dict()
145
+ else:
146
+ result_dict = result
147
+
148
+ debug_print(f"Result from add_application_config: {result_dict}")
149
+ return result_dict
150
+ except Exception as e:
151
+ debug_print(f"Error in add_application_config: {e}")
152
+ traceback.print_exc(file=sys.stderr)
153
+ return {"error": f"Failed to add application configuration: {e!s}"}
154
+
155
+ @register_as_tool
156
+ @with_header_auth(ApplicationSettingsApi)
157
+ async def delete_application_config(self,
158
+ id: str,
159
+ ctx=None,
160
+ api_client=None) -> Dict[str, Any]:
161
+ """
162
+ Delete an Application Perspective configuration.
163
+ This tool allows you to delete an existing Application Perspective by its ID.
164
+
165
+ Args:
166
+ application_id: The ID of the application perspective to delete
167
+ ctx: The MCP context (optional)
168
+
169
+ Returns:
170
+ Dictionary containing the result of the deletion or error information
171
+ """
172
+ try:
173
+ if not id:
174
+ return {"error": "Application perspective ID is required for deletion"}
175
+
176
+
177
+ debug_print(f"Deleting application perspective with ID: {id}")
178
+ # Call the delete_application_config method from the SDK
179
+ self.settings_api.delete_application_config(id=id)
180
+
181
+ result_dict = {
182
+ "success": True,
183
+ "message": f"Application Confiuguration '{id}' has been successfully deleted"
184
+ }
185
+
186
+ debug_print(f"Successfully deleted application perspective with ID: {id}")
187
+ return result_dict
188
+ except Exception as e:
189
+ debug_print(f"Error in delete_application_config: {e}")
190
+ traceback.print_exc(file=sys.stderr)
191
+ return {"error": f"Failed to delete application configuration: {e!s}"}
192
+
193
+ @register_as_tool
194
+ @with_header_auth(ApplicationSettingsApi)
195
+ async def get_application_config(self,
196
+ id: str,
197
+ ctx=None,
198
+ api_client=None) -> Dict[str, Any]:
199
+ """
200
+ Get an Application Perspective configuration by ID.
201
+ This tool retrieves the configuration settings for a specific Application Perspective.
202
+
203
+ Args:
204
+ id: The ID of the application perspective to retrieve
205
+ ctx: The MCP context (optional)
206
+
207
+ Returns:
208
+ Dictionary containing the application perspective configuration or error information
209
+ """
210
+ try:
211
+ debug_print(f"Fetching application perspective with ID: {id}")
212
+ # Call the get_application_config method from the SDK
213
+ result = api_client.get_application_config(id=id)
214
+
215
+ # Convert the result to a dictionary
216
+ if hasattr(result, 'to_dict'):
217
+ result_dict = result.to_dict()
218
+ else:
219
+ result_dict = result
220
+
221
+ debug_print(f"Result from get_application_config: {result_dict}")
222
+ return result_dict
223
+ except Exception as e:
224
+ debug_print(f"Error in get_application_config: {e}")
225
+ traceback.print_exc(file=sys.stderr)
226
+ return {"error": f"Failed to get application configuration: {e!s}"}
227
+
228
+ @register_as_tool
229
+ @with_header_auth(ApplicationSettingsApi)
230
+ async def update_application_config(
231
+ self,
232
+ id: str,
233
+ access_rules: List[Dict[str, str]],
234
+ boundary_scope: str,
235
+ label: str,
236
+ scope: str,
237
+ tag_filter_expression: Optional[List[Dict[str, str]]] = None,
238
+ match_specification: Optional[List[Dict[str, Any]]] = None,
239
+ ctx=None,
240
+ api_client=None
241
+ ) -> Dict[str, Any]:
242
+ """
243
+ Update an existing Application Perspective configuration.
244
+ This tool allows you to update an existing Application Perspective with specified application Id.
245
+
246
+ Args:
247
+ id: The ID of the application perspective to retrieve
248
+ access_rules: List of access rules for the application perspective
249
+ boundary_scope: Boundary scope for the application perspective
250
+ label: Label for the application perspective
251
+ scope: Scope of the application perspective
252
+ tag_filter_expression: Tag filter expression for the application perspective (Optional)
253
+ ctx: The MCP context (optional)
254
+ Returns:
255
+ Dictionary containing the created application perspective configuration or error information
256
+ """
257
+
258
+ try:
259
+ debug_print("Update existing application perspective configuration")
260
+ if not (access_rules or boundary_scope or label or scope or id):
261
+ return {"error": "Required enitities are missing or invalid"}
262
+
263
+ request_body = {
264
+ "access_rules": access_rules,
265
+ "boundary_scope": boundary_scope,
266
+ "id": id,
267
+ "label": label,
268
+ "match_specification": match_specification,
269
+ "scope": scope,
270
+ "tag_filter_expression": tag_filter_expression
271
+ }
272
+ # Create a ApplicationConfig instance with the provided parameters
273
+ application_config = ApplicationConfig(**request_body)
274
+ debug_print(f"Application Config: {application_config.to_dict()}")
275
+
276
+ # Call the put_application_config method from the SDK
277
+ result = api_client.put_application_config(
278
+ id=id,
279
+ application_config=application_config
280
+ )
281
+
282
+ # Convert the result to a dictionary
283
+ if hasattr(result, 'to_dict'):
284
+ result_dict = result.to_dict()
285
+ else:
286
+ result_dict = result
287
+
288
+ debug_print(f"Result from put_application_config: {result_dict}")
289
+ return result_dict
290
+ except Exception as e:
291
+ debug_print(f"Error in put_application_config: {e}")
292
+ traceback.print_exc(file=sys.stderr)
293
+ return {"error": f"Failed to update application configuration: {e!s}"}
294
+
295
+ @register_as_tool
296
+ @with_header_auth(ApplicationSettingsApi)
297
+ async def get_all_endpoint_configs(self,
298
+ ctx=None,
299
+ api_client=None) -> List[Dict[str, Any]]:
300
+ """
301
+ All Endpoint Perspectives Configuration
302
+ Get a list of all Endpoint Perspectives with their configuration settings.
303
+ Args:
304
+ ctx: The MCP context (optional)
305
+
306
+ Returns:
307
+ Dictionary containing endpoints data or error information
308
+ """
309
+ try:
310
+ debug_print("Fetching all endpoint configs")
311
+ result = api_client.get_endpoint_configs()
312
+ # Convert the result to a dictionary
313
+ if hasattr(result, 'to_dict'):
314
+ result_dict = result.to_dict()
315
+ else:
316
+ # If it's already a dict or another format, use it as is
317
+ result_dict = result
318
+
319
+ debug_print(f"Result from get_endpoint_configs: {result_dict}")
320
+ return result_dict
321
+
322
+ except Exception as e:
323
+ debug_print(f"Error in get_endpoint_configs: {e}")
324
+ traceback.print_exc(file=sys.stderr)
325
+ return [{"error": f"Failed to get endpoint configs: {e!s}"}]
326
+
327
+ @register_as_tool
328
+ @with_header_auth(ApplicationSettingsApi)
329
+ async def create_endpoint_config(
330
+ self,
331
+ endpoint_case: str,
332
+ service_id: str,
333
+ endpoint_name_by_collected_path_template_rule_enabled: Optional[bool]= None,
334
+ endpoint_name_by_first_path_segment_rule_enabled: Optional[bool] = None,
335
+ rules: Optional[List[Dict[str, Any]]] = None,
336
+ ctx=None,
337
+ api_client=None
338
+ ) -> Dict[str, Any]:
339
+ """
340
+ Create or update endpoint configuration for a service.
341
+
342
+ Args:
343
+ serviceId (str): Instana Service ID to configure.
344
+ endpointCase (str): Case format for endpoints. One of: 'ORIGINAL', 'LOWER', 'UPPER'.
345
+ endpointNameByCollectedPathTemplateRuleEnabled (Optional[bool]): Enable path template rule. (Optional)
346
+ endpointNameByFirstPathSegmentRuleEnabled (Optional[bool]): Enable first path segment rule. (Optional)
347
+ rules (Optional[List[Dict[str, Any]]]): Optional list of custom HTTP endpoint rules. (Optional)
348
+ ctx: The MCP context (optional)
349
+
350
+ Returns:
351
+ Dict[str, Any]: Response from the create/update endpoint configuration API.
352
+ """
353
+ try:
354
+ debug_print("Creating endpoint configs")
355
+ if not endpoint_case or not service_id:
356
+ return {"error": "Required enitities are missing or invalid"}
357
+
358
+ request_body = {
359
+ "endpoint_case": endpoint_case,
360
+ "endpoint_name_by_collected_path_template_rule_enabled": endpoint_name_by_collected_path_template_rule_enabled,
361
+ "endpoint_name_by_first_path_segment_rule_enabled": endpoint_name_by_first_path_segment_rule_enabled,
362
+ "rules": rules,
363
+ "serviceId": service_id
364
+ }
365
+ endpoint_config = EndpointConfig(**request_body)
366
+
367
+ result = api_client.create_endpoint_config(
368
+ endpoint_config=endpoint_config
369
+ )
370
+
371
+ # Convert the result to a dictionary
372
+ if hasattr(result, 'to_dict'):
373
+ result_dict = result.to_dict()
374
+ else:
375
+ # If it's already a dict or another format, use it as is
376
+ result_dict = result
377
+
378
+ debug_print(f"Result from get_endpoint_configs: {result_dict}")
379
+ return result_dict
380
+
381
+ except Exception as e:
382
+ debug_print(f"Error in get_endpoint_configs: {e}")
383
+ traceback.print_exc(file=sys.stderr)
384
+ return {"error": f"Failed to get endpoint configs: {e!s}"}
385
+
386
+ @register_as_tool
387
+ @with_header_auth(ApplicationSettingsApi)
388
+ async def delete_endpoint_config(
389
+ self,
390
+ id: str,
391
+ ctx=None,
392
+ api_client=None
393
+ ) -> Dict[str, Any]:
394
+ """
395
+ Delete an endpoint configuration of a service.
396
+
397
+ Args:
398
+ id: An Instana generated unique identifier for a Service.
399
+ ctx: The MCP context (optional)
400
+
401
+ Returns:
402
+ Dict[str, Any]: Response from the delete endpoint configuration API.
403
+ """
404
+ try:
405
+ debug_print("Delete endpoint configs")
406
+ if not id:
407
+ return {"error": "Required enitities are missing or invalid"}
408
+
409
+ api_client.delete_endpoint_config(id=id)
410
+
411
+ result_dict = {
412
+ "success": True,
413
+ "message": f"Endpoint Confiuguration '{id}' has been successfully deleted"
414
+ }
415
+
416
+ debug_print(f"Successfully deleted endpoint perspective with ID: {id}")
417
+ return result_dict
418
+
419
+ except Exception as e:
420
+ debug_print(f"Error in delete_endpoint_config: {e}")
421
+ traceback.print_exc(file=sys.stderr)
422
+ return {"error": f"Failed to delete endpoint configs: {e!s}"}
423
+
424
+ @register_as_tool
425
+ @with_header_auth(ApplicationSettingsApi)
426
+ async def get_endpoint_config(
427
+ self,
428
+ id: str,
429
+ ctx=None,
430
+ api_client=None
431
+ ) -> Dict[str, Any]:
432
+ """
433
+ This MCP tool is used for endpoint if one wants to retrieve the endpoint configuration of a service.
434
+ Args:
435
+ id: An Instana generated unique identifier for a Service.
436
+ ctx: The MCP context (optional)
437
+
438
+ Returns:
439
+ Dict[str, Any]: Response from the create/update endpoint configuration API.
440
+
441
+ """
442
+ try:
443
+ debug_print("get endpoint config")
444
+ if not id:
445
+ return {"error": "Required enitities are missing or invalid"}
446
+
447
+ result = api_client.get_endpoint_config(
448
+ id=id
449
+ )
450
+ # Convert the result to a dictionary
451
+ if hasattr(result, 'to_dict'):
452
+ result_dict = result.to_dict()
453
+ else:
454
+ # If it's already a dict or another format, use it as is
455
+ result_dict = result
456
+
457
+ debug_print(f"Result from get_endpoint_configs: {result_dict}")
458
+ return result_dict
459
+ except Exception as e:
460
+ debug_print(f"Error in get_endpoint_configs: {e}")
461
+ traceback.print_exc(file=sys.stderr)
462
+ return {"error": f"Failed to get endpoint configs: {e!s}"}
463
+
464
+ @register_as_tool
465
+ @with_header_auth(ApplicationSettingsApi)
466
+ async def update_endpoint_config(
467
+ self,
468
+ id: str,
469
+ endpoint_case: str,
470
+ service_id: str,
471
+ endpoint_name_by_collected_path_template_rule_enabled: Optional[bool]= None,
472
+ endpoint_name_by_first_path_segment_rule_enabled: Optional[bool] = None,
473
+ rules: Optional[List[Dict[str, Any]]] = None,
474
+ ctx=None,
475
+ api_client=None
476
+ ) -> Dict[str, Any]:
477
+ """
478
+ update endpoint configuration for a service.
479
+
480
+ Args:
481
+ id: An Instana generated unique identifier for a Service.
482
+ serviceId: Instana Service ID to configure.
483
+ endpointCase: Case format for endpoints. One of: 'ORIGINAL', 'LOWER', 'UPPER'.
484
+ endpointNameByCollectedPathTemplateRuleEnabled: Enable path template rule. (Optional)
485
+ endpointNameByFirstPathSegmentRuleEnabled: Enable first path segment rule. (Optional)
486
+ rules: Optional list of custom HTTP endpoint rules. (Optional)
487
+ ctx: The MCP context (optional)
488
+
489
+ Returns:
490
+ Dict[str, Any]: Response from the create/update endpoint configuration API.
491
+ """
492
+ try:
493
+ debug_print("Updating endpoint configs")
494
+ if not endpoint_case or not service_id:
495
+ return {"error": "Required enitities are missing or invalid"}
496
+
497
+ request_body = {
498
+ "endpoint_case": endpoint_case,
499
+ "endpoint_name_by_collected_path_template_rule_enabled": endpoint_name_by_collected_path_template_rule_enabled,
500
+ "endpoint_name_by_first_path_segment_rule_enabled": endpoint_name_by_first_path_segment_rule_enabled,
501
+ "rules": rules,
502
+ "serviceId": service_id
503
+ }
504
+ endpoint_config = EndpointConfig(**request_body)
505
+
506
+ result = api_client.update_endpoint_config(
507
+ id=id,
508
+ endpoint_config=endpoint_config
509
+ )
510
+
511
+ # Convert the result to a dictionary
512
+ if hasattr(result, 'to_dict'):
513
+ result_dict = result.to_dict()
514
+ else:
515
+ # If it's already a dict or another format, use it as is
516
+ result_dict = result
517
+
518
+ debug_print(f"Result from get_endpoint_configs: {result_dict}")
519
+ return result_dict
520
+
521
+ except Exception as e:
522
+ debug_print(f"Error in get_endpoint_configs: {e}")
523
+ traceback.print_exc(file=sys.stderr)
524
+ return {"error": f"Failed to get endpoint configs: {e!s}"}
525
+
526
+ @register_as_tool
527
+ @with_header_auth(ApplicationSettingsApi)
528
+ async def get_all_manual_service_configs(self,
529
+ ctx=None,
530
+ api_client=None) -> List[Dict[str, Any]]:
531
+ """
532
+ All Manual Service Perspectives Configuration
533
+ Get a list of all Manual Service Perspectives with their configuration settings.
534
+ Args:
535
+ ctx: The MCP context (optional)
536
+
537
+ Returns:
538
+ Dictionary containing endpoints data or error information
539
+ """
540
+ try:
541
+ debug_print("Fetching all manual configs")
542
+ result = api_client.get_all_manual_service_configs()
543
+ # Convert the result to a dictionary
544
+ if hasattr(result, 'to_dict'):
545
+ result_dict = result.to_dict()
546
+ else:
547
+ # If it's already a dict or another format, use it as is
548
+ result_dict = result
549
+
550
+ debug_print(f"Result from get_all_manual_service_configs: {result_dict}")
551
+ return result_dict
552
+
553
+ except Exception as e:
554
+ debug_print(f"Error in get_all_manual_service_configs: {e}")
555
+ traceback.print_exc(file=sys.stderr)
556
+ return [{"error": f"Failed to get manual service configs: {e!s}"}]
557
+
558
+ @register_as_tool
559
+ @with_header_auth(ApplicationSettingsApi)
560
+ async def add_manual_service_config(
561
+ self,
562
+ tagFilterExpression: Dict[str, Any],
563
+ unmonitoredServiceName: Optional[str] = None,
564
+ existingServiceId: Optional[str] = None,
565
+ description: Optional[str] = None,
566
+ enabled: Optional[bool] = True,
567
+ ctx=None,
568
+ api_client=None
569
+ ) -> Dict[str, Any]:
570
+ """
571
+ Create a manual service mapping configuration.
572
+
573
+ Requires `CanConfigureServiceMapping` permission on the API token.
574
+
575
+ Args:
576
+ tagFilterExpression : Boolean expression of tag filters to match relevant calls.
577
+ unmonitoredServiceName : Custom name for an unmonitored service to map. (Optional)
578
+ existingServiceId : Service ID to link the matched calls to. (Optional)
579
+ description : Description of the mapping configuration. (Optional)
580
+ enabled : Enable or disable the configuration. Defaults to True. (Optional)
581
+ ctx: Optional execution context.
582
+
583
+ Returns:
584
+ Dict[str, Any]: API response indicating success or failure.
585
+ """
586
+ try:
587
+ debug_print("Creating manual service configuration")
588
+
589
+ if not (unmonitoredServiceName and existingServiceId):
590
+ return {
591
+ "error": "You must provide either 'unmonitoredServiceName' or 'existingServiceId'."
592
+ }
593
+
594
+ if not tagFilterExpression:
595
+ return {"error": "Required enitities are missing or invalid"}
596
+
597
+
598
+ body = {
599
+ "tagFilterExpression": tagFilterExpression,
600
+ "enabled": enabled
601
+ }
602
+
603
+ if unmonitoredServiceName:
604
+ body["unmonitoredServiceName"] = unmonitoredServiceName
605
+ if existingServiceId:
606
+ body["existingServiceId"] = existingServiceId
607
+ if description:
608
+ body["description"] = description
609
+
610
+ new_manual_service_config = NewManualServiceConfig(**body)
611
+
612
+ result = api_client.add_manual_service_config(
613
+ new_manual_service_config=new_manual_service_config
614
+ )
615
+
616
+ if hasattr(result, "to_dict"):
617
+ result_dict = result.to_dict()
618
+ else:
619
+ result_dict = result
620
+
621
+ debug_print(f"Manual service configuration result: {result_dict}")
622
+ return result_dict
623
+
624
+ except Exception as e:
625
+ debug_print(f"Error creating manual service configuration: {e}")
626
+ traceback.print_exc(file=sys.stderr)
627
+ return {"error": f"Failed to create manual service configuration: {e!s}"}
628
+
629
+ @register_as_tool
630
+ @with_header_auth(ApplicationSettingsApi)
631
+ async def delete_manual_service_config(
632
+ self,
633
+ id: str,
634
+ ctx=None,
635
+ api_client=None
636
+ ) -> Dict[str, Any]:
637
+ """
638
+ Delete a manual service configuration.
639
+
640
+ Args:
641
+ id: A unique id of the manual service configuration.
642
+ ctx: The MCP context (optional)
643
+
644
+ Returns:
645
+ Dict[str, Any]: Response from the delete manual service configuration API.
646
+ """
647
+ try:
648
+ debug_print("Delete manual service configs")
649
+ if not id:
650
+ return {"error": "Required enitities are missing or invalid"}
651
+
652
+ api_client.delete_manual_service_config(id=id)
653
+
654
+ result_dict = {
655
+ "success": True,
656
+ "message": f"Manual Service Confiuguration '{id}' has been successfully deleted"
657
+ }
658
+
659
+ debug_print(f"Successfully deleted manual service config perspective with ID: {id}")
660
+ return result_dict
661
+
662
+ except Exception as e:
663
+ debug_print(f"Error in delete_manual_service_config: {e}")
664
+ traceback.print_exc(file=sys.stderr)
665
+ return {"error": f"Failed to delete manual service configs: {e!s}"}
666
+
667
+ @register_as_tool
668
+ @with_header_auth(ApplicationSettingsApi)
669
+ async def update_manual_service_config(
670
+ self,
671
+ id: str,
672
+ tagFilterExpression: Dict[str, Any],
673
+ unmonitoredServiceName: Optional[str] = None,
674
+ existingServiceId: Optional[str] = None,
675
+ description: Optional[str] = None,
676
+ enabled: Optional[bool] = True,
677
+ ctx=None,
678
+ api_client=None
679
+ ) -> Dict[str, Any]:
680
+ """
681
+ The manual service configuration APIs enables mapping calls to services using tag filter expressions based on call tags.
682
+
683
+ There are two use cases on the usage of these APIs:
684
+
685
+ Map to an Unmonitored Service with a Custom Name. For example, Map HTTP calls to different Google domains (www.ibm.com, www.ibm.fr) into a single service named IBM using the call.http.host tag.
686
+ Link Calls to an Existing Monitored Service. For example, Link database calls (jdbc:mysql://10.128.0.1:3306) to an existing service like MySQL@3306 on demo-host by referencing its service ID.
687
+
688
+ Args:
689
+ id: A unique id of the manual service configuration.
690
+ tagFilterExpression : Boolean expression of tag filters to match relevant calls.
691
+ unmonitoredServiceName : Custom name for an unmonitored service to map. (Optional)
692
+ existingServiceId : Service ID to link the matched calls to. (Optional)
693
+ description: Description of the mapping configuration. (Optional)
694
+ enabled: Enable or disable the configuration. Defaults to True. (Optional)
695
+ ctx: Optional execution context.
696
+
697
+ Returns:
698
+ Dict[str, Any]: API response indicating success or failure.
699
+ """
700
+ try:
701
+ debug_print("Creating manual service configuration")
702
+
703
+ if not (unmonitoredServiceName and existingServiceId):
704
+ return {
705
+ "error": "You must provide either 'unmonitoredServiceName' or 'existingServiceId'."
706
+ }
707
+ if not id or not tagFilterExpression:
708
+ return {"error": "Required enitities are missing or invalid"}
709
+
710
+ body = {
711
+ "tagFilterExpression": tagFilterExpression,
712
+ "id": id
713
+ }
714
+
715
+ if unmonitoredServiceName:
716
+ body["unmonitoredServiceName"] = unmonitoredServiceName
717
+ if existingServiceId:
718
+ body["existingServiceId"] = existingServiceId
719
+ if description:
720
+ body["description"] = description
721
+
722
+ manual_service_config = ManualServiceConfig(**body)
723
+
724
+ result = api_client.update_manual_service_config(
725
+ id=id,
726
+ manual_service_config=manual_service_config
727
+ )
728
+
729
+ if hasattr(result, "to_dict"):
730
+ result_dict = result.to_dict()
731
+ else:
732
+ result_dict = result
733
+
734
+ debug_print(f"Manual service configuration result: {result_dict}")
735
+ return result_dict
736
+
737
+ except Exception as e:
738
+ debug_print(f"Error creating manual service configuration: {e}")
739
+ traceback.print_exc(file=sys.stderr)
740
+ return {"error": f"Failed to create manual service configuration: {e!s}"}
741
+
742
+
743
+ @register_as_tool
744
+ @with_header_auth(ApplicationSettingsApi)
745
+ async def replace_all_manual_service_config(
746
+ self,
747
+ tagFilterExpression: Dict[str, Any],
748
+ unmonitoredServiceName: Optional[str] = None,
749
+ existingServiceId: Optional[str] = None,
750
+ description: Optional[str] = None,
751
+ enabled: Optional[bool] = True,
752
+ ctx=None,
753
+ api_client=None
754
+ ) -> Dict[str, Any]:
755
+ """
756
+ This tool is used if one wants to update more than 1 manual service configurations.
757
+
758
+ There are two use cases on the usage of these APIs:
759
+
760
+ Map to an Unmonitored Service with a Custom Name. For example, Map HTTP calls to different Google domains (www.ibm.com, www.ibm.fr) into a single service named IBM using the call.http.host tag.
761
+ Link Calls to an Existing Monitored Service. For example, Link database calls (jdbc:mysql://10.128.0.1:3306) to an existing service like MySQL@3306 on demo-host by referencing its service ID.
762
+
763
+ Args:
764
+ id: A unique id of the manual service configuration.
765
+ tagFilterExpression : Boolean expression of tag filters to match relevant calls.
766
+ unmonitoredServiceName : Custom name for an unmonitored service to map. (Optional)
767
+ existingServiceId : Service ID to link the matched calls to. (Optional)
768
+ description: Description of the mapping configuration. (Optional)
769
+ enabled: Enable or disable the configuration. Defaults to True. (Optional)
770
+ ctx: Optional execution context.
771
+
772
+ Returns:
773
+ Dict[str, Any]: API response indicating success or failure.
774
+ """
775
+ try:
776
+ debug_print("Creating manual service configuration")
777
+
778
+ if not (unmonitoredServiceName and existingServiceId):
779
+ return {
780
+ "error": "You must provide either 'unmonitoredServiceName' or 'existingServiceId'."
781
+ }
782
+ if not tagFilterExpression:
783
+ return {"error": "Required enitities are missing or invalid"}
784
+
785
+ request_body = {}
786
+
787
+ if tagFilterExpression:
788
+ request_body["tagFilterExpression"] = {"tagFilterExpression": tagFilterExpression}
789
+ if unmonitoredServiceName:
790
+ request_body["unmonitoredServiceName"] = {"unmonitoredServiceName": unmonitoredServiceName}
791
+ if existingServiceId:
792
+ request_body["existingServiceId"] = {"existingServiceId": existingServiceId}
793
+ if description:
794
+ request_body["description"] = {"description": description}
795
+
796
+ new_manual_service_config = NewManualServiceConfig(**request_body)
797
+
798
+ result = api_client.replace_all_manual_service_configs(
799
+ new_manual_service_config=new_manual_service_config
800
+ )
801
+
802
+ if hasattr(result, "to_dict"):
803
+ result_dict = result.to_dict()
804
+ else:
805
+ result_dict = result
806
+
807
+ debug_print(f"Manual service configuration result: {result_dict}")
808
+ return result_dict
809
+
810
+ except Exception as e:
811
+ debug_print(f"Error creating manual service configuration: {e}")
812
+ traceback.print_exc(file=sys.stderr)
813
+ return {"error": f"Failed to create manual service configuration: {e!s}"}
814
+
815
+
816
+ @register_as_tool
817
+ @with_header_auth(ApplicationSettingsApi)
818
+ async def get_all_service_configs(self,
819
+ ctx=None,
820
+ api_client=None) -> List[Dict[str, Any]]:
821
+ """
822
+ This tool gives list of All Service Perspectives Configuration
823
+ Get a list of all Service Perspectives with their configuration settings.
824
+ Args:
825
+ ctx: The MCP context (optional)
826
+
827
+ Returns:
828
+ Dictionary containing endpoints data or error information
829
+ """
830
+ try:
831
+ debug_print("Fetching all service configs")
832
+ result = api_client.get_service_configs()
833
+ # Convert the result to a dictionary
834
+ if hasattr(result, 'to_dict'):
835
+ result_dict = result.to_dict()
836
+ else:
837
+ # If it's already a dict or another format, use it as is
838
+ result_dict = result
839
+
840
+ debug_print(f"Result from get_service_configs: {result_dict}")
841
+ return result_dict
842
+
843
+ except Exception as e:
844
+ debug_print(f"Error in get_all_service_configs: {e}")
845
+ traceback.print_exc(file=sys.stderr)
846
+ return [{"error": f"Failed to get application data metrics: {e}"}]
847
+
848
+ @register_as_tool
849
+ @with_header_auth(ApplicationSettingsApi)
850
+ async def add_service_configs(self,
851
+ enabled: bool,
852
+ match_specification: List[Dict[str, str]],
853
+ name: str,
854
+ label:str,
855
+ id: str,
856
+ comment: Optional[str] = None,
857
+ ctx=None,
858
+ api_client=None) -> List[Dict[str, Any]]:
859
+ """
860
+ This tool gives is used to add new Service Perspectives Configuration
861
+ Get a list of all Service Perspectives with their configuration settings.
862
+ Args:
863
+
864
+ ctx: The MCP context (optional)
865
+
866
+ Returns:
867
+ Dictionary containing endpoints data or error information
868
+ """
869
+ try:
870
+ debug_print("Adding new service config")
871
+ if not (enabled and match_specification and name and label and id):
872
+ return [{"error": "Required entities are missing or invalid"}]
873
+
874
+ body = {
875
+ "match_specification": match_specification,
876
+ "enabled": enabled,
877
+ "id": id,
878
+ "label": label,
879
+ "name": name
880
+ }
881
+
882
+ if comment:
883
+ body["comment"] = comment
884
+
885
+ service_config = ServiceConfig(**body)
886
+
887
+ result = api_client.add_service_config(
888
+ service_config=service_config
889
+ )
890
+
891
+ # Convert the result to a dictionary
892
+ if hasattr(result, 'to_dict'):
893
+ result_dict = result.to_dict()
894
+ else:
895
+ # If it's already a dict or another format, use it as is
896
+ result_dict = result
897
+
898
+ debug_print(f"Result from add_service_config: {result_dict}")
899
+ return result_dict
900
+
901
+ except Exception as e:
902
+ debug_print(f"Error in add_service_config: {e}")
903
+ traceback.print_exc(file=sys.stderr)
904
+ return [{"error": f"Failed to get application data metrics: {e}"}]
905
+
906
+ @register_as_tool
907
+ @with_header_auth(ApplicationSettingsApi)
908
+ async def replace_all_service_configs(self,
909
+ enabled: bool,
910
+ match_specification: List[Dict[str, str]],
911
+ name: str,
912
+ label:str,
913
+ id: str,
914
+ comment: Optional[str] = None,
915
+ ctx=None,
916
+ api_client=None) -> List[Dict[str, Any]]:
917
+ """
918
+
919
+ Args:
920
+
921
+ ctx: The MCP context (optional)
922
+
923
+ Returns:
924
+ Dictionary containing endpoints data or error information
925
+ """
926
+ try:
927
+ debug_print("Fetching all service configs")
928
+ if not (enabled or match_specification or name or label or id):
929
+ return [{"error": "Required entities are missing or invalid"}]
930
+
931
+ body = {
932
+ "match_specification": match_specification,
933
+ "enabled": enabled,
934
+ "id": id,
935
+ "label": label,
936
+ "name": name
937
+ }
938
+
939
+ if comment:
940
+ body["comment"] = comment
941
+
942
+ service_config_list = [ServiceConfig(**body)]
943
+
944
+ result = api_client.replace_all(
945
+ service_config=service_config_list
946
+ )
947
+
948
+ # Convert the result to a dictionary
949
+ if hasattr(result, 'to_dict'):
950
+ result_dict = result.to_dict()
951
+ else:
952
+ # If it's already a dict or another format, use it as is
953
+ result_dict = result
954
+
955
+ debug_print(f"Result from get_service_configs: {result_dict}")
956
+ return result_dict
957
+
958
+ except Exception as e:
959
+ debug_print(f"Error in get_service_configs: {e}")
960
+ traceback.print_exc(file=sys.stderr)
961
+ return [{"error": f"Failed to get application data metrics: {e}"}]
962
+ @register_as_tool
963
+ @with_header_auth(ApplicationSettingsApi)
964
+ async def order_service_config(self,
965
+ request_body: List[str],
966
+ ctx=None,
967
+ api_client=None) -> Dict[str, Any]:
968
+ """
969
+ order Service Configurations (Custom Service Rules)
970
+
971
+ This tool changes the order of service configurations based on the provided list of IDs.
972
+ All service configuration IDs must be included in the request.
973
+
974
+ Args:
975
+ request_body: List of service configuration IDs in the desired order.
976
+ ctx: The MCP context (optional)
977
+
978
+ Returns:
979
+ A dictionary with the API response or error message.
980
+ """
981
+ try:
982
+ debug_print("ordering service configurations")
983
+
984
+ if not request_body:
985
+ return {"error": "The list of service configuration IDs cannot be empty."}
986
+
987
+ result = api_client.order_service_config(
988
+ request_body=request_body
989
+ )
990
+
991
+ # Convert result to dict if needed
992
+ if hasattr(result, 'to_dict'):
993
+ return result.to_dict()
994
+ return result
995
+
996
+ except Exception as e:
997
+ debug_print(f"Error in order_service_config: {e}")
998
+ traceback.print_exc(file=sys.stderr)
999
+ return {"error": f"Failed to order service configs: {e!s}"}
1000
+
1001
+ @register_as_tool
1002
+ @with_header_auth(ApplicationSettingsApi)
1003
+ async def delete_service_config(self,
1004
+ id: str,
1005
+ ctx=None,
1006
+ api_client=None) -> Dict[str, Any]:
1007
+ """
1008
+ Delete a Service Perspective configuration.
1009
+ This tool allows you to delete an existing Service Config by its ID.
1010
+
1011
+ Args:
1012
+ id: The ID of the application perspective to delete
1013
+ ctx: The MCP context (optional)
1014
+
1015
+ Returns:
1016
+ Dictionary containing the result of the deletion or error information
1017
+ """
1018
+ try:
1019
+ if not id:
1020
+ return {"error": "Service perspective ID is required for deletion"}
1021
+
1022
+
1023
+ debug_print(f"Deleting application perspective with ID: {id}")
1024
+ # Call the delete_service_config method from the SDK
1025
+ api_client.delete_service_config(id=id)
1026
+
1027
+ result_dict = {
1028
+ "success": True,
1029
+ "message": f"Service Confiuguration '{id}' has been successfully deleted"
1030
+ }
1031
+
1032
+ debug_print(f"Successfully deleted service perspective with ID: {id}")
1033
+ return result_dict
1034
+ except Exception as e:
1035
+ debug_print(f"Error in delete_service_config: {e}")
1036
+ traceback.print_exc(file=sys.stderr)
1037
+ return {"error": f"Failed to delete service configuration: {e!s}"}
1038
+
1039
+ @register_as_tool
1040
+ @with_header_auth(ApplicationSettingsApi)
1041
+ async def get_service_config(
1042
+ self,
1043
+ id: str,
1044
+ ctx=None,
1045
+ api_client=None
1046
+ ) -> Dict[str, Any]:
1047
+ """
1048
+ This MCP tool is used if one wants to retrieve the particular custom service configuration.
1049
+ Args:
1050
+ id: An Instana generated unique identifier for a Service.
1051
+ ctx: The MCP context (optional)
1052
+
1053
+ Returns:
1054
+ Dict[str, Any]: Response from the create/update endpoint configuration API.
1055
+
1056
+ """
1057
+ try:
1058
+ debug_print("get service config")
1059
+ if not id:
1060
+ return {"error": "Required entities are missing or invalid"}
1061
+
1062
+ result = api_client.get_service_config(
1063
+ id=id
1064
+ )
1065
+ # Convert the result to a dictionary
1066
+ if hasattr(result, 'to_dict'):
1067
+ result_dict = result.to_dict()
1068
+ else:
1069
+ # If it's already a dict or another format, use it as is
1070
+ result_dict = result
1071
+
1072
+ debug_print(f"Result from get_service_config: {result_dict}")
1073
+ return result_dict
1074
+ except Exception as e:
1075
+ debug_print(f"Error in get_service_config: {e}")
1076
+ traceback.print_exc(file=sys.stderr)
1077
+ return {"error": f"Failed to get service config: {e!s}"}
1078
+
1079
+ @register_as_tool
1080
+ @with_header_auth(ApplicationSettingsApi)
1081
+ async def update_service_configs(self,
1082
+ enabled: bool,
1083
+ match_specification: List[Dict[str, str]],
1084
+ name: str,
1085
+ label:str,
1086
+ id: str,
1087
+ comment: Optional[str] = None,
1088
+ ctx=None,
1089
+ api_client=None) -> List[Dict[str, Any]]:
1090
+ """
1091
+ This tool gives is used if one wants to update a particular custom service rule.
1092
+ Args:
1093
+
1094
+ ctx: The MCP context (optional)
1095
+
1096
+ Returns:
1097
+ Dictionary containing endpoints data or error information
1098
+ """
1099
+ try:
1100
+ debug_print("Adding new service config")
1101
+ if not (id and name and label and isinstance(match_specification, list)):
1102
+ return [{"error": "Required entities are missing or invalid"}]
1103
+
1104
+ body = {
1105
+ "match_specification": match_specification,
1106
+ "enabled": enabled,
1107
+ "id": id,
1108
+ "label": label,
1109
+ "name": name
1110
+ }
1111
+
1112
+ if comment:
1113
+ body["comment"] = comment
1114
+
1115
+ service_config = ServiceConfig(**body)
1116
+
1117
+ result = api_client.put_service_config(
1118
+ id=id,
1119
+ service_config=service_config
1120
+ )
1121
+
1122
+ # Convert the result to a dictionary
1123
+ if hasattr(result, 'to_dict'):
1124
+ result_dict = result.to_dict()
1125
+ else:
1126
+ # If it's already a dict or another format, use it as is
1127
+ result_dict = result
1128
+
1129
+ debug_print(f"Result from add_service_config: {result_dict}")
1130
+ return result_dict
1131
+
1132
+ except Exception as e:
1133
+ debug_print(f"Error in add_service_config: {e}")
1134
+ traceback.print_exc(file=sys.stderr)
1135
+ return [{"error": f"Failed to add new service config: {e!s}"}]