Skip to main content
Glama
debadatta30

s3-mcp-server

by debadatta30

S3 MCPサーバー(ECS Fargate上)

リモート、OAuth2認証されたMCPサーバーで、読み取り専用のS3ツール(list_buckets、list_objects、get_bucket_public_access、get_bucket_size)を公開します。ECS FargateタスクとしてALBとCloudFrontの背後で実行され、Auth0をIDプロバイダーとして使用するように設計されています。

構成の概要

  • CloudFrontがエッジでTLSを終端し、すべてのヘッダー(Authorizationを含む)をプレーンHTTPで内部ALBに転送します。

  • ALBがリクエストをポート8080のECS Fargateタスクに渡します。

  • タスク(server.py)は、任意のツールを実行する前にBearerトークンをAuth0に対して検証し、その後タスクのIAMロールを使用してS3と通信します。静的AWS認証情報はどこにもありません。

  • タスクは、NAT Gatewayを介してAuth0のJWKSエンドポイントとS3の公開APIの両方に到達します(プライベートサブネットで実行され、S3 VPCエンドポイントが設定されていないため)。

OAuthハンドシェイク(ログイン、PKCEコード交換)は、MCPクライアント(例:Claude Desktop)とAuth0の間で完全に実行されます。サーバーは、最初(自身のディスカバリードキュメントの提供)と最後(結果のトークンの検証)にのみ関与します。デバッグ時には、ログインの失敗はこのサーバーのログには決して表示されないことに注意してください。サーバーはその交換に参加していなかったためです。

Related MCP server: aws-safe-mcp

Auth0の設定

Auth0テナント(無料枠で十分)が必要で、その中に2つのものが必要です:

  1. API — これにより、Auth0が不透明なトークンではなく、実際のJWTアクセストークンを発行するようになります。

    • ダッシュボード → Applications → APIs → Create API

    • Identifier:クライアントが接続する正確なURL(例:https://your-domain.example.com/sse)。これはAUTH0_AUDIENCEになり、デプロイする背後とバイト単位で一致する必要があります。

    • 署名アルゴリズム:RS256(デフォルト)

  2. アプリケーション — MCPクライアントが認証するためのものです。

    • ダッシュボード → Applications → Applications → Create Application

    • タイプ:Regular Web Application

    • Settingsで、Allowed Callback URLsをMCPクライアントが使用するリダイレクトURIに設定します(Claude Desktopの場合:https://claude.ai/api/mcp/auth_callback)

    • Domain、Client ID、Client Secretをメモします。これらはAUTH0_DOMAIN / AUTH0_CLIENT_ID / MCPクライアントに提供するクライアントシークレットになります(サーバー自体はシークレットを必要としません)。

  3. APIに対してアプリケーションを認可する — この手順は見落としがちです。 APIとアプリケーションを別々に作成しても、それらはリンクされません。APIのApplication Accessタブに移動し、アプリケーションをオンに切り替えます。これをスキップすると、すべての認証試行がinvalid_request / 「Client is not authorized to access resource server」で失敗し、クライアントがログインページを表示する前に失敗します。

ローカルテスト(トランスポート層にAWSは不要)

cp .env.example .env
# edit .env with your Auth0 tenant details if you want to test auth locally
pip install -r requirements.txt
python server.py

サーバーはhttp://localhost:8080/sseでリッスンします。/healthは認証なしで200 okを返します — これはALBターゲットグループのヘルスチェックパスです。

他のすべてのルートにはAuthorization: Bearer <token>が必要です。トークンは、AUTH0_CLIENT_IDに一致するアプリクライアントのAuth0アクセストークン(JWT、audがAUTH0_AUDIENCEに一致)です。

Docker

docker build -t s3-mcp-server .
docker run -p 8080:8080 --env-file .env s3-mcp-server

Fargateへのデプロイ

cd infra
pip install -r requirements.txt   # into a venv
cdk bootstrap   # first time only, per account/region
cdk deploy \
  -c auth0_domain=your-tenant.us.auth0.com \
  -c auth0_client_id=your-application-client-id \
  -c auth0_audience=https://your-domain.example.com/sse

auth0_audienceは、CDKが作成しようとしているCloudFrontドメインと一致し、末尾に/sseが付加される必要があります。最初のデプロイ時にはおそらくわかりません。一度デプロイしてDistributionURL出力を取得し、実際のaudience値で再デプロイします(これは一度だけ発生します。CloudFrontドメインは同じスタックの後続のデプロイでも安定しています)。

毎回-cフラグを渡す代わりに、gitignoreされたinfra/cdk.context.jsonにそれらを配置できます:

{
  "auth0_domain": "your-tenant.us.auth0.com",
  "auth0_client_id": "your-application-client-id",
  "auth0_audience": "https://your-domain.example.com/sse"
}

よくある落とし穴

  1. ALBのアイドルタイムアウト。 SSE接続は長時間存続します。ALBのデフォルトのアイドルタイムアウト(60秒)はそれらを切断します。このスタックはそれを300(5分)に設定しています。クライアントが定期的なpingを送信しない場合は、さらに上げてください。

  2. ヘルスチェックパス。 ターゲットグループのヘルスチェックは/healthを指しており、/sseではありません。/sseは認証が必要でストリーミング応答であり、ALBのヘルスチェッカーはどちらも想定していません。

  3. 認証情報。 コンテナはAWS認証情報を設定しません。boto3はコンテナ認証情報エンドポイントを介してFargateタスクのIAMロールから自動的に取得します。キーをイメージや環境変数に焼き付けないでください。

  4. Egress。 タスクは最初のリクエストでAuth0のJWKSをHTTPS経由でフェッチし(その後1時間キャッシュ)、すべてのツール呼び出しでS3の公開APIを呼び出します。どちらもNAT Gatewayを経由して外に出ます。このスタックはS3 VPC Gateway Endpointを設定していないためです。タスクのサブネットに実際にNATルートがあることを確認するか、S3のGateway Endpointを追加してください(無料で、そのトラフィックをパブリックインターネットから遮断します)。

  5. MCP SDK独自のDNSリバインディング保護は、実際のドメインの背後でこれを静かに壊します。 FastMCPのTransportSecuritySettingsはデフォルトで許可ホストリストが空であり、実際のHostヘッダー(CloudFrontドメインなど)で到着するすべてのリクエストを拒否します。認証が成功した後でも、421 Misdirected Requestで拒否されます。これはOAuthフローの後に発生するため、認証バグと誤解されやすいです。このサーバーはserver.pyでそれを無効にしています。Auth0AuthMiddlewareがすでにすべてのルートをBearerトークン認証でガードしているためです:

    mcp = FastMCP(
        "s3-mcp-server",
        transport_security=TransportSecuritySettings(enable_dns_rebinding_protection=False),
    )

最小限のIAMタスクロールポリシー(上記の読み取り専用ツール用)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "S3ReadOnly",
      "Effect": "Allow",
      "Action": [
        "s3:ListAllMyBuckets",
        "s3:ListBucket",
        "s3:GetBucketAcl",
        "s3:GetBucketPolicyStatus",
        "s3:GetBucketPolicy"
      ],
      "Resource": "*"
    }
  ]
}

このエージェントが実際に表示できるバケットがわかったら、Resourceを特定のバケットARNにスコープダウンします。後で書き込みツール(例:ライフサイクルポリシーの変更)を追加する場合は、このポリシーを広げるのではなく、独自の狭いステートメントを与えてください。

MCPクライアント設定

リモートSSEサーバーをサポートする任意のMCPクライアントを、デプロイされたURLに向け、上記の設定手順のAuth0アプリケーションのClient IDとSecretを指定します:

{
  "mcpServers": {
    "s3": {
      "url": "https://your-domain.example.com/sse",
      "oauth_client_id": "your-application-client-id",
      "oauth_client_secret": "your-application-client-secret"
    }
  }
}

正確な設定の形状はクライアントによって異なります。Claude Desktopはこれらを生のJSONではなく、Connectors設定のフォームフィールドとして公開します。いずれにせよ、OAuthリダイレクト/トークン交換は、MCP認可仕様に従ってクライアントによって処理されます。このサーバーは、各リクエストで結果のBearerトークンを検証するだけです。

Related MCP Connectors

Related MCP Servers