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
- analyze-data: Analyze data from DynamoDB and S3 resources
- data-summary: Generate a summary of available data sources
- S3 Objects: Access and read objects from your S3 bucket
- Automatic MIME type detection
- Object metadata (size, last modified)
- dynamodb-get: Get an item by key
- dynamodb-put: Store an item
- dynamodb-delete: Delete an item by key
- dynamodb-query: Query items with conditions
- s3-get: Download object content
- s3-put: Upload object content
- s3-delete: Delete an object
- s3-move: Move/rename an object
- code-exec: Execute JavaScript/TypeScript code with access to all MCP tools. Code can be provided directly as a string or referenced via S3 object key
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": "s3://your-bucket/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 }'
# Get an item 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": "dynamodb-get", "arguments": { "key": {"id": "user123"} } }, "id": 1 }' # Put an item 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": "dynamodb-put", "arguments": { "item": { "id": "user123", "name": "John Doe", "email": "john@example.com" } } }, "id": 1 }' # Query items 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": "dynamodb-query", "arguments": { "keyConditionExpression": "id = :id", "expressionAttributeValues": {":id": "user123"} } }, "id": 1 }' # Delete an item 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": "dynamodb-delete", "arguments": { "key": {"id": "user123"} } }, "id": 1 }'
# Get object content 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": "s3-get", "arguments": { "key": "documents/report.txt" } }, "id": 1 }' # Upload content 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": "s3-put", "arguments": { "key": "documents/new-file.txt", "content": "Hello, World!", "contentType": "text/plain" } }, "id": 1 }' # Move/rename object 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": "s3-move", "arguments": { "sourceKey": "old-location/file.txt", "destinationKey": "new-location/file.txt" } }, "id": 1 }' # Delete object 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": "s3-delete", "arguments": { "key": "documents/old-file.txt" } }, "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[\"dynamodb-get\"]({key: {id: input.userId}}); return {message: \"User data retrieved\", data: result}; }", "input": { "userId": "user123" } } }, "id": 1 }' # Execute code from S3 object 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": { "userId": "user123", "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" }