모델 컨텍스트 프로토콜(MCP)은 Claude, ChatGPT 등과 같은 대형 언어 모델(LLM)이 외부 세계와 상호작용할 수 있도록 하는 표준 방식으로 빠르게 부상하고 있습니다. MCP는 도구를 구조화된 방식으로 정의함으로써 LLM이 행동을 요청하고, 실시간 데이터를 가져오거나, 외부 API와 상호작용할 수 있게 하여 고정된 훈련 데이터를 넘어섭니다.
하지만 이러한 MCP "서버"(도구를 제공하는)는 배포 시 특히 현대 클라우드 환경에서 도전이 됩니다. 초기 많은 MCP 구현은 로컬 실행을 위해 설계되었으며, 표준 입력/출력(stdio)으로 통신하거나 Server-Sent Events(SSE)와 같은 프로토콜을 사용하여 스트리밍을 지원합니다. 기능적으로는 잘 작동하지만 이러한 접근 방식은 지속적인 연결 및 상태 기반 행동에 의존하게 되어 AWS Lambda와 같은 확장 가능하고 무상태이며 이벤트 기반 플랫폼에 맞지 않는 경우가 많습니다.
AWS Lambda는 자동 확장, 사용량 기반 비용 효율성, 그리고 제로 서버 관리 오버헤드와 같은 엄청난 이점을 제공합니다. 이 서버리스 환경에서 어떻게 간편하게, 생산 준비가 완료된 MCP 서버를 운영할 수 있을까요?
MCPEngine이 등장합니다. 이는 이러한 문제를 해결하기 위해 특별히 설계된 MCP의 오픈 소스 Python 구현입니다. MCPEngine은 HTTP 스트리밍을 지원하며 SSE와도 완벽히 호환되어 AWS Lambda의 요청/응답 모델에 맞습니다. 또한, 내장 인증 지원 및 간소화된 패키징을 포함하여 생산 배포를 위한 필수 기능을 묶어 제공합니다.
이 글에서는 AWS Lambda에서 MCP 서버를 구축하고 배포하기 위해 MCPEngine을 활용하는 방법을 탐구하며, 무상태 도구, 상태 관리 및 인증에 대해 설명합니다.
Apidog-MCP-Server는 Cursor가 API 문서를 직접 읽을 수 있도록 하여, 이 문서가 온라인에 게시된 문서거나 로컬 OpenAPI 파일일 수 있습니다.
API 디자인을 AI의 진실의 원천으로 설정함으로써 Apidog MCP 서버는 스키마를 기반으로 한 코드 생성, 엔드포인트를 통한 지능형 검색 및 코드 수정이 API 계약과 완벽하게 일치하는지 보장하여 개발 워크플로우를 간소화합니다.
MCP 지원이 곧 Apidog에 도착할 것이라고 공유하게 되어 기쁩니다! 🚀
— Apidog (@ApidogHQ) 2025년 3월 19일
Apidog MCP Server를 통해 API 문서를 직접 Agentic AI에 제공하여 코딩 경험을 한층 강화하세요! Cursor, Cline 또는 Windsurf를 사용하든 - 개발 프로세스가 더 빠르고 원활해질 것입니다.… pic.twitter.com/ew8U38mU0K

핵심 개념: MCPEngine과 Lambda
배포에 뛰어들기 전에 Lambda 통합을 위한 MCPEngine의 주요 구성 요소를 이해해 보겠습니다:
MCPEngine
: 도구를 조정하고 MCP 통신을 처리하는 중심 클래스입니다.@engine.tool()
데코레이터: Python 함수를 MCP 도구로 등록합니다. 함수 이름은 도구의 이름이 되고, docstring은 LLM에게 제공될 설명 역할을 합니다.engine.get_lambda_handler()
: 이 메서드는 AWS Lambda 호환 핸들러 함수를 생성합니다. 이 핸들러를 노출시키면 MCPEngine이 Lambda의 이벤트 페이로드를 MCP 요청으로 변환하고 응답 형식을 지정합니다.
간단한 무상태 도구 만들기
기본 사항부터 시작해 보겠습니다: Lambda에서 배포된 무상태 도구입니다. 이 예제는 간단한 인사 도구를 제공합니다.
전제 조건:
- Python 3.8+
- Lambda, ECR 및 IAM을 관리할 수 있는 권한이 있는 AWS 계정.
- 로컬에 설치된 Docker.
- 구성된 AWS CLI.
1. MCPEngine 설치:
pip install mcpengine[cli,lambda]
2. 애플리케이션 생성 (app.py
):
# app.py
from mcpengine import MCPEngine, Context
# 엔진 초기화
engine = MCPEngine()
@engine.tool()
def personalized_greeting(name: str) -> str:
"""
지정된 이름에 대한 친근한 인사말을 생성합니다.
누군가를 인사하라고 요청받을 때 이 도구를 사용하세요.
"""
# 간단한 무상태 로직
return f"안녕하세요, {name}! 서버리스 MCP 세계에 오신 것을 환영합니다."
# Lambda 핸들러 함수 가져오기
handler = engine.get_lambda_handler()
이 코드는 personalized_greeting
라는 단일 도구를 정의하며, 이는 name
을 받아 문자열을 반환합니다. handler
변수는 AWS Lambda가 호출할 것입니다.
배포 워크플로우: 코드에서 클라우드로
MCPEngine 애플리케이션을 Lambda에 배포하려면 Docker로 컨테이너화하고, Amazon Elastic Container Registry(ECR)로 푸시한 다음 Lambda 함수를 구성해야 합니다.
1. 애플리케이션을 Docker화 (Dockerfile
):
# 공식 AWS Lambda Python 기본 이미지를 사용합니다
FROM public.ecr.aws/lambda/python:3.12
# 컨테이너 내 작업 디렉토리 설정
WORKDIR ${LAMBDA_TASK_ROOT}
# Docker 캐시를 활용하기 위해 먼저 요구 사항을 복사합니다
COPY requirements.txt .
# 종속성 설치 (mcpengine이 requirements.txt에 나열되어 있다고 가정)
# 또는 직접 설치: RUN pip install --no-cache-dir mcpengine[cli,lambda]
RUN pip install --no-cache-dir -r requirements.txt
# 애플리케이션 코드의 나머지를 복사합니다
COPY app.py .
# 핸들러 함수 실행 명령 설정 (app.handler는 app.py의 핸들러를 의미합니다)
CMD ["app.handler"]
(필수로 requirements.txt
파일이 mcpengine[cli,lambda]
를 나열해야 하며, 그렇지 않으면 RUN
명령을 수정하십시오).
2. Docker 이미지를 빌드하고 ECR에 푸시:
먼저 ECR 리포지토리를 생성하세요(<region>
및 <repo-name>
를 교체하세요):
aws ecr create-repository --repository-name <repo-name> --region <region>
출력에서 AWS 계정 ID와 리포지토리 URI를 메모하세요 (<account-id>.dkr.ecr.<region>.amazonaws.com/<repo-name>
).
이제 이미지를 빌드, 태그 및 푸시합니다:
# ECR로 Docker 인증
aws ecr get-login-password --region <region> | docker login --username AWS --password-stdin <account-id>.dkr.ecr.<region>.amazonaws.com
# 이미지를 빌드합니다 (필요하다면 --platform로 교차 아키텍처 빌드 사용)
docker build --platform=linux/amd64 -t <repo-name>:latest .
# 이미지를 ECR으로 태그
docker tag <repo-name>:latest <account-id>.dkr.ecr.<region>.amazonaws.com/<repo-name>:latest
# 이미지를 ECR에 푸시
docker push <account-id>.dkr.ecr.<region>.amazonaws.com/<repo-name>:latest
3. Lambda 함수 생성 및 구성:
먼저 Lambda를 위한 IAM 실행 역할이 필요합니다. 없다면, 기본 역할을 생성합니다:
# (간소화 - 필요에 따라 신뢰 정책 및 권한을 조정하세요)
aws iam create-role --role-name lambda-mcp-role --assume-role-policy-document '{"Version": "2012-10-17","Statement": [{"Effect": "Allow","Principal": {"Service": "lambda.amazonaws.com"},"Action": "sts:AssumeRole"}]}'
aws iam attach-role-policy --role-name lambda-mcp-role --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
이제 ECR 이미지를 사용하여 Lambda 함수를 생성합니다(자리표시자를 교체하세요):
aws lambda create-function \
--function-name mcp-greeter-function \
--package-type Image \
--code ImageUri=<account-id>.dkr.ecr.<region>.amazonaws.com/<repo-name>:latest \
--role arn:aws:iam::<account-id>:role/lambda-mcp-role \
--timeout 30 \
--memory-size 512 \
--region <region>
4. 함수 URL로 노출:
API Gateway 없이 HTTP를 통해 Lambda를 호출 가능하게 하려면 함수 URL을 생성합니다:
aws lambda create-function-url-config \
--function-name mcp-greeter-function \
--auth-type NONE \
--region <region>
# 공개 액세스를 허용하기 위한 권한 추가 (인증이 필요한 경우 조정하세요)
aws lambda add-permission \
--function-name mcp-greeter-function \
--statement-id FunctionURLAllowPublicAccess \
--action lambda:InvokeFunctionUrl \
--principal '*' \
--function-url-auth-type NONE \
--region <region>
create-function-url-config
명령에서 반환된 함수 URL을 노트하세요. 이제 당신의 무상태 MCP 서버가 활성화되었습니다!
상태 관리: lifespan
컨텍스트 사용
Lambda는 무상태이지만, 많은 도구는 시작 시 초기화되는 데이터베이스, 연결 풀 또는 다른 리소스에 접근해야 합니다. MCPEngine은 lifespan
인자로 비동기 컨텍스트 관리자를 수용하여 이를 해결합니다.
lifespan
함수는 Lambda 컨테이너가 시작될 때(즉, yield
이전에) 설정 코드를 실행하고, 컨테이너가 종료될 때(즉, yield
이후에) 종료 코드를 실행합니다. yielded값은 도구 함수 내에서 ctx
(Context) 객체를 통해 사용할 수 있습니다.
이제 RDS Postgres 데이터베이스에 이벤트를 저장하는 간단한 이벤트 로거를 구축해 보겠습니다.
1. app.py
수정:
# app.py (상태 저장 예제)
import os
import psycopg2
from contextlib import asynccontextmanager
from mcpengine import MCPEngine, Context
# DB 연결 세부 정보가 환경 변수에 있다고 가정
DB_HOST = os.environ.get("DB_HOST")
DB_USER = os.environ.get("DB_USER")
DB_PASS = os.environ.get("DB_PASS")
DB_NAME = os.environ.get("DB_NAME")
@asynccontextmanager
async def db_connection_manager():
"""데이터베이스 연결 풀을 관리합니다."""
conn = None
try:
print("DB 연결을 설정 중입니다...")
conn = psycopg2.connect(
host=DB_HOST,
user=DB_USER,
password=DB_PASS,
dbname=DB_NAME
)
# 존재하지 않을 경우 테이블 생성 (간단한 예제)
with conn.cursor() as cur:
cur.execute("""
CREATE TABLE IF NOT EXISTS events (
id SERIAL PRIMARY KEY,
event_name TEXT NOT NULL,
timestamp TIMESTAMP DEFAULT now()
);
""")
conn.commit()
print("DB 연결 준비 완료.")
yield {"db_conn": conn} # ctx.db_conn을 통해 연결 사용 가능
finally:
if conn:
print("DB 연결을 닫는 중입니다.")
conn.close()
# lifespan 관리자를 사용하여 엔진 초기화
engine = MCPEngine(lifespan=db_connection_manager)
@engine.tool()
def log_event(event_name: str, ctx: Context) -> str:
"""주어진 이름의 이벤트를 데이터베이스에 기록합니다."""
try:
with ctx.db_conn.cursor() as cur:
cur.execute("INSERT INTO events (event_name) VALUES (%s)", (event_name,))
ctx.db_conn.commit()
return f"이벤트 '{event_name}'가 성공적으로 기록되었습니다."
except Exception as e:
# 기본 오류 처리
ctx.db_conn.rollback()
return f"이벤트 기록 오류: {e}"
@engine.tool()
def get_latest_events(limit: int = 5, ctx: Context) -> list[str]:
"""데이터베이스에서 가장 최근에 기록된 이벤트를 가져옵니다."""
try:
with ctx.db_conn.cursor() as cur:
cur.execute("SELECT event_name, timestamp FROM events ORDER BY timestamp DESC LIMIT %s", (limit,))
events = [f"[{row[1].strftime('%Y-%m-%d %H:%M:%S')}] {row[0]}" for row in cur.fetchall()]
return events
except Exception as e:
return [f"이벤트 가져오기 오류: {e}"]
# Lambda 핸들러 가져오기
handler = engine.get_lambda_handler()
2. 배포 고려사항:
- 데이터베이스: 접근 가능한 RDS 인스턴스(또는 다른 데이터베이스)가 필요합니다.
- 네트워킹: Lambda 함수의 VPC 설정을 구성하여 RDS 인스턴스에 접근할 수 있도록 해야 합니다(보안 그룹, 서브넷).
- 환경 변수: Lambda 함수에
DB_HOST
,DB_USER
,DB_PASS
,DB_NAME
을 환경 변수를 통해 전달합니다. - IAM: Lambda 실행 역할은 다른 AWS 서비스에 접근할 경우 추가 권한이 필요할 수 있습니다(예: DB 자격증명에 대한 Secrets Manager).
필요에 따라 Dockerfile
을 업데이트하고(psycopg2-binary
를 설치하기 위하여), 이미지를 다시 빌드/푸시한 다음 Lambda 함수의 코드와 구성을(환경 변수, VPC 설정) 업데이트합니다.
인증을 통한 도구 보호
생산 도구에는 인증이 필요합니다. MCPEngine은 Google, AWS Cognito, Auth0 등의 OpenID Connect(OIDC) 제공자와 통합됩니다.
1. OIDC 제공자 구성:
선택한 제공자(예: Google Cloud Console)와 함께 OAuth 클라이언트 ID를 설정합니다. 클라이언트 ID와 필요에 따라 클라이언트 비밀번호가 필요합니다(흐름에 따라 달라질 수 있음).
2. 인증을 위한 app.py
업데이트:
# app.py (인증된 예 - 코드 조각)
import os
# ... 다른 import ...
from mcpengine import MCPEngine, Context, GoogleIdpConfig # 또는 다른 IdpConfig
# ... db_connection_manager ...
# IDP 구성 - Google을 예로 사용
# GOOGLE_CLIENT_ID 환경 변수가 설정되어 있다고 가정
google_config = GoogleIdpConfig(
client_id=os.environ.get("GOOGLE_CLIENT_ID")
# 발급자는 종종 유추되거나 명시적으로 설정될 수 있습니다
)
# lifespan 및 IDP 구성으로 엔진 초기화
engine = MCPEngine(
lifespan=db_connection_manager,
idp_config=google_config
)
# log_event 도구 보호
@engine.auth() # 이 데코레이터 추가
@engine.tool()
def log_event(event_name: str, ctx: Context) -> str:
"""주어진 이름의 이벤트를 데이터베이스에 기록합니다. 인증이 필요합니다."""
# 필요한 경우 인증된 사용자 정보를 접근합니다: user_email = ctx.user.email
user_email = ctx.user.email if ctx.user else "알 수 없음"
print(f"인증된 사용자: {user_email}")
try:
# ... (데이터베이스 로직은 동일) ...
return f"이벤트 '{event_name}'가 {user_email}에 의해 성공적으로 기록되었습니다."
except Exception as e:
# ... 오류 처리 ...
return f"{user_email}에 대한 이벤트 기록 오류: {e}"
# get_latest_events는 인증 없이 사용할 수 있거나 보호할 수도 있습니다
@engine.tool()
def get_latest_events(limit: int = 5, ctx: Context) -> list[str]:
# ... (로직은 동일) ...
# Lambda 핸들러 가져오기
handler = engine.get_lambda_handler()
주요 변경 사항:
GoogleIdpConfig
(또는 제공자에 적합한 것을 가져옴)를 가져왔습니다.idp_config
인수로MCPEngine
을 인스턴스화했습니다.- 인증이 필요한 함수 앞에
@engine.auth()
데코레이터를 추가했습니다. MCPEngine은 유효한 JWT 토큰이 IDP의 공개 키에 대해 검증되지 않은 요청을 자동으로 거부합니다. - JWT 클레임에서 인증된 사용자 정보는
ctx.user
를 통해 사용할 수 있습니다.
3. 배포:
- Lambda 함수로 인증을 위해 필요한 환경 변수(예:
GOOGLE_CLIENT_ID
)를 전달합니다. - 이미지를 다시 빌드/푸시하고 Lambda 함수를 업데이트합니다.
LLM 클라이언트 연결
함수 URL을 통해 MCP 서버가 Lambda에 배포된 후, 호환 가능한 클라이언트를 연결할 수 있습니다. mcpengine proxy
를 사용하는 것은 Claude와 같은 클라이언트에 연결하기 위한 편리한 방법입니다:
mcpengine proxy <선택한 서비스 이름> <람다 함수 URL> --mode http --claude
인증을 사용할 경우:
mcpengine proxy <선택한 서비스 이름> <람다 함수 URL> \
--mode http \
--claude \
--client-id <당신의 구글 클라이언트 ID> \
--client-secret <당신의 구글 클라이언트 비밀번호> # 토큰 획득 흐름에 필요
이 명령은 Claude가 연결되는 로컬 프록시를 실행합니다. 프록시는 요청을 HTTP를 통해 Lambda 함수 URL로 전달하며, 구성된 경우 인증 흐름을 처리합니다. LLM은 이제 서버리스 도구를 발견하고 호출할 수 있습니다.
결론
AWS Lambda에 MCP 서버를 배포하면 LLM 기능을 확장하기 위한 놀라운 확장성 및 운영 효율성을 해제할 수 있습니다. 전통적인 MCP 구현은 무상태 환경에서 종종 어려움을 겪지만, MCPEngine은 강력하고 오픈 소스 솔루션을 제공합니다. 스트리밍 가능한 HTTP를 지원하고, lifespan
을 통한 컨텍스트 관리를 제공하며, 인증을 위한 OIDC과 원활하게 통합됨으로써 MCPEngine은 서버리스 MCP를 가능하게 할 뿐만 아니라 생산 사용 사례를 위해 실용적으로 만듭니다. 간단한 무상태 도구를 구축하든 복잡하고 상태 있는 인증된 애플리케이션을 구축하든, MCPEngine과 AWS Lambda의 조합은 AI 기반 상호작용의 다음 세대를 위한 강력한 플랫폼을 제공합니다.
개발 팀이 최대 생산성로 협력할 수 있는 통합된 올인원 플랫폼이 필요하신가요?
Apidog은 모든 요구 사항을 충족하며, Postman을 훨씬 저렴한 가격에 대체합니다!