Skip to content

Commit 3684ab0

Browse files
matthewgoodman13Matthew Goodman
andauthored
fix: (ecs-mcp-server) update troubleshooting API parameters, add integration testing, app_name validation (#1200)
* fix: update troubleshooting API parameters, add integration testing, update app_name validation - Replace app_name parameter with cluster_name/service_name for better ECS resource targeting - Extract common utilities to troubleshooting_tools/utils.py module - Update all troubleshooting tools to use new parameter structure - Add comprehensive integration testing framework (mcp-inspector) with validation scenarios - Update app_name validation for length and maintain only lowercase chars on creation * fix(ecs-mcp-server): addressed rev1 comments Renamed troubleshooting API parameters: - cluster_name → ecs_cluster_name - service_name → ecs_service_name - task_id → ecs_task_id - stack_id/stack_name → cfn_stack_name Update parameter validation, transformers, documentation, examples, and tests to reflect the new naming convention. Make fetch_network_configuration require ecs_cluster_name parameter and remove unused find_clusters import. Improve consistency across parameter descriptions by capitalizing resource names (ECS Cluster, ECS Service, ECS Task, CloudWatch Logs, etc.) Removed "assessment" and "image_issues" when validation fails. * fix(ecs-mcp-server): boost cov --------- Co-authored-by: Matthew Goodman <mtgoo@amazon.com>
1 parent 52befc7 commit 3684ab0

35 files changed

+7780
-4353
lines changed

src/ecs-mcp-server/awslabs/ecs_mcp_server/api/ecs_troubleshooting.py

Lines changed: 106 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -62,48 +62,51 @@
6262
ACTIONS = {
6363
"get_ecs_troubleshooting_guidance": {
6464
"func": get_ecs_troubleshooting_guidance,
65-
"required_params": ["app_name"],
66-
"optional_params": ["symptoms_description"],
67-
"transformer": lambda app_name, params: {
68-
"app_name": app_name,
65+
"required_params": ["ecs_cluster_name"],
66+
"optional_params": ["ecs_service_name", "symptoms_description"],
67+
"transformer": lambda params: {
68+
"cluster_name": params["ecs_cluster_name"],
69+
"service_name": params.get("ecs_service_name"),
6970
"symptoms_description": params.get("symptoms_description"),
7071
},
7172
"description": "Initial assessment and data collection",
7273
"param_descriptions": {
73-
"app_name": "The name of the application/stack to troubleshoot",
74-
"symptoms_description": "Description of symptoms experienced by the user",
74+
"ecs_cluster_name": "The name of the ECS Cluster to troubleshoot",
75+
"ecs_service_name": "The name of the ECS Service to troubleshoot (optional)",
76+
"symptoms_description": "Description of symptoms experienced by the uhser",
7577
},
7678
"example": (
7779
'action="get_ecs_troubleshooting_guidance", '
78-
'parameters={"symptoms_description": "ALB returning 503 errors"}'
80+
'parameters={"ecs_cluster_name": "my-cluster", "ecs_service_name": "my-service", '
81+
'"symptoms_description": "ALB returning 503 errors"}'
7982
),
8083
},
8184
"fetch_cloudformation_status": {
8285
"func": fetch_cloudformation_status,
83-
"required_params": ["stack_id"],
86+
"required_params": ["cfn_stack_name"],
8487
"optional_params": [],
85-
"transformer": lambda app_name, params: {"stack_id": params.get("stack_id", app_name)},
86-
"description": "Infrastructure-level diagnostics for CloudFormation stacks",
87-
"param_descriptions": {"stack_id": "The CloudFormation stack identifier to analyze"},
88-
"example": 'action="fetch_cloudformation_status", parameters={"stack_id": "my-app-stack"}',
88+
"transformer": lambda params: {"stack_id": params.get("cfn_stack_name")},
89+
"description": "Infrastructure-level diagnostics for CloudFormation Stacks",
90+
"param_descriptions": {"cfn_stack_name": "The CloudFormation Stack identifier to analyze"},
91+
"example": (
92+
'action="fetch_cloudformation_status", parameters={"cfn_stack_name": "my-app-stack"}'
93+
),
8994
},
9095
"fetch_service_events": {
9196
"func": fetch_service_events,
92-
"required_params": ["app_name", "cluster_name", "service_name"],
97+
"required_params": ["ecs_cluster_name", "ecs_service_name"],
9398
"optional_params": ["time_window", "start_time", "end_time"],
94-
"transformer": lambda app_name, params: {
95-
"app_name": app_name,
96-
"cluster_name": params["cluster_name"],
97-
"service_name": params["service_name"],
99+
"transformer": lambda params: {
100+
"cluster_name": params["ecs_cluster_name"],
101+
"service_name": params["ecs_service_name"],
98102
"time_window": params.get("time_window", 3600),
99103
"start_time": params.get("start_time"),
100104
"end_time": params.get("end_time"),
101105
},
102-
"description": "Service-level diagnostics for ECS services",
106+
"description": "Service-level diagnostics for ECS Services",
103107
"param_descriptions": {
104-
"app_name": "The name of the application to analyze",
105-
"cluster_name": "The name of the ECS cluster",
106-
"service_name": "The name of the ECS service to analyze",
108+
"ecs_cluster_name": "The name of the ECS Cluster",
109+
"ecs_service_name": "The name of the ECS Service to analyze",
107110
"time_window": "Time window in seconds to look back for events (default: 3600)",
108111
"start_time": (
109112
"Explicit start time for the analysis window "
@@ -116,25 +119,23 @@
116119
},
117120
"example": (
118121
'action="fetch_service_events", '
119-
'parameters={"cluster_name": "my-cluster", "service_name": "my-service", '
122+
'parameters={"ecs_cluster_name": "my-cluster", "ecs_service_name": "my-service", '
120123
'"time_window": 7200}'
121124
),
122125
},
123126
"fetch_task_failures": {
124127
"func": fetch_task_failures,
125-
"required_params": ["app_name", "cluster_name"],
128+
"required_params": ["ecs_cluster_name"],
126129
"optional_params": ["time_window", "start_time", "end_time"],
127-
"transformer": lambda app_name, params: {
128-
"app_name": app_name,
129-
"cluster_name": params["cluster_name"],
130+
"transformer": lambda params: {
131+
"cluster_name": params["ecs_cluster_name"],
130132
"time_window": params.get("time_window", 3600),
131133
"start_time": params.get("start_time"),
132134
"end_time": params.get("end_time"),
133135
},
134-
"description": "Task-level diagnostics for ECS task failures",
136+
"description": "Task-level diagnostics for ECS Task failures",
135137
"param_descriptions": {
136-
"app_name": "The name of the application to analyze",
137-
"cluster_name": "The name of the ECS cluster",
138+
"ecs_cluster_name": "The name of the ECS Cluster",
138139
"time_window": "Time window in seconds to look back for failures (default: 3600)",
139140
"start_time": (
140141
"Explicit start time for the analysis window "
@@ -147,29 +148,33 @@
147148
},
148149
"example": (
149150
'action="fetch_task_failures", '
150-
'parameters={"cluster_name": "my-cluster", "time_window": 3600}'
151+
'parameters={"ecs_cluster_name": "my-cluster", "time_window": 3600}'
151152
),
152153
},
153154
"fetch_task_logs": {
154155
"func": fetch_task_logs,
155-
"required_params": ["app_name", "cluster_name"],
156-
"optional_params": ["task_id", "time_window", "filter_pattern", "start_time", "end_time"],
157-
"transformer": lambda app_name, params: {
158-
"app_name": app_name,
159-
"cluster_name": params["cluster_name"],
160-
"task_id": params.get("task_id"),
156+
"required_params": ["ecs_cluster_name"],
157+
"optional_params": [
158+
"ecs_task_id",
159+
"time_window",
160+
"filter_pattern",
161+
"start_time",
162+
"end_time",
163+
],
164+
"transformer": lambda params: {
165+
"cluster_name": params["ecs_cluster_name"],
166+
"task_id": params.get("ecs_task_id"),
161167
"time_window": params.get("time_window", 3600),
162168
"filter_pattern": params.get("filter_pattern"),
163169
"start_time": params.get("start_time"),
164170
"end_time": params.get("end_time"),
165171
},
166-
"description": "Application-level diagnostics through CloudWatch logs",
172+
"description": "Application-level diagnostics through CloudWatch Logs",
167173
"param_descriptions": {
168-
"app_name": "The name of the application to analyze",
169-
"cluster_name": "The name of the ECS cluster",
170-
"task_id": "Specific task ID to retrieve logs for",
174+
"ecs_cluster_name": "The name of the ECS Cluster",
175+
"ecs_task_id": "Specific ECS Task ID to retrieve logs for",
171176
"time_window": "Time window in seconds to look back for logs (default: 3600)",
172-
"filter_pattern": "CloudWatch logs filter pattern",
177+
"filter_pattern": "CloudWatch Logs filter pattern",
173178
"start_time": (
174179
"Explicit start time for the analysis window "
175180
"(UTC, takes precedence over time_window if provided)"
@@ -181,38 +186,61 @@
181186
},
182187
"example": (
183188
'action="fetch_task_logs", '
184-
'parameters={"cluster_name": "my-cluster", "filter_pattern": "ERROR", '
189+
'parameters={"ecs_cluster_name": "my-cluster", "filter_pattern": "ERROR", '
185190
'"time_window": 1800}'
186191
),
187192
},
188193
"detect_image_pull_failures": {
189194
"func": detect_image_pull_failures,
190-
"required_params": ["app_name"],
191-
"optional_params": [],
192-
"transformer": lambda app_name, params: {"app_name": app_name},
195+
"required_params": [], # No single required param, but need at least one combo
196+
"optional_params": [
197+
"ecs_cluster_name",
198+
"ecs_service_name",
199+
"cfn_stack_name",
200+
"family_prefix",
201+
"ecs_task_id",
202+
],
203+
"transformer": lambda params: {
204+
"cluster_name": params.get("ecs_cluster_name"),
205+
"service_name": params.get("ecs_service_name"),
206+
"stack_name": params.get("cfn_stack_name"),
207+
"family_prefix": params.get("family_prefix"),
208+
"task_id": params.get("ecs_task_id"),
209+
},
193210
"description": "Specialized tool for detecting container image pull failures",
194-
"param_descriptions": {"app_name": "Application name to check for image pull failures"},
195-
"example": 'action="detect_image_pull_failures", parameters={}',
211+
"param_descriptions": {
212+
"ecs_cluster_name": (
213+
"Name of the ECS Cluster (required if ecs_service_name/ecs_task_id provided)"
214+
),
215+
"ecs_service_name": "Name of the ECS Service (requires ecs_cluster_name)",
216+
"cfn_stack_name": "Name of the CloudFormation Stack to find related Task Definitions",
217+
"family_prefix": "Prefix to filter Task Definition families (e.g., 'my-app')",
218+
"ecs_task_id": (
219+
"ID of an ECS Task to get its Task Definition (requires ecs_cluster_name)"
220+
),
221+
},
222+
"example": (
223+
'action="detect_image_pull_failures", '
224+
'parameters={"ecs_cluster_name": "my-cluster", "ecs_service_name": "my-service"}'
225+
),
196226
},
197227
"fetch_network_configuration": {
198228
"func": fetch_network_configuration,
199-
"required_params": ["app_name"],
200-
"optional_params": ["vpc_id", "cluster_name"],
201-
"transformer": lambda app_name, params: {
202-
"app_name": app_name,
229+
"required_params": ["ecs_cluster_name"],
230+
"optional_params": ["vpc_id"],
231+
"transformer": lambda params: {
232+
"cluster_name": params["ecs_cluster_name"],
203233
"vpc_id": params.get("vpc_id"),
204-
"cluster_name": params.get("cluster_name"),
205234
},
206235
"description": "Network-level diagnostics for ECS deployments",
207236
"param_descriptions": {
208-
"app_name": "The name of the application to analyze",
209-
"vpc_id": "Specific VPC ID to analyze",
210-
"cluster_name": "Specific ECS cluster name",
237+
"ecs_cluster_name": "Name of the ECS Cluster to analyze",
238+
"vpc_id": "Specific VPC ID to analyze (optional)",
211239
},
212240
"example": (
213241
'action="fetch_network_configuration", '
214-
'parameters={"vpc_id": "vpc-12345678", '
215-
'"cluster_name": "my-cluster"}'
242+
'parameters={"ecs_cluster_name": "my-cluster", '
243+
'"vpc_id": "vpc-12345678"}'
216244
),
217245
},
218246
}
@@ -284,7 +312,6 @@ def generate_troubleshooting_docs():
284312
doc_footer = """```
285313
286314
Parameters:
287-
app_name: Application/stack name (required for most actions)
288315
action: The troubleshooting action to perform (see available actions above)
289316
parameters: Action-specific parameters (see parameter specifications above)
290317
@@ -311,17 +338,29 @@ def _validate_action(action: str) -> None:
311338
raise ValueError(f"Invalid action '{action}'. Valid actions: {valid_actions}")
312339

313340

314-
def _validate_parameters(action: str, app_name: Optional[str], parameters: Dict[str, Any]) -> None:
341+
def _validate_parameters(action: str, parameters: Dict[str, Any]) -> None:
315342
"""Validate required parameters for the given action."""
316343
required = ACTIONS[action]["required_params"]
317344

318-
# Check app_name if required
319-
if "app_name" in required and (not app_name or not app_name.strip()):
320-
raise ValueError(f"app_name is required for action '{action}'")
321-
322-
# Check other required parameters
345+
# Special case for detect_image_pull_failures which needs at least one of several combinations
346+
if action == "detect_image_pull_failures":
347+
if not any(
348+
[
349+
(parameters.get("ecs_cluster_name") and parameters.get("ecs_service_name")),
350+
(parameters.get("ecs_cluster_name") and parameters.get("ecs_task_id")),
351+
parameters.get("cfn_stack_name"),
352+
parameters.get("family_prefix"),
353+
]
354+
):
355+
raise ValueError(
356+
"At least one of: ecs_cluster_name+ecs_service_name, ecs_cluster_name+ecs_task_id, "
357+
"cfn_stack_name, or family_prefix must be provided for 'detect_image_pull_failures'"
358+
)
359+
return
360+
361+
# Check required parameters
323362
for param in required:
324-
if param != "app_name" and param not in parameters:
363+
if param not in parameters:
325364
raise ValueError(f"Missing required parameter '{param}' for action '{action}'")
326365

327366

@@ -330,7 +369,6 @@ def _validate_parameters(action: str, app_name: Optional[str], parameters: Dict[
330369

331370

332371
async def ecs_troubleshooting_tool(
333-
app_name: Optional[str] = None,
334372
action: TroubleshootingAction = "get_ecs_troubleshooting_guidance",
335373
parameters: Optional[Dict[str, Any]] = None,
336374
) -> Dict[str, Any]:
@@ -342,7 +380,6 @@ async def ecs_troubleshooting_tool(
342380
to perform.
343381
344382
Args:
345-
app_name: Application/stack name (required for most actions)
346383
action: The troubleshooting action to perform
347384
parameters: Action-specific parameters
348385
@@ -383,13 +420,13 @@ async def ecs_troubleshooting_tool(
383420
}
384421

385422
# Validate parameters
386-
_validate_parameters(action, app_name, parameters)
423+
_validate_parameters(action, parameters)
387424

388425
# Get action configuration
389426
action_config = ACTIONS[action]
390427

391428
# Transform parameters using action-specific transformer
392-
func_params = action_config["transformer"](app_name, parameters)
429+
func_params = action_config["transformer"](parameters)
393430

394431
# Call the function and await it if it's a coroutine
395432
result = action_config["func"](**func_params)

0 commit comments

Comments
 (0)