Skip to content

saitharun14/mcp-server-lambda

Repository files navigation

MCP Server with Spring AI and AWS Cognito

A Model Context Protocol (MCP) server built with Spring AI MCP, Spring Boot, GraalVM, and AWS Cognito OAuth2, deployed on AWS EC2 with Google SSO and Streaming HTTP/SSE support.

Overview

This project implements a production-ready MCP server using the Spring AI MCP Server library (spring-ai-starter-mcp-server-webmvc:1.1.0-M4) that provides tools for AI assistants to interact with through the JSON-RPC 2.0 protocol with Server-Sent Events (SSE) streaming. The server leverages:

  • Spring AI MCP - Automatic tool discovery and registration with @Tool annotations
  • AWS Cognito - Enterprise-grade OAuth2 authentication
  • Google SSO - Seamless user authentication via Google accounts
  • Spring Boot 3.3.x - Modern Java framework with production-ready features
  • GraalVM - Optional native image compilation for faster startup
  • EC2 Deployment - Runs on EC2 with embedded Tomcat for full Spring MVC support
  • CloudFormation - Infrastructure as Code for reproducible deployments

Features

  • OAuth2 Authentication - AWS Cognito with Google SSO integration
  • Spring Security - Enterprise-grade security with OAuth2 Client support
  • Streamable HTTP Transport - MCP spec 2025-03-26 (implemented using SSE)
  • Spring AI MCP Server - Official Spring AI MCP library
  • Automatic Tool Discovery - @Tool annotated methods auto-registered
  • Spring Boot 3.3.x - Latest Spring framework with enhanced features
  • EC2 Deployment - Full CloudFormation template with VPC, security groups
  • GraalVM Native Image - Optional native compilation for faster startup
  • Web UI - Thymeleaf template showing user info and authentication status
  • Example Tools - Pre-built tools (add, multiply, echo, calculator, getCurrentTime)
  • Rich Documentation - Tools with markdown descriptions
  • Extensible Architecture - Add tools by defining @Tool methods

Architecture

Streaming HTTP/SSE Architecture (Recommended)

┌─────────────┐
│   Client    │
│  (Claude)   │
└─────┬───────┘
      │ HTTPS + SSE Streaming
      │
┌─────▼──────────────┐
│  Lambda Function   │
│  URL (Streaming)   │
└─────┬──────────────┘
      │
┌─────▼──────────┐
│  Lambda        │
│  Function      │
│  ┌──────────┐  │
│  │ Spring   │  │
│  │ AI MCP   │  │
│  │ Server   │  │
│  │ (SSE)    │  │
│  └──────────┘  │
└────────────────┘

Note: This architecture has been replaced with EC2 deployment (see below).

Current Architecture: EC2 with OAuth2 and Streamable HTTP

┌──────────────┐
│    User      │
│  (Browser)   │
└──────┬───────┘
       │ 1. Login with Google
       │
┌──────▼────────────────┐
│  AWS Cognito          │
│  User Pool            │
│  ┌─────────────────┐  │
│  │ Google Identity │  │
│  │    Provider     │  │
│  └─────────────────┘  │
└──────┬────────────────┘
       │ 2. OAuth2 Tokens
       │
┌──────▼────────────────────────┐
│  EC2 Instance                 │
│  ┌─────────────────────────┐  │
│  │  Spring Boot App        │  │
│  │  ┌───────────────────┐  │  │
│  │  │ Spring Security   │  │  │
│  │  │ OAuth2 Client     │  │  │
│  │  └────────┬──────────┘  │  │
│  │           │              │  │
│  │  ┌────────▼──────────┐  │  │
│  │  │ Spring AI MCP     │  │  │
│  │  │ Server            │  │  │
│  │  │ (Streamable HTTP) │  │  │
│  │  │ /mcp/messages     │  │  │
│  │  └────────┬──────────┘  │  │
│  │           │              │  │
│  │  ┌────────▼──────────┐  │  │
│  │  │ @Tool Services    │  │  │
│  │  │ (Auto-discovered) │  │  │
│  │  └───────────────────┘  │  │
│  └─────────────────────────┘  │
└────────────────────────────────┘

┌──────────────┐
│  AI Client   │
│  (Claude)    │
└──────┬───────┘
       │ 3. MCP Requests via Streamable HTTP
       │    (JSON-RPC over SSE)
       │
       └──────► /mcp/messages (Streamable HTTP endpoint)

Key Components

  1. AWS Cognito User Pool

    • Manages user authentication
    • Integrates Google as identity provider
    • Issues OAuth2 tokens (ID token, access token, refresh token)
  2. Spring Security OAuth2

    • Validates OAuth2 tokens
    • Manages user sessions
    • Protects MCP endpoints
  3. Spring AI MCP Server

    • Auto-configured via spring-ai-starter-mcp-server-webmvc
    • Handles JSON-RPC 2.0 protocol
    • Uses Streamable HTTP transport (implemented via SSE)
    • Endpoint: /mcp/messages for Streamable HTTP
    • Automatically discovers @Tool annotated methods
  4. EC2 Instance

    • Runs embedded Tomcat server
    • Deployed via CloudFormation
    • Configured with security groups and IAM roles

Transport Protocol

This server uses Streamable HTTP transport (MCP spec 2025-03-26):

  • Protocol: Streamable HTTP (the MCP transport mode)
  • Implementation: Server-Sent Events (SSE) technology
  • Endpoint: /mcp/messages
  • Benefits: Real-time streaming, better for long-running operations
  • Support: Full Spring MVC with servlet container

Prerequisites

  • Java 21 or higher
  • Gradle 8.x or higher
  • AWS CLI configured with appropriate credentials
  • EC2 Key Pair for SSH access
  • Google Cloud account for OAuth2 credentials (Client ID and Secret)
  • GraalVM (optional, for native image compilation)
  • jq (optional, for testing scripts)

Project Structure

mcp-server-lambda/
├── src/
│   ├── main/
│   │   ├── java/com/example/mcp/
│   │   │   ├── service/tools/      # MCP tool definitions (@Tool methods)
│   │   │   │   ├── BasicMcpTools.java
│   │   │   │   └── AdvancedMcpTools.java
│   │   │   ├── controller/         # Spring MVC controllers
│   │   │   │   └── AuthController.java
│   │   │   ├── config/             # Spring configuration
│   │   │   │   └── SecurityConfig.java
│   │   │   └── McpServerApplication.java
│   │   └── resources/
│   │       ├── templates/          # Thymeleaf templates
│   │       │   └── index.html
│   │       ├── META-INF/native-image/  # GraalVM configs
│   │       └── application.yml     # Application config with OAuth2
│   └── test/
│       └── java/com/example/mcp/
│           └── McpToolsTest.java
├── build.gradle                    # Gradle build with Spring AI MCP and OAuth2
├── cloudformation-ec2.yaml         # CloudFormation template for EC2
├── deploy-ec2.sh                   # EC2 deployment script
├── build-and-deploy-app.sh         # Build and deploy application
├── QUICKSTART.md                   # Quick start guide
└── README.md

Available Tools

The server comes with several example tools:

  1. add - Adds two numbers

    • Parameters: a (number), b (number)
    • Returns: Result of a + b
  2. multiply - Multiplies two numbers

    • Parameters: a (number), b (number)
    • Returns: Result of a × b
  3. echo - Echoes back a message

    • Parameters: message (string)
    • Returns: "Echo: {message}"
  4. calculator - Performs arithmetic operations

    • Parameters: operation (add/subtract/multiply/divide), a (number), b (number)
    • Returns: Result of the operation
  5. get_current_time - Returns current time in timezone

    • Parameters: timezone (string, default: "UTC")
    • Returns: Current time in the specified timezone

Quick Start

For a quick 10-minute setup, see QUICKSTART.md.

Building

Standard JAR Build

# Build the project
./gradlew clean build

This creates a Spring Boot executable JAR at: build/libs/mcp-server-lambda-1.0.0.jar

GraalVM Native Image Build

For faster startup times (optional):

# Install GraalVM first
# Then run native build
./gradlew nativeCompile

This creates a native executable at: build/native/nativeCompile/mcp-server

Deployment

Step 1: Set Up Google OAuth Credentials

  1. Go to Google Cloud Console
  2. Create an OAuth 2.0 Client ID
  3. Note down the Client ID and Client Secret
  4. You'll update redirect URIs after deployment

Step 2: Create EC2 Key Pair (if needed)

aws ec2 create-key-pair --key-name mcp-server-key \
  --query 'KeyMaterial' --output text > mcp-server-key.pem
chmod 400 mcp-server-key.pem

Step 3: Deploy Infrastructure

Set environment variables:

export GOOGLE_CLIENT_ID="your-google-client-id"
export GOOGLE_CLIENT_SECRET="your-google-client-secret"
export KEY_PAIR_NAME="mcp-server-key"

Deploy the CloudFormation stack:

./deploy-ec2.sh dev us-east-1

This will create:

  • VPC with public subnet
  • EC2 instance with Java 21
  • Security groups for HTTP/HTTPS
  • AWS Cognito User Pool with Google identity provider
  • OAuth2 configuration

Step 4: Update Google OAuth Redirect URIs

After deployment, add these redirect URIs in Google Cloud Console:

http://[EC2_PUBLIC_DNS]:8080/login/oauth2/code/cognito
http://[EC2_PUBLIC_IP]:8080/login/oauth2/code/cognito

Step 5: Build and Deploy Application

./build-and-deploy-app.sh dev us-east-1

This will:

  • Build the Spring Boot JAR
  • Retrieve Cognito Client Secret
  • Upload JAR to EC2
  • Configure systemd service
  • Start the application

Testing

Access the Web UI

Open your browser and navigate to:

http://[EC2_PUBLIC_DNS]:8080

Click "Login with Google via Cognito" to authenticate.

Test Health Endpoint

curl http://[EC2_PUBLIC_IP]:8080/actuator/health

Manual Testing Examples

Replace [EC2_PUBLIC_IP] with your EC2 instance's public IP address.

Initialize the MCP server:

curl -X POST http://[EC2_PUBLIC_IP]:8080/mcp \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {}
  }'

List available tools:

curl -X POST http://[EC2_PUBLIC_IP]:8080/mcp \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/list",
    "params": {}
  }'

Call the addition tool:

curl -X POST http://[EC2_PUBLIC_IP]:8080/mcp \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
      "name": "add",
      "arguments": {
        "a": 10,
        "b": 5
      }
    }
  }'

View Application Logs

SSH into EC2 and view logs:

ssh -i mcp-server-key.pem ec2-user@[EC2_PUBLIC_IP]
sudo journalctl -u mcp-server -f

Creating Custom Tools

With Spring AI MCP, adding a new tool is as simple as creating a method annotated with @Tool. The method is automatically discovered and registered as an MCP tool.

Example: Creating a Custom Tool

package com.example.mcp.service.tools;

import org.springframework.ai.mcp.server.Tool;
import org.springframework.ai.mcp.server.ToolParam;
import org.springframework.stereotype.Service;
import java.util.Map;

@Service
public class MyCustomTools {

    @Tool(name = "toUppercase", description = """
        Converts the provided text to uppercase.

        **Use Cases:**
        - Format text for headers or titles
        - Normalize text case
        - Text processing operations

        **Returns:**
        A map containing:
        - text: The original text
        - uppercased: The text converted to uppercase
        """)
    public Map<String, Object> toUppercase(
            @ToolParam(description = "The text to convert to uppercase") String text) {

        String uppercased = text.toUpperCase();

        return Map.of(
            "text", text,
            "uppercased", uppercased
        );
    }

    @Tool(name = "reverseString", description = """
        Reverses the provided text string.

        **Use Cases:**
        - String manipulation demos
        - Text processing operations
        - Palindrome checking

        **Returns:**
        A map containing:
        - original: The original text
        - reversed: The text reversed
        """)
    public Map<String, Object> reverseString(
            @ToolParam(description = "The text to reverse") String text) {

        String reversed = new StringBuilder(text).reverse().toString();

        return Map.of(
            "original", text,
            "reversed", reversed
        );
    }
}

Key Points:

  • Use @Service to make the class a Spring component
  • Use @Tool annotation on methods to define MCP tools
    • name: The tool name (how it's called via MCP)
    • description: Detailed description with use cases and return format (supports markdown)
  • Use @ToolParam to describe each parameter
  • Return Map<String, Object> with structured results
  • Spring AI automatically discovers and registers @Tool methods as MCP tools
  • Support rich descriptions with markdown formatting

The tools will be automatically registered and available via the MCP protocol with no additional configuration needed!

Configuration

Environment Variables

The application is configured via environment variables set in the systemd service:

  • SERVER_PORT - Application port (default: 8080)
  • COGNITO_CLIENT_ID - AWS Cognito User Pool Client ID
  • COGNITO_CLIENT_SECRET - AWS Cognito User Pool Client Secret
  • COGNITO_ISSUER_URI - Cognito issuer URI (e.g., https://cognito-idp.us-east-1.amazonaws.com/us-east-1_XXXXX)
  • COGNITO_USER_POOL_ID - AWS Cognito User Pool ID
  • COGNITO_DOMAIN - Cognito domain for authentication
  • AWS_REGION - AWS region (e.g., us-east-1)

Application Configuration (application.yml)

Key configuration sections:

spring:
  security:
    oauth2:
      client:
        registration:
          cognito:
            client-id: ${COGNITO_CLIENT_ID}
            client-secret: ${COGNITO_CLIENT_SECRET}
            scope: [openid, profile, email]

  ai:
    mcp:
      server:
        protocol: STREAMABLE  # MCP spec 2025-03-26
        type: ASYNC           # For streaming
        sse:
          endpoint: "/mcp/messages"

AWS EC2 Configuration

  • Instance Type: t3.small (or configurable via CloudFormation parameter)
  • Runtime: Java 21 (Amazon Corretto)
  • Service: systemd service (mcp-server.service)
  • Auto-start: Enabled on boot

GraalVM Native Image Settings

Native image configuration files are located in:

  • src/main/resources/META-INF/native-image/reflect-config.json
  • src/main/resources/META-INF/native-image/resource-config.json
  • src/main/resources/META-INF/native-image/native-image.properties

Performance Considerations

Standard JAR on EC2

  • Startup time: ~10-15 seconds
  • Memory usage: ~300-400MB
  • Response time: <100ms
  • JAR size: ~80-100MB

GraalVM Native Image on EC2

  • Startup time: ~1-2 seconds
  • Memory usage: ~100-150MB
  • Response time: <50ms
  • Binary size: ~40-60MB

GraalVM Native Image (Legacy)

  • Cold start: ~500ms-1s
  • Warm execution: <50ms
  • Package size: ~30-40MB

Troubleshooting

Build Issues

Problem: Gradle build fails

# Clean and rebuild
./gradlew clean build --refresh-dependencies

Problem: Native image compilation fails

# Ensure GraalVM is properly installed
java -version  # Should show GraalVM

# Check native-image tool
which native-image

Deployment Issues

Problem: CloudFormation stack creation fails

  • Check AWS CloudFormation console for detailed errors
  • Verify Google OAuth credentials are correct
  • Ensure EC2 key pair exists in the region

Problem: Application won't start on EC2

  • SSH into EC2: ssh -i mcp-server-key.pem ec2-user@[EC2_IP]
  • Check service status: sudo systemctl status mcp-server
  • View logs: sudo journalctl -u mcp-server -n 100 --no-pager

Problem: OAuth2 login fails

  • Verify Google OAuth redirect URIs are configured correctly
  • Check Cognito User Pool and Client configuration
  • Verify Cognito Client Secret in systemd service file

Problem: Can't connect to EC2

  • Verify security group allows inbound traffic on port 8080
  • Check EC2 instance is running
  • Verify public IP/DNS is correct

Testing Issues

Problem: Connection refused

  • Verify EC2 instance is running
  • Check application is running: ssh into EC2 and run sudo systemctl status mcp-server
  • Verify security group allows inbound traffic

Problem: CORS errors

  • Check SecurityConfig.java CORS configuration
  • Verify allowed origins are configured correctly

Monitoring and Logs

View Application Logs

SSH into EC2 and view real-time logs:

ssh -i mcp-server-key.pem ec2-user@[EC2_PUBLIC_IP]
sudo journalctl -u mcp-server -f

View last 100 log lines:

sudo journalctl -u mcp-server -n 100 --no-pager

Service Management

Restart application:

ssh -i mcp-server-key.pem ec2-user@[EC2_PUBLIC_IP]
sudo systemctl restart mcp-server

Stop application:

sudo systemctl stop mcp-server

Check service status:

sudo systemctl status mcp-server

CloudWatch (Optional)

Configure CloudWatch agent on EC2 for centralized logging:

  • Agent is pre-installed via UserData script
  • Configure log groups in CloudWatch console

Cost Estimation

EC2 pricing (approximate for t3.small in us-east-1):

  • On-Demand: ~$0.0208/hour = ~$15/month
  • Data transfer: First 100GB/month free, then $0.09/GB
  • Cognito: Free tier: 50,000 MAUs (Monthly Active Users)
  • CloudWatch: Basic monitoring free, detailed monitoring $0.14/instance/month

For typical development usage:

  • Monthly cost: ~$15-20 (EC2 instance running 24/7)
  • Can reduce costs by stopping instance when not needed

Security Considerations

  1. OAuth2 Authentication: All endpoints protected by AWS Cognito
  2. EC2 Security Groups: Configure to allow only necessary traffic
  3. SSH Access: Limit SSH access to specific IP ranges
  4. HTTPS: Configure SSL/TLS with AWS Certificate Manager (recommended for production)
  5. Secrets: Cognito Client Secret managed via systemd environment variables
  6. IAM Roles: EC2 instance uses least-privilege IAM role
  7. VPC: Instance deployed in dedicated VPC with public subnet

Contributing

To contribute to this project:

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests
  5. Submit a pull request

License

This project is licensed under the MIT License.

Resources

Documentation

AWS Services

OAuth2 & SSO

Support

For issues and questions:

  • Open an issue on GitHub
  • Check the Troubleshooting section
  • View application logs on EC2: sudo journalctl -u mcp-server -f
  • Check AWS CloudFormation console for deployment issues

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published