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.
- {mcp_instana-0.1.1.dist-info → mcp_instana-0.2.0.dist-info}/METADATA +459 -138
- mcp_instana-0.2.0.dist-info/RECORD +59 -0
- src/application/application_analyze.py +373 -160
- src/application/application_catalog.py +3 -1
- src/application/application_global_alert_config.py +653 -0
- src/application/application_metrics.py +6 -2
- src/application/application_resources.py +3 -1
- src/application/application_settings.py +966 -370
- src/application/application_topology.py +6 -2
- src/automation/action_catalog.py +416 -0
- src/automation/action_history.py +338 -0
- src/core/server.py +159 -9
- src/core/utils.py +2 -2
- src/event/events_tools.py +602 -275
- src/infrastructure/infrastructure_analyze.py +7 -3
- src/infrastructure/infrastructure_catalog.py +3 -1
- src/infrastructure/infrastructure_metrics.py +6 -2
- src/infrastructure/infrastructure_resources.py +7 -5
- src/infrastructure/infrastructure_topology.py +5 -3
- src/prompts/__init__.py +16 -0
- src/prompts/application/__init__.py +1 -0
- src/prompts/application/application_alerts.py +54 -0
- src/prompts/application/application_catalog.py +26 -0
- src/prompts/application/application_metrics.py +57 -0
- src/prompts/application/application_resources.py +26 -0
- src/prompts/application/application_settings.py +75 -0
- src/prompts/application/application_topology.py +30 -0
- src/prompts/events/__init__.py +1 -0
- src/prompts/events/events_tools.py +161 -0
- src/prompts/infrastructure/infrastructure_analyze.py +72 -0
- src/prompts/infrastructure/infrastructure_catalog.py +53 -0
- src/prompts/infrastructure/infrastructure_metrics.py +45 -0
- src/prompts/infrastructure/infrastructure_resources.py +74 -0
- src/prompts/infrastructure/infrastructure_topology.py +38 -0
- src/prompts/settings/__init__.py +0 -0
- src/prompts/settings/custom_dashboard.py +157 -0
- src/prompts/website/__init__.py +1 -0
- src/prompts/website/website_analyze.py +35 -0
- src/prompts/website/website_catalog.py +40 -0
- src/prompts/website/website_configuration.py +105 -0
- src/prompts/website/website_metrics.py +34 -0
- src/settings/__init__.py +1 -0
- src/settings/custom_dashboard_tools.py +417 -0
- src/website/__init__.py +0 -0
- src/website/website_analyze.py +433 -0
- src/website/website_catalog.py +171 -0
- src/website/website_configuration.py +770 -0
- src/website/website_metrics.py +241 -0
- mcp_instana-0.1.1.dist-info/RECORD +0 -30
- src/prompts/mcp_prompts.py +0 -900
- src/prompts/prompt_loader.py +0 -29
- src/prompts/prompt_registry.json +0 -21
- {mcp_instana-0.1.1.dist-info → mcp_instana-0.2.0.dist-info}/WHEEL +0 -0
- {mcp_instana-0.1.1.dist-info → mcp_instana-0.2.0.dist-info}/entry_points.txt +0 -0
- {mcp_instana-0.1.1.dist-info → mcp_instana-0.2.0.dist-info}/licenses/LICENSE.md +0 -0
|
@@ -6,25 +6,32 @@ This module provides application settings-specific MCP tools for Instana monitor
|
|
|
6
6
|
The API endpoints of this group provides a way to create, read, update, delete (CRUD) for various configuration settings.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
+
import logging
|
|
9
10
|
import re
|
|
10
11
|
import sys
|
|
11
12
|
import traceback
|
|
12
13
|
from datetime import datetime
|
|
13
|
-
from typing import Any, Dict, List, Optional
|
|
14
|
+
from typing import Any, Dict, List, Optional, Union
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
14
17
|
|
|
15
18
|
from src.core.utils import BaseInstanaClient, register_as_tool, with_header_auth
|
|
16
19
|
|
|
17
20
|
# Import the necessary classes from the SDK
|
|
18
21
|
try:
|
|
19
|
-
from instana_client.api
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
from instana_client.
|
|
23
|
-
from instana_client.
|
|
24
|
-
from instana_client.models
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
from instana_client.api import (
|
|
23
|
+
ApplicationSettingsApi, #type: ignore
|
|
24
|
+
)
|
|
25
|
+
from instana_client.api_client import ApiClient #type: ignore
|
|
26
|
+
from instana_client.configuration import Configuration #type: ignore
|
|
27
|
+
from instana_client.models import (
|
|
28
|
+
ApplicationConfig, #type: ignore
|
|
29
|
+
EndpointConfig, #type: ignore
|
|
30
|
+
ManualServiceConfig, #type: ignore
|
|
31
|
+
NewApplicationConfig, #type: ignore
|
|
32
|
+
NewManualServiceConfig, #type: ignore
|
|
33
|
+
ServiceConfig, #type: ignore
|
|
34
|
+
)
|
|
28
35
|
except ImportError as e:
|
|
29
36
|
print(f"Error importing Instana SDK: {e}", file=sys.stderr)
|
|
30
37
|
traceback.print_exc(file=sys.stderr)
|
|
@@ -98,59 +105,147 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
98
105
|
@register_as_tool
|
|
99
106
|
@with_header_auth(ApplicationSettingsApi)
|
|
100
107
|
async def add_application_config(self,
|
|
101
|
-
|
|
102
|
-
boundary_scope: str,
|
|
103
|
-
label: str,
|
|
104
|
-
scope: str,
|
|
105
|
-
tag_filter_expression: Optional[List[Dict[str, str]]] = None,
|
|
108
|
+
payload: Union[Dict[str, Any], str],
|
|
106
109
|
ctx=None,
|
|
107
110
|
api_client=None) -> Dict[str, Any]:
|
|
108
111
|
"""
|
|
109
112
|
Add a new Application Perspective configuration.
|
|
110
113
|
This tool allows you to create a new Application Perspective with specified settings.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
Sample Payload: {
|
|
115
|
+
"accessRules": [
|
|
116
|
+
{
|
|
117
|
+
"accessType": "READ_WRITE",
|
|
118
|
+
"relationType": "GLOBAL",
|
|
119
|
+
"relatedId": null
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
"boundaryScope": "INBOUND",
|
|
123
|
+
"label": "Discount Build 6987",
|
|
124
|
+
"scope": "INCLUDE_IMMEDIATE_DOWNSTREAM_DATABASE_AND_MESSAGING",
|
|
125
|
+
"tagFilterExpression": {
|
|
126
|
+
"type": "EXPRESSION",
|
|
127
|
+
"logicalOperator": "AND",
|
|
128
|
+
"elements": [
|
|
129
|
+
{
|
|
130
|
+
"type": "TAG_FILTER",
|
|
131
|
+
"name": "kubernetes.label",
|
|
132
|
+
"stringValue": "stage=canary",
|
|
133
|
+
"numberValue": null,
|
|
134
|
+
"booleanValue": null,
|
|
135
|
+
"key": "stage",
|
|
136
|
+
"value": "canary",
|
|
137
|
+
"operator": "EQUALS",
|
|
138
|
+
"entity": "DESTINATION"
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"type": "TAG_FILTER",
|
|
142
|
+
"name": "kubernetes.label",
|
|
143
|
+
"stringValue": "build=6987",
|
|
144
|
+
"numberValue": null,
|
|
145
|
+
"booleanValue": null,
|
|
146
|
+
"key": "build",
|
|
147
|
+
"value": "6987",
|
|
148
|
+
"operator": "EQUALS",
|
|
149
|
+
"entity": "DESTINATION"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"type": "TAG_FILTER",
|
|
153
|
+
"name": "kubernetes.label",
|
|
154
|
+
"stringValue": "app=discount",
|
|
155
|
+
"numberValue": null,
|
|
156
|
+
"booleanValue": null,
|
|
157
|
+
"key": "app",
|
|
158
|
+
"value": "discount",
|
|
159
|
+
"operator": "EQUALS",
|
|
160
|
+
"entity": "DESTINATION"
|
|
161
|
+
}
|
|
162
|
+
]
|
|
163
|
+
}
|
|
164
|
+
}
|
|
118
165
|
Returns:
|
|
119
166
|
Dictionary containing the created application perspective configuration or error information
|
|
120
167
|
"""
|
|
121
168
|
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
169
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
"
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
170
|
+
if not payload:
|
|
171
|
+
return {"error": "payload is required"}
|
|
172
|
+
|
|
173
|
+
# Parse the payload if it's a string
|
|
174
|
+
if isinstance(payload, str):
|
|
175
|
+
logger.debug("Payload is a string, attempting to parse")
|
|
176
|
+
try:
|
|
177
|
+
import json
|
|
178
|
+
try:
|
|
179
|
+
parsed_payload = json.loads(payload)
|
|
180
|
+
logger.debug("Successfully parsed payload as JSON")
|
|
181
|
+
request_body = parsed_payload
|
|
182
|
+
except json.JSONDecodeError as e:
|
|
183
|
+
logger.debug(f"JSON parsing failed: {e}, trying with quotes replaced")
|
|
184
|
+
|
|
185
|
+
# Try replacing single quotes with double quotes
|
|
186
|
+
fixed_payload = payload.replace("'", "\"")
|
|
187
|
+
try:
|
|
188
|
+
parsed_payload = json.loads(fixed_payload)
|
|
189
|
+
logger.debug("Successfully parsed fixed JSON")
|
|
190
|
+
request_body = parsed_payload
|
|
191
|
+
except json.JSONDecodeError:
|
|
192
|
+
# Try as Python literal
|
|
193
|
+
import ast
|
|
194
|
+
try:
|
|
195
|
+
parsed_payload = ast.literal_eval(payload)
|
|
196
|
+
logger.debug("Successfully parsed payload as Python literal")
|
|
197
|
+
request_body = parsed_payload
|
|
198
|
+
except (SyntaxError, ValueError) as e2:
|
|
199
|
+
logger.debug(f"Failed to parse payload string: {e2}")
|
|
200
|
+
return {"error": f"Invalid payload format: {e2}", "payload": payload}
|
|
201
|
+
except Exception as e:
|
|
202
|
+
logger.debug(f"Error parsing payload string: {e}")
|
|
203
|
+
return {"error": f"Failed to parse payload: {e}", "payload": payload}
|
|
204
|
+
else:
|
|
205
|
+
# If payload is already a dictionary, use it directly
|
|
206
|
+
logger.debug("Using provided payload dictionary")
|
|
207
|
+
request_body = payload
|
|
208
|
+
|
|
209
|
+
# Import the NewApplicationConfig class
|
|
210
|
+
try:
|
|
211
|
+
from instana_client.models.new_application_config import (
|
|
212
|
+
NewApplicationConfig,
|
|
213
|
+
)
|
|
214
|
+
logger.debug("Successfully imported NewApplicationConfig")
|
|
215
|
+
except ImportError as e:
|
|
216
|
+
logger.debug(f"Error importing NewApplicationConfig: {e}")
|
|
217
|
+
return {"error": f"Failed to import NewApplicationConfig: {e!s}"}
|
|
218
|
+
|
|
219
|
+
# Create an NewApplicationConfig object from the request body
|
|
220
|
+
try:
|
|
221
|
+
logger.debug(f"Creating NewApplicationConfig with params: {request_body}")
|
|
222
|
+
config_object = NewApplicationConfig(**request_body)
|
|
223
|
+
logger.debug("Successfully created config object")
|
|
224
|
+
except Exception as e:
|
|
225
|
+
logger.debug(f"Error creating NewApplicationConfig: {e}")
|
|
226
|
+
return {"error": f"Failed to create config object: {e!s}"}
|
|
136
227
|
|
|
137
228
|
# Call the add_application_config method from the SDK
|
|
229
|
+
logger.debug("Calling add_application_config with config object")
|
|
138
230
|
result = api_client.add_application_config(
|
|
139
|
-
new_application_config=
|
|
231
|
+
new_application_config=config_object
|
|
140
232
|
)
|
|
141
233
|
|
|
142
234
|
# Convert the result to a dictionary
|
|
143
235
|
if hasattr(result, 'to_dict'):
|
|
144
236
|
result_dict = result.to_dict()
|
|
145
237
|
else:
|
|
146
|
-
|
|
238
|
+
# If it's already a dict or another format, use it as is
|
|
239
|
+
result_dict = result or {
|
|
240
|
+
"success": True,
|
|
241
|
+
"message": "Create new application config"
|
|
242
|
+
}
|
|
147
243
|
|
|
148
|
-
|
|
244
|
+
logger.debug(f"Result from add_application_config: {result_dict}")
|
|
149
245
|
return result_dict
|
|
150
246
|
except Exception as e:
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
return {"error": f"Failed to add application configuration: {e!s}"}
|
|
247
|
+
logger.error(f"Error in add_application_config: {e}")
|
|
248
|
+
return {"error": f"Failed to add new application config: {e!s}"}
|
|
154
249
|
|
|
155
250
|
@register_as_tool
|
|
156
251
|
@with_header_auth(ApplicationSettingsApi)
|
|
@@ -176,7 +271,7 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
176
271
|
|
|
177
272
|
debug_print(f"Deleting application perspective with ID: {id}")
|
|
178
273
|
# Call the delete_application_config method from the SDK
|
|
179
|
-
|
|
274
|
+
api_client.delete_application_config(id=id)
|
|
180
275
|
|
|
181
276
|
result_dict = {
|
|
182
277
|
"success": True,
|
|
@@ -230,12 +325,7 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
230
325
|
async def update_application_config(
|
|
231
326
|
self,
|
|
232
327
|
id: str,
|
|
233
|
-
|
|
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,
|
|
328
|
+
payload: Union[Dict[str, Any], str],
|
|
239
329
|
ctx=None,
|
|
240
330
|
api_client=None
|
|
241
331
|
) -> Dict[str, Any]:
|
|
@@ -245,52 +335,143 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
245
335
|
|
|
246
336
|
Args:
|
|
247
337
|
id: The ID of the application perspective to retrieve
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
338
|
+
Sample payload: {
|
|
339
|
+
"accessRules": [
|
|
340
|
+
{
|
|
341
|
+
"accessType": "READ",
|
|
342
|
+
"relationType": "ROLE"
|
|
343
|
+
}
|
|
344
|
+
],
|
|
345
|
+
"boundaryScope": "INBOUND",
|
|
346
|
+
"id": "CxJ55sRbQwqBIfw5DzpRmQ",
|
|
347
|
+
"label": "Discount Build 1",
|
|
348
|
+
"scope": "INCLUDE_NO_DOWNSTREAM",
|
|
349
|
+
"tagFilterExpression": {
|
|
350
|
+
"type": "EXPRESSION",
|
|
351
|
+
"logicalOperator": "AND",
|
|
352
|
+
"elements": [
|
|
353
|
+
{
|
|
354
|
+
"type": "TAG_FILTER",
|
|
355
|
+
"name": "kubernetes.label",
|
|
356
|
+
"stringValue": "stage=canary",
|
|
357
|
+
"numberValue": null,
|
|
358
|
+
"booleanValue": null,
|
|
359
|
+
"key": "stage",
|
|
360
|
+
"value": "canary",
|
|
361
|
+
"operator": "EQUALS",
|
|
362
|
+
"entity": "DESTINATION"
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
"type": "TAG_FILTER",
|
|
366
|
+
"name": "kubernetes.label",
|
|
367
|
+
"stringValue": "build=6987",
|
|
368
|
+
"numberValue": null,
|
|
369
|
+
"booleanValue": null,
|
|
370
|
+
"key": "build",
|
|
371
|
+
"value": "6987",
|
|
372
|
+
"operator": "EQUALS",
|
|
373
|
+
"entity": "DESTINATION"
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
"type": "TAG_FILTER",
|
|
377
|
+
"name": "kubernetes.label",
|
|
378
|
+
"stringValue": "app=discount",
|
|
379
|
+
"numberValue": null,
|
|
380
|
+
"booleanValue": null,
|
|
381
|
+
"key": "app",
|
|
382
|
+
"value": "discount",
|
|
383
|
+
"operator": "EQUALS",
|
|
384
|
+
"entity": "DESTINATION"
|
|
385
|
+
}
|
|
386
|
+
]
|
|
387
|
+
}
|
|
388
|
+
}
|
|
253
389
|
ctx: The MCP context (optional)
|
|
254
390
|
Returns:
|
|
255
391
|
Dictionary containing the created application perspective configuration or error information
|
|
256
392
|
"""
|
|
257
|
-
|
|
258
393
|
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
394
|
|
|
263
|
-
|
|
264
|
-
"
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
"
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
395
|
+
if not payload or not id:
|
|
396
|
+
return {"error": "missing arguments"}
|
|
397
|
+
|
|
398
|
+
# Parse the payload if it's a string
|
|
399
|
+
if isinstance(payload, str):
|
|
400
|
+
logger.debug("Payload is a string, attempting to parse")
|
|
401
|
+
try:
|
|
402
|
+
import json
|
|
403
|
+
try:
|
|
404
|
+
parsed_payload = json.loads(payload)
|
|
405
|
+
logger.debug("Successfully parsed payload as JSON")
|
|
406
|
+
request_body = parsed_payload
|
|
407
|
+
except json.JSONDecodeError as e:
|
|
408
|
+
logger.debug(f"JSON parsing failed: {e}, trying with quotes replaced")
|
|
409
|
+
|
|
410
|
+
# Try replacing single quotes with double quotes
|
|
411
|
+
fixed_payload = payload.replace("'", "\"")
|
|
412
|
+
try:
|
|
413
|
+
parsed_payload = json.loads(fixed_payload)
|
|
414
|
+
logger.debug("Successfully parsed fixed JSON")
|
|
415
|
+
request_body = parsed_payload
|
|
416
|
+
except json.JSONDecodeError:
|
|
417
|
+
# Try as Python literal
|
|
418
|
+
import ast
|
|
419
|
+
try:
|
|
420
|
+
parsed_payload = ast.literal_eval(payload)
|
|
421
|
+
logger.debug("Successfully parsed payload as Python literal")
|
|
422
|
+
request_body = parsed_payload
|
|
423
|
+
except (SyntaxError, ValueError) as e2:
|
|
424
|
+
logger.debug(f"Failed to parse payload string: {e2}")
|
|
425
|
+
return {"error": f"Invalid payload format: {e2}", "payload": payload}
|
|
426
|
+
except Exception as e:
|
|
427
|
+
logger.debug(f"Error parsing payload string: {e}")
|
|
428
|
+
return {"error": f"Failed to parse payload: {e}", "payload": payload}
|
|
429
|
+
else:
|
|
430
|
+
# If payload is already a dictionary, use it directly
|
|
431
|
+
logger.debug("Using provided payload dictionary")
|
|
432
|
+
request_body = payload
|
|
433
|
+
|
|
434
|
+
# Import the ApplicationConfig class
|
|
435
|
+
try:
|
|
436
|
+
from instana_client.models.application_config import (
|
|
437
|
+
ApplicationConfig,
|
|
438
|
+
)
|
|
439
|
+
logger.debug("Successfully imported ApplicationConfig")
|
|
440
|
+
except ImportError as e:
|
|
441
|
+
logger.debug(f"Error importing ApplicationConfig: {e}")
|
|
442
|
+
return {"error": f"Failed to import ApplicationConfig: {e!s}"}
|
|
443
|
+
|
|
444
|
+
# Create an ApplicationConfig object from the request body
|
|
445
|
+
try:
|
|
446
|
+
logger.debug(f"Creating ApplicationConfig with params: {request_body}")
|
|
447
|
+
config_object = ApplicationConfig(**request_body)
|
|
448
|
+
logger.debug("Successfully updated application config object")
|
|
449
|
+
except Exception as e:
|
|
450
|
+
logger.debug(f"Error updating ApplicationConfig: {e}")
|
|
451
|
+
return {"error": f"Failed to update config object: {e!s}"}
|
|
275
452
|
|
|
276
453
|
# Call the put_application_config method from the SDK
|
|
454
|
+
logger.debug("Calling put_application_config with config object")
|
|
277
455
|
result = api_client.put_application_config(
|
|
278
456
|
id=id,
|
|
279
|
-
application_config=
|
|
457
|
+
application_config=config_object
|
|
280
458
|
)
|
|
281
459
|
|
|
282
460
|
# Convert the result to a dictionary
|
|
283
461
|
if hasattr(result, 'to_dict'):
|
|
284
462
|
result_dict = result.to_dict()
|
|
285
463
|
else:
|
|
286
|
-
|
|
464
|
+
# If it's already a dict or another format, use it as is
|
|
465
|
+
result_dict = result or {
|
|
466
|
+
"success": True,
|
|
467
|
+
"message": "Update existing application config"
|
|
468
|
+
}
|
|
287
469
|
|
|
288
|
-
|
|
470
|
+
logger.debug(f"Result from put_application_config: {result_dict}")
|
|
289
471
|
return result_dict
|
|
290
472
|
except Exception as e:
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
return {"error": f"Failed to update application configuration: {e!s}"}
|
|
473
|
+
logger.error(f"Error in put_application_config: {e}")
|
|
474
|
+
return {"error": f"Failed to update existing application config: {e!s}"}
|
|
294
475
|
|
|
295
476
|
@register_as_tool
|
|
296
477
|
@with_header_auth(ApplicationSettingsApi)
|
|
@@ -328,60 +509,103 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
328
509
|
@with_header_auth(ApplicationSettingsApi)
|
|
329
510
|
async def create_endpoint_config(
|
|
330
511
|
self,
|
|
331
|
-
|
|
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,
|
|
512
|
+
payload: Union[Dict[str, Any], str],
|
|
336
513
|
ctx=None,
|
|
337
514
|
api_client=None
|
|
338
515
|
) -> Dict[str, Any]:
|
|
339
516
|
"""
|
|
340
517
|
Create or update endpoint configuration for a service.
|
|
341
518
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
519
|
+
Sample Payload: {
|
|
520
|
+
"serviceId": "d0cedae516f2182ede16f57f67476dd4c7dab9cd",
|
|
521
|
+
"endpointCase": "LOWER",
|
|
522
|
+
"endpointNameByFirstPathSegmentRuleEnabled": false,
|
|
523
|
+
"endpointNameByCollectedPathTemplateRuleEnabled": false,
|
|
524
|
+
"rules": null
|
|
525
|
+
}
|
|
349
526
|
|
|
350
527
|
Returns:
|
|
351
528
|
Dict[str, Any]: Response from the create/update endpoint configuration API.
|
|
352
529
|
"""
|
|
353
530
|
try:
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
"
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
531
|
+
if not payload:
|
|
532
|
+
return {"error": "missing arguments"}
|
|
533
|
+
|
|
534
|
+
# Parse the payload if it's a string
|
|
535
|
+
if isinstance(payload, str):
|
|
536
|
+
logger.debug("Payload is a string, attempting to parse")
|
|
537
|
+
try:
|
|
538
|
+
import json
|
|
539
|
+
try:
|
|
540
|
+
parsed_payload = json.loads(payload)
|
|
541
|
+
logger.debug("Successfully parsed payload as JSON")
|
|
542
|
+
request_body = parsed_payload
|
|
543
|
+
except json.JSONDecodeError as e:
|
|
544
|
+
logger.debug(f"JSON parsing failed: {e}, trying with quotes replaced")
|
|
545
|
+
|
|
546
|
+
# Try replacing single quotes with double quotes
|
|
547
|
+
fixed_payload = payload.replace("'", "\"")
|
|
548
|
+
try:
|
|
549
|
+
parsed_payload = json.loads(fixed_payload)
|
|
550
|
+
logger.debug("Successfully parsed fixed JSON")
|
|
551
|
+
request_body = parsed_payload
|
|
552
|
+
except json.JSONDecodeError:
|
|
553
|
+
# Try as Python literal
|
|
554
|
+
import ast
|
|
555
|
+
try:
|
|
556
|
+
parsed_payload = ast.literal_eval(payload)
|
|
557
|
+
logger.debug("Successfully parsed payload as Python literal")
|
|
558
|
+
request_body = parsed_payload
|
|
559
|
+
except (SyntaxError, ValueError) as e2:
|
|
560
|
+
logger.debug(f"Failed to parse payload string: {e2}")
|
|
561
|
+
return {"error": f"Invalid payload format: {e2}", "payload": payload}
|
|
562
|
+
except Exception as e:
|
|
563
|
+
logger.debug(f"Error parsing payload string: {e}")
|
|
564
|
+
return {"error": f"Failed to parse payload: {e}", "payload": payload}
|
|
565
|
+
else:
|
|
566
|
+
# If payload is already a dictionary, use it directly
|
|
567
|
+
logger.debug("Using provided payload dictionary")
|
|
568
|
+
request_body = payload
|
|
569
|
+
|
|
570
|
+
# Import the EndpointConfig class
|
|
571
|
+
try:
|
|
572
|
+
from instana_client.models.endpoint_config import (
|
|
573
|
+
EndpointConfig,
|
|
574
|
+
)
|
|
575
|
+
logger.debug("Successfully imported EndpointConfig")
|
|
576
|
+
except ImportError as e:
|
|
577
|
+
logger.debug(f"Error importing EndpointConfig: {e}")
|
|
578
|
+
return {"error": f"Failed to import EndpointConfig: {e!s}"}
|
|
579
|
+
|
|
580
|
+
# Create an EndpointConfig object from the request body
|
|
581
|
+
try:
|
|
582
|
+
logger.debug(f"Creating EndpointConfig with params: {request_body}")
|
|
583
|
+
config_object = EndpointConfig(**request_body)
|
|
584
|
+
logger.debug("Successfully created endpoint config object")
|
|
585
|
+
except Exception as e:
|
|
586
|
+
logger.debug(f"Error creating EndpointConfig: {e}")
|
|
587
|
+
return {"error": f"Failed to create config object: {e!s}"}
|
|
588
|
+
|
|
589
|
+
# Call the create_endpoint_config method from the SDK
|
|
590
|
+
logger.debug("Calling create_endpoint_config with config object")
|
|
367
591
|
result = api_client.create_endpoint_config(
|
|
368
|
-
endpoint_config=
|
|
592
|
+
endpoint_config=config_object
|
|
369
593
|
)
|
|
370
|
-
|
|
371
594
|
# Convert the result to a dictionary
|
|
372
595
|
if hasattr(result, 'to_dict'):
|
|
373
596
|
result_dict = result.to_dict()
|
|
374
597
|
else:
|
|
375
598
|
# If it's already a dict or another format, use it as is
|
|
376
|
-
result_dict = result
|
|
599
|
+
result_dict = result or {
|
|
600
|
+
"success": True,
|
|
601
|
+
"message": "Create new endpoint config"
|
|
602
|
+
}
|
|
377
603
|
|
|
378
|
-
|
|
604
|
+
logger.debug(f"Result from create_endpoint_config: {result_dict}")
|
|
379
605
|
return result_dict
|
|
380
|
-
|
|
381
606
|
except Exception as e:
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
return {"error": f"Failed to get endpoint configs: {e!s}"}
|
|
607
|
+
logger.error(f"Error in create_endpoint_config: {e}")
|
|
608
|
+
return {"error": f"Failed to create new endpoint config: {e!s}"}
|
|
385
609
|
|
|
386
610
|
@register_as_tool
|
|
387
611
|
@with_header_auth(ApplicationSettingsApi)
|
|
@@ -466,11 +690,7 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
466
690
|
async def update_endpoint_config(
|
|
467
691
|
self,
|
|
468
692
|
id: str,
|
|
469
|
-
|
|
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,
|
|
693
|
+
payload: Union[Dict[str, Any], str],
|
|
474
694
|
ctx=None,
|
|
475
695
|
api_client=None
|
|
476
696
|
) -> Dict[str, Any]:
|
|
@@ -479,33 +699,99 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
479
699
|
|
|
480
700
|
Args:
|
|
481
701
|
id: An Instana generated unique identifier for a Service.
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
endpointNameByCollectedPathTemplateRuleEnabled:
|
|
485
|
-
endpointNameByFirstPathSegmentRuleEnabled:
|
|
486
|
-
rules:
|
|
702
|
+
{
|
|
703
|
+
"serviceId": "20ba31821b079e7d845a08096124880db3eeeb40",
|
|
704
|
+
"endpointNameByCollectedPathTemplateRuleEnabled": true,
|
|
705
|
+
"endpointNameByFirstPathSegmentRuleEnabled": true,
|
|
706
|
+
"rules": [
|
|
707
|
+
{
|
|
708
|
+
"enabled": true,
|
|
709
|
+
"pathSegments": [
|
|
710
|
+
{
|
|
711
|
+
"name": "api",
|
|
712
|
+
"type": "FIXED"
|
|
713
|
+
},
|
|
714
|
+
{
|
|
715
|
+
"name": "version",
|
|
716
|
+
"type": "PARAMETER"
|
|
717
|
+
}
|
|
718
|
+
],
|
|
719
|
+
"testCases": [
|
|
720
|
+
"/api/v2/users"
|
|
721
|
+
]
|
|
722
|
+
}
|
|
723
|
+
],
|
|
724
|
+
"endpointCase": "UPPER"
|
|
725
|
+
}
|
|
487
726
|
ctx: The MCP context (optional)
|
|
488
727
|
|
|
489
728
|
Returns:
|
|
490
729
|
Dict[str, Any]: Response from the create/update endpoint configuration API.
|
|
491
730
|
"""
|
|
492
731
|
try:
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
"
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
732
|
+
if not payload or not id:
|
|
733
|
+
return {"error": "missing arguments"}
|
|
734
|
+
|
|
735
|
+
# Parse the payload if it's a string
|
|
736
|
+
if isinstance(payload, str):
|
|
737
|
+
logger.debug("Payload is a string, attempting to parse")
|
|
738
|
+
try:
|
|
739
|
+
import json
|
|
740
|
+
try:
|
|
741
|
+
parsed_payload = json.loads(payload)
|
|
742
|
+
logger.debug("Successfully parsed payload as JSON")
|
|
743
|
+
request_body = parsed_payload
|
|
744
|
+
except json.JSONDecodeError as e:
|
|
745
|
+
logger.debug(f"JSON parsing failed: {e}, trying with quotes replaced")
|
|
746
|
+
|
|
747
|
+
# Try replacing single quotes with double quotes
|
|
748
|
+
fixed_payload = payload.replace("'", "\"")
|
|
749
|
+
try:
|
|
750
|
+
parsed_payload = json.loads(fixed_payload)
|
|
751
|
+
logger.debug("Successfully parsed fixed JSON")
|
|
752
|
+
request_body = parsed_payload
|
|
753
|
+
except json.JSONDecodeError:
|
|
754
|
+
# Try as Python literal
|
|
755
|
+
import ast
|
|
756
|
+
try:
|
|
757
|
+
parsed_payload = ast.literal_eval(payload)
|
|
758
|
+
logger.debug("Successfully parsed payload as Python literal")
|
|
759
|
+
request_body = parsed_payload
|
|
760
|
+
except (SyntaxError, ValueError) as e2:
|
|
761
|
+
logger.debug(f"Failed to parse payload string: {e2}")
|
|
762
|
+
return {"error": f"Invalid payload format: {e2}", "payload": payload}
|
|
763
|
+
except Exception as e:
|
|
764
|
+
logger.debug(f"Error parsing payload string: {e}")
|
|
765
|
+
return {"error": f"Failed to parse payload: {e}", "payload": payload}
|
|
766
|
+
else:
|
|
767
|
+
# If payload is already a dictionary, use it directly
|
|
768
|
+
logger.debug("Using provided payload dictionary")
|
|
769
|
+
request_body = payload
|
|
770
|
+
|
|
771
|
+
# Import the EndpointConfig class
|
|
772
|
+
try:
|
|
773
|
+
from instana_client.models.endpoint_config import (
|
|
774
|
+
EndpointConfig,
|
|
775
|
+
)
|
|
776
|
+
logger.debug("Successfully imported EndpointConfig")
|
|
777
|
+
except ImportError as e:
|
|
778
|
+
logger.debug(f"Error importing EndpointConfig: {e}")
|
|
779
|
+
return {"error": f"Failed to import EndpointConfig: {e!s}"}
|
|
780
|
+
|
|
781
|
+
# Create an EndpointConfig object from the request body
|
|
782
|
+
try:
|
|
783
|
+
logger.debug(f"Creating EndpointConfig with params: {request_body}")
|
|
784
|
+
config_object = EndpointConfig(**request_body)
|
|
785
|
+
logger.debug("Successfully updated endpoint config object")
|
|
786
|
+
except Exception as e:
|
|
787
|
+
logger.debug(f"Error updating EndpointConfig: {e}")
|
|
788
|
+
return {"error": f"Failed to update config object: {e!s}"}
|
|
789
|
+
|
|
790
|
+
# Call the update_endpoint_config method from the SDK
|
|
791
|
+
logger.debug("Calling update_endpoint_config with config object")
|
|
506
792
|
result = api_client.update_endpoint_config(
|
|
507
793
|
id=id,
|
|
508
|
-
endpoint_config=
|
|
794
|
+
endpoint_config=config_object
|
|
509
795
|
)
|
|
510
796
|
|
|
511
797
|
# Convert the result to a dictionary
|
|
@@ -513,15 +799,17 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
513
799
|
result_dict = result.to_dict()
|
|
514
800
|
else:
|
|
515
801
|
# If it's already a dict or another format, use it as is
|
|
516
|
-
result_dict = result
|
|
802
|
+
result_dict = result or {
|
|
803
|
+
"success": True,
|
|
804
|
+
"message": "update existing endpoint config"
|
|
805
|
+
}
|
|
517
806
|
|
|
518
|
-
|
|
807
|
+
logger.debug(f"Result from update_endpoint_config: {result_dict}")
|
|
519
808
|
return result_dict
|
|
520
|
-
|
|
521
809
|
except Exception as e:
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
810
|
+
logger.error(f"Error in update_endpoint_config: {e}")
|
|
811
|
+
return {"error": f"Failed to update existing application config: {e!s}"}
|
|
812
|
+
|
|
525
813
|
|
|
526
814
|
@register_as_tool
|
|
527
815
|
@with_header_auth(ApplicationSettingsApi)
|
|
@@ -559,11 +847,7 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
559
847
|
@with_header_auth(ApplicationSettingsApi)
|
|
560
848
|
async def add_manual_service_config(
|
|
561
849
|
self,
|
|
562
|
-
|
|
563
|
-
unmonitoredServiceName: Optional[str] = None,
|
|
564
|
-
existingServiceId: Optional[str] = None,
|
|
565
|
-
description: Optional[str] = None,
|
|
566
|
-
enabled: Optional[bool] = True,
|
|
850
|
+
payload: Union[Dict[str, Any], str],
|
|
567
851
|
ctx=None,
|
|
568
852
|
api_client=None
|
|
569
853
|
) -> Dict[str, Any]:
|
|
@@ -572,59 +856,109 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
572
856
|
|
|
573
857
|
Requires `CanConfigureServiceMapping` permission on the API token.
|
|
574
858
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
859
|
+
Sample payload:
|
|
860
|
+
{
|
|
861
|
+
"description": "Map source service example",
|
|
862
|
+
"enabled": true,
|
|
863
|
+
"existingServiceId": "c467ca0fa21477fee3cde75a140b2963307388a7",
|
|
864
|
+
"tagFilterExpression": {
|
|
865
|
+
"type": "TAG_FILTER",
|
|
866
|
+
"name": "service.name",
|
|
867
|
+
"stringValue": "front",
|
|
868
|
+
"numberValue": null,
|
|
869
|
+
"booleanValue": null,
|
|
870
|
+
"key": null,
|
|
871
|
+
"value": "front",
|
|
872
|
+
"operator": "EQUALS",
|
|
873
|
+
"entity": "SOURCE"
|
|
874
|
+
},
|
|
875
|
+
"unmonitoredServiceName": null
|
|
876
|
+
}
|
|
581
877
|
ctx: Optional execution context.
|
|
582
878
|
|
|
583
879
|
Returns:
|
|
584
880
|
Dict[str, Any]: API response indicating success or failure.
|
|
585
881
|
"""
|
|
586
882
|
try:
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
883
|
+
if not payload:
|
|
884
|
+
return {"error": "missing arguments"}
|
|
885
|
+
|
|
886
|
+
# Parse the payload if it's a string
|
|
887
|
+
if isinstance(payload, str):
|
|
888
|
+
logger.debug("Payload is a string, attempting to parse")
|
|
889
|
+
try:
|
|
890
|
+
import json
|
|
891
|
+
try:
|
|
892
|
+
parsed_payload = json.loads(payload)
|
|
893
|
+
logger.debug("Successfully parsed payload as JSON")
|
|
894
|
+
request_body = parsed_payload
|
|
895
|
+
except json.JSONDecodeError as e:
|
|
896
|
+
logger.debug(f"JSON parsing failed: {e}, trying with quotes replaced")
|
|
897
|
+
|
|
898
|
+
# Try replacing single quotes with double quotes
|
|
899
|
+
fixed_payload = payload.replace("'", "\"")
|
|
900
|
+
try:
|
|
901
|
+
parsed_payload = json.loads(fixed_payload)
|
|
902
|
+
logger.debug("Successfully parsed fixed JSON")
|
|
903
|
+
request_body = parsed_payload
|
|
904
|
+
except json.JSONDecodeError:
|
|
905
|
+
# Try as Python literal
|
|
906
|
+
import ast
|
|
907
|
+
try:
|
|
908
|
+
parsed_payload = ast.literal_eval(payload)
|
|
909
|
+
logger.debug("Successfully parsed payload as Python literal")
|
|
910
|
+
request_body = parsed_payload
|
|
911
|
+
except (SyntaxError, ValueError) as e2:
|
|
912
|
+
logger.debug(f"Failed to parse payload string: {e2}")
|
|
913
|
+
return {"error": f"Invalid payload format: {e2}", "payload": payload}
|
|
914
|
+
except Exception as e:
|
|
915
|
+
logger.debug(f"Error parsing payload string: {e}")
|
|
916
|
+
return {"error": f"Failed to parse payload: {e}", "payload": payload}
|
|
917
|
+
else:
|
|
918
|
+
# If payload is already a dictionary, use it directly
|
|
919
|
+
logger.debug("Using provided payload dictionary")
|
|
920
|
+
request_body = payload
|
|
921
|
+
|
|
922
|
+
# Import the NewManualServiceConfig class
|
|
923
|
+
try:
|
|
924
|
+
from instana_client.models.new_manual_service_config import (
|
|
925
|
+
NewManualServiceConfig,
|
|
926
|
+
)
|
|
927
|
+
logger.debug("Successfully imported NewManualServiceConfig")
|
|
928
|
+
except ImportError as e:
|
|
929
|
+
logger.debug(f"Error importing NewManualServiceConfig: {e}")
|
|
930
|
+
return {"error": f"Failed to import NewManualServiceConfig: {e!s}"}
|
|
931
|
+
|
|
932
|
+
# Create an NewManualServiceConfig object from the request body
|
|
933
|
+
try:
|
|
934
|
+
logger.debug(f"Creating NewManualServiceConfig with params: {request_body}")
|
|
935
|
+
config_object = NewManualServiceConfig(**request_body)
|
|
936
|
+
logger.debug("Successfully created manual service config object")
|
|
937
|
+
except Exception as e:
|
|
938
|
+
logger.debug(f"Error creating NewManualServiceConfig: {e}")
|
|
939
|
+
return {"error": f"Failed to create config object: {e!s}"}
|
|
940
|
+
|
|
941
|
+
# Call the add_manual_service_config method from the SDK
|
|
942
|
+
logger.debug("Calling add_manual_service_config with config object")
|
|
612
943
|
result = api_client.add_manual_service_config(
|
|
613
|
-
new_manual_service_config=
|
|
944
|
+
new_manual_service_config=config_object
|
|
614
945
|
)
|
|
615
946
|
|
|
616
|
-
|
|
947
|
+
# Convert the result to a dictionary
|
|
948
|
+
if hasattr(result, 'to_dict'):
|
|
617
949
|
result_dict = result.to_dict()
|
|
618
950
|
else:
|
|
619
|
-
|
|
951
|
+
# If it's already a dict or another format, use it as is
|
|
952
|
+
result_dict = result or {
|
|
953
|
+
"success": True,
|
|
954
|
+
"message": "Create new manual service config"
|
|
955
|
+
}
|
|
620
956
|
|
|
621
|
-
|
|
957
|
+
logger.debug(f"Result from add_manual_service_config: {result_dict}")
|
|
622
958
|
return result_dict
|
|
623
|
-
|
|
624
959
|
except Exception as e:
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
return {"error": f"Failed to create manual service configuration: {e!s}"}
|
|
960
|
+
logger.error(f"Error in add_manual_service_config: {e}")
|
|
961
|
+
return {"error": f"Failed to create new manual service config: {e!s}"}
|
|
628
962
|
|
|
629
963
|
@register_as_tool
|
|
630
964
|
@with_header_auth(ApplicationSettingsApi)
|
|
@@ -669,11 +1003,7 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
669
1003
|
async def update_manual_service_config(
|
|
670
1004
|
self,
|
|
671
1005
|
id: str,
|
|
672
|
-
|
|
673
|
-
unmonitoredServiceName: Optional[str] = None,
|
|
674
|
-
existingServiceId: Optional[str] = None,
|
|
675
|
-
description: Optional[str] = None,
|
|
676
|
-
enabled: Optional[bool] = True,
|
|
1006
|
+
payload: Union[Dict[str, Any], str],
|
|
677
1007
|
ctx=None,
|
|
678
1008
|
api_client=None
|
|
679
1009
|
) -> Dict[str, Any]:
|
|
@@ -686,72 +1016,120 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
686
1016
|
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
1017
|
|
|
688
1018
|
Args:
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
1019
|
+
id: A unique id of the manual service configuration.
|
|
1020
|
+
Sample payload: {
|
|
1021
|
+
"description": "Map source service example",
|
|
1022
|
+
"enabled": true,
|
|
1023
|
+
"existingServiceId": "c467ca0fa21477fee3cde75a140b2963307388a7",
|
|
1024
|
+
"id": "BDGeDcG4TRSzRkJ1mGOk-Q",
|
|
1025
|
+
"tagFilterExpression": {
|
|
1026
|
+
"type": "TAG_FILTER",
|
|
1027
|
+
"name": "service.name",
|
|
1028
|
+
"stringValue": "front",
|
|
1029
|
+
"numberValue": null,
|
|
1030
|
+
"booleanValue": null,
|
|
1031
|
+
"key": null,
|
|
1032
|
+
"value": "front",
|
|
1033
|
+
"operator": "EQUALS",
|
|
1034
|
+
"entity": "SOURCE"
|
|
1035
|
+
},
|
|
1036
|
+
"unmonitoredServiceName": null
|
|
1037
|
+
}
|
|
696
1038
|
|
|
697
1039
|
Returns:
|
|
698
1040
|
Dict[str, Any]: API response indicating success or failure.
|
|
699
1041
|
"""
|
|
700
1042
|
try:
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
1043
|
+
if not payload or not id:
|
|
1044
|
+
return {"error": "missing arguments"}
|
|
1045
|
+
|
|
1046
|
+
# Parse the payload if it's a string
|
|
1047
|
+
if isinstance(payload, str):
|
|
1048
|
+
logger.debug("Payload is a string, attempting to parse")
|
|
1049
|
+
try:
|
|
1050
|
+
import json
|
|
1051
|
+
try:
|
|
1052
|
+
parsed_payload = json.loads(payload)
|
|
1053
|
+
logger.debug("Successfully parsed payload as JSON")
|
|
1054
|
+
request_body = parsed_payload
|
|
1055
|
+
except json.JSONDecodeError as e:
|
|
1056
|
+
logger.debug(f"JSON parsing failed: {e}, trying with quotes replaced")
|
|
1057
|
+
|
|
1058
|
+
# Try replacing single quotes with double quotes
|
|
1059
|
+
fixed_payload = payload.replace("'", "\"")
|
|
1060
|
+
try:
|
|
1061
|
+
parsed_payload = json.loads(fixed_payload)
|
|
1062
|
+
logger.debug("Successfully parsed fixed JSON")
|
|
1063
|
+
request_body = parsed_payload
|
|
1064
|
+
except json.JSONDecodeError:
|
|
1065
|
+
# Try as Python literal
|
|
1066
|
+
import ast
|
|
1067
|
+
try:
|
|
1068
|
+
parsed_payload = ast.literal_eval(payload)
|
|
1069
|
+
logger.debug("Successfully parsed payload as Python literal")
|
|
1070
|
+
request_body = parsed_payload
|
|
1071
|
+
except (SyntaxError, ValueError) as e2:
|
|
1072
|
+
logger.debug(f"Failed to parse payload string: {e2}")
|
|
1073
|
+
return {"error": f"Invalid payload format: {e2}", "payload": payload}
|
|
1074
|
+
except Exception as e:
|
|
1075
|
+
logger.debug(f"Error parsing payload string: {e}")
|
|
1076
|
+
return {"error": f"Failed to parse payload: {e}", "payload": payload}
|
|
1077
|
+
else:
|
|
1078
|
+
# If payload is already a dictionary, use it directly
|
|
1079
|
+
logger.debug("Using provided payload dictionary")
|
|
1080
|
+
request_body = payload
|
|
1081
|
+
|
|
1082
|
+
# Import the ManualServiceConfig class
|
|
1083
|
+
try:
|
|
1084
|
+
from instana_client.models.manual_service_config import (
|
|
1085
|
+
ManualServiceConfig,
|
|
1086
|
+
)
|
|
1087
|
+
logger.debug("Successfully imported ManualServiceConfig")
|
|
1088
|
+
except ImportError as e:
|
|
1089
|
+
logger.debug(f"Error importing ManualServiceConfig: {e}")
|
|
1090
|
+
return {"error": f"Failed to import ManualServiceConfig: {e!s}"}
|
|
1091
|
+
|
|
1092
|
+
# Create an ManualServiceConfig object from the request body
|
|
1093
|
+
try:
|
|
1094
|
+
logger.debug(f"Creating ManualServiceConfig with params: {request_body}")
|
|
1095
|
+
config_object = ManualServiceConfig(**request_body)
|
|
1096
|
+
logger.debug("Successfully update manual service config object")
|
|
1097
|
+
except Exception as e:
|
|
1098
|
+
logger.debug(f"Error creating ManualServiceConfig: {e}")
|
|
1099
|
+
return {"error": f"Failed to update manual config object: {e!s}"}
|
|
1100
|
+
|
|
1101
|
+
# Call the update_manual_service_config method from the SDK
|
|
1102
|
+
logger.debug("Calling update_manual_service_config with config object")
|
|
724
1103
|
result = api_client.update_manual_service_config(
|
|
725
1104
|
id=id,
|
|
726
|
-
manual_service_config=
|
|
1105
|
+
manual_service_config=config_object
|
|
727
1106
|
)
|
|
728
1107
|
|
|
729
|
-
|
|
1108
|
+
# Convert the result to a dictionary
|
|
1109
|
+
if hasattr(result, 'to_dict'):
|
|
730
1110
|
result_dict = result.to_dict()
|
|
731
1111
|
else:
|
|
732
|
-
|
|
1112
|
+
# If it's already a dict or another format, use it as is
|
|
1113
|
+
result_dict = result or {
|
|
1114
|
+
"success": True,
|
|
1115
|
+
"message": "update manual service config"
|
|
1116
|
+
}
|
|
733
1117
|
|
|
734
|
-
|
|
1118
|
+
logger.debug(f"Result from update_manual_service_config: {result_dict}")
|
|
735
1119
|
return result_dict
|
|
736
|
-
|
|
737
1120
|
except Exception as e:
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
return {"error": f"Failed to create manual service configuration: {e!s}"}
|
|
1121
|
+
logger.error(f"Error in update_manual_service_config: {e}")
|
|
1122
|
+
return {"error": f"Failed to update manual config: {e!s}"}
|
|
741
1123
|
|
|
742
1124
|
|
|
743
1125
|
@register_as_tool
|
|
744
1126
|
@with_header_auth(ApplicationSettingsApi)
|
|
745
1127
|
async def replace_all_manual_service_config(
|
|
746
1128
|
self,
|
|
747
|
-
|
|
748
|
-
unmonitoredServiceName: Optional[str] = None,
|
|
749
|
-
existingServiceId: Optional[str] = None,
|
|
750
|
-
description: Optional[str] = None,
|
|
751
|
-
enabled: Optional[bool] = True,
|
|
1129
|
+
payload: Union[Dict[str, Any], str],
|
|
752
1130
|
ctx=None,
|
|
753
1131
|
api_client=None
|
|
754
|
-
) -> Dict[str, Any]:
|
|
1132
|
+
) -> List[Dict[str, Any]]:
|
|
755
1133
|
"""
|
|
756
1134
|
This tool is used if one wants to update more than 1 manual service configurations.
|
|
757
1135
|
|
|
@@ -761,57 +1139,110 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
761
1139
|
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
1140
|
|
|
763
1141
|
Args:
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
1142
|
+
Sample payload: [
|
|
1143
|
+
{
|
|
1144
|
+
"description": "Map source service",
|
|
1145
|
+
"enabled": true,
|
|
1146
|
+
"existingServiceId": "c467ca0fa21477fee3cde75a140b2963307388a7",
|
|
1147
|
+
"tagFilterExpression": {
|
|
1148
|
+
"type": "TAG_FILTER",
|
|
1149
|
+
"name": "service.name",
|
|
1150
|
+
"stringValue": "front",
|
|
1151
|
+
"numberValue": null,
|
|
1152
|
+
"booleanValue": null,
|
|
1153
|
+
"key": null,
|
|
1154
|
+
"value": "front",
|
|
1155
|
+
"operator": "EQUALS",
|
|
1156
|
+
"entity": "SOURCE"
|
|
1157
|
+
},
|
|
1158
|
+
"unmonitoredServiceName": null
|
|
1159
|
+
}
|
|
1160
|
+
]
|
|
770
1161
|
ctx: Optional execution context.
|
|
771
1162
|
|
|
772
1163
|
Returns:
|
|
773
1164
|
Dict[str, Any]: API response indicating success or failure.
|
|
774
1165
|
"""
|
|
775
1166
|
try:
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
1167
|
+
if not payload:
|
|
1168
|
+
return [{"error": "missing arguments"}]
|
|
1169
|
+
|
|
1170
|
+
# Parse the payload if it's a string
|
|
1171
|
+
if isinstance(payload, str):
|
|
1172
|
+
logger.debug("Payload is a string, attempting to parse")
|
|
1173
|
+
try:
|
|
1174
|
+
import json
|
|
1175
|
+
try:
|
|
1176
|
+
parsed_payload = json.loads(payload)
|
|
1177
|
+
logger.debug("Successfully parsed payload as JSON")
|
|
1178
|
+
request_body = parsed_payload
|
|
1179
|
+
except json.JSONDecodeError as e:
|
|
1180
|
+
logger.debug(f"JSON parsing failed: {e}, trying with quotes replaced")
|
|
1181
|
+
|
|
1182
|
+
# Try replacing single quotes with double quotes
|
|
1183
|
+
fixed_payload = payload.replace("'", "\"")
|
|
1184
|
+
try:
|
|
1185
|
+
parsed_payload = json.loads(fixed_payload)
|
|
1186
|
+
logger.debug("Successfully parsed fixed JSON")
|
|
1187
|
+
request_body = parsed_payload
|
|
1188
|
+
except json.JSONDecodeError:
|
|
1189
|
+
# Try as Python literal
|
|
1190
|
+
import ast
|
|
1191
|
+
try:
|
|
1192
|
+
parsed_payload = ast.literal_eval(payload)
|
|
1193
|
+
logger.debug("Successfully parsed payload as Python literal")
|
|
1194
|
+
request_body = parsed_payload
|
|
1195
|
+
except (SyntaxError, ValueError) as e2:
|
|
1196
|
+
logger.debug(f"Failed to parse payload string: {e2}")
|
|
1197
|
+
return [{"error": f"Invalid payload format: {e2}", "payload": payload}]
|
|
1198
|
+
except Exception as e:
|
|
1199
|
+
logger.debug(f"Error parsing payload string: {e}")
|
|
1200
|
+
return [{"error": f"Failed to parse payload: {e}", "payload": payload}]
|
|
1201
|
+
else:
|
|
1202
|
+
# If payload is already a dictionary, use it directly
|
|
1203
|
+
logger.debug("Using provided payload dictionary")
|
|
1204
|
+
request_body = payload
|
|
1205
|
+
|
|
1206
|
+
# Import the NewManualServiceConfig class
|
|
1207
|
+
try:
|
|
1208
|
+
from instana_client.models.new_manual_service_config import (
|
|
1209
|
+
NewManualServiceConfig,
|
|
1210
|
+
)
|
|
1211
|
+
logger.debug("Successfully imported ManualServiceConfig")
|
|
1212
|
+
except ImportError as e:
|
|
1213
|
+
logger.debug(f"Error importing ManualServiceConfig: {e}")
|
|
1214
|
+
return [{"error": f"Failed to import ManualServiceConfig: {e!s}"}]
|
|
1215
|
+
|
|
1216
|
+
# Create an ManualServiceConfig object from the request body
|
|
1217
|
+
try:
|
|
1218
|
+
logger.debug(f"Creating ManualServiceConfig with params: {request_body}")
|
|
1219
|
+
config_object = [NewManualServiceConfig(**request_body)]
|
|
1220
|
+
logger.debug("Successfully replace all manual service config object")
|
|
1221
|
+
except Exception as e:
|
|
1222
|
+
logger.debug(f"Error creating ManualServiceConfig: {e}")
|
|
1223
|
+
return [{"error": f"Failed to replace all manual config object: {e!s}"}]
|
|
1224
|
+
|
|
1225
|
+
# Call the replace_all_manual_service_config method from the SDK
|
|
1226
|
+
logger.debug("Calling replace_all_manual_service_config with config object")
|
|
1227
|
+
result = api_client.replace_all_manual_service_config(
|
|
1228
|
+
new_manual_service_config=config_object
|
|
800
1229
|
)
|
|
801
1230
|
|
|
802
|
-
|
|
1231
|
+
# Convert the result to a dictionary
|
|
1232
|
+
if hasattr(result, 'to_dict'):
|
|
803
1233
|
result_dict = result.to_dict()
|
|
804
1234
|
else:
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
1235
|
+
# If it's already a dict or another format, use it as is
|
|
1236
|
+
result_dict = result or {
|
|
1237
|
+
"success": True,
|
|
1238
|
+
"message": "Create replace all manual service config"
|
|
1239
|
+
}
|
|
809
1240
|
|
|
1241
|
+
logger.debug(f"Result from replace_all_manual_service_config: {result_dict}")
|
|
1242
|
+
return [result_dict]
|
|
810
1243
|
except Exception as e:
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
return {"error": f"Failed to create manual service configuration: {e!s}"}
|
|
814
|
-
|
|
1244
|
+
logger.error(f"Error in replace_all_manual_service_config: {e}")
|
|
1245
|
+
return [{"error": f"Failed to replace all manual config: {e!s}"}]
|
|
815
1246
|
|
|
816
1247
|
@register_as_tool
|
|
817
1248
|
@with_header_auth(ApplicationSettingsApi)
|
|
@@ -847,45 +1278,98 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
847
1278
|
|
|
848
1279
|
@register_as_tool
|
|
849
1280
|
@with_header_auth(ApplicationSettingsApi)
|
|
850
|
-
async def
|
|
851
|
-
|
|
852
|
-
match_specification: List[Dict[str, str]],
|
|
853
|
-
name: str,
|
|
854
|
-
label:str,
|
|
855
|
-
id: str,
|
|
856
|
-
comment: Optional[str] = None,
|
|
1281
|
+
async def add_service_config(self,
|
|
1282
|
+
payload: Union[Dict[str, Any], str],
|
|
857
1283
|
ctx=None,
|
|
858
|
-
api_client=None) ->
|
|
1284
|
+
api_client=None) -> Dict[str, Any]:
|
|
859
1285
|
"""
|
|
860
1286
|
This tool gives is used to add new Service Perspectives Configuration
|
|
861
1287
|
Get a list of all Service Perspectives with their configuration settings.
|
|
862
1288
|
Args:
|
|
863
|
-
|
|
1289
|
+
{
|
|
1290
|
+
"comment": null,
|
|
1291
|
+
"enabled": true,
|
|
1292
|
+
"label": "{gce.zone}-{jvm.args.abc}",
|
|
1293
|
+
"matchSpecification": [
|
|
1294
|
+
{
|
|
1295
|
+
"key": "gce.zone",
|
|
1296
|
+
"value": ".*"
|
|
1297
|
+
},
|
|
1298
|
+
{
|
|
1299
|
+
"key": "jvm.args.abc",
|
|
1300
|
+
"value": ".*"
|
|
1301
|
+
}
|
|
1302
|
+
],
|
|
1303
|
+
"name": "ABC is good"
|
|
1304
|
+
}
|
|
864
1305
|
ctx: The MCP context (optional)
|
|
865
1306
|
|
|
866
1307
|
Returns:
|
|
867
1308
|
Dictionary containing endpoints data or error information
|
|
868
1309
|
"""
|
|
869
1310
|
try:
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
"
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
1311
|
+
if not payload:
|
|
1312
|
+
return {"error": "missing arguments"}
|
|
1313
|
+
|
|
1314
|
+
# Parse the payload if it's a string
|
|
1315
|
+
if isinstance(payload, str):
|
|
1316
|
+
logger.debug("Payload is a string, attempting to parse")
|
|
1317
|
+
try:
|
|
1318
|
+
import json
|
|
1319
|
+
try:
|
|
1320
|
+
parsed_payload = json.loads(payload)
|
|
1321
|
+
logger.debug("Successfully parsed payload as JSON")
|
|
1322
|
+
request_body = parsed_payload
|
|
1323
|
+
except json.JSONDecodeError as e:
|
|
1324
|
+
logger.debug(f"JSON parsing failed: {e}, trying with quotes replaced")
|
|
1325
|
+
|
|
1326
|
+
# Try replacing single quotes with double quotes
|
|
1327
|
+
fixed_payload = payload.replace("'", "\"")
|
|
1328
|
+
try:
|
|
1329
|
+
parsed_payload = json.loads(fixed_payload)
|
|
1330
|
+
logger.debug("Successfully parsed fixed JSON")
|
|
1331
|
+
request_body = parsed_payload
|
|
1332
|
+
except json.JSONDecodeError:
|
|
1333
|
+
# Try as Python literal
|
|
1334
|
+
import ast
|
|
1335
|
+
try:
|
|
1336
|
+
parsed_payload = ast.literal_eval(payload)
|
|
1337
|
+
logger.debug("Successfully parsed payload as Python literal")
|
|
1338
|
+
request_body = parsed_payload
|
|
1339
|
+
except (SyntaxError, ValueError) as e2:
|
|
1340
|
+
logger.debug(f"Failed to parse payload string: {e2}")
|
|
1341
|
+
return {"error": f"Invalid payload format: {e2}", "payload": payload}
|
|
1342
|
+
except Exception as e:
|
|
1343
|
+
logger.debug(f"Error parsing payload string: {e}")
|
|
1344
|
+
return {"error": f"Failed to parse payload: {e}", "payload": payload}
|
|
1345
|
+
else:
|
|
1346
|
+
# If payload is already a dictionary, use it directly
|
|
1347
|
+
logger.debug("Using provided payload dictionary")
|
|
1348
|
+
request_body = payload
|
|
1349
|
+
|
|
1350
|
+
# Import the ServiceConfig class
|
|
1351
|
+
try:
|
|
1352
|
+
from instana_client.models.service_config import (
|
|
1353
|
+
ServiceConfig,
|
|
1354
|
+
)
|
|
1355
|
+
logger.debug("Successfully imported ServiceConfig")
|
|
1356
|
+
except ImportError as e:
|
|
1357
|
+
logger.debug(f"Error importing ServiceConfig: {e}")
|
|
1358
|
+
return {"error": f"Failed to import ServiceConfig: {e!s}"}
|
|
1359
|
+
|
|
1360
|
+
# Create an ServiceConfig object from the request body
|
|
1361
|
+
try:
|
|
1362
|
+
logger.debug(f"Creating ServiceConfig with params: {request_body}")
|
|
1363
|
+
config_object = ServiceConfig(**request_body)
|
|
1364
|
+
logger.debug("Successfully add service config object")
|
|
1365
|
+
except Exception as e:
|
|
1366
|
+
logger.debug(f"Error creating ServiceConfig: {e}")
|
|
1367
|
+
return {"error": f"Failed to add service config object: {e!s}"}
|
|
1368
|
+
|
|
1369
|
+
# Call the ServiceConfig method from the SDK
|
|
1370
|
+
logger.debug("Calling add_service_config with config object")
|
|
887
1371
|
result = api_client.add_service_config(
|
|
888
|
-
service_config=
|
|
1372
|
+
service_config=config_object
|
|
889
1373
|
)
|
|
890
1374
|
|
|
891
1375
|
# Convert the result to a dictionary
|
|
@@ -893,72 +1377,127 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
893
1377
|
result_dict = result.to_dict()
|
|
894
1378
|
else:
|
|
895
1379
|
# If it's already a dict or another format, use it as is
|
|
896
|
-
result_dict = result
|
|
1380
|
+
result_dict = result or {
|
|
1381
|
+
"success": True,
|
|
1382
|
+
"message": "Create service config"
|
|
1383
|
+
}
|
|
897
1384
|
|
|
898
|
-
|
|
1385
|
+
logger.debug(f"Result from add_service_config: {result_dict}")
|
|
899
1386
|
return result_dict
|
|
900
|
-
|
|
901
1387
|
except Exception as e:
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
return [{"error": f"Failed to get application data metrics: {e}"}]
|
|
1388
|
+
logger.error(f"Error in add_service_config: {e}")
|
|
1389
|
+
return {"error": f"Failed to add service config: {e!s}"}
|
|
905
1390
|
|
|
906
1391
|
@register_as_tool
|
|
907
1392
|
@with_header_auth(ApplicationSettingsApi)
|
|
908
1393
|
async def replace_all_service_configs(self,
|
|
909
|
-
|
|
910
|
-
match_specification: List[Dict[str, str]],
|
|
911
|
-
name: str,
|
|
912
|
-
label:str,
|
|
913
|
-
id: str,
|
|
914
|
-
comment: Optional[str] = None,
|
|
1394
|
+
payload: Union[Dict[str, Any], str],
|
|
915
1395
|
ctx=None,
|
|
916
1396
|
api_client=None) -> List[Dict[str, Any]]:
|
|
917
1397
|
"""
|
|
918
|
-
|
|
919
1398
|
Args:
|
|
920
|
-
|
|
1399
|
+
[
|
|
1400
|
+
{
|
|
1401
|
+
"comment": null,
|
|
1402
|
+
"enabled": true,
|
|
1403
|
+
"id": "8C-jGYx8Rsue854tzkh8KQ",
|
|
1404
|
+
"label": "{docker.container.name}",
|
|
1405
|
+
"matchSpecification": [
|
|
1406
|
+
{
|
|
1407
|
+
"key": "docker.container.name",
|
|
1408
|
+
"value": ".*"
|
|
1409
|
+
}
|
|
1410
|
+
],
|
|
1411
|
+
"name": "Rule"
|
|
1412
|
+
}
|
|
1413
|
+
]
|
|
921
1414
|
ctx: The MCP context (optional)
|
|
922
1415
|
|
|
923
1416
|
Returns:
|
|
924
1417
|
Dictionary containing endpoints data or error information
|
|
925
1418
|
"""
|
|
926
1419
|
try:
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
"
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1420
|
+
if not payload:
|
|
1421
|
+
return [{"error": "missing arguments"}]
|
|
1422
|
+
|
|
1423
|
+
# Parse the payload if it's a string
|
|
1424
|
+
if isinstance(payload, str):
|
|
1425
|
+
logger.debug("Payload is a string, attempting to parse")
|
|
1426
|
+
try:
|
|
1427
|
+
import json
|
|
1428
|
+
try:
|
|
1429
|
+
parsed_payload = json.loads(payload)
|
|
1430
|
+
logger.debug("Successfully parsed payload as JSON")
|
|
1431
|
+
request_body = parsed_payload
|
|
1432
|
+
except json.JSONDecodeError as e:
|
|
1433
|
+
logger.debug(f"JSON parsing failed: {e}, trying with quotes replaced")
|
|
1434
|
+
|
|
1435
|
+
# Try replacing single quotes with double quotes
|
|
1436
|
+
fixed_payload = payload.replace("'", "\"")
|
|
1437
|
+
try:
|
|
1438
|
+
parsed_payload = json.loads(fixed_payload)
|
|
1439
|
+
logger.debug("Successfully parsed fixed JSON")
|
|
1440
|
+
request_body = parsed_payload
|
|
1441
|
+
except json.JSONDecodeError:
|
|
1442
|
+
# Try as Python literal
|
|
1443
|
+
import ast
|
|
1444
|
+
try:
|
|
1445
|
+
parsed_payload = ast.literal_eval(payload)
|
|
1446
|
+
logger.debug("Successfully parsed payload as Python literal")
|
|
1447
|
+
request_body = parsed_payload
|
|
1448
|
+
except (SyntaxError, ValueError) as e2:
|
|
1449
|
+
logger.debug(f"Failed to parse payload string: {e2}")
|
|
1450
|
+
return [{"error": f"Invalid payload format: {e2}", "payload": payload}]
|
|
1451
|
+
except Exception as e:
|
|
1452
|
+
logger.debug(f"Error parsing payload string: {e}")
|
|
1453
|
+
return [{"error": f"Failed to parse payload: {e}", "payload": payload}]
|
|
1454
|
+
else:
|
|
1455
|
+
# If payload is already a dictionary, use it directly
|
|
1456
|
+
logger.debug("Using provided payload dictionary")
|
|
1457
|
+
request_body = payload
|
|
1458
|
+
|
|
1459
|
+
# Import the ServiceConfig class
|
|
1460
|
+
try:
|
|
1461
|
+
from instana_client.models.service_config import (
|
|
1462
|
+
ServiceConfig,
|
|
1463
|
+
)
|
|
1464
|
+
logger.debug("Successfully imported ServiceConfig")
|
|
1465
|
+
except ImportError as e:
|
|
1466
|
+
logger.debug(f"Error importing ServiceConfig: {e}")
|
|
1467
|
+
return [{"error": f"Failed to import ServiceConfig: {e!s}"}]
|
|
1468
|
+
|
|
1469
|
+
# Create an ServiceConfig object from the request body
|
|
1470
|
+
try:
|
|
1471
|
+
logger.debug(f"Creating ServiceConfig with params: {request_body}")
|
|
1472
|
+
config_object = [ServiceConfig(**request_body)]
|
|
1473
|
+
logger.debug("Successfully replace all manual service config object")
|
|
1474
|
+
except Exception as e:
|
|
1475
|
+
logger.debug(f"Error creating ServiceConfig: {e}")
|
|
1476
|
+
return [{"error": f"Failed to replace all manual config object: {e!s}"}]
|
|
1477
|
+
|
|
1478
|
+
# Call the replace_all method from the SDK
|
|
1479
|
+
logger.debug("Calling replace_all with config object")
|
|
944
1480
|
result = api_client.replace_all(
|
|
945
|
-
service_config=
|
|
1481
|
+
service_config=config_object
|
|
946
1482
|
)
|
|
947
1483
|
|
|
948
|
-
# Convert the result to a
|
|
1484
|
+
# Convert the result to a list of dictionaries
|
|
949
1485
|
if hasattr(result, 'to_dict'):
|
|
950
1486
|
result_dict = result.to_dict()
|
|
951
1487
|
else:
|
|
952
1488
|
# If it's already a dict or another format, use it as is
|
|
953
|
-
result_dict = result
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
1489
|
+
result_dict = result or {
|
|
1490
|
+
"success": True,
|
|
1491
|
+
"message": "replace all service config"
|
|
1492
|
+
}
|
|
957
1493
|
|
|
1494
|
+
logger.debug(f"Result from replace_all: {result_dict}")
|
|
1495
|
+
return [result_dict]
|
|
958
1496
|
except Exception as e:
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
1497
|
+
logger.error(f"Error in replace_all: {e}")
|
|
1498
|
+
return [{"error": f"Failed to replace all service config: {e!s}"}]
|
|
1499
|
+
|
|
1500
|
+
|
|
962
1501
|
@register_as_tool
|
|
963
1502
|
@with_header_auth(ApplicationSettingsApi)
|
|
964
1503
|
async def order_service_config(self,
|
|
@@ -1078,45 +1617,100 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
1078
1617
|
|
|
1079
1618
|
@register_as_tool
|
|
1080
1619
|
@with_header_auth(ApplicationSettingsApi)
|
|
1081
|
-
async def
|
|
1082
|
-
enabled: bool,
|
|
1083
|
-
match_specification: List[Dict[str, str]],
|
|
1084
|
-
name: str,
|
|
1085
|
-
label:str,
|
|
1620
|
+
async def update_service_config(self,
|
|
1086
1621
|
id: str,
|
|
1087
|
-
|
|
1622
|
+
payload: Union[Dict[str, Any], str],
|
|
1088
1623
|
ctx=None,
|
|
1089
|
-
api_client=None) ->
|
|
1624
|
+
api_client=None) -> Dict[str, Any]:
|
|
1090
1625
|
"""
|
|
1091
1626
|
This tool gives is used if one wants to update a particular custom service rule.
|
|
1092
1627
|
Args:
|
|
1093
|
-
|
|
1628
|
+
{
|
|
1629
|
+
"comment": null,
|
|
1630
|
+
"enabled": true,
|
|
1631
|
+
"id": "9uma4MhnTTSyBzwu_FKBJA",
|
|
1632
|
+
"label": "{gce.zone}-{jvm.args.abc}",
|
|
1633
|
+
"matchSpecification": [
|
|
1634
|
+
{
|
|
1635
|
+
"key": "gce.zone",
|
|
1636
|
+
"value": ".*"
|
|
1637
|
+
},
|
|
1638
|
+
{
|
|
1639
|
+
"key": "jvm.args.abc",
|
|
1640
|
+
"value": ".*"
|
|
1641
|
+
}
|
|
1642
|
+
],
|
|
1643
|
+
"name": "DEF is good"
|
|
1644
|
+
}
|
|
1094
1645
|
ctx: The MCP context (optional)
|
|
1095
1646
|
|
|
1096
1647
|
Returns:
|
|
1097
1648
|
Dictionary containing endpoints data or error information
|
|
1098
1649
|
"""
|
|
1099
1650
|
try:
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
"
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1651
|
+
if not payload or not id:
|
|
1652
|
+
return [{"error": "missing arguments"}]
|
|
1653
|
+
|
|
1654
|
+
# Parse the payload if it's a string
|
|
1655
|
+
if isinstance(payload, str):
|
|
1656
|
+
logger.debug("Payload is a string, attempting to parse")
|
|
1657
|
+
try:
|
|
1658
|
+
import json
|
|
1659
|
+
try:
|
|
1660
|
+
parsed_payload = json.loads(payload)
|
|
1661
|
+
logger.debug("Successfully parsed payload as JSON")
|
|
1662
|
+
request_body = parsed_payload
|
|
1663
|
+
except json.JSONDecodeError as e:
|
|
1664
|
+
logger.debug(f"JSON parsing failed: {e}, trying with quotes replaced")
|
|
1665
|
+
|
|
1666
|
+
# Try replacing single quotes with double quotes
|
|
1667
|
+
fixed_payload = payload.replace("'", "\"")
|
|
1668
|
+
try:
|
|
1669
|
+
parsed_payload = json.loads(fixed_payload)
|
|
1670
|
+
logger.debug("Successfully parsed fixed JSON")
|
|
1671
|
+
request_body = parsed_payload
|
|
1672
|
+
except json.JSONDecodeError:
|
|
1673
|
+
# Try as Python literal
|
|
1674
|
+
import ast
|
|
1675
|
+
try:
|
|
1676
|
+
parsed_payload = ast.literal_eval(payload)
|
|
1677
|
+
logger.debug("Successfully parsed payload as Python literal")
|
|
1678
|
+
request_body = parsed_payload
|
|
1679
|
+
except (SyntaxError, ValueError) as e2:
|
|
1680
|
+
logger.debug(f"Failed to parse payload string: {e2}")
|
|
1681
|
+
return [{"error": f"Invalid payload format: {e2}", "payload": payload}]
|
|
1682
|
+
except Exception as e:
|
|
1683
|
+
logger.debug(f"Error parsing payload string: {e}")
|
|
1684
|
+
return [{"error": f"Failed to parse payload: {e}", "payload": payload}]
|
|
1685
|
+
else:
|
|
1686
|
+
# If payload is already a dictionary, use it directly
|
|
1687
|
+
logger.debug("Using provided payload dictionary")
|
|
1688
|
+
request_body = payload
|
|
1689
|
+
|
|
1690
|
+
# Import the ServiceConfig class
|
|
1691
|
+
try:
|
|
1692
|
+
from instana_client.models.service_config import (
|
|
1693
|
+
ServiceConfig,
|
|
1694
|
+
)
|
|
1695
|
+
logger.debug("Successfully imported ServiceConfig")
|
|
1696
|
+
except ImportError as e:
|
|
1697
|
+
logger.debug(f"Error importing ServiceConfig: {e}")
|
|
1698
|
+
return [{"error": f"Failed to import ServiceConfig: {e!s}"}]
|
|
1699
|
+
|
|
1700
|
+
# Create an ServiceConfig object from the request body
|
|
1701
|
+
try:
|
|
1702
|
+
logger.debug(f"Creating ServiceConfig with params: {request_body}")
|
|
1703
|
+
config_object = [ServiceConfig(**request_body)]
|
|
1704
|
+
logger.debug("Successfully update service config object")
|
|
1705
|
+
except Exception as e:
|
|
1706
|
+
logger.debug(f"Error creating ServiceConfig: {e}")
|
|
1707
|
+
return [{"error": f"Failed to replace all manual config object: {e!s}"}]
|
|
1708
|
+
|
|
1709
|
+
# Call the put_service_config method from the SDK
|
|
1710
|
+
logger.debug("Calling put_service_config with config object")
|
|
1117
1711
|
result = api_client.put_service_config(
|
|
1118
1712
|
id=id,
|
|
1119
|
-
service_config=
|
|
1713
|
+
service_config=config_object
|
|
1120
1714
|
)
|
|
1121
1715
|
|
|
1122
1716
|
# Convert the result to a dictionary
|
|
@@ -1124,12 +1718,14 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
1124
1718
|
result_dict = result.to_dict()
|
|
1125
1719
|
else:
|
|
1126
1720
|
# If it's already a dict or another format, use it as is
|
|
1127
|
-
result_dict = result
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1721
|
+
result_dict = result or {
|
|
1722
|
+
"success": True,
|
|
1723
|
+
"message": "put service config"
|
|
1724
|
+
}
|
|
1131
1725
|
|
|
1726
|
+
logger.debug(f"Result from put_service_config: {result_dict}")
|
|
1727
|
+
return [result_dict]
|
|
1132
1728
|
except Exception as e:
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1729
|
+
logger.error(f"Error in put_service_config: {e}")
|
|
1730
|
+
return [{"error": f"Failed to update service config: {e!s}"}]
|
|
1731
|
+
|