Strands Agents SDKでMCPホストアプリを作ってみよう！

# こんなWebアプリを簡単に作ろう！

好きなMCPサーバーをセットできます。

![スクリーンショット 2025-05-26 3.22.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633856/b920e0f2-aa45-41dc-81fd-d547ab5026b3.png)

超簡易構成図

![スクリーンショット 2025-05-26 2.49.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633856/90231c75-97c3-49b2-8fb4-7671f077b946.png)

完成品は、ここから誰でも試せます！

https://strands-mcp-agent.streamlit.app/


# Strands Agents SDKとは？

AWSが最近発表した、AIエージェント開発用フレームワークです。

https://aws.amazon.com/jp/blogs/news/introducing-strands-agents-an-open-source-ai-agents-sdk/

LangGraphとかMastraみたいなやつですね。
めちゃシンプルかつ高機能で、特にAWSやBedrockとの親和性が高いです！


# 手順

### AWSアカウントを作成

https://aws.amazon.com/jp/register-flow/


### Bedrockのモデルを有効化

「Bedrock > モデルアクセス」から、すべてのモデルを有効化すればOKです。


### CloudShellでアプリを開発

AWSのシェル環境「CloudShell」を使って、ブラウザ完結でアプリを動かしてみます。
もちろんローカルPCで実施してもOKです。

Pythonバージョンを上げます。

```bash: CloudShell
# Python 3.12をインストール
sudo yum install python3.12

# シンボリックリンクを修正
sudo ln -sf /usr/bin/python3.12 /usr/bin/python3

# pipもアップグレード
sudo python -m ensurepip --upgrade
```

nanoエディターでPythonファイルを作成します。

```bash: CloudShell
nano app.py
```

以下のコードを貼り付けます。

```py: app.py
import asyncio
import streamlit as st
from strands import Agent
from strands.models import BedrockModel
from strands.tools.mcp import MCPClient
from mcp import stdio_client, StdioServerParameters

# メインエリア
st.title("Strands MCPエージェント")
st.text("👈 サイドバーで好きなMCPサーバーを設定して、Strands Agents SDKを動かしてみよう！")
question = st.text_input("質問を入力", "Bedrockでマルチエージェントは作れる？")

# サイドバー
with st.sidebar:
    mcp_args = st.text_input("MCPサーバーのパッケージ名（uvx用）", "awslabs.aws-documentation-mcp-server@latest")
    model_id = st.text_input("BedrockのモデルID", "us.anthropic.claude-3-7-sonnet-20250219-v1:0")
    st.text("※一部のモデル（Claude 4など）はクォータが厳しく、応答なしエラーが発生することがあります。")


def create_mcp_client(mcp_args):
    """MCPクライアントを作成"""
    return MCPClient(lambda: stdio_client(
        StdioServerParameters(command="uvx", args=[mcp_args])
    ))


def create_agent(client, model_id):
    """エージェントを作成"""
    return Agent(
        model=BedrockModel(model_id=model_id),
        tools=client.list_tools_sync()
    )


def extract_tool_info(chunk):
    """チャンクからツール情報を抽出"""
    event = chunk.get('event', {})
    if 'contentBlockStart' in event:
        tool_use = event['contentBlockStart'].get('start', {}).get('toolUse', {})
        return tool_use.get('toolUseId'), tool_use.get('name')
    return None, None


def extract_text(chunk):
    """チャンクからテキストを抽出"""
    if text := chunk.get('data'):
        return text
    elif delta := chunk.get('delta', {}).get('text'):
        return delta
    return ""


async def stream_response(agent, question, container):
    """レスポンスをストリーミング表示"""
    text_holder = container.empty()
    buffer = ""
    shown_tools = set()
    
    async for chunk in agent.stream_async(question):
        if isinstance(chunk, dict):
            # ツール実行を検出して表示
            tool_id, tool_name = extract_tool_info(chunk)
            if tool_id and tool_name and tool_id not in shown_tools:
                shown_tools.add(tool_id)
                if buffer:
                    text_holder.markdown(buffer)
                    buffer = ""
                container.info(f"🔧 **{tool_name}** ツールを実行中...")
                text_holder = container.empty()
            
            # テキストを抽出して表示
            if text := extract_text(chunk):
                buffer += text
                text_holder.markdown(buffer + "▌")
    
    # 最終表示
    if buffer:
        text_holder.markdown(buffer)


# ボタンを押したら生成開始
if st.button("質問する"):
    client = create_mcp_client(mcp_args)
    
    with st.spinner("回答を生成中…"):
        with client:
            agent = create_agent(client, model_id)
            container = st.container()
            
            # 非同期実行
            loop = asyncio.new_event_loop()
            loop.run_until_complete(stream_response(agent, question, container))
            loop.close()
```

実行してみましょう。

```bash: CloudShell
# Strands Agentsをインストール
pip install uv streamlit strands-agents strands-agents-tools

# アプリを起動
streamlit run app.py
```

CloudShellで2つ目のタブ（リージョン名）を起動して、PinggyというトンネリングサービスにSSH接続します。（Web公開されます）

```bash: CloudShell（2タブ目）
ssh -p 443 -R0:localhost:8501 a.pinggy.io
```

表示されたURLにアクセスしてみましょう！

![スクリーンショット 2025-05-26 3.22.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633856/b920e0f2-aa45-41dc-81fd-d547ab5026b3.png)

好きなローカルMCPサーバーを入力して、AIエージェントにツールとして使わせることが可能です。

:::note warn
Pinggyで公開URLを発行しているので、どこからでもアクセスできてしまいます。セキュリティにはご留意ください。

Pinggyの無料セッション経過（60分）、もしくはCloudShellの自動停止（20〜30分）でアプリは止まってしまいます。
:::



# ソースコード

こちらに公開しています。

https://github.com/minorun365/strands-mcp-agent


# 次のステップ

今回作ったアプリをコンテナにしてWeb公開してみる

https://qiita.com/minorun365/items/84bef6f06e450a310a6a

GUIで簡単にAIエージェントを作ってみる

https://qiita.com/minorun365/items/1f4dbf5842a47bd175cc

LLMOps（監視と評価）に入門してみる

https://qiita.com/minorun365/items/70ad2f5a0afaac6e5cb9


# おまけ

Bedrockにちゃんと入門してみたくなったら…

https://www.amazon.co.jp/dp/4815626448

![スクリーンショット 2025-05-25 19.39.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633856/258bb25b-776f-4af7-a815-7df7d86b56b9.png)


MCPの入門書も出版します！ すでにアマゾン等で予約可能。

https://www.amazon.co.jp/dp/4798075736

![やさしいMCP入門.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633856/06b35473-06d7-4ec7-9fb4-e73f2b7de8fe.png)