🎃

MCPで学ぶAWS開発入門 - アイデアから実装までの最短距離

に公開

AWS、Stripeといったクラウドサービスを使いこなすのは、初心者にとって大きなハードルです。ドキュメントは膨大で、正しい実装方法を見つけるだけでも時間がかかります。「実際に動くものを作りながら学びたい」と思っても、どこから手をつければいいのか迷ってしまうことでしょう。

Model Context Protocol (MCP)は、そんな初心者の強い味方になります。MCPを使えば、AIがドキュメントを参照しながら実装を進めてくれるため、動くシステムを短時間で作りながら学習できます。この記事では、StripeとAWSを組み合わせた決済システムを例に、MCPを活用した学習方法と実装のポイントを紹介します。

MCPとは - 動かしながら学ぶための新しいツール

MCPは、AIがサービスの公式ドキュメントを参照しながらコードを生成してくれる開発支援ツールです。例えば、AWS MCPやStripe MCPを使えば、それぞれのサービスの最新APIや機能を活用したコードを、AIが自動的に作成してくれます。

特に初心者にとってのMCPの魅力は、以下の点にあります:

  • 実際に動くコードから学べる: 理論だけでなく、動作するコードを見ながら学習できる
  • 最新の情報に基づいた実装: 公式ドキュメントを参照するため、古い情報に惑わされない
  • 全体像の把握: システム設計から実装まで、一連の流れを体験できる
  • 試行錯誤の容易さ: 指示を変えるだけで、異なる実装方法を簡単に試せる

MCPは本番環境で長期運用するコードを作るというよりも、「アイデアを形にする」「学習のために実装を進める」という目的に最適なツールなのです。

実例:Stripe決済システムを作りながらAWSを学ぶ

具体的な例として、StripeとAWS Lambdaを組み合わせたシンプルな決済システムを実装してみましょう。このプロジェクトを通じて、以下のようなAWSの基本概念を学ぶことができます:

  • サーバーレスアーキテクチャの基本
  • AWS Lambda関数の作成と実行
  • Lambda Function URLの設定
  • DynamoDBによるデータ保存
  • AWS CDKを使ったインフラのコード化

ステップ1: 環境準備とMCPの設定

まずは、MCPを使うための環境を整えましょう。今回は以下の2つが事前に必要となります。

  • uv: Pythonパッケージマネージャー
  • nvm: Node.jsパッケージマネージャー

uvのインストール

AWSが提供するMCPは、Pythonパッケージマネージャーの「uv」を使用します。まずはこちらをインストールしましょう。

curl -LsSf https://astral.sh/uv/install.sh | sh
source $HOME/.local/bin/env

インストールが完了したら、バージョンを確認してみます:

uv --version
# 出力例: uv 0.6.13 (a0f5c7250 2025-04-07)

続いてuvを使ってPythonをインストールします:

uv python install 3.10
# 出力例: Installed Python 3.10.16 in 1.53s
#         + cpython-3.10.16-macos-aarch64-none

nvmのインストール

Node.jsの環境管理には、nvmを使います。

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash

Node.jsのバージョンを指定してインストールしましょう。

nvm use 20

次に、Claude DesktopアプリにMCPサーバーを設定します。MCPの設定はmacOSでは~/Library/Application\ Support/Claude/claude_desktop_config.jsonにあります。

エディタでclaude_desktop_config.jsonを開き、以下の内容を記述します:

{
  "mcpServers": {
    "awslabs.aws-documentation-mcp-server": {
        "command": "uvx",
        "args": ["awslabs.aws-documentation-mcp-server@latest"],
        "env": {
          "FASTMCP_LOG_LEVEL": "ERROR"
        },
        "disabled": false,
        "autoApprove": []
    },
    "stripe": {
      "command": "npx",
      "args": [
        "-y",
        "@stripe/mcp",
        "--tools=documentation.read",
        "--api-key=sk_dummy"
      ]
    }
  }
}

この設定で、AWS公式ドキュメント、Stripeドキュメントを活用できるようになります。Claude Desktopアプリを再起動すると、2つのMCPが提供する機能(ツール)が追加されます。

ステップ2: シンプルなプロンプトで実装を開始

初心者にとって重要なのは、「まずは動くものを作る」ということです。以下のようなシンプルなプロンプトから始めましょう:

AWS Lambdaを使って、Stripe Checkout SessionのURLを発行するGET APIを追加したい。CDKで構成を管理する前提で設計をしてください。
最新情報を確認したいので、AWSもStripeも必ずドキュメントをチェックしてから作業してください。

MCPの設定ができていれば、Stripeやのツールを使う許可を求めてきます。「このチャットで許可する」をクリックして作業を始めさせましょう。

このプロンプトに対して、MCPは以下のようなステップでコードを生成します:

  1. AWS LambdaとStripe APIのドキュメントを検索
  2. Stripe Checkout Sessionを作成するLambda関数の基本コードを生成
  3. CDKによるインフラ構成コードを生成

ステップ3: 生成されたコードから学ぶ

MCPが生成した基本的なLambda関数のコードを見てみましょう:

const AWS = require('aws-sdk');
const stripe = require('stripe');
const secretsManager = new AWS.SecretsManager();

exports.handler = async (event) => {
  console.log('Event received:', JSON.stringify(event, null, 2));
  
  try {
    // Stripe APIキーの取得
    const secretData = await getSecret(process.env.STRIPE_SECRET_KEY_ARN);
    const stripeClient = stripe(secretData.stripeSecretKey);
    
    // クエリパラメータの取得
    const queryParams = event.queryStringParameters || {};
    const productId = queryParams.productId;
    const priceId = queryParams.priceId;
    const quantity = parseInt(queryParams.quantity || '1', 10);
    const mode = queryParams.mode || 'payment';
    
    // 必須パラメータのバリデーション
    if (!priceId && !productId) {
      return formatResponse(400, {
        error: 'Bad Request',
        message: 'Either productId or priceId is required',
      });
    }

    // Stripe Checkout Sessionの設定
    const sessionParams = {
      payment_method_types: ['card'],
      line_items: [],
      mode: mode, // 'payment', 'subscription', or 'setup'
      success_url: process.env.SUCCESS_URL,
      cancel_url: process.env.CANCEL_URL,
    };

    // 商品情報の設定
    if (priceId) {
      // priceIdが提供されている場合は直接使用
      sessionParams.line_items.push({
        price: priceId,
        quantity: quantity,
      });
    } else if (productId) {
      // productIdが提供されている場合、最初に商品の価格を取得
      const productDetails = await stripeClient.products.retrieve(productId);
      const prices = await stripeClient.prices.list({
        product: productId,
        limit: 1,
        active: true,
      });
      
      if (prices.data.length === 0) {
        return formatResponse(400, {
          error: 'Bad Request',
          message: 'No active price found for the provided productId',
        });
      }
      
      sessionParams.line_items.push({
        price: prices.data[0].id,
        quantity: quantity,
      });
    }

    // Checkoutセッションの作成
    const session = await stripeClient.checkout.sessions.create(sessionParams);
    
    // 成功レスポンスの返却
    return formatResponse(200, {
      success: true,
      checkoutUrl: session.url,
      sessionId: session.id,
    });
  } catch (error) {
    console.error('Error:', error);
    return formatResponse(500, {
      error: 'Internal Server Error',
      message: error.message,
    });
  }
};

// Secrets Managerからシークレットを取得する関数
async function getSecret(secretArn) {
  const data = await secretsManager.getSecretValue({ SecretId: secretArn }).promise();
  if ('SecretString' in data) {
    return JSON.parse(data.SecretString);
  }
  throw new Error('Secret not found in the expected format');
}

// API Gatewayのレスポンス形式に整形する関数
function formatResponse(statusCode, body) {
  return {
    statusCode: statusCode,
    headers: {
      'Content-Type': 'application/json',
      'Access-Control-Allow-Origin': '*', // CORS設定
      'Access-Control-Allow-Credentials': true,
    },
    body: JSON.stringify(body),
  };
}

このコードから、以下のような重要な概念を学ぶことができます:

  1. Lambda関数の基本構造: exports.handlerがエントリーポイントとなる関数
  2. AWS SDKの使い方: Secrets Managerの呼び出し方法
  3. StripeのAPI利用法: Checkout Sessionの作成方法
  4. エラーハンドリング: try-catchによる例外処理
  5. レスポンス形式: API GatewayやLambda Function URLに適したレスポンス形式

ステップ4: 機能拡張で学習を深める

基本的な実装ができたら、さらに要件を追加して学習を深めましょう。例えば:

API Gatewayを使わずに、Lambda functions URLでやれないか?
DynamoDBを利用して、在庫の仮押さえ・確保・開放の仕組みも追加して

このような追加指示により、MCPは機能拡張したアーキテクチャを提案してくれます:

  1. Lambda Function URL: API Gatewayを使わないシンプルなエンドポイント
  2. Lambda関数: Stripe APIを呼び出すメイン処理
  3. DynamoDB: 在庫情報や予約状態を管理するデータベース
  4. Webhook Lambda: 支払い完了・キャンセル時の在庫処理を担当
  5. CDK: これらのリソースをコードで管理

このアーキテクチャを通じて、Lambda Function URLやDynamoDBといった追加サービスの使い方も学べます。

ステップ5: CDKで学ぶインフラのコード化

AWS CDK(Cloud Development Kit)は、TypeScriptやPythonなどのプログラミング言語でAWSリソースを定義できるツールです。ここでは、AWS CDK MCP Serverを追加することで、より確実なリソース定義を作成できます。

エディタでclaude_desktop_config.jsonを開き、AWS CDK MCP Serverの定義を追加しましょう:

{
  "mcpServers": {
    "awslabs.aws-documentation-mcp-server": {
        "command": "uvx",
        "args": ["awslabs.aws-documentation-mcp-server@latest"],
        "env": {
          "FASTMCP_LOG_LEVEL": "ERROR"
        },
        "disabled": false,
        "autoApprove": []
    },
    "awslabs.cdk-mcp-server": {
      "command": "uvx",
      "args": ["awslabs.cdk-mcp-server@latest"],
      "env": {
        "FASTMCP_LOG_LEVEL": "ERROR"
      },
      "disabled": false,
      "autoApprove": []
    },
    "stripe": {
      "command": "npx",
      "args": [
        "-y",
        "@stripe/mcp",
        "--tools=documentation.read",
        "--api-key=sk_dummy"
      ]
    }
  }
}

MCPが生成したCDKコードを見てみましょう:

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as path from 'path';

export class StripeCheckoutStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // DynamoDBテーブルの作成 - 在庫管理用
    const inventoryTable = new dynamodb.Table(this, 'InventoryTable', {
      partitionKey: { name: 'productId', type: dynamodb.AttributeType.STRING },
      billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
      removalPolicy: cdk.RemovalPolicy.DESTROY, // 開発環境用
      pointInTimeRecovery: true,
    });

    // 在庫予約情報を保存するテーブル
    const reservationsTable = new dynamodb.Table(this, 'ReservationsTable', {
      partitionKey: { name: 'sessionId', type: dynamodb.AttributeType.STRING },
      billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
      removalPolicy: cdk.RemovalPolicy.DESTROY, // 開発環境用
      timeToLiveAttribute: 'expiresAt', // TTLを有効化 (自動削除)
    });

    // GSIを追加して、productIdでの検索を可能に
    reservationsTable.addGlobalSecondaryIndex({
      indexName: 'productId-index',
      partitionKey: { name: 'productId', type: dynamodb.AttributeType.STRING },
      projectionType: dynamodb.ProjectionType.ALL
    });

    // Stripe APIキーをSecrets Managerで管理
    const stripeSecret = new secretsmanager.Secret(this, 'StripeApiKey', {
      secretName: 'stripe/api-key',
      description: 'Stripe API Key for Checkout',
    });

    // チェックアウトLambda関数
    const checkoutLambda = new lambda.Function(this, 'StripeCheckoutFunction', {
      runtime: lambda.Runtime.NODEJS_18_X,
      handler: 'index.handler',
      code: lambda.Code.fromAsset(path.join(__dirname, '../lambda/checkout')),
      environment: {
        STRIPE_SECRET_KEY_ARN: stripeSecret.secretArn,
        SUCCESS_URL: 'https://example.com/success',
        CANCEL_URL: 'https://example.com/cancel',
        INVENTORY_TABLE_NAME: inventoryTable.tableName,
        RESERVATIONS_TABLE_NAME: reservationsTable.tableName,
      },
      timeout: cdk.Duration.seconds(30),
      memorySize: 256,
    });

    // Lambda Function URLの設定
    const checkoutFunctionUrl = checkoutLambda.addFunctionUrl({
      authType: lambda.FunctionUrlAuthType.NONE, // 認証なし (公開API)
      cors: {
        allowedOrigins: ['*'], // 全てのオリジンからのアクセスを許可 (本番環境では制限する)
        allowedMethods: [lambda.HttpMethod.GET],
        allowedHeaders: ['*'],
      },
    });

    // Lambda関数にDynamoDBへのアクセス権限を付与
    inventoryTable.grantReadWriteData(checkoutLambda);
    reservationsTable.grantReadWriteData(checkoutLambda);

    // Secrets Managerへのアクセス権限を付与
    stripeSecret.grantRead(checkoutLambda);

    // Webhookを処理するLambda関数
    const webhookLambda = new lambda.Function(this, 'StripeWebhookFunction', {
      runtime: lambda.Runtime.NODEJS_18_X,
      handler: 'index.handler',
      code: lambda.Code.fromAsset(path.join(__dirname, '../lambda/webhook')),
      environment: {
        STRIPE_SECRET_KEY_ARN: stripeSecret.secretArn,
        INVENTORY_TABLE_NAME: inventoryTable.tableName,
        RESERVATIONS_TABLE_NAME: reservationsTable.tableName,
      },
      timeout: cdk.Duration.seconds(30),
      memorySize: 256,
    });

    // Webhook Lambda Function URLの設定
    const webhookFunctionUrl = webhookLambda.addFunctionUrl({
      authType: lambda.FunctionUrlAuthType.NONE, // 認証なし (Stripeからのコールバック用)
      cors: {
        allowedOrigins: ['*'],
        allowedMethods: [lambda.HttpMethod.POST],
        allowedHeaders: ['*'],
      },
    });

    // Webhook LambdaにもDynamoDBへのアクセス権限を付与
    inventoryTable.grantReadWriteData(webhookLambda);
    reservationsTable.grantReadWriteData(webhookLambda);

    // Secrets Managerへのアクセス権限を付与
    stripeSecret.grantRead(webhookLambda);

    // 出力値の設定
    new cdk.CfnOutput(this, 'CheckoutApiUrl', {
      value: checkoutFunctionUrl.url,
      description: 'URL for the Stripe Checkout API',
    });

    new cdk.CfnOutput(this, 'WebhookUrl', {
      value: webhookFunctionUrl.url,
      description: 'URL for Stripe webhook callbacks',
    });
  }
}

このCDKコードから学べる重要な概念:

  1. スタックの構造: AWSリソースをどのように組み合わせるか
  2. Lambda関数の設定: ランタイム、タイムアウト、メモリサイズなど
  3. DynamoDBテーブルの設計: パーティションキー、TTL、インデックスなど
  4. セキュリティ設定: IAMポリシー、シークレット管理
  5. リソース間の関連付け: 権限付与、環境変数によるリンク

ステップ6: 実装から実行へ

設計と実装が完了したら、実際にコードを実行してみましょう。Claude Desktopアプリで作業を行い、実際のプロジェクト構造を作成し、コードを生成できます。これにより、設計から実装、そして実行までの一連のプロセスを体験できます。

MCPを使った学習のメリットと注意点

メリット

  1. 実践的な学習: 理論だけでなく、実際に動くコードから学べる
  2. 全体像の把握: システム設計から実装まで一貫して体験できる
  3. 最新情報の活用: 常に最新のドキュメントを参照するため、古い情報に惑わされない
  4. 迅速なプロトタイピング: アイデアを素早く形にして検証できる

注意点

  1. 本番運用の考慮: MCPで生成したコードをそのまま長期運用することには課題があるかも
  2. 深い理解の必要性: 生成されたコードの意味を理解することが重要
  3. セキュリティの考慮: 特に決済システムでは、セキュリティ面の十分な確認が必要
  4. コスト管理: 実際にAWSリソースをデプロイする際はコスト管理に注意

次のステップ: プロトタイプから本格開発へ

MCPで学習やプロトタイピングを進めた後、次のステップとして以下のようなアプローチがおすすめです:

  1. 生成されたコードの詳細分析: 各部分がなぜそのように実装されているのか理解を深める
  2. セキュリティ強化: 認証・認可の追加、APIキー管理の改善など
  3. テストの充実: 単体テスト、統合テストの追加
  4. CI/CDパイプラインの構築: 継続的な開発・デプロイ環境の整備
  5. モニタリングとログ管理: CloudWatchなどを使った運用監視体制の確立

まとめ: MCPを活用した効果的な学習

MCPは、AWS、Stripeなどの複雑なクラウドサービスを学ぶための強力なツールです。特に初心者にとって、「動かしながら学ぶ」という体験は非常に価値があります。

実際に動くシステムを作りながらAWSの基本概念を学び、自分のアイデアを形にする過程で、クラウド開発の本質を理解できるでしょう。MCPは長期運用コードの生成というよりも、学習ツール・プロトタイピングツールとして最大の価値を発揮します。

あなたのアイデアをMCPで形にして、クラウド開発の可能性を体験してみてください。そして、その経験を足がかりに、より本格的なAWS開発スキルを身につけていきましょう。

デジタルキューブ

Discussion