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.
@@ -1,17 +1,18 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-instana
3
- Version: 0.3.0
3
+ Version: 0.6.2
4
4
  Summary: MCP server for Instana
5
5
  Author-email: Elina Priyadarshinee <Elina.priyadarshinee1@ibm.com>, Guangya Liu <gyliu@ibm.com>, Isabell Sippli <ischwert@de.ibm.com>, Jay Sharma <Jay.Sharma3@ibm.com>, Madhu Tadiparthi <madhu.tadiparthi@ibm.com>, Riya Kumari <Riya.Kumari3@ibm.com>
6
6
  License: Apache-2.0
7
7
  License-File: LICENSE.md
8
8
  Requires-Python: >=3.10
9
- Requires-Dist: fastmcp==2.10.3
10
- Requires-Dist: instana-client==1.0.0
9
+ Requires-Dist: fastmcp==2.13.0
10
+ Requires-Dist: instana-client==1.0.2
11
11
  Requires-Dist: mcp
12
12
  Requires-Dist: pydantic==2.11.7
13
13
  Requires-Dist: python-dotenv==1.1.0
14
14
  Requires-Dist: requests==2.32.4
15
+ Requires-Dist: traceloop-sdk>=0.47.5
15
16
  Provides-Extra: dev
16
17
  Requires-Dist: coverage>=7.10.1; extra == 'dev'
17
18
  Requires-Dist: pytest-asyncio>=1.1.0; extra == 'dev'
@@ -24,7 +25,7 @@ Description-Content-Type: text/markdown
24
25
 
25
26
  <!-- START doctoc generated TOC please keep comment here to allow auto update -->
26
27
  <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
27
- **Table of Contents**
28
+ <!-- mcp-name: io.github.instana/mcp-instana -->
28
29
 
29
30
  - [MCP Server for IBM Instana](#mcp-server-for-ibm-instana)
30
31
  - [Architecture Overview](#architecture-overview)
@@ -71,23 +72,8 @@ Description-Content-Type: text/markdown
71
72
  - [**pyproject.toml** (Development)](#pyprojecttoml-development)
72
73
  - [**pyproject-runtime.toml** (Production)](#pyproject-runtimetoml-production)
73
74
  - [Building the Docker Image](#building-the-docker-image)
74
- - [**Prerequisites**](#prerequisites-1)
75
+ - [**Prerequisites**](#prerequisites)
75
76
  - [**Build Command**](#build-command)
76
- - [**What the Build Does**](#what-the-build-does)
77
- - [Running the Docker Container](#running-the-docker-container)
78
- - [**Basic Usage**](#basic-usage)
79
- - [**Environment Variables**](#environment-variables)
80
- - [**Docker Compose Example**](#docker-compose-example)
81
- - [Docker Security Features](#docker-security-features)
82
- - [**Security Best Practices Implemented**](#security-best-practices-implemented)
83
- - [**Image Size Optimization**](#image-size-optimization)
84
- - [Testing the Docker Container](#testing-the-docker-container)
85
- - [**Health Check**](#health-check)
86
- - [**MCP Inspector Testing**](#mcp-inspector-testing)
87
- - [**Logs and Debugging**](#logs-and-debugging)
88
- - [Production Deployment](#production-deployment)
89
- - [**Recommended Production Setup**](#recommended-production-setup)
90
- - [**Kubernetes Example**](#kubernetes-example)
91
77
  - [Troubleshooting](#troubleshooting)
92
78
  - [**Docker Issues**](#docker-issues)
93
79
  - [**Container Won't Start**](#container-wont-start)
@@ -1024,164 +1010,23 @@ The project uses a **two-file dependency management strategy**:
1024
1010
  #### **Prerequisites**
1025
1011
  - Docker installed and running
1026
1012
  - Access to the project source code
1013
+ - Docker BuildKit for multi-architecture builds (enabled by default in recent Docker versions)
1027
1014
 
1028
1015
  #### **Build Command**
1029
1016
  ```bash
1030
1017
  # Build the optimized production image
1031
- docker build -t mcp-instana .
1018
+ docker build -t mcp-instana:latest .
1032
1019
 
1033
1020
  # Build with a specific tag
1034
- docker build -t mcp-instana:v1.0.0 .
1035
- ```
1036
-
1037
- #### **What the Build Does**
1038
- 1. **Multi-stage build** for optimal size and security
1039
- 2. **Builder stage**: Installs only runtime dependencies from `pyproject-runtime.toml`
1040
- 3. **Runtime stage**: Creates minimal production image with non-root user
1041
- 4. **Security**: No hardcoded secrets, proper user permissions
1042
- 5. **Optimization**: Only essential dependencies (20 vs 95+ in development)
1021
+ docker build -t mcp-instana:< image_tag > .
1043
1022
 
1044
- ### Running the Docker Container
1045
-
1046
- #### **Basic Usage**
1023
+ #### **Run Command**
1047
1024
  ```bash
1048
- # Run with environment variables (recommended)
1049
- docker run -p 8080:8080 \
1050
- -e INSTANA_API_TOKEN=your_instana_token \
1051
- -e INSTANA_BASE_URL=https://your-instana-instance.instana.io \
1052
- mcp-instana
1025
+ # Run the container (no credentials needed in the container)
1026
+ docker run -p 8080:8080 mcp-instana
1053
1027
 
1054
1028
  # Run with custom port
1055
- docker run -p 8081:8080 \
1056
- -e INSTANA_API_TOKEN=your_instana_token \
1057
- -e INSTANA_BASE_URL=https://your-instana-instance.instana.io \
1058
- mcp-instana
1059
- ```
1060
-
1061
- #### **Environment Variables**
1062
- The container requires the following environment variables:
1063
-
1064
- | Variable | Description | Example |
1065
- |----------|-------------|---------|
1066
- | `INSTANA_API_TOKEN` | Your Instana API token | `your_instana_token` |
1067
- | `INSTANA_BASE_URL` | Your Instana instance URL | `https://your-instana-instance.instana.io` |
1068
- | `PORT` | Server port (optional, defaults to 8080) | `8080` |
1069
-
1070
- #### **Docker Compose Example**
1071
- ```yaml
1072
- version: '3.8'
1073
- services:
1074
- mcp-instana:
1075
- build: .
1076
- ports:
1077
- - "8080:8080"
1078
- environment:
1079
- - INSTANA_API_TOKEN=${INSTANA_API_TOKEN}
1080
- - INSTANA_BASE_URL=${INSTANA_BASE_URL}
1081
- restart: unless-stopped
1082
- healthcheck:
1083
- test: ["CMD", "python", "-c", "import requests; requests.get('http://127.0.0.1:8080/health', timeout=5)"]
1084
- interval: 30s
1085
- timeout: 10s
1086
- retries: 3
1087
- start_period: 40s
1088
- ```
1089
-
1090
- ### Docker Security Features
1091
-
1092
- #### **Security Best Practices Implemented**
1093
- - ✅ **Non-root user**: Container runs as `mcpuser` (not root)
1094
- - ✅ **No hardcoded secrets**: All credentials passed via environment variables
1095
- - ✅ **Minimal dependencies**: Only 20 essential runtime dependencies
1096
- - ✅ **Multi-stage build**: Build tools don't make it to final image
1097
- - ✅ **Health checks**: Built-in container health monitoring
1098
- - ✅ **Optimized base image**: Uses `python:3.11-slim`
1099
-
1100
- #### **Image Size Optimization**
1101
- - **Original approach**: 95+ dependencies → ~1-2GB+ image
1102
- - **Optimized approach**: 20 dependencies → ~266MB image
1103
- - **Size reduction**: ~70-80% smaller images
1104
- - **Benefits**: Faster deployments, lower storage costs, reduced attack surface
1105
-
1106
- ### Testing the Docker Container
1107
-
1108
- #### **Health Check**
1109
- ```bash
1110
- # Check if container is healthy
1111
- docker ps
1112
-
1113
- # Test the MCP endpoint
1114
- curl http://localhost:8080/mcp/
1115
- ```
1116
-
1117
- #### **MCP Inspector Testing**
1118
- ```bash
1119
- # Test with MCP Inspector
1120
- npx @modelcontextprotocol/inspector http://localhost:8080/mcp/
1121
- ```
1122
-
1123
- #### **Logs and Debugging**
1124
- ```bash
1125
- # View container logs
1126
- docker logs <container_id>
1127
-
1128
- # Follow logs in real-time
1129
- docker logs -f <container_id>
1130
-
1131
- # Execute commands in running container
1132
- docker exec -it <container_id> /bin/bash
1133
- ```
1134
-
1135
- ### Production Deployment
1136
-
1137
- #### **Recommended Production Setup**
1138
- 1. **Use environment variables** for all secrets
1139
- 2. **Set up proper logging** and monitoring
1140
- 3. **Configure health checks** for container orchestration
1141
- 4. **Use container orchestration** (Kubernetes, Docker Swarm, etc.)
1142
- 5. **Implement proper backup** and disaster recovery
1143
-
1144
- #### **Kubernetes Example**
1145
- ```yaml
1146
- apiVersion: apps/v1
1147
- kind: Deployment
1148
- metadata:
1149
- name: mcp-instana
1150
- spec:
1151
- replicas: 2
1152
- selector:
1153
- matchLabels:
1154
- app: mcp-instana
1155
- template:
1156
- metadata:
1157
- labels:
1158
- app: mcp-instana
1159
- spec:
1160
- containers:
1161
- - name: mcp-instana
1162
- image: mcp-instana:latest
1163
- ports:
1164
- - containerPort: 8080
1165
- env:
1166
- - name: INSTANA_API_TOKEN
1167
- valueFrom:
1168
- secretKeyRef:
1169
- name: instana-secrets
1170
- key: api-token
1171
- - name: INSTANA_BASE_URL
1172
- value: "https://your-instana-instance.instana.io"
1173
- livenessProbe:
1174
- httpGet:
1175
- path: /health
1176
- port: 8080
1177
- initialDelaySeconds: 30
1178
- periodSeconds: 10
1179
- readinessProbe:
1180
- httpGet:
1181
- path: /health
1182
- port: 8080
1183
- initialDelaySeconds: 5
1184
- periodSeconds: 5
1029
+ docker run -p 8081:8080 mcp-instana
1185
1030
  ```
1186
1031
 
1187
1032
  ## Troubleshooting
@@ -1192,18 +1037,17 @@ spec:
1192
1037
  ```bash
1193
1038
  # Check container logs
1194
1039
  docker logs <container_id>
1195
-
1196
1040
  # Common issues:
1197
- # 1. Missing environment variables
1198
- # 2. Port already in use
1199
- # 3. Invalid Instana credentials
1041
+ # 1. Port already in use
1042
+ # 2. Invalid container image
1043
+ # 3. Missing dependencies
1044
+ # Credentials are passed via HTTP headers from the MCP client
1200
1045
  ```
1201
1046
 
1202
1047
  #### **Connection Issues**
1203
1048
  ```bash
1204
1049
  # Test container connectivity
1205
1050
  docker exec -it <container_id> curl http://127.0.0.1:8080/health
1206
-
1207
1051
  # Check port mapping
1208
1052
  docker port <container_id>
1209
1053
  ```
@@ -1212,7 +1056,6 @@ docker port <container_id>
1212
1056
  ```bash
1213
1057
  # Check container resource usage
1214
1058
  docker stats <container_id>
1215
-
1216
1059
  # Monitor container health
1217
1060
  docker inspect <container_id> | grep -A 10 Health
1218
1061
  ```
@@ -1228,3 +1071,4 @@ docker inspect <container_id> | grep -A 10 Health
1228
1071
  - If that works, your Python environment may not be able to verify the certificate and might not have access to the same certificates as your shell or system. Ensure your Python environment uses system certificates (macOS). You can do this by installing certificates to Python:
1229
1072
  `//Applications/Python\ 3.13/Install\ Certificates.command`
1230
1073
  - If you cannot reach the endpoint with SSL verification, try without it. If that works, check your system's CA certificates and ensure they are up-to-date.
1074
+ ```
@@ -1,26 +1,27 @@
1
1
  src/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ src/observability.py,sha256=FQIMF4reR-UtXqnBSa3Ub0wuPvlf9VNSTeMieJmFdMM,959
2
3
  src/application/__init__.py,sha256=cfWHjA5NzAvL0jcekaIO3NTe5DRtznYRqg9_8hVwSzc,37
3
- src/application/application_alert_config.py,sha256=dbYGzUNZdfcEp_-zLLLm8_AfJZHoSPamLp4eHNYQ42I,31196
4
- src/application/application_analyze.py,sha256=EXOaGRn8wEoFUltAmAnzFRiTrkLzh7c_RHdP5Nj2-Hk,26947
4
+ src/application/application_alert_config.py,sha256=LDUgkpMBx_5tX88dzE0emr31kydzMZWDCi36JzZDyEw,31590
5
+ src/application/application_analyze.py,sha256=cb5kbt2GYntyFAHXvix9r7nFthoftCa9haWmXjxpDEM,27046
5
6
  src/application/application_catalog.py,sha256=MVjU1CyYf6NSeCt2o0aTlNDAKeMUY3vr-wDOprnt2D8,6540
6
- src/application/application_global_alert_config.py,sha256=HqBlLLhMmpwy8pJlz9SDkHJ6w1vHH0LD7_LX4YWe7uk,31684
7
- src/application/application_metrics.py,sha256=swuoMk6fWNbW-IzivxJv8C7CgJie66u9d0JvtZ2jU4Y,15476
8
- src/application/application_resources.py,sha256=USbIrPnkokUBOiack7EChOTH2vqJM2D0EF-9MjGbn5w,18279
9
- src/application/application_settings.py,sha256=UZHZwY3GNW4JMl1Q6gGtGCE4iYERR7ijK0hslCYBMTo,76210
7
+ src/application/application_global_alert_config.py,sha256=PGDPXAuSu_J6uKohMhKWh1gpPOUkk4COvQF4mXsEPZI,31474
8
+ src/application/application_metrics.py,sha256=M8OymCr2TxlckpnUtASFaLb5kZD1AbnZy2ApvfQgGbI,15466
9
+ src/application/application_resources.py,sha256=3cZEOaAppDwSMG_DItR1BTbjuz_JkxedorvbvxzDcbw,20262
10
+ src/application/application_settings.py,sha256=gwkGKeiKpXLGmRe_MYtSvGglXHsgP9JakaBkm8TeKbo,83655
10
11
  src/application/application_topology.py,sha256=WinVNHSSTF771vq5hl2GxTLm3bdpehAQJ1AMumfy7M0,4870
11
12
  src/automation/action_catalog.py,sha256=jivR73riqBt-MGDnhSoEsshG7bHk7JpxvVuRx7FCtOA,17493
12
13
  src/automation/action_history.py,sha256=aMHUd5RLMrgdDvJXv0x6dkUA7I_35O0A89RPhFUfORI,16494
13
14
  src/core/__init__.py,sha256=gZdGV4TVETqe3XV-_lzQMw0IgyTTCqYYgP3PH9YdWbU,30
14
- src/core/server.py,sha256=WvkxAWYBiKdo3mdxoc6v_9SCO8Ij6hS2vCSMSlIvbTY,25022
15
+ src/core/server.py,sha256=LSdYYO-G9g1k_2yRqZVta2e8ZaGr3hFvj0duSbc7o-Y,25107
15
16
  src/core/utils.py,sha256=vB2ArNkOy0YIHk8Xv8nYHGEeTpw79pStJz6HL2aHWK8,11527
16
17
  src/event/__init__.py,sha256=ru-xCHU9xksGf9aJslvI37-6SI2yoBOpsoaED-vbaaQ,31
17
- src/event/events_tools.py,sha256=R17EYk1jUUIQl5706idtOKzLWWNLchv2fRd-U8S1Uno,42775
18
+ src/event/events_tools.py,sha256=ajCARRjxd6tkqH_fdFC1cOg9TYRV_gJE4BBVqDmyLV0,45365
18
19
  src/infrastructure/__init__.py,sha256=xZuRO1Zb2iPyO5G3PRM90dfesaFheL7DMSSJMujtLVk,40
19
20
  src/infrastructure/infrastructure_analyze.py,sha256=eBo9Rq7DRzsIKup4LR-ar_1g4kr_IPWeKcHMb-w1hLA,29455
20
- src/infrastructure/infrastructure_catalog.py,sha256=rE_ySS-MHDwGc_msat6XsxKolgYbduZSnPp4adXFQKM,29827
21
- src/infrastructure/infrastructure_metrics.py,sha256=he_KAadNp5VAWdDM65ZuDavHOZvwOX4tEuXJYwXjm78,7081
22
- src/infrastructure/infrastructure_resources.py,sha256=gFxjzGbUdlFXVjbuWKUxC0NEuwkPsZNkrWhnZ2Zd2t0,29297
23
- src/infrastructure/infrastructure_topology.py,sha256=mVz9jrpjstDNh_iUxZfGo912Ze5I3zH3XlapFIwm_3A,14830
21
+ src/infrastructure/infrastructure_catalog.py,sha256=tXrDAJ0ZzS6cfrx-aDJ63GkYGKUbePV_NLH-opm5bsE,31254
22
+ src/infrastructure/infrastructure_metrics.py,sha256=P5uiy2uGLrmh8LuYcW1GG_DkOAvAb64nEpKD_lBeydo,7136
23
+ src/infrastructure/infrastructure_resources.py,sha256=sVnYUkeMiaMAp3ln6ujHzRLOwceHr_XLE9LB0Qmrgbg,29240
24
+ src/infrastructure/infrastructure_topology.py,sha256=rVY7QnLGANHPXlezAhbc6wJfOoUO2TRr_VlXtkifo4A,15456
24
25
  src/log/__init__.py,sha256=NwPZccMqR5aR6PrISe760gkABtpg7zpbwOK-uMPB-_Y,29
25
26
  src/log/log_alert_configuration.py,sha256=-8ORRo889n4X4GalwhTQa9uHU4JuERy2OfnhP7k6LOM,18430
26
27
  src/prompts/__init__.py,sha256=oXt7rf_EASRtgL43lKRgH8xhX6abJ3fhpHfarIlkFog,405
@@ -52,8 +53,8 @@ src/website/website_analyze.py,sha256=0VyK8f-9vW1LzZ70b7IxKzwqSL2bWw8wDFSOCx2pEr
52
53
  src/website/website_catalog.py,sha256=Z21urtdTf8sU2SZiCwGq5OyPLaswS9lw12TCD9pS__4,7127
53
54
  src/website/website_configuration.py,sha256=hBHSFgj6GXOfLvlq8p-tBU4KFQc99uS167DBz4cjjK0,35926
54
55
  src/website/website_metrics.py,sha256=6McxbVYZq5i61Ml3QcVeHS86y2rCIPzdDuUDOxNtnMM,10954
55
- mcp_instana-0.3.0.dist-info/METADATA,sha256=pBBWf3CzWNzeHE-5yRbEVJZ3ZV5EW2w34sVepEmiyjc,47089
56
- mcp_instana-0.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
57
- mcp_instana-0.3.0.dist-info/entry_points.txt,sha256=p1aZ9Ks0aJKpoIy6Mk-08cGYAfkXMNbwYIlokm513A4,140
58
- mcp_instana-0.3.0.dist-info/licenses/LICENSE.md,sha256=Ox7lseFP2kBRXBjsLweW1jLmWiCyrKjwF8ZUvCbKd70,11310
59
- mcp_instana-0.3.0.dist-info/RECORD,,
56
+ mcp_instana-0.6.2.dist-info/METADATA,sha256=utEaEnN7t95AmdwuR4a-ODN_Q5WJEl-zeSJfRrP06bY,42334
57
+ mcp_instana-0.6.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
58
+ mcp_instana-0.6.2.dist-info/entry_points.txt,sha256=p1aZ9Ks0aJKpoIy6Mk-08cGYAfkXMNbwYIlokm513A4,140
59
+ mcp_instana-0.6.2.dist-info/licenses/LICENSE.md,sha256=Ox7lseFP2kBRXBjsLweW1jLmWiCyrKjwF8ZUvCbKd70,11310
60
+ mcp_instana-0.6.2.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -525,11 +525,16 @@ class ApplicationAlertMCPTools(BaseInstanaClient):
525
525
  logger.debug(f"Error importing ApplicationAlertConfig: {e}")
526
526
  return {"error": f"Failed to import ApplicationAlertConfig: {e!s}"}
527
527
 
528
- # Create an ApplicationAlertConfig object from the request body
528
+ # Create an ApplicationAlertConfig object from the request body using from_dict
529
+ # This properly handles nested objects and discriminators
529
530
  try:
530
531
  logger.debug(f"Creating ApplicationAlertConfig with params: {request_body}")
531
- config_object = ApplicationAlertConfig(**request_body)
532
+ config_object = ApplicationAlertConfig.from_dict(request_body)
532
533
  logger.debug("Successfully created config object")
534
+
535
+ # Debug: Log what will be sent to API
536
+ config_dict = config_object.to_dict()
537
+ logger.debug(f"Config object as dict (what will be sent to API): {config_dict}")
533
538
  except Exception as e:
534
539
  logger.debug(f"Error creating ApplicationAlertConfig: {e}")
535
540
  return {"error": f"Failed to create config object: {e!s}"}
@@ -655,10 +660,11 @@ class ApplicationAlertMCPTools(BaseInstanaClient):
655
660
  logger.debug(f"Error importing ApplicationAlertConfig: {e}")
656
661
  return {"error": f"Failed to import ApplicationAlertConfig: {e!s}"}
657
662
 
658
- # Create an ApplicationAlertConfig object from the request body
663
+ # Create an ApplicationAlertConfig object from the request body using from_dict
664
+ # This properly handles nested objects and discriminators
659
665
  try:
660
666
  logger.debug(f"Creating ApplicationAlertConfig with params: {request_body}")
661
- config_object = ApplicationAlertConfig(**request_body)
667
+ config_object = ApplicationAlertConfig.from_dict(request_body)
662
668
  logger.debug("Successfully created config object")
663
669
  except Exception as e:
664
670
  logger.debug(f"Error creating ApplicationAlertConfig: {e}")
@@ -84,21 +84,24 @@ class ApplicationAnalyzeMCPTools(BaseInstanaClient):
84
84
  return {"error": "Both trace_id and call_id must be provided"}
85
85
 
86
86
  logger.debug(f"Fetching call details for trace_id={trace_id}, call_id={call_id}")
87
- result = api_client.get_call_details(
87
+ result = api_client.get_call_details_without_preload_content(
88
88
  trace_id=trace_id,
89
89
  call_id=call_id
90
90
  )
91
91
 
92
- # Convert the result to a dictionary
93
- if hasattr(result, 'to_dict'):
94
- result_dict = result.to_dict()
95
- else:
96
- # If it's already a dict or another format, use it as is
97
- result_dict = result
92
+ import json
98
93
 
99
- logger.debug(f"Result from get_call_details: {result_dict}")
100
- # Ensure we return a dictionary
101
- return dict(result_dict) if not isinstance(result_dict, dict) else result_dict
94
+ try:
95
+ response_text = result.data.decode('utf-8')
96
+ result_dict = json.loads(response_text)
97
+ logger.debug("Successfully retrieved call details")
98
+ return result_dict
99
+
100
+ # Convert the result to a dictionary
101
+ except (json.JSONDecodeError, AttributeError) as json_err:
102
+ error_message = f"Failed to parse JSON response: {json_err}"
103
+ logger.error(error_message)
104
+ return {"error": error_message}
102
105
 
103
106
  except Exception as e:
104
107
  logger.error(f"Error getting call details: {e}", exc_info=True)
@@ -41,7 +41,7 @@ class ApplicationGlobalAlertMCPTools(BaseInstanaClient):
41
41
  async def find_active_global_application_alert_configs(self,
42
42
  application_id: str,
43
43
  alert_ids: Optional[List[str]] = None,
44
- ctx=None, api_client=None) -> List[Dict[str, Any]]:
44
+ ctx=None, api_client=None) -> Dict[str, Any]:
45
45
  """
46
46
  Get All Global Smart Alert Configuration.
47
47
 
@@ -63,36 +63,37 @@ class ApplicationGlobalAlertMCPTools(BaseInstanaClient):
63
63
 
64
64
  # Validate required parameters
65
65
  if not application_id:
66
- return [{"error": "application_id is required"}]
66
+ return {"error": "application_id is required"}
67
67
 
68
68
  # Call the find_active_global_application_alert_configs method from the SDK
69
69
  logger.debug(f"Calling find_active_global_application_alert_configs with application_id={application_id}, alert_ids={alert_ids}")
70
- result = api_client.find_active_global_application_alert_configs(
70
+ response = api_client.find_active_global_application_alert_configs_without_preload_content(
71
71
  application_id=application_id,
72
72
  alert_ids=alert_ids
73
73
  )
74
74
 
75
- # Convert the result to a list format
76
- if isinstance(result, list):
77
- # If it's already a list, convert each item to dict if needed
78
- result_list = []
79
- for item in result:
80
- if hasattr(item, 'to_dict'):
81
- result_list.append(item.to_dict())
82
- else:
83
- result_list.append(item)
84
- elif hasattr(result, 'to_dict'):
85
- # If it's a single object, wrap it in a list
86
- result_list = [result.to_dict()]
87
- else:
88
- # If it's already a dict or other format, wrap it in a list
89
- result_list = [result] if result else []
75
+ import json
76
+
77
+ raw_data = response.data.decode('utf-8')
78
+ logger.debug(f"Raw data: {raw_data}")
79
+
80
+ try:
81
+ result = json.loads(raw_data)
82
+ logger.debug(f"Parsed JSON result: {result}")
83
+
84
+ if isinstance(result, list):
85
+ return {"configs": result}
86
+ else:
87
+ return {"configs": [result] if result else []}
88
+
89
+ except json.JSONDecodeError as e:
90
+ error_msg = f"Failed to parse response JSON: {e}"
91
+ logger.error(error_msg)
92
+ return {"error": error_msg}
90
93
 
91
- logger.debug(f"Result from find_active_global_application_alert_configs: {result_list}")
92
- return result_list
93
94
  except Exception as e:
94
95
  logger.error(f"Error in find_active_global_application_alert_configs: {e}", exc_info=True)
95
- return [{"error": f"Failed to get active global application alert config: {e!s}"}]
96
+ return {"error": f"Failed to get active global application alert config: {e!s}"}
96
97
 
97
98
 
98
99
  @register_as_tool(
@@ -134,18 +134,18 @@ class ApplicationMetricsMCPTools(BaseInstanaClient):
134
134
  )
135
135
  @with_header_auth(ApplicationMetricsApi)
136
136
  async def get_application_metrics(self,
137
- application_ids: Optional[List[str]] = None,
137
+ application_id: Optional[str] = None,
138
138
  metrics: Optional[List[Dict[str, str]]] = None,
139
139
  time_frame: Optional[Dict[str, int]] = None,
140
140
  fill_time_series: Optional[bool] = True,
141
141
  ctx=None, api_client=None) -> Dict[str, Any]:
142
142
  """
143
- Get metrics for specific applications.
143
+ Get metrics for a specific application.
144
144
 
145
145
  This API endpoint retrieves one or more supported aggregations of metrics for an Application Perspective.
146
146
 
147
147
  Args:
148
- application_ids: List of application IDs to get metrics for
148
+ application_id: Application ID to get metrics for (single application)
149
149
  metrics: List of metrics to retrieve with their aggregations
150
150
  Example: [{"metric": "latency", "aggregation": "MEAN"}]
151
151
  time_frame: Dictionary with 'from' and 'to' timestamps in milliseconds
@@ -157,7 +157,7 @@ class ApplicationMetricsMCPTools(BaseInstanaClient):
157
157
  Dictionary containing application metrics data or error information
158
158
  """
159
159
  try:
160
- logger.debug(f"get_application_metrics called with application_ids={application_ids}")
160
+ logger.debug(f"get_application_metrics called with application_id={application_id}")
161
161
 
162
162
  # Set default time range if not provided
163
163
  if not time_frame:
@@ -183,9 +183,9 @@ class ApplicationMetricsMCPTools(BaseInstanaClient):
183
183
  "timeFrame": time_frame
184
184
  }
185
185
 
186
- # Add application IDs if provided
187
- if application_ids:
188
- request_body["applicationIds"] = application_ids
186
+ # Add application ID if provided
187
+ if application_id:
188
+ request_body["applicationId"] = application_id
189
189
 
190
190
  # Create the GetApplications object
191
191
  get_applications = GetApplications(**request_body)
@@ -215,18 +215,18 @@ class ApplicationMetricsMCPTools(BaseInstanaClient):
215
215
  )
216
216
  @with_header_auth(ApplicationMetricsApi)
217
217
  async def get_endpoints_metrics(self,
218
- endpoint_ids: Optional[List[str]] = None,
218
+ endpoint_id: Optional[str] = None,
219
219
  metrics: Optional[List[Dict[str, str]]] = None,
220
220
  time_frame: Optional[Dict[str, int]] = None,
221
221
  fill_time_series: Optional[bool] = True,
222
222
  ctx=None, api_client=None) -> Dict[str, Any]:
223
223
  """
224
- Get metrics for specific endpoints.
224
+ Get metrics for a specific endpoint.
225
225
 
226
226
  This API endpoint retrieves one or more supported aggregations of metrics for an Endpoint.
227
227
 
228
228
  Args:
229
- endpoint_ids: List of endpoint IDs to get metrics for
229
+ endpoint_id: Endpoint ID to get metrics for (single endpoint)
230
230
  metrics: List of metrics to retrieve with their aggregations
231
231
  Example: [{"metric": "latency", "aggregation": "MEAN"}]
232
232
  time_frame: Dictionary with 'from' and 'to' timestamps in milliseconds
@@ -238,7 +238,7 @@ class ApplicationMetricsMCPTools(BaseInstanaClient):
238
238
  Dictionary containing endpoint metrics data or error information
239
239
  """
240
240
  try:
241
- logger.debug(f"get_endpoints_metrics called with endpoint_ids={endpoint_ids}")
241
+ logger.debug(f"get_endpoints_metrics called with endpoint_id={endpoint_id}")
242
242
 
243
243
  # Set default time range if not provided
244
244
  if not time_frame:
@@ -264,9 +264,9 @@ class ApplicationMetricsMCPTools(BaseInstanaClient):
264
264
  "timeFrame": time_frame
265
265
  }
266
266
 
267
- # Add endpoint IDs if provided
268
- if endpoint_ids:
269
- request_body["endpointIds"] = endpoint_ids
267
+ # Add endpoint ID if provided
268
+ if endpoint_id:
269
+ request_body["endpointId"] = endpoint_id
270
270
 
271
271
  # Create the GetEndpoints object
272
272
  get_endpoints = GetEndpoints(**request_body)
@@ -296,19 +296,19 @@ class ApplicationMetricsMCPTools(BaseInstanaClient):
296
296
  )
297
297
  @with_header_auth(ApplicationMetricsApi)
298
298
  async def get_services_metrics(self,
299
- service_ids: Optional[List[str]] = None,
299
+ service_id: Optional[str] = None,
300
300
  metrics: Optional[List[Dict[str, str]]] = None,
301
301
  time_frame: Optional[Dict[str, int]] = None,
302
302
  fill_time_series: Optional[bool] = True,
303
303
  include_snapshot_ids: Optional[bool] = False,
304
304
  ctx=None, api_client=None) -> Dict[str, Any]:
305
305
  """
306
- Get metrics for specific services.
306
+ Get metrics for a specific service.
307
307
 
308
308
  This API endpoint retrieves one or more supported aggregations of metrics for a Service.
309
309
 
310
310
  Args:
311
- service_ids: List of service IDs to get metrics for
311
+ service_id: Service ID to get metrics for (single service)
312
312
  metrics: List of metrics to retrieve with their aggregations
313
313
  Example: [{"metric": "latency", "aggregation": "MEAN"}]
314
314
  time_frame: Dictionary with 'from' and 'to' timestamps in milliseconds
@@ -321,7 +321,7 @@ class ApplicationMetricsMCPTools(BaseInstanaClient):
321
321
  Dictionary containing service metrics data or error information
322
322
  """
323
323
  try:
324
- logger.debug(f"get_services_metrics called with service_ids={service_ids}")
324
+ logger.debug(f"get_services_metrics called with service_id={service_id}")
325
325
 
326
326
  # Set default time range if not provided
327
327
  if not time_frame:
@@ -347,9 +347,9 @@ class ApplicationMetricsMCPTools(BaseInstanaClient):
347
347
  "timeFrame": time_frame
348
348
  }
349
349
 
350
- # Add service IDs if provided
351
- if service_ids:
352
- request_body["serviceIds"] = service_ids
350
+ # Add service ID if provided
351
+ if service_id:
352
+ request_body["serviceId"] = service_id
353
353
 
354
354
  # Create the GetServices object
355
355
  get_services = GetServices(**request_body)