Building a Custom MCP Server for AWS S3: The Lessons I learned & develop MCP πŸͺ£πŸ”ŒπŸ€–

Published
2025-04-25
Tags

What is MCP?

Model Context Protocol (MCP) is a standardized way for Large Language Models to interact with external systems:

  • Core Function: Allows AI models to access external tools and data sources
  • Key Benefit: Extends AI capabilities beyond training data
  • Analogy: Acts like "USB-C for AI models" - a universal connector
  • Implementation: Developers create MCP servers that LLMs can connect to


MCP Architecture

According to the official MCP documentation, MCP works by establishing a standardized communication protocol between language models and external components:

  • Client-Server Architecture: LLMs connect to MCP servers that expose capabilities


Core Components:

  • Tools: Functions that perform specific actions (like searching or computing)
  • Resources: Access points to external data (files, databases, APIs)
  • Schema Definitions: Structured data formats for consistency
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  LLM Platform  β”‚     β”‚          MCP Server           β”‚
β”‚                β”‚     β”‚                               β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚     β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚ β”‚ Claude/GPT β”‚ β”‚     β”‚  β”‚MCP SDK β”‚<--->β”‚Custom   β”‚   β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚     β”‚  β”‚        β”‚     β”‚Server   β”‚   β”‚
β”‚       β–²        β”‚     β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜   β”‚
β”‚       β”‚        β”‚     β”‚                      β”‚        β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚     β”‚                      β–Ό        β”‚
β”‚ β”‚MCP Client  β”‚β—„β”œβ”€β”€β”€β”€β”€β”€              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚     β”‚              β”‚External API β”‚  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
                       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

This architecture allows LLMs to:

  • Access real-time information beyond their training data
  • Use specialized tools without needing custom integrations
  • Interact with protected resources in a controlled manner


The AWS S3 MCP Server Project

  • This video shows demo that summarize PDF file content in AWS S3 bucket.

Motivation

As a software engineer working with both AWS services and AI tools, I often found myself manually serving as the intermediary between these systems - downloading files from S3, extracting their contents, and then providing that information to an LLM for analysis or summarization.

This workflow was inefficient and interrupted my productivity. I wanted Claude to directly access my S3 buckets, browse their contents, and analyze documents without my manual intervention.

By building aws-s3-mcp, I aimed to:

  • Eliminate the download-upload cycle between S3 and AI assistants
  • Allow AI to analyze cloud-stored documents directly
  • Maintain security controls for S3 access
  • Demonstrate the power of custom MCP servers for specific use cases


Implementation

The aws-s3-mcp server implements three core tools:

  1. list-buckets: Shows available S3 buckets
  2. list-objects: Displays files within a specific bucket
  3. get-object: Retrieves and processes file content, with special handling for PDFs


Key technologies used:

  • TypeScript for type safety and modern development features
  • AWS SDK v3 for S3 interaction
  • MCP SDK from @modelcontextprotocol/sdk to implement the protocol
  • pdf-parse for extracting text from PDF files
  • Docker for containerization and security isolation


The implementation uses a class-based architecture with a standardized tool interface:

// The IMCPTool interface definition that all tools implement
export interface IMCPTool<
  TParams extends Record<string, z.ZodType> = Record<string, z.ZodType>
> {
  // Tool name
  readonly name: string;

  // Tool description
  readonly description: string;

  // Parameter definitions
  readonly parameters: TParams;

  // Execute the tool
  execute(args: InferZodParams<TParams>): Promise<{
    content: {
      type: "text";
      text: string;
      [key: string]: unknown;
    }[];
    isError?: boolean;
    [key: string]: unknown;
  }>;
}

// Example implementation: List Buckets Tool
export class ListBucketsTool implements IMCPTool {
  readonly name = "list-buckets";
  readonly description = "List available S3 buckets";
  readonly parameters = {} as const;

  constructor(private s3Resource: S3Resource) {}

  async execute() {
    try {
      const buckets = await this.s3Resource.listBuckets();
      return {
        content: [
          {
            type: "text" as const,
            text: JSON.stringify(buckets, null, 2),
          },
        ],
      };
    } catch (error) {
      return createErrorResponse(
        error,
        `Error listing buckets: ${
          error instanceof Error ? error.message : String(error)
        }`
      );
    }
  }
}

Why these approaches?:

  • Interface-Driven Design: Ensures all tools follow the same structure
  • Dependency Injection: Makes testing easier with mock resources
  • Type Safety: TypeScript generics provide parameter checking
  • Separation of Concerns: S3 interaction logic is isolated from tool implementation


MCP Server Security Considerations

Security is critical when working with MCP servers, especially those that access cloud resources.

According to the Equixly 2025 security analysis, MCP servers face several common vulnerabilities:

  • 🚨 Command Injection: 43% of analyzed MCP servers were vulnerable
  • πŸ”Β Path Traversal: 22% of servers affected
  • 🌐 Server-Side Request Forgery (SSRF): 30% of servers at risk


Approach: Docker Isolation with Deno

To ensure these security risks, Docker & Deno are effective way.

Dockerfile with Deno:

FROM denoland/deno:latest

WORKDIR /app

# Create non-root user for enhanced security
USER deno

# Use specific permission constraints for enhanced security
ENTRYPOINT ["deno", "run", \\
  "--allow-net=s3.amazonaws.com", \\
  "--allow-env=AWS_REGION,AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY", \\
  "--allow-read=/app", \\
  "--allow-sys"]
CMD ["src/index.ts"]

The implementation includes:

  • Container Isolation: Docker provides sandboxing to limit potential damage
  • Non-Root Execution: The deno user reduces privileges
  • Permission-Based Security: Deno's explicit permission system prevents unauthorized actions
  • Network Security: Only allows connections to necessary endpoints
  • Environment Variable Control: Only exposes required environment variables


Secure vs. Insecure Configuration

When configuring Claude Desktop to use the aws-s3-mcp server, security depends on your setup:

Insecure Configuration:

"mcpServer": {
  "command": "npx",
  "args": ["aws-s3-mcp"],
  "env": {
    "AWS_ACCESS_KEY_ID": "your_key",
    "AWS_SECRET_ACCESS_KEY": "your_secret"
 }
}

Secure Configuration:

"mcpServer": {
 "command": "docker",
 "args": [
   "run",
   "-i",
   "--rm",
   "-e",
   "AWS_REGION",
   "-e",
   "S3_BUCKETS",
   "-e",
   "AWS_ACCESS_KEY_ID",
   "-e",
   "AWS_SECRET_ACCESS_KEY",
   "aws-s3-mcp:latest"
 ],
 "env": {
   "AWS_REGION": "us-east-1",
   "S3_BUCKETS": "bucket1,bucket2",
   "AWS_ACCESS_KEY_ID": "${YOUR_ACCESS_KEY}",
   "AWS_SECRET_ACCESS_KEY": "${YOUR_SECRET_KEY}"
 }
}

This configuration connects Claude to your AWS S3 buckets securely, using Docker for isolation and environment variables for credentials. The claude_desktop_config.json file can be found at:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\\Claude\\claude_desktop_config.json

After configuring, restart Claude Desktop to apply the changes. You will then be able to ask Claude to interact with your S3 buckets.

The secure approach uses Docker isolation and environment variables, significantly reducing the risk of credential exposure or unauthorized access.

If you're interested in details, check out the PR.πŸ‘‡


Are We in a Transition Period? The End of Local MCP Servers

On March 26, 2025, the MCP specification was updated to include Streamable HTTP support: https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http


Additionally, the MCP TypeScript SDK was also updated to support Streamable HTTP: https://github.com/modelcontextprotocol/typescript-sdk/releases/tag/1.10.0


These changes are significant because they enable the development of remote MCP servers, particularly for TypeScript implementations. In the future, MCP servers will likely be deployed to remote servers, and MCP clients will connect to these remote servers rather than running locally. Similar support will likely follow in SDKs for other languages.

As remote MCP servers become more prevalent, the need to run MCP servers locally will diminish. This transition represents a significant shift in the MCP ecosystem, moving from local execution to a more distributed, cloud-based architecture.

For those interested in hosting remote MCP servers in cloud environments, Microsoft has published a comprehensive guide: https://techcommunity.microsoft.com/blog/appsonazureblog/host-remote-mcp-servers-in-azure-app-service/4405082


Conclusion

The aws-s3-mcp project demonstrates the power of custom MCP servers to extend AI capabilities in targeted, practical ways. By connecting Claude directly to AWS S3, it eliminates manual workflows and enables new AI-assisted cloud storage use cases.

As the MCP ecosystem continues to evolve, I can expect to see more specialized servers, improved security practices, and expanded capabilities like remote execution and streaming responses. These developments will further enhance the symbiotic relationship between AI assistants and external systems.

The project is available on: