14
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

API GatewayとLambdaでリモートMCPサーバーができた!

Posted at

はい、できました🎉🎉🎉

API GatewayとLambdaでMCPサーバーを構築できました🎉🎉🎉

概要図はこんな感じになります。

image.png

MCPのTransports仕様について

MCPの2025-03-26版仕様ではTransportsとしてstdioStreamable HTTPがあります。

  • stdio: 標準入出力を使用し、MCPクライアントがMCPサーバーをサブプロセスとして起動。標準入出力を使用するため、MCPクライアントとMCPサーバーは同一の環境(パソコン)
  • Streamable HTTP: HTTP POSTとGETで構成された通信仕様。HTTPでの通信のためMCPクライアントとMCPサーバーが別の環境(パソコン)として実現か可能に

一つ前の2024-11-05版仕様ではHTTP+SSEという仕様がありましたが、Streamable HTTPに置き換わりました。(後方互換としてオプションとしては残っています)

今回はStreamable HTTPを使用します。

ステートフル or ステートレス

MCPはセッション管理を行うステートフルなプロトコルとして設計されています。(と、私は理解してます)

例えば、ブラウザ操作をするPlaywrightのMCPサーバーの場合、

  1. Webサイトを開いて
  2. リンクをクリックして画面遷移して
  3. ページをスクロールして
  4. スクリーンショットを取得する

のように、複数ステップにわたる操作を実施させたい場合があります。しかも、4つのステップを一度に依頼するのではなく、一つのステップごとにユーザーの応答をうけ、チャット形式でやり取りすることも考えられます。

そのため、MCPではセッション管理をしてステートフルな仕様となっていますが、これが AWS Lambdaと相性が良くありません

MCP仕様のシーケンス図に表現されているのですが、クライアントとサーバーとのコネクションを維持したまま複数のやり取りを行う仕様となっています。

下図の上の赤矢印のところでPOSTリクエストを受けたままコネクションは維持しつつ、下の赤矢印のGETリクエストでツール実行とかのリクエストが来ます。(たぶん)

image.png

LambdaでMCPサーバーを実現するとどうなるかというと、POSTを受けるLambdaのインスタンスとGETを受けるLambdaのインスタンスが「 必ず 」別のインスタンスになり、セッション情報を共有できません。

なのですが、セッション管理は必須ではないみたいでして、MCPのTypeScript SDKにはセッション管理をしない「ステートレス」での利用方法が存在します。

要はセッションIDをリクエストごとに新しくするような動作をしてそうですが、これであればLambdaでも実現できることがわかりました。

API Gateway + LambdaとMCP公式SDKは相性が悪い?

MCPの公式SDKを使い、Streamable HTTP方式のMCPサーバーを作成する場合、SDKがいい感じにWebサーバー機能を提供しており、API Gateway + Lambda環境では使いづらいです。(使えるかもしれませんが)

ということで、API Gateway + LambdaでMCPサーバーを構築するライブラリーを作りました

前置きが長くなりましたが、API Gateway + Lambdaの環境でMCPサーバーを構築するライブラリーを作成し、公開しました🎉🎉🎉

MCPサーバーをどうコーディングするかの具体例の解説ではなく、ライブラリーの形になっているのには理由がありまして。。

白状すると、Amazon Q Developer CLIが全部作ってくれたので、細かな実装は私も把握してません🙊

ライブラリーの内容が知りたい方はDeepWikiを御覧ください。
https://deepwiki.com/moritalous/lambda-mcp-adaptor

ライブラリーの使い方を解説

ライブラリーの使い方を手順を追って解説します。

  1. SAMプロジェクトを作成
    Node.js22とTypeScriptの環境をベースにします

    sam init --name sam-app \
      --runtime nodejs22.x \
      --dependency-manager npm \
      --app-template hello-world-typescript
    

    不格好ですが、hello-worldのまま進めます。

  2. ライブラリーをインストール

    cd sam-app/hello-world/
    npm add github:moritalous/lambda-mcp-adaptor
    
  3. Lambdaハンドラーを実装
    手順としては、

    1. createMCPServerでMCPサーバーを生成
    2. MCPツールを定義。(ツール以外にプロンプト、リソースも対応しており、複数指定可能)
    3. createLambdaHandlerでLambdaハンドラーを生成

    以上です。

    以下のコードが、1つのツールを持ったMCPサーバーLambdaのハンドラーです。全量がこれです。

    結構隠蔽しまくりましたので、ほぼMCPツールを定義するだけです

    sam-app/hello-world/app.ts
    import { createMCPServer, createLambdaHandler } from 'lambda-mcp-adaptor';
    import { z } from 'zod';
    
    // MCPサーバーを定義
    const server = createMCPServer({
        name: 'My MCP Server',
        version: '1.0.0',
        description: 'A powerful MCP server with authentication',
    });
    
    // MCPのツールを定義
    server.tool(
        'calculate',
        {
            operation: z.enum(['add', 'subtract', 'multiply', 'divide']),
            a: z.number(),
            b: z.number(),
        },
        async ({ operation, a, b }) => {
            let result;
            switch (operation) {
                case 'add':
                    result = a + b;
                    break;
                case 'subtract':
                    result = a - b;
                    break;
                case 'multiply':
                    result = a * b;
                    break;
                case 'divide':
                    result = a / b;
                    break;
            }
    
            return {
                content: [{ type: 'text', text: `${a} ${operation} ${b} = ${result}` }],
            };
        },
    );
    
    // Lambdaハンドラーを作成
    export const lambdaHandler = createLambdaHandler(server);
    
  4. template.yamlを修正

    /mcpのエンドポイントでPOSTを受けるように設定します

    template.yaml(HelloWorldFunctionのEvents部分のみ抜粋)
      Events:
        MCPPost:
          Type: Api
          Properties:
            Path: /mcp
            Method: post
    

    OutputsはProd/mcpに変更しましょう

    HelloWorldApi:
      Description: "API Gateway endpoint URL for Prod stage for Hello World function"
      Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/mcp"
    
  5. SAMビルドとデプロイ

    sam build
    sam deploy
    

これで完成です。

VSCodeから接続確認

VSCodeをMCPホストとして、接続確認を行います。

  1. [表示] -> [コマンドパレット] を選択
  2. 「MCP: サーバーの追加」を選択
  3. 「HTTP (HTTP またはサーバー送信イベント)」を選択
  4. デプロイしたAPI Gatewayのエンドポイント(https://9999999999.execute-api.{リージョン}.amazonaws.com/Prod/mcp)を入力
  5. 任意の名前(calculate など)を入力
  6. 設定を保存する場所を選択

これで設定完了です。

GitHub Copilot Chatをエージェントモードにし、「calculateツールを使って、 2足す3を実行して」と質問します。

image.png

うまく動作していますね!

認証機能もあるよ

認証はAmazon Cognitoでの実現を検討したのですが、MCPの仕様で必須となっているOAuth 2.0 Dynamic Client Registration Protocolに未対応のため、採用できませんでした。

代わりに固定のAPIキーを使った認証はできるようにしています。

認証機能を使ったサンプルはこちらに格納していますので参考にしてください。


ぜひ使ってみてください!

気に入っていただけた方はGitHubのスター、Qittaのいいね、いただけると私がニヤニヤします。

14
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?