A remote HTTP Model Context Protocol (MCP) server that provides access to AWS services including DynamoDB and S3. This server now uses the official MCP SDK for JSON-RPC handling and exposes prompts, resources, and tools via both JSON-RPC and legacy HTTP endpoints with bearer token authentication.
- POST /mcp: Primary JSON-RPC endpoint using MCP SDK for all operations
- Supports all MCP operations: prompts, resources, and tools
- Proper JSON-RPC 2.0 protocol compliance
- Batch request support
- GET /: Server information and capabilities
- GET /health: Health check endpoint
- GET /mcp/prompts: List available prompts (deprecated)
- POST /mcp/prompts/:name: Get specific prompt content (deprecated)
- GET /mcp/resources: List S3 resources (deprecated, requires auth)
- GET /mcp/resources/*: Read specific S3 resource (deprecated, requires auth)
- GET /mcp/tools: List available tools (deprecated)
- POST /mcp/tools/:name: Execute specific tool (deprecated, requires auth)
- Uses Bearer token authentication
- AWS_SECRET_ACCESS_KEY is passed via
Authorization: Bearer <secret_key>
header - Other AWS credentials are configured via environment variables
- User Isolation: Each bearer token creates an isolated namespace for data storage
- KV Store operations are automatically namespaced per user
- Files Store operations are automatically namespaced per user
- Resources (file listings) are automatically filtered per user namespace
- Demo Mode: Requests without valid authentication use a shared "demo" namespace with limited capabilities
- analyze-data: Analyze data from DynamoDB and S3 resources
- data-summary: Generate a summary of available data sources
- Files: Access and read files from your personal file store
- Automatic MIME type detection
- File metadata (size, last modified, version information)
- Namespace isolation per user
- kv-get: Get a value by key from your personal key-value store
- kv-put: Store a key-value pair in your personal key-value store
- kv-delete: Delete a key-value pair from your personal key-value store
- kv-list: List keys in your personal key-value store with optional prefix filter
- kv-query: Query key-value pairs by key pattern from your personal key-value store
- files-get: Get a file by key from your personal file store (supports versioning)
- files-put: Store a file in your personal file store
- files-delete: Delete a file from your personal file store (supports version-specific deletion)
- files-list: List files in your personal file store with optional prefix filter
- files-list-versions: List all versions of files in your personal file store
- files-move: Move/rename a file within your personal file store
- files-copy: Copy a file within your personal file store
- files-exists: Check if a file exists in your personal file store
- files-metadata: Get metadata for a file in your personal file store (supports versioning)
- code-exec: Execute JavaScript/TypeScript code with access to all MCP tools. Code can be provided directly as a string or referenced via files store key
The code-exec
tool allows you to execute JavaScript/TypeScript code with full access to all MCP tools (DynamoDB and S3 operations). This enables powerful automation and data processing workflows.
Your code must define an async execute
function that accepts two parameters:
input
: Object containing input parameters for your scripttools
: Object containing all available MCP tools as callable functions
async function execute(input, tools) {
// Your code logic here
const result = await tools["dynamodb-get"]({key: {id: input.userId}});
return { message: "Processing complete", data: result };
}
Within your executed code, you have access to all MCP tools via the tools
object:
tools["kv-get"](args)
- Get from your personal key-value storetools["kv-put"](args)
- Put to your personal key-value storetools["kv-delete"](args)
- Delete from your personal key-value storetools["kv-list"](args)
- List keys in your personal key-value storetools["kv-query"](args)
- Query your personal key-value storetools["files-get"](args)
- Get file from your personal file storetools["files-put"](args)
- Put file to your personal file storetools["files-delete"](args)
- Delete file from your personal file storetools["files-list"](args)
- List files in your personal file storetools["files-list-versions"](args)
- List file versions in your personal file storetools["files-move"](args)
- Move file in your personal file storetools["files-copy"](args)
- Copy file in your personal file storetools["files-exists"](args)
- Check if file exists in your personal file storetools["files-metadata"](args)
- Get file metadata from your personal file store
Provide the code directly in the request:
{ "code": "async function execute(input, tools) { /* your code */ }", "input": { "param1": "value1" } }
Store your code in your personal file store and reference it by key:
{ "key": "scripts/my-processor.js", "input": { "param1": "value1" } }
This approach is ideal for:
- Reusable scripts and workflows
- Version control of automation code (files store supports versioning)
- Sharing scripts across different executions
- Complex multi-function scripts
- Personal script libraries isolated per user
- Code execution runs in a sandboxed environment
- No access to global objects or external network calls
- Only MCP tools are available for external operations
- All AWS operations use the same authentication as other MCP tools
Set the following environment variables on your Val Town:
AWS_REGION=us-east-1 AWS_ACCESS_KEY_ID=your-access-key-id S3_BUCKET=your-bucket-name DYNAMODB_TABLE=your-table-name MCP_SERVER_NAME=aws-mcp-server MCP_SERVER_VERSION=1.0.0
Note: AWS_SECRET_ACCESS_KEY
is NOT set as an environment variable. Instead, it's passed via the Authorization header for security.
- Deploy to Val Town as an HTTP val
- Set the required environment variables
- The server will be available at your Val Town URL
All MCP operations should now use the JSON-RPC endpoint at POST /mcp
. This provides proper MCP protocol compliance and better error handling.
All authenticated requests require the Authorization header:
Authorization: Bearer your-aws-secret-access-key
curl -X POST https://your-val-town-url.web.val.run/mcp \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "prompts/list", "params": {}, "id": 1 }'
curl -X POST https://your-val-town-url.web.val.run/mcp \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "prompts/get", "params": { "name": "analyze-data", "arguments": { "table_key": "user123", "s3_key": "data/analytics.json" } }, "id": 1 }'
curl -X POST https://your-val-town-url.web.val.run/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-aws-secret-access-key" \ -d '{ "jsonrpc": "2.0", "method": "resources/list", "params": {}, "id": 1 }'
curl -X POST https://your-val-town-url.web.val.run/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-aws-secret-access-key" \ -d '{ "jsonrpc": "2.0", "method": "resources/read", "params": { "uri": "files://path/to/file.txt" }, "id": 1 }'
curl -X POST https://your-val-town-url.web.val.run/mcp \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "tools/list", "params": {}, "id": 1 }'
# Store a file in your personal file store curl -X POST https://your-val-town-url.web.val.run/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-aws-secret-access-key" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "files-put", "arguments": { "key": "documents/my-file.txt", "content": "Hello, World!", "contentType": "text/plain" } }, "id": 1 }' # Get a file from your personal file store curl -X POST https://your-val-town-url.web.val.run/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-aws-secret-access-key" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "files-get", "arguments": { "key": "documents/my-file.txt" } }, "id": 1 }' # List files in your personal file store curl -X POST https://your-val-town-url.web.val.run/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-aws-secret-access-key" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "files-list", "arguments": { "prefix": "documents/" } }, "id": 1 }' # List all versions of a file curl -X POST https://your-val-town-url.web.val.run/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-aws-secret-access-key" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "files-list-versions", "arguments": { "key": "documents/my-file.txt" } }, "id": 1 }' # Get a specific version of a file curl -X POST https://your-val-town-url.web.val.run/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-aws-secret-access-key" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "files-get", "arguments": { "key": "documents/my-file.txt", "versionId": "specific-version-id" } }, "id": 1 }' # Copy a file within your personal file store curl -X POST https://your-val-town-url.web.val.run/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-aws-secret-access-key" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "files-copy", "arguments": { "sourceKey": "documents/my-file.txt", "destinationKey": "backup/my-file-backup.txt" } }, "id": 1 }' # Check if a file exists curl -X POST https://your-val-town-url.web.val.run/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-aws-secret-access-key" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "files-exists", "arguments": { "key": "documents/my-file.txt" } }, "id": 1 }' # Get file metadata curl -X POST https://your-val-town-url.web.val.run/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-aws-secret-access-key" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "files-metadata", "arguments": { "key": "documents/my-file.txt" } }, "id": 1 }' # Delete a file curl -X POST https://your-val-town-url.web.val.run/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-aws-secret-access-key" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "files-delete", "arguments": { "key": "documents/my-file.txt" } }, "id": 1 }'
# Store a key-value pair in your personal store curl -X POST https://your-val-town-url.web.val.run/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-aws-secret-access-key" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "kv-put", "arguments": { "key": "user-settings", "value": {"theme": "dark", "language": "en"}, "ttl": 3600 } }, "id": 1 }' # Get a value from your personal store curl -X POST https://your-val-town-url.web.val.run/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-aws-secret-access-key" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "kv-get", "arguments": { "key": "user-settings" } }, "id": 1 }' # List keys in your personal store curl -X POST https://your-val-town-url.web.val.run/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-aws-secret-access-key" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "kv-list", "arguments": { "prefix": "user-" } }, "id": 1 }' # Query key-value pairs by pattern curl -X POST https://your-val-town-url.web.val.run/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-aws-secret-access-key" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "kv-query", "arguments": { "keyPattern": "config*" } }, "id": 1 }'
# Execute code directly (inline) curl -X POST https://your-val-town-url.web.val.run/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-aws-secret-access-key" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "code-exec", "arguments": { "code": "async function execute(input, tools) { const result = await tools[\"kv-get\"]({key: input.key}); return {message: \"Data retrieved\", data: result}; }", "input": { "key": "user-settings" } } }, "id": 1 }' # Execute code from files store curl -X POST https://your-val-town-url.web.val.run/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-aws-secret-access-key" \ -d '{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "code-exec", "arguments": { "key": "scripts/data-processor.js", "input": { "operation": "analyze" } } }, "id": 1 }'
You can send multiple JSON-RPC requests in a single HTTP request:
curl -X POST https://your-val-town-url.web.val.run/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-aws-secret-access-key" \ -d '[ { "jsonrpc": "2.0", "method": "prompts/list", "params": {}, "id": 1 }, { "jsonrpc": "2.0", "method": "tools/list", "params": {}, "id": 2 }, { "jsonrpc": "2.0", "method": "resources/list", "params": {}, "id": 3 } ]'
The following endpoints are maintained for backward compatibility but are deprecated. Use the JSON-RPC endpoint instead.
curl https://your-val-town-url.web.val.run/
curl https://your-val-town-url.web.val.run/mcp/prompts
curl -X POST https://your-val-town-url.web.val.run/mcp/prompts/analyze-data \ -H "Content-Type: application/json" \ -d '{"table_key": "user123", "s3_key": "data/analytics.json"}'
curl https://your-val-town-url.web.val.run/mcp/resources \ -H "Authorization: Bearer your-aws-secret-access-key"
curl "https://your-val-town-url.web.val.run/mcp/resources/s3://your-bucket/path/to/file.txt" \ -H "Authorization: Bearer your-aws-secret-access-key"
curl https://your-val-town-url.web.val.run/mcp/tools
# Get an item curl -X POST https://your-val-town-url.web.val.run/mcp/tools/dynamodb-get \ -H "Authorization: Bearer your-aws-secret-access-key" \ -H "Content-Type: application/json" \ -d '{"key": {"id": "user123"}}' # Put an item curl -X POST https://your-val-town-url.web.val.run/mcp/tools/dynamodb-put \ -H "Authorization: Bearer your-aws-secret-access-key" \ -H "Content-Type: application/json" \ -d '{"item": {"id": "user123", "name": "John Doe", "email": "john@example.com"}}' # Query items curl -X POST https://your-val-town-url.web.val.run/mcp/tools/dynamodb-query \ -H "Authorization: Bearer your-aws-secret-access-key" \ -H "Content-Type: application/json" \ -d '{"keyConditionExpression": "id = :id", "expressionAttributeValues": {":id": "user123"}}' # Delete an item curl -X POST https://your-val-town-url.web.val.run/mcp/tools/dynamodb-delete \ -H "Authorization: Bearer your-aws-secret-access-key" \ -H "Content-Type: application/json" \ -d '{"key": {"id": "user123"}}'
# Get object content curl -X POST https://your-val-town-url.web.val.run/mcp/tools/s3-get \ -H "Authorization: Bearer your-aws-secret-access-key" \ -H "Content-Type: application/json" \ -d '{"key": "documents/report.txt"}' # Upload content curl -X POST https://your-val-town-url.web.val.run/mcp/tools/s3-put \ -H "Authorization: Bearer your-aws-secret-access-key" \ -H "Content-Type: application/json" \ -d '{"key": "documents/new-file.txt", "content": "Hello, World!", "contentType": "text/plain"}' # Move/rename object curl -X POST https://your-val-town-url.web.val.run/mcp/tools/s3-move \ -H "Authorization: Bearer your-aws-secret-access-key" \ -H "Content-Type: application/json" \ -d '{"sourceKey": "old-location/file.txt", "destinationKey": "new-location/file.txt"}' # Delete object curl -X POST https://your-val-town-url.web.val.run/mcp/tools/s3-delete \ -H "Authorization: Bearer your-aws-secret-access-key" \ -H "Content-Type: application/json" \ -d '{"key": "documents/old-file.txt"}'
# Execute code directly (inline) curl -X POST https://your-val-town-url.web.val.run/mcp/tools/code-exec \ -H "Authorization: Bearer your-aws-secret-access-key" \ -H "Content-Type: application/json" \ -d '{ "code": "async function execute(input, tools) { const result = await tools[\"dynamodb-get\"]({key: {id: input.userId}}); return {message: \"User data retrieved\", data: result}; }", "input": { "userId": "user123" } }' # Execute code from S3 object curl -X POST https://your-val-town-url.web.val.run/mcp/tools/code-exec \ -H "Authorization: Bearer your-aws-secret-access-key" \ -H "Content-Type: application/json" \ -d '{ "key": "scripts/data-processor.js", "input": { "userId": "user123", "operation": "analyze" } }'
To integrate with MCP clients like Claude Desktop, you can create a client that makes JSON-RPC requests to the /mcp
endpoint. Here's an example configuration:
{ "mcpServers": { "aws-http-mcp-server": { "command": "node", "args": ["mcp-http-client.js"], "env": { "MCP_SERVER_URL": "https://your-val-town-url.web.val.run", "AWS_SECRET_ACCESS_KEY": "${AWS_SECRET_ACCESS_KEY}" } } } }
{ "jsonrpc": "2.0", "result": { "content": [ { "type": "text", "text": "Response content here" } ] }, "id": 1 }
{ "jsonrpc": "2.0", "error": { "code": -32603, "message": "Error message description" }, "id": 1 }
-32700
: Parse error (Invalid JSON)-32600
: Invalid Request-32601
: Method not found-32602
: Invalid params-32603
: Internal error-32001
: Authentication error (custom)
- Bearer Token Authentication: AWS_SECRET_ACCESS_KEY is passed via Authorization header, not stored in environment variables
- HTTPS Only: Always use HTTPS in production to protect the bearer token
- IAM Permissions: Use IAM roles with minimal required permissions
- Token Rotation: Regularly rotate your AWS secret access keys
- Rate Limiting: Consider implementing rate limiting for production use
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::your-bucket-name", "arn:aws:s3:::your-bucket-name/*" ] } ] }
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:DeleteItem", "dynamodb:Query", "dynamodb:Scan" ], "Resource": "arn:aws:dynamodb:region:account:table/your-table-name" } ] }
The server includes comprehensive error handling for:
- Missing or invalid Authorization headers
- AWS service errors
- Invalid parameters
- Network connectivity issues
- JSON-RPC protocol errors
All JSON-RPC errors follow the standard format with appropriate error codes.
Legacy endpoints return errors with HTTP status codes:
401
: Authentication errors400
: Bad request (invalid parameters)404
: Resource or tool not found500
: Server errors (AWS service errors, etc.)
{ "content": [ { "type": "text", "text": "Response content here" } ] }
{ "error": "Error message description" }
The server provides a health check endpoint:
curl https://your-val-town-url.web.val.run/health
Response:
{ "status": "healthy", "timestamp": "2024-01-01T00:00:00.000Z" }