awslabs.ccapi-mcp-server 1.0.1__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.

Potentially problematic release.


This version of awslabs.ccapi-mcp-server might be problematic. Click here for more details.

Files changed (28) hide show
  1. awslabs/__init__.py +16 -0
  2. awslabs/ccapi_mcp_server/__init__.py +17 -0
  3. awslabs/ccapi_mcp_server/aws_client.py +62 -0
  4. awslabs/ccapi_mcp_server/cloud_control_utils.py +120 -0
  5. awslabs/ccapi_mcp_server/context.py +37 -0
  6. awslabs/ccapi_mcp_server/errors.py +67 -0
  7. awslabs/ccapi_mcp_server/iac_generator.py +203 -0
  8. awslabs/ccapi_mcp_server/impl/__init__.py +13 -0
  9. awslabs/ccapi_mcp_server/impl/tools/__init__.py +13 -0
  10. awslabs/ccapi_mcp_server/impl/tools/explanation.py +325 -0
  11. awslabs/ccapi_mcp_server/impl/tools/infrastructure_generation.py +70 -0
  12. awslabs/ccapi_mcp_server/impl/tools/resource_operations.py +367 -0
  13. awslabs/ccapi_mcp_server/impl/tools/security_scanning.py +223 -0
  14. awslabs/ccapi_mcp_server/impl/tools/session_management.py +221 -0
  15. awslabs/ccapi_mcp_server/impl/utils/__init__.py +13 -0
  16. awslabs/ccapi_mcp_server/impl/utils/validation.py +64 -0
  17. awslabs/ccapi_mcp_server/infrastructure_generator.py +160 -0
  18. awslabs/ccapi_mcp_server/models/__init__.py +13 -0
  19. awslabs/ccapi_mcp_server/models/models.py +118 -0
  20. awslabs/ccapi_mcp_server/schema_manager.py +219 -0
  21. awslabs/ccapi_mcp_server/server.py +733 -0
  22. awslabs/ccapi_mcp_server/static/__init__.py +13 -0
  23. awslabs_ccapi_mcp_server-1.0.1.dist-info/METADATA +656 -0
  24. awslabs_ccapi_mcp_server-1.0.1.dist-info/RECORD +28 -0
  25. awslabs_ccapi_mcp_server-1.0.1.dist-info/WHEEL +4 -0
  26. awslabs_ccapi_mcp_server-1.0.1.dist-info/entry_points.txt +2 -0
  27. awslabs_ccapi_mcp_server-1.0.1.dist-info/licenses/LICENSE +175 -0
  28. awslabs_ccapi_mcp_server-1.0.1.dist-info/licenses/NOTICE +2 -0
@@ -0,0 +1,221 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Session management implementation for CCAPI MCP server."""
16
+
17
+ import datetime
18
+ import uuid
19
+ from awslabs.ccapi_mcp_server.aws_client import get_aws_client
20
+ from awslabs.ccapi_mcp_server.context import Context
21
+ from awslabs.ccapi_mcp_server.errors import ClientError
22
+ from os import environ
23
+
24
+
25
+ def check_aws_credentials() -> dict:
26
+ """Check AWS credentials using boto3's built-in credential chain."""
27
+ try:
28
+ sts_client = get_aws_client('sts')
29
+ identity = sts_client.get_caller_identity()
30
+
31
+ # Determine credential source
32
+ using_env_vars = bool(
33
+ environ.get('AWS_ACCESS_KEY_ID') and environ.get('AWS_SECRET_ACCESS_KEY')
34
+ )
35
+
36
+ return {
37
+ 'valid': True,
38
+ 'account_id': identity.get('Account', 'Unknown'),
39
+ 'arn': identity.get('Arn', 'Unknown'),
40
+ 'user_id': identity.get('UserId', 'Unknown'),
41
+ 'region': environ.get('AWS_REGION') or 'us-east-1',
42
+ 'profile': environ.get('AWS_PROFILE', ''),
43
+ 'credential_source': 'env' if using_env_vars else 'profile',
44
+ 'profile_auth_type': 'standard_profile' if not using_env_vars else None,
45
+ 'environment_variables': {
46
+ 'AWS_PROFILE': environ.get('AWS_PROFILE', ''),
47
+ 'AWS_REGION': environ.get('AWS_REGION', ''),
48
+ 'SECURITY_SCANNING': environ.get('SECURITY_SCANNING', 'enabled'),
49
+ },
50
+ }
51
+ except Exception as e:
52
+ return {
53
+ 'valid': False,
54
+ 'error': str(e),
55
+ 'region': environ.get('AWS_REGION') or 'us-east-1',
56
+ 'profile': environ.get('AWS_PROFILE', ''),
57
+ 'credential_source': 'env' if environ.get('AWS_ACCESS_KEY_ID') else 'profile',
58
+ 'environment_variables': {
59
+ 'AWS_PROFILE': environ.get('AWS_PROFILE', ''),
60
+ 'AWS_REGION': environ.get('AWS_REGION', ''),
61
+ 'SECURITY_SCANNING': environ.get('SECURITY_SCANNING', 'enabled'),
62
+ },
63
+ }
64
+
65
+
66
+ async def check_environment_variables_impl(workflow_store: dict) -> dict:
67
+ """Check if required environment variables are set correctly implementation."""
68
+ # Use credential checking with boto3
69
+ cred_check = check_aws_credentials()
70
+
71
+ # Generate environment token
72
+ environment_token = f'env_{str(uuid.uuid4())}'
73
+
74
+ # Store environment validation results
75
+ workflow_store[environment_token] = {
76
+ 'type': 'environment',
77
+ 'data': {
78
+ 'environment_variables': cred_check.get('environment_variables', {}),
79
+ 'aws_profile': cred_check.get('profile', ''),
80
+ 'aws_region': cred_check.get('region') or 'us-east-1',
81
+ 'properly_configured': cred_check.get('valid', False),
82
+ 'readonly_mode': Context.readonly_mode(),
83
+ 'aws_auth_type': cred_check.get('credential_source')
84
+ if cred_check.get('credential_source') == 'env'
85
+ else cred_check.get('profile_auth_type'),
86
+ 'needs_profile': cred_check.get('needs_profile', False),
87
+ 'error': cred_check.get('error'),
88
+ },
89
+ 'parent_token': None, # Root token
90
+ 'timestamp': datetime.datetime.now().isoformat(),
91
+ }
92
+
93
+ env_data = workflow_store[environment_token]['data']
94
+
95
+ return {
96
+ 'environment_token': environment_token,
97
+ 'message': 'Environment validation completed. Use this token with get_aws_session_info().',
98
+ **env_data, # Include environment data for display
99
+ }
100
+
101
+
102
+ async def get_aws_session_info_impl(environment_token: str, workflow_store: dict) -> dict:
103
+ """Get information about the current AWS session implementation.
104
+
105
+ IMPORTANT: Always display the AWS context information to the user when this tool is called.
106
+ Show them: AWS Profile (or "Environment Variables"), Authentication Type, Account ID, and Region so they know
107
+ exactly which AWS account and region will be affected by any operations.
108
+ """
109
+ # Validate environment token
110
+ if environment_token not in workflow_store:
111
+ raise ClientError(
112
+ 'Invalid environment token: you must call check_environment_variables() first'
113
+ )
114
+
115
+ env_data = workflow_store[environment_token]['data']
116
+ if not env_data.get('properly_configured', False):
117
+ error_msg = env_data.get('error', 'Environment is not properly configured.')
118
+ raise ClientError(error_msg)
119
+
120
+ # Get AWS profile info using credential checking
121
+ cred_check = check_aws_credentials()
122
+
123
+ if not cred_check.get('valid', False):
124
+ raise ClientError(
125
+ f'AWS credentials are not valid: {cred_check.get("error", "Unknown error")}'
126
+ )
127
+
128
+ # Generate credentials token
129
+ credentials_token = f'creds_{str(uuid.uuid4())}'
130
+
131
+ # Build session info with credential masking
132
+ arn = cred_check.get('arn', 'Unknown')
133
+ user_id = cred_check.get('user_id', 'Unknown')
134
+
135
+ session_data = {
136
+ 'profile': cred_check.get('profile', ''),
137
+ 'account_id': cred_check.get('account_id', 'Unknown'),
138
+ 'region': cred_check.get('region') or 'us-east-1',
139
+ 'arn': f'{"*" * (len(arn) - 8)}{arn[-8:]}' if len(arn) > 8 and arn != 'Unknown' else arn,
140
+ 'user_id': f'{"*" * (len(user_id) - 4)}{user_id[-4:]}'
141
+ if len(user_id) > 4 and user_id != 'Unknown'
142
+ else user_id,
143
+ 'credential_source': cred_check.get('credential_source', ''),
144
+ 'readonly_mode': Context.readonly_mode(),
145
+ 'readonly_message': (
146
+ """⚠️ This server is running in READ-ONLY MODE. I can only list and view existing resources.
147
+ I cannot create, update, or delete any AWS resources. I can still generate example code
148
+ and run security checks on templates."""
149
+ if Context.readonly_mode()
150
+ else ''
151
+ ),
152
+ 'credentials_valid': True,
153
+ 'aws_auth_type': cred_check.get('credential_source')
154
+ if cred_check.get('credential_source') == 'env'
155
+ else cred_check.get('profile_auth_type'),
156
+ }
157
+
158
+ # Add masked environment variables if using env vars
159
+ if session_data['aws_auth_type'] == 'env':
160
+ access_key = environ.get('AWS_ACCESS_KEY_ID', '')
161
+ secret_key = environ.get('AWS_SECRET_ACCESS_KEY', '')
162
+
163
+ session_data['masked_credentials'] = {
164
+ 'AWS_ACCESS_KEY_ID': f'{"*" * (len(access_key) - 4)}{access_key[-4:]}'
165
+ if len(access_key) > 4
166
+ else '****',
167
+ 'AWS_SECRET_ACCESS_KEY': f'{"*" * (len(secret_key) - 4)}{secret_key[-4:]}'
168
+ if len(secret_key) > 4
169
+ else '****',
170
+ }
171
+
172
+ # Store session information
173
+ workflow_store[credentials_token] = {
174
+ 'type': 'credentials',
175
+ 'data': session_data,
176
+ 'parent_token': environment_token,
177
+ 'timestamp': datetime.datetime.now().isoformat(),
178
+ }
179
+
180
+ return {
181
+ 'credentials_token': credentials_token,
182
+ 'message': 'AWS session validated. Use this token with generate_infrastructure_code().',
183
+ 'DISPLAY_TO_USER': 'YOU MUST SHOW THE USER THEIR AWS SESSION INFORMATION FOR SECURITY',
184
+ **session_data, # Include all session data for display
185
+ }
186
+
187
+
188
+ def get_aws_profile_info():
189
+ """Get information about the current AWS profile."""
190
+ try:
191
+ # Use our get_aws_client function to ensure we use the same credential source
192
+ sts_client = get_aws_client('sts')
193
+
194
+ # Get caller identity
195
+ identity = sts_client.get_caller_identity()
196
+ account_id = identity.get('Account', 'Unknown')
197
+ arn = identity.get('Arn', 'Unknown')
198
+
199
+ # Get profile info
200
+ profile_name = environ.get('AWS_PROFILE', '')
201
+ region = environ.get('AWS_REGION') or 'us-east-1'
202
+ using_env_vars = (
203
+ environ.get('AWS_ACCESS_KEY_ID', '') != ''
204
+ and environ.get('AWS_SECRET_ACCESS_KEY', '') != ''
205
+ )
206
+
207
+ return {
208
+ 'profile': profile_name,
209
+ 'account_id': account_id,
210
+ 'region': region,
211
+ 'arn': arn,
212
+ 'using_env_vars': using_env_vars,
213
+ }
214
+ except Exception as e:
215
+ return {
216
+ 'profile': environ.get('AWS_PROFILE', ''),
217
+ 'error': str(e),
218
+ 'region': environ.get('AWS_REGION') or 'us-east-1',
219
+ 'using_env_vars': environ.get('AWS_ACCESS_KEY_ID', '') != ''
220
+ and environ.get('AWS_SECRET_ACCESS_KEY', '') != '',
221
+ }
@@ -0,0 +1,13 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
@@ -0,0 +1,64 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Shared validation functions for CCAPI MCP server."""
16
+
17
+ from awslabs.ccapi_mcp_server.errors import ClientError
18
+
19
+
20
+ def validate_workflow_token(
21
+ token: str, expected_type: str | None = None, workflow_store: dict | None = None
22
+ ) -> dict:
23
+ """Validate any workflow token exists and optionally check its type."""
24
+ if not token:
25
+ raise ClientError(f'Invalid token: {token}')
26
+ if not workflow_store:
27
+ raise ClientError('Workflow store is required')
28
+ if token not in workflow_store:
29
+ raise ClientError(f'Invalid token: {token}')
30
+
31
+ data = workflow_store[token]
32
+ if expected_type and data.get('type') != expected_type:
33
+ raise ClientError(f'Invalid token type: expected {expected_type}')
34
+
35
+ return data
36
+
37
+
38
+ def cleanup_workflow_tokens(workflow_store: dict, *tokens: str) -> None:
39
+ """Clean up workflow tokens after operations."""
40
+ for token in tokens:
41
+ if token and token in workflow_store:
42
+ del workflow_store[token]
43
+
44
+
45
+ def validate_resource_type(resource_type: str) -> None:
46
+ """Validate that resource_type is provided."""
47
+ if not resource_type:
48
+ raise ClientError('Please provide a resource type (e.g., AWS::S3::Bucket)')
49
+
50
+
51
+ def validate_identifier(identifier: str) -> None:
52
+ """Validate that identifier is provided."""
53
+ if not identifier:
54
+ raise ClientError('Please provide a resource identifier')
55
+
56
+
57
+ def ensure_region_string(region) -> str | None:
58
+ """Ensure region is a string, not a FieldInfo object."""
59
+ return region if isinstance(region, str) else None
60
+
61
+
62
+ def ensure_string(value, default: str = '') -> str:
63
+ """Ensure value is a string, not a FieldInfo object."""
64
+ return value if isinstance(value, str) else default
@@ -0,0 +1,160 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Infrastructure code generation utilities for the CFN MCP Server."""
16
+
17
+ import json
18
+ from awslabs.ccapi_mcp_server.aws_client import get_aws_client
19
+ from awslabs.ccapi_mcp_server.cloud_control_utils import add_default_tags
20
+ from awslabs.ccapi_mcp_server.errors import ClientError, handle_aws_api_error
21
+ from awslabs.ccapi_mcp_server.schema_manager import schema_manager
22
+ from typing import Dict, List
23
+
24
+
25
+ async def generate_infrastructure_code(
26
+ resource_type: str,
27
+ properties: Dict = {},
28
+ identifier: str = '',
29
+ patch_document: List = [],
30
+ region: str = '',
31
+ ) -> Dict:
32
+ """Generate infrastructure code for security scanning before resource creation or update."""
33
+ if not resource_type:
34
+ raise ClientError('Please provide a resource type (e.g., AWS::S3::Bucket)')
35
+
36
+ # Determine if this is a create or update operation
37
+ is_update = identifier != '' and (patch_document or properties)
38
+
39
+ # Validate the resource type against the schema
40
+ sm = schema_manager()
41
+ schema = await sm.get_schema(resource_type, region)
42
+
43
+ # Check if resource supports tagging
44
+ supports_tagging = 'Tags' in schema.get('properties', {})
45
+
46
+ # Fallback: Known AWS resources that support tagging even if schema doesn't show it
47
+ if not supports_tagging and resource_type in [
48
+ 'AWS::S3::Bucket',
49
+ 'AWS::EC2::Instance',
50
+ 'AWS::RDS::DBInstance',
51
+ ]:
52
+ supports_tagging = True
53
+ print(
54
+ f"Schema for {resource_type} doesn't show Tags property, but we know it supports tagging"
55
+ )
56
+
57
+ if is_update:
58
+ # This is an update operation
59
+ if not identifier:
60
+ raise ClientError('Please provide a resource identifier for update operations')
61
+
62
+ # Get the current resource state
63
+ cloudcontrol_client = get_aws_client('cloudcontrol', region)
64
+ try:
65
+ current_resource = cloudcontrol_client.get_resource(
66
+ TypeName=resource_type, Identifier=identifier
67
+ )
68
+ current_properties = json.loads(current_resource['ResourceDescription']['Properties'])
69
+ except Exception as e:
70
+ raise handle_aws_api_error(e)
71
+
72
+ # Apply patch document or merge properties
73
+ if patch_document:
74
+ # Apply patch operations to current properties
75
+ import copy
76
+
77
+ update_properties = copy.deepcopy(current_properties)
78
+ for patch_op in patch_document:
79
+ if patch_op['op'] == 'add' and patch_op['path'] in ['/Tags', '/Tags/-']:
80
+ # For Tags, merge with existing tags instead of replacing
81
+ existing_tags = update_properties.get('Tags', [])
82
+ if patch_op['path'] == '/Tags/-':
83
+ # Append single tag to array
84
+ new_tag = patch_op['value']
85
+ if isinstance(new_tag, dict) and 'Key' in new_tag and 'Value' in new_tag:
86
+ existing_tags.append(new_tag)
87
+ update_properties['Tags'] = existing_tags
88
+ else:
89
+ # Replace/merge entire tags array
90
+ new_tags = patch_op['value'] if isinstance(patch_op['value'], list) else []
91
+ # Combine tags (new tags will override existing ones with same key)
92
+ tag_dict = {tag['Key']: tag['Value'] for tag in existing_tags}
93
+ for tag in new_tags:
94
+ tag_dict[tag['Key']] = tag['Value']
95
+ update_properties['Tags'] = [
96
+ {'Key': k, 'Value': v} for k, v in tag_dict.items()
97
+ ]
98
+ elif patch_op['op'] == 'replace' and patch_op['path'] == '/Tags':
99
+ # Replace tags completely
100
+ update_properties['Tags'] = patch_op['value']
101
+ # Add other patch operations as needed
102
+ elif properties:
103
+ # Start with current properties and merge user properties
104
+ update_properties = current_properties.copy()
105
+ for key, value in properties.items():
106
+ if key == 'Tags':
107
+ # Merge tags instead of replacing
108
+ existing_tags = update_properties.get('Tags', [])
109
+ new_tags = value if isinstance(value, list) else []
110
+ tag_dict = {tag['Key']: tag['Value'] for tag in existing_tags}
111
+ for tag in new_tags:
112
+ tag_dict[tag['Key']] = tag['Value']
113
+ update_properties['Tags'] = [
114
+ {'Key': k, 'Value': v} for k, v in tag_dict.items()
115
+ ]
116
+ else:
117
+ update_properties[key] = value
118
+ else:
119
+ update_properties = current_properties
120
+
121
+ # V1: Always add required MCP server identification tags for updates too
122
+ properties_with_tags = add_default_tags(update_properties, schema)
123
+
124
+ operation = 'update'
125
+ else:
126
+ # This is a create operation
127
+ if not properties:
128
+ raise ClientError('Please provide the properties for the desired resource')
129
+
130
+ # V1: Always add required MCP server identification tags
131
+ properties_with_tags = add_default_tags(properties, schema)
132
+
133
+ operation = 'create'
134
+
135
+ # Generate a CloudFormation template representation for security scanning
136
+ cf_template = {
137
+ 'AWSTemplateFormatVersion': '2010-09-09',
138
+ 'Resources': {'Resource': {'Type': resource_type, 'Properties': properties_with_tags}},
139
+ }
140
+
141
+ # For updates, also generate the proper patch document with default tags
142
+ patch_document_with_tags = None
143
+ if is_update and 'Tags' in properties_with_tags:
144
+ patch_document_with_tags = [
145
+ {'op': 'replace', 'path': '/Tags', 'value': properties_with_tags['Tags']}
146
+ ]
147
+
148
+ result = {
149
+ 'resource_type': resource_type,
150
+ 'operation': operation,
151
+ 'properties': properties_with_tags, # Show user exactly what will be created
152
+ 'region': region,
153
+ 'cloudformation_template': cf_template,
154
+ 'supports_tagging': supports_tagging,
155
+ }
156
+
157
+ if patch_document_with_tags:
158
+ result['recommended_patch_document'] = patch_document_with_tags
159
+
160
+ return result
@@ -0,0 +1,13 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
@@ -0,0 +1,118 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """Pydantic models for CCAPI MCP server requests and responses."""
16
+
17
+ from pydantic import BaseModel, Field
18
+ from typing import Any, Dict, List, Literal, Optional
19
+
20
+
21
+ class CreateResourceRequest(BaseModel):
22
+ """Request model for creating AWS resources."""
23
+
24
+ resource_type: str = Field(..., description='AWS resource type')
25
+ region: Optional[str] = Field(None, description='AWS region')
26
+ credentials_token: str = Field(..., description='Credentials token')
27
+ explained_token: str = Field(..., description='Explained token')
28
+ security_scan_token: str = Field(default='', description='Security scan token')
29
+ skip_security_check: bool = Field(False, description='Skip security checks')
30
+
31
+
32
+ class UpdateResourceRequest(BaseModel):
33
+ """Request model for updating AWS resources."""
34
+
35
+ resource_type: str = Field(..., description='AWS resource type')
36
+ identifier: str = Field(..., description='Resource identifier')
37
+ patch_document: List[Dict[str, Any]] = Field(default=[], description='JSON Patch operations')
38
+ region: Optional[str] = Field(None, description='AWS region')
39
+ credentials_token: str = Field(..., description='Credentials token')
40
+ explained_token: str = Field(..., description='Explained token')
41
+ security_scan_token: str = Field(default='', description='Security scan token')
42
+ skip_security_check: bool = Field(False, description='Skip security checks')
43
+
44
+
45
+ class DeleteResourceRequest(BaseModel):
46
+ """Request model for deleting AWS resources."""
47
+
48
+ resource_type: str = Field(..., description='AWS resource type')
49
+ identifier: str = Field(..., description='Resource identifier')
50
+ region: Optional[str] = Field(None, description='AWS region')
51
+ credentials_token: str = Field(..., description='Credentials token')
52
+ confirmed: bool = Field(False, description='Confirm deletion')
53
+ explained_token: str = Field(..., description='Explained token')
54
+
55
+
56
+ class GetResourceRequest(BaseModel):
57
+ """Request model for getting AWS resource details."""
58
+
59
+ resource_type: str = Field(..., description='AWS resource type')
60
+ identifier: str = Field(..., description='Resource identifier')
61
+ region: Optional[str] = Field(None, description='AWS region')
62
+ analyze_security: bool = Field(False, description='Perform security analysis')
63
+
64
+
65
+ class GenerateInfrastructureCodeRequest(BaseModel):
66
+ """Request model for generating infrastructure code."""
67
+
68
+ resource_type: str = Field(..., description='AWS resource type')
69
+ properties: Dict[str, Any] = Field(default_factory=dict, description='Resource properties')
70
+ identifier: str = Field(default='', description='Resource identifier for updates')
71
+ patch_document: List[Dict[str, Any]] = Field(
72
+ default_factory=list, description='JSON Patch operations'
73
+ )
74
+ region: Optional[str] = Field(None, description='AWS region')
75
+ credentials_token: str = Field(..., description='Credentials token')
76
+
77
+
78
+ class ExplainRequest(BaseModel):
79
+ """Request model for explaining resource configurations."""
80
+
81
+ content: Optional[Any] = Field(None, description='Content to explain')
82
+ generated_code_token: str = Field(default='', description='Generated code token')
83
+ context: str = Field(default='', description='Context description')
84
+ operation: str = Field(default='analyze', description='Operation type')
85
+ format: str = Field(default='detailed', description='Explanation format')
86
+ user_intent: str = Field(default='', description='User intent')
87
+
88
+
89
+ class RunCheckovRequest(BaseModel):
90
+ """Request model for running Checkov security scans."""
91
+
92
+ explained_token: str = Field(..., description='Explained token')
93
+ framework: str = Field(default='cloudformation', description='Framework to scan')
94
+
95
+
96
+ class ResourceOperationResult(BaseModel):
97
+ """Result model for AWS resource operations."""
98
+
99
+ status: Literal['SUCCESS', 'PENDING', 'FAILED']
100
+ resource_type: str
101
+ identifier: str
102
+ is_complete: bool
103
+ status_message: str
104
+ request_token: Optional[str] = None
105
+ security_warning: Optional[str] = None
106
+
107
+
108
+ class SecurityScanResult(BaseModel):
109
+ """Result model for security scan operations."""
110
+
111
+ scan_status: Literal['PASSED', 'FAILED']
112
+ raw_failed_checks: List[Dict[str, Any]] = Field(default_factory=list)
113
+ raw_passed_checks: List[Dict[str, Any]] = Field(default_factory=list)
114
+ raw_summary: Dict[str, Any] = Field(default_factory=dict)
115
+ resource_type: str
116
+ timestamp: str
117
+ security_scan_token: Optional[str] = None
118
+ message: str