これは「mastra-stripe-lineハンズオン」資料の一部です。
前回までのステップについては、以下の記事をご確認ください。
このワークショップで学べること
このステップでは、 Mastraを利用した場合のツールの利用法を学びます。
生成AIがMastraを通して外部サービス / APIを利用する場合、Toolという仕組みを利用します。この仕組みを利用することで、生成AIがユーザーのリクエストに対して適切なAPIやサービスを自身で判断して利用することができるようになります。
接続先にはStripeを利用します。Stripeが提供する検索APIを活用し、ユーザーの意図にあった情報を取得する仕組みを、Mastraで構築する方法を学びましょう。
準備が必要なもの
このステップでは、以下の準備が必要です。
- セットアップ済みのMastraアプリ: 「前のステップのワークショップ資料をご確認ください」
- Stripeアカウント
Step1: Stripeに商品データを登録する
まずは https://dashboard.stripe.com からStripeアカウントを作成しましょう。
本番環境の申請は不要ですので、それに関する案内はすべてスキップしましょう。
ワークショップに参加される方は、この手順は不要です
ワークショップ時間短縮のため、事前に商品登録を済ませたStripeアカウントを用意しました。
以下のAPIキーをコピーしてご利用ください。
<削除済み>
なお、このAPIキーは、イベント終了後に削除されます。
APIキーを参加者に共有しますので、会場での説明を受けたのち、「Step2」へ進んでください
商品を登録する
Stripeダッシュボードから日本酒の商品データを登録します。このワークショップでは、Code-for-SAKEが公開している日本酒データを参考に、関西地方の日本酒を登録します。
1. 商品カタログを開く
ダッシュボード左メニューから「商品カタログ」を選択し、「商品を追加」ボタンをクリックします。
2. 基本情報を入力する
| 項目 | 入力例 |
|---|---|
| 名前 | 七本鎗 純米 玉栄 |
| 説明 | 滋賀県産玉栄を使用した純米酒 |
3. 料金を設定する
「料金情報を追加」セクションで以下を設定します。
- 価格:1650(円単位で入力)
- 通貨:JPY
- 「この価格をデフォルトにする」にチェック
4. メタデータを追加する
「その他のオプション」を展開し、「メタデータ」セクションで以下のキーと値を追加します。
| キー | 値の例 | 説明 |
|---|---|---|
brand |
七本鎗 | ブランド名 |
brewery |
冨田酒造 | 酒造名 |
type |
純米 | 種類 |
prefecture |
滋賀県 | 都道府県 |
ricePolishingRate |
60% | 精米歩合 |
5. 保存する
「商品を保存」をクリックして登録完了です。
登録する商品の例
ワークショップでは、以下のような商品を3〜5件ほど登録しておくと検索機能の動作確認がしやすくなります。
| 名前 | 酒造 | 都道府県 | 種類 | 精米歩合 |
|---|---|---|---|---|
| 七本鎗 純米 玉栄 | 冨田酒造 | 滋賀県 | 純米 | 60% |
| 紀土 純米吟醸 | 平和酒造 | 和歌山県 | 純米吟醸 | 50% |
| 龍力 特別純米 | 本田商店 | 兵庫県 | 特別純米 | 65% |
| 風の森 秋津穂 | 油長酒造 | 奈良県 | 純米 | 65% |
| 澤屋まつもと 守破離 | 松本酒造 | 京都府 | 純米吟醸 | 50% |
時間に余裕がある方は、Code-for-SAKEのデータから好みの日本酒を探して追加登録してみてください。
APIキーを取得できた方は、以下のcurlコマンドをお試しください。2行目のrk_またはsk_からはじまるあなたのStripe APIキーと記載されている部分を、APIキーに差し替えましょう。
curl -G https://api.stripe.com/v1/products \
-u "<rk_またはsk_からはじまるあなたのStripe APIキー>:" \
-d limit=3
APIキーが「sk_test_123」ならば、このように記載します。最後の:を消すと意図しない結果になります。間違って消さないようにご注意ください。
curl -G https://api.stripe.com/v1/products \
-u "sk_test_123:" \
-d limit=3
次のようなレスポンスが表示されていれば準備OKです。IDなどが完全に一致している必要はなく、エラーが発生していないかだけをチェックしましょう。
{
"object": "list",
"data": [
{
"id": "prod_TKYAMzKdr77f0m",
"object": "product",
"active": true,
"attributes": [],
"created": 1761819987,
"default_price": null,
"description": "無濾過生原酒",
"images": [],
"livemode": false,
"marketing_features": [
APIキーを登録する
取得したAPIキーは環境変数に保存しましょう。.envファイルを開き、STRIPE_SECRET_KEY=<取得したAPIキー>として追加します。
LINE_CHANNEL_ACCESS_TOKEN=
LINE_CHANNEL_SECRET=
+STRIPE_SECRET_KEY=rk_test_xxx
sk_から始まるシークレットAPIキーを利用しても良いのですが、全てのAPIにアクセスできるなど、漏洩時のリスクが高くなります。そのためなるべくプロジェクトごとにrk_から始まる制限付きAPIキーを発行することをお勧めします。
Step2: Mastraエージェントを追加する
Stripeアカウントの準備ができたので、早速始めましょう。
メモリ機能を有効化するため、以下のライブラリを追加でインストールしましょう。
npm i @mastra/memory @mastra/libsql
続いてStripeの商品データ検索だけを行うエージェントを、新しく作ります。
src/mastra/agents/stripe-agents.tsというファイルを作成しましょう。下にコードを記載していますので、中身をコピーし、src/mastra/agents/stripe-agents.tsにペーストして保存してください。
import { google } from '@ai-sdk/google';
import { Agent } from '@mastra/core/agent';
import { Memory } from '@mastra/memory';
import { LibSQLStore } from '@mastra/libsql';
const stripeMemory = new Memory({
storage: new LibSQLStore({
url: 'file:./mastra.db',
}),
});
export const stripeAgent = new Agent({
name: 'Stripe Commerce Agent',
instructions: `
あなたは気の利いたビジネスアシスタントです。Stripe を使った商品検索などを丁寧にサポートしてください。
## 応答ポリシー
1. ユーザーの要望を明確にするために質問することを恐れない
2. 金額やIDなど不確定な情報は必ず確認し、推測で Stripe ツールを呼び出さない
4. 関西の日本酒だけをお勧めしましょう。他の地域のお酒をリクエストされたら、うまいこと関西のお酒に変換してお勧めしましょう。
5. 決済リンクを発行した場合は URL と注意点(有効期限や通貨など)を簡潔に案内する
6. 処理結果はログを残す
## よくある利用例
- 「この商品の価格を教えて」
- 「滋賀県の日本酒を検索して」
- 「3000円以下の純米酒を探して」
- 「特定のブランドの商品を検索して」
`,
model: google('gemini-2.5-flash'),
memory: stripeMemory,
});
ここでは新しいエージェントの定義を行なっています。instructionsにいわゆる「システムプロンプト」と呼ばれるプロンプトを設定し、エージェントの振る舞いを定義しました。
今回はCode-for-SAKEが公開している日本酒データをStripeアカウントに取り込んでいますので、日本酒のレコメンドを行うエージェントとして振る舞うように指示しています。
モデルはワークショップで統一して利用するGeminiを利用します。modelに渡すモデルを変更するだけで、Anthropic / OpenAIなどが提供するモデルにも対応できますので、興味のある方は後日お試しください。
まだこのエージェントは振る舞いや利用する生成AIのモデルを定義したのみの状態です。そのためこの状態でエージェントを起動させても、AIの知識だけで頑張って説明しようとします。Stripeに保存されている情報を利用して回答できるように、次はMastraのツールを追加しましょう。
Step3: Stripe APIにアクセスするツールを用意する
ここからはMastraのエージェントが Stripe にアクセスするための仕組みを実装します。
3-1: ライブラリを追加
まずはStripe APIにアクセスするためのSDKを追加しましょう。以下のコマンドを実行します。
npm i stripe
インストールが完了したら、次のステップに進みます。
3-2: シンプルなツールを実装する
Stripe APIを呼び出すツールを実装します。src/mastra/tools/stripe-tools.tsを作成し、以下のコードをコピー & ペーストしましょう。
import { createTool } from '@mastra/core/tools';
import { z } from 'zod';
import Stripe from 'stripe';
export const searchProductsTool = createTool({
id: 'search-products',
description: 'Stripeで商品を検索する。ブランド、酒造、種類、都道府県、精米歩合、価格範囲などで検索できます。',
outputSchema: z.object({
products: z.array(
z.object({
id: z.string().describe('Product ID'),
name: z.string().describe('商品名'),
description: z.string().nullable().describe('商品説明'),
price: z.number().nullable().describe('価格(円)'),
priceId: z.string().nullable().describe('Price ID'),
metadata: z.record(z.string()).describe('メタデータ'),
})
).describe('検索結果の商品一覧'),
count: z.number().describe('検索結果件数'),
}),
execute: async (context: any) => {
const apiKey = process.env.STRIPE_SECRET_KEY;
if (!apiKey) {
throw new Error('Stripe API キーが設定されていません。環境変数 STRIPE_SECRET_KEY または STRIPE_API_KEY を設定してください。');
}
const stripe = new Stripe(apiKey, {
apiVersion: '2025-10-29.clover' as any,
});
try {
// あとで検索クエリを作成する
// Stripeにあるデータを取得・検索する
const products = await stripe.products.list({ limit: limit });
// フィルタリング処理を後で追加する
let filteredProducts: Stripe.Product[] = products.data;
// 結果を整形
const formattedProducts = filteredProducts.map((product: Stripe.Product) => {
const price = product.default_price;
const priceAmount =
typeof price === 'object' && price?.unit_amount ? price.unit_amount : null;
const priceId =
typeof price === 'string' ? price : typeof price === 'object' ? price?.id || null : null;
return {
id: product.id,
name: product.name,
description: product.description,
price: priceAmount,
priceId: priceId,
metadata: product.metadata || {},
};
});
return {
products: formattedProducts,
count: formattedProducts.length,
};
} catch (error) {
if (error instanceof Error) {
throw new Error(`Stripe商品検索エラー: ${error.message}`);
}
throw error;
}
},
});
export const stripeTools = {
searchProductsTool,
};
ここではMastraのcreateTool関数を利用してエージェントが利用するツールを登録しています。Stripe SDKを利用してデータを取得し、レスポンスから必要なデータだけを取り出して返すシンプルな実装です。実際には検索処理などを追加しますが、まずは最もシンプルな形として作りましょう。
outputSchemaにzodで型を定義していますが、これはエージェントが「このツールを使うと、どんな情報を持つレスポンスを受け取れるか」を理解することに役立ちます。descriptionやoutputSchemaなどの情報から、エージェントはユーザーの質問に対して適切なツールを選択して実行します。
3-3: 作成したツールをエージェントに登録する
作成したツールをエージェントに登録しましょう。src/mastra/agents/stripe-agents.tsを開きます。
まずは作成したツールをimportさせましょう。
import { LibSQLStore } from '@mastra/libsql';
+import { stripeTools } from '../tools/stripe-tools';
その後、Agentのinstructionとtoolsを更新します。テキストやコードは、以下のコードからコピーしてください。
export const stripeAgent = new Agent({
name: 'Stripe Commerce Agent',
instructions: `
あなたは気の利いたビジネスアシスタントです。Stripe を使った商品検索などを丁寧にサポートしてください。
## 使える主な機能
- searchProductsTool: Stripe 上の商品を検索する。ブランド、酒造、種類、都道府県、精米歩合、価格範囲などで検索できます
## 応答ポリシー
1. ユーザーの要望を明確にするために質問することを恐れない
2. 関西の日本酒だけをお勧めしましょう。他の地域のお酒をリクエストされたら、うまいこと関西のお酒に変換してお勧めしましょう。
3. 処理結果はログを残す
## よくある利用例
- 「この商品の価格を教えて」
- 「滋賀県の日本酒を検索して」
- 「特定のブランドの商品を検索して」
`,
model: google('gemini-2.5-flash'),
tools: {
...stripeTools,
},
...
参考までに、変更点をまとめたのがこちらです。
export const stripeAgent = new Agent({
name: 'Stripe Commerce Agent',
instructions: `
あなたは気の利いたビジネスアシスタントです。Stripe を使った商品検索などを丁寧にサポートしてください。
+ ## 使える主な機能
+ - searchProductsTool: Stripe 上の商品を検索する。ブランド、酒造、種類、都道府県、精米歩合、価格範囲などで検索できます
## 応答ポリシー
1. ユーザーの要望を明確にするために質問することを恐れない
2. 関西の日本酒だけをお勧めしましょう。他の地域のお酒をリクエストされたら、うまいこと関西のお酒に変換してお勧めしましょう。
3. 処理結果はログを残す
## よくある利用例
- 「この商品の価格を教えて」
- 「滋賀県の日本酒を検索して」
- 「特定のブランドの商品を検索して」
`,
model: google('gemini-2.5-flash'),
+ tools: {
+ ...stripeTools,
+ },
memory: stripeMemory,
});
instructionにツールの名前と説明を追加することで、エージェントがより確実にツールを選択してくれるようになる・・・といわれています。実際に利用できるツールの選定や実行などは、tools側への設定が必要となりますので、忘れないように設定しましょう。
3-4: Mastraにエージェントを登録する
最後にフレームワークへ作成したエージェントを登録しましょう。src/mastra/index.tsを開き、
-
stripeAgentのimport -
MastraのagentsにstripeAgentを登録
の2つを実施してください。変更後は次のようなコードになります。
import { Mastra } from '@mastra/core/mastra';
import { PinoLogger } from '@mastra/loggers';
import { weatherAgent } from './agents/weather-agent';
import { stripeAgent } from './agents/stripe-agents';
export const mastra = new Mastra({
agents: { weatherAgent, stripeAgent },
logger: new PinoLogger({
name: 'Mastra',
level: 'info',
}),
});
このステップを実施する前とのDiffはこちらです。
import { Mastra } from '@mastra/core/mastra';
import { PinoLogger } from '@mastra/loggers';
import { weatherAgent } from './agents/weather-agent';
+import { stripeAgent } from './agents/stripe-agents';
export const mastra = new Mastra({
- agents: { weatherAgent },
+ agents: { weatherAgent, stripeAgent },
logger: new PinoLogger({
name: 'Mastra',
level: 'info',
}),
});
動かしてみる
あとは開発サーバーにて動作を試してみましょう。前のステップと同じようにnpm run devでアプリを起動します。
npm run dev
表示されたPlayground: http://localhost:4111/のリンクをクリックします。Playgroundが開いたら、左側のAgentsの中から「Stripe Commerce Agent」をクリックします。
入力欄に「滋賀県の日本酒を探して」などと入力してみましょう。Stripeのデータを利用して商品を取得しているかどうかをチェックしてみましょう。なお、現在はまだ検索機能が実装されていないため、Stripe APIの検索結果は常に一定であることにご注意ください。
TODO
一般公開する時は、Stripe ワークベンチを使ったAPIエラー調査方法も紹介しておく
Step4: レコメンドするための検索クエリを実装する
最後のステップでは、AIが最適な商品を検索できるようにするため、searchProductsToolに商品検索機能を追加しましょう。
Stripe Search API
Stripeには商品や顧客・決済履歴などを検索できるAPIが用意されています。
今回のワークショップでは、metadataにお酒に関するさまざまな情報を商品へ登録したStripeアカウントを事前に用意しました。
そこでSearch APIを利用して、metadataにあるデータをクエリした商品検索を実装しましょう。
https://docs.stripe.com/search?locale=ja-JP
4-1: Search APIを利用するためのクエリビルダー関数を作る
まず検索クエリを作成する関数を作成しましょう。src/mastra/tools/stripe-tools.tsに以下の関数をコピーアンドペーストで追加してください。
// Stripe Query Languageでの検索クエリを構築
function buildQuery(options: {
brand?: string;
brewery?: string;
type?: string;
prefecture?: string;
ricePolishingRate?: string;
}) {
const conditions: string[] = [];
if (options.brand) {
conditions.push(`metadata['brand']:'${options.brand}'`);
}
if (options.brewery) {
conditions.push(`metadata['brewery']:'${options.brewery}'`);
}
if (options.type) {
conditions.push(`metadata['type']:'${options.type}'`);
}
if (options.prefecture) {
conditions.push(`metadata['prefecture']:'${options.prefecture}'`);
}
if (options.ricePolishingRate) {
conditions.push(`metadata['ricePolishingRate']:'${options.ricePolishingRate}'`);
}
return conditions.join(' AND ');
}
今回は検索をシンプルにするため、AND条件のみで検索クエリを作成しています。
4-2: エージェントに利用したいクエリを伝える設定を追加する
次のステップでは、検索クエリをcreateTool()に登録します。src/mastra/tools/stripe-tools.tsを開き、createToolの引数にinputSchemaを追加してください。
export const searchProductsTool = createTool({
id: 'search-products',
description: 'Stripeで商品を検索する。ブランド、酒造、種類、都道府県、精米歩合、価格範囲などで検索できます。',
inputSchema: z.object({
brand: z.string().optional().describe('ブランド名'),
brewery: z.string().optional().describe('酒造名'),
type: z.string().optional().describe('種類(例: 純米吟醸)'),
prefecture: z.string().optional().describe('都道府県'),
ricePolishingRate: z.string().optional().describe('精米歩合'),
limit: z.number().optional().default(10).describe('取得件数(デフォルト: 10)'),
}),
outputSchema: z.object({
追加された部分だけをハイライトすると、次の通りです。
export const searchProductsTool = createTool({
id: 'search-products',
description: 'Stripeで商品を検索する。ブランド、酒造、種類、都道府県、精米歩合、価格範囲などで検索できます。',
+ inputSchema: z.object({
+ brand: z.string().optional().describe('ブランド名'),
+ brewery: z.string().optional().describe('酒造名'),
+ type: z.string().optional().describe('種類(例: 純米吟醸)'),
+ prefecture: z.string().optional().describe('都道府県'),
+ ricePolishingRate: z.string().optional().describe('精米歩合'),
+ limit: z.number().optional().default(10).describe('取得件数(デフォルト: 10)'),
+ }),
outputSchema: z.object({
inputSchemaを登録することで、AIエージェントが「ユーザーからの質問やリクエストから、どんな情報を抽出してツールに渡すべきか?」を考えさせることができます。今回は商品の検索ですので、商品検索に関するスキーマをzod形式で登録しましょう。
4-3: ツールにStripe Search APIを実装する
続いて作成した関数を利用した検索処理をツールへ追加しましょう。
src/mastra/tools/stripe-tools.tsのsearchProductsTool関数を、以下のコードをコピーアンドペーストして置き換えてください。
export const searchProductsTool = createTool({
id: 'search-products',
description: 'Stripeで商品を検索する。ブランド、酒造、種類、都道府県、精米歩合、価格範囲などで検索できます。',
inputSchema: z.object({
brand: z.string().optional().describe('ブランド名'),
brewery: z.string().optional().describe('酒造名'),
type: z.string().optional().describe('種類(例: 純米吟醸)'),
prefecture: z.string().optional().describe('都道府県'),
ricePolishingRate: z.string().optional().describe('精米歩合'),
limit: z.number().optional().default(10).describe('取得件数(デフォルト: 10)'),
}),
outputSchema: z.object({
products: z.array(
z.object({
id: z.string().describe('Product ID'),
name: z.string().describe('商品名'),
description: z.string().nullable().describe('商品説明'),
price: z.number().nullable().describe('価格(円)'),
priceId: z.string().nullable().describe('Price ID'),
metadata: z.record(z.string()).describe('メタデータ'),
})
).describe('検索結果の商品一覧'),
count: z.number().describe('検索結果件数'),
}),
execute: async (context: any) => {
const apiKey = process.env.STRIPE_SECRET_KEY;
if (!apiKey) {
throw new Error('Stripe API キーが設定されていません。環境変数 STRIPE_SECRET_KEY または STRIPE_API_KEY を設定してください。');
}
const stripe = new Stripe(apiKey, {
apiVersion: '2025-10-29.clover',
});
const { brand, brewery, type, prefecture, ricePolishingRate, limit = 10 } = context;
try {
// 検索クエリを構築
const query = buildQuery({ brand, brewery, type, prefecture, ricePolishingRate });
const searchParams = query
? ({
limit: limit,
query: query,
} as Stripe.ProductSearchParams)
: ({
limit: limit,
} as any);
// Product検索(クエリがない場合は list を使用)
const products = query && query.length > 0
? await stripe.products.search(searchParams)
: await stripe.products.list({ limit: limit });
// フィルタリング処理を後で追加する
let filteredProducts: Stripe.Product[] = products.data;
// 結果を整形
const formattedProducts = filteredProducts.map((product: Stripe.Product) => {
const price = product.default_price;
const priceAmount =
typeof price === 'object' && price?.unit_amount ? price.unit_amount : null;
const priceId =
typeof price === 'string' ? price : typeof price === 'object' ? price?.id || null : null;
return {
id: product.id,
name: product.name,
description: product.description,
price: priceAmount,
priceId: priceId,
metadata: product.metadata || {},
};
});
return {
products: formattedProducts,
count: formattedProducts.length,
};
} catch (error) {
if (error instanceof Error) {
throw new Error(`Stripe商品検索エラー: ${error.message}`);
}
throw error;
}
},
});
変更があった部分のDiffはこちら
const stripe = new Stripe(apiKey, {
apiVersion: '2025-10-29.clover',
});
+ const { brand, brewery, type, prefecture, ricePolishingRate, limit = 10 } = context;
try {
// 検索クエリを構築
+ const query = buildQuery({ brand, brewery, type, prefecture, ricePolishingRate });
+ const searchParams = query
+ ? ({
+ limit: limit,
+ query: query,
+ } as Stripe.ProductSearchParams)
+ : ({
+ limit: limit,
+ } as any);
+ // Product検索(クエリがない場合は list を使用)
+ const products = query && query.length > 0
+ ? await stripe.products.search(searchParams)
+ : await stripe.products.list({ limit: limit });
// 価格範囲でフィルタリング
let filteredProducts: Stripe.Product[] = products.data;
4-1にて作成したStripe Search APIへのクエリ作成関数buildQueryを使用する実装に更新しています。buildQueryに渡す各種クエリの値は、executeメソッドの引数から取得します。ここでのデータについては、4-2で定義したinputSchemaに基づいてMastraが作成してくれます。
AIエージェントが検索クエリを適切に生成できなかった場合に備えたFallbackも実装しています。buildQueryで作成したクエリが空だったばあい、Stripe Search APIではなくProductを検索する API を実行します。これはSearch APIがクエリを必須にしているため、エラーを起こさないようにする措置です。
動かしてみる
ここまでの実装が完了したら、Mastraのエージェントテスト画面でアプリを動かしてみましょう。
以下にプロンプトの例を用意してあります。
兵庫県の純米吟醸を探して
平和酒造の日本酒が気になる。
まとめ
このパートでは、Mastraを利用した場合のツールの利用法について体験しました。
生成AIがMastraを通して外部サービス / APIを利用する場合、Toolという仕組みを利用します。この仕組みを利用することで、生成AIがユーザーのリクエストに対して適切なAPIやサービスを自身で判断して利用することができるようになります。
接続先にはStripeを利用します。Stripeが提供する検索APIを活用し、ユーザーの意図にあった情報を取得する仕組みを構築する方法を体験できたかと思います。
また、Stripe上のデータを利用・検索するには、metadataに情報を保存し、Search APIを利用することで柔軟な検索についても実現できます。
ぜひMastra / Stripe API活用などについて、今回のワークショップを元にお試しください。
Next challenge
- [Easy] Stripe MCPを利用した決済リンク(Payment Links)の発行
- [Normal] Toolをアップデートして、Stripe Checkoutによる決済セッション / URLの発行
- [Hard] Stripe APIを駆使し、2回目以降の注文を効率化する仕組みを構築する
- [Very hard] ACP(Agent Commerce Procotol)への挑戦






