🗂

作って覚えるMCP - ClaudeとAWS / Stripe MCPを使った実践チュートリアル

に公開

MCP(Model Context Protocol)の登場により、生成AIを使ったシステムの企画や開発フローに大きな変化が起き始めています。公式のドキュメントを参照したシステムの設計や実装、エラーが発生した時の調査やエラーコードの分析、そして新しくサービスを覚える際に「とりあえず動かしながら触ってみる」体験の加速。人や会社によって関わり方は様々ですが、MCPを使った開発フローを知っているか知らないかは、製品の市場投入や学びのサイクル速度に大きな影響を与えることは間違い無いでしょう。

https://modelcontextprotocol.io/introduction

今回はClaude DesktopにAWSとStripeのMCPを追加し、サービスの設計や開発を効率化する方法をステップバイステップで紹介します。

0. 今回作ろうとするもの

今回実装するのは、StripeのCheckout機能を使った簡易的な決済システムです。具体的には以下の機能を持つAPIを作成します。

  • AWS LambdaでStripe Checkout SessionのURLを発行するAPI
  • Lambda Function URLを使ったエンドポイント提供
  • DynamoDBによる在庫管理機能
  • Webhook処理によるトランザクション管理
  • AWS CDKによるインフラのコード化

1. 環境構築

まず、必要なツールをインストールしましょう。

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

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

1-1. 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

1-2. nodenvのインストール

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

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

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

nvm use 20

2. MCPサーバーの設定

それでは、Claude DesktopアプリにMCPを設定していきましょう。MCPの設定はmacOSではm~/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"
      ]
    }
  }
}

ここでは2つのMCPツールを追加しています。1つ目はAWS公式ドキュメントを参照するためのMCPで、もう1つはStripeドキュメントを参照するためのMCPです。

この状態でClaude Desktopアプリを再起動すると、2つのMCPが提供する機能(ツール)が追加されます。

このボタンをクリックすると、登録されたツールの中にAWSやStripeの機能が用意されていることがわかります。

3. Claudeに設計やコードを作らせる

MCPの接続ができましたので、早速AIに開発をやらせてみましょう。Claude Desktopアプリを開いて、作りたいものについて指示を出します。

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

コードを書かせる用途であれば、「じっくり考える」機能もオンにして開発を始めてみましょう。

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

Claudeは以下のようなステップでコードを生成してくれます:

  1. AWS LambdaとStripe APIに関するドキュメントの検索
  2. Lambda関数の実装設計
  3. CDKでのインフラ構成設計

生成された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レスポンス形式に整形する関数
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),
  };
}

実装されたコードを読むと、StripeのAPIキーをAmazon Secrets Manager経由で取得しようとしたり、商品が取得できなかった場合のエラーハンドリングなども実装されていることがわかります。このままの実装でリリースできる要件かどうかについては、作りたいサービスによって検討が必要ですが、ここまでのコードを提案してくれるとなると、少なくとも雛形を作るだけでも役立ちそうに感じますね。

4. CDKスタックの実装もやらせてみよう

ちなみにAWSでリソースをデプロイ・管理するために、AWS CDKを追加することもできます。この場合は、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"
      ]
    }
  }
}

次に、CDKでインフラをコード化します。以下のプロンプトを使用してCDKスタックを生成してもらいましょう:

先ほどのLambda関数をデプロイするためのCDKスタックを実装してください。Secrets Managerの設定も含めてください。

これでClaudeのアーティファクトにCDKの定義が生成されます。今回試したケースでは、このようなファイルが作られました。

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
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);

    // 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',
      },
      timeout: cdk.Duration.seconds(30),
      memorySize: 256,
    });

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

    // API Gateway
    const api = new apigateway.RestApi(this, 'StripeCheckoutApi', {
      restApiName: 'Stripe Checkout Service',
      description: 'API for generating Stripe Checkout Session URLs',
      defaultCorsPreflightOptions: {
        allowOrigins: apigateway.Cors.ALL_ORIGINS,
        allowMethods: apigateway.Cors.ALL_METHODS,
      },
    });

    // API Gatewayにリソースとメソッドを追加
    const checkoutResource = api.root.addResource('checkout');
    checkoutResource.addMethod('GET', new apigateway.LambdaIntegration(checkoutLambda));

    // 出力値の設定
    new cdk.CfnOutput(this, 'ApiUrl', {
      value: api.url,
      description: 'URL of the API endpoint',
    });
  }
}

5. 機能の拡張

おおよその実装が出来上がると、追加の要件も思いついてくるのが開発の定めです。これについても、Claudeに要件を伝えて作業させてみましょう。

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

この追加要件に基づいて、Claudeは以下のようなアーキテクチャを提案してくれます:

  1. Lambda Function URL: API Gatewayを使わずにGETリクエストを受け付けるエンドポイント
  2. DynamoDB: 在庫情報と仮押さえ状態を管理するテーブル
  3. Webhook Lambda: 支払い完了・キャンセル時の在庫処理を担当するLambda関数

提案されたコードを見ると、確かにDynamoDBのテーブルなどが追加されていることがわかります。

// 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
});

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

まとめ

このようにClaudeに各サービスのドキュメントや実装支援を行うMCPを追加することで、短い文章から要件の意図をできるだけ汲み取ったアプリケーションやインフラ定義を生成させることができます。これを元に開発を進めることもできますし、新しくAWSやStripeを覚えようという時に「とりあえず動くものを出して、動かしたり壊したりして覚えよう」という学び方も可能となります。

また、Claude CodeのMCPを併用したり、これらのMCPをCursor / Cline / Roo Codeなどに追加することで、実際のプロジェクトコードにするなどのやり方もできそうです。

ぜひ皆さんも、この記事をきっかけにMCPに挑戦してみてください。

デジタルキューブ

Discussion