ひとつのLambdaの中でMCPサーバーとクライアントをStdio通信させる

Strands AgentsはMCPに対応してます。

https://strandsagents.com/0.1.x/user-guide/concepts/tools/mcp-tools/

以下のような最低限のコードでMCPツールを使用できます。

```python
from mcp import stdio_client, StdioServerParameters
from strands import Agent
from strands.tools.mcp import MCPClient

# Connect to an MCP server using stdio transport
# Note: uvx command syntax differs by platform

# For macOS/Linux:
stdio_mcp_client = MCPClient(lambda: stdio_client(
    StdioServerParameters(
        command="uvx", 
        args=["awslabs.aws-documentation-mcp-server@latest"]
    )
))

# Create an agent with MCP tools
with stdio_mcp_client:
    # Get the tools from the MCP server
    tools = stdio_mcp_client.list_tools_sync()

    # Create an agent with these tools
    agent = Agent(tools=tools)
```

:::note
いいですね。病みつきですね。
:::

## Lambda化する

AWS好きなみなさんが次に考えることは、そう、「**これ、Lambdaで動かしたい**」ですよね。

で、SAMを使ってこのままLambdaにしてみます。


```python:app.py
import json

from mcp import StdioServerParameters, stdio_client
from strands import Agent
from strands.tools.mcp import MCPClient


def lambda_handler(event, context):
    prompt = event["prompt"]

    stdio_mcp_client = MCPClient(
        lambda: stdio_client(
            StdioServerParameters(
                command="uvx", 
                args=["awslabs.aws-documentation-mcp-server@latest"]
            )
        )
    )

    with stdio_mcp_client:
        # Get the tools from the MCP server
        tools = stdio_mcp_client.list_tools_sync()

        # Create an agent with these tools
        agent = Agent(tools=tools)

        result = agent(prompt=prompt)

    return {
        "statusCode": 200,
        "body": json.dumps(
            {"message": result.message["content"][0]["text"]}, ensure_ascii=False
        ),
    }
```

```text:requirements.txt
strands-agents
awslabs.aws-documentation-mcp-server
```

`event["prompt"]`を入力にしますので、イベントJSONに以下の値を入れてテスト実行してみます。

```json
{
  "prompt": "S3のバケットポリシーについてドキュメントを検索して"
}
```

すると、残念ながらエラーになります。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/604fe5d4-91ca-4b2f-8cc9-1c39834e079c.png)

せっかくなので「Diagnose with Amazon Q」ボタンを押してみましょう。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/82b50799-4a9c-4b00-a893-16505dff27bf.png)

> Check if the 'uvx' executable is present in the Lambda function's execution environment

流石Q Developerです。`uvx`コマンドが無いからエラーになったんですね。

## じゃあ、どうすんの？

uvxコマンドをインストールするのもいいですが、そもそもこの「uvx」って何者なんでしょうか？？

ドキュメントはここにありました。

https://docs.astral.sh/uv/guides/tools/

> Many Python packages provide applications that can be used as tools. uv has specialized support for easily invoking and installing tools.

日本語に訳すと、

> 多くの Python パッケージは、ツールとして使用できるアプリケーションを提供します。uv は、ツールを簡単に呼び出してインストールするための特別なサポートを備えています。

だとのこと。で、ツールってなんなの？？

これについてはPythonのドキュメントに記載がありました。

https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#creating-executable-scripts

上で例に出した`AWS Documentation MCP Server`の場合、`pyproject.toml`には以下のように記述があります。

```toml:pyproject.toml
[project.scripts]
"awslabs.aws-documentation-mcp-server" = "awslabs.aws_documentation_mcp_server.server:main"
```

要するに、`uvx awslabs.aws-documentation-mcp-server`を実行すると

**「指定したライブラリーをいい感じにダウンロードして、`project.scripts`に記述のあるスクリプト`awslabs.aws_documentation_mcp_server.server:main`を実行するよ**

ということだと思われます。
ちなみに、`awslabs.aws_documentation_mcp_server`がパッケージで`server`がモジュール、`main`は関数を指しています。

ここまでわかったら、`uvx`コマンドを使用しなくても、`python`コマンドで実行できます。

```shell
uvx awslabs.aws-documentation-mcp-server
```
は
```shell
python -m awslabs.aws_documentation_mcp_server.server
```

と同義です。

最後の`:main`の指定がないですが、`-m`オプションでモジュール名を指定する場合、関数名を指定することはできません。ただ、`server.py`に以下の記述があるため、結局main関数が呼ばれる動作になります。

```python
if __name__ == '__main__':
    main()
```

## ということで動作するコード

`python -m awslabs.aws_documentation_mcp_server.server`をMCPのSDKに合わせた形で指定します。

```python
import json

from mcp import StdioServerParameters, stdio_client
from strands import Agent
from strands.tools.mcp import MCPClient


def lambda_handler(event, context):
    prompt = event["prompt"]

    stdio_mcp_client = MCPClient(
        lambda: stdio_client(
            StdioServerParameters(
                command="python",
                args=["-m", "awslabs.aws_documentation_mcp_server.server"],
            )
        )
    )

    with stdio_mcp_client:
        # Get the tools from the MCP server
        tools = stdio_mcp_client.list_tools_sync()

        # Create an agent with these tools
        agent = Agent(tools=tools)

        result = agent(prompt=prompt)

    return {
        "statusCode": 200,
        "body": json.dumps(
            {"message": result.message["content"][0]["text"]}, ensure_ascii=False
        ),
    }
```

わーい😀

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/fba1a0ca-638f-43ce-9dca-64923994e558.png)

4分かかったけど

ソースおいときますね

https://github.com/moritalous/mcp-lambda