🥯

MCP connector で AWS Lambda 上の MCPサーバーを使ってみる (Streamable HTTP)

に公開

この記事では以下のことをやります。

  • 既存のstdioのMCPサーバーをStreamable HTTP化する
  • Streamable HTTP化したMCPサーバーをAWS Lambda上で動かす
  • MCP connectorでLambda上のMCPサーバーを使用する

MCP connector について

先日のAnthropicのCode with ClaudeでMCP connectorという機能が発表されました。
https://docs.anthropic.com/en/docs/agents-and-tools/mcp-connector
今まではMCPサーバーを使用するにはClaude DesktopなどのMCPクライアントが必要でしたが、今回発表されたMCP connectorでは、Anthropic APIが直接MCPサーバを使用してくれます。

ただし、使用できるのはSSEやStreamable HTTPで接続するMCPサーバーのみで、ローカルPCでの動作が前提のstdioのMCPサーバーには接続できません。(当たり前といえば当たり前ですが)

現時点で世の中の多くのMCPサーバーがstdioなので、実際に使う機会はまだ多くなさそうに思えますが、今後はサーバーサイドで動くMCPサーバーも増えてくるでしょう。
現時点で使用したい場合は、AnthropicのドキュメントにはリモートMCPサーバーがリストアップされているので、それらを試すことが出来ます。
https://docs.anthropic.com/en/docs/agents-and-tools/remote-mcp-servers

で、ちょっと思ったのが「リストアップされてるのがSSEばかりなんだけどStreamable HTTPでも本当に使えるの?」ということです。

ということで、実際にAWS Lambda上でStreamable HTTPのMCPサーバーを立ち上げてMCP connectorで繋がるか試してみました。
なぜLambdaで動かすかというと、仕組み上LambdaではSSEのMCPサーバーは動かすことができないので、Streamable HTTPを試すのには適しているかなぁと思ったからです。

どうせなら既存の(stdioの)MCPサーバーをLambdaで動かしたい。

前述の通り、現時点で世の中の多くのMCPサーバーがstdioです。これらがそのままStreamable HTTPになれば、世の中の多くのMCPサーバーがLambdaで動かせるということになります。MCPサーバーをイチから作るのは面倒くさいので、今回はそうしたいと思います。

そのために、こちらのmcp-proxyを使います。
https://github.com/sparfenyuk/mcp-proxy
mcp-proxyは、stdioからSSE/StreamableHTTPに変換してくれるツールです。(今回は特に関係ないですがSSEからstdioへの変換もできます。)

ということで、mcp-proxyを使って、こちらのtimeのMCPサーバーをLambda上で動かしてみます。
https://github.com/modelcontextprotocol/servers/tree/main/src/time

MCPサーバーをLambdaで動かした。

動きました。コンテナ化してCDKでAWS上にデプロイしました。サンプルはGitHubeにおきました。
https://github.com/k-ibaraki/streamable-http-mcp-server-lambda-sample

中身は記事の後半で軽く触れますが、先に実際に使ってみた結果から記載します。

MCP connectorを使ってみた。

まずは、MCPサーバーなしでAPI呼び出しを試してみます。

🙎: "こんにちは。今の日本の時間は?"

🤖: "こんにちは!申し訳ありませんが、私はリアルタイムの時刻情報にアクセスできません。現在の日本時間を知りたい場合は、お使いのデバイスの時計やインターネットで「日本時間」を検索していただけますでしょうか。\n\n何か他にお手伝いできることがありましたら、お気軽にお聞かせください。"

curlの全体(展開すると実際のAPI呼び出しと応答の全文が読めます)
$ curl  -X POST \
  'https://api.anthropic.com/v1/messages' \
  --header 'Accept: */*' \
  --header 'Content-Type: application/json' \
  --header 'anthropic-version: 2023-06-01' \
  --header 'anthropic-beta: mcp-client-2025-04-04' \
  --header 'X-API-Key: (ここにキーを入れる)' \
  --data-raw '{
  "model": "claude-sonnet-4-20250514",
  "max_tokens": 1024,
  "messages": [
    {
      "role": "user",
      "content": "こんにちは。今の日本の時間は?"
    }
  ]
}'
{"id":"msg_xxxx","type":"message","role":"assistant","model":"claude-sonnet-4-20250514","content":[{"type":"text","text":"こんにちは!申し訳ありませんが、私はリアルタイムの時刻情報にアクセスできません。現在の日本時間を知りたい場合は、お使いのデバイスの時計やインターネットで「日本時間」を検索していただけますでしょうか。\n\n何か他にお手伝いできることがありましたら、お気軽にお聞かせください。"}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":21,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":123,"service_tier":"standard"}}%  

こんな感じで素のモデルは時間を知りません。
ですが、MCP connectorを使ってLambda上のMCPサーバーのURLを追加して呼び出すと次のようになります。

🙎: "こんにちは。今の日本の時間は?"

🤖: "こんにちは!日本の現在時刻をお調べします。"
🤖: "現在の日本時間は 19:56 です(2025年5月25日)。"

curlの全体(展開すると実際のAPI呼び出しと応答の全文が読めます)
$ curl  -X POST \
  'https://api.anthropic.com/v1/messages' \
  --header 'Accept: */*' \
  --header 'Content-Type: application/json' \
  --header 'anthropic-version: 2023-06-01' \
  --header 'anthropic-beta: mcp-client-2025-04-04' \
  --header 'X-API-Key: (ここにキーを入れる)' \
  --data-raw '{
  "model": "claude-sonnet-4-20250514",
  "max_tokens": 1024,
  "messages": [
    {
      "role": "user",
      "content": "こんにちは。今の日本の時間は?"
    }
  ],
  "mcp_servers": [
    {
      "type": "url",
      "url": "https://xxxx.lambda-url.ap-northeast-1.on.aws/mcp/",
      "name": "time"
    }
  ]
}'
{"id":"msg_xxxx","type":"message","role":"assistant","model":"claude-sonnet-4-20250514","content":[{"type":"text","text":"こんにちは!日本の現在時刻をお調べします。"},{"type":"mcp_tool_use","id":"mcptoolu_zzzz","name":"get_current_time","input":{"timezone":"Asia/Tokyo"},"server_name":"time"},{"type":"mcp_tool_result","tool_use_id":"mcptoolu_zzzz","is_error":false,"content":[{"type":"text","text":"{\n  \"timezone\": \"Asia/Tokyo\",\n  \"datetime\": \"2025-05-25T19:56:18+09:00\",\n  \"is_dst\": false\n}"}]},{"type":"text","text":"現在の日本時間は **19:56** です(2025年5月25日)。"}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":1434,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":107,"service_tier":"standard","server_tool_use":{"web_search_requests":0}}}%

ちゃんと時間を教えてくれました。すごい!
Streamable HTTPでもちゃんと使えた! Lambda上でもMCPサーバー動いた!

SSEとStreamable HTTPを区別するパラメータがないので不安だったのですが無事に動きました。どうやって判断しているのでしょうか?

作ったLambdaを簡単に解説します。

解説します。と言っても、このDockerfileだけなので説明することは少ないです。

https://github.com/k-ibaraki/streamable-http-mcp-server-lambda-sample/blob/main/container/Dockerfile

やっていることは以下になります。
(後半の記述のほうが重要なので、最後のコマンドから順に説明します。)

mcp-proxyでStreamable HTTPに変換

https://github.com/k-ibaraki/streamable-http-mcp-server-lambda-sample/blob/main/container/Dockerfile#L21-L22
mcp-proxyで、mcp-server-timeをStreamable HTTPに変換して起動しています。
--stateless オプションを付けることでStreamable HTTPになります。(付けないとSSE)

Lambda Web AdapterでLambda化

https://github.com/k-ibaraki/streamable-http-mcp-server-lambda-sample/blob/main/container/Dockerfile#L18-L19
Lambda Web AdapterでコンテナをLambda化しています。このコマンドを追加するだけでWebアプリとして公開されるコンテナがLambda上で動くようになります。
Lambda Web Adapterの細かい説明は省きますが、超便利アイテムなので知っておくと捗ります。
https://aws.amazon.com/jp/builders-flash/202301/lambda-web-adapter/

uvの書き込みディレクトリの設定

https://github.com/k-ibaraki/streamable-http-mcp-server-lambda-sample/blob/main/container/Dockerfile#L6-L9
uvが書き込むディレクトリを/tmp配下にしています。Lambdaの制約で他のディレクトリに書き込もうとするとエラーになります。それなりにハマりました。無理にuv使わないほうが良いかもしれないと思いつつ設定で回避しました。

CDKでデプロイ

コンテナをLambdaにデプロイするのは手動だと結構面倒くさいので、CDKでサクッとデプロイしています。本題ではないので説明は省略します。
https://github.com/k-ibaraki/streamable-http-mcp-server-lambda-sample/blob/main/mcp-server-lambda-sample.ts

最後に

今回はシンプルなTimeのMCPサーバーで試しましたが、同じ方法で他の多くのMCPサーバーをStreamable HTTP化してLambda上で動かせると思います。
そしてMCP connectorを使えば簡単に呼べるようになりました。夢が広がりますね!

最後に注意点です。今回はセキュリティを考慮せずにLambdaの関数URLで認証なしの公開をしちゃっています。現在時間を取得するだけですしね。しかし、個人情報や業務情報や機密情報を扱うMCPサーバーでは、同じことをしたらかなりまずいことになるので注意しましょう。

NCDCエンジニアブログ

Discussion