Skip to content

Homepage

AWS AWS Lambda MCP Cookbook - a Serverless MCP Server Blueprint

alt_text

The Problem

Starting a production grade Serverless MCP can be overwhelming. You need to figure out many questions and challenges that have nothing to do with your business domain:

  • How to deploy to the cloud? What IAC framework do you choose?
  • How to write a SaaS-oriented CI/CD pipeline? What does it need to contain?
  • How do you handle observability, logging, tracing, metrics?
  • How do you write a production grade Lambda function?
  • How do you handle testing?
  • What makes an AWS Lambda handler resilient, traceable, and easy to maintain? How do you write such a code?

The Solution

This project aims to reduce cognitive load and answer these questions for you by providing a skeleton Python Serverless service blueprint that implements best practices for AWS Lambda, Serverless CI/CD, and AWS CDK in one blueprint project.

This project is a blueprint for new Serverless MCP servers.

It provides two implementation options:

  1. Pure, native Lambda function with no FastMCP.
  2. Lambda with AWS web adapter and FastMCP

Choose the architecture that you see fit, each with its own pros and cons.

design

Option 1: Serverless Native Lambda MCP Server

This project provides a working, open source based, AWS Lambda based Python MCP server implementation.

The MCP server uses JSON RPC over HTTP (non streamable) via API Gateway's body payload parameter. See integration tests and see how the test event is generated.

It contains an advanced implementation including IaC CDK code and a CI/CD pipeline, testing, observability and more (see Features section).

It's started based on AWS sample for MCP - but had major refactors since, combined with the AWS Lambda Handler cookbook template.

Better fitted for POCs or tool oriented MCPs. Can be secured with custom authentication code and WAF.

Option 2: Serverless Lambda Web Adapter & FastMCP

Based on AWS Web Adapter and FastMCP.

Use an HTTP API GW and Lambda function. Can be used with a REST API GW with a custom domain too.

Better fitted for production-grade MCP servers as it upholds to the official MCP protocol and has native auth mechanism (OAuth).

Monitoring Design

monitoring

Features

  • PURE Lambda - not web adapter, no FastMCP required or Web adapter with FastMCP.
  • Python Serverless MCP server with a recommended file structure.
  • MCP Tools input validation: check argument types and values
  • CDK infrastructure with infrastructure tests and security tests.
  • Tests - unit, integration (tests for full MCP messages) and E2E with a real MCP client
  • CI/CD pipelines based on Github actions that deploys to AWS with python linters, complexity checks and style formatters.
  • CI/CD pipeline deploys to dev/staging and production environments with different gates between each environment
  • Makefile for simple developer experience.
  • The AWS Lambda handler embodies Serverless best practices and has all the bells and whistles for a proper production ready handler.
  • AWS Lambda handler uses AWS Lambda Powertools.
  • AWS Lambda handler 3 layer architecture: handler layer, logic layer and data access layer
  • Session context storage in DynamoDB - global getter and setter (get_session, set_session) - be advised, has security issue - need to match session id to user
  • API protected by WAF with four AWS managed rules in production deployment
  • CloudWatch dashboards - High level and low level including CloudWatch alarms

The GitHub blueprint project can be found at https://github.com/ran-isenberg/aws-lambda-mcp-cookbook.

Serverless Best Practices

The AWS Lambda handler will implement multiple best practice utilities.

Each utility is implemented when a new blog post is published about that utility.

The utilities cover multiple aspects of a production-ready service, including:

While the code examples are written in Python, the principles are valid to any supported AWS Lambda handler programming language.

Security

For pure Lambda:

  • WAF connected in production accounts (requires having an environment variable during deployment called 'ENVIRONMENT' with a value of 'production')
  • Auth/Authz function placeholder in the mcp.py handler function - see authentication.py
  • It is recommended to either use IAM/Cognito/Lambda authorizer or use the authentication.py and implement identity provider token validation flow.

For FastMCP:

  • Use FastMCP Auth parameter for Oauth implementation.
  • If you use session id management, you need to make sure the session id matches the user id by yourself.

Known Issues

  • There might be security issues with this implementation, MCP is very new and has many issues.
  • Session saving - there's no match validation between session id and user id/tenant id. This is a TODO item.
  • It is not possible to manually update session data, only fetch.
  • Pure Lambda variation has limited MCP protocol support, it's based used for tools only simple MCP. For full blown services, use the FastMCP variation.

Handler Examples

Pure Lambda:

service/handlers/mcp.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from aws_lambda_env_modeler import init_environment_variables
from aws_lambda_powertools.logging import correlation_paths
from aws_lambda_powertools.metrics import MetricUnit
from aws_lambda_powertools.utilities.typing import LambdaContext

from service.handlers.models.env_vars import McpHandlerEnvVars
from service.handlers.utils.authentication import authenticate
from service.handlers.utils.mcp import mcp
from service.handlers.utils.observability import logger, metrics, tracer
from service.logic.tools.math import add_two_numbers


@mcp.tool()
def math(a: int, b: int) -> int:
    """Add two numbers together"""

    # Uncomment the following line if you want to use session data
    # session_data: Optional[SessionData] = mcp.get_session()

    # call logic layer
    result = add_two_numbers(a, b)

    # save session data
    mcp.set_session(data={'result': result})

    metrics.add_metric(name='ValidMcpEvents', unit=MetricUnit.Count, value=1)
    return result


@init_environment_variables(model=McpHandlerEnvVars)
@logger.inject_lambda_context(correlation_id_path=correlation_paths.API_GATEWAY_REST)
@metrics.log_metrics
@tracer.capture_lambda_handler(capture_response=False)
def lambda_handler(event: dict, context: LambdaContext) -> dict:
    authenticate(event, context)
    return mcp.handle_request(event, context)

Handler is found at service/handlers/mcp.py

MCP engine found at service/mcp_lambda_handler folder

FastMCP Lambda:

service/mcp_server.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from fastmcp import FastMCP

from service.handlers.utils.observability import logger
from service.logic.prompts.hld import hld_prompt
from service.logic.resources.profiles import get_profile_by_id
from service.logic.tools.math import add_two_numbers

mcp: FastMCP = FastMCP(name='mcp-lambda-server')


@mcp.tool
def math(a: int, b: int) -> int:
    """Add two numbers together"""
    logger.info('using math tool', extra={'a': a, 'b': b})
    return add_two_numbers(a, b)


# Dynamic resource template
@mcp.resource('users://{user_id}/profile')
def get_profile(user_id: int) -> dict[str, str]:
    """Fetch user profile by user ID."""
    logger.info('fetching user profile', extra={'user_id': user_id})
    return get_profile_by_id(user_id)


@mcp.prompt()
def generate_serverless_design_prompt(design_requirements: str) -> str:
    """Generate a serverless design prompt based on the provided design requirements."""
    logger.info('generating serverless design prompt', extra={'design_requirements': design_requirements})
    return hld_prompt(design_requirements)


app = mcp.http_app(transport='http', stateless_http=True, json_response=True)

Handler is found at service/mcp_server.py

License

This library is licensed under the MIT License. See the LICENSE file.