awslabs.cdk-mcp-server 0.1.0__tar.gz → 0.1.1__tar.gz

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 (68) hide show
  1. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/.gitignore +3 -0
  2. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/PKG-INFO +1 -1
  3. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/data/schema_generator.py +2 -1
  4. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/pyproject.toml +26 -1
  5. awslabs_cdk_mcp_server-0.1.1/tests/__init__.py +10 -0
  6. awslabs_cdk_mcp_server-0.1.1/tests/core/test_resources.py +190 -0
  7. awslabs_cdk_mcp_server-0.1.1/tests/core/test_search_utils.py +77 -0
  8. awslabs_cdk_mcp_server-0.1.1/tests/core/test_server.py +55 -0
  9. awslabs_cdk_mcp_server-0.1.1/tests/core/test_tools.py +153 -0
  10. awslabs_cdk_mcp_server-0.1.1/tests/data/test_cdk_nag_parser.py +280 -0
  11. awslabs_cdk_mcp_server-0.1.1/tests/data/test_genai_cdk_loader.py +345 -0
  12. awslabs_cdk_mcp_server-0.1.1/tests/data/test_lambda_powertools_loader.py +97 -0
  13. awslabs_cdk_mcp_server-0.1.1/tests/data/test_schema_generator.py +448 -0
  14. awslabs_cdk_mcp_server-0.1.1/tests/data/test_solutions_constructs_parser.py +200 -0
  15. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/uv.lock +185 -1
  16. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/.pre-commit-config.yaml +0 -0
  17. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/.python-version +0 -0
  18. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/CHANGELOG.md +0 -0
  19. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/LICENSE +0 -0
  20. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/NOTICE +0 -0
  21. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/README.md +0 -0
  22. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/__init__.py +0 -0
  23. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/__init__.py +0 -0
  24. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/core/__init__.py +0 -0
  25. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/core/resources.py +0 -0
  26. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/core/search_utils.py +0 -0
  27. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/core/server.py +0 -0
  28. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/core/tools.py +0 -0
  29. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/data/__init__.py +0 -0
  30. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/data/cdk_nag_parser.py +0 -0
  31. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/data/construct_descriptions.py +0 -0
  32. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/data/genai_cdk_loader.py +0 -0
  33. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/data/lambda_layer_parser.py +0 -0
  34. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/data/lambda_powertools_loader.py +0 -0
  35. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/data/solutions_constructs_parser.py +0 -0
  36. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/server.py +0 -0
  37. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/CDK_GENERAL_GUIDANCE.md +0 -0
  38. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/CDK_NAG_GUIDANCE.md +0 -0
  39. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/__init__.py +0 -0
  40. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/genai_cdk/bedrock/agent/actiongroups.md +0 -0
  41. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/genai_cdk/bedrock/agent/alias.md +0 -0
  42. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/genai_cdk/bedrock/agent/collaboration.md +0 -0
  43. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/genai_cdk/bedrock/agent/creation.md +0 -0
  44. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/genai_cdk/bedrock/agent/custom_orchestration.md +0 -0
  45. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/genai_cdk/bedrock/agent/overview.md +0 -0
  46. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/genai_cdk/bedrock/agent/prompt_override.md +0 -0
  47. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/genai_cdk/bedrock/bedrockguardrails.md +0 -0
  48. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/chunking.md +0 -0
  49. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/datasources.md +0 -0
  50. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/kendra.md +0 -0
  51. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/overview.md +0 -0
  52. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/parsing.md +0 -0
  53. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/transformation.md +0 -0
  54. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/vector/aurora.md +0 -0
  55. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/vector/creation.md +0 -0
  56. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/vector/opensearch.md +0 -0
  57. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/genai_cdk/bedrock/knowledgebases/vector/pinecone.md +0 -0
  58. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/genai_cdk/bedrock/profiles.md +0 -0
  59. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/genai_cdk/opensearch-vectorindex/overview.md +0 -0
  60. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/genai_cdk/opensearchserverless/overview.md +0 -0
  61. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/lambda_powertools/bedrock.md +0 -0
  62. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/lambda_powertools/cdk.md +0 -0
  63. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/lambda_powertools/dependencies.md +0 -0
  64. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/lambda_powertools/index.md +0 -0
  65. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/lambda_powertools/insights.md +0 -0
  66. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/lambda_powertools/logging.md +0 -0
  67. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/lambda_powertools/metrics.md +0 -0
  68. {awslabs_cdk_mcp_server-0.1.0 → awslabs_cdk_mcp_server-0.1.1}/awslabs/cdk_mcp_server/static/lambda_powertools/tracing.md +0 -0
@@ -57,3 +57,6 @@ target/
57
57
 
58
58
  # PyPI
59
59
  .pypirc
60
+
61
+ # auto generated files
62
+ scripts/generate_schema_*.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: awslabs.cdk-mcp-server
3
- Version: 0.1.0
3
+ Version: 0.1.1
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/
@@ -20,7 +20,8 @@ from typing import Any, Dict, List, Optional, Tuple
20
20
 
21
21
  def generate_fallback_script(lambda_code_path: str, output_path: str) -> str:
22
22
  """Generate a standalone script for schema generation."""
23
- return f'''#!/usr/bin/env python3
23
+ return f'''# pyright: ignore
24
+ #!/usr/bin/env python3
24
25
  """
25
26
  Schema Generator for Bedrock Agent Action Groups
26
27
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "awslabs.cdk-mcp-server"
3
- version = "0.1.0"
3
+ version = "0.1.1"
4
4
  description = "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
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -45,6 +45,10 @@ dev = [
45
45
  "pre-commit>=4.1.0",
46
46
  "ruff>=0.9.7",
47
47
  "pyright>=1.1.398",
48
+ "pytest>=8.0.0",
49
+ "pytest-cov>=4.1.0",
50
+ "pytest-mock>=3.12.0",
51
+ "pytest-asyncio>=0.23.5",
48
52
  ]
49
53
 
50
54
  [build-system]
@@ -107,3 +111,24 @@ packages = ["awslabs"]
107
111
 
108
112
  [tool.bandit]
109
113
  exclude_dirs = ["venv","tests"]
114
+
115
+ [tool.pytest.ini_options]
116
+ minversion = "8.0"
117
+ addopts = "-ra -q --cov=awslabs.cdk_mcp_server --cov-report=term-missing"
118
+ testpaths = [
119
+ "tests",
120
+ ]
121
+ python_files = ["test_*.py"]
122
+ python_classes = ["Test*"]
123
+ python_functions = ["test_*"]
124
+ filterwarnings = [
125
+ "ignore::DeprecationWarning",
126
+ "ignore::UserWarning",
127
+ ]
128
+ asyncio_mode = "auto"
129
+
130
+ [tool.pyright]
131
+ ignore = ["scripts/generate_schema_*.py"]
132
+
133
+ [tool.coverage.run]
134
+ source = ["awslabs"]
@@ -0,0 +1,10 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
4
+ # with the License. A copy of the License is located at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
9
+ # OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
10
+ # and limitations under the License.
@@ -0,0 +1,190 @@
1
+ import pytest
2
+ from awslabs.cdk_mcp_server.core.resources import (
3
+ RulePack,
4
+ get_all_cdk_nag_rules,
5
+ get_available_sections_resource,
6
+ get_cdk_nag_errors,
7
+ get_cdk_nag_warnings,
8
+ get_genai_cdk_construct_nested_section_resource,
9
+ get_genai_cdk_construct_resource,
10
+ get_genai_cdk_construct_section_resource,
11
+ get_genai_cdk_overview_resource,
12
+ get_lambda_powertools_guidance,
13
+ get_lambda_powertools_index,
14
+ get_solutions_construct_pattern_resource,
15
+ )
16
+ from unittest.mock import patch
17
+
18
+
19
+ @pytest.mark.asyncio
20
+ async def test_get_all_cdk_nag_rules():
21
+ """Test getting all CDK Nag rules for a rule pack."""
22
+ with patch('awslabs.cdk_mcp_server.core.resources.get_rule_pack') as mock_get_rule_pack:
23
+ mock_get_rule_pack.return_value = 'Test rule pack content'
24
+
25
+ # Test with valid rule pack
26
+ result = await get_all_cdk_nag_rules(RulePack.AWS_SOLUTIONS.value)
27
+ assert result == 'Test rule pack content'
28
+ mock_get_rule_pack.assert_called_once_with(RulePack.AWS_SOLUTIONS)
29
+
30
+ # Test with invalid rule pack
31
+ result = await get_all_cdk_nag_rules('Invalid Pack')
32
+ assert 'Invalid rule pack' in result
33
+ assert RulePack.AWS_SOLUTIONS.value in result
34
+
35
+
36
+ @pytest.mark.asyncio
37
+ async def test_get_cdk_nag_warnings():
38
+ """Test getting CDK Nag warnings for a rule pack."""
39
+ with patch('awslabs.cdk_mcp_server.core.resources.get_warnings') as mock_get_warnings:
40
+ mock_get_warnings.return_value = 'Test warnings content'
41
+
42
+ # Test with valid rule pack
43
+ result = await get_cdk_nag_warnings(RulePack.AWS_SOLUTIONS.value)
44
+ assert result == 'Test warnings content'
45
+ mock_get_warnings.assert_called_once_with(RulePack.AWS_SOLUTIONS)
46
+
47
+ # Test with invalid rule pack
48
+ result = await get_cdk_nag_warnings('Invalid Pack')
49
+ assert 'Invalid rule pack' in result
50
+ assert RulePack.AWS_SOLUTIONS.value in result
51
+
52
+
53
+ @pytest.mark.asyncio
54
+ async def test_get_cdk_nag_errors():
55
+ """Test getting CDK Nag errors for a rule pack."""
56
+ with patch('awslabs.cdk_mcp_server.core.resources.get_errors') as mock_get_errors:
57
+ mock_get_errors.return_value = 'Test errors content'
58
+
59
+ # Test with valid rule pack
60
+ result = await get_cdk_nag_errors(RulePack.AWS_SOLUTIONS.value)
61
+ assert result == 'Test errors content'
62
+ mock_get_errors.assert_called_once_with(RulePack.AWS_SOLUTIONS)
63
+
64
+ # Test with invalid rule pack
65
+ result = await get_cdk_nag_errors('Invalid Pack')
66
+ assert 'Invalid rule pack' in result
67
+ assert RulePack.AWS_SOLUTIONS.value in result
68
+
69
+
70
+ @pytest.mark.asyncio
71
+ async def test_get_lambda_powertools_guidance():
72
+ """Test getting Lambda Powertools guidance."""
73
+ with patch(
74
+ 'awslabs.cdk_mcp_server.core.resources.get_lambda_powertools_section'
75
+ ) as mock_get_section:
76
+ mock_get_section.return_value = 'Test guidance content'
77
+
78
+ # Test with specific topic
79
+ result = await get_lambda_powertools_guidance('logging')
80
+ assert result == 'Test guidance content'
81
+ mock_get_section.assert_called_once_with('logging')
82
+
83
+ # Test with empty topic
84
+ result = await get_lambda_powertools_guidance()
85
+ assert result == 'Test guidance content'
86
+ mock_get_section.assert_called_with('')
87
+
88
+
89
+ @pytest.mark.asyncio
90
+ async def test_get_lambda_powertools_index():
91
+ """Test getting Lambda Powertools index."""
92
+ with patch(
93
+ 'awslabs.cdk_mcp_server.core.resources.get_lambda_powertools_section'
94
+ ) as mock_get_section:
95
+ mock_get_section.return_value = 'Test index content'
96
+
97
+ result = await get_lambda_powertools_index()
98
+ assert result == 'Test index content'
99
+ mock_get_section.assert_called_once_with('index')
100
+
101
+
102
+ @pytest.mark.asyncio
103
+ async def test_get_solutions_construct_pattern_resource():
104
+ """Test getting Solutions Construct pattern resource."""
105
+ with patch('awslabs.cdk_mcp_server.core.resources.get_pattern_raw') as mock_get_pattern:
106
+ # Test with valid pattern
107
+ mock_get_pattern.return_value = {'content': 'Test pattern content'}
108
+ result = await get_solutions_construct_pattern_resource('aws-lambda-dynamodb')
109
+ assert result == 'Test pattern content'
110
+ mock_get_pattern.assert_called_once_with('aws-lambda-dynamodb')
111
+
112
+ # Test with invalid pattern
113
+ mock_get_pattern.return_value = {'error': 'Pattern not found'}
114
+ with patch('awslabs.cdk_mcp_server.data.solutions_constructs_parser') as mock_fetch_list:
115
+ mock_fetch_list.return_value = ['pattern1', 'pattern2']
116
+ result = await get_solutions_construct_pattern_resource('invalid-pattern')
117
+ assert "Pattern 'invalid-pattern' not found" in result
118
+
119
+
120
+ @pytest.mark.asyncio
121
+ async def test_get_genai_cdk_construct_section_resource():
122
+ """Test getting GenAI CDK construct section resource."""
123
+ with patch(
124
+ 'awslabs.cdk_mcp_server.core.resources.get_genai_cdk_construct_section'
125
+ ) as mock_get_section:
126
+ mock_get_section.return_value = 'Test section content'
127
+
128
+ result = await get_genai_cdk_construct_section_resource('bedrock', 'agent', 'actiongroups')
129
+ assert result == 'Test section content'
130
+ mock_get_section.assert_called_once_with('bedrock', 'agent', 'actiongroups')
131
+
132
+
133
+ @pytest.mark.asyncio
134
+ async def test_get_genai_cdk_construct_nested_section_resource():
135
+ """Test getting GenAI CDK construct nested section resource."""
136
+ with patch(
137
+ 'awslabs.cdk_mcp_server.core.resources.get_genai_cdk_construct_section'
138
+ ) as mock_get_section:
139
+ mock_get_section.return_value = 'Test nested section content'
140
+
141
+ result = await get_genai_cdk_construct_nested_section_resource(
142
+ 'bedrock', 'knowledgebases', 'vector', 'opensearch'
143
+ )
144
+ assert result == 'Test nested section content'
145
+ mock_get_section.assert_called_once_with('bedrock', 'knowledgebases', 'vector/opensearch')
146
+
147
+
148
+ @pytest.mark.asyncio
149
+ async def test_get_available_sections_resource():
150
+ """Test getting available sections resource."""
151
+ with patch(
152
+ 'awslabs.cdk_mcp_server.core.resources.list_available_sections'
153
+ ) as mock_list_sections:
154
+ # Test with sections available
155
+ mock_list_sections.return_value = ['section1', 'section2']
156
+ result = await get_available_sections_resource('bedrock', 'agent')
157
+ assert 'Available Sections for Agent in Bedrock' in result
158
+ assert 'section1' in result
159
+ assert 'section2' in result
160
+
161
+ # Test with no sections
162
+ mock_list_sections.return_value = []
163
+ result = await get_available_sections_resource('bedrock', 'agent')
164
+ assert 'No sections found' in result
165
+
166
+
167
+ @pytest.mark.asyncio
168
+ async def test_get_genai_cdk_construct_resource():
169
+ """Test getting GenAI CDK construct resource."""
170
+ with patch(
171
+ 'awslabs.cdk_mcp_server.core.resources.get_genai_cdk_construct'
172
+ ) as mock_get_construct:
173
+ mock_get_construct.return_value = 'Test construct content'
174
+
175
+ result = await get_genai_cdk_construct_resource('bedrock', 'Agent')
176
+ assert result == 'Test construct content'
177
+ mock_get_construct.assert_called_once_with('bedrock', 'Agent')
178
+
179
+
180
+ @pytest.mark.asyncio
181
+ async def test_get_genai_cdk_overview_resource():
182
+ """Test getting GenAI CDK overview resource."""
183
+ with patch(
184
+ 'awslabs.cdk_mcp_server.core.resources.get_genai_cdk_overview'
185
+ ) as mock_get_overview:
186
+ mock_get_overview.return_value = 'Test overview content'
187
+
188
+ result = await get_genai_cdk_overview_resource('bedrock')
189
+ assert result == 'Test overview content'
190
+ mock_get_overview.assert_called_once_with('bedrock')
@@ -0,0 +1,77 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
4
+ # with the License. A copy of the License is located at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
9
+ # OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
10
+ # and limitations under the License.
11
+ from awslabs.cdk_mcp_server.core import search_utils
12
+
13
+
14
+ def test_normalize_term():
15
+ """Test term normalization."""
16
+ assert search_utils.normalize_term('Test-Term') == 'test term'
17
+ assert search_utils.normalize_term('Test_Term') == 'test term'
18
+ assert search_utils.normalize_term('Test Term') == 'test term'
19
+ assert search_utils.normalize_term('Test%20Term') == 'test term'
20
+
21
+
22
+ def test_get_term_variations():
23
+ """Test getting term variations."""
24
+ variations = search_utils.get_term_variations('knowledgebase')
25
+ assert 'knowledgebases' in variations
26
+ assert 'knowledge-base' in variations
27
+ assert 'knowledge-bases' in variations
28
+
29
+ variations = search_utils.get_term_variations('agent')
30
+ assert 'agents' in variations
31
+
32
+ variations = search_utils.get_term_variations('actiongroup')
33
+ assert 'actiongroups' in variations
34
+ assert 'action-group' in variations
35
+ assert 'action-groups' in variations
36
+
37
+
38
+ def test_expand_search_terms():
39
+ """Test expanding search terms."""
40
+ terms = ['knowledgebase', 'agent']
41
+ expanded = search_utils.expand_search_terms(terms)
42
+ assert 'knowledgebase' in expanded
43
+ assert 'knowledgebases' in expanded
44
+ assert 'agent' in expanded
45
+ assert 'agents' in expanded
46
+
47
+
48
+ def test_calculate_match_score():
49
+ """Test calculating match score."""
50
+ item_text = 'This is a test item with some content'
51
+ search_terms = ['test', 'content']
52
+ name_parts = ['TestItem']
53
+
54
+ result = search_utils.calculate_match_score(item_text, search_terms, name_parts)
55
+ assert result['score'] > 0
56
+ assert len(result['matched_terms']) > 0
57
+ assert result['has_match'] is True
58
+
59
+
60
+ def test_search_items_with_terms():
61
+ """Test searching items with terms."""
62
+ items = [
63
+ {'name': 'TestItem1', 'description': 'This is a test item'},
64
+ {'name': 'TestItem2', 'description': 'This is another test item'},
65
+ ]
66
+
67
+ def get_text_fn(item):
68
+ return item['description']
69
+
70
+ def get_name_parts_fn(item):
71
+ return [item['name']]
72
+
73
+ results = search_utils.search_items_with_terms(items, ['test'], get_text_fn, get_name_parts_fn)
74
+
75
+ assert len(results) == 2
76
+ assert results[0]['score'] >= results[1]['score']
77
+ assert 'test' in results[0]['matched_terms']
@@ -0,0 +1,55 @@
1
+ import pytest
2
+ from awslabs.cdk_mcp_server.core.server import main, mcp
3
+ from unittest.mock import patch
4
+
5
+
6
+ def test_mcp_server_initialization():
7
+ """Test MCP server initialization."""
8
+ # Check server name
9
+ assert mcp.name == 'AWS CDK MCP Server'
10
+
11
+ # Check dependencies
12
+ assert 'pydantic' in mcp.dependencies
13
+ assert 'aws-lambda-powertools' in mcp.dependencies
14
+ assert 'httpx' in mcp.dependencies
15
+
16
+
17
+ @pytest.mark.asyncio
18
+ async def test_mcp_server_tool_registration():
19
+ """Test MCP server tool registration."""
20
+ # Get all registered tools
21
+ tools = await mcp.list_tools()
22
+
23
+ # Check CDK tools
24
+ assert any(t.name == 'CDKGeneralGuidance' for t in tools)
25
+ assert any(t.name == 'ExplainCDKNagRule' for t in tools)
26
+ assert any(t.name == 'CheckCDKNagSuppressions' for t in tools)
27
+
28
+ # Check Bedrock tools
29
+ assert any(t.name == 'GenerateBedrockAgentSchema' for t in tools)
30
+
31
+ # Check Solutions Constructs tools
32
+ assert any(t.name == 'GetAwsSolutionsConstructPattern' for t in tools)
33
+
34
+ # Check GenAI CDK Constructs tools
35
+ assert any(t.name == 'SearchGenAICDKConstructs' for t in tools)
36
+
37
+ # Check Lambda tools
38
+ assert any(t.name == 'LambdaLayerDocumentationProvider' for t in tools)
39
+
40
+
41
+ @patch('awslabs.cdk_mcp_server.core.server.mcp.run')
42
+ def test_main_with_default_args(mock_run):
43
+ """Test main function with default arguments."""
44
+ with patch('sys.argv', ['server.py']):
45
+ main()
46
+ mock_run.assert_called_once_with()
47
+
48
+
49
+ @patch('awslabs.cdk_mcp_server.core.server.mcp.run')
50
+ def test_main_with_sse_args(mock_run):
51
+ """Test main function with SSE transport arguments."""
52
+ with patch('sys.argv', ['server.py', '--sse', '--port', '9999']):
53
+ main()
54
+ assert mcp.settings.port == 9999
55
+ mock_run.assert_called_once_with(transport='sse')
@@ -0,0 +1,153 @@
1
+ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance
4
+ # with the License. A copy of the License is located at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES
9
+ # OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions
10
+ # and limitations under the License.
11
+ import pytest
12
+ from awslabs.cdk_mcp_server.core.tools import (
13
+ bedrock_schema_generator_from_file,
14
+ cdk_guidance,
15
+ check_cdk_nag_suppressions_tool,
16
+ explain_cdk_nag_rule,
17
+ get_aws_solutions_construct_pattern,
18
+ lambda_layer_documentation_provider,
19
+ search_genai_cdk_constructs,
20
+ )
21
+ from unittest.mock import MagicMock
22
+
23
+
24
+ @pytest.fixture
25
+ def mock_context():
26
+ """Fixture that provides a mocked MCP context."""
27
+ context = MagicMock()
28
+ context.settings = MagicMock()
29
+ return context
30
+
31
+
32
+ @pytest.mark.asyncio
33
+ async def test_cdk_guidance(mock_context):
34
+ """Test CDK guidance tool."""
35
+ result = await cdk_guidance(mock_context)
36
+ assert isinstance(result, str)
37
+ assert len(result) > 0
38
+
39
+
40
+ @pytest.mark.asyncio
41
+ async def test_explain_cdk_nag_rule(mock_context):
42
+ """Test CDK Nag rule explanation tool."""
43
+ result = await explain_cdk_nag_rule(mock_context, rule_id='AwsSolutions-APIG3')
44
+ assert isinstance(result, dict)
45
+ assert 'rule_id' in result
46
+ assert 'content' in result
47
+ assert 'source' in result
48
+ assert 'status' in result
49
+ assert result['rule_id'] == 'AwsSolutions-APIG3'
50
+ assert result['source'] == 'https://github.com/cdklabs/cdk-nag/blob/main/RULES.md'
51
+ assert result['status'] == 'success'
52
+
53
+
54
+ @pytest.mark.asyncio
55
+ async def test_check_cdk_nag_suppressions_tool(mock_context):
56
+ """Test CDK Nag suppressions check tool."""
57
+ result = await check_cdk_nag_suppressions_tool(mock_context, code='test code')
58
+ assert isinstance(result, dict)
59
+ assert 'has_suppressions' in result
60
+ assert 'message' in result
61
+ assert 'status' in result
62
+
63
+
64
+ @pytest.mark.asyncio
65
+ async def test_bedrock_schema_generator_from_file_throws_error(mock_context):
66
+ """Test Bedrock schema generator tool."""
67
+ with pytest.raises(Exception):
68
+ await bedrock_schema_generator_from_file(
69
+ mock_context,
70
+ lambda_code_path='test.py', # non existing path
71
+ output_path='test.json', # non existing path
72
+ )
73
+
74
+
75
+ @pytest.mark.asyncio
76
+ async def test_get_aws_solutions_construct_pattern(mock_context):
77
+ """Test AWS Solutions Construct pattern tool."""
78
+ result = await get_aws_solutions_construct_pattern(mock_context, pattern_name='aws-alb-lambda')
79
+ assert isinstance(result, dict)
80
+ assert 'description' in result
81
+ assert 'use_cases' in result
82
+ assert 'services' in result
83
+ assert 'documentation_uri' in result
84
+ assert result['pattern_name'] == 'aws-alb-lambda'
85
+ assert result['services'] == ['Application Load Balancer', 'Lambda']
86
+ assert (
87
+ result['description']
88
+ == 'This AWS Solutions Construct implements an an Application Load Balancer to an AWS Lambda function'
89
+ )
90
+ assert result['documentation_uri'] == 'aws-solutions-constructs://aws-alb-lambda'
91
+
92
+
93
+ @pytest.mark.asyncio
94
+ async def test_search_genai_cdk_constructs(mock_context):
95
+ """Test GenAI CDK constructs search tool."""
96
+ result = await search_genai_cdk_constructs(mock_context, query='knowledge base')
97
+ assert isinstance(result, dict)
98
+ assert 'count' in result
99
+ assert 'results' in result
100
+ assert 'status' in result
101
+ assert 'installation_required' in result
102
+ assert result['status'] == 'success'
103
+ assert (
104
+ result['installation_required']['package_name'] == '@cdklabs/generative-ai-cdk-constructs'
105
+ )
106
+ assert (
107
+ result['installation_required']['message']
108
+ == 'This construct requires the @cdklabs/generative-ai-cdk-constructs package to be installed'
109
+ )
110
+
111
+
112
+ @pytest.mark.asyncio
113
+ async def test_lambda_layer_documentation_provider_generic(mock_context):
114
+ """Test Lambda layer documentation provider tool."""
115
+ result = await lambda_layer_documentation_provider(mock_context, layer_type='generic')
116
+ assert isinstance(result, dict)
117
+ assert 'code_examples' in result
118
+ assert 'directory_structure' in result
119
+ assert 'source_url' in result
120
+ assert 'layer_type' in result
121
+ assert result['layer_type'] == 'generic'
122
+
123
+
124
+ @pytest.mark.asyncio
125
+ async def test_lambda_layer_documentation_provider_python(mock_context):
126
+ """Test Lambda layer documentation provider tool."""
127
+ result = await lambda_layer_documentation_provider(mock_context, layer_type='python')
128
+ assert isinstance(result, dict)
129
+ assert 'layer_type' in result
130
+ assert 'documentation_source' in result
131
+ assert 'documentation_usage_guide' in result
132
+ assert 'code_generation_guidance' in result
133
+ assert result['layer_type'] == 'python'
134
+ assert result['documentation_source']['server'] == 'awslabs.aws-documentation-mcp-server'
135
+ assert result['documentation_source']['tool'] == 'read_documentation'
136
+ assert result['documentation_source']['parameters']['max_length'] == 10000
137
+ assert (
138
+ result['documentation_usage_guide']['when_to_fetch_full_docs']
139
+ == 'Fetch full documentation to view detailed property definitions, learn about optional parameters, and find additional code examples'
140
+ )
141
+ assert result['documentation_usage_guide']['contains_sample_code'] == True # noqa: E712
142
+ assert result['documentation_usage_guide']['contains_props_documentation'] == True # noqa: E712
143
+ assert result['code_generation_guidance']['imports'] == [
144
+ "import { PythonLayerVersion } from '@aws-cdk/aws-lambda-python-alpha'"
145
+ ]
146
+ assert result['code_generation_guidance']['construct_types'] == {
147
+ 'python': 'PythonLayerVersion'
148
+ }
149
+ assert result['code_generation_guidance']['required_properties'] == {'python': ['entry']}
150
+ assert (
151
+ result['code_generation_guidance']['sample_code']
152
+ == "new python.PythonLayerVersion(this, 'MyLayer', {\n entry: '/path/to/my/layer', // point this to your library's directory\n})"
153
+ )