awslabs.terraform-mcp-server 0.0.2__py3-none-any.whl → 0.0.6__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: awslabs.terraform-mcp-server
3
- Version: 0.0.2
3
+ Version: 0.0.6
4
4
  Summary: An AWS Labs Model Context Protocol (MCP) server for terraform
5
5
  Requires-Python: >=3.10
6
6
  Requires-Dist: beautifulsoup4>=4.12.0
@@ -22,11 +22,7 @@ awslabs/terraform_mcp_server/static/AWS_TERRAFORM_BEST_PRACTICES.md,sha256=cftJ9
22
22
  awslabs/terraform_mcp_server/static/MCP_INSTRUCTIONS.md,sha256=k6XQFgab3_W7_g0AZNXdyxyrzA2RjOzP9DbXS_9FiP4,6010
23
23
  awslabs/terraform_mcp_server/static/TERRAFORM_WORKFLOW_GUIDE.md,sha256=qvqtaikyyQ_XHL-LyU_BDos1ym2kKBR7ljMZtLn3FAs,9322
24
24
  awslabs/terraform_mcp_server/static/__init__.py,sha256=J5JGKYybg48XyBi2hepC101RDNFBxSXZS5YGvj0tql8,549
25
- awslabs/terraform_mcp_server/tests/__init__.py,sha256=eWyaXCIPzCC8_4kXuKnK9s79gg6_J3XYoAtQzkNesWI,45
26
- awslabs/terraform_mcp_server/tests/run_tests.sh,sha256=u6GUSQffYJyn0RkAd4qeqkczYc_cf-lL3kC4a1YYjB4,1068
27
- awslabs/terraform_mcp_server/tests/test_parameter_annotations.py,sha256=7DrC-idSQmrSAAPLHQ09V3sU7a_8HkfCrOnqJn2SQeo,8361
28
- awslabs/terraform_mcp_server/tests/test_tool_implementations.py,sha256=Uh8vyFJJ9ti14hkmQDkWJEtGUJvFR3IZ0P90ez0LiMY,11641
29
- awslabs_terraform_mcp_server-0.0.2.dist-info/METADATA,sha256=wNYoFnJY-jLzLtJuCwGHQ8kxrIUC9uEp8D4BvYWxQiY,4344
30
- awslabs_terraform_mcp_server-0.0.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
31
- awslabs_terraform_mcp_server-0.0.2.dist-info/entry_points.txt,sha256=jCTPQeUJ74jpIDcYwVHQCk-y0n0ehRFFerh_Qm4ZU1c,90
32
- awslabs_terraform_mcp_server-0.0.2.dist-info/RECORD,,
25
+ awslabs_terraform_mcp_server-0.0.6.dist-info/METADATA,sha256=bzMibDYrJ7WJw5duVCwOlTSJyHs5ow_CvJ0OZC-nE8g,4344
26
+ awslabs_terraform_mcp_server-0.0.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
27
+ awslabs_terraform_mcp_server-0.0.6.dist-info/entry_points.txt,sha256=jCTPQeUJ74jpIDcYwVHQCk-y0n0ehRFFerh_Qm4ZU1c,90
28
+ awslabs_terraform_mcp_server-0.0.6.dist-info/RECORD,,
@@ -1 +0,0 @@
1
- """Test package for terraform_mcp_server."""
@@ -1,35 +0,0 @@
1
- #!/bin/bash
2
- # Script to run the Terraform MCP server tests
3
-
4
- SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
5
- PROJECT_ROOT="$SCRIPT_DIR/../../.."
6
-
7
- # Set PYTHONPATH to include the project root
8
- export PYTHONPATH="$PROJECT_ROOT:$PYTHONPATH"
9
-
10
- # Function to run a test module
11
- run_test() {
12
- echo "Running $1..."
13
- cd "$PROJECT_ROOT"
14
- python -m awslabs.terraform_mcp_server.tests.$1
15
- echo "Test completed: $1"
16
- }
17
-
18
- # Get the test name from the first argument, default to all tests
19
- TEST_NAME=$1
20
- if [ -z "$TEST_NAME" ]; then
21
- echo "=== Running All Tests ==="
22
- run_test "test_parameter_annotations"
23
- run_test "test_tool_implementations"
24
- elif [ "$TEST_NAME" == "params" ]; then
25
- run_test "test_parameter_annotations"
26
- elif [ "$TEST_NAME" == "tools" ]; then
27
- run_test "test_tool_implementations"
28
- else
29
- echo "Unknown test: $TEST_NAME"
30
- echo "Usage: $0 [params|tools]"
31
- echo " params - Run parameter annotation tests"
32
- echo " tools - Run tool implementation tests"
33
- echo " (no args) - Run all tests"
34
- exit 1
35
- fi
@@ -1,207 +0,0 @@
1
- """Test script for verifying parameter annotations in MCP tools."""
2
-
3
- import json
4
- import sys
5
- from awslabs.terraform_mcp_server.server import mcp
6
- from pathlib import Path
7
-
8
-
9
- # Add project root to path to allow importing the server
10
- project_root = str(Path(__file__).parent.parent.parent.parent)
11
- if project_root not in sys.path:
12
- sys.path.insert(0, project_root)
13
-
14
-
15
- def print_tool_parameters():
16
- """Print the parameters for each tool after annotations are added."""
17
- tool_names = [
18
- 'SearchAwsProviderDocs',
19
- 'ExecuteTerraformCommand',
20
- 'SearchAwsccProviderDocs',
21
- 'SearchSpecificAwsIaModules',
22
- 'RunCheckovScan',
23
- ]
24
-
25
- print('\n=== Current Tool Parameter Schemas ===\n')
26
- for tool_name in tool_names:
27
- try:
28
- tool = mcp._tool_manager.get_tool(tool_name)
29
- if tool is None:
30
- print(f'Tool {tool_name} not found')
31
- continue
32
-
33
- if not hasattr(tool, 'parameters') or tool.parameters is None:
34
- print(f'Tool {tool_name} has no parameters schema')
35
- continue
36
-
37
- print(f'=== {tool_name} Parameters Schema ===')
38
- print(json.dumps(tool.parameters, indent=2))
39
- print('\n')
40
- except Exception as e:
41
- print(f'Error getting tool {tool_name}: {e}')
42
-
43
-
44
- def add_parameter_annotations():
45
- """Add parameter annotations to the MCP tools."""
46
- print('Adding parameter annotations to MCP tools...\n')
47
-
48
- # Add parameter descriptions for SearchAwsProviderDocs
49
- search_tool = mcp._tool_manager.get_tool('SearchAwsProviderDocs')
50
- if (
51
- search_tool is not None
52
- and hasattr(search_tool, 'parameters')
53
- and search_tool.parameters is not None
54
- ):
55
- if (
56
- 'properties' in search_tool.parameters
57
- and 'asset_name' in search_tool.parameters['properties']
58
- ):
59
- search_tool.parameters['properties']['asset_name']['description'] = (
60
- 'Name of the AWS service (asset) to look for (e.g., "aws_s3_bucket", "aws_lambda_function")'
61
- )
62
- if (
63
- 'properties' in search_tool.parameters
64
- and 'asset_type' in search_tool.parameters['properties']
65
- ):
66
- search_tool.parameters['properties']['asset_type']['description'] = (
67
- "Type of documentation to search - 'resource', 'data_source', or 'both' (default)"
68
- )
69
-
70
- # Add parameter descriptions for SearchAwsccProviderDocs
71
- awscc_docs_tool = mcp._tool_manager.get_tool('SearchAwsccProviderDocs')
72
- if (
73
- awscc_docs_tool is not None
74
- and hasattr(awscc_docs_tool, 'parameters')
75
- and awscc_docs_tool.parameters is not None
76
- ):
77
- if (
78
- 'properties' in awscc_docs_tool.parameters
79
- and 'asset_name' in awscc_docs_tool.parameters['properties']
80
- ):
81
- awscc_docs_tool.parameters['properties']['asset_name']['description'] = (
82
- 'Name of the AWSCC service (asset) to look for (e.g., awscc_s3_bucket, awscc_lambda_function)'
83
- )
84
- if (
85
- 'properties' in awscc_docs_tool.parameters
86
- and 'asset_type' in awscc_docs_tool.parameters['properties']
87
- ):
88
- awscc_docs_tool.parameters['properties']['asset_type']['description'] = (
89
- "Type of documentation to search - 'resource', 'data_source', or 'both' (default)"
90
- )
91
-
92
- # Add parameter descriptions for SearchSpecificAwsIaModules
93
- modules_tool = mcp._tool_manager.get_tool('SearchSpecificAwsIaModules')
94
- if (
95
- modules_tool is not None
96
- and hasattr(modules_tool, 'parameters')
97
- and modules_tool.parameters is not None
98
- ):
99
- if (
100
- 'properties' in modules_tool.parameters
101
- and 'query' in modules_tool.parameters['properties']
102
- ):
103
- modules_tool.parameters['properties']['query']['description'] = (
104
- 'Optional search term to filter modules (empty returns all four modules)'
105
- )
106
-
107
- # Add parameter descriptions for ExecuteTerraformCommand
108
- terraform_tool = mcp._tool_manager.get_tool('ExecuteTerraformCommand')
109
- if (
110
- terraform_tool is not None
111
- and hasattr(terraform_tool, 'parameters')
112
- and terraform_tool.parameters is not None
113
- ):
114
- if (
115
- 'properties' in terraform_tool.parameters
116
- and 'request' in terraform_tool.parameters['properties']
117
- ):
118
- terraform_tool.parameters['properties']['request']['description'] = (
119
- 'Details about the Terraform command to execute'
120
- )
121
-
122
- # Since request is a complex object with nested properties, update its schema
123
- if (
124
- 'properties' in terraform_tool.parameters['properties']['request']
125
- and 'properties'
126
- in terraform_tool.parameters['properties']['request']['properties']
127
- ):
128
- props = terraform_tool.parameters['properties']['request']['properties']
129
- if 'command' in props:
130
- props['command']['description'] = (
131
- 'Terraform command to execute (init, plan, validate, apply, destroy)'
132
- )
133
- if 'working_directory' in props:
134
- props['working_directory']['description'] = (
135
- 'Directory containing Terraform files'
136
- )
137
- if 'variables' in props:
138
- props['variables']['description'] = 'Terraform variables to pass'
139
- if 'aws_region' in props:
140
- props['aws_region']['description'] = 'AWS region to use'
141
- if 'strip_ansi' in props:
142
- props['strip_ansi']['description'] = (
143
- 'Whether to strip ANSI color codes from output'
144
- )
145
-
146
- # Add parameter descriptions for RunCheckovScan
147
- checkov_scan_tool = mcp._tool_manager.get_tool('RunCheckovScan')
148
- if (
149
- checkov_scan_tool is not None
150
- and hasattr(checkov_scan_tool, 'parameters')
151
- and checkov_scan_tool.parameters is not None
152
- ):
153
- if (
154
- 'properties' in checkov_scan_tool.parameters
155
- and 'request' in checkov_scan_tool.parameters['properties']
156
- ):
157
- checkov_scan_tool.parameters['properties']['request']['description'] = (
158
- 'Details about the Checkov scan to execute'
159
- )
160
-
161
- # Since request is a complex object with nested properties, update its schema
162
- if (
163
- 'properties' in checkov_scan_tool.parameters['properties']['request']
164
- and 'properties'
165
- in checkov_scan_tool.parameters['properties']['request']['properties']
166
- ):
167
- props = checkov_scan_tool.parameters['properties']['request']['properties']
168
- if 'working_directory' in props:
169
- props['working_directory']['description'] = (
170
- 'Directory containing Terraform files to scan'
171
- )
172
- if 'framework' in props:
173
- props['framework']['description'] = (
174
- 'Framework to scan (terraform, cloudformation, etc.)'
175
- )
176
- if 'check_ids' in props:
177
- props['check_ids']['description'] = (
178
- 'Optional list of specific check IDs to run'
179
- )
180
- if 'skip_check_ids' in props:
181
- props['skip_check_ids']['description'] = 'Optional list of check IDs to skip'
182
- if 'output_format' in props:
183
- props['output_format']['description'] = (
184
- 'Format for scan results (default: json)'
185
- )
186
-
187
- print('Parameter annotations added successfully.\n')
188
-
189
-
190
- def main():
191
- """Run the parameter annotation test."""
192
- print('=== Terraform MCP Parameter Annotation Test ===\n')
193
-
194
- # Print original parameter schemas
195
- print('Original parameter schemas:')
196
- print_tool_parameters()
197
-
198
- # Add parameter annotations
199
- add_parameter_annotations()
200
-
201
- # Print updated parameter schemas
202
- print('Updated parameter schemas:')
203
- print_tool_parameters()
204
-
205
-
206
- if __name__ == '__main__':
207
- main()
@@ -1,309 +0,0 @@
1
- """Test script for Terraform MCP server implementation functions."""
2
-
3
- import asyncio
4
- import json
5
- import sys
6
- from awslabs.terraform_mcp_server.impl.tools.search_aws_provider_docs import (
7
- search_aws_provider_docs_impl,
8
- )
9
- from awslabs.terraform_mcp_server.impl.tools.search_awscc_provider_docs import (
10
- search_awscc_provider_docs_impl,
11
- )
12
- from awslabs.terraform_mcp_server.impl.tools.search_specific_aws_ia_modules import (
13
- search_specific_aws_ia_modules_impl,
14
- )
15
- from loguru import logger
16
- from typing import Any
17
-
18
-
19
- # Configure logger for enhanced diagnostics with stacktraces
20
- logger.configure(
21
- handlers=[
22
- {
23
- 'sink': sys.stderr,
24
- 'backtrace': True,
25
- 'diagnose': True,
26
- 'format': '<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>',
27
- }
28
- ]
29
- )
30
-
31
-
32
- def print_aws_provider_results(results):
33
- """Print formatted results data using the provided logger.
34
-
35
- Args:
36
- results: List of result objects containing asset information
37
- logger: Logger object to use for output
38
- """
39
- logger.info(f'Found {len(results)} results')
40
-
41
- for i, result in enumerate(results):
42
- logger.info(f'\nResult {i + 1}:')
43
- logger.info(f' Asset Name: {result.asset_name}')
44
- logger.info(f' Asset Type: {result.asset_type}')
45
- logger.info(f' URL: {result.url}')
46
-
47
- # Handle description
48
- if result.description:
49
- description_preview = (
50
- result.description[:50] + '...'
51
- if len(result.description) > 50
52
- else result.description
53
- )
54
- logger.info(f' Description: {description_preview}')
55
- else:
56
- logger.info(' No description')
57
-
58
- # Handle example usage
59
- if result.example_usage:
60
- logger.info(f' Example Usage: {len(result.example_usage)} found')
61
-
62
- # Handle arguments
63
- if result.arguments:
64
- logger.info(f' Arguments: {len(result.arguments)} found')
65
-
66
- # Handle attributes
67
- if result.attributes:
68
- logger.info(f' Attributes: {len(result.attributes)} found')
69
-
70
-
71
- def print_awscc_provider_results(results):
72
- """Print formatted results data using the provided logger.
73
-
74
- Args:
75
- results: List of result objects containing asset information
76
- logger: Logger object to use for output
77
- """
78
- logger.info(f'Found {len(results)} results')
79
-
80
- for i, result in enumerate(results):
81
- logger.info(f'\nResult {i + 1}:')
82
- logger.info(f' Asset Name: {result.asset_name}')
83
- logger.info(f' Asset Type: {result.asset_type}')
84
- logger.info(f' URL: {result.url}')
85
-
86
- # Handle description
87
- if result.description:
88
- description_preview = (
89
- result.description[:50] + '...'
90
- if len(result.description) > 50
91
- else result.description
92
- )
93
- logger.info(f' Description: {description_preview}')
94
- else:
95
- logger.info(' No description')
96
-
97
- # Handle example usage
98
- if result.example_usage:
99
- logger.info(f' Example Usage: {len(result.example_usage)} found')
100
-
101
- # Handle schema arguments
102
- if result.schema_arguments:
103
- logger.info(f' Schema arguments: {len(result.schema_arguments)} found')
104
-
105
-
106
- async def test_search_aws_provider_docs():
107
- """Test the AWS provider docs search function."""
108
- logger.info('=== Testing search_aws_provider_docs_impl ===')
109
-
110
- # Test case 1: Common resource with just 1 example snippet
111
- logger.info('**********---Test case 1: Searching for aws_s3_bucket as a resource---**********')
112
- results = await search_aws_provider_docs_impl('aws_s3_bucket', 'resource')
113
- print_aws_provider_results(results)
114
-
115
- # Test case 2: Common resource with multiple example snippets
116
- logger.info(
117
- '**********---Test case 2: Searching for aws_api_gateway_rest_api as a resource---**********'
118
- )
119
- results = await search_aws_provider_docs_impl('api_gateway_rest_api', 'resource')
120
- print_aws_provider_results(results)
121
-
122
- # Test case 3: Common resource with multiple example snippets and multiple arguments in subsections
123
- logger.info(
124
- '**********---Test case 3: Searching for aws_lambda_function as a resource---**********'
125
- )
126
- results = await search_aws_provider_docs_impl('aws_lambda_function', 'resource')
127
- print_aws_provider_results(results)
128
-
129
- # Test case 4: Specifying data source as asset type
130
- logger.info(
131
- '**********---Test case 4: Searching for aws_lambda_function as a data source ---**********'
132
- )
133
- results = await search_aws_provider_docs_impl('aws_lambda_function', 'data_source')
134
- print_aws_provider_results(results)
135
-
136
- # Test case 5: Searching for both kinds
137
- logger.info('**********---Test case 5: Searching for aws_dynamodb_table as both ---**********')
138
- results = await search_aws_provider_docs_impl('aws_dynamodb_table', 'both')
139
- print_aws_provider_results(results)
140
-
141
- # Test case 6: Non-existent resource
142
- logger.info('**********---Test case 6: Searching for non-existent resource---**********')
143
- results = await search_aws_provider_docs_impl('aws_nonexistent_resource')
144
- print_aws_provider_results(results)
145
-
146
-
147
- async def test_search_awscc_provider_docs():
148
- """Test the AWSCC provider docs search function."""
149
- logger.info('\n=== Testing search_awscc_provider_docs_impl ===')
150
-
151
- # Test case 1: Common resource
152
- logger.info(
153
- '**********---Test case 1: Searching for awscc_apigateway_api_key as a resource---**********'
154
- )
155
- results = await search_awscc_provider_docs_impl('awscc_apigateway_api_key', 'resource')
156
- print_awscc_provider_results(results)
157
-
158
- # Test case 2: Resource with attribute
159
- logger.info(
160
- '**********---Test case 2: Searching for awscc_apigateway_api_key as a data source---**********'
161
- )
162
- results = await search_awscc_provider_docs_impl('awscc_apigateway_api_key', 'data_source')
163
- print_awscc_provider_results(results)
164
-
165
- # Test case 3: lambda_function resource
166
- logger.info(
167
- '**********---Test case 7: Searching for lambda_function as a resource---**********'
168
- )
169
- results = await search_awscc_provider_docs_impl('lambda_function', 'resource')
170
- print_awscc_provider_results(results)
171
-
172
- # Test case 4: Searching for both kinds
173
- logger.info(
174
- '**********---Test case 4: Searching for lambda_function as both kinds---**********'
175
- )
176
- results = await search_awscc_provider_docs_impl('awscc_lambda_function', 'both')
177
- print_awscc_provider_results(results)
178
-
179
- # Test case 5: Non-existent resource
180
- logger.info('**********---Test case 5: Searching for non-existent resource---**********')
181
- results = await search_awscc_provider_docs_impl('awscc_nonexistent_resource')
182
- print_awscc_provider_results(results)
183
-
184
-
185
- async def test_search_specific_aws_ia_modules():
186
- """Test the AWS IA modules search function."""
187
- logger.info('\n=== Testing search_specific_aws_ia_modules_impl ===')
188
-
189
- # Test case 1: Search all modules
190
- logger.info('Test case 1: Searching all AWS IA modules')
191
- results = await search_specific_aws_ia_modules_impl('')
192
-
193
- logger.info(f'Found {len(results)} modules')
194
- for i, result in enumerate(results):
195
- logger.info(f'\nModule {i + 1}:')
196
- logger.info(f' Name: {result.name}')
197
- logger.info(f' Namespace: {result.namespace}')
198
- logger.info(
199
- f' Description: {result.description[:100]}...'
200
- if result.description
201
- else ' No description'
202
- )
203
- logger.info(f' URL: {result.url}')
204
-
205
- # Test case 2: Search with query
206
- logger.info("\nTest case 2: Searching for 'bedrock' modules")
207
- results = await search_specific_aws_ia_modules_impl('bedrock')
208
-
209
- logger.info(f'Found {len(results)} modules')
210
- for i, result in enumerate(results):
211
- logger.info(f'\nModule {i + 1}:')
212
- logger.info(f' Name: {result.name}')
213
- logger.info(f' Namespace: {result.namespace}')
214
- logger.info(
215
- f' Description: {result.description[:100]}...'
216
- if result.description
217
- else ' No description'
218
- )
219
-
220
-
221
- async def test_execute_terraform_command():
222
- """Test the Terraform command execution function.
223
-
224
- Note: This test requires a valid Terraform configuration in a temporary directory.
225
- Skip this test if you don't have a valid Terraform configuration to test with.
226
- """
227
- logger.info('\n=== Testing execute_terraform_command_impl ===')
228
- logger.info('Skipping actual execution as it requires a valid Terraform configuration.')
229
- logger.info('To test this function, you would need to:')
230
- logger.info('1. Create a temporary directory with valid Terraform files')
231
- logger.info('2. Run terraform init, plan, etc. on those files')
232
-
233
- # Example of how you would call it (commented out)
234
- """
235
- request = TerraformExecutionRequest(
236
- command="validate",
237
- working_directory="/path/to/terraform/config",
238
- variables={"environment": "test"},
239
- aws_region="us-west-2",
240
- strip_ansi=True
241
- )
242
-
243
- result = await execute_terraform_command_impl(request)
244
- logger.info(f"Command: {result.command}")
245
- logger.info(f"Status: {result.status}")
246
- logger.info(f"Return Code: {result.return_code}")
247
- if result.stdout:
248
- logger.info(f"Stdout: {result.stdout[:100]}...")
249
- if result.stderr:
250
- logger.info(f"Stderr: {result.stderr[:100]}...")
251
- """
252
-
253
-
254
- async def test_run_checkov_scan():
255
- """Test the Checkov scan function.
256
-
257
- Note: This test requires a valid Terraform configuration in a temporary directory.
258
- Skip this test if you don't have a valid Terraform configuration to test with.
259
- """
260
- logger.info('\n=== Testing run_checkov_scan_impl ===')
261
- logger.info('Skipping actual execution as it requires a valid Terraform configuration.')
262
- logger.info('To test this function, you would need to:')
263
- logger.info('1. Create a temporary directory with valid Terraform files')
264
- logger.info('2. Run Checkov on those files')
265
-
266
- # Example of how you would call it (commented out)
267
- """
268
- request = CheckovScanRequest(
269
- working_directory="/path/to/terraform/config",
270
- framework="terraform",
271
- output_format="json"
272
- )
273
-
274
- result = await run_checkov_scan_impl(request)
275
- logger.info(f"Status: {result.status}")
276
- logger.info(f"Return Code: {result.return_code}")
277
- logger.info(f"Found {len(result.vulnerabilities)} vulnerabilities")
278
- for i, vuln in enumerate(result.vulnerabilities[:3]): # Show first 3 only
279
- logger.info(f"\nVulnerability {i+1}:")
280
- logger.info(f" ID: {vuln.id}")
281
- logger.info(f" Resource: {vuln.resource}")
282
- logger.info(f" Description: {vuln.description[:100]}..." if vuln.description else " No description")
283
- """
284
-
285
-
286
- def format_json(obj: Any) -> str:
287
- """Format an object as pretty JSON."""
288
- if hasattr(obj, 'model_dump'):
289
- # For Pydantic v2
290
- data = obj.model_dump()
291
- elif hasattr(obj, 'dict'):
292
- # For Pydantic v1
293
- data = obj.dict()
294
- else:
295
- data = obj
296
- return json.dumps(data, indent=2, default=str)
297
-
298
-
299
- async def main():
300
- """Run all tests."""
301
- try:
302
- await test_search_aws_provider_docs()
303
- await test_search_awscc_provider_docs()
304
- except Exception as e:
305
- logger.exception(f'Error running tests: {e}')
306
-
307
-
308
- if __name__ == '__main__':
309
- asyncio.run(main())