MCP Server for AWS S3 operations with presigned URL support
Project description
Amazon S3 MCP Server
A Model Context Protocol (MCP) server for AWS S3 operations, providing secure access to S3 buckets through presigned URLs.
Features
- List Objects: Browse S3 bucket contents with prefix filtering and pagination
- Download Objects: Generate time-limited presigned URLs for secure downloads
- Upload Objects: Generate presigned URLs for direct client-to-S3 uploads with encryption and ACL support
- Delete Objects: Remove objects from S3 buckets (with version support)
- Dual Format Support: All operations return both Markdown (human-readable) and JSON formats
- Security First: Uses presigned URLs to avoid credential exposure and enable direct client-to-S3 communication
- Comprehensive Validation: Pydantic v2 models ensure input validation and type safety
Architecture
This server uses presigned URLs instead of direct upload/download for several key benefits:
- Security: Time-limited access without exposing AWS credentials
- Scalability: Direct client-to-S3 communication (no server bottleneck)
- Performance: Eliminates server as intermediary for data transfer
- Flexibility: Works with any HTTP client (browsers, curl, etc.)
Prerequisites
- Python: 3.10 or higher
- AWS Account: With S3 access
- AWS Credentials: Configured via AWS CLI, environment variables, or IAM roles
- UV (optional): Fast Python package manager - Install UV
Installation
1. Clone or Download
cd /path/to/mcp-servers/aws-s3-mcp
2. Install Dependencies
Option A: Using pip (Traditional)
# Install in development mode
pip install -e .
# Or install with dev dependencies for testing
pip install -e ".[dev]"
Option B: Using UV (Recommended for Claude Desktop)
# Install UV first (if not already installed)
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install dependencies with UV
uv sync
# Or install with dev dependencies
uv sync --all-extras
3. Configure AWS Credentials
Choose one of the following methods:
Option A: AWS CLI (Recommended for development)
aws configure
Option B: Environment Variables
export AWS_ACCESS_KEY_ID=your-access-key
export AWS_SECRET_ACCESS_KEY=your-secret-key
export AWS_REGION=us-east-1
Option C: Named Profile
# Create .env file
cp .env.example .env
# Edit .env and set:
# AWS_PROFILE=my-profile
# AWS_REGION=us-east-1
Usage
Running the Server
Using Python
# Run directly as module
python -m s3_mcp
# Or use the installed command
s3-mcp
Using UV
# Run with UV
uv run s3-mcp
# Or run from specific directory
uv --directory /path/to/mcp-servers/aws-s3-mcp run s3-mcp
# Or use UVX (tool runner)
uvx --from /path/to/mcp-servers/aws-s3-mcp s3-mcp
Claude Desktop Configuration
Add to your Claude Desktop configuration file:
MacOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
Option 1: Using Python (Traditional)
{
"mcpServers": {
"s3": {
"command": "python",
"args": ["-m", "s3_mcp"],
"env": {
"AWS_REGION": "us-east-1",
"AWS_PROFILE": "default"
}
}
}
}
Option 2: Using UV with Directory Path
{
"mcpServers": {
"s3": {
"command": "uv",
"args": [
"--directory",
"/path/to/mcp-servers/aws-s3-mcp",
"run",
"s3-mcp"
],
"env": {
"AWS_REGION": "us-east-1",
"AWS_PROFILE": "default"
}
}
}
}
Option 3: Using UVX (UV Tool Runner)
{
"mcpServers": {
"s3": {
"command": "uvx",
"args": [
"--from",
"/path/to/mcp-servers/aws-s3-mcp",
"s3-mcp"
],
"env": {
"AWS_REGION": "us-east-1",
"AWS_PROFILE": "default"
}
}
}
}
Option 4: Using UV from Project Directory
{
"mcpServers": {
"s3": {
"command": "uv",
"args": ["run", "s3-mcp"],
"cwd": "/path/to/mcp-servers/aws-s3-mcp",
"env": {
"AWS_REGION": "us-east-1",
"AWS_PROFILE": "default"
}
}
}
}
Note: Replace /path/to/mcp-servers/aws-s3-mcp with the actual absolute path to your installation directory.
Which Option Should I Use?
- Option 1 (Python): Best if you have Python installed globally and the package installed with pip
- Option 2 (UV with --directory): Best for development, keeps dependencies isolated per project
- Option 3 (UVX): Best for quick tool execution, handles dependencies automatically
- Option 4 (UV with cwd): Alternative to Option 2, uses
cwdinstead of--directory
Recommended: Use Option 2 or Option 3 with UV for better dependency isolation and faster startup times.
Available Tools
1. s3_list_objects
List objects in an S3 bucket with optional prefix filtering and pagination.
Parameters:
bucket_name(required): S3 bucket name (3-63 chars, lowercase)prefix(optional): Filter objects by key prefix (e.g., "logs/2024/")limit(optional): Max objects to return (1-1000, default: 20)continuation_token(optional): Token from previous response for paginationresponse_format(optional): "markdown" (default) or "json"
Example:
List objects in my-data-bucket with prefix "images/"
Response includes:
- Object keys, sizes, last modified dates, storage classes
- Pagination token if more results exist
2. s3_get_object
Generate a presigned URL for downloading an S3 object.
Parameters:
bucket_name(required): S3 bucket namekey(required): Object key (path within bucket)expires_in(optional): URL expiration in seconds (1-604800, default: 3600)response_content_disposition(optional): Override Content-Disposition headerresponse_content_type(optional): Override Content-Type headerresponse_format(optional): "markdown" (default) or "json"
Example:
Generate download URL for my-data-bucket/reports/2024-report.pdf
Response includes:
- Time-limited presigned URL (valid for specified duration)
- Expiration timestamp
- Usage example (curl command)
Security Notes:
- URLs are time-limited (max 7 days, default 1 hour)
- Anyone with the URL can download during validity period
- URLs cannot be revoked before expiration
3. s3_put_object
Generate a presigned URL for uploading an object to S3.
Parameters:
bucket_name(required): S3 bucket namekey(required): Destination object keyexpires_in(optional): URL expiration in seconds (1-604800, default: 3600)content_type(optional): MIME type (e.g., "image/png", "application/pdf")server_side_encryption(optional): "AES256" or "aws:kms"metadata(optional): Custom metadata as key-value pairsacl(optional): Access control ("private", "public-read", etc.)response_format(optional): "markdown" (default) or "json"
Example:
Generate upload URL for my-bucket/uploads/document.pdf with encryption
Response includes:
- Presigned URL for PUT operation
- Required headers (Content-Type, encryption, etc.)
- Usage example (curl command with all required headers)
Security Notes:
- Enforce encryption with
server_side_encryptionparameter - Use "private" ACL unless public access required
- URL is scoped to specific object key
4. s3_delete_object
Delete an object from an S3 bucket.
⚠️ WARNING: This is a DESTRUCTIVE operation. Deleted objects cannot be recovered unless versioning is enabled.
Parameters:
bucket_name(required): S3 bucket namekey(required): Object key to deleteversion_id(optional): Specific version to delete (for versioned buckets)response_format(optional): "markdown" (default) or "json"
Example:
Delete my-bucket/temp/old-file.txt
Behavior:
- Non-versioned buckets: Object is permanently deleted
- Versioned buckets: Delete marker created (object can be recovered)
- With version_id: Specific version permanently deleted
Response includes:
- Deletion confirmation
- Version information if applicable
Development
Quick Start with Makefile
The project includes a Makefile for common development tasks:
make help # Show all available commands
make install-dev # Install package with dev dependencies
make test # Run test suite
make test-cov # Run tests with coverage report
make lint # Check code quality
make format # Format code
make check # Run all checks (lint + typecheck + test)
make inspector # Launch MCP Inspector for testing
make clean # Remove build artifacts
Project Structure
aws-s3-mcp/
├── s3_mcp/
│ ├── __init__.py # Package initialization
│ ├── __main__.py # Entry point
│ ├── server.py # FastMCP server and tool definitions
│ └── s3/
│ ├── __init__.py # S3 module exports
│ ├── client.py # S3 client wrapper with error handling
│ ├── operations.py # Core business logic for all operations
│ └── utils.py # Shared utilities and formatters
├── tests/ # Test suite (pytest + moto)
├── pyproject.toml # Project configuration and dependencies
├── .env.example # Example environment configuration
└── README.md # This file
Running Tests
# Using Makefile (recommended)
make install-dev # Install with dev dependencies
make test # Run tests
make test-cov # Run tests with coverage report
# Or use pytest directly
pip install -e ".[dev]"
pytest -v
pytest --cov=s3_mcp --cov-report=html
Test Suite:
- ✅ 50 unit tests covering all operations
- ✅ 95% coverage on core business logic (operations.py)
- ✅ 88% coverage on utilities (utils.py)
- ✅ Tests include: list objects, get/put/delete operations, pagination, error handling, versioning, encryption, ACLs
Testing with MCP Inspector
Test the server interactively using MCP Inspector:
# Using Makefile
make inspector
# Or run directly
npx @modelcontextprotocol/inspector python -m s3_mcp.server
This will:
- Start the MCP Inspector web interface
- Launch your S3 MCP server
- Open a browser where you can test all tools
- View tool schemas, test with different parameters, and inspect responses
Code Quality
# Using Makefile (recommended)
make format # Format code with ruff
make lint # Check code with ruff
make typecheck # Run mypy type checking
make check # Run all checks (lint + typecheck + test)
# Or run tools directly
ruff format .
ruff check .
mypy s3_mcp/
Configuration
Environment Variables
Create a .env file from .env.example:
# AWS Region (default: us-east-1)
AWS_REGION=us-east-1
# AWS Profile (optional)
AWS_PROFILE=my-profile
# Maximum Presigned URL Expiration (optional, default: 604800 seconds = 7 days)
MAX_PRESIGNED_URL_EXPIRATION=604800
AWS IAM Permissions
The server requires the following IAM permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::your-bucket-name",
"arn:aws:s3:::your-bucket-name/*"
]
}
]
}
For versioned buckets, also add:
{
"Effect": "Allow",
"Action": [
"s3:DeleteObjectVersion",
"s3:GetObjectVersion"
],
"Resource": "arn:aws:s3:::your-bucket-name/*"
}
Security Best Practices
Presigned URLs
- Use short expiration times for sensitive data (minutes, not hours)
- Monitor URL generation in CloudTrail logs
- Validate bucket policies to prevent unauthorized presigned URL generation
- Consider VPC endpoints for private S3 access
AWS Credentials
- Never commit credentials to version control
- Use IAM roles in production (EC2, ECS, Lambda)
- Rotate access keys regularly
- Use least privilege IAM permissions
- Enable MFA for sensitive operations
Data Protection
- Enable encryption by default (use
server_side_encryptionparameter) - Enable versioning for important buckets
- Configure lifecycle policies for automated data management
- Enable access logging for audit trails
Troubleshooting
Common Issues
Issue: ImportError: No module named 'fastmcp'
- Solution: Run
pip install -e .to install dependencies
Issue: NoCredentialsError: Unable to locate credentials
- Solution: Configure AWS credentials using
aws configureor environment variables
Issue: AccessDenied: Access Denied
- Solution: Verify IAM permissions for the S3 operations you're attempting
Issue: NoSuchBucket: The specified bucket does not exist
- Solution: Verify bucket name and region configuration
Issue: Presigned URL returns 403 Forbidden
- Solution: Check that:
- Required headers are included (for PUT operations)
- URL hasn't expired
- Bucket policy allows the operation
Debugging
Enable debug logging:
import logging
logging.basicConfig(level=logging.DEBUG)
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes with tests
- Run code quality checks (
black,ruff,pytest) - Submit a pull request
License
This project is licensed under the MIT License.
Related Resources
- AWS S3 Documentation
- AWS Presigned URLs
- Model Context Protocol
- FastMCP Framework
- Boto3 Documentation
Support
For issues, questions, or contributions:
- Open an issue on GitHub
- Check existing issues for solutions
- Review AWS S3 documentation for API-specific questions
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file s3_mcp-0.1.0.tar.gz.
File metadata
- Download URL: s3_mcp-0.1.0.tar.gz
- Upload date:
- Size: 149.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ec6d5536373d1c71841d1bb4a523ef3d6323efbbd6f1b202e9d14fb31dd6fa5c
|
|
| MD5 |
8e22643265ec776661ae5cab22a74613
|
|
| BLAKE2b-256 |
ada9f0b86a0909887b8d3c4a203123ab986876aeac35eb4c52af96198ca6a98a
|
Provenance
The following attestation bundles were made for s3_mcp-0.1.0.tar.gz:
Publisher:
publish-packages.yml on Synteles/mcp-servers
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
s3_mcp-0.1.0.tar.gz -
Subject digest:
ec6d5536373d1c71841d1bb4a523ef3d6323efbbd6f1b202e9d14fb31dd6fa5c - Sigstore transparency entry: 798166560
- Sigstore integration time:
-
Permalink:
Synteles/mcp-servers@b7e3e2ab453410e375c85da074ed912958e88116 -
Branch / Tag:
refs/tags/s3-mcp-v0.1.0 - Owner: https://github.com/Synteles
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-packages.yml@b7e3e2ab453410e375c85da074ed912958e88116 -
Trigger Event:
push
-
Statement type:
File details
Details for the file s3_mcp-0.1.0-py3-none-any.whl.
File metadata
- Download URL: s3_mcp-0.1.0-py3-none-any.whl
- Upload date:
- Size: 17.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cf953845070e5670b5379e71dced9ec5ad7b0b1c463b46e3e84fbbec957475f9
|
|
| MD5 |
e9f6170dbb834737c0171ef23b5a6c71
|
|
| BLAKE2b-256 |
341c5d8959bb19361e16c9736602ade7d201066bedbc3a6a202cc2c73a1f144d
|
Provenance
The following attestation bundles were made for s3_mcp-0.1.0-py3-none-any.whl:
Publisher:
publish-packages.yml on Synteles/mcp-servers
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
s3_mcp-0.1.0-py3-none-any.whl -
Subject digest:
cf953845070e5670b5379e71dced9ec5ad7b0b1c463b46e3e84fbbec957475f9 - Sigstore transparency entry: 798166564
- Sigstore integration time:
-
Permalink:
Synteles/mcp-servers@b7e3e2ab453410e375c85da074ed912958e88116 -
Branch / Tag:
refs/tags/s3-mcp-v0.1.0 - Owner: https://github.com/Synteles
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-packages.yml@b7e3e2ab453410e375c85da074ed912958e88116 -
Trigger Event:
push
-
Statement type: