AWS lambda-mcp-serverを使ってみる。
まえがき
MCPサーバーのツール部分をAWS Lambda上に移して使用する方法が紹介されていた。
MCPサーバーは自作してデスクトップ上で実行するという方法がよく知られているがAWS上にツール部分を移動させることによりClaude DesktopがAWS Lambda上にアクセスしてそのLambda関数を使用した内容をClaude Desktop上で使用できるようになった。
何が良いか
基本的にAWS Lambdaがアクセスできる範囲はAWSのロールやポリシーで定められている。そのためAWS で実行権限等を設定できるようなMCPサーバーを作成することができ、今までローカル上の無法地帯状態で使用するしかなかったMCPサーバーが適切な権限下で使用できるのではないかという試みだと思う。
参考記事
公式のサイトからgit cloneでクローンする
まずこのコマンドをvscode上で動かします。
git clone https://github.com/awslabs/mcp.git
GitHubにある AWS Labs の MCPリポジトリを、ローカルマシンにコピーしてます。
すでに公式が作成しているものを利用するということですね。
このコマンドを打つとディレクトリに「mcp」というディレクトリができます。
その下のsrcをツリーコマンドで覗いてみた結果は以下のようになってます。(長いので畳んでます。)
treeコマンド実行結果
.
├── aws-diagram-mcp-server
│ ├── Dockerfile
│ ├── LICENSE
│ ├── NOTICE
│ ├── README.md
│ ├── awslabs
│ │ ├── __init__.py
│ │ └── aws_diagram_mcp_server
│ │ ├── __init__.py
│ │ ├── diagrams_tools.py
│ │ ├── models.py
│ │ ├── scanner.py
│ │ └── server.py
│ ├── docker-healthcheck.sh
│ ├── pyproject.toml
│ ├── run_tests.sh
│ ├── tests
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── resources
│ │ │ ├── __init__.py
│ │ │ └── example_diagrams
│ │ │ ├── __init__.py
│ │ │ ├── aws_example.py
│ │ │ ├── flow_example.py
│ │ │ └── sequence_example.py
│ │ ├── test_diagrams.py
│ │ ├── test_models.py
│ │ ├── test_scanner.py
│ │ └── test_server.py
│ └── uv.lock
├── aws-documentation-mcp-server
│ ├── CHANGELOG.md
│ ├── Dockerfile
│ ├── LICENSE
│ ├── NOTICE
│ ├── README.md
│ ├── awslabs
│ │ ├── __init__.py
│ │ └── aws_documentation_mcp_server
│ │ ├── __init__.py
│ │ ├── models.py
│ │ ├── server.py
│ │ └── util.py
│ ├── basic-usage.gif
│ ├── docker-healthcheck.sh
│ ├── pyproject.toml
│ ├── tests
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── resources
│ │ │ └── lambda_sns_raw.html
│ │ ├── test_models.py
│ │ ├── test_read_documentation_live.py
│ │ ├── test_recommend_live.py
│ │ ├── test_search_live.py
│ │ ├── test_server.py
│ │ └── test_util.py
│ └── uv.lock
├── bedrock-kb-retrieval-mcp-server
│ ├── CHANGELOG.md
│ ├── Dockerfile
│ ├── LICENSE
│ ├── NOTICE
│ ├── README.md
│ ├── awslabs
│ │ ├── __init__.py
│ │ └── bedrock_kb_retrieval_mcp_server
│ │ ├── __init__.py
│ │ ├── knowledgebases
│ │ │ ├── __init__.py
│ │ │ ├── clients.py
│ │ │ ├── discovery.py
│ │ │ └── runtime.py
│ │ ├── models.py
│ │ └── server.py
│ ├── docker-healthcheck.sh
│ ├── pyproject.toml
│ ├── run_tests.sh
│ ├── tests
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── test_clients.py
│ │ ├── test_discovery.py
│ │ ├── test_models.py
│ │ ├── test_runtime.py
│ │ └── test_server.py
│ └── uv.lock
├── cdk-mcp-server
│ ├── CHANGELOG.md
│ ├── Dockerfile
│ ├── LICENSE
│ ├── NOTICE
│ ├── README.md
│ ├── awslabs
│ │ ├── __init__.py
│ │ └── cdk_mcp_server
│ │ ├── __init__.py
│ │ ├── core
│ │ │ ├── __init__.py
│ │ │ ├── resources.py
│ │ │ ├── search_utils.py
│ │ │ ├── server.py
│ │ │ └── tools.py
│ │ ├── data
│ │ │ ├── __init__.py
│ │ │ ├── cdk_nag_parser.py
│ │ │ ├── construct_descriptions.py
│ │ │ ├── genai_cdk_loader.py
│ │ │ ├── lambda_layer_parser.py
│ │ │ ├── lambda_powertools_loader.py
│ │ │ ├── schema_generator.py
│ │ │ └── solutions_constructs_parser.py
│ │ ├── server.py
│ │ └── static
│ │ ├── CDK_GENERAL_GUIDANCE.md
│ │ ├── CDK_NAG_GUIDANCE.md
│ │ ├── __init__.py
│ │ └── lambda_powertools
│ │ ├── bedrock.md
│ │ ├── cdk.md
│ │ ├── dependencies.md
│ │ ├── index.md
│ │ ├── insights.md
│ │ ├── logging.md
│ │ ├── metrics.md
│ │ └── tracing.md
│ ├── docker-healthcheck.sh
│ ├── pyproject.toml
│ ├── tests
│ │ ├── __init__.py
│ │ ├── core
│ │ │ ├── test_resources.py
│ │ │ ├── test_resources_enhanced.py
│ │ │ ├── test_search_utils.py
│ │ │ ├── test_server.py
│ │ │ └── test_tools.py
│ │ └── data
│ │ ├── test_cdk_nag_parser.py
│ │ ├── test_genai_cdk_loader.py
│ │ ├── test_lambda_powertools_loader.py
│ │ ├── test_schema_generator.py
│ │ └── test_solutions_constructs_parser.py
│ └── uv.lock
├── core-mcp-server
│ ├── CHANGELOG.md
│ ├── Dockerfile
│ ├── LICENSE
│ ├── NOTICE
│ ├── README.md
│ ├── awslabs
│ │ ├── __init__.py
│ │ └── core_mcp_server
│ │ ├── __init__.py
│ │ ├── server.py
│ │ └── static
│ │ ├── PROMPT_UNDERSTANDING.md
│ │ └── __init__.py
│ ├── docker-healthcheck.sh
│ ├── pyproject.toml
│ ├── tests
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── test_init.py
│ │ ├── test_main.py
│ │ ├── test_response_types.py
│ │ ├── test_server.py
│ │ └── test_static.py
│ └── uv.lock
├── cost-analysis-mcp-server
│ ├── CHANGELOG.md
│ ├── Dockerfile
│ ├── LICENSE
│ ├── NOTICE
│ ├── README.md
│ ├── awslabs
│ │ ├── __init__.py
│ │ └── cost_analysis_mcp_server
│ │ ├── __init__.py
│ │ ├── cdk_analyzer.py
│ │ ├── helpers.py
│ │ ├── report_generator.py
│ │ ├── server.py
│ │ ├── static
│ │ │ ├── COST_REPORT_TEMPLATE.md
│ │ │ ├── __init__.py
│ │ │ └── patterns
│ │ │ ├── BEDROCK.md
│ │ │ └── __init__.py
│ │ └── terraform_analyzer.py
│ ├── docker-healthcheck.sh
│ ├── pyproject.toml
│ ├── tests
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── test_cdk_analyzer.py
│ │ ├── test_helpers.py
│ │ ├── test_report_generator.py
│ │ ├── test_server.py
│ │ └── test_terraform_analyzer.py
│ └── uv.lock
├── git-repo-research-mcp-server
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── NOTICE
│ ├── README.md
│ ├── awslabs
│ │ ├── __init__.py
│ │ └── git_repo_research_mcp_server
│ │ ├── __init__.py
│ │ ├── defaults.py
│ │ ├── embeddings.py
│ │ ├── github_search.py
│ │ ├── indexer.py
│ │ ├── models.py
│ │ ├── repository.py
│ │ ├── search.py
│ │ ├── server.py
│ │ └── utils.py
│ ├── pyproject.toml
│ ├── run_tests.sh
│ ├── tests
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── test_errors_repository.py
│ │ ├── test_github_search_edge_cases.py
│ │ ├── test_graphql_github_search.py
│ │ ├── test_local_repository.py
│ │ ├── test_repository_utils.py
│ │ ├── test_rest_github_search.py
│ │ ├── test_search.py
│ │ ├── test_server.py
│ │ └── test_url_repository.py
│ └── uv.lock
├── lambda-mcp-server
│ ├── CHANGELOG.md
│ ├── Dockerfile
│ ├── LICENSE
│ ├── NOTICE
│ ├── README.md
│ ├── awslabs
│ │ ├── __init__.py
│ │ └── lambda_mcp_server
│ │ ├── __init__.py
│ │ └── server.py
│ ├── docker-healthcheck.sh
│ ├── examples
│ │ ├── README.md
│ │ └── sample_functions
│ │ ├── customer-id-from-email
│ │ │ └── app.py
│ │ ├── customer-info-from-id
│ │ │ └── app.py
│ │ └── template.yml
│ ├── pyproject.toml
│ ├── run_tests.sh
│ ├── tests
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── test_format_lambda_response.py
│ │ ├── test_integration.py
│ │ ├── test_integration_coverage.py
│ │ ├── test_register_lambda_functions.py
│ │ ├── test_server.py
│ │ ├── test_server_coverage.py
│ │ └── test_server_coverage_additional.py
│ └── uv.lock
├── nova-canvas-mcp-server
│ ├── CHANGELOG.md
│ ├── Dockerfile
│ ├── LICENSE
│ ├── NOTICE
│ ├── README.md
│ ├── awslabs
│ │ ├── __init__.py
│ │ └── nova_canvas_mcp_server
│ │ ├── __init__.py
│ │ ├── consts.py
│ │ ├── models.py
│ │ ├── novacanvas.py
│ │ └── server.py
│ ├── docker-healthcheck.sh
│ ├── pyproject.toml
│ ├── run_tests.sh
│ ├── tests
│ │ ├── README.md
│ │ ├── __init__.py
│ │ ├── conftest.py
│ │ ├── test_models.py
│ │ ├── test_novacanvas.py
│ │ └── test_server.py
│ └── uv.lock
└── terraform-mcp-server
├── CHANGELOG.md
├── Dockerfile
├── README.md
├── awslabs
│ ├── __init__.py
│ └── terraform_mcp_server
│ ├── __init__.py
│ ├── impl
│ │ ├── resources
│ │ │ ├── __init__.py
│ │ │ ├── terraform_aws_provider_resources_listing.py
│ │ │ └── terraform_awscc_provider_resources_listing.py
│ │ └── tools
│ │ ├── __init__.py
│ │ ├── execute_terraform_command.py
│ │ ├── run_checkov_scan.py
│ │ ├── search_aws_provider_docs.py
│ │ ├── search_awscc_provider_docs.py
│ │ ├── search_specific_aws_ia_modules.py
│ │ ├── search_user_provided_module.py
│ │ └── utils.py
│ ├── models
│ │ ├── __init__.py
│ │ └── models.py
│ ├── scripts
│ │ ├── generate_aws_provider_resources.py
│ │ ├── generate_awscc_provider_resources.py
│ │ └── scrape_aws_terraform_best_practices.py
│ ├── server.py
│ └── static
│ ├── AWSCC_PROVIDER_RESOURCES.md
│ ├── AWS_PROVIDER_RESOURCES.md
│ ├── AWS_TERRAFORM_BEST_PRACTICES.md
│ ├── MCP_INSTRUCTIONS.md
│ ├── TERRAFORM_WORKFLOW_GUIDE.md
│ └── __init__.py
├── docker-healthcheck.sh
├── pyproject.toml
├── run_tests.sh
├── tests
│ ├── README.md
│ ├── __init__.py
│ ├── conftest.py
│ ├── test_command_impl.py
│ ├── test_execute_terraform_command.py
│ ├── test_models.py
│ ├── test_parameter_annotations.py
│ ├── test_resources.py
│ ├── test_run_checkov_scan.py
│ ├── test_search_user_provided_module.py
│ ├── test_server.py
│ ├── test_tool_implementations.py
│ ├── test_utils.py
│ └── test_utils_additional.py
└── uv.lock
64 directories, 289 files
bedlock用などいろいろなファイルがダウンロードされていることがわかりますね。
今回使用するのは「lambda-mcp-server」になりそうです。
以下の記事によれば「lambda-mcp-server内のexamplesディレクトリの中に、サンプルとなるLambda関数のコードが格納されているので、AWS SAMを使ってデプロイ」という流れになりそうなのでやってみましょう。
example1
lambda-mcp-serverにあるひとつめのexampleコードは以下のようになっていました。
customer-id-from-email
def lambda_handler(event: dict, context: dict) -> dict:
"""AWS Lambda function to retrieve customer ID based on customer email address.
Args:
event (dict): The Lambda event object containing the customer email
Expected format: {"email": "example@domain.com"}
context (dict): AWS Lambda context object
Returns:
dict: Customer ID if found, otherwise an error message
Success format: {"customerId": "123"}
Error format: {"error": "Customer not found"}
"""
try:
# Extract email from the event
email = event.get('email')
if not email:
return {'error': 'Missing email parameter'}
# This would normally query a database
# For demo purposes, we'll return mock data
# Simulate database lookup
if email == 'john.doe@example.com':
return {'customerId': '12345'}
else:
return {'customerId': '54321'}
except Exception as e:
return {'error': str(e)}
event: Lambda関数に渡される入力データ(メールアドレスを含む)
context: AWSランタイムに関する情報を含むオブジェクト
戻り値: 顧客IDまたはエラーメッセージを含む辞書
{"email": "example@domain.com"}
期待される入力は、emailキーを持つJSON形式のデータです。
email = event.get('email')
でemailというキーでバリューを呼び出しています。
if email == 'john.doe@example.com':
return {'customerId': '12345'}
else:
return {'customerId': '54321'}
:::
このLambda関数で、emailがjohn.doe@example.comならcustomerIdと12345のキーバリューを返しています。
example2
customer-info-from-id
def lambda_handler(event: dict, context: dict) -> dict:
"""AWS Lambda function to retrieve customer information based on customer ID.
Args:
event (dict): The Lambda event object containing the customer ID
Expected format: {"customerId": "123"}
context (dict): AWS Lambda context object
Returns:
dict: Customer information if found, otherwise an error message
Success format: {"customerId": "123", "name": "John Doe", "email": "john@example.com", ...}
Error format: {"error": "Customer not found"}
"""
try:
# Extract customer ID from the event
customer_id = event.get('customerId')
if not customer_id:
return {'error': 'Missing customerId parameter'}
# This would normally query a database
# For demo purposes, we'll return mock data
# Simulate database lookup
match customer_id:
case '12345':
return {
'customerId': '12345',
'name': 'John Doe',
'email': 'john.doe@example.com',
'phone': '+1-555-123-4567',
'address': {
'street': '123 Main St',
'city': 'Anytown',
'state': 'CA',
'zipCode': '12345',
},
'accountCreated': '2022-01-15',
}
case '54321':
return {
'customerId': '54321',
'name': 'Jane Smith',
'email': 'jane.smith@example.com',
'phone': '+1-555-987-6543',
'address': {
'street': '456 Oak Ave',
'city': 'Othertown',
'state': 'NY',
'zipCode': '67890',
},
'accountCreated': '2022-02-20',
}
case _:
return {'error': 'Customer not found'}
except Exception as e:
return {'error': str(e)}
今度はさっきの関数とは逆に
{"customerId": "123"}
customerIdが
'12345'なら以下のような構造を返すというようになっています。
{
"customerId": "12345",
"name": "John Doe",
"email": "john.doe@example.com",
"phone": "+1-555-123-4567",
"address": {
"street": "123 Main St",
"city": "Anytown",
"state": "CA",
"zipCode": "12345"
},
"accountCreated": "2022-01-15"
}
関数の中身はわかったので次は実際にawsにデプロイしてみます。
Lambda関数のデプロイ
デプロイは以下のコードをターミナルに打つと実行できます。
sam deploy awslabs-mcp-sample-functions --guided
AWS SAMというものを使用してこういった簡単なデプロイができるみたいですね。
ただこれだとどういった方法でデプロイするかわからないので、デプロイ条件が書かれた以下のファイルをみてみましょう。
sample functionの中にtemplate,yamlというファイルがあります。
template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Sample functions for MCP servers.
Resources:
CustomerInfoFromId:
Type: AWS::Serverless::Function
#checkov:skip=CKV_AWS_115:Because this is an example, there is no requirement to reserve concurrency
#checkov:skip=CKV_AWS_116:Because this is an example, there is no requirement for a DLQ
#checkov:skip=CKV_AWS_117:Because this is an example, there is no requirement to run within a VPC
Properties:
CodeUri: ./customer-info-from-id
Description: Customer status from { 'customerId' }
MemorySize: 128
Timeout: 3
Handler: app.lambda_handler
Runtime: python3.13
Architectures:
- arm64
Metadata:
cfn_nag:
rules_to_suppress:
- id: W89
reason: "Because this is an example, there is no requirement to run within a VPC"
- id: W92
reason: "Because this is an example, there is no requirement to reserve concurrency"
CustomerIdFromEmail:
Type: AWS::Serverless::Function
#checkov:skip=CKV_AWS_115:Because this is an example, there is no requirement to reserve concurrency
#checkov:skip=CKV_AWS_116:Because this is an example, there is no requirement for a DLQ
#checkov:skip=CKV_AWS_117:Because this is an example, there is no requirement to run within a VPC
Properties:
CodeUri: ./customer-id-from-email
Description: Get customer ID from { 'email' }
MemorySize: 128
Timeout: 3
Handler: app.lambda_handler
Runtime: python3.13
Architectures:
- arm64
Metadata:
cfn_nag:
rules_to_suppress:
- id: W89
reason: "Because this is an example, there is no requirement to run within a VPC"
- id: W92
reason: "Because this is an example, there is no requirement to reserve concurrency"
Outputs:
CustomerInfoFromId:
Description: "CustomerInfoFromId Function ARN"
Value: Fn::GetAtt CustomerInfoFromId.Arn
CustomerIdFromEmail:
Description: "CustomerIdFromEmail Function ARN"
Value: Fn::GetAtt CustomerIdFromEmail.Arn
samとcloudformationの使用が初めてなので丁寧にやってみます。
AWSTemplateFormatVersion: CloudFormationテンプレートのバージョンを指定
Transform: SAM仕様を使用宣言してCloudFormationがSAM構文を理解するようにしてます。
Description: このテンプレートの説明みたいなもの。
CodeUri: ./customer-info-from-id
Lambda関数のコードがあるローカルディレクトリを指定
Description: Customer status from { 'customerId' }
これは説明
MemorySize: 128
Timeout: 3
Lambda関数のメモリサイズとタイムアウトを指定している。
Handler: app.lambda_handler
Runtime: python3.13
app.pyファイルのlambda_handler関数をpython3.13で使用という意味
Architectures:
- arm64
アーキテクチャーはarm64を指定
メタデータ部分
W89: VPC内で実行しないので、VPC関連の警告を抑制
W92: Lambda関数の同時実行数を予約する必要がないんで、関連する警告を抑制
アウトプット部分
CloudFormation スタックの出力を定義:
Fn::GetAtt: 以下
CustomerInfoFromId: カスタマーIDから情報を取得する関数のARN
CustomerIdFromEmail: メールアドレスからカスタマーIDを取得する関数のARN
を指定できる。
じゃあ次に
brew install aws-sam-cli
でaws-sam-cliを導入してから
sam deploy awslabs-mcp-sample-functions --guided
してみましょう。
参考記事に倣って以下の設定はすべてエンターでデフォルト値設定しました。
Configuring SAM deploy
======================
Looking for config file [samconfig.toml] : Not found
Setting default arguments for 'sam deploy'
=========================================
Stack Name [awslabs-mcp-sample-functions]:
AWS Region [ap-northeast-1]:
#Shows you resources changes to be deployed and require a 'Y' to initiate deploy
Confirm changes before deploy [y/N]:
#SAM needs permission to be able to create roles to connect to the resources in your template
Allow SAM CLI IAM role creation [Y/n]:
#Preserves the state of previously provisioned resources when an operation fails
Disable rollback [y/N]:
Save arguments to configuration file [Y/n]:
ここで実行したのですが認証関係でエラーが出ました。
クレデンシャル設定を忘れていたのでここでAWSユーザーの設定をします。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"cloudformation:*",
"s3:*",
"lambda:*",
"iam:CreateRole",
"iam:DeleteRole",
"iam:PutRolePolicy",
"iam:DeleteRolePolicy",
"iam:AttachRolePolicy",
"iam:DetachRolePolicy",
"iam:PassRole",
"iam:TagRole",
"iam:UntagRole",
"iam:GetRole",
"iam:ListRoles",
"logs:*",
"apigateway:*",
"events:*",
"serverlessrepo:*",
"cloudformation:CreateChangeSet",
"cloudformation:DeleteChangeSet",
"cloudformation:DescribeChangeSet",
"cloudformation:ExecuteChangeSet",
"cloudformation:ListChangeSets",
"cloudformation:ListStacks",
"cloudformation:CreateStack",
"cloudformation:UpdateStack",
"cloudformation:DeleteStack",
"cloudformation:DescribeStacks"
],
"Resource": "*"
}
]
}
テスト環境なのでおそらく最小権限ではなくまたアスタリスクを多用していますが、本来は適切なポリシー設定が必要です。
そして
aws configure
でアクセスキーなどの必要な設定を行います。
リージョンは
ap-northeast-1
アウトプットフォーマットはjson
sam deploy awslabs-mcp-sample-functions --guided
を行います。
すると今度は以下のエラーがでました。
ROLLBACK_COMPLETE state and can not be updated.
どうも一度作成したcloudformationのスタックを削除しないといけないようです。
aws cloudformation delete-stack --stack-name sam-app
で一回けします。
で再度
sam deploy awslabs-mcp-sample-functions --guided
...デプロイできました!
結構ポリシー周りで何度かやり直しましたがなんとかなりました。
cloude desktopからaws mcp lambda関数を使う。
cloude desktopから使用するために
claude_desktop_config.jsonに以下の設定を追加します。
"awslabs.lambda-mcp-server": {
"command": "uvx",
"args": ["awslabs.lambda-mcp-server@latest"],
"env": {
"AWS_PROFILE": "<profileの名前>",
"AWS_REGION": "ap-northeast-1",
"FUNCTION_PREFIX": "sam-app-Customer",
"LAMBDA_FUNCTIONS": [
"CustomerInfoFromId",
"CustomerIdFromEmail"
]
}
}
commandの"uvx"には実際には
which uvxで出てきたパスを指定する必要があります。
claude desktopの起動・実行
Claude Desktopを使用して以下のように質問するとLambda関数で設定されている内容が返ってきました🙌
まとめ
今回はLambda関数を使用できるMCPサーバーを試してみました。
最近MCPサーバーはClaude DesktopではなくWebからも対応可能?とのことなのでこれと組み合わせればより使用例が広がるように思いました。次はそちらを試してみたいです。
Discussion