awslabs.terraform-mcp-server 0.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.terraform-mcp-server might be problematic. Click here for more details.

Files changed (32) hide show
  1. awslabs/__init__.py +2 -0
  2. awslabs/terraform_mcp_server/__init__.py +3 -0
  3. awslabs/terraform_mcp_server/impl/resources/__init__.py +11 -0
  4. awslabs/terraform_mcp_server/impl/resources/terraform_aws_provider_resources_listing.py +52 -0
  5. awslabs/terraform_mcp_server/impl/resources/terraform_awscc_provider_resources_listing.py +55 -0
  6. awslabs/terraform_mcp_server/impl/tools/__init__.py +15 -0
  7. awslabs/terraform_mcp_server/impl/tools/execute_terraform_command.py +206 -0
  8. awslabs/terraform_mcp_server/impl/tools/run_checkov_scan.py +359 -0
  9. awslabs/terraform_mcp_server/impl/tools/search_aws_provider_docs.py +677 -0
  10. awslabs/terraform_mcp_server/impl/tools/search_awscc_provider_docs.py +627 -0
  11. awslabs/terraform_mcp_server/impl/tools/search_specific_aws_ia_modules.py +444 -0
  12. awslabs/terraform_mcp_server/impl/tools/utils.py +558 -0
  13. awslabs/terraform_mcp_server/models/__init__.py +27 -0
  14. awslabs/terraform_mcp_server/models/models.py +260 -0
  15. awslabs/terraform_mcp_server/scripts/generate_aws_provider_resources.py +1224 -0
  16. awslabs/terraform_mcp_server/scripts/generate_awscc_provider_resources.py +1020 -0
  17. awslabs/terraform_mcp_server/scripts/scrape_aws_terraform_best_practices.py +129 -0
  18. awslabs/terraform_mcp_server/server.py +329 -0
  19. awslabs/terraform_mcp_server/static/AWSCC_PROVIDER_RESOURCES.md +3125 -0
  20. awslabs/terraform_mcp_server/static/AWS_PROVIDER_RESOURCES.md +3833 -0
  21. awslabs/terraform_mcp_server/static/AWS_TERRAFORM_BEST_PRACTICES.md +2523 -0
  22. awslabs/terraform_mcp_server/static/MCP_INSTRUCTIONS.md +126 -0
  23. awslabs/terraform_mcp_server/static/TERRAFORM_WORKFLOW_GUIDE.md +198 -0
  24. awslabs/terraform_mcp_server/static/__init__.py +22 -0
  25. awslabs/terraform_mcp_server/tests/__init__.py +1 -0
  26. awslabs/terraform_mcp_server/tests/run_tests.sh +35 -0
  27. awslabs/terraform_mcp_server/tests/test_parameter_annotations.py +207 -0
  28. awslabs/terraform_mcp_server/tests/test_tool_implementations.py +309 -0
  29. awslabs_terraform_mcp_server-0.0.1.dist-info/METADATA +97 -0
  30. awslabs_terraform_mcp_server-0.0.1.dist-info/RECORD +32 -0
  31. awslabs_terraform_mcp_server-0.0.1.dist-info/WHEEL +4 -0
  32. awslabs_terraform_mcp_server-0.0.1.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,129 @@
1
+ """Script to download and extract content from AWS Terraform best practices PDF and save it as markdown."""
2
+
3
+ import io
4
+ import os
5
+ import PyPDF2
6
+ import re
7
+ import requests
8
+ from pathlib import Path
9
+
10
+
11
+ # URL to the PDF
12
+ PDF_URL = 'https://docs.aws.amazon.com/pdfs/prescriptive-guidance/latest/terraform-aws-provider-best-practices/terraform-aws-provider-best-practices.pdf'
13
+
14
+ # Output file path
15
+ OUTPUT_DIR = Path(__file__).parent.parent / 'static'
16
+ OUTPUT_FILE = 'AWS_TERRAFORM_BEST_PRACTICES.md'
17
+
18
+
19
+ def download_pdf(url):
20
+ """Download PDF from URL and return as bytes."""
21
+ print(f'Downloading PDF from {url}...')
22
+ response = requests.get(url)
23
+ response.raise_for_status() # Raise an exception for HTTP errors
24
+ return response.content
25
+
26
+
27
+ def extract_text_from_pdf(pdf_bytes):
28
+ """Extract text from PDF bytes."""
29
+ print('Extracting text from PDF...')
30
+ pdf_file = io.BytesIO(pdf_bytes)
31
+ reader = PyPDF2.PdfReader(pdf_file)
32
+
33
+ text = ''
34
+ for page_num in range(len(reader.pages)):
35
+ page = reader.pages[page_num]
36
+ text += page.extract_text() + '\n\n'
37
+
38
+ return text
39
+
40
+
41
+ def convert_to_markdown(text):
42
+ """Convert extracted text to markdown format."""
43
+ print('Converting text to markdown...')
44
+
45
+ # Add title
46
+ markdown = '# AWS Terraform Provider Best Practices\n\n'
47
+ markdown += (
48
+ '_This document was automatically extracted from the AWS Prescriptive Guidance PDF._\n\n'
49
+ )
50
+ markdown += f'_Source: [{PDF_URL}]({PDF_URL})_\n\n'
51
+
52
+ # Process the text
53
+ lines = text.split('\n')
54
+ current_section = ''
55
+
56
+ for line in lines:
57
+ # Skip empty lines
58
+ if not line.strip():
59
+ continue
60
+
61
+ # Try to identify headers
62
+ if re.match(r'^[A-Z][A-Za-z\s]{2,}$', line.strip()) and len(line.strip()) < 80:
63
+ # Looks like a header
64
+ current_section = line.strip()
65
+
66
+ # Stop before Document history section
67
+ if current_section.lower() == 'document history':
68
+ break
69
+
70
+ markdown += f'## {current_section}\n\n'
71
+ continue
72
+
73
+ # Process content
74
+ if re.match(r'^\d+\.\s+[A-Z]', line.strip()):
75
+ # Numbered section
76
+ markdown += f'### {line.strip()}\n\n'
77
+ else:
78
+ # Regular text
79
+ # Check if it's a bullet point
80
+ if line.strip().startswith('•') or line.strip().startswith('-'):
81
+ markdown += f'{line.strip()}\n\n'
82
+ else:
83
+ markdown += f'{line.strip()}\n\n'
84
+
85
+ # Clean up the markdown
86
+ # Remove page numbers and headers/footers
87
+ markdown = re.sub(r'\n\d+\n', '\n', markdown)
88
+
89
+ # Fix bullet points
90
+ markdown = markdown.replace('•', '*')
91
+
92
+ # Remove excessive newlines
93
+ markdown = re.sub(r'\n{3,}', '\n\n', markdown)
94
+
95
+ return markdown
96
+
97
+
98
+ def main():
99
+ """Execute the main workflow to download, extract, and convert AWS Terraform best practices to markdown.
100
+
101
+ Downloads the PDF from the specified URL, extracts the text content, converts it to markdown format,
102
+ and saves it to the output file. Creates the output directory if it doesn't exist.
103
+ """
104
+ # Create output directory if it doesn't exist
105
+ os.makedirs(OUTPUT_DIR, exist_ok=True)
106
+
107
+ try:
108
+ # Download the PDF
109
+ pdf_bytes = download_pdf(PDF_URL)
110
+
111
+ # Extract text from PDF
112
+ text = extract_text_from_pdf(pdf_bytes)
113
+
114
+ # Convert to markdown
115
+ markdown = convert_to_markdown(text)
116
+
117
+ # Write to file
118
+ output_path = OUTPUT_DIR / OUTPUT_FILE
119
+ with open(output_path, 'w', encoding='utf-8') as f:
120
+ f.write(markdown)
121
+
122
+ print(f'Successfully saved to {output_path}')
123
+
124
+ except Exception as e:
125
+ print(f'Error: {e}')
126
+
127
+
128
+ if __name__ == '__main__':
129
+ main()
@@ -0,0 +1,329 @@
1
+ #!/usr/bin/env python3
2
+ """terraform MCP server implementation."""
3
+
4
+ import argparse
5
+ from awslabs.terraform_mcp_server.impl.resources import (
6
+ terraform_aws_provider_assets_listing_impl,
7
+ terraform_awscc_provider_resources_listing_impl,
8
+ )
9
+ from awslabs.terraform_mcp_server.impl.tools import (
10
+ execute_terraform_command_impl,
11
+ run_checkov_scan_impl,
12
+ search_aws_provider_docs_impl,
13
+ search_awscc_provider_docs_impl,
14
+ search_specific_aws_ia_modules_impl,
15
+ )
16
+ from awslabs.terraform_mcp_server.models import (
17
+ CheckovScanRequest,
18
+ CheckovScanResult,
19
+ ModuleSearchResult,
20
+ TerraformAWSCCProviderDocsResult,
21
+ TerraformAWSProviderDocsResult,
22
+ TerraformExecutionRequest,
23
+ TerraformExecutionResult,
24
+ )
25
+ from awslabs.terraform_mcp_server.static import (
26
+ AWS_TERRAFORM_BEST_PRACTICES,
27
+ MCP_INSTRUCTIONS,
28
+ TERRAFORM_WORKFLOW_GUIDE,
29
+ )
30
+ from mcp.server.fastmcp import FastMCP
31
+ from pydantic import Field
32
+ from typing import Dict, List, Literal, Optional
33
+
34
+
35
+ mcp = FastMCP(
36
+ 'terraform_mcp_server',
37
+ instructions=f'{MCP_INSTRUCTIONS}',
38
+ dependencies=[
39
+ 'pydantic',
40
+ 'loguru',
41
+ 'requests',
42
+ 'beautifulsoup4',
43
+ 'PyPDF2',
44
+ ],
45
+ )
46
+
47
+
48
+ # * Tools
49
+ @mcp.tool(name='ExecuteTerraformCommand')
50
+ async def execute_terraform_command(
51
+ command: Literal['init', 'plan', 'validate', 'apply', 'destroy'] = Field(
52
+ ..., description='Terraform command to execute'
53
+ ),
54
+ working_directory: str = Field(..., description='Directory containing Terraform files'),
55
+ variables: Optional[Dict[str, str]] = Field(None, description='Terraform variables to pass'),
56
+ aws_region: Optional[str] = Field(None, description='AWS region to use'),
57
+ strip_ansi: bool = Field(True, description='Whether to strip ANSI color codes from output'),
58
+ ) -> TerraformExecutionResult:
59
+ """Execute Terraform workflow commands against an AWS account.
60
+
61
+ This tool runs Terraform commands (init, plan, validate, apply, destroy) in the
62
+ specified working directory, with optional variables and region settings.
63
+
64
+ Parameters:
65
+ command: Terraform command to execute
66
+ working_directory: Directory containing Terraform files
67
+ variables: Terraform variables to pass
68
+ aws_region: AWS region to use
69
+ strip_ansi: Whether to strip ANSI color codes from output
70
+
71
+ Returns:
72
+ A TerraformExecutionResult object containing command output and status
73
+ """
74
+ request = TerraformExecutionRequest(
75
+ command=command,
76
+ working_directory=working_directory,
77
+ variables=variables,
78
+ aws_region=aws_region,
79
+ strip_ansi=strip_ansi,
80
+ )
81
+ return await execute_terraform_command_impl(request)
82
+
83
+
84
+ @mcp.tool(name='SearchAwsProviderDocs')
85
+ async def search_aws_provider_docs(
86
+ asset_name: str = Field(
87
+ ...,
88
+ description='Name of the AWS service (asset) to look for (e.g., "aws_s3_bucket", "aws_lambda_function")',
89
+ ),
90
+ asset_type: str = Field(
91
+ 'resource',
92
+ description="Type of documentation to search - 'resource' (default), 'data_source', or 'both'",
93
+ ),
94
+ ) -> List[TerraformAWSProviderDocsResult]:
95
+ """Search AWS provider documentation for resources and attributes.
96
+
97
+ This tool searches the Terraform AWS provider documentation for information about
98
+ a specific asset in the AWS Provider Documentation, assets can be either resources or data sources. It retrieves comprehensive details including descriptions, example code snippets, argument references, and attribute references.
99
+
100
+ Use the 'asset_type' parameter to specify if you are looking for information about provider resources, data sources, or both. Valid values are 'resource', 'data_source' or 'both'.
101
+
102
+ The tool will automatically handle prefixes - you can search for either 'aws_s3_bucket' or 's3_bucket'.
103
+
104
+ Examples:
105
+ - To get documentation for an S3 bucket resource:
106
+ search_aws_provider_docs(asset_name='aws_s3_bucket')
107
+
108
+ - To search only for data sources:
109
+ search_aws_provider_docs(asset_name='aws_ami', asset_type='data_source')
110
+
111
+ - To search for both resource and data source documentation of a given name:
112
+ search_aws_provider_docs(asset_name='aws_instance', asset_type='both')
113
+
114
+ Parameters:
115
+ asset_name: Name of the service (asset) to look for (e.g., 'aws_s3_bucket', 'aws_lambda_function')
116
+ asset_type: Type of documentation to search - 'resource' (default), 'data_source', or 'both'
117
+
118
+ Returns:
119
+ A list of matching documentation entries with details including:
120
+ - Resource name and description
121
+ - URL to the official documentation
122
+ - Example code snippets
123
+ - Arguments with descriptions
124
+ - Attributes with descriptions
125
+ """
126
+ return await search_aws_provider_docs_impl(asset_name, asset_type)
127
+
128
+
129
+ @mcp.tool(name='SearchAwsccProviderDocs')
130
+ async def search_awscc_provider_docs(
131
+ asset_name: str = Field(
132
+ ...,
133
+ description='Name of the AWSCC service (asset) to look for (e.g., awscc_s3_bucket, awscc_lambda_function)',
134
+ ),
135
+ asset_type: str = Field(
136
+ 'resource',
137
+ description="Type of documentation to search - 'resource' (default), 'data_source', or 'both'",
138
+ ),
139
+ ) -> List[TerraformAWSCCProviderDocsResult]:
140
+ """Search AWSCC provider documentation for resources and attributes.
141
+
142
+ The AWSCC provider is based on the AWS Cloud Control API
143
+ and provides a more consistent interface to AWS resources compared to the standard AWS provider.
144
+
145
+ This tool searches the Terraform AWSCC provider documentation for information about
146
+ a specific asset in the AWSCC Provider Documentation, assets can be either resources or data sources. It retrieves comprehensive details including descriptions, example code snippets, and schema references.
147
+
148
+ Use the 'asset_type' parameter to specify if you are looking for information about provider resources, data sources, or both. Valid values are 'resource', 'data_source' or 'both'.
149
+
150
+ The tool will automatically handle prefixes - you can search for either 'awscc_s3_bucket' or 's3_bucket'.
151
+
152
+ Examples:
153
+ - To get documentation for an S3 bucket resource:
154
+ search_awscc_provider_docs(asset_name='awscc_s3_bucket')
155
+ search_awscc_provider_docs(asset_name='awscc_s3_bucket', asset_type='resource')
156
+
157
+ - To search only for data sources:
158
+ search_aws_provider_docs(asset_name='awscc_appsync_api', kind='data_source')
159
+
160
+ - To search for both resource and data source documentation of a given name:
161
+ search_aws_provider_docs(asset_name='awscc_appsync_api', kind='both')
162
+
163
+ - Search of a resource without the prefix:
164
+ search_awscc_provider_docs(resource_type='ec2_instance')
165
+
166
+ Parameters:
167
+ asset_name: Name of the AWSCC Provider resource or data source to look for (e.g., 'awscc_s3_bucket', 'awscc_lambda_function')
168
+ asset_type: Type of documentation to search - 'resource' (default), 'data_source', or 'both'. Some resources and data sources share the same name
169
+
170
+ Returns:
171
+ A list of matching documentation entries with details including:
172
+ - Resource name and description
173
+ - URL to the official documentation
174
+ - Example code snippets
175
+ - Schema information (required, optional, read-only, and nested structures attributes)
176
+ """
177
+ return await search_awscc_provider_docs_impl(asset_name, asset_type)
178
+
179
+
180
+ @mcp.tool(name='SearchSpecificAwsIaModules')
181
+ async def search_specific_aws_ia_modules(
182
+ query: str = Field(
183
+ ..., description='Optional search term to filter modules (empty returns all four modules)'
184
+ ),
185
+ ) -> List[ModuleSearchResult]:
186
+ """Search for specific AWS-IA Terraform modules.
187
+
188
+ This tool checks for information about four specific AWS-IA modules:
189
+ - aws-ia/bedrock/aws - Amazon Bedrock module for generative AI applications
190
+ - aws-ia/opensearch-serverless/aws - OpenSearch Serverless collection for vector search
191
+ - aws-ia/sagemaker-endpoint/aws - SageMaker endpoint deployment module
192
+ - aws-ia/serverless-streamlit-app/aws - Serverless Streamlit application deployment
193
+
194
+ It returns detailed information about these modules, including their README content,
195
+ variables.tf content, and submodules when available.
196
+
197
+ The search is performed across module names, descriptions, README content, and variable
198
+ definitions. This allows you to find modules based on their functionality or specific
199
+ configuration options.
200
+
201
+ Examples:
202
+ - To get information about all four modules:
203
+ search_specific_aws_ia_modules()
204
+
205
+ - To find modules related to Bedrock:
206
+ search_specific_aws_ia_modules(query='bedrock')
207
+
208
+ - To find modules related to vector search:
209
+ search_specific_aws_ia_modules(query='vector search')
210
+
211
+ - To find modules with specific configuration options:
212
+ search_specific_aws_ia_modules(query='endpoint_name')
213
+
214
+ Parameters:
215
+ query: Optional search term to filter modules (empty returns all four modules)
216
+
217
+ Returns:
218
+ A list of matching modules with their details, including:
219
+ - Basic module information (name, namespace, version)
220
+ - Module documentation (README content)
221
+ - Input and output parameter counts
222
+ - Variables from variables.tf with descriptions and default values
223
+ - Submodules information
224
+ - Version details and release information
225
+ """
226
+ return await search_specific_aws_ia_modules_impl(query)
227
+
228
+
229
+ @mcp.tool(name='RunCheckovScan')
230
+ async def run_checkov_scan(
231
+ working_directory: str = Field(..., description='Directory containing Terraform files'),
232
+ framework: str = Field(
233
+ 'terraform', description='Framework to scan (terraform, cloudformation, etc.)'
234
+ ),
235
+ check_ids: Optional[List[str]] = Field(None, description='Specific check IDs to run'),
236
+ skip_check_ids: Optional[List[str]] = Field(None, description='Check IDs to skip'),
237
+ output_format: str = Field('json', description='Output format (json, cli, etc.)'),
238
+ ) -> CheckovScanResult:
239
+ """Run Checkov security scan on Terraform code.
240
+
241
+ This tool runs Checkov to scan Terraform code for security and compliance issues,
242
+ identifying potential vulnerabilities and misconfigurations according to best practices.
243
+
244
+ Checkov (https://www.checkov.io/) is an open-source static code analysis tool that
245
+ can detect hundreds of security and compliance issues in infrastructure-as-code.
246
+
247
+ Parameters:
248
+ working_directory: Directory containing Terraform files to scan
249
+ framework: Framework to scan (default: terraform)
250
+ check_ids: Optional list of specific check IDs to run
251
+ skip_check_ids: Optional list of check IDs to skip
252
+ output_format: Format for scan results (default: json)
253
+
254
+ Returns:
255
+ A CheckovScanResult object containing scan results and identified vulnerabilities
256
+ """
257
+ request = CheckovScanRequest(
258
+ working_directory=working_directory,
259
+ framework=framework,
260
+ check_ids=check_ids,
261
+ skip_check_ids=skip_check_ids,
262
+ output_format=output_format,
263
+ )
264
+ return await run_checkov_scan_impl(request)
265
+
266
+
267
+ # * Resources
268
+ @mcp.resource(
269
+ name='terraform_development_workflow',
270
+ uri='terraform://development_workflow',
271
+ description='Terraform Development Workflow Guide with integrated validation and security scanning',
272
+ mime_type='text/markdown',
273
+ )
274
+ async def terraform_development_workflow() -> str:
275
+ """Provides guidance for developing Terraform code and integrates with Terraform workflow commands."""
276
+ return f'{TERRAFORM_WORKFLOW_GUIDE}'
277
+
278
+
279
+ @mcp.resource(
280
+ name='terraform_aws_provider_resources_listing',
281
+ uri='terraform://aws_provider_resources_listing',
282
+ description='Comprehensive listing of AWS provider resources and data sources by service category',
283
+ mime_type='text/markdown',
284
+ )
285
+ async def terraform_aws_provider_resources_listing() -> str:
286
+ """Provides an up-to-date categorized listing of all AWS provider resources and data sources."""
287
+ return await terraform_aws_provider_assets_listing_impl()
288
+
289
+
290
+ @mcp.resource(
291
+ name='terraform_awscc_provider_resources_listing',
292
+ uri='terraform://awscc_provider_resources_listing',
293
+ description='Comprehensive listing of AWSCC provider resources and data sources by service category',
294
+ mime_type='text/markdown',
295
+ )
296
+ async def terraform_awscc_provider_resources_listing() -> str:
297
+ """Provides an up-to-date categorized listing of all AWSCC provider resources and data sources."""
298
+ return await terraform_awscc_provider_resources_listing_impl()
299
+
300
+
301
+ @mcp.resource(
302
+ name='terraform_aws_best_practices',
303
+ uri='terraform://aws_best_practices',
304
+ description='AWS Terraform Provider Best Practices from AWS Prescriptive Guidance',
305
+ mime_type='text/markdown',
306
+ )
307
+ async def terraform_aws_best_practices() -> str:
308
+ """Provides AWS Terraform Provider Best Practices guidance."""
309
+ return f'{AWS_TERRAFORM_BEST_PRACTICES}'
310
+
311
+
312
+ def main():
313
+ """Run the MCP server with CLI argument support."""
314
+ parser = argparse.ArgumentParser(description='A Model Context Protocol (MCP) server')
315
+ parser.add_argument('--sse', action='store_true', help='Use SSE transport')
316
+ parser.add_argument('--port', type=int, default=8888, help='Port to run the server on')
317
+
318
+ args = parser.parse_args()
319
+
320
+ # Run server with appropriate transport
321
+ if args.sse:
322
+ mcp.settings.port = args.port
323
+ mcp.run(transport='sse')
324
+ else:
325
+ mcp.run()
326
+
327
+
328
+ if __name__ == '__main__':
329
+ main()