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.
The codebase is organized into a clean, modular structure:
├── index.ts # Main HTTP entry point
├── src/
│ ├── auth/
│ │ └── auth-manager.ts # Authentication and namespace management
│ ├── aws/
│ │ └── clients.ts # AWS client configuration utilities
│ ├── mcp/
│ │ ├── handler.ts # Main MCP request handler
│ │ └── protocol-handlers.ts # MCP protocol-specific handlers (prompts, resources)
│ ├── storage/
│ │ ├── kv-store.ts # Namespaced DynamoDB key-value store
│ │ └── files-store.ts # Namespaced S3 files store
│ ├── tools/
│ │ ├── definitions.ts # Tool schema definitions
│ │ └── handlers/
│ │ ├── index.ts # Unified tool handlers export
│ │ ├── kv-tools.ts # Key-value store tool handlers
│ │ ├── files-tools.ts # Files store tool handlers
│ │ ├── code-tools.ts # Code execution tool handlers
│ │ ├── openai-tools.ts # OpenAI API tool handlers
│ │ └── system-tools.ts # System tools using Val Town API
│ └── valtown/
│ └── client.ts # Val Town API client for project file access
└── README.md
This modular structure provides:
- Separation of concerns: Each component has a single responsibility
- Easy testing: Individual components can be tested in isolation
- Maintainability: Changes to one area don't affect others
- Extensibility: New tools and handlers can be easily added
- 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
- 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
- System Mode: When the bearer token matches the AWS_SECRET_ACCESS_KEY environment variable, the system enters "system mode" with access to:
- All regular user tools and operations within the user's own namespace
- Special system tools for reading project source code
- Project structure inspection capabilities
- Enhanced debugging and administration features
- Important: System mode preserves the original user's namespace instead of switching to a separate "system" namespace, ensuring consistent data access across modes
- 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
- register-skill: Register a stored code file as a reusable skill tool with custom input schema
- unregister-skill: Remove a previously registered skill
- list-skills: List all registered skills for the current user
- [user-skills]: Dynamically registered skills appear as individual tools with their own schemas
- admin-register-system-skill: Register a system-wide skill available to all users (admin only)
- admin-unregister-system-skill: Remove a system-wide skill (admin only)
- admin-list-system-skills: List all registered system skills (admin only)
- admin-update-system-skill: Update an existing system skill (admin only)
- system-read-file: Read a source code file from the project using Val Town API (requires system mode access)
- system-list-files: List all files in the project directory structure using Val Town API (requires system mode access)
- system-project-info: Get project information and structure overview using Val Town API (requires system mode access)
- openai-chat-completion: Create chat completions using OpenAI's API via Val Town's built-in OpenAI integration (supports GPT-4, GPT-3.5, and other models)
The server includes OpenAI chat completion functionality using Val Town's built-in OpenAI integration. This provides seamless access to OpenAI's models without requiring additional API key configuration.
# Simple chat completion 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": "openai-chat-completion", "arguments": { "messages": [ {"role": "user", "content": "Say hello in a creative way"} ], "model": "gpt-4o-mini", "max_tokens": 30 } }, "id": 1 }' # Chat with system prompt 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": "openai-chat-completion", "arguments": { "messages": [ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Explain quantum computing in simple terms."} ], "model": "gpt-4o-mini", "max_tokens": 500, "temperature": 0.7 } }, "id": 1 }'
The code-exec
tool allows you to execute JavaScript/TypeScript code with full access to all MCP tools (KV store and Files store operations). This enables powerful automation and data processing workflows.
The skill management system allows you to register stored code files as reusable tools with custom input schemas. The system supports both user-specific skills and system-wide skills managed by administrators.
- Scope: Personal to each user
- Registration: Use
register-skill
tool - Override: User skills can override system skills with the same name
- Namespace: Stored with user-specific namespace isolation
- Scope: Available to all users
- Registration: Use
admin-register-system-skill
tool (requires system mode) - Management: Only administrators can create, update, or remove system skills
- Priority: User skills take precedence over system skills with the same name
The system includes a virtual "system" folder that provides:
- Read-only access for regular users to view system resources
- Read-write access for administrators in system mode
- Organized structure for system skills, templates, and documentation
system/
├── skills/ # System skill source files
│ └── README.md # Documentation about system skills
└── templates/ # Reusable code templates
When a skill is called, the system resolves it in this order:
- User Skills: Check if the user has registered a skill with that name
- System Skills: Fall back to system-wide skills if no user skill exists
- Error: Return "skill not found" if neither exists
Administrators can manage system-wide skills that are available to all users:
{ "name": "system-data-processor", "description": "System-wide data processing utility", "fileKey": "system/skills/data-processor.js", "inputSchema": { "type": "object", "properties": { "operation": { "type": "string", "enum": ["analyze", "transform", "validate"], "description": "Type of processing operation" }, "data": { "type": "object", "description": "Data to process" } }, "required": ["operation", "data"] } }
- admin-register-system-skill: Register a new system skill
- admin-unregister-system-skill: Remove a system skill
- admin-list-system-skills: List all system skills
- admin-update-system-skill: Update an existing system skill
{ "name": "data-analyzer", "description": "Analyzes data from KV store and generates reports", "fileKey": "skills/data-analyzer.js", "inputSchema": { "type": "object", "properties": { "dataKey": { "type": "string", "description": "Key of data to analyze" }, "reportType": { "type": "string", "enum": ["summary", "detailed"], "description": "Type of report to generate" } }, "required": ["dataKey"] } }
- register-skill: Register a new skill from a files store key
- unregister-skill: Remove a registered skill
- list-skills: List all your registered skills
Once registered, skills appear in the tools list and can be called directly:
{ "jsonrpc": "2.0", "method": "tools/call", "params": { "name": "data-analyzer", "arguments": { "dataKey": "user-data-2024", "reportType": "detailed" } }, "id": 1 }
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["kv-get"]({key: input.key});
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 storetools["openai-chat-completion"](args)
- Create OpenAI chat completions
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.
OpenAI Integration: OpenAI functionality uses Val Town's built-in OpenAI integration, which automatically handles API key management. No additional environment variables are required for OpenAI tools.
The KV Store requires a DynamoDB table with the following schema:
Table Configuration:
- Partition Key:
pk
(String) - Contains the user namespace - Sort Key:
sk
(String) - Contains the user's key - Billing Mode: On-demand or Provisioned (your choice)
- TTL: Optional - Set TTL attribute to
ttl
if you want automatic expiration
Example AWS CLI command to create the table:
aws dynamodb create-table \ --table-name your-table-name \ --attribute-definitions \ AttributeName=pk,AttributeType=S \ AttributeName=sk,AttributeType=S \ --key-schema \ AttributeName=pk,KeyType=HASH \ AttributeName=sk,KeyType=RANGE \ --billing-mode PAY_PER_REQUEST
Example CloudFormation template:
DynamoDBTable: Type: AWS::DynamoDB::Table Properties: TableName: your-table-name AttributeDefinitions: - AttributeName: pk AttributeType: S - AttributeName: sk AttributeType: S KeySchema: - AttributeName: pk KeyType: HASH - AttributeName: sk KeyType: RANGE BillingMode: PAY_PER_REQUEST TimeToLiveSpecification: AttributeName: ttl Enabled: true
Data Structure: Each item in the table has the following structure:
pk
(String): User namespace (e.g., "user123", "demo")sk
(String): User's key (e.g., "settings", "config-app")data
(Any): The actual value stored by the usercreated
(String): ISO timestamp when item was createdupdated
(String): ISO timestamp when item was last updatedttl
(Number, Optional): Unix timestamp for automatic expiration
- 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 }'
# Register a skill 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": "register-skill", "arguments": { "name": "data-analyzer", "description": "Analyzes data from KV store and generates reports", "fileKey": "skills/data-analyzer.js", "inputSchema": { "type": "object", "properties": { "dataKey": { "type": "string", "description": "Key of data to analyze" }, "reportType": { "type": "string", "enum": ["summary", "detailed"], "description": "Type of report to generate" } }, "required": ["dataKey"] } } }, "id": 1 }' # List registered skills 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": "list-skills", "arguments": {} }, "id": 1 }' # Execute a registered skill 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": "data-analyzer", "arguments": { "dataKey": "user-data-2024", "reportType": "detailed" } }, "id": 1 }' # Unregister a skill 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": "unregister-skill", "arguments": { "name": "data-analyzer" } }, "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" ], "Resource": "arn:aws:dynamodb:region:account:table/your-table-name" } ] }
Note: The KV Store uses DynamoDB Query operations with a composite key structure (partition key + sort key) for efficient namespace isolation and prefix matching. Scan operations are not required.
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" }
When the bearer token matches the AWS_SECRET_ACCESS_KEY environment variable, the system enters "system mode" and provides additional tools for project inspection and debugging using the Val Town API:
# Read a project source file using Val Town API 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": "system-read-file", "arguments": { "path": "src/auth/auth-manager.ts" } }, "id": 1 }' # List project files recursively using Val Town API 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": "system-list-files", "arguments": { "path": "/src", "recursive": true } }, "id": 1 }' # Get comprehensive project information using Val Town API 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": "system-project-info", "arguments": {} }, "id": 1 }'
- System mode is only activated when the bearer token exactly matches the AWS_SECRET_ACCESS_KEY environment variable
- System tools provide read-only access to project files and structure via Val Town API
- No write operations are permitted through system tools
- System mode enables debugging and administration without compromising user data isolation
- All file access is handled through Val Town's secure API infrastructure
- Namespace Preservation: System mode preserves the original user's namespace, ensuring consistent access to the user's data across regular and system operations