mcp-instana 0.1.0__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.2.0.dist-info/METADATA +1229 -0
- mcp_instana-0.2.0.dist-info/RECORD +59 -0
- {mcp_instana-0.1.0.dist-info → mcp_instana-0.2.0.dist-info}/WHEEL +1 -1
- mcp_instana-0.2.0.dist-info/entry_points.txt +4 -0
- mcp_instana-0.1.0.dist-info/LICENSE → mcp_instana-0.2.0.dist-info/licenses/LICENSE.md +3 -3
- src/application/__init__.py +1 -0
- src/{client/application_alert_config_mcp_tools.py → application/application_alert_config.py} +251 -273
- src/application/application_analyze.py +628 -0
- src/application/application_catalog.py +155 -0
- src/application/application_global_alert_config.py +653 -0
- src/{client/application_metrics_mcp_tools.py → application/application_metrics.py} +113 -131
- src/{client/application_resources_mcp_tools.py → application/application_resources.py} +131 -151
- src/application/application_settings.py +1731 -0
- src/application/application_topology.py +111 -0
- src/automation/action_catalog.py +416 -0
- src/automation/action_history.py +338 -0
- src/core/__init__.py +1 -0
- src/core/server.py +586 -0
- src/core/utils.py +213 -0
- src/event/__init__.py +1 -0
- src/event/events_tools.py +850 -0
- src/infrastructure/__init__.py +1 -0
- src/{client/infrastructure_analyze_mcp_tools.py → infrastructure/infrastructure_analyze.py} +207 -206
- src/{client/infrastructure_catalog_mcp_tools.py → infrastructure/infrastructure_catalog.py} +197 -265
- src/infrastructure/infrastructure_metrics.py +171 -0
- src/{client/infrastructure_resources_mcp_tools.py → infrastructure/infrastructure_resources.py} +198 -227
- src/{client/infrastructure_topology_mcp_tools.py → infrastructure/infrastructure_topology.py} +110 -109
- src/log/__init__.py +1 -0
- src/log/log_alert_configuration.py +331 -0
- 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.0.dist-info/METADATA +0 -649
- mcp_instana-0.1.0.dist-info/RECORD +0 -19
- mcp_instana-0.1.0.dist-info/entry_points.txt +0 -3
- src/client/What is the sum of queue depth for all q +0 -55
- src/client/events_mcp_tools.py +0 -531
- src/client/instana_client_base.py +0 -93
- src/client/log_alert_configuration_mcp_tools.py +0 -316
- src/client/show the top 5 services with the highest +0 -28
- src/mcp_server.py +0 -343
|
@@ -0,0 +1,770 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Website Configuration MCP Tools Module
|
|
3
|
+
|
|
4
|
+
This module provides website configuration-specific MCP tools for Instana monitoring.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Any, Dict, List, Optional, Union
|
|
9
|
+
|
|
10
|
+
# Import the necessary classes from the SDK
|
|
11
|
+
try:
|
|
12
|
+
from instana_client.api.website_configuration_api import WebsiteConfigurationApi
|
|
13
|
+
except ImportError as e:
|
|
14
|
+
import logging
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
logger.error(f"Error importing Instana SDK: {e}", exc_info=True)
|
|
17
|
+
raise
|
|
18
|
+
|
|
19
|
+
from src.core.utils import BaseInstanaClient, register_as_tool, with_header_auth
|
|
20
|
+
|
|
21
|
+
# Configure logger for this module
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
class WebsiteConfigurationMCPTools(BaseInstanaClient):
|
|
25
|
+
"""Tools for website configuration in Instana MCP."""
|
|
26
|
+
|
|
27
|
+
def __init__(self, read_token: str, base_url: str):
|
|
28
|
+
"""Initialize the Website Configuration MCP tools client."""
|
|
29
|
+
super().__init__(read_token=read_token, base_url=base_url)
|
|
30
|
+
|
|
31
|
+
@register_as_tool
|
|
32
|
+
@with_header_auth(WebsiteConfigurationApi)
|
|
33
|
+
async def get_websites(self, ctx=None, api_client=None) -> List[Dict[str, Any]]:
|
|
34
|
+
"""
|
|
35
|
+
Get all websites.
|
|
36
|
+
|
|
37
|
+
This API endpoint retrieves all configured websites in your Instana environment.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
ctx: The MCP context (optional)
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
Dictionary containing websites data or error information
|
|
44
|
+
"""
|
|
45
|
+
try:
|
|
46
|
+
logger.debug("get_websites called")
|
|
47
|
+
|
|
48
|
+
# Call the get_websites method from the SDK
|
|
49
|
+
result = api_client.get_websites()
|
|
50
|
+
|
|
51
|
+
# Convert the result to a dictionary
|
|
52
|
+
if hasattr(result, 'to_dict'):
|
|
53
|
+
result_dict = result.to_dict()
|
|
54
|
+
else:
|
|
55
|
+
# If it's already a dict or another format, use it as is
|
|
56
|
+
result_dict = result
|
|
57
|
+
|
|
58
|
+
logger.debug(f"Result from get_websites: {result_dict}")
|
|
59
|
+
return result_dict
|
|
60
|
+
except Exception as e:
|
|
61
|
+
logger.error(f"Error in get_websites: {e}", exc_info=True)
|
|
62
|
+
return [{"error": f"Failed to get websites: {e!s}"}]
|
|
63
|
+
|
|
64
|
+
@register_as_tool
|
|
65
|
+
@with_header_auth(WebsiteConfigurationApi)
|
|
66
|
+
async def get_website(self, website_id: str, ctx=None, api_client=None) -> Dict[str, Any]:
|
|
67
|
+
"""
|
|
68
|
+
Get a specific website by ID.
|
|
69
|
+
|
|
70
|
+
This API endpoint retrieves configuration details for a specific website.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
website_id: ID of the website to retrieve
|
|
74
|
+
ctx: The MCP context (optional)
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Dictionary containing website data or error information
|
|
78
|
+
"""
|
|
79
|
+
try:
|
|
80
|
+
logger.debug(f"get_website called with website_id={website_id}")
|
|
81
|
+
|
|
82
|
+
# Call the get_website method from the SDK
|
|
83
|
+
result = api_client.get_website(website_id=website_id)
|
|
84
|
+
|
|
85
|
+
# Convert the result to a dictionary
|
|
86
|
+
if hasattr(result, 'to_dict'):
|
|
87
|
+
result_dict = result.to_dict()
|
|
88
|
+
else:
|
|
89
|
+
# If it's already a dict or another format, use it as is
|
|
90
|
+
result_dict = result
|
|
91
|
+
|
|
92
|
+
logger.debug(f"Result from get_website: {result_dict}")
|
|
93
|
+
return result_dict
|
|
94
|
+
except Exception as e:
|
|
95
|
+
logger.error(f"Error in get_website: {e}", exc_info=True)
|
|
96
|
+
return {"error": f"Failed to get website: {e!s}"}
|
|
97
|
+
|
|
98
|
+
@register_as_tool
|
|
99
|
+
@with_header_auth(WebsiteConfigurationApi)
|
|
100
|
+
async def create_website(self,
|
|
101
|
+
name: str,
|
|
102
|
+
payload: Optional[Dict[str, Any]] = None,
|
|
103
|
+
ctx=None,
|
|
104
|
+
api_client=None) -> Dict[str, Any]:
|
|
105
|
+
"""
|
|
106
|
+
Create a new website configuration.
|
|
107
|
+
|
|
108
|
+
This API endpoint creates a new website configuration in your Instana environment.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
name: Name of the website
|
|
112
|
+
payload: Website configuration payload as a dictionary or JSON string
|
|
113
|
+
ctx: The MCP context (optional)
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Dictionary containing created website data or error information
|
|
117
|
+
"""
|
|
118
|
+
try:
|
|
119
|
+
# Parse the payload if it's a string
|
|
120
|
+
if isinstance(payload, str):
|
|
121
|
+
logger.debug("Payload is a string, attempting to parse")
|
|
122
|
+
try:
|
|
123
|
+
import json
|
|
124
|
+
try:
|
|
125
|
+
parsed_payload = json.loads(payload)
|
|
126
|
+
logger.debug("Successfully parsed payload as JSON")
|
|
127
|
+
request_body = parsed_payload
|
|
128
|
+
except json.JSONDecodeError as e:
|
|
129
|
+
logger.debug(f"JSON parsing failed: {e}, trying with quotes replaced")
|
|
130
|
+
|
|
131
|
+
# Try replacing single quotes with double quotes
|
|
132
|
+
fixed_payload = payload.replace("'", "\"")
|
|
133
|
+
try:
|
|
134
|
+
parsed_payload = json.loads(fixed_payload)
|
|
135
|
+
logger.debug("Successfully parsed fixed JSON")
|
|
136
|
+
request_body = parsed_payload
|
|
137
|
+
except json.JSONDecodeError:
|
|
138
|
+
# Try as Python literal
|
|
139
|
+
import ast
|
|
140
|
+
try:
|
|
141
|
+
parsed_payload = ast.literal_eval(payload)
|
|
142
|
+
logger.debug("Successfully parsed payload as Python literal")
|
|
143
|
+
request_body = parsed_payload
|
|
144
|
+
except (SyntaxError, ValueError) as e2:
|
|
145
|
+
logger.debug(f"Failed to parse payload string: {e2}")
|
|
146
|
+
return {"error": f"Invalid payload format: {e2}", "payload": payload}
|
|
147
|
+
except Exception as e:
|
|
148
|
+
logger.debug(f"Error parsing payload string: {e}")
|
|
149
|
+
return {"error": f"Failed to parse payload: {e}", "payload": payload}
|
|
150
|
+
else:
|
|
151
|
+
# If payload is already a dictionary, use it directly
|
|
152
|
+
logger.debug("Using provided payload dictionary")
|
|
153
|
+
request_body = payload
|
|
154
|
+
|
|
155
|
+
# Create an Website object from the request body
|
|
156
|
+
try:
|
|
157
|
+
query_params = {}
|
|
158
|
+
if request_body and "display_name" in request_body:
|
|
159
|
+
query_params["display_name"] = request_body["display_name"]
|
|
160
|
+
if request_body and "id" in request_body:
|
|
161
|
+
query_params["id"] = request_body["id"]
|
|
162
|
+
logger.debug(f"Creating Website with params: {query_params}")
|
|
163
|
+
except Exception as e:
|
|
164
|
+
logger.debug(f"Error creating create_website: {e}")
|
|
165
|
+
return {"error": f"Failed to create website: {e!s}"}
|
|
166
|
+
|
|
167
|
+
# Call the create_website method from the SDK
|
|
168
|
+
logger.debug("Calling create_website with config object")
|
|
169
|
+
result = api_client.create_website(
|
|
170
|
+
name=name
|
|
171
|
+
)
|
|
172
|
+
# Convert the result to a dictionary
|
|
173
|
+
if hasattr(result, 'to_dict'):
|
|
174
|
+
result_dict = result.to_dict()
|
|
175
|
+
else:
|
|
176
|
+
# If it's already a dict or another format, use it as is
|
|
177
|
+
result_dict = result or {
|
|
178
|
+
"success": True,
|
|
179
|
+
"message": "Create website"
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
logger.debug(f"Result from create_website: {result_dict}")
|
|
183
|
+
return result_dict
|
|
184
|
+
except Exception as e:
|
|
185
|
+
logger.error(f"Error in create_website: {e}")
|
|
186
|
+
return {"error": f"Failed to create website: {e!s}"}
|
|
187
|
+
|
|
188
|
+
@register_as_tool
|
|
189
|
+
@with_header_auth(WebsiteConfigurationApi)
|
|
190
|
+
async def delete_website(self, website_id: str, ctx=None, api_client=None) -> Dict[str, Any]:
|
|
191
|
+
"""
|
|
192
|
+
Delete a website configuration.
|
|
193
|
+
|
|
194
|
+
This API endpoint deletes a website configuration from your Instana environment.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
website_id: ID of the website to delete
|
|
198
|
+
ctx: The MCP context (optional)
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
Dictionary containing deletion result or error information
|
|
202
|
+
"""
|
|
203
|
+
try:
|
|
204
|
+
logger.debug(f"delete_website called with website_id={website_id}")
|
|
205
|
+
|
|
206
|
+
# Call the delete_website method from the SDK
|
|
207
|
+
api_client.delete_website(website_id=website_id)
|
|
208
|
+
|
|
209
|
+
logger.debug("Website deleted successfully")
|
|
210
|
+
return {"success": True, "message": f"Website {website_id} deleted successfully"}
|
|
211
|
+
except Exception as e:
|
|
212
|
+
logger.error(f"Error in delete_website: {e}", exc_info=True)
|
|
213
|
+
return {"error": f"Failed to delete website: {e!s}"}
|
|
214
|
+
|
|
215
|
+
@register_as_tool
|
|
216
|
+
@with_header_auth(WebsiteConfigurationApi)
|
|
217
|
+
async def rename_website(self,
|
|
218
|
+
website_id: str,
|
|
219
|
+
name: Optional[str] = None,
|
|
220
|
+
ctx=None,
|
|
221
|
+
api_client=None) -> Dict[str, Any]:
|
|
222
|
+
"""
|
|
223
|
+
Rename a website configuration.
|
|
224
|
+
|
|
225
|
+
This API endpoint renames a website configuration in your Instana environment.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
website_id: ID of the website to rename
|
|
229
|
+
name: New name for the website
|
|
230
|
+
ctx: The MCP context (optional)
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
Dictionary containing rename result or error information
|
|
234
|
+
"""
|
|
235
|
+
|
|
236
|
+
try:
|
|
237
|
+
logger.debug(f"rename_website called with website_id={website_id}")
|
|
238
|
+
|
|
239
|
+
if not website_id:
|
|
240
|
+
return {"error": "website_id parameter is required"}
|
|
241
|
+
|
|
242
|
+
# Call the rename_website method from the SDK
|
|
243
|
+
result = api_client.rename_website(website_id=website_id,name=name)
|
|
244
|
+
|
|
245
|
+
# Convert the result to a dictionary
|
|
246
|
+
if hasattr(result, 'to_dict'):
|
|
247
|
+
result_dict = result.to_dict()
|
|
248
|
+
else:
|
|
249
|
+
# If it's already a dict or another format, use it as is
|
|
250
|
+
result_dict = result
|
|
251
|
+
|
|
252
|
+
logger.debug(f"Result from rename_website: {result_dict}")
|
|
253
|
+
return result_dict
|
|
254
|
+
except Exception as e:
|
|
255
|
+
logger.error(f"Error in rename_website: {e}", exc_info=True)
|
|
256
|
+
return {"error": f"Failed to rename website: {e!s}"}
|
|
257
|
+
|
|
258
|
+
@register_as_tool
|
|
259
|
+
@with_header_auth(WebsiteConfigurationApi)
|
|
260
|
+
async def get_website_geo_location_configuration(self,
|
|
261
|
+
website_id: str,
|
|
262
|
+
ctx=None,
|
|
263
|
+
api_client=None) -> Dict[str, Any]:
|
|
264
|
+
"""
|
|
265
|
+
Get geo-location configuration for a website.
|
|
266
|
+
|
|
267
|
+
This API endpoint retrieves geo-location configuration for a specific website.
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
website_id: ID of the website
|
|
271
|
+
ctx: The MCP context (optional)
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
Dictionary containing geo-location configuration or error information
|
|
275
|
+
"""
|
|
276
|
+
try:
|
|
277
|
+
logger.debug(f"get_website_geo_location_configuration called with website_id={website_id}")
|
|
278
|
+
|
|
279
|
+
# Call the get_website_geo_location_configuration method from the SDK
|
|
280
|
+
result = api_client.get_website_geo_location_configuration(website_id=website_id)
|
|
281
|
+
|
|
282
|
+
# Convert the result to a dictionary
|
|
283
|
+
if hasattr(result, 'to_dict'):
|
|
284
|
+
result_dict = result.to_dict()
|
|
285
|
+
else:
|
|
286
|
+
# If it's already a dict or another format, use it as is
|
|
287
|
+
result_dict = result
|
|
288
|
+
|
|
289
|
+
logger.debug(f"Result from get_website_geo_location_configuration: {result_dict}")
|
|
290
|
+
return result_dict
|
|
291
|
+
except Exception as e:
|
|
292
|
+
logger.error(f"Error in get_website_geo_location_configuration: {e}", exc_info=True)
|
|
293
|
+
return {"error": f"Failed to get website geo-location configuration: {e!s}"}
|
|
294
|
+
|
|
295
|
+
@register_as_tool
|
|
296
|
+
@with_header_auth(WebsiteConfigurationApi)
|
|
297
|
+
async def update_website_geo_location_configuration(self,
|
|
298
|
+
website_id: str,
|
|
299
|
+
payload: Union[Dict[str, Any], str],
|
|
300
|
+
ctx=None, api_client=None) -> Dict[str, Any]:
|
|
301
|
+
"""
|
|
302
|
+
Update geo-location configuration for a website.
|
|
303
|
+
|
|
304
|
+
This API endpoint updates geo-location configuration for a specific website.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
website_id: ID of the website
|
|
308
|
+
payload: Geo-location configuration payload as a dictionary or JSON string
|
|
309
|
+
ctx: The MCP context (optional)
|
|
310
|
+
|
|
311
|
+
Returns:
|
|
312
|
+
Dictionary containing updated configuration or error information
|
|
313
|
+
"""
|
|
314
|
+
try:
|
|
315
|
+
logger.debug("update_website_geo_location_configuration called")
|
|
316
|
+
|
|
317
|
+
# Parse the payload
|
|
318
|
+
if isinstance(payload, str):
|
|
319
|
+
logger.debug("payload is a string, attempting to parse")
|
|
320
|
+
try:
|
|
321
|
+
import json
|
|
322
|
+
try:
|
|
323
|
+
parsed_payload = json.loads(payload)
|
|
324
|
+
logger.debug("Successfully parsed payload as JSON")
|
|
325
|
+
request_body = parsed_payload
|
|
326
|
+
except json.JSONDecodeError as e:
|
|
327
|
+
logger.debug(f"JSON parsing failed: {e}, trying with quotes replaced")
|
|
328
|
+
|
|
329
|
+
# Try replacing single quotes with double quotes
|
|
330
|
+
fixed_payload = payload.replace("'", "\"")
|
|
331
|
+
try:
|
|
332
|
+
parsed_payload = json.loads(fixed_payload)
|
|
333
|
+
logger.debug("Successfully parsed fixed JSON")
|
|
334
|
+
request_body = parsed_payload
|
|
335
|
+
except json.JSONDecodeError:
|
|
336
|
+
# Try as Python literal
|
|
337
|
+
import ast
|
|
338
|
+
try:
|
|
339
|
+
parsed_payload = ast.literal_eval(payload)
|
|
340
|
+
logger.debug("Successfully parsed payload as Python literal")
|
|
341
|
+
request_body = parsed_payload
|
|
342
|
+
except (SyntaxError, ValueError) as e2:
|
|
343
|
+
logger.debug(f"Failed to parse payload string: {e2}")
|
|
344
|
+
return {"error": f"Invalid payload format: {e2}", "payload": payload}
|
|
345
|
+
except Exception as e:
|
|
346
|
+
logger.debug(f"Error parsing payload string: {e}")
|
|
347
|
+
return {"error": f"Failed to parse payload: {e}", "payload": payload}
|
|
348
|
+
else:
|
|
349
|
+
# If payload is already a dictionary, use it directly
|
|
350
|
+
logger.debug("Using provided payload dictionary")
|
|
351
|
+
request_body = payload
|
|
352
|
+
|
|
353
|
+
try:
|
|
354
|
+
from instana_client.models.geo_location_configuration import (
|
|
355
|
+
GeoLocationConfiguration,
|
|
356
|
+
)
|
|
357
|
+
logger.debug("Successfully imported GeoLocationConfiguration")
|
|
358
|
+
except ImportError as e:
|
|
359
|
+
logger.debug(f"Error importing GeoLocationConfiguration: {e}")
|
|
360
|
+
return {"error": f"Failed to import GeoLocationConfiguration: {e!s}"}
|
|
361
|
+
|
|
362
|
+
# Create an GeoLocationConfiguration object from the request body
|
|
363
|
+
try:
|
|
364
|
+
query_params = {}
|
|
365
|
+
|
|
366
|
+
# Required field: geo_detail_removal (with alias geoDetailRemoval)
|
|
367
|
+
# Always provide a default value to ensure the required field is present
|
|
368
|
+
query_params["geo_detail_removal"] = "NO_REMOVAL" # Default value
|
|
369
|
+
|
|
370
|
+
if request_body:
|
|
371
|
+
if "geoDetailRemoval" in request_body:
|
|
372
|
+
query_params["geo_detail_removal"] = request_body["geoDetailRemoval"]
|
|
373
|
+
elif "geo_detail_removal" in request_body:
|
|
374
|
+
query_params["geo_detail_removal"] = request_body["geo_detail_removal"]
|
|
375
|
+
|
|
376
|
+
# Optional field: geo_mapping_rules
|
|
377
|
+
if "geoMappingRules" in request_body:
|
|
378
|
+
query_params["geo_mapping_rules"] = request_body["geoMappingRules"]
|
|
379
|
+
elif "geo_mapping_rules" in request_body:
|
|
380
|
+
query_params["geo_mapping_rules"] = request_body["geo_mapping_rules"]
|
|
381
|
+
|
|
382
|
+
logger.debug(f"Creating GeoLocationConfiguration with params: {query_params}")
|
|
383
|
+
config_object = GeoLocationConfiguration(**query_params)
|
|
384
|
+
logger.debug("Successfully created GeoLocationConfiguration object")
|
|
385
|
+
except Exception as e:
|
|
386
|
+
logger.debug(f"Error creating GeoLocationConfiguration: {e}")
|
|
387
|
+
return {"error": f"Failed to create GeoLocationConfiguration: {e!s}"}
|
|
388
|
+
|
|
389
|
+
# Call the update_website_geo_location_configuration method from the SDK
|
|
390
|
+
logger.debug("Calling update_website_geo_location_configuration with config object")
|
|
391
|
+
result = api_client.update_website_geo_location_configuration(
|
|
392
|
+
website_id=website_id,
|
|
393
|
+
geo_location_configuration=config_object,
|
|
394
|
+
)
|
|
395
|
+
# Convert the result to a dictionary
|
|
396
|
+
if hasattr(result, 'to_dict'):
|
|
397
|
+
result_dict = result.to_dict()
|
|
398
|
+
else:
|
|
399
|
+
# If it's already a dict or another format, use it as is
|
|
400
|
+
result_dict = result or {
|
|
401
|
+
"success": True,
|
|
402
|
+
"message": "Update website geo-location configuration"
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
logger.debug(f"Result from update_website_geo_location_configuration: {result_dict}")
|
|
406
|
+
return result_dict
|
|
407
|
+
except Exception as e:
|
|
408
|
+
logger.error(f"Error in update_website_geo_location_configuration: {e}")
|
|
409
|
+
return {"error": f"Failed to update website geo-location configuration: {e!s}"}
|
|
410
|
+
|
|
411
|
+
@register_as_tool
|
|
412
|
+
@with_header_auth(WebsiteConfigurationApi)
|
|
413
|
+
async def get_website_ip_masking_configuration(self, website_id: str, ctx=None, api_client=None) -> Dict[str, Any]:
|
|
414
|
+
"""
|
|
415
|
+
Get IP masking configuration for a website.
|
|
416
|
+
|
|
417
|
+
This API endpoint retrieves IP masking configuration for a specific website.
|
|
418
|
+
|
|
419
|
+
Args:
|
|
420
|
+
website_id: ID of the website
|
|
421
|
+
ctx: The MCP context (optional)
|
|
422
|
+
|
|
423
|
+
Returns:
|
|
424
|
+
Dictionary containing IP masking configuration or error information
|
|
425
|
+
"""
|
|
426
|
+
try:
|
|
427
|
+
logger.debug(f"get_website_ip_masking_configuration called with website_id={website_id}")
|
|
428
|
+
|
|
429
|
+
# Call the get_website_ip_masking_configuration method from the SDK
|
|
430
|
+
result = api_client.get_website_ip_masking_configuration(website_id=website_id)
|
|
431
|
+
|
|
432
|
+
# Convert the result to a dictionary
|
|
433
|
+
if hasattr(result, 'to_dict'):
|
|
434
|
+
result_dict = result.to_dict()
|
|
435
|
+
else:
|
|
436
|
+
# If it's already a dict or another format, use it as is
|
|
437
|
+
result_dict = result
|
|
438
|
+
|
|
439
|
+
logger.debug(f"Result from get_website_ip_masking_configuration: {result_dict}")
|
|
440
|
+
return result_dict
|
|
441
|
+
except Exception as e:
|
|
442
|
+
logger.error(f"Error in get_website_ip_masking_configuration: {e}", exc_info=True)
|
|
443
|
+
return {"error": f"Failed to get website IP masking configuration: {e!s}"}
|
|
444
|
+
|
|
445
|
+
@register_as_tool
|
|
446
|
+
@with_header_auth(WebsiteConfigurationApi)
|
|
447
|
+
async def update_website_ip_masking_configuration(self,
|
|
448
|
+
website_id: str,
|
|
449
|
+
payload: Optional[Dict[str, Any]] = None,
|
|
450
|
+
ctx=None, api_client=None) -> Dict[str, Any]:
|
|
451
|
+
"""
|
|
452
|
+
Update IP masking configuration for a website.
|
|
453
|
+
|
|
454
|
+
This API endpoint updates IP masking configuration for a specific website.
|
|
455
|
+
|
|
456
|
+
Args:
|
|
457
|
+
website_id: ID of the website
|
|
458
|
+
payload: IP masking configuration payload as a dictionary or JSON string
|
|
459
|
+
{
|
|
460
|
+
"ipMasking": "DEFAULT"
|
|
461
|
+
}
|
|
462
|
+
ctx: The MCP context (optional)
|
|
463
|
+
|
|
464
|
+
Returns:
|
|
465
|
+
Dictionary containing updated configuration or error information
|
|
466
|
+
"""
|
|
467
|
+
try:
|
|
468
|
+
logger.debug("update_website_geo_location_configuration called")
|
|
469
|
+
|
|
470
|
+
# Parse the payload
|
|
471
|
+
if isinstance(payload, str):
|
|
472
|
+
logger.debug("payload is a string, attempting to parse")
|
|
473
|
+
try:
|
|
474
|
+
import json
|
|
475
|
+
try:
|
|
476
|
+
parsed_payload = json.loads(payload)
|
|
477
|
+
logger.debug("Successfully parsed payload as JSON")
|
|
478
|
+
request_body = parsed_payload
|
|
479
|
+
except json.JSONDecodeError as e:
|
|
480
|
+
logger.debug(f"JSON parsing failed: {e}, trying with quotes replaced")
|
|
481
|
+
|
|
482
|
+
# Try replacing single quotes with double quotes
|
|
483
|
+
fixed_payload = payload.replace("'", "\"")
|
|
484
|
+
try:
|
|
485
|
+
parsed_payload = json.loads(fixed_payload)
|
|
486
|
+
logger.debug("Successfully parsed fixed JSON")
|
|
487
|
+
request_body = parsed_payload
|
|
488
|
+
except json.JSONDecodeError:
|
|
489
|
+
# Try as Python literal
|
|
490
|
+
import ast
|
|
491
|
+
try:
|
|
492
|
+
parsed_payload = ast.literal_eval(payload)
|
|
493
|
+
logger.debug("Successfully parsed payload as Python literal")
|
|
494
|
+
request_body = parsed_payload
|
|
495
|
+
except (SyntaxError, ValueError) as e2:
|
|
496
|
+
logger.debug(f"Failed to parse payload string: {e2}")
|
|
497
|
+
return {"error": f"Invalid payload format: {e2}", "payload": payload}
|
|
498
|
+
except Exception as e:
|
|
499
|
+
logger.debug(f"Error parsing payload string: {e}")
|
|
500
|
+
return {"error": f"Failed to parse payload: {e}", "payload": payload}
|
|
501
|
+
else:
|
|
502
|
+
# If payload is already a dictionary, use it directly
|
|
503
|
+
logger.debug("Using provided payload dictionary")
|
|
504
|
+
request_body = payload
|
|
505
|
+
|
|
506
|
+
try:
|
|
507
|
+
from instana_client.models.ip_masking_configuration import (
|
|
508
|
+
IpMaskingConfiguration,
|
|
509
|
+
)
|
|
510
|
+
logger.debug("Successfully imported IpMaskingConfiguration")
|
|
511
|
+
except ImportError as e:
|
|
512
|
+
logger.debug(f"Error importing IpMaskingConfiguration: {e}")
|
|
513
|
+
return {"error": f"Failed to import IpMaskingConfiguration: {e!s}"}
|
|
514
|
+
|
|
515
|
+
# Create an IpMaskingConfiguration object from the request body
|
|
516
|
+
try:
|
|
517
|
+
query_params = {}
|
|
518
|
+
|
|
519
|
+
# Required field: ip_masking (with alias ipMasking)
|
|
520
|
+
# Always provide a default value to ensure the required field is present
|
|
521
|
+
query_params["ip_masking"] = "DEFAULT" # Default value
|
|
522
|
+
|
|
523
|
+
if request_body:
|
|
524
|
+
if "ipMasking" in request_body:
|
|
525
|
+
query_params["ip_masking"] = request_body["ipMasking"]
|
|
526
|
+
elif "ip_masking" in request_body:
|
|
527
|
+
query_params["ip_masking"] = request_body["ip_masking"]
|
|
528
|
+
|
|
529
|
+
logger.debug(f"Creating IpMaskingConfiguration with params: {query_params}")
|
|
530
|
+
config_object = IpMaskingConfiguration(**query_params)
|
|
531
|
+
logger.debug("Successfully created IpMaskingConfiguration object")
|
|
532
|
+
except Exception as e:
|
|
533
|
+
logger.debug(f"Error creating IpMaskingConfiguration: {e}")
|
|
534
|
+
return {"error": f"Failed to create IpMaskingConfiguration: {e!s}"}
|
|
535
|
+
|
|
536
|
+
# Call the update_website_geo_location_configuration method from the SDK
|
|
537
|
+
logger.debug("Calling update_website_ip_masking_configuration with config object")
|
|
538
|
+
result = api_client.update_website_ip_masking_configuration(
|
|
539
|
+
website_id=website_id,
|
|
540
|
+
ip_masking_configuration=config_object,
|
|
541
|
+
)
|
|
542
|
+
# Convert the result to a dictionary
|
|
543
|
+
if hasattr(result, 'to_dict'):
|
|
544
|
+
result_dict = result.to_dict()
|
|
545
|
+
else:
|
|
546
|
+
# If it's already a dict or another format, use it as is
|
|
547
|
+
result_dict = result or {
|
|
548
|
+
"success": True,
|
|
549
|
+
"message": "Update website ip-masking configuration"
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
logger.debug(f"Result from update_website_ip_masking_configuration: {result_dict}")
|
|
553
|
+
return result_dict
|
|
554
|
+
except Exception as e:
|
|
555
|
+
logger.error(f"Error in update_website_ip_masking_configuration: {e}")
|
|
556
|
+
return {"error": f"Failed to update website ip-masking configuration: {e!s}"}
|
|
557
|
+
|
|
558
|
+
@register_as_tool
|
|
559
|
+
@with_header_auth(WebsiteConfigurationApi)
|
|
560
|
+
async def get_website_geo_mapping_rules(self, website_id: str, ctx=None, api_client=None) -> List[Dict[str, Any]]:
|
|
561
|
+
"""
|
|
562
|
+
Get custom geo mapping rules for website
|
|
563
|
+
|
|
564
|
+
This API endpoint retrieves custom geo mapping rules for a specific website.
|
|
565
|
+
|
|
566
|
+
Args:
|
|
567
|
+
website_id: ID of the website
|
|
568
|
+
ctx: The MCP context (optional)
|
|
569
|
+
|
|
570
|
+
Returns:
|
|
571
|
+
List containing custom geo mapping rules or error information
|
|
572
|
+
"""
|
|
573
|
+
try:
|
|
574
|
+
logger.debug(f"get_website_geo_mapping_rules called with website_id={website_id}")
|
|
575
|
+
|
|
576
|
+
# Call the get_website_geo_mapping_rules method from the SDK
|
|
577
|
+
# Use the raw response method to get the actual CSV data
|
|
578
|
+
try:
|
|
579
|
+
result = api_client.get_website_geo_mapping_rules(website_id=website_id)
|
|
580
|
+
|
|
581
|
+
# If the high-level method returns None, try the raw response
|
|
582
|
+
if result is None:
|
|
583
|
+
response = api_client.get_website_geo_mapping_rules_without_preload_content(website_id=website_id)
|
|
584
|
+
# Get the raw response data
|
|
585
|
+
if hasattr(response, 'data'):
|
|
586
|
+
csv_data = response.data.decode('utf-8') if isinstance(response.data, bytes) else str(response.data)
|
|
587
|
+
else:
|
|
588
|
+
csv_data = str(response)
|
|
589
|
+
else:
|
|
590
|
+
csv_data = str(result)
|
|
591
|
+
|
|
592
|
+
except Exception as api_error:
|
|
593
|
+
logger.warning(f"High-level API call failed: {api_error}, trying raw response")
|
|
594
|
+
# Fallback to raw HTTP response
|
|
595
|
+
response = api_client.get_website_geo_mapping_rules_without_preload_content(website_id=website_id)
|
|
596
|
+
if hasattr(response, 'data'):
|
|
597
|
+
csv_data = response.data.decode('utf-8') if isinstance(response.data, bytes) else str(response.data)
|
|
598
|
+
else:
|
|
599
|
+
csv_data = str(response)
|
|
600
|
+
|
|
601
|
+
# Handle CSV response format
|
|
602
|
+
result_list: List[Dict[str, Any]] = []
|
|
603
|
+
|
|
604
|
+
if csv_data and ',' in csv_data:
|
|
605
|
+
# Parse CSV response
|
|
606
|
+
import csv
|
|
607
|
+
import io
|
|
608
|
+
|
|
609
|
+
csv_reader = csv.DictReader(io.StringIO(csv_data))
|
|
610
|
+
for row in csv_reader:
|
|
611
|
+
result_list.append(dict(row))
|
|
612
|
+
elif csv_data:
|
|
613
|
+
# If it's not CSV but has data, return as single item
|
|
614
|
+
result_list.append({"data": csv_data})
|
|
615
|
+
|
|
616
|
+
logger.debug(f"Result from get_website_geo_mapping_rules: {result_list}")
|
|
617
|
+
return result_list
|
|
618
|
+
except Exception as e:
|
|
619
|
+
logger.error(f"Error in get_website_geo_mapping_rules: {e}", exc_info=True)
|
|
620
|
+
return [{"error": f"Failed to get website geo mapping rules: {e!s}"}]
|
|
621
|
+
|
|
622
|
+
@register_as_tool
|
|
623
|
+
@with_header_auth(WebsiteConfigurationApi)
|
|
624
|
+
async def set_website_geo_mapping_rules(self,
|
|
625
|
+
website_id: str,
|
|
626
|
+
body: Optional[str] = None,
|
|
627
|
+
ctx=None, api_client=None) -> Dict[str, Any]:
|
|
628
|
+
"""
|
|
629
|
+
Set custom geo mapping rules for website
|
|
630
|
+
|
|
631
|
+
This API endpoint sets custom geo mapping rules for a specific website.
|
|
632
|
+
|
|
633
|
+
Args:
|
|
634
|
+
website_id: ID of the website
|
|
635
|
+
body: Geo mapping rules payload as a string
|
|
636
|
+
ctx: The MCP context (optional)
|
|
637
|
+
|
|
638
|
+
Returns:
|
|
639
|
+
Dictionary containing custom geo mapping rules or error information
|
|
640
|
+
"""
|
|
641
|
+
try:
|
|
642
|
+
logger.debug(f"set_website_geo_mapping_rules called with website_id={website_id}")
|
|
643
|
+
|
|
644
|
+
if not website_id:
|
|
645
|
+
return {"error": "website_id parameter is required"}
|
|
646
|
+
|
|
647
|
+
# Call the set_website_geo_mapping_rules method from the SDK
|
|
648
|
+
# The API automatically sets content-type to text/csv
|
|
649
|
+
result = api_client.set_website_geo_mapping_rules(
|
|
650
|
+
website_id=website_id,
|
|
651
|
+
body=body
|
|
652
|
+
)
|
|
653
|
+
|
|
654
|
+
# Convert the result to a dictionary
|
|
655
|
+
if hasattr(result, 'to_dict'):
|
|
656
|
+
result_dict = result.to_dict()
|
|
657
|
+
else:
|
|
658
|
+
# If it's already a dict or another format, use it as is
|
|
659
|
+
result_dict = result
|
|
660
|
+
|
|
661
|
+
logger.debug(f"Result from set_website_geo_mapping_rules: {result_dict}")
|
|
662
|
+
return result_dict
|
|
663
|
+
except Exception as e:
|
|
664
|
+
logger.error(f"Error in set_website_geo_mapping_rules: {e}", exc_info=True)
|
|
665
|
+
return {"error": f"Failed to set website geo mapping rules: {e!s}"}
|
|
666
|
+
|
|
667
|
+
@register_as_tool
|
|
668
|
+
@with_header_auth(WebsiteConfigurationApi)
|
|
669
|
+
async def upload_source_map_file(self,
|
|
670
|
+
website_id: str,
|
|
671
|
+
source_map_config_id: str,
|
|
672
|
+
file_format: Optional[str] = None,
|
|
673
|
+
source_map: Optional[str] = None,
|
|
674
|
+
url: Optional[str] = None,
|
|
675
|
+
ctx=None, api_client=None) -> Dict[str, Any]:
|
|
676
|
+
"""
|
|
677
|
+
Upload source map file for a website.
|
|
678
|
+
|
|
679
|
+
This API endpoint uploads a source map file for a specific website.
|
|
680
|
+
|
|
681
|
+
Args:
|
|
682
|
+
website_id: ID of the website
|
|
683
|
+
source_map_config_id: ID of the source map config
|
|
684
|
+
file_format: Format of the source map file
|
|
685
|
+
source_map: Source map file
|
|
686
|
+
url: URL of the source map file
|
|
687
|
+
ctx: The MCP context (optional)
|
|
688
|
+
|
|
689
|
+
Returns:
|
|
690
|
+
Dictionary containing updated configuration or error information
|
|
691
|
+
"""
|
|
692
|
+
try:
|
|
693
|
+
if not website_id:
|
|
694
|
+
return {"error": "website_id parameter is required"}
|
|
695
|
+
if not source_map_config_id:
|
|
696
|
+
return {"error": "source_map_config_id parameter is required"}
|
|
697
|
+
|
|
698
|
+
logger.debug("upload_source_map_file called")
|
|
699
|
+
|
|
700
|
+
# Call the upload_source_map_file method from the SDK
|
|
701
|
+
logger.debug("Calling upload_source_map_file with config object")
|
|
702
|
+
result = api_client.upload_source_map_file(
|
|
703
|
+
website_id=website_id,
|
|
704
|
+
source_map_config_id=source_map_config_id,
|
|
705
|
+
file_format=file_format,
|
|
706
|
+
source_map=source_map,
|
|
707
|
+
url=url,
|
|
708
|
+
)
|
|
709
|
+
# Convert the result to a dictionary
|
|
710
|
+
if hasattr(result, 'to_dict'):
|
|
711
|
+
result_dict = result.to_dict()
|
|
712
|
+
else:
|
|
713
|
+
# If it's already a dict or another format, use it as is
|
|
714
|
+
result_dict = result or {
|
|
715
|
+
"success": True,
|
|
716
|
+
"message": "Upload source map file"
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
logger.debug(f"Result from upload_source_map_file: {result_dict}")
|
|
720
|
+
return result_dict
|
|
721
|
+
except Exception as e:
|
|
722
|
+
logger.error(f"Error in upload_source_map_file: {e}")
|
|
723
|
+
return {"error": f"Failed to upload source map file: {e!s}"}
|
|
724
|
+
|
|
725
|
+
@register_as_tool
|
|
726
|
+
@with_header_auth(WebsiteConfigurationApi)
|
|
727
|
+
async def clear_source_map_upload_configuration(self,
|
|
728
|
+
website_id: str,
|
|
729
|
+
source_map_config_id: str,
|
|
730
|
+
ctx=None, api_client=None) -> Dict[str, Any]:
|
|
731
|
+
"""
|
|
732
|
+
Clear source map upload configuration for a website.
|
|
733
|
+
|
|
734
|
+
This API endpoint clears source map upload configuration for a specific website.
|
|
735
|
+
|
|
736
|
+
Args:
|
|
737
|
+
website_id: ID of the website
|
|
738
|
+
source_map_config_id: ID of the source map config
|
|
739
|
+
ctx: The MCP context (optional)
|
|
740
|
+
|
|
741
|
+
Returns:
|
|
742
|
+
Dictionary containing source map upload configuration or error information
|
|
743
|
+
"""
|
|
744
|
+
try:
|
|
745
|
+
if not website_id:
|
|
746
|
+
return {"error": "website_id parameter is required"}
|
|
747
|
+
if not source_map_config_id:
|
|
748
|
+
return {"error": "source_map_config_id parameter is required"}
|
|
749
|
+
|
|
750
|
+
logger.debug(f"clear_source_map_upload_configuration called with website_id={website_id} and source_map_config_id={source_map_config_id}")
|
|
751
|
+
|
|
752
|
+
# Call the clear_source_map_upload_configuration method from the SDK
|
|
753
|
+
result = api_client.clear_source_map_upload_configuration(
|
|
754
|
+
website_id=website_id,
|
|
755
|
+
source_map_config_id=source_map_config_id)
|
|
756
|
+
|
|
757
|
+
# Convert the result to a dictionary
|
|
758
|
+
if hasattr(result, 'to_dict'):
|
|
759
|
+
result_dict = result.to_dict()
|
|
760
|
+
else:
|
|
761
|
+
# If it's already a dict or another format, use it as is
|
|
762
|
+
result_dict = result
|
|
763
|
+
|
|
764
|
+
logger.debug(f"Result from clear_source_map_upload_configuration: {result_dict}")
|
|
765
|
+
return result_dict
|
|
766
|
+
except Exception as e:
|
|
767
|
+
logger.error(f"Error in clear_source_map_upload_configuration: {e}", exc_info=True)
|
|
768
|
+
return {"error": f"Failed to clear source map upload configuration: {e!s}"}
|
|
769
|
+
|
|
770
|
+
|