awslabs.cdk-mcp-server 0.0.10417__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.
Files changed (72) hide show
  1. awslabs/__init__.py +2 -0
  2. awslabs/cdk_mcp_server/__init__.py +8 -0
  3. awslabs/cdk_mcp_server/core/__init__.py +1 -0
  4. awslabs/cdk_mcp_server/core/resources.py +271 -0
  5. awslabs/cdk_mcp_server/core/search_utils.py +182 -0
  6. awslabs/cdk_mcp_server/core/server.py +74 -0
  7. awslabs/cdk_mcp_server/core/tools.py +324 -0
  8. awslabs/cdk_mcp_server/data/__init__.py +1 -0
  9. awslabs/cdk_mcp_server/data/cdk_nag_parser.py +331 -0
  10. awslabs/cdk_mcp_server/data/construct_descriptions.py +32 -0
  11. awslabs/cdk_mcp_server/data/genai_cdk_loader.py +423 -0
  12. awslabs/cdk_mcp_server/data/lambda_powertools_loader.py +48 -0
  13. awslabs/cdk_mcp_server/data/schema_generator.py +666 -0
  14. awslabs/cdk_mcp_server/data/solutions_constructs_parser.py +782 -0
  15. awslabs/cdk_mcp_server/server.py +7 -0
  16. awslabs/cdk_mcp_server/static/CDK_GENERAL_GUIDANCE.md +232 -0
  17. awslabs/cdk_mcp_server/static/CDK_NAG_GUIDANCE.md +192 -0
  18. awslabs/cdk_mcp_server/static/__init__.py +5 -0
  19. awslabs/cdk_mcp_server/static/bedrock/agent/actiongroups.md +137 -0
  20. awslabs/cdk_mcp_server/static/bedrock/agent/alias.md +39 -0
  21. awslabs/cdk_mcp_server/static/bedrock/agent/collaboration.md +91 -0
  22. awslabs/cdk_mcp_server/static/bedrock/agent/creation.md +149 -0
  23. awslabs/cdk_mcp_server/static/bedrock/agent/custom_orchestration.md +74 -0
  24. awslabs/cdk_mcp_server/static/bedrock/agent/overview.md +78 -0
  25. awslabs/cdk_mcp_server/static/bedrock/agent/prompt_override.md +70 -0
  26. awslabs/cdk_mcp_server/static/bedrock/bedrockguardrails.md +188 -0
  27. awslabs/cdk_mcp_server/static/bedrock/knowledgebases/chunking.md +137 -0
  28. awslabs/cdk_mcp_server/static/bedrock/knowledgebases/datasources.md +225 -0
  29. awslabs/cdk_mcp_server/static/bedrock/knowledgebases/kendra.md +81 -0
  30. awslabs/cdk_mcp_server/static/bedrock/knowledgebases/overview.md +116 -0
  31. awslabs/cdk_mcp_server/static/bedrock/knowledgebases/parsing.md +36 -0
  32. awslabs/cdk_mcp_server/static/bedrock/knowledgebases/transformation.md +30 -0
  33. awslabs/cdk_mcp_server/static/bedrock/knowledgebases/vector/aurora.md +185 -0
  34. awslabs/cdk_mcp_server/static/bedrock/knowledgebases/vector/creation.md +80 -0
  35. awslabs/cdk_mcp_server/static/bedrock/knowledgebases/vector/opensearch.md +56 -0
  36. awslabs/cdk_mcp_server/static/bedrock/knowledgebases/vector/pinecone.md +66 -0
  37. awslabs/cdk_mcp_server/static/bedrock/profiles.md +153 -0
  38. awslabs/cdk_mcp_server/static/genai_cdk/bedrock/agent/actiongroups.md +137 -0
  39. awslabs/cdk_mcp_server/static/genai_cdk/bedrock/agent/alias.md +39 -0
  40. awslabs/cdk_mcp_server/static/genai_cdk/bedrock/agent/collaboration.md +91 -0
  41. awslabs/cdk_mcp_server/static/genai_cdk/bedrock/agent/creation.md +149 -0
  42. awslabs/cdk_mcp_server/static/genai_cdk/bedrock/agent/custom_orchestration.md +74 -0
  43. awslabs/cdk_mcp_server/static/genai_cdk/bedrock/agent/overview.md +78 -0
  44. awslabs/cdk_mcp_server/static/genai_cdk/bedrock/agent/prompt_override.md +70 -0
  45. awslabs/cdk_mcp_server/static/genai_cdk/bedrock/bedrockguardrails.md +188 -0
  46. awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/chunking.md +137 -0
  47. awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/datasources.md +225 -0
  48. awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/kendra.md +81 -0
  49. awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/overview.md +116 -0
  50. awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/parsing.md +36 -0
  51. awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/transformation.md +30 -0
  52. awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/vector/aurora.md +185 -0
  53. awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/vector/creation.md +80 -0
  54. awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/vector/opensearch.md +56 -0
  55. awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/vector/pinecone.md +66 -0
  56. awslabs/cdk_mcp_server/static/genai_cdk/bedrock/profiles.md +153 -0
  57. awslabs/cdk_mcp_server/static/genai_cdk/opensearch-vectorindex/overview.md +135 -0
  58. awslabs/cdk_mcp_server/static/genai_cdk/opensearchserverless/overview.md +17 -0
  59. awslabs/cdk_mcp_server/static/lambda_powertools/bedrock.md +127 -0
  60. awslabs/cdk_mcp_server/static/lambda_powertools/cdk.md +99 -0
  61. awslabs/cdk_mcp_server/static/lambda_powertools/dependencies.md +45 -0
  62. awslabs/cdk_mcp_server/static/lambda_powertools/index.md +36 -0
  63. awslabs/cdk_mcp_server/static/lambda_powertools/insights.md +95 -0
  64. awslabs/cdk_mcp_server/static/lambda_powertools/logging.md +43 -0
  65. awslabs/cdk_mcp_server/static/lambda_powertools/metrics.md +93 -0
  66. awslabs/cdk_mcp_server/static/lambda_powertools/tracing.md +63 -0
  67. awslabs/cdk_mcp_server/static/opensearch-vectorindex/overview.md +135 -0
  68. awslabs/cdk_mcp_server/static/opensearchserverless/overview.md +17 -0
  69. awslabs_cdk_mcp_server-0.0.10417.dist-info/METADATA +14 -0
  70. awslabs_cdk_mcp_server-0.0.10417.dist-info/RECORD +72 -0
  71. awslabs_cdk_mcp_server-0.0.10417.dist-info/WHEEL +4 -0
  72. awslabs_cdk_mcp_server-0.0.10417.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,324 @@
1
+ """AWS CDK MCP tool handlers."""
2
+
3
+ import logging
4
+ import os
5
+ import re
6
+ from awslabs.cdk_mcp_server.core import search_utils
7
+ from awslabs.cdk_mcp_server.data.cdk_nag_parser import (
8
+ check_cdk_nag_suppressions,
9
+ get_rule,
10
+ )
11
+ from awslabs.cdk_mcp_server.data.genai_cdk_loader import (
12
+ list_available_constructs,
13
+ )
14
+ from awslabs.cdk_mcp_server.data.schema_generator import generate_bedrock_schema_from_file
15
+ from awslabs.cdk_mcp_server.data.solutions_constructs_parser import (
16
+ fetch_pattern_list,
17
+ get_pattern_info,
18
+ search_patterns,
19
+ )
20
+ from awslabs.cdk_mcp_server.static import (
21
+ CDK_GENERAL_GUIDANCE,
22
+ )
23
+ from mcp.server.fastmcp import Context
24
+ from typing import Any, Dict, List, Optional
25
+
26
+
27
+ # Set up logging
28
+ logger = logging.getLogger(__name__)
29
+
30
+
31
+ async def cdk_guidance(
32
+ ctx: Context,
33
+ ) -> str:
34
+ """Use this tool to get prescriptive CDK advice for building applications on AWS.
35
+
36
+ Args:
37
+ ctx: MCP context
38
+ """
39
+ return CDK_GENERAL_GUIDANCE
40
+
41
+
42
+ async def explain_cdk_nag_rule(
43
+ ctx: Context,
44
+ rule_id: str,
45
+ ) -> Dict[str, Any]:
46
+ """Explain a specific CDK Nag rule with AWS Well-Architected guidance.
47
+
48
+ CDK Nag is a crucial tool for ensuring your CDK applications follow AWS security best practices.
49
+
50
+ Basic implementation:
51
+ ```typescript
52
+ import { App } from 'aws-cdk-lib';
53
+ import { AwsSolutionsChecks } from 'cdk-nag';
54
+
55
+ const app = new App();
56
+ // Create your stack
57
+ const stack = new MyStack(app, 'MyStack');
58
+ // Apply CDK Nag
59
+ AwsSolutionsChecks.check(app);
60
+ ```
61
+
62
+ Optional integration patterns:
63
+
64
+ 1. Using environment variables:
65
+ ```typescript
66
+ if (process.env.ENABLE_CDK_NAG === 'true') {
67
+ AwsSolutionsChecks.check(app);
68
+ }
69
+ ```
70
+
71
+ 2. Using CDK context parameters:
72
+ ```typescript
73
+ 3. Environment-specific application:
74
+ ```typescript
75
+ const environment = app.node.tryGetContext('environment') || 'development';
76
+ if (['production', 'staging'].includes(environment)) {
77
+ AwsSolutionsChecks.check(stack);
78
+ }
79
+ ```
80
+
81
+ For more information on specific rule packs:
82
+ - Use resource `cdk-nag://rules/{rule_pack}` to get all rules for a specific pack
83
+ - Use resource `cdk-nag://warnings/{rule_pack}` to get warnings for a specific pack
84
+ - Use resource `cdk-nag://errors/{rule_pack}` to get errors for a specific pack
85
+
86
+ Args:
87
+ ctx: MCP context
88
+ rule_id: The CDK Nag rule ID (e.g., 'AwsSolutions-IAM4')
89
+
90
+ Returns:
91
+ Dictionary with detailed explanation and remediation steps
92
+ """
93
+ # Use the resource we created to fetch the rule information
94
+ try:
95
+ rule_content = await get_rule(rule_id)
96
+
97
+ # If the rule was found, return a structured response
98
+ if not rule_content.startswith('Rule'):
99
+ return {
100
+ 'rule_id': rule_id,
101
+ 'content': rule_content,
102
+ 'source': 'https://github.com/cdklabs/cdk-nag/blob/main/RULES.md',
103
+ 'status': 'success',
104
+ }
105
+ else:
106
+ # Rule not found
107
+ return {
108
+ 'rule_id': rule_id,
109
+ 'error': f'Rule {rule_id} not found in CDK Nag documentation.',
110
+ 'source': 'https://github.com/cdklabs/cdk-nag/blob/main/RULES.md',
111
+ 'status': 'not_found',
112
+ }
113
+ except Exception as e:
114
+ # Handle any errors
115
+ return {
116
+ 'rule_id': rule_id,
117
+ 'error': f'Failed to fetch rule information: {str(e)}',
118
+ 'source': 'https://github.com/cdklabs/cdk-nag/blob/main/RULES.md',
119
+ 'status': 'error',
120
+ }
121
+
122
+
123
+ async def check_cdk_nag_suppressions_tool(
124
+ ctx: Context,
125
+ code: Optional[str] = None,
126
+ file_path: Optional[str] = None,
127
+ ) -> Dict[str, Any]:
128
+ """Check if CDK code contains Nag suppressions that require human review.
129
+
130
+ Scans TypeScript/JavaScript code for NagSuppressions usage to ensure security
131
+ suppressions receive proper human oversight and justification.
132
+
133
+ Args:
134
+ ctx: MCP context
135
+ code: CDK code to analyze (TypeScript/JavaScript)
136
+ file_path: Path to a file containing CDK code to analyze
137
+
138
+ Returns:
139
+ Analysis results with suppression details and security guidance
140
+ """
141
+ # Use the imported function from cdk_nag_parser.py
142
+ return check_cdk_nag_suppressions(code=code, file_path=file_path)
143
+
144
+
145
+ async def bedrock_schema_generator_from_file(
146
+ ctx: Context, lambda_code_path: str, output_path: str
147
+ ) -> Dict[str, Any]:
148
+ """Generate OpenAPI schema for Bedrock Agent Action Groups from a file.
149
+
150
+ This tool converts a Lambda file with BedrockAgentResolver into a Bedrock-compatible
151
+ OpenAPI schema. It uses a progressive approach to handle common issues:
152
+ 1. Direct import of the Lambda file
153
+ 2. Simplified version with problematic imports commented out
154
+ 3. Fallback script generation if needed
155
+
156
+ Args:
157
+ ctx: MCP context
158
+ lambda_code_path: Path to Python file containing BedrockAgentResolver app
159
+ output_path: Where to save the generated schema
160
+
161
+ Returns:
162
+ Dictionary with schema generation results, including status, path to generated schema,
163
+ and diagnostic information if errors occurred
164
+ """
165
+ # Ensure the output directory exists
166
+ os.makedirs(os.path.dirname(os.path.abspath(output_path)), exist_ok=True)
167
+
168
+ # Generate the schema
169
+ result = generate_bedrock_schema_from_file(
170
+ lambda_code_path=lambda_code_path,
171
+ output_path=output_path,
172
+ )
173
+
174
+ return result
175
+
176
+
177
+ async def get_aws_solutions_construct_pattern(
178
+ ctx: Context,
179
+ pattern_name: Optional[str] = None,
180
+ services: Optional[List[str]] = None,
181
+ ) -> Dict[str, Any]:
182
+ """Search and discover AWS Solutions Constructs patterns.
183
+
184
+ AWS Solutions Constructs are vetted architecture patterns that combine multiple
185
+ AWS services to solve common use cases following AWS Well-Architected best practices.
186
+
187
+ Key benefits:
188
+ - Accelerated Development: Implement common patterns without boilerplate code
189
+ - Best Practices Built-in: Security, reliability, and performance best practices
190
+ - Reduced Complexity: Simplified interfaces for multi-service architectures
191
+ - Well-Architected: Patterns follow AWS Well-Architected Framework principles
192
+
193
+ When to use Solutions Constructs:
194
+ - Implementing common architecture patterns (e.g., API + Lambda + DynamoDB)
195
+ - You want secure defaults and best practices applied automatically
196
+ - You need to quickly prototype or build production-ready infrastructure
197
+
198
+ This tool provides metadata about patterns. For complete documentation,
199
+ use the resource URI returned in the 'documentation_uri' field.
200
+
201
+ Args:
202
+ ctx: MCP context
203
+ pattern_name: Optional name of the specific pattern (e.g., 'aws-lambda-dynamodb')
204
+ services: Optional list of AWS services to search for patterns that use them
205
+ (e.g., ['lambda', 'dynamodb'])
206
+
207
+ Returns:
208
+ Dictionary with pattern metadata including description, services, and documentation URI
209
+ """
210
+ if pattern_name:
211
+ result = await get_pattern_info(pattern_name)
212
+ return result
213
+ elif services:
214
+ patterns = await search_patterns(services)
215
+ return {
216
+ 'results': patterns,
217
+ 'count': len(patterns),
218
+ 'status': 'success',
219
+ 'metadata': {'services_searched': services},
220
+ }
221
+ else:
222
+ available_patterns = await fetch_pattern_list()
223
+ return {
224
+ 'error': 'Either pattern_name or services must be provided',
225
+ 'available_patterns': available_patterns,
226
+ 'status': 'error',
227
+ }
228
+
229
+
230
+ async def search_genai_cdk_constructs(
231
+ ctx: Context,
232
+ query: Optional[str] = None,
233
+ construct_type: Optional[str] = None,
234
+ ) -> Dict[str, Any]:
235
+ """Search for GenAI CDK constructs by name or type.
236
+
237
+ The search is flexible and will match any of your search terms (OR logic).
238
+ It handles common variations like singular/plural forms and terms with/without spaces.
239
+
240
+ Examples:
241
+ - "bedrock agent" - Returns all agent-related constructs
242
+ - "knowledgebase vector" - Returns knowledge base constructs related to vector stores
243
+ - "agent actiongroups" - Returns action groups for agents
244
+
245
+ Args:
246
+ ctx: MCP context
247
+ query: Search term(s) to find constructs by name or description
248
+ construct_type: Optional filter by construct type ('bedrock', 'opensearchserverless', etc.)
249
+
250
+ Returns:
251
+ Dictionary with matching constructs and resource URIs
252
+ """
253
+ try:
254
+ # Get list of constructs
255
+ constructs = list_available_constructs(construct_type)
256
+
257
+ # If no query, return all constructs
258
+ if not query:
259
+ results = []
260
+ for construct in constructs:
261
+ results.append(
262
+ {
263
+ 'name': construct['name'],
264
+ 'type': construct['type'],
265
+ 'description': construct['description'],
266
+ 'resource_uri': f'genai-cdk-constructs://{construct["type"]}/{construct["name"]}',
267
+ }
268
+ )
269
+
270
+ return {
271
+ 'results': results,
272
+ 'count': len(results),
273
+ 'status': 'success',
274
+ 'installation_required': {
275
+ 'package_name': '@cdklabs/generative-ai-cdk-constructs',
276
+ 'message': 'This construct requires the @cdklabs/generative-ai-cdk-constructs package to be installed',
277
+ },
278
+ }
279
+
280
+ # Define functions to extract searchable text and name parts
281
+ def get_text_fn(construct: Dict[str, Any]) -> str:
282
+ # Create a searchable string from the construct
283
+ name = construct['name'].lower().replace('_', ' ')
284
+ # Split camelCase words (e.g., actionGroups -> action Groups)
285
+ name = re.sub(r'([a-z])([A-Z])', r'\1 \2', name).lower()
286
+ return f'{name} {construct["type"]} {construct["description"]}'.lower()
287
+
288
+ def get_name_parts_fn(construct: Dict[str, Any]) -> List[str]:
289
+ name = construct['name'].lower().replace('_', ' ')
290
+ # Split camelCase words
291
+ name = re.sub(r'([a-z])([A-Z])', r'\1 \2', name).lower()
292
+ return name.split()
293
+
294
+ # Use common search utility
295
+ search_terms = query.lower().split()
296
+ scored_constructs = search_utils.search_items_with_terms(
297
+ constructs, search_terms, get_text_fn, get_name_parts_fn
298
+ )
299
+
300
+ # Format results with resource URIs and matched keywords
301
+ results = []
302
+ for scored_item in scored_constructs:
303
+ construct = scored_item['item']
304
+ results.append(
305
+ {
306
+ 'name': construct['name'],
307
+ 'type': construct['type'],
308
+ 'description': construct['description'],
309
+ 'resource_uri': f'genai-cdk-constructs://{construct["type"]}/{construct["name"]}',
310
+ 'matched_keywords': scored_item['matched_terms'],
311
+ }
312
+ )
313
+
314
+ return {
315
+ 'results': results,
316
+ 'count': len(results),
317
+ 'status': 'success',
318
+ 'installation_required': {
319
+ 'package_name': '@cdklabs/generative-ai-cdk-constructs',
320
+ 'message': 'This construct requires the @cdklabs/generative-ai-cdk-constructs package to be installed',
321
+ },
322
+ }
323
+ except Exception as e:
324
+ return {'error': f'Error searching constructs: {str(e)}', 'status': 'error'}
@@ -0,0 +1 @@
1
+ """Data modules for the AWS CDK MCP server."""
@@ -0,0 +1,331 @@
1
+ """CDK Nag rules parsing utilities."""
2
+
3
+ import httpx
4
+ import re
5
+ import urllib.parse
6
+ from typing import Any, Dict, Optional, Tuple
7
+
8
+
9
+ # Constants
10
+ CDK_NAG_RULES_URL = 'https://raw.githubusercontent.com/cdklabs/cdk-nag/main/RULES.md'
11
+
12
+
13
+ # Helper functions
14
+ async def fetch_cdk_nag_content() -> str:
15
+ """Fetch the CDK Nag rules content from GitHub.
16
+
17
+ Returns:
18
+ The raw content of the RULES.md file from the CDK Nag repository.
19
+ """
20
+ async with httpx.AsyncClient() as client:
21
+ response = await client.get(CDK_NAG_RULES_URL)
22
+ return response.text
23
+
24
+
25
+ def extract_rule_pack_section(content: str, rule_pack: str) -> str:
26
+ """Extract a specific rule pack section from the content.
27
+
28
+ Args:
29
+ content: The full content of the RULES.md file.
30
+ rule_pack: The name of the rule pack to extract.
31
+
32
+ Returns:
33
+ The section of the content for the specified rule pack.
34
+ If the rule pack is not found, returns an error message.
35
+ """
36
+ # Use a direct string search approach
37
+ start_marker = f'## {rule_pack}'
38
+ start_pos = content.find(start_marker)
39
+
40
+ if start_pos < 0:
41
+ return f"Rule pack '{rule_pack}' not found in CDK Nag documentation."
42
+
43
+ # Find the next section heading
44
+ next_section_pos = content.find('\n## ', start_pos + len(start_marker))
45
+
46
+ if next_section_pos >= 0:
47
+ rule_pack_section = content[start_pos:next_section_pos]
48
+ else:
49
+ # If no next section, take until the end of the content
50
+ rule_pack_section = content[start_pos:]
51
+
52
+ return rule_pack_section
53
+
54
+
55
+ def extract_section_by_marker(section: str, marker: str) -> Tuple[bool, str]:
56
+ """Extract a subsection based on a marker (e.g., '### Warnings').
57
+
58
+ Args:
59
+ section: The section to extract from.
60
+ marker: The marker to look for (e.g., '### Warnings').
61
+
62
+ Returns:
63
+ A tuple containing:
64
+ - A boolean indicating whether the marker was found.
65
+ - The extracted subsection if found, or an error message if not found.
66
+ """
67
+ marker_pos = section.find(marker)
68
+
69
+ if marker_pos < 0:
70
+ return False, f'No {marker.lstrip("#").strip()} found.'
71
+
72
+ # Find the next subsection heading
73
+ next_subsection_pos = section.find('\n### ', marker_pos + len(marker))
74
+
75
+ if next_subsection_pos >= 0:
76
+ subsection = section[marker_pos:next_subsection_pos]
77
+ else:
78
+ # If no next subsection, take until the end of the section
79
+ subsection = section[marker_pos:]
80
+
81
+ return True, subsection
82
+
83
+
84
+ def extract_rule_info(content: str, rule_id: str) -> Optional[Dict[str, str]]:
85
+ """Extract information about a specific rule from the content.
86
+
87
+ Args:
88
+ content: The full content of the RULES.md file.
89
+ rule_id: The ID of the rule to extract information for.
90
+
91
+ Returns:
92
+ A dictionary containing the rule information, or None if the rule is not found.
93
+ """
94
+ # Find the rule in the table
95
+ # The table format is: | Rule ID | Cause | Explanation | [Relevant Control ID(s)] |
96
+ pattern = rf'\|\s*{re.escape(rule_id)}\s*\|(.*?)\|(.*?)\|'
97
+ match = re.search(pattern, content, re.DOTALL)
98
+
99
+ if not match:
100
+ return None
101
+
102
+ result = {
103
+ 'rule_id': rule_id,
104
+ 'cause': match.group(1).strip(),
105
+ 'explanation': match.group(2).strip(),
106
+ }
107
+
108
+ # Check if there's a fourth column (Relevant Control ID(s))
109
+ control_pattern = rf'\|\s*{re.escape(rule_id)}\s*\|(.*?)\|(.*?)\|(.*?)\|'
110
+ control_match = re.search(control_pattern, content, re.DOTALL)
111
+
112
+ if control_match and len(control_match.groups()) >= 3:
113
+ result['control_ids'] = control_match.group(3).strip()
114
+
115
+ return result
116
+
117
+
118
+ def format_rule_info(rule_info: Optional[Dict[str, str]]) -> str:
119
+ """Format rule information as a markdown string.
120
+
121
+ Args:
122
+ rule_info: A dictionary containing rule information.
123
+
124
+ Returns:
125
+ A formatted markdown string.
126
+ """
127
+ if not rule_info:
128
+ return "Rule information not found."
129
+
130
+ result = f'# {rule_info["rule_id"]}\n\n'
131
+ result += f'## Cause\n\n{rule_info["cause"]}\n\n'
132
+ result += f'## Explanation\n\n{rule_info["explanation"]}\n\n'
133
+
134
+ if 'control_ids' in rule_info:
135
+ result += f'## Relevant Control ID(s)\n\n{rule_info["control_ids"]}\n\n'
136
+
137
+ return result
138
+
139
+
140
+ # Main functions
141
+ async def get_rule_pack(rule_pack: str) -> str:
142
+ """Get the full content for a rule pack.
143
+
144
+ Args:
145
+ rule_pack: The name of the rule pack to get.
146
+
147
+ Returns:
148
+ The full content for the specified rule pack.
149
+ """
150
+ # Decode the rule pack name if it's URL-encoded
151
+ rule_pack = urllib.parse.unquote(rule_pack)
152
+
153
+ # Fetch the content
154
+ content = await fetch_cdk_nag_content()
155
+
156
+ # Extract the section for this rule pack
157
+ return extract_rule_pack_section(content, rule_pack)
158
+
159
+
160
+ async def get_warnings(rule_pack: str) -> str:
161
+ """Get only the warnings section for a rule pack.
162
+
163
+ Args:
164
+ rule_pack: The name of the rule pack to get warnings for.
165
+
166
+ Returns:
167
+ The warnings section for the specified rule pack.
168
+ """
169
+ # Decode the rule pack name if it's URL-encoded
170
+ rule_pack = urllib.parse.unquote(rule_pack)
171
+
172
+ # Fetch the content
173
+ content = await fetch_cdk_nag_content()
174
+
175
+ # Extract the section for this rule pack
176
+ rule_pack_section = extract_rule_pack_section(content, rule_pack)
177
+
178
+ # Check if we got an error message
179
+ if rule_pack_section.startswith(f"Rule pack '{rule_pack}' not found"):
180
+ return rule_pack_section
181
+
182
+ # Extract the warnings section
183
+ found, warnings_section = extract_section_by_marker(rule_pack_section, '### Warnings')
184
+
185
+ if not found:
186
+ return f"No warnings found for rule pack '{rule_pack}'."
187
+
188
+ return warnings_section
189
+
190
+
191
+ async def get_errors(rule_pack: str) -> str:
192
+ """Get only the errors section for a rule pack.
193
+
194
+ Args:
195
+ rule_pack: The name of the rule pack to get errors for.
196
+
197
+ Returns:
198
+ The errors section for the specified rule pack.
199
+ """
200
+ # Decode the rule pack name if it's URL-encoded
201
+ rule_pack = urllib.parse.unquote(rule_pack)
202
+
203
+ # Fetch the content
204
+ content = await fetch_cdk_nag_content()
205
+
206
+ # Extract the section for this rule pack
207
+ rule_pack_section = extract_rule_pack_section(content, rule_pack)
208
+
209
+ # Check if we got an error message
210
+ if rule_pack_section.startswith(f"Rule pack '{rule_pack}' not found"):
211
+ return rule_pack_section
212
+
213
+ # Extract the errors section
214
+ found, errors_section = extract_section_by_marker(rule_pack_section, '### Errors')
215
+
216
+ if not found:
217
+ return f"No errors found for rule pack '{rule_pack}'."
218
+
219
+ return errors_section
220
+
221
+
222
+ async def get_rule(rule_id: str) -> str:
223
+ """Get information about a specific rule.
224
+
225
+ Args:
226
+ rule_id: The ID of the rule to get information for.
227
+
228
+ Returns:
229
+ A formatted string containing information about the rule.
230
+ """
231
+ # Fetch the content
232
+ content = await fetch_cdk_nag_content()
233
+
234
+ # Extract the rule information
235
+ rule_info = extract_rule_info(content, rule_id)
236
+
237
+ # Format the rule information
238
+ if rule_info:
239
+ return format_rule_info(rule_info)
240
+ else:
241
+ return f'Rule {rule_id} not found in CDK Nag documentation.'
242
+
243
+
244
+ def check_cdk_nag_suppressions(
245
+ code: Optional[str] = None,
246
+ file_path: Optional[str] = None,
247
+ ) -> Dict[str, Any]:
248
+ """Check if CDK code contains Nag suppressions that require human review.
249
+
250
+ This function scans TypeScript/JavaScript code for any instances of NagSuppressions being used
251
+ and flags them for human review. It helps ensure that security suppressions are only
252
+ applied with proper human oversight and justification.
253
+
254
+ Args:
255
+ code: CDK code to analyze (TypeScript/JavaScript)
256
+ file_path: Path to a file containing CDK code to analyze
257
+
258
+ Returns:
259
+ Dictionary with analysis results including:
260
+ - has_suppressions: Whether suppressions were found
261
+ - suppressions: List of detected suppressions with line numbers and context
262
+ - recommendation: Security guidance for human developers
263
+ """
264
+ # Validate input parameters
265
+ if code is None and file_path is None:
266
+ return {'error': 'Either code or file_path must be provided', 'status': 'error'}
267
+
268
+ if code is not None and file_path is not None:
269
+ return {'error': 'Only one of code or file_path should be provided', 'status': 'error'}
270
+
271
+ # If file_path is provided, read the file content
272
+ if file_path is not None:
273
+ try:
274
+ with open(file_path, 'r') as f:
275
+ code = f.read()
276
+ except Exception as e:
277
+ return {'error': f'Failed to read file: {str(e)}', 'status': 'error'}
278
+
279
+ # Ensure code is not None at this point
280
+ if code is None:
281
+ code = "" # Default to empty string if somehow still None
282
+
283
+ # Define patterns to look for
284
+ patterns = [
285
+ (
286
+ r'import\s+{\s*.*NagSuppressions.*\s*}\s+from\s+[\'"]cdk-nag[\'"]',
287
+ 'NagSuppressions import',
288
+ ),
289
+ (r'NagSuppressions\.addStackSuppressions', 'Stack-level suppression'),
290
+ (r'NagSuppressions\.addResourceSuppressions', 'Resource-level suppression'),
291
+ (r'NagSuppressions\.addResourceSuppressionsByPath', 'Path-based suppression'),
292
+ ]
293
+
294
+ # Find all matches
295
+ suppressions_found = []
296
+ lines = code.split('\n')
297
+
298
+ for i, line in enumerate(lines):
299
+ for pattern, suppression_type in patterns:
300
+ if re.search(pattern, line):
301
+ # Get context (3 lines before and after)
302
+ start = max(0, i - 3)
303
+ end = min(len(lines), i + 4)
304
+ context = '\n'.join(lines[start:end])
305
+
306
+ suppressions_found.append(
307
+ {
308
+ 'line_number': i + 1,
309
+ 'line': line.strip(),
310
+ 'type': suppression_type,
311
+ 'context': context,
312
+ }
313
+ )
314
+
315
+ # Generate response
316
+ if suppressions_found:
317
+ return {
318
+ 'has_suppressions': True,
319
+ 'suppressions': suppressions_found,
320
+ 'recommendation': '⚠️ SECURITY ALERT: This code contains CDK Nag suppressions that require human review.',
321
+ 'action_required': 'Review each suppression and ensure it has proper justification.',
322
+ 'security_impact': 'CDK Nag suppressions can bypass important security checks. Each suppression should be carefully reviewed by a human developer and have a documented justification.',
323
+ 'best_practice': 'Fix the underlying security issue rather than suppressing the warning whenever possible.',
324
+ 'status': 'success',
325
+ }
326
+ else:
327
+ return {
328
+ 'has_suppressions': False,
329
+ 'message': 'No CDK Nag suppressions detected in the provided code.',
330
+ 'status': 'success',
331
+ }
@@ -0,0 +1,32 @@
1
+ """GenAI CDK construct descriptions."""
2
+
3
+ from typing import Dict
4
+
5
+
6
+ def get_construct_descriptions() -> Dict[str, str]:
7
+ """Get a dictionary mapping construct names to their descriptions."""
8
+ return {
9
+ # Agent-related constructs
10
+ 'Agent_creation': 'Create and configure Bedrock Agents with foundation models, instructions, and optional features',
11
+ 'Agent_actiongroups': 'Define custom functions for Bedrock Agents to call via Lambda and OpenAPI schemas',
12
+ 'Agent_alias': 'Create versioned aliases for Bedrock Agents to manage deployment and integration',
13
+ 'Agent_collaboration': 'Configure multiple Bedrock Agents to work together on complex tasks',
14
+ 'Agent_custom_orchestration': 'Override default agent orchestration flow with custom Lambda functions',
15
+ 'Agent_prompt_override': 'Customize prompts and LLM configurations for different agent processing steps',
16
+ # Knowledge Base constructs
17
+ 'Knowledgebases_kendra': 'Create knowledge bases from Amazon Kendra GenAI indexes for RAG applications',
18
+ 'Knowledgebases_datasources': 'Configure data sources for Bedrock Knowledge Bases including S3, web crawlers, and more',
19
+ 'Knowledgebases_parsing': 'Define strategies for processing and interpreting document contents in knowledge bases',
20
+ 'Knowledgebases_transformation': 'Apply custom processing steps to documents during knowledge base ingestion',
21
+ 'Knowledgebases_chunking': 'Configure document chunking strategies for optimal knowledge base performance',
22
+ 'Knowledgebases_vector_opensearch': 'Use OpenSearch Serverless as a vector store (vector database) for Bedrock Knowledge Bases',
23
+ 'Knowledgebases_vector_aurora': 'Use Amazon RDS Aurora PostgreSQL as a vector store (vector database) for Bedrock Knowledge Bases',
24
+ 'Knowledgebases_vector_pinecone': 'Use Pinecone as a vector store (vector database) for Bedrock Knowledge Bases',
25
+ 'Knowledgebases_vector_creation': 'Create and configure vector stores (vector databases) for Bedrock Knowledge Bases',
26
+ # Other Bedrock constructs
27
+ 'Bedrockguardrails': 'Configure content filtering and safety guardrails for Bedrock foundation models',
28
+ 'Profiles': 'Create and manage inference profiles for tracking usage and costs across regions',
29
+ # OpenSearch constructs
30
+ 'Opensearchserverless_overview': 'Create and configure Amazon OpenSearch Serverless for vector search applications',
31
+ 'Opensearch_vectorindex_overview': 'Configure vector indexes in Amazon OpenSearch for semantic search',
32
+ }