awslabs.dynamodb-mcp-server 0.1.3__tar.gz → 0.1.4__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.
Potentially problematic release.
This version of awslabs.dynamodb-mcp-server might be problematic. Click here for more details.
- {awslabs_dynamodb_mcp_server-0.1.3 → awslabs_dynamodb_mcp_server-0.1.4}/Dockerfile +2 -2
- {awslabs_dynamodb_mcp_server-0.1.3 → awslabs_dynamodb_mcp_server-0.1.4}/PKG-INFO +2 -1
- {awslabs_dynamodb_mcp_server-0.1.3 → awslabs_dynamodb_mcp_server-0.1.4}/README.md +1 -0
- {awslabs_dynamodb_mcp_server-0.1.3 → awslabs_dynamodb_mcp_server-0.1.4}/awslabs/dynamodb_mcp_server/common.py +14 -0
- {awslabs_dynamodb_mcp_server-0.1.3 → awslabs_dynamodb_mcp_server-0.1.4}/awslabs/dynamodb_mcp_server/server.py +32 -0
- {awslabs_dynamodb_mcp_server-0.1.3 → awslabs_dynamodb_mcp_server-0.1.4}/pyproject.toml +1 -1
- {awslabs_dynamodb_mcp_server-0.1.3 → awslabs_dynamodb_mcp_server-0.1.4}/tests/test_dynamodb_server.py +15 -0
- awslabs_dynamodb_mcp_server-0.1.4/tests/test_readonly_delete_table.py +14 -0
- {awslabs_dynamodb_mcp_server-0.1.3 → awslabs_dynamodb_mcp_server-0.1.4}/.gitignore +0 -0
- {awslabs_dynamodb_mcp_server-0.1.3 → awslabs_dynamodb_mcp_server-0.1.4}/.pre-commit-config.yaml +0 -0
- {awslabs_dynamodb_mcp_server-0.1.3 → awslabs_dynamodb_mcp_server-0.1.4}/.python-version +0 -0
- {awslabs_dynamodb_mcp_server-0.1.3 → awslabs_dynamodb_mcp_server-0.1.4}/CHANGELOG.md +0 -0
- {awslabs_dynamodb_mcp_server-0.1.3 → awslabs_dynamodb_mcp_server-0.1.4}/LICENSE +0 -0
- {awslabs_dynamodb_mcp_server-0.1.3 → awslabs_dynamodb_mcp_server-0.1.4}/NOTICE +0 -0
- {awslabs_dynamodb_mcp_server-0.1.3 → awslabs_dynamodb_mcp_server-0.1.4}/awslabs/__init__.py +0 -0
- {awslabs_dynamodb_mcp_server-0.1.3 → awslabs_dynamodb_mcp_server-0.1.4}/awslabs/dynamodb_mcp_server/__init__.py +0 -0
- {awslabs_dynamodb_mcp_server-0.1.3 → awslabs_dynamodb_mcp_server-0.1.4}/docker-healthcheck.sh +0 -0
- {awslabs_dynamodb_mcp_server-0.1.3 → awslabs_dynamodb_mcp_server-0.1.4}/uv.lock +0 -0
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
# and limitations under the License.
|
|
11
11
|
|
|
12
12
|
#FROM public.ecr.aws/sam/build-python3.10:1.137.1-20250411084548
|
|
13
|
-
FROM public.ecr.aws/sam/build-python3.10@sha256:
|
|
13
|
+
FROM public.ecr.aws/sam/build-python3.10@sha256:e78695db10ca8cb129e59e30f7dc9789b0dbd0181dba195d68419c72bac51ac1 AS uv
|
|
14
14
|
|
|
15
15
|
# Install the project into `/app`
|
|
16
16
|
WORKDIR /app
|
|
@@ -44,7 +44,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \
|
|
|
44
44
|
# Make the directory just in case it doesn't exist
|
|
45
45
|
RUN mkdir -p /root/.local
|
|
46
46
|
|
|
47
|
-
FROM public.ecr.aws/sam/build-python3.10@sha256:
|
|
47
|
+
FROM public.ecr.aws/sam/build-python3.10@sha256:e78695db10ca8cb129e59e30f7dc9789b0dbd0181dba195d68419c72bac51ac1
|
|
48
48
|
|
|
49
49
|
# Place executables in the environment at the front of the path and include other binaries
|
|
50
50
|
ENV PATH="/app/.venv/bin:$PATH:/usr/sbin"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: awslabs.dynamodb-mcp-server
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: The official MCP Server for interacting with AWS DynamoDB
|
|
5
5
|
Project-URL: homepage, https://awslabs.github.io/mcp/
|
|
6
6
|
Project-URL: docs, https://awslabs.github.io/mcp/servers/dynamodb-mcp-server/
|
|
@@ -104,6 +104,7 @@ Add the MCP to your favorite agentic tools. e.g. for Amazon Q Developer CLI MCP,
|
|
|
104
104
|
"command": "uvx",
|
|
105
105
|
"args": ["awslabs.dynamodb-mcp-server@latest"],
|
|
106
106
|
"env": {
|
|
107
|
+
"DDB-MCP-READONLY": "true",
|
|
107
108
|
"AWS_PROFILE": "default",
|
|
108
109
|
"AWS_REGION": "us-west-2",
|
|
109
110
|
"FASTMCP_LOG_LEVEL": "ERROR"
|
|
@@ -74,6 +74,7 @@ Add the MCP to your favorite agentic tools. e.g. for Amazon Q Developer CLI MCP,
|
|
|
74
74
|
"command": "uvx",
|
|
75
75
|
"args": ["awslabs.dynamodb-mcp-server@latest"],
|
|
76
76
|
"env": {
|
|
77
|
+
"DDB-MCP-READONLY": "true",
|
|
77
78
|
"AWS_PROFILE": "default",
|
|
78
79
|
"AWS_REGION": "us-west-2",
|
|
79
80
|
"FASTMCP_LOG_LEVEL": "ERROR"
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import os
|
|
1
2
|
from functools import wraps
|
|
2
3
|
from typing import Any, Callable, Dict, List, Literal, Optional
|
|
3
4
|
from typing_extensions import TypedDict
|
|
@@ -26,6 +27,19 @@ def handle_exceptions(func: Callable) -> Callable:
|
|
|
26
27
|
return wrapper
|
|
27
28
|
|
|
28
29
|
|
|
30
|
+
def mutation_check(func):
|
|
31
|
+
"""Decorator to block mutations if DDB-MCP-READONLY is set to true."""
|
|
32
|
+
|
|
33
|
+
@wraps(func)
|
|
34
|
+
async def wrapper(*args, **kwargs):
|
|
35
|
+
readonly = os.environ.get('DDB-MCP-READONLY', '').lower()
|
|
36
|
+
if readonly in ('true', '1', 'yes'): # treat these as true
|
|
37
|
+
return {'error': 'Mutation not allowed: DDB-MCP-READONLY is set to true.'}
|
|
38
|
+
return await func(*args, **kwargs)
|
|
39
|
+
|
|
40
|
+
return wrapper
|
|
41
|
+
|
|
42
|
+
|
|
29
43
|
# Type definitions
|
|
30
44
|
AttributeValue = Dict[Literal['S', 'N', 'B', 'BOOL', 'NULL', 'L', 'M', 'SS', 'NS', 'BS'], Any]
|
|
31
45
|
KeyAttributeValue = Dict[Literal['S', 'N', 'B'], Any]
|
|
@@ -30,6 +30,7 @@ from awslabs.dynamodb_mcp_server.common import (
|
|
|
30
30
|
UpdateTableInput,
|
|
31
31
|
WarmThroughput,
|
|
32
32
|
handle_exceptions,
|
|
33
|
+
mutation_check,
|
|
33
34
|
)
|
|
34
35
|
from botocore.config import Config
|
|
35
36
|
from mcp.server.fastmcp import FastMCP
|
|
@@ -99,6 +100,7 @@ resource_arn: str = Field(description='The Amazon Resource Name (ARN) of the Dyn
|
|
|
99
100
|
|
|
100
101
|
@app.tool()
|
|
101
102
|
@handle_exceptions
|
|
103
|
+
@mutation_check
|
|
102
104
|
async def put_resource_policy(
|
|
103
105
|
resource_arn: str = resource_arn,
|
|
104
106
|
policy: Union[str, Dict[str, Any]] = Field(
|
|
@@ -236,6 +238,7 @@ async def query(
|
|
|
236
238
|
|
|
237
239
|
@app.tool()
|
|
238
240
|
@handle_exceptions
|
|
241
|
+
@mutation_check
|
|
239
242
|
async def update_item(
|
|
240
243
|
table_name: str = table_name,
|
|
241
244
|
key: Dict[str, KeyAttributeValue] = key,
|
|
@@ -303,6 +306,7 @@ async def get_item(
|
|
|
303
306
|
|
|
304
307
|
@app.tool()
|
|
305
308
|
@handle_exceptions
|
|
309
|
+
@mutation_check
|
|
306
310
|
async def put_item(
|
|
307
311
|
table_name: str = table_name,
|
|
308
312
|
item: Dict[str, AttributeValue] = Field(
|
|
@@ -337,6 +341,7 @@ async def put_item(
|
|
|
337
341
|
|
|
338
342
|
@app.tool()
|
|
339
343
|
@handle_exceptions
|
|
344
|
+
@mutation_check
|
|
340
345
|
async def delete_item(
|
|
341
346
|
table_name: str = table_name,
|
|
342
347
|
key: Dict[str, KeyAttributeValue] = key,
|
|
@@ -370,6 +375,7 @@ async def delete_item(
|
|
|
370
375
|
|
|
371
376
|
@app.tool()
|
|
372
377
|
@handle_exceptions
|
|
378
|
+
@mutation_check
|
|
373
379
|
async def update_time_to_live(
|
|
374
380
|
table_name: str = table_name,
|
|
375
381
|
time_to_live_specification: TimeToLiveSpecification = Field(
|
|
@@ -387,6 +393,7 @@ async def update_time_to_live(
|
|
|
387
393
|
|
|
388
394
|
@app.tool()
|
|
389
395
|
@handle_exceptions
|
|
396
|
+
@mutation_check
|
|
390
397
|
async def update_table(
|
|
391
398
|
table_name: str = table_name,
|
|
392
399
|
attribute_definitions: List[AttributeDefinition] = Field(
|
|
@@ -483,6 +490,7 @@ async def list_tables(
|
|
|
483
490
|
|
|
484
491
|
@app.tool()
|
|
485
492
|
@handle_exceptions
|
|
493
|
+
@mutation_check
|
|
486
494
|
async def create_table(
|
|
487
495
|
table_name: str = Field(
|
|
488
496
|
description='The name of the table to create.',
|
|
@@ -536,6 +544,7 @@ async def describe_table(
|
|
|
536
544
|
|
|
537
545
|
@app.tool()
|
|
538
546
|
@handle_exceptions
|
|
547
|
+
@mutation_check
|
|
539
548
|
async def create_backup(
|
|
540
549
|
table_name: str = table_name,
|
|
541
550
|
backup_name: str = Field(
|
|
@@ -602,6 +611,7 @@ async def list_backups(
|
|
|
602
611
|
|
|
603
612
|
@app.tool()
|
|
604
613
|
@handle_exceptions
|
|
614
|
+
@mutation_check
|
|
605
615
|
async def restore_table_from_backup(
|
|
606
616
|
backup_arn: str = Field(
|
|
607
617
|
description='The Amazon Resource Name (ARN) associated with the backup.',
|
|
@@ -717,6 +727,7 @@ async def describe_continuous_backups(
|
|
|
717
727
|
|
|
718
728
|
@app.tool()
|
|
719
729
|
@handle_exceptions
|
|
730
|
+
@mutation_check
|
|
720
731
|
async def untag_resource(
|
|
721
732
|
resource_arn: str = resource_arn,
|
|
722
733
|
tag_keys: List[str] = Field(description='List of tags to remove.', min_length=1),
|
|
@@ -730,6 +741,7 @@ async def untag_resource(
|
|
|
730
741
|
|
|
731
742
|
@app.tool()
|
|
732
743
|
@handle_exceptions
|
|
744
|
+
@mutation_check
|
|
733
745
|
async def tag_resource(
|
|
734
746
|
resource_arn: str = resource_arn,
|
|
735
747
|
tags: List[Tag] = Field(description='Tags to be assigned.'),
|
|
@@ -762,6 +774,7 @@ async def list_tags_of_resource(
|
|
|
762
774
|
|
|
763
775
|
@app.tool()
|
|
764
776
|
@handle_exceptions
|
|
777
|
+
@mutation_check
|
|
765
778
|
async def delete_table(
|
|
766
779
|
table_name: str = table_name,
|
|
767
780
|
region_name: str = Field(default=None, description='The aws region to run the tool'),
|
|
@@ -802,6 +815,25 @@ async def update_continuous_backups(
|
|
|
802
815
|
return response['ContinuousBackupsDescription']
|
|
803
816
|
|
|
804
817
|
|
|
818
|
+
@app.tool()
|
|
819
|
+
@handle_exceptions
|
|
820
|
+
async def list_imports(
|
|
821
|
+
next_token: str = Field(default=None, description='Token to fetch the next page of results.'),
|
|
822
|
+
region_name: str = Field(default=None, description='The aws region to run the tool'),
|
|
823
|
+
) -> dict:
|
|
824
|
+
"""Lists imports completed within the past 90 days."""
|
|
825
|
+
client = get_dynamodb_client(region_name)
|
|
826
|
+
params = {}
|
|
827
|
+
if next_token:
|
|
828
|
+
params['NextToken'] = next_token
|
|
829
|
+
params['PageSize'] = 25
|
|
830
|
+
response = client.list_imports(**params)
|
|
831
|
+
return {
|
|
832
|
+
'ImportSummaryList': response.get('ImportSummaryList', []),
|
|
833
|
+
'NextToken': response.get('NextToken'),
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
|
|
805
837
|
def main():
|
|
806
838
|
"""Main entry point for the MCP server application."""
|
|
807
839
|
app.run()
|
|
@@ -872,3 +872,18 @@ async def test_exception_handling(test_table):
|
|
|
872
872
|
# Verify error is returned
|
|
873
873
|
assert 'error' in error_result
|
|
874
874
|
print(error_result)
|
|
875
|
+
|
|
876
|
+
|
|
877
|
+
@pytest.mark.asyncio
|
|
878
|
+
async def test_list_imports(test_table):
|
|
879
|
+
"""Test listing imports for a table (should be empty with moto)."""
|
|
880
|
+
from awslabs.dynamodb_mcp_server.server import list_imports
|
|
881
|
+
|
|
882
|
+
# Call list_imports with no imports present
|
|
883
|
+
result = await list_imports(
|
|
884
|
+
region_name='us-west-2',
|
|
885
|
+
next_token=None,
|
|
886
|
+
)
|
|
887
|
+
|
|
888
|
+
# Should return a dict with ImportSummaryList and NextToken keys
|
|
889
|
+
assert isinstance(result, dict)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
from awslabs.dynamodb_mcp_server import server
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@pytest.mark.asyncio
|
|
6
|
+
async def test_delete_table_blocked_by_readonly(monkeypatch):
|
|
7
|
+
"""Test that delete_table is blocked if DDB-MCP-READONLY is set to true."""
|
|
8
|
+
# Set the environment variable to simulate read-only mode
|
|
9
|
+
monkeypatch.setenv('DDB-MCP-READONLY', 'true')
|
|
10
|
+
|
|
11
|
+
# Call delete_table and expect an error
|
|
12
|
+
result = await server.delete_table(table_name='TestTable', region_name='us-west-2')
|
|
13
|
+
assert 'error' in result
|
|
14
|
+
assert 'DDB-MCP-READONLY' in result['error']
|
|
File without changes
|
{awslabs_dynamodb_mcp_server-0.1.3 → awslabs_dynamodb_mcp_server-0.1.4}/.pre-commit-config.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{awslabs_dynamodb_mcp_server-0.1.3 → awslabs_dynamodb_mcp_server-0.1.4}/docker-healthcheck.sh
RENAMED
|
File without changes
|
|
File without changes
|