awslabs.cdk-mcp-server 1.0.1__py3-none-any.whl → 1.0.3__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.
@@ -23,6 +23,40 @@ from datetime import datetime, timedelta
23
23
  from typing import Any, Dict, List
24
24
 
25
25
 
26
+ # Regular expression patterns for parsing documentation
27
+ # AsciiDoc patterns
28
+ # Matches a Description section in AsciiDoc format: "= Description" followed by text until the next section or end
29
+ ADOC_DESCRIPTION_SECTION_PATTERN = r'= Description\s*\n+(.*?)(?=\n=|\Z)'
30
+
31
+ # Matches an Overview section in AsciiDoc format: "= Overview" followed by text until the next section or end
32
+ ADOC_OVERVIEW_SECTION_PATTERN = r'= Overview\s*\n+(.*?)(?=\n=|\Z)'
33
+
34
+ # Matches the first paragraph in a section: start of text until first blank line or end
35
+ FIRST_PARAGRAPH_PATTERN = r'^(.*?)(?=\n\n|\Z)'
36
+
37
+ # Matches a title and the following paragraph in AsciiDoc: "= Title" followed by blank line and text
38
+ ADOC_TITLE_AND_PARAGRAPH_PATTERN = r'= ([^\n]+)\s*\n\n(.*?)(?=\n\n|\n=|\Z)'
39
+
40
+ # Markdown patterns
41
+ # Matches a Description section in Markdown: "## Description" followed by text until the next section or end
42
+ MD_DESCRIPTION_SECTION_PATTERN = r'## Description\s*\n+(.*?)(?=\n##|\Z)'
43
+
44
+ # Matches an Overview section in Markdown: "## Overview" followed by text until the next section or end
45
+ MD_OVERVIEW_SECTION_PATTERN = r'## Overview\s*\n+(.*?)(?=\n##|\Z)'
46
+
47
+ # Matches the first paragraph after a title in Markdown: "# Title" followed by blank line and text
48
+ MD_TITLE_AND_PARAGRAPH_PATTERN = r'# [^\n]*\n\n(.*?)(?=\n\n|\n##|\Z)'
49
+
50
+ # Matches any text before the first ## heading
51
+ MD_TEXT_BEFORE_FIRST_HEADING_PATTERN = r'\n\n(.*?)(?=\n##|\Z)'
52
+
53
+ # Matches a title in Markdown: "# Title"
54
+ MD_TITLE_PATTERN = r'# ([^\n]+)'
55
+
56
+ # Pattern to replace multiple whitespace characters with a single space
57
+ WHITESPACE_CLEANUP_PATTERN = r'\s+'
58
+
59
+
26
60
  # Set up logging
27
61
  logging.basicConfig(level=logging.INFO)
28
62
  logger = logging.getLogger(__name__)
@@ -115,22 +149,32 @@ async def get_pattern_info(pattern_name: str) -> Dict[str, Any]:
115
149
  logger.info(f'Using cached info for {pattern_name}')
116
150
  return _pattern_details_cache[pattern_name]['data']
117
151
 
118
- # Fetch README.md content
152
+ # Try to fetch README.adoc first (preferred)
119
153
  async with httpx.AsyncClient() as client:
120
- readme_url = f'{GITHUB_RAW_CONTENT_URL}/{REPO_OWNER}/{REPO_NAME}/main/{PATTERNS_PATH}/{pattern_name}/README.md'
121
- logger.info(f'Fetching README from {readme_url}')
122
- response = await client.get(readme_url)
123
-
124
- if response.status_code != 200:
125
- logger.warning(
126
- f'Failed to fetch README for {pattern_name}: HTTP {response.status_code}'
127
- )
128
- return {
129
- 'error': f'Pattern {pattern_name} not found or README.md not available',
130
- 'status_code': response.status_code,
131
- }
154
+ readme_adoc_url = f'{GITHUB_RAW_CONTENT_URL}/{REPO_OWNER}/{REPO_NAME}/main/{PATTERNS_PATH}/{pattern_name}/README.adoc'
155
+ logger.info(f'Fetching README.adoc from {readme_adoc_url}')
156
+ adoc_response = await client.get(readme_adoc_url)
157
+
158
+ if adoc_response.status_code == 200:
159
+ readme_content = adoc_response.text
160
+ logger.info(f'Successfully fetched README.adoc for {pattern_name}')
161
+ else:
162
+ # Fall back to README.md (if README.adoc is not available)
163
+ readme_md_url = f'{GITHUB_RAW_CONTENT_URL}/{REPO_OWNER}/{REPO_NAME}/main/{PATTERNS_PATH}/{pattern_name}/README.md'
164
+ logger.info(f'README.adoc not found, trying README.md from {readme_md_url}')
165
+ md_response = await client.get(readme_md_url)
166
+
167
+ if md_response.status_code != 200:
168
+ logger.warning(
169
+ f'Failed to fetch README for {pattern_name}: HTTP {md_response.status_code}'
170
+ )
171
+ return {
172
+ 'error': f'Pattern {pattern_name} not found or README not available',
173
+ 'status_code': md_response.status_code,
174
+ }
132
175
 
133
- readme_content = response.text
176
+ readme_content = md_response.text
177
+ logger.info(f'Successfully fetched README.md for {pattern_name}')
134
178
 
135
179
  # Extract only metadata
136
180
  services = extract_services_from_pattern_name(pattern_name)
@@ -313,41 +357,69 @@ def extract_services_from_pattern_name(pattern_name: str) -> List[str]:
313
357
 
314
358
 
315
359
  def extract_description(content: str) -> str:
316
- """Extract the pattern description from README.md content.
360
+ """Extract the pattern description from README content.
317
361
 
318
362
  Args:
319
- content: README.md content
363
+ content: README content (can be .md or .adoc format)
320
364
 
321
365
  Returns:
322
366
  Pattern description
323
367
  """
368
+ # Check if this is an AsciiDoc (.adoc) file
369
+ if any(marker in content for marker in ['= Overview', '= Description']):
370
+ # First, try to find a dedicated Description section in AsciiDoc
371
+ desc_section_match = re.search(ADOC_DESCRIPTION_SECTION_PATTERN, content, re.DOTALL)
372
+ if desc_section_match:
373
+ desc_text = desc_section_match.group(1).strip()
374
+ # Replace newlines with spaces to ensure a single line description
375
+ return re.sub(WHITESPACE_CLEANUP_PATTERN, ' ', desc_text)
376
+
377
+ # Next, try to find an Overview section in AsciiDoc
378
+ overview_section_match = re.search(ADOC_OVERVIEW_SECTION_PATTERN, content, re.DOTALL)
379
+ if overview_section_match:
380
+ # Take the first paragraph of the overview
381
+ overview = overview_section_match.group(1).strip()
382
+ first_para_match = re.search(FIRST_PARAGRAPH_PATTERN, overview, re.DOTALL)
383
+ if first_para_match:
384
+ # Replace newlines with spaces to ensure a single line description
385
+ return re.sub(WHITESPACE_CLEANUP_PATTERN, ' ', first_para_match.group(1).strip())
386
+ # Replace newlines with spaces to ensure a single line description
387
+ return re.sub(WHITESPACE_CLEANUP_PATTERN, ' ', overview)
388
+
389
+ # Try to find the first paragraph after a title in AsciiDoc format
390
+ title_match = re.search(ADOC_TITLE_AND_PARAGRAPH_PATTERN, content, re.DOTALL)
391
+ if title_match:
392
+ # Replace newlines with spaces to ensure a single line description
393
+ return re.sub(WHITESPACE_CLEANUP_PATTERN, ' ', title_match.group(2).strip())
394
+
395
+ # For Markdown format
324
396
  # First, try to find a dedicated Description section
325
- desc_section_match = re.search(r'## Description\s*\n+(.*?)(?=\n##|\Z)', content, re.DOTALL)
397
+ desc_section_match = re.search(MD_DESCRIPTION_SECTION_PATTERN, content, re.DOTALL)
326
398
  if desc_section_match:
327
399
  return desc_section_match.group(1).strip()
328
400
 
329
401
  # Next, try to find an Overview section
330
- overview_section_match = re.search(r'## Overview\s*\n+(.*?)(?=\n##|\Z)', content, re.DOTALL)
402
+ overview_section_match = re.search(MD_OVERVIEW_SECTION_PATTERN, content, re.DOTALL)
331
403
  if overview_section_match:
332
404
  # Take the first paragraph of the overview
333
405
  overview = overview_section_match.group(1).strip()
334
- first_para_match = re.search(r'^(.*?)(?=\n\n|\Z)', overview, re.DOTALL)
406
+ first_para_match = re.search(FIRST_PARAGRAPH_PATTERN, overview, re.DOTALL)
335
407
  if first_para_match:
336
408
  return first_para_match.group(1).strip()
337
409
  return overview
338
410
 
339
411
  # Try to find the first paragraph after the title
340
- match = re.search(r'# [^\n]*\n\n(.*?)(?=\n\n|\n##|\Z)', content, re.DOTALL)
412
+ match = re.search(MD_TITLE_AND_PARAGRAPH_PATTERN, content, re.DOTALL)
341
413
  if match:
342
414
  return match.group(1).strip()
343
415
 
344
416
  # Fallback: Try to find any text before the first ## heading
345
- match = re.search(r'\n\n(.*?)(?=\n##|\Z)', content, re.DOTALL)
417
+ match = re.search(MD_TEXT_BEFORE_FIRST_HEADING_PATTERN, content, re.DOTALL)
346
418
  if match:
347
419
  return match.group(1).strip()
348
420
 
349
421
  # If all else fails, extract the title as a fallback
350
- title_match = re.search(r'# ([^\n]+)', content)
422
+ title_match = re.search(MD_TITLE_PATTERN, content)
351
423
  if title_match:
352
424
  pattern_name = title_match.group(1).strip()
353
425
  return f'A pattern for integrating {pattern_name} services'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: awslabs.cdk-mcp-server
3
- Version: 1.0.1
3
+ Version: 1.0.3
4
4
  Summary: An AWS CDK MCP server that provides guidance on AWS Cloud Development Kit best practices, infrastructure as code patterns, and security compliance with CDK Nag. This server offers tools to validate infrastructure designs, explain CDK Nag rules, analyze suppressions, generate Bedrock Agent schemas, and discover Solutions Constructs patterns.
5
5
  Project-URL: Homepage, https://awslabs.github.io/mcp/
6
6
  Project-URL: Documentation, https://awslabs.github.io/mcp/servers/cdk-mcp-server/
@@ -168,7 +168,9 @@ graph TD
168
168
 
169
169
  ## Installation
170
170
 
171
- Here are some ways you can work with MCP across AWS, and we'll be adding support to more products including Amazon Q Developer CLI soon: (e.g. for Amazon Q Developer CLI MCP, `~/.aws/amazonq/mcp.json`):
171
+ [![Install MCP Server](https://cursor.com/deeplink/mcp-install-light.svg)](https://cursor.com/install-mcp?name=awslabs.cdk-mcp-server&config=eyJjb21tYW5kIjoidXZ4IGF3c2xhYnMuY2RrLW1jcC1zZXJ2ZXJAbGF0ZXN0IiwiZW52Ijp7IkZBU1RNQ1BfTE9HX0xFVkVMIjoiRVJST1IifSwiZGlzYWJsZWQiOmZhbHNlLCJhdXRvQXBwcm92ZSI6W119)
172
+
173
+ Configure the MCP server in your MCP client configuration (e.g., for Amazon Q Developer CLI, edit `~/.aws/amazonq/mcp.json`):
172
174
 
173
175
  ```json
174
176
  {
@@ -13,7 +13,7 @@ awslabs/cdk_mcp_server/data/genai_cdk_loader.py,sha256=OyG7fD_dWZL0HLmAgR0s4b8L8
13
13
  awslabs/cdk_mcp_server/data/lambda_layer_parser.py,sha256=Ti4W5gjTB3WkxWSnIbGXKJnJs-MH7_kVW3pkuiMUtTM,8668
14
14
  awslabs/cdk_mcp_server/data/lambda_powertools_loader.py,sha256=nqCw4LzyK3_gIOsf1WoZXcL6_GnSlRDGMdRtdP3sWSk,2468
15
15
  awslabs/cdk_mcp_server/data/schema_generator.py,sha256=xNul0eZq4aG_CRTNYQtLLJn22Df5lsUIAIrBteKn6NU,29326
16
- awslabs/cdk_mcp_server/data/solutions_constructs_parser.py,sha256=3ofM1IaDZ_7vf6TjwDJ2sJnwO4q_1mtrWvadbjji3Kk,28300
16
+ awslabs/cdk_mcp_server/data/solutions_constructs_parser.py,sha256=eWBf0HQn5x4cGqYZTXf8kwVcTQSgXgB2gf4ZTXWLmqs,32323
17
17
  awslabs/cdk_mcp_server/static/CDK_GENERAL_GUIDANCE.md,sha256=-O0LIpmeDXr1JoVxxbQnVp0oNqvSbX20xOgcZqknr_c,8532
18
18
  awslabs/cdk_mcp_server/static/CDK_NAG_GUIDANCE.md,sha256=zJtHJp9ruaaJ-xa68k9kDrPmEaXpiWCZZf7JIy8NK0w,5839
19
19
  awslabs/cdk_mcp_server/static/__init__.py,sha256=ZC2OMY573QylDo6c1LkAsyHFoLgF3pIRumY_Ar8Un0s,915
@@ -25,9 +25,9 @@ awslabs/cdk_mcp_server/static/lambda_powertools/insights.md,sha256=jcyOHZvKHk2Cg
25
25
  awslabs/cdk_mcp_server/static/lambda_powertools/logging.md,sha256=6CSgD8QB3Bs4s_x4RnbKwZoWvG6aG4etCnmDH6HU9XY,1797
26
26
  awslabs/cdk_mcp_server/static/lambda_powertools/metrics.md,sha256=DQlznxRizJep8jphzFgbk7crH5LwWjSjdygP-1K6mxk,2559
27
27
  awslabs/cdk_mcp_server/static/lambda_powertools/tracing.md,sha256=Q3dSCvgktb9sUsuuQ5ONU2Qdb1OTwbNOYpK--MDzBNw,2539
28
- awslabs_cdk_mcp_server-1.0.1.dist-info/METADATA,sha256=O0YoaENd8cCSfgQwAFSOMrBzPXOqrwNDwYP093J5f_c,9135
29
- awslabs_cdk_mcp_server-1.0.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
30
- awslabs_cdk_mcp_server-1.0.1.dist-info/entry_points.txt,sha256=LertzmID_mUU1YYZPySAF1IY1zE7ySTvzFxiGyo3VjY,78
31
- awslabs_cdk_mcp_server-1.0.1.dist-info/licenses/LICENSE,sha256=CeipvOyAZxBGUsFoaFqwkx54aPnIKEtm9a5u2uXxEws,10142
32
- awslabs_cdk_mcp_server-1.0.1.dist-info/licenses/NOTICE,sha256=MNXNmhkltaxAzlo-r5BhjfS30nUE7I_w7cyDY8cxDL0,90
33
- awslabs_cdk_mcp_server-1.0.1.dist-info/RECORD,,
28
+ awslabs_cdk_mcp_server-1.0.3.dist-info/METADATA,sha256=yxNlaXoUjG0kmyoWYPixR1EmAuA1T2Z29IjD8F2uhYU,9358
29
+ awslabs_cdk_mcp_server-1.0.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
30
+ awslabs_cdk_mcp_server-1.0.3.dist-info/entry_points.txt,sha256=LertzmID_mUU1YYZPySAF1IY1zE7ySTvzFxiGyo3VjY,78
31
+ awslabs_cdk_mcp_server-1.0.3.dist-info/licenses/LICENSE,sha256=CeipvOyAZxBGUsFoaFqwkx54aPnIKEtm9a5u2uXxEws,10142
32
+ awslabs_cdk_mcp_server-1.0.3.dist-info/licenses/NOTICE,sha256=MNXNmhkltaxAzlo-r5BhjfS30nUE7I_w7cyDY8cxDL0,90
33
+ awslabs_cdk_mcp_server-1.0.3.dist-info/RECORD,,