参考资料
- https://github.com/awslabs/run-model-context-protocol-servers-with-aws-lambda
- https://aws.amazon.com/cn/blogs/china/deploy-streamable-http-github-mcp-server-using-amazon-lambda/
- https://modelcontextprotocol.io/specification/2025-06-18/basic/transports
- https://dev.to/aws-builders/mcp-server-with-aws-lambda-and-http-api-gateway-1j49
MCP Transports 基础理论
参考官方文档(https://modelcontextprotocol.io/specification/2025-06-18/basic/transports)MCP 的传输机制(Transport)指的是客户端与服务端之间的通信机制。MCP 的客户端与服务端之间的通信,需遵循以下原则:
- 消息格式:所有 MCP 消息都必须是 JSON-RPC 格式;所有 JSON-RPC 消息必须使用 UTF-8 编码
- 两种标准化的传输方式:stdio (标准输入/输出)、Streamable HTTP 。在可能的情况下,客户端应当支持标准输入输出 (stdio)
- 客户端和服务器可以通过模块化的方式实现自定义的传输机制
早期 MCP 规范是基于 HTTP + SSE(Server-Sent Events)组合的传输方式。其中SSE 是一种基于 HTTP 的标准协议,允许服务器向客户端推送实时数据。客户端发起一个标准 HTTP 请求,服务器保持连接不中断,并以 text/event-stream 格式持续发送数据块。由于实现复杂(需要实现两个不同端点),并且在代理,负载均衡,超时重连等方面难以处理,现在 被标记为过时 (deprecated) 。HTTP + SSE的请求流程如下
- 客户端通过 HTTP GET 请求连接一个 SSE 端点(如
/events),服务器在这个连接上持续推送事件(server → client)。整个调用期间 MCP Client 需要一直和 MCP Server 保持/sse 端点的长链接不能断开,否则会可能丢失某次调用的数据。 - 客户端通过 HTTP POST 到一个消息端点(如
/message)向服务器发消息(client → server)
Streamable HTTP (Response Streaming) 则提供一个 单一 HTTP 端点,服务器必须提供一个支持 POST 和 GET 方法的单个 HTTP 端点路径
- 客户端向该端点用 POST 发送 JSON-RPC 请求/通知/响应,POST 请求的正文必须是单个 JSON-RPC 请求、通知或响应。客户端必须在
Accept头部中包含,列出application/json和text/event-stream作为支持的内容类型。 - 如果客户端输入是一个 JSON-RPC 请求,服务器必须返回
Content-Type: text/event-stream以启动 SSE 流,或者返回Content-Type: application/json以返回一个 JSON 对象。 - 为了向下兼容 SSE,客户端可以发送一个 HTTP GET 请求到 MCP 端点。这可以用来打开一个 SSE 流,允许服务器向客户端通信,而无需客户端先通过 HTTP POST 发送数据
- 此外MCP 服务器可以是无状态的,或者可以使用请求头中的
sessionId来处理状态
MCP的HTTP+SSE 和 Streamable HTTP规范的比较和区别,网络上已经有很好的解释可以参考,https://zhuanlan.zhihu.com/p/1932231185808994798
关于通过lambda函数实现mcp的使用方法有如下几种
在 Lambda 函数中编写自定义 MCP 服务器
可以参考MCP Lambda Handler Module,提供了一个极简、可扩展的框架,用于构建具有可插拔会话管理支持的 MCP HTTP 端点。作者提供了完整的示例Deploy MCP Server in AWS Lambda Without Containers: A Complete Guide,主要的作用是通过自定义的 MCP 处理器,将 MCP 协议适配到 Lambda 的请求-响应模型,同时保持完全的协议兼容性。
之所以没有使用容器实现是由于冷启动时间较长,以及维护容器镜像带来的更复杂的部署和管理成本
py
def _handle_http_post(self, parsed_event: Any) -> dict[str, Any]:
method_handlers = {
MCPMethod.INITIALIZE: self._handle_initialize,
MCPMethod.TOOLS_LIST: self._handle_tools_list,
MCPMethod.TOOLS_CALL: self._handle_tools_call,
MCPMethod.PING: self._handle_ping,
}
method = parsed_event.body.method
if method in method_handlers:
return method_handlers[method](parsed_event)
return self._create_error_response(
ERROR_METHOD_NOT_FOUND,
f"Method not found: {method}"
)
还有方案直接在lambda中运行stdio mcp服务器,https://github.com/awslabs/run-model-context-protocol-servers-with-aws-lambda,主要目的是将现有的stdio的mcp服务直接迁移到lambda中。Lambda 函数被触发时,会在内部启动 MCP 服务器进程,将请求转换为 stdio 输入,获取响应后再封装回 Lambda 返回值。
lambda函数的入口代码如下
py
import sys
from mcp.client.stdio import StdioServerParameters
from mcp_lambda import stdio_server_adapter
server_params = StdioServerParameters(
command=sys.executable,
args=[
"-m",
"my_mcp_server_python_module",
"--my-server-command-line-parameter",
"some_value",
],
)
def handler(event, context):
return stdio_server_adapter(server_params, event, context)
通过 stdio 调用现有的 Lambda 函数
可以参考AWS Lambda MCP Server。MCP 服务器充当 MCP 客户端和Lambda 函数之间的桥梁,可以在不修改代码的情况下将Lambda 函数作为 MCP 工具调用。图示如下
Model
MCP Client
MCP2Lambda
(MCP Server)
Lambda Function
Other AWS Services
Internet
VPC
mcp的配置示例如下
json
{
"mcpServers": {
"awslabs.lambda-tool-mcp-server": {
"command": "uvx",
"args": ["awslabs.lambda-tool-mcp-server@latest"],
"env": {
"AWS_PROFILE": "your-aws-profile",
"AWS_REGION": "us-east-1",
"FUNCTION_LIST": "your-first-function, your-second-function"
}
}
}
}
使用Lambda Web Adapter转发请求
Lambda Web Adapter 是一个用 Rust 编写的扩展,是一个轻量级的二进制文件,作为 Lambda 层的扩展运行。它会将 Lambda 的 HTTP 请求转换为标准的 HTTP 调用发送给 Python 进程(如 FastAPI 或直接运行的 MCP Server)。

具体流程如下
- HTTP 请求 → API Gateway / ALB 向 Lambda 函数发送事件
- Lambda runtime → 接收事件
- Lambda adapter → 将 Lambda 事件转换为 HTTP 请求
- 业务 Web 应用 → 在 8080 端口接收标准 HTTP 请求
- 响应流程 → adapter 将 HTTP 响应还原为 Lambda 格式
这种方式和自定义的MCP适配器的做法比较类似
通过Lambda Web Adapter实现MCP服务
Lambda Web Adapter (LWA) 是一个由 AWS 官方推出的开源项目,能在不修改代码的情况下,把任何使用标准 HTTP 协议的 Web 应用直接跑在 AWS Lambda 上。Model Context Protocol (MCP) 传统上多用于本地 stdio 通信,但要部署到Lambda需要使用 Streamable HTTP模式。这里使用 FastAPI + FastMCP ,并通过 LWA 打包成 Docker 镜像部署。
当一个新的 Lambda 执行环境启动时,Lambda Web Adapter 会作为 Lambda 扩展启动,随后是 Web 应用程序。默认情况下,Lambda Web Adapter 会向 Web 应用程序发送 HTTP GET 请求到
http://127.0.0.1:8080/。端口号和路径可以通过两个环境变量AWS_LWA_READINESS_CHECK_PORT和AWS_LWA_READINESS_CHECK_PATH进行自定义。Lambda Web Adapter 将每隔 10 毫秒重试此请求,直到 Web 应用返回 HTTP 响应( 状态码 >= 100 且 < 500 )或函数超时。此外,您可以通过将
AWS_LWA_READINESS_CHECK_PROTOCOL设置为tcp来配置适配器使用 TCP 连接进行就绪检查。通过就绪检查后,Lambda Web Adapter 将启动 Lambda 运行时并将调用转发到 Web 应用程序。
要使用 Lambda Web Adapter 与 Docker 镜像一起使用,需要在 Dockerfile 中打包的 Web 应用(HTTP API),并在容器内的 /opt/extensions 中添加一行来复制 Lambda Web Adapter 二进制文件
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.9.1 /lambda-adapter /opt/extensions/lambda-adapter
中国区也提供了事先打包好的layer供用户使用
arn:aws-cn:lambda:cn-north-1:041581134020:layer:LambdaAdapterLayerX86:25
arn:aws-cn:lambda:cn-northwest-1:069767869989:layer:LambdaAdapterLayerX86:25
首先编写mcp服务逻辑如下,实现一个查询是否为节假日的服务
py
from fastmcp import FastMCP
import holidays
from datetime import date
mcp = FastMCP("HolidayChecker")
@mcp.tool()
def check_holiday(country: str = "CN", date_str: str = None) -> str:
"""
检查指定日期是否为公共节假日。
:param country: ISO国家代码 (如 'CN', 'US')
:param date_str: 日期格式 'YYYY-MM-DD',不传则默认为今天
"""
try:
# 如果不传日期,默认取今天
target_date = date.fromisoformat(date_str) if date_str else date.today()
# 获取该国家的节日列表
country_holidays = holidays.country_holidays(country)
holiday_name = country_holidays.get(target_date)
if holiday_name:
return f"{target_date} 是 {country} 的节假日: {holiday_name}"
return f"{target_date} 不是 {country} 的公共节假日"
except Exception as e:
return f"查询失败: {str(e)}"
mcp.run(transport="http", host="0.0.0.0", port=8080)
本地启动后检查连接正常

创建dockerfile打包镜像
dockerfile
FROM public.ecr.aws/docker/library/python:3.12-slim
# 从 LWA 镜像中复制适配器二进制文件
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.9.1 /lambda-adapter /opt/extensions/lambda-adapter
WORKDIR /var/task
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY app.py .
ENV PORT=8080
EXPOSE 8080
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8080"]
构建镜像并推送到ECR仓库随后部署lambda函数。通过alb将lambda暴露出来,这里lambda不需要配置vpc。

在尝试客户端代码出现报错
json
Error from MCP server: StreamableHTTPError: Streamable HTTP error: Error POSTing to endpoint: {"jsonrpc":"2.0","id":"server-error","error":{"code":-32600,"message":"Session not found"}}
at StreamableHTTPClientTransport.send (file:///home/ec2-user/.npm/_npx/5a9d879542beca3a/node_modules/@modelcontextprotocol/sdk/dist/esm/client/streamableHttp.js:364:23)
at process.processTicksAndRejections (node:internal/process/task_queues:105:5) {
code: 404
}
检查发现FastMCP 实际上提供了一个关键的参数:stateless_http 。在 Lambda 这种无状态的环境中,默认的有状态(Stateful)SSE 会因为找不到 sessionId 而崩溃。要解决 Session not found 错误,需要在 mcp.run 中开启 无状态模式 ,明确指定 stateless_http=True
mcp.run(transport="http", host="0.0.0.0", port=8080, stateless_http=True)
有状态和无状态服务的区别?
- 有状态模式(默认)
- 客户端建立 SSE 连接,服务器在内存中创建一个 Session。
- 客户端发送 POST 请求执行 Tool。
- 服务器根据 POST 里的
sessionId去内存找那个连接。由于 Lambda 会自动销毁或扩展,POST 请求可能打到了实例 B,而 SSE 连接在实例 A,导致Session not found。
- 无状态模式 (
stateless_http=True)- 服务器不再强依赖内存中的 Session 绑定。
- 它允许请求以更标准、独立的方式处理,非常适合 Lambda 这种一次性的执行逻辑。
加上参数后使用strands确实调用成功了,但是mcp inspector会出现超时目前还不知道原因。客户端代码如下
py
from mcp.client.streamable_http import streamablehttp_client
from strands.tools.mcp.mcp_client import MCPClient
from strands import Agent
from strands.models.openai import OpenAIModel
import logging
logging.basicConfig(level=logging.DEBUG)
streamable_http_mcp_client = MCPClient(
lambda: streamablehttp_client("http://172.31.2.178:8090/mcp")
)
litellm_model = OpenAIModel(
model_id="gpt-4o-mini",
client_args={
"api_key": "sk-uzpq0u0n5FN14HorW45hUw",
"base_url": "http://localhost:4000",
},
)
# Create an agent with MCP tools
with streamable_http_mcp_client:
try:
tools = streamable_http_mcp_client.list_tools_sync()
for tool in tools:
print(tool.tool_name)
# print("Tools:", tools)
agent = Agent(
model=litellm_model,
tools=tools,
system_prompt="You are a helpful assistant.",
)
response = agent("What festival is on October 18st?")
except Exception as e:
print("Connection failed:", repr(e))
输出结果如下
