mcp-instana 0.3.0__py3-none-any.whl → 0.6.2__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.3.0.dist-info → mcp_instana-0.6.2.dist-info}/METADATA +18 -174
- {mcp_instana-0.3.0.dist-info → mcp_instana-0.6.2.dist-info}/RECORD +18 -17
- {mcp_instana-0.3.0.dist-info → mcp_instana-0.6.2.dist-info}/WHEEL +1 -1
- src/application/application_alert_config.py +10 -4
- src/application/application_analyze.py +13 -10
- src/application/application_global_alert_config.py +22 -21
- src/application/application_metrics.py +21 -21
- src/application/application_resources.py +44 -4
- src/application/application_settings.py +190 -66
- src/core/server.py +3 -0
- src/event/events_tools.py +57 -9
- src/infrastructure/infrastructure_catalog.py +30 -4
- src/infrastructure/infrastructure_metrics.py +1 -0
- src/infrastructure/infrastructure_resources.py +1 -4
- src/infrastructure/infrastructure_topology.py +27 -15
- src/observability.py +29 -0
- {mcp_instana-0.3.0.dist-info → mcp_instana-0.6.2.dist-info}/entry_points.txt +0 -0
- {mcp_instana-0.3.0.dist-info → mcp_instana-0.6.2.dist-info}/licenses/LICENSE.md +0 -0
|
@@ -42,6 +42,9 @@ class ApplicationResourcesMCPTools(BaseInstanaClient):
|
|
|
42
42
|
)
|
|
43
43
|
@with_header_auth(ApplicationResourcesApi)
|
|
44
44
|
async def get_application_endpoints(self,
|
|
45
|
+
app_id: Optional[str] = None,
|
|
46
|
+
service_id: Optional[str] = None,
|
|
47
|
+
endpoint_id: Optional[str] = None,
|
|
45
48
|
name_filter: Optional[str] = None,
|
|
46
49
|
types: Optional[List[str]] = None,
|
|
47
50
|
technologies: Optional[List[str]] = None,
|
|
@@ -58,6 +61,9 @@ class ApplicationResourcesMCPTools(BaseInstanaClient):
|
|
|
58
61
|
For example, use this tool when asked about 'application endpoints', 'service endpoints', 'API endpoints in my application','endpoint id of an Endpoint', or when someone wants to 'list all endpoints'.
|
|
59
62
|
|
|
60
63
|
Args:
|
|
64
|
+
app_id: Application ID to filter endpoints by application (optional)
|
|
65
|
+
service_id: Service ID to filter endpoints by service (optional)
|
|
66
|
+
endpoint_id: Endpoint ID to get a specific endpoint (optional)
|
|
61
67
|
name_filter: Name of service to filter by (optional)
|
|
62
68
|
types: List of endpoint types to filter by (optional)
|
|
63
69
|
technologies: List of technologies to filter by (optional)
|
|
@@ -83,6 +89,9 @@ class ApplicationResourcesMCPTools(BaseInstanaClient):
|
|
|
83
89
|
|
|
84
90
|
# Call the get_application_endpoints method from the SDK
|
|
85
91
|
result = api_client.get_application_endpoints(
|
|
92
|
+
app_id=app_id,
|
|
93
|
+
service_id=service_id,
|
|
94
|
+
endpoint_id=endpoint_id,
|
|
86
95
|
name_filter=name_filter,
|
|
87
96
|
types=types,
|
|
88
97
|
technologies=technologies,
|
|
@@ -101,7 +110,21 @@ class ApplicationResourcesMCPTools(BaseInstanaClient):
|
|
|
101
110
|
result_dict = result
|
|
102
111
|
|
|
103
112
|
logger.debug(f"Result from get_application_endpoints: {result_dict}")
|
|
104
|
-
|
|
113
|
+
|
|
114
|
+
# Check if we got a single Endpoint or EndpointResult (list)
|
|
115
|
+
# Single Endpoint is returned when app_id, service_id, AND endpoint_id are all provided
|
|
116
|
+
if app_id and service_id and endpoint_id:
|
|
117
|
+
# Single Endpoint object returned
|
|
118
|
+
return {
|
|
119
|
+
"type": "single_endpoint",
|
|
120
|
+
"endpoint": result_dict
|
|
121
|
+
}
|
|
122
|
+
else:
|
|
123
|
+
# EndpointResult (list) returned
|
|
124
|
+
return {
|
|
125
|
+
"type": "endpoint_list",
|
|
126
|
+
"data": result_dict
|
|
127
|
+
}
|
|
105
128
|
except Exception as e:
|
|
106
129
|
logger.error(f"Error in get_application_endpoints: {e}", exc_info=True)
|
|
107
130
|
return {"error": f"Failed to get application endpoints: {e!s}"}
|
|
@@ -112,6 +135,8 @@ class ApplicationResourcesMCPTools(BaseInstanaClient):
|
|
|
112
135
|
)
|
|
113
136
|
@with_header_auth(ApplicationResourcesApi)
|
|
114
137
|
async def get_application_services(self,
|
|
138
|
+
app_id: Optional[str] = None,
|
|
139
|
+
service_id: Optional[str] = None,
|
|
115
140
|
name_filter: Optional[str] = None,
|
|
116
141
|
window_size: Optional[int] = None,
|
|
117
142
|
to_time: Optional[int] = None,
|
|
@@ -127,6 +152,8 @@ class ApplicationResourcesMCPTools(BaseInstanaClient):
|
|
|
127
152
|
For example, use this tool when asked about 'application services', 'microservices in my application', 'list all services', or when someone wants to 'get service information'. A use case could be to retrieve all service ids present in an Application Perspective.
|
|
128
153
|
|
|
129
154
|
Args:
|
|
155
|
+
app_id: Application ID to filter services by application (optional)
|
|
156
|
+
service_id: Service ID to filter specific service (optional)
|
|
130
157
|
name_filter: Name of application/service to filter by (optional)
|
|
131
158
|
window_size: Size of time window in milliseconds (optional)
|
|
132
159
|
to_time: End timestamp in milliseconds (optional)
|
|
@@ -151,6 +178,8 @@ class ApplicationResourcesMCPTools(BaseInstanaClient):
|
|
|
151
178
|
|
|
152
179
|
# Call the get_application_services method from the SDK
|
|
153
180
|
result = api_client.get_application_services(
|
|
181
|
+
app_id=app_id,
|
|
182
|
+
service_id=service_id,
|
|
154
183
|
name_filter=name_filter,
|
|
155
184
|
window_size=window_size,
|
|
156
185
|
to=to_time,
|
|
@@ -169,6 +198,16 @@ class ApplicationResourcesMCPTools(BaseInstanaClient):
|
|
|
169
198
|
|
|
170
199
|
logger.debug(f"Result from get_application_services: {result_dict}")
|
|
171
200
|
|
|
201
|
+
# Check if we got a single Service or ServiceResult (list)
|
|
202
|
+
# Single Service is returned when both app_id AND service_id are provided
|
|
203
|
+
if app_id and service_id:
|
|
204
|
+
# Single Service object returned
|
|
205
|
+
return {
|
|
206
|
+
"type": "single_service",
|
|
207
|
+
"service": result_dict
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
# ServiceResult (list) returned - extract and format the data
|
|
172
211
|
# Extract service labels and IDs from the items
|
|
173
212
|
services = []
|
|
174
213
|
service_labels = []
|
|
@@ -176,14 +215,14 @@ class ApplicationResourcesMCPTools(BaseInstanaClient):
|
|
|
176
215
|
|
|
177
216
|
for item in items:
|
|
178
217
|
if isinstance(item, dict):
|
|
179
|
-
|
|
218
|
+
svc_id = item.get('id', '')
|
|
180
219
|
label = item.get('label', '')
|
|
181
220
|
technologies = item.get('technologies', [])
|
|
182
221
|
|
|
183
|
-
if label and
|
|
222
|
+
if label and svc_id:
|
|
184
223
|
service_labels.append(label)
|
|
185
224
|
services.append({
|
|
186
|
-
'id':
|
|
225
|
+
'id': svc_id,
|
|
187
226
|
'label': label,
|
|
188
227
|
'technologies': technologies
|
|
189
228
|
})
|
|
@@ -201,6 +240,7 @@ class ApplicationResourcesMCPTools(BaseInstanaClient):
|
|
|
201
240
|
service_labels = [service['label'] for service in limited_services]
|
|
202
241
|
|
|
203
242
|
return {
|
|
243
|
+
"type": "service_list",
|
|
204
244
|
"message": f"Found {len(services)} services in application perspectives. Showing first {len(limited_services)}:",
|
|
205
245
|
"service_labels": service_labels,
|
|
206
246
|
"services": limited_services,
|
|
@@ -34,6 +34,8 @@ try:
|
|
|
34
34
|
NewApplicationConfig, #type: ignore
|
|
35
35
|
NewManualServiceConfig, #type: ignore
|
|
36
36
|
ServiceConfig, #type: ignore
|
|
37
|
+
TagFilter, #type: ignore
|
|
38
|
+
TagFilterExpression, #type: ignore
|
|
37
39
|
)
|
|
38
40
|
except ImportError as e:
|
|
39
41
|
print(f"Error importing Instana SDK: {e}", file=sys.stderr)
|
|
@@ -232,6 +234,40 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
232
234
|
logger.debug(f"Error importing NewApplicationConfig: {e}")
|
|
233
235
|
return {"error": f"Failed to import NewApplicationConfig: {e!s}"}
|
|
234
236
|
|
|
237
|
+
# Convert nested tagFilterExpression to model objects
|
|
238
|
+
if 'tagFilterExpression' in request_body and isinstance(request_body['tagFilterExpression'], dict):
|
|
239
|
+
try:
|
|
240
|
+
tag_expr = request_body['tagFilterExpression']
|
|
241
|
+
logger.debug(f"Converting tagFilterExpression: {tag_expr}")
|
|
242
|
+
|
|
243
|
+
# If it's an EXPRESSION type with elements, convert each element
|
|
244
|
+
if tag_expr.get('type') == 'EXPRESSION' and 'elements' in tag_expr:
|
|
245
|
+
converted_elements = []
|
|
246
|
+
for element in tag_expr['elements']:
|
|
247
|
+
if isinstance(element, dict):
|
|
248
|
+
# Remove conflicting fields before creating TagFilter
|
|
249
|
+
element_copy = element.copy()
|
|
250
|
+
element_copy.pop('value', None)
|
|
251
|
+
element_copy.pop('key', None)
|
|
252
|
+
converted_elements.append(TagFilter(**element_copy))
|
|
253
|
+
else:
|
|
254
|
+
converted_elements.append(element)
|
|
255
|
+
tag_expr['elements'] = converted_elements
|
|
256
|
+
request_body['tagFilterExpression'] = TagFilterExpression(**tag_expr)
|
|
257
|
+
# If it's a TAG_FILTER type, convert directly
|
|
258
|
+
elif tag_expr.get('type') == 'TAG_FILTER':
|
|
259
|
+
tag_expr_copy = tag_expr.copy()
|
|
260
|
+
tag_expr_copy.pop('value', None)
|
|
261
|
+
tag_expr_copy.pop('key', None)
|
|
262
|
+
request_body['tagFilterExpression'] = TagFilter(**tag_expr_copy)
|
|
263
|
+
else:
|
|
264
|
+
request_body['tagFilterExpression'] = TagFilterExpression(**tag_expr)
|
|
265
|
+
|
|
266
|
+
logger.debug("Successfully converted tagFilterExpression to model objects")
|
|
267
|
+
except Exception as e:
|
|
268
|
+
logger.debug(f"Error converting tagFilterExpression: {e}")
|
|
269
|
+
return {"error": f"Failed to convert tagFilterExpression: {e!s}"}
|
|
270
|
+
|
|
235
271
|
# Create an NewApplicationConfig object from the request body
|
|
236
272
|
try:
|
|
237
273
|
logger.debug(f"Creating NewApplicationConfig with params: {request_body}")
|
|
@@ -466,6 +502,40 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
466
502
|
logger.debug(f"Error importing ApplicationConfig: {e}")
|
|
467
503
|
return {"error": f"Failed to import ApplicationConfig: {e!s}"}
|
|
468
504
|
|
|
505
|
+
# Convert nested tagFilterExpression to model objects
|
|
506
|
+
if 'tagFilterExpression' in request_body and isinstance(request_body['tagFilterExpression'], dict):
|
|
507
|
+
try:
|
|
508
|
+
tag_expr = request_body['tagFilterExpression']
|
|
509
|
+
logger.debug(f"Converting tagFilterExpression: {tag_expr}")
|
|
510
|
+
|
|
511
|
+
# If it's an EXPRESSION type with elements, convert each element
|
|
512
|
+
if tag_expr.get('type') == 'EXPRESSION' and 'elements' in tag_expr:
|
|
513
|
+
converted_elements = []
|
|
514
|
+
for element in tag_expr['elements']:
|
|
515
|
+
if isinstance(element, dict):
|
|
516
|
+
# Remove conflicting fields before creating TagFilter
|
|
517
|
+
element_copy = element.copy()
|
|
518
|
+
element_copy.pop('value', None)
|
|
519
|
+
element_copy.pop('key', None)
|
|
520
|
+
converted_elements.append(TagFilter(**element_copy))
|
|
521
|
+
else:
|
|
522
|
+
converted_elements.append(element)
|
|
523
|
+
tag_expr['elements'] = converted_elements
|
|
524
|
+
request_body['tagFilterExpression'] = TagFilterExpression(**tag_expr)
|
|
525
|
+
# If it's a TAG_FILTER type, convert directly
|
|
526
|
+
elif tag_expr.get('type') == 'TAG_FILTER':
|
|
527
|
+
tag_expr_copy = tag_expr.copy()
|
|
528
|
+
tag_expr_copy.pop('value', None)
|
|
529
|
+
tag_expr_copy.pop('key', None)
|
|
530
|
+
request_body['tagFilterExpression'] = TagFilter(**tag_expr_copy)
|
|
531
|
+
else:
|
|
532
|
+
request_body['tagFilterExpression'] = TagFilterExpression(**tag_expr)
|
|
533
|
+
|
|
534
|
+
logger.debug("Successfully converted tagFilterExpression to model objects")
|
|
535
|
+
except Exception as e:
|
|
536
|
+
logger.debug(f"Error converting tagFilterExpression: {e}")
|
|
537
|
+
return {"error": f"Failed to convert tagFilterExpression: {e!s}"}
|
|
538
|
+
|
|
469
539
|
# Create an ApplicationConfig object from the request body
|
|
470
540
|
try:
|
|
471
541
|
logger.debug(f"Creating ApplicationConfig with params: {request_body}")
|
|
@@ -517,16 +587,23 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
517
587
|
"""
|
|
518
588
|
try:
|
|
519
589
|
debug_print("Fetching all endpoint configs")
|
|
520
|
-
result = api_client.
|
|
590
|
+
result = api_client.get_endpoint_configs_without_preload_content()
|
|
521
591
|
# Convert the result to a dictionary
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
592
|
+
import json
|
|
593
|
+
try:
|
|
594
|
+
response_text=result.data.decode('utf-8')
|
|
595
|
+
json_data=json.loads(response_text)
|
|
596
|
+
if isinstance(json_data, list):
|
|
597
|
+
result_dict=json_data
|
|
598
|
+
else:
|
|
599
|
+
# If it's a single object, wrap it in a list
|
|
600
|
+
result_dict=[json_data] if json_data else []
|
|
601
|
+
debug_print("Successfully retrieved endpoint configs data")
|
|
602
|
+
return result_dict
|
|
603
|
+
except (json.JSONDecodeError, AttributeError) as json_err:
|
|
604
|
+
error_message = f"Failed to parse JSON response: {json_err}"
|
|
605
|
+
logger.error(error_message)
|
|
606
|
+
return [{"error": error_message}]
|
|
530
607
|
|
|
531
608
|
except Exception as e:
|
|
532
609
|
debug_print(f"Error in get_endpoint_configs: {e}")
|
|
@@ -870,16 +947,24 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
870
947
|
"""
|
|
871
948
|
try:
|
|
872
949
|
debug_print("Fetching all manual configs")
|
|
873
|
-
result = api_client.
|
|
950
|
+
result = api_client.get_all_manual_service_configs_without_preload_content()
|
|
874
951
|
# Convert the result to a dictionary
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
952
|
+
import json
|
|
953
|
+
try:
|
|
954
|
+
response_text=result.data.decode('utf-8')
|
|
955
|
+
json_data=json.loads(response_text)
|
|
956
|
+
logger.debug("Successfully retrieved manual service configs data")
|
|
957
|
+
if isinstance(json_data, list):
|
|
958
|
+
result_dict=json_data
|
|
959
|
+
else:
|
|
960
|
+
# If it's a single object, wrap it in a list
|
|
961
|
+
result_dict=[json_data] if json_data else []
|
|
962
|
+
debug_print("Successfully retrieved manual service configs data")
|
|
963
|
+
return result_dict
|
|
964
|
+
except (json.JSONDecodeError, AttributeError) as json_err:
|
|
965
|
+
error_message = f"Failed to parse JSON response: {json_err}"
|
|
966
|
+
logger.error(error_message)
|
|
967
|
+
return [{"error": error_message}]
|
|
883
968
|
|
|
884
969
|
except Exception as e:
|
|
885
970
|
debug_print(f"Error in get_all_manual_service_configs: {e}")
|
|
@@ -975,20 +1060,38 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
975
1060
|
logger.debug(f"Error importing NewManualServiceConfig: {e}")
|
|
976
1061
|
return {"error": f"Failed to import NewManualServiceConfig: {e!s}"}
|
|
977
1062
|
|
|
978
|
-
#
|
|
1063
|
+
# Convert tagFilterExpression dict to TagFilter object with all required fields
|
|
1064
|
+
if 'tagFilterExpression' in request_body and isinstance(request_body['tagFilterExpression'], dict):
|
|
1065
|
+
try:
|
|
1066
|
+
tag_filter_dict = request_body['tagFilterExpression']
|
|
1067
|
+
logger.debug(f"Converting tagFilterExpression to TagFilter object: {tag_filter_dict}")
|
|
1068
|
+
|
|
1069
|
+
# Use TagFilter.from_dict() which properly handles all fields including aliases
|
|
1070
|
+
tag_filter = TagFilter.from_dict(tag_filter_dict)
|
|
1071
|
+
request_body['tagFilterExpression'] = tag_filter
|
|
1072
|
+
logger.debug("Successfully converted tagFilterExpression to TagFilter object")
|
|
1073
|
+
except Exception as e:
|
|
1074
|
+
logger.debug(f"Error converting tagFilterExpression: {e}")
|
|
1075
|
+
return {"error": f"Failed to convert tagFilterExpression: {e!s}"}
|
|
1076
|
+
|
|
1077
|
+
# Create NewManualServiceConfig object
|
|
1078
|
+
logger.debug("Creating NewManualServiceConfig object")
|
|
979
1079
|
try:
|
|
980
|
-
logger.debug(f"Creating NewManualServiceConfig with params: {request_body}")
|
|
981
1080
|
config_object = NewManualServiceConfig(**request_body)
|
|
982
|
-
logger.debug("Successfully created
|
|
1081
|
+
logger.debug("Successfully created NewManualServiceConfig object")
|
|
983
1082
|
except Exception as e:
|
|
984
1083
|
logger.debug(f"Error creating NewManualServiceConfig: {e}")
|
|
985
1084
|
return {"error": f"Failed to create config object: {e!s}"}
|
|
986
1085
|
|
|
987
|
-
# Call the add_manual_service_config method from the SDK
|
|
1086
|
+
# Call the add_manual_service_config method from the SDK with the config object
|
|
988
1087
|
logger.debug("Calling add_manual_service_config with config object")
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1088
|
+
try:
|
|
1089
|
+
result = api_client.add_manual_service_config(
|
|
1090
|
+
new_manual_service_config=config_object
|
|
1091
|
+
)
|
|
1092
|
+
except Exception as e:
|
|
1093
|
+
logger.debug(f"Error calling add_manual_service_config: {e}")
|
|
1094
|
+
return {"error": f"Failed to create config object: {e!s}"}
|
|
992
1095
|
|
|
993
1096
|
# Convert the result to a dictionary
|
|
994
1097
|
if hasattr(result, 'to_dict'):
|
|
@@ -1141,22 +1244,21 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
1141
1244
|
logger.debug(f"Error importing ManualServiceConfig: {e}")
|
|
1142
1245
|
return {"error": f"Failed to import ManualServiceConfig: {e!s}"}
|
|
1143
1246
|
|
|
1144
|
-
#
|
|
1247
|
+
# Keep tagFilterExpression as dictionary - SDK will handle serialization
|
|
1248
|
+
if 'tagFilterExpression' in request_body and isinstance(request_body['tagFilterExpression'], dict):
|
|
1249
|
+
logger.debug(f"tagFilterExpression will be passed as dict: {request_body['tagFilterExpression']}")
|
|
1250
|
+
|
|
1251
|
+
# Call the update_manual_service_config method from the SDK directly with dict
|
|
1252
|
+
logger.debug("Calling update_manual_service_config with request body")
|
|
1145
1253
|
try:
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1254
|
+
result = api_client.update_manual_service_config(
|
|
1255
|
+
id=id,
|
|
1256
|
+
manual_service_config=request_body
|
|
1257
|
+
)
|
|
1149
1258
|
except Exception as e:
|
|
1150
|
-
logger.debug(f"Error
|
|
1259
|
+
logger.debug(f"Error calling update_manual_service_config: {e}")
|
|
1151
1260
|
return {"error": f"Failed to update manual config object: {e!s}"}
|
|
1152
1261
|
|
|
1153
|
-
# Call the update_manual_service_config method from the SDK
|
|
1154
|
-
logger.debug("Calling update_manual_service_config with config object")
|
|
1155
|
-
result = api_client.update_manual_service_config(
|
|
1156
|
-
id=id,
|
|
1157
|
-
manual_service_config=config_object
|
|
1158
|
-
)
|
|
1159
|
-
|
|
1160
1262
|
# Convert the result to a dictionary
|
|
1161
1263
|
if hasattr(result, 'to_dict'):
|
|
1162
1264
|
result_dict = result.to_dict()
|
|
@@ -1421,7 +1523,9 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
1421
1523
|
# Create an ServiceConfig object from the request body
|
|
1422
1524
|
try:
|
|
1423
1525
|
logger.debug(f"Creating ServiceConfig with params: {request_body}")
|
|
1424
|
-
|
|
1526
|
+
request_body_with_id = dict(request_body)
|
|
1527
|
+
request_body_with_id.setdefault("id", "temp-id")
|
|
1528
|
+
config_object = ServiceConfig(**request_body_with_id)
|
|
1425
1529
|
logger.debug("Successfully add service config object")
|
|
1426
1530
|
except Exception as e:
|
|
1427
1531
|
logger.debug(f"Error creating ServiceConfig: {e}")
|
|
@@ -1432,7 +1536,6 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
1432
1536
|
result = api_client.add_service_config(
|
|
1433
1537
|
service_config=config_object
|
|
1434
1538
|
)
|
|
1435
|
-
|
|
1436
1539
|
# Convert the result to a dictionary
|
|
1437
1540
|
if hasattr(result, 'to_dict'):
|
|
1438
1541
|
result_dict = result.to_dict()
|
|
@@ -1495,7 +1598,6 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
1495
1598
|
request_body = parsed_payload
|
|
1496
1599
|
except json.JSONDecodeError as e:
|
|
1497
1600
|
logger.debug(f"JSON parsing failed: {e}, trying with quotes replaced")
|
|
1498
|
-
|
|
1499
1601
|
# Try replacing single quotes with double quotes
|
|
1500
1602
|
fixed_payload = payload.replace("'", "\"")
|
|
1501
1603
|
try:
|
|
@@ -1533,7 +1635,10 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
1533
1635
|
# Create an ServiceConfig object from the request body
|
|
1534
1636
|
try:
|
|
1535
1637
|
logger.debug(f"Creating ServiceConfig with params: {request_body}")
|
|
1536
|
-
|
|
1638
|
+
configs = request_body.get("serviceConfigs")
|
|
1639
|
+
if not isinstance(configs, list):
|
|
1640
|
+
return [{"error": "serviceConfigs must be a list"}]
|
|
1641
|
+
config_object = [ServiceConfig(**item) for item in configs]
|
|
1537
1642
|
logger.debug("Successfully replace all manual service config object")
|
|
1538
1643
|
except Exception as e:
|
|
1539
1644
|
logger.debug(f"Error creating ServiceConfig: {e}")
|
|
@@ -1541,22 +1646,22 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
1541
1646
|
|
|
1542
1647
|
# Call the replace_all method from the SDK
|
|
1543
1648
|
logger.debug("Calling replace_all with config object")
|
|
1544
|
-
result = api_client.
|
|
1649
|
+
result = api_client.replace_all_without_preload_content(
|
|
1545
1650
|
service_config=config_object
|
|
1546
1651
|
)
|
|
1547
1652
|
|
|
1548
1653
|
# Convert the result to a list of dictionaries
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
}
|
|
1654
|
+
import json
|
|
1655
|
+
try:
|
|
1656
|
+
response_text = result.data.decode('utf-8')
|
|
1657
|
+
result_dict = json.loads(response_text)
|
|
1658
|
+
logger.debug("Successfully replaced all service configs.")
|
|
1659
|
+
return result_dict
|
|
1660
|
+
except (json.JSONDecodeError, AttributeError) as json_err:
|
|
1661
|
+
error_message = f"Failed to parse JSON response: {json_err}"
|
|
1662
|
+
logger.error(error_message)
|
|
1663
|
+
return [{"error": error_message}]
|
|
1557
1664
|
|
|
1558
|
-
logger.debug(f"Result from replace_all: {result_dict}")
|
|
1559
|
-
return [result_dict]
|
|
1560
1665
|
except Exception as e:
|
|
1561
1666
|
logger.error(f"Error in replace_all: {e}")
|
|
1562
1667
|
return [{"error": f"Failed to replace all service config: {e!s}"}]
|
|
@@ -1590,14 +1695,33 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
1590
1695
|
if not request_body:
|
|
1591
1696
|
return {"error": "The list of service configuration IDs cannot be empty."}
|
|
1592
1697
|
|
|
1593
|
-
|
|
1594
|
-
request_body=request_body
|
|
1698
|
+
response = api_client.order_service_config_with_http_info(
|
|
1699
|
+
request_body=request_body,
|
|
1700
|
+
_content_type='application/json',
|
|
1595
1701
|
)
|
|
1596
1702
|
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1703
|
+
status = getattr(response, 'status', None)
|
|
1704
|
+
data = getattr(response, 'data', None)
|
|
1705
|
+
|
|
1706
|
+
if hasattr(data, 'to_dict'):
|
|
1707
|
+
payload = data.to_dict()
|
|
1708
|
+
elif data is None:
|
|
1709
|
+
payload = None
|
|
1710
|
+
else:
|
|
1711
|
+
payload = data
|
|
1712
|
+
|
|
1713
|
+
if status == 204 or payload is None:
|
|
1714
|
+
return {
|
|
1715
|
+
"success": True,
|
|
1716
|
+
"status": status,
|
|
1717
|
+
"message": "Service configs reordered successfully"
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
return {
|
|
1721
|
+
"success": True,
|
|
1722
|
+
"status": status,
|
|
1723
|
+
"data": payload
|
|
1724
|
+
}
|
|
1601
1725
|
|
|
1602
1726
|
except Exception as e:
|
|
1603
1727
|
debug_print(f"Error in order_service_config: {e}")
|
|
@@ -1725,7 +1849,7 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
1725
1849
|
"""
|
|
1726
1850
|
try:
|
|
1727
1851
|
if not payload or not id:
|
|
1728
|
-
return
|
|
1852
|
+
return {"error": "missing arguments"}
|
|
1729
1853
|
|
|
1730
1854
|
# Parse the payload if it's a string
|
|
1731
1855
|
if isinstance(payload, str):
|
|
@@ -1754,10 +1878,10 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
1754
1878
|
request_body = parsed_payload
|
|
1755
1879
|
except (SyntaxError, ValueError) as e2:
|
|
1756
1880
|
logger.debug(f"Failed to parse payload string: {e2}")
|
|
1757
|
-
return
|
|
1881
|
+
return {"error": f"Invalid payload format: {e2}", "payload": payload}
|
|
1758
1882
|
except Exception as e:
|
|
1759
1883
|
logger.debug(f"Error parsing payload string: {e}")
|
|
1760
|
-
return
|
|
1884
|
+
return {"error": f"Failed to parse payload: {e}", "payload": payload}
|
|
1761
1885
|
else:
|
|
1762
1886
|
# If payload is already a dictionary, use it directly
|
|
1763
1887
|
logger.debug("Using provided payload dictionary")
|
|
@@ -1771,16 +1895,16 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
1771
1895
|
logger.debug("Successfully imported ServiceConfig")
|
|
1772
1896
|
except ImportError as e:
|
|
1773
1897
|
logger.debug(f"Error importing ServiceConfig: {e}")
|
|
1774
|
-
return
|
|
1898
|
+
return {"error": f"Failed to import ServiceConfig: {e!s}"}
|
|
1775
1899
|
|
|
1776
1900
|
# Create an ServiceConfig object from the request body
|
|
1777
1901
|
try:
|
|
1778
1902
|
logger.debug(f"Creating ServiceConfig with params: {request_body}")
|
|
1779
|
-
config_object =
|
|
1903
|
+
config_object = ServiceConfig(**request_body)
|
|
1780
1904
|
logger.debug("Successfully update service config object")
|
|
1781
1905
|
except Exception as e:
|
|
1782
1906
|
logger.debug(f"Error creating ServiceConfig: {e}")
|
|
1783
|
-
return
|
|
1907
|
+
return {"error": f"Failed to update service config object: {e!s}"}
|
|
1784
1908
|
|
|
1785
1909
|
# Call the put_service_config method from the SDK
|
|
1786
1910
|
logger.debug("Calling put_service_config with config object")
|
|
@@ -1800,8 +1924,8 @@ class ApplicationSettingsMCPTools(BaseInstanaClient):
|
|
|
1800
1924
|
}
|
|
1801
1925
|
|
|
1802
1926
|
logger.debug(f"Result from put_service_config: {result_dict}")
|
|
1803
|
-
return
|
|
1927
|
+
return result_dict
|
|
1804
1928
|
except Exception as e:
|
|
1805
1929
|
logger.error(f"Error in put_service_config: {e}")
|
|
1806
|
-
return
|
|
1930
|
+
return {"error": f"Failed to update service config: {e!s}"}
|
|
1807
1931
|
|
src/core/server.py
CHANGED
|
@@ -20,6 +20,8 @@ from src.prompts import PROMPT_REGISTRY
|
|
|
20
20
|
|
|
21
21
|
load_dotenv()
|
|
22
22
|
|
|
23
|
+
from src.observability import task, workflow
|
|
24
|
+
|
|
23
25
|
# Configure logging
|
|
24
26
|
logging.basicConfig(
|
|
25
27
|
level=logging.INFO, # Default level, can be overridden
|
|
@@ -404,6 +406,7 @@ def get_enabled_client_configs(enabled_categories: str):
|
|
|
404
406
|
logger.warning(f"Unknown category '{category}'")
|
|
405
407
|
return enabled_configs
|
|
406
408
|
|
|
409
|
+
@workflow(name="instana_mcp_workflow")
|
|
407
410
|
def main():
|
|
408
411
|
"""Main entry point for the MCP server."""
|
|
409
412
|
try:
|