This is page 2 of 7. Use http://codebase.md/stripe/agent-toolkit?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .github
│ ├── ISSUE_TEMPLATE
│ │ ├── bug_report.yml
│ │ ├── config.yml
│ │ └── feature_request.yml
│ └── workflows
│ ├── main.yml
│ ├── npm_release_shared.yml
│ ├── pypi_release.yml
│ └── sync-skills.yml
├── .gitignore
├── .vscode
│ ├── extensions.json
│ ├── launch.json
│ └── settings.json
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── gemini-extension.json
├── LICENSE
├── llm
│ ├── ai-sdk
│ │ ├── jest.config.ts
│ │ ├── LICENSE
│ │ ├── meter
│ │ │ ├── examples
│ │ │ │ ├── .env.example
│ │ │ │ ├── .gitignore
│ │ │ │ ├── anthropic.ts
│ │ │ │ ├── google.ts
│ │ │ │ ├── openai.ts
│ │ │ │ ├── README.md
│ │ │ │ └── tsconfig.json
│ │ │ ├── index.ts
│ │ │ ├── meter-event-logging.ts
│ │ │ ├── meter-event-types.ts
│ │ │ ├── README.md
│ │ │ ├── tests
│ │ │ │ ├── ai-sdk-billing-wrapper-anthropic.test.ts
│ │ │ │ ├── ai-sdk-billing-wrapper-general.test.ts
│ │ │ │ ├── ai-sdk-billing-wrapper-google.test.ts
│ │ │ │ ├── ai-sdk-billing-wrapper-openai.test.ts
│ │ │ │ ├── ai-sdk-billing-wrapper-other-providers.test.ts
│ │ │ │ ├── meter-event-logging.test.ts
│ │ │ │ └── model-name-normalization.test.ts
│ │ │ ├── tsconfig.json
│ │ │ ├── types.ts
│ │ │ ├── utils.ts
│ │ │ └── wrapperV2.ts
│ │ ├── package.json
│ │ ├── pnpm-lock.yaml
│ │ ├── provider
│ │ │ ├── examples
│ │ │ │ ├── .env.example
│ │ │ │ ├── .gitignore
│ │ │ │ ├── anthropic.ts
│ │ │ │ ├── google.ts
│ │ │ │ ├── openai.ts
│ │ │ │ ├── README.md
│ │ │ │ └── tsconfig.json
│ │ │ ├── index.ts
│ │ │ ├── README.md
│ │ │ ├── stripe-language-model.ts
│ │ │ ├── stripe-provider.ts
│ │ │ ├── tests
│ │ │ │ ├── stripe-language-model.test.ts
│ │ │ │ ├── stripe-provider.test.ts
│ │ │ │ └── utils.test.ts
│ │ │ ├── tsconfig.build.json
│ │ │ ├── tsconfig.json
│ │ │ ├── types.ts
│ │ │ └── utils.ts
│ │ ├── README.md
│ │ ├── tsconfig.json
│ │ └── tsup.config.ts
│ ├── README.md
│ └── token-meter
│ ├── examples
│ │ ├── anthropic.ts
│ │ ├── gemini.ts
│ │ └── openai.ts
│ ├── index.ts
│ ├── jest.config.ts
│ ├── LICENSE
│ ├── meter-event-logging.ts
│ ├── meter-event-types.ts
│ ├── package.json
│ ├── pnpm-lock.yaml
│ ├── README.md
│ ├── tests
│ │ ├── meter-event-logging.test.ts
│ │ ├── model-name-normalization.test.ts
│ │ ├── token-meter-anthropic.test.ts
│ │ ├── token-meter-gemini.test.ts
│ │ ├── token-meter-general.test.ts
│ │ ├── token-meter-openai.test.ts
│ │ └── type-detection.test.ts
│ ├── token-meter.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ ├── types.ts
│ └── utils
│ └── type-detection.ts
├── README.md
├── SECURITY.md
├── skills
│ ├── get-started-kiro.md
│ ├── README.md
│ ├── stripe-best-practices.md
│ └── sync.js
└── tools
├── modelcontextprotocol
│ ├── .dxtignore
│ ├── .gitignore
│ ├── .node-version
│ ├── .prettierrc
│ ├── build-dxt.js
│ ├── Dockerfile
│ ├── eslint.config.mjs
│ ├── jest.config.ts
│ ├── LICENSE
│ ├── manifest.json
│ ├── package.json
│ ├── pnpm-lock.yaml
│ ├── README.md
│ ├── server.json
│ ├── src
│ │ ├── index.ts
│ │ └── test
│ │ └── index.test.ts
│ ├── stripe_icon.png
│ └── tsconfig.json
├── python
│ ├── .editorconfig
│ ├── .flake8
│ ├── examples
│ │ ├── crewai
│ │ │ ├── .env.template
│ │ │ ├── main.py
│ │ │ └── README.md
│ │ ├── langchain
│ │ │ ├── __init__.py
│ │ │ ├── .env.template
│ │ │ ├── main.py
│ │ │ └── README.md
│ │ ├── openai
│ │ │ ├── .env.template
│ │ │ ├── customer_support
│ │ │ │ ├── .env.template
│ │ │ │ ├── emailer.py
│ │ │ │ ├── env.py
│ │ │ │ ├── main.py
│ │ │ │ ├── pyproject.toml
│ │ │ │ ├── README.md
│ │ │ │ ├── repl.py
│ │ │ │ └── support_agent.py
│ │ │ ├── file_search
│ │ │ │ ├── main.py
│ │ │ │ └── README.md
│ │ │ └── web_search
│ │ │ ├── .env.template
│ │ │ ├── main.py
│ │ │ └── README.md
│ │ └── strands
│ │ └── main.py
│ ├── Makefile
│ ├── pyproject.toml
│ ├── README.md
│ ├── requirements.txt
│ ├── stripe_agent_toolkit
│ │ ├── __init__.py
│ │ ├── api.py
│ │ ├── configuration.py
│ │ ├── crewai
│ │ │ ├── tool.py
│ │ │ └── toolkit.py
│ │ ├── functions.py
│ │ ├── langchain
│ │ │ ├── tool.py
│ │ │ └── toolkit.py
│ │ ├── openai
│ │ │ ├── hooks.py
│ │ │ ├── tool.py
│ │ │ └── toolkit.py
│ │ ├── prompts.py
│ │ ├── schema.py
│ │ ├── strands
│ │ │ ├── __init__.py
│ │ │ ├── hooks.py
│ │ │ ├── tool.py
│ │ │ └── toolkit.py
│ │ └── tools.py
│ └── tests
│ ├── __init__.py
│ ├── test_configuration.py
│ └── test_functions.py
├── README.md
└── typescript
├── .gitignore
├── .prettierrc
├── eslint.config.mjs
├── examples
│ ├── ai-sdk
│ │ ├── .env.template
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── README.md
│ │ └── tsconfig.json
│ ├── cloudflare
│ │ ├── .dev.vars.example
│ │ ├── .gitignore
│ │ ├── biome.json
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── app.ts
│ │ │ ├── imageGenerator.ts
│ │ │ ├── index.ts
│ │ │ ├── oauth.ts
│ │ │ └── utils.ts
│ │ ├── tsconfig.json
│ │ ├── worker-configuration.d.ts
│ │ └── wrangler.jsonc
│ ├── langchain
│ │ ├── .env.template
│ │ ├── index.ts
│ │ ├── package.json
│ │ ├── README.md
│ │ └── tsconfig.json
│ └── openai
│ ├── .env.template
│ ├── index.ts
│ ├── package.json
│ ├── README.md
│ └── tsconfig.json
├── jest.config.ts
├── LICENSE
├── package.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── README.md
├── src
│ ├── ai-sdk
│ │ ├── index.ts
│ │ ├── tool.ts
│ │ └── toolkit.ts
│ ├── cloudflare
│ │ ├── index.ts
│ │ └── README.md
│ ├── langchain
│ │ ├── index.ts
│ │ ├── tool.ts
│ │ └── toolkit.ts
│ ├── modelcontextprotocol
│ │ ├── index.ts
│ │ ├── README.md
│ │ ├── register-paid-tool.ts
│ │ └── toolkit.ts
│ ├── openai
│ │ ├── index.ts
│ │ └── toolkit.ts
│ ├── shared
│ │ ├── api.ts
│ │ ├── balance
│ │ │ └── retrieveBalance.ts
│ │ ├── configuration.ts
│ │ ├── coupons
│ │ │ ├── createCoupon.ts
│ │ │ └── listCoupons.ts
│ │ ├── customers
│ │ │ ├── createCustomer.ts
│ │ │ └── listCustomers.ts
│ │ ├── disputes
│ │ │ ├── listDisputes.ts
│ │ │ └── updateDispute.ts
│ │ ├── documentation
│ │ │ └── searchDocumentation.ts
│ │ ├── invoiceItems
│ │ │ └── createInvoiceItem.ts
│ │ ├── invoices
│ │ │ ├── createInvoice.ts
│ │ │ ├── finalizeInvoice.ts
│ │ │ └── listInvoices.ts
│ │ ├── paymentIntents
│ │ │ └── listPaymentIntents.ts
│ │ ├── paymentLinks
│ │ │ └── createPaymentLink.ts
│ │ ├── prices
│ │ │ ├── createPrice.ts
│ │ │ └── listPrices.ts
│ │ ├── products
│ │ │ ├── createProduct.ts
│ │ │ └── listProducts.ts
│ │ ├── refunds
│ │ │ └── createRefund.ts
│ │ ├── subscriptions
│ │ │ ├── cancelSubscription.ts
│ │ │ ├── listSubscriptions.ts
│ │ │ └── updateSubscription.ts
│ │ └── tools.ts
│ └── test
│ ├── modelcontextprotocol
│ │ └── register-paid-tool.test.ts
│ └── shared
│ ├── balance
│ │ ├── functions.test.ts
│ │ └── parameters.test.ts
│ ├── configuration.test.ts
│ ├── customers
│ │ ├── functions.test.ts
│ │ └── parameters.test.ts
│ ├── disputes
│ │ └── functions.test.ts
│ ├── documentation
│ │ ├── functions.test.ts
│ │ └── parameters.test.ts
│ ├── invoiceItems
│ │ ├── functions.test.ts
│ │ ├── parameters.test.ts
│ │ └── prompts.test.ts
│ ├── invoices
│ │ ├── functions.test.ts
│ │ ├── parameters.test.ts
│ │ └── prompts.test.ts
│ ├── paymentIntents
│ │ ├── functions.test.ts
│ │ ├── parameters.test.ts
│ │ └── prompts.test.ts
│ ├── paymentLinks
│ │ ├── functions.test.ts
│ │ ├── parameters.test.ts
│ │ └── prompts.test.ts
│ ├── prices
│ │ ├── functions.test.ts
│ │ └── parameters.test.ts
│ ├── products
│ │ ├── functions.test.ts
│ │ └── parameters.test.ts
│ ├── refunds
│ │ ├── functions.test.ts
│ │ └── parameters.test.ts
│ └── subscriptions
│ ├── functions.test.ts
│ ├── parameters.test.ts
│ └── prompts.test.ts
├── tsconfig.json
└── tsup.config.ts
```
# Files
--------------------------------------------------------------------------------
/tools/typescript/examples/langchain/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {StripeAgentToolkit} from '@stripe/agent-toolkit/langchain';
2 | import {ChatOpenAI} from '@langchain/openai';
3 | import type {ChatPromptTemplate} from '@langchain/core/prompts';
4 | import {pull} from 'langchain/hub';
5 | import {AgentExecutor, createStructuredChatAgent} from 'langchain/agents';
6 |
7 | require('dotenv').config();
8 |
9 | const llm = new ChatOpenAI({
10 | model: 'gpt-4o',
11 | });
12 |
13 | const stripeAgentToolkit = new StripeAgentToolkit({
14 | secretKey: process.env.STRIPE_SECRET_KEY!,
15 | configuration: {
16 | actions: {
17 | paymentLinks: {
18 | create: true,
19 | },
20 | products: {
21 | create: true,
22 | },
23 | prices: {
24 | create: true,
25 | },
26 | },
27 | },
28 | });
29 |
30 | (async (): Promise<void> => {
31 | const prompt = await pull<ChatPromptTemplate>(
32 | 'hwchase17/structured-chat-agent'
33 | );
34 |
35 | const tools = stripeAgentToolkit.getTools();
36 |
37 | const agent = await createStructuredChatAgent({
38 | llm,
39 | tools,
40 | prompt,
41 | });
42 |
43 | const agentExecutor = new AgentExecutor({
44 | agent,
45 | tools,
46 | });
47 |
48 | const response = await agentExecutor.invoke({
49 | input: `
50 | Create a payment link for a new product called 'test' with a price
51 | of $100. Come up with a funny description about buy bots,
52 | maybe a haiku.
53 | `,
54 | });
55 |
56 | console.log(response);
57 | })();
58 |
```
--------------------------------------------------------------------------------
/tools/python/stripe_agent_toolkit/openai/tool.py:
--------------------------------------------------------------------------------
```python
1 | """
2 | This tool allows agents to interact with the Stripe API.
3 | """
4 |
5 | from __future__ import annotations
6 |
7 | from collections.abc import Awaitable
8 | from typing import Any
9 | import json
10 |
11 | from agents import FunctionTool
12 | from agents.run_context import RunContextWrapper
13 |
14 | def StripeTool(api, tool) -> FunctionTool:
15 | async def on_invoke_tool(ctx: RunContextWrapper[Any], input_str: str) -> str:
16 | return api.run(tool["method"], **json.loads(input_str))
17 |
18 | parameters = tool["args_schema"].model_json_schema()
19 | parameters["additionalProperties"] = False
20 | parameters["type"] = "object"
21 |
22 | # Remove the description field from parameters as it's not needed in the OpenAI function schema
23 | if "description" in parameters:
24 | del parameters["description"]
25 |
26 | if "title" in parameters:
27 | del parameters["title"]
28 |
29 | # Remove title and default fields from properties
30 | if "properties" in parameters:
31 | for prop in parameters["properties"].values():
32 | if "title" in prop:
33 | del prop["title"]
34 | if "default" in prop:
35 | del prop["default"]
36 |
37 | return FunctionTool(
38 | name=tool["method"],
39 | description=tool["description"],
40 | params_json_schema=parameters,
41 | on_invoke_tool=on_invoke_tool,
42 | strict_json_schema=False
43 | )
44 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/test/shared/products/parameters.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {createProductParameters} from '@/shared/products/createProduct';
2 | import {listProductsParameters} from '@/shared/products/listProducts';
3 |
4 | describe('createProductParameters', () => {
5 | it('should return the correct parameters if no context', () => {
6 | const parameters = createProductParameters({});
7 |
8 | const fields = Object.keys(parameters.shape);
9 | expect(fields).toEqual(['name', 'description']);
10 | expect(fields.length).toBe(2);
11 | });
12 |
13 | it('should return the correct parameters if customer is specified', () => {
14 | const parameters = createProductParameters({customer: 'cus_123'});
15 |
16 | const fields = Object.keys(parameters.shape);
17 | expect(fields).toEqual(['name', 'description']);
18 | expect(fields.length).toBe(2);
19 | });
20 | });
21 |
22 | describe('listProductsParameters', () => {
23 | it('should return the correct parameters if no context', () => {
24 | const parameters = listProductsParameters({});
25 |
26 | const fields = Object.keys(parameters.shape);
27 | expect(fields).toEqual(['limit']);
28 | expect(fields.length).toBe(1);
29 | });
30 |
31 | it('should return the correct parameters if customer is specified', () => {
32 | const parameters = listProductsParameters({customer: 'cus_123'});
33 |
34 | const fields = Object.keys(parameters.shape);
35 | expect(fields).toEqual(['limit']);
36 | expect(fields.length).toBe(1);
37 | });
38 | });
39 |
```
--------------------------------------------------------------------------------
/tools/python/stripe_agent_toolkit/openai/toolkit.py:
--------------------------------------------------------------------------------
```python
1 | """Stripe Agent Toolkit."""
2 |
3 | from typing import List, Optional
4 | from pydantic import PrivateAttr
5 | import json
6 |
7 | from agents import FunctionTool
8 |
9 |
10 | from ..api import StripeAPI
11 | from ..tools import tools
12 | from ..configuration import Configuration, is_tool_allowed
13 | from .tool import StripeTool
14 | from .hooks import BillingHooks
15 |
16 | class StripeAgentToolkit:
17 | _tools: List[FunctionTool] = PrivateAttr(default=[])
18 | _stripe_api: StripeAPI = PrivateAttr(default=None)
19 |
20 | def __init__(
21 | self, secret_key: str, configuration: Optional[Configuration] = None
22 | ):
23 | super().__init__()
24 |
25 | context = configuration.get("context") if configuration else None
26 |
27 | self._stripe_api = StripeAPI(secret_key=secret_key, context=context)
28 |
29 | filtered_tools = [
30 | tool for tool in tools if is_tool_allowed(tool, configuration)
31 | ]
32 |
33 | self._tools = [
34 | StripeTool(self._stripe_api, tool)
35 | for tool in filtered_tools
36 | ]
37 |
38 | def get_tools(self) -> List[FunctionTool]:
39 | """Get the tools in the toolkit."""
40 | return self._tools
41 |
42 | def billing_hook(self, type: Optional[str] = None, customer: Optional[str] = None, meter: Optional[str] = None, meters: Optional[dict[str, str]] = None) -> BillingHooks:
43 | return BillingHooks(self._stripe_api, type, customer, meter, meters)
44 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/shared/balance/retrieveBalance.ts:
--------------------------------------------------------------------------------
```typescript
1 | import Stripe from 'stripe';
2 | import {z} from 'zod';
3 | import type {Context} from '@/shared/configuration';
4 | import type {StripeToolDefinition} from '@/shared/tools';
5 |
6 | export const retrieveBalancePrompt = (_context: Context = {}) => `
7 | This tool will retrieve the balance from Stripe. It takes no input.
8 | `;
9 |
10 | export const retrieveBalanceParameters = (
11 | _context: Context = {}
12 | ): z.AnyZodObject => z.object({});
13 |
14 | export const retrieveBalanceAnnotations = () => ({
15 | destructiveHint: false,
16 | idempotentHint: true,
17 | openWorldHint: true,
18 | readOnlyHint: true,
19 | title: 'Retrieve balance',
20 | });
21 |
22 | export const retrieveBalance = async (
23 | stripe: Stripe,
24 | context: Context,
25 | params: z.infer<ReturnType<typeof retrieveBalanceParameters>>
26 | ) => {
27 | try {
28 | const balance = await stripe.balance.retrieve(
29 | params,
30 | context.account ? {stripeAccount: context.account} : undefined
31 | );
32 |
33 | return balance;
34 | } catch (error) {
35 | return 'Failed to retrieve balance';
36 | }
37 | };
38 |
39 | const tool = (context: Context): StripeToolDefinition => ({
40 | method: 'retrieve_balance',
41 | name: 'Retrieve Balance',
42 | description: retrieveBalancePrompt(context),
43 | inputSchema: retrieveBalanceParameters(context),
44 | annotations: retrieveBalanceAnnotations(),
45 | actions: {
46 | balance: {
47 | read: true,
48 | },
49 | },
50 | execute: retrieveBalance,
51 | });
52 |
53 | export default tool;
54 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/test/shared/prices/parameters.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {createPriceParameters} from '@/shared/prices/createPrice';
2 | import {listPricesParameters} from '@/shared/prices/listPrices';
3 |
4 | describe('createPriceParameters', () => {
5 | it('should return the correct parameters if no context', () => {
6 | const parameters = createPriceParameters({});
7 |
8 | const fields = Object.keys(parameters.shape);
9 | expect(fields).toEqual(['product', 'unit_amount', 'currency']);
10 | expect(fields.length).toBe(3);
11 | });
12 |
13 | it('should return the correct parameters if customer is specified', () => {
14 | const parameters = createPriceParameters({customer: 'cus_123'});
15 |
16 | const fields = Object.keys(parameters.shape);
17 | expect(fields).toEqual(['product', 'unit_amount', 'currency']);
18 | expect(fields.length).toBe(3);
19 | });
20 | });
21 |
22 | describe('listPricesParameters', () => {
23 | it('should return the correct parameters if no context', () => {
24 | const parameters = listPricesParameters({});
25 |
26 | const fields = Object.keys(parameters.shape);
27 | expect(fields).toEqual(['product', 'limit']);
28 | expect(fields.length).toBe(2);
29 | });
30 |
31 | it('should return the correct parameters if customer is specified', () => {
32 | const parameters = listPricesParameters({customer: 'cus_123'});
33 |
34 | const fields = Object.keys(parameters.shape);
35 | expect(fields).toEqual(['product', 'limit']);
36 | expect(fields.length).toBe(2);
37 | });
38 | });
39 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/test/shared/invoices/prompts.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {createInvoicePrompt} from '@/shared/invoices/createInvoice';
2 | import {listInvoicesPrompt} from '@/shared/invoices/listInvoices';
3 | import {finalizeInvoicePrompt} from '@/shared/invoices/finalizeInvoice';
4 |
5 | describe('createInvoicePrompt', () => {
6 | it('should return the correct prompt when no customer is specified', () => {
7 | const prompt = createInvoicePrompt({});
8 | expect(prompt).toContain('- customer (str)');
9 | });
10 |
11 | it('should return the correct prompt when a customer is specified', () => {
12 | const prompt = createInvoicePrompt({customer: 'cus_123'});
13 | expect(prompt).toContain('context: cus_123');
14 | expect(prompt).not.toContain('- customer (str)');
15 | });
16 | });
17 |
18 | describe('listInvoicesPrompt', () => {
19 | it('should return the correct prompt when no customer is specified', () => {
20 | const prompt = listInvoicesPrompt({});
21 | expect(prompt).toContain('- customer (str, optional)');
22 | });
23 |
24 | it('should return the correct prompt when a customer is specified', () => {
25 | const prompt = listInvoicesPrompt({customer: 'cus_123'});
26 | expect(prompt).toContain('context: cus_123');
27 | expect(prompt).not.toContain('- customer (str, optional)');
28 | });
29 | });
30 |
31 | describe('finalizeInvoicePrompt', () => {
32 | it('should return the correct prompt', () => {
33 | const prompt = finalizeInvoicePrompt();
34 | expect(prompt).toContain('invoice');
35 | });
36 | });
37 |
```
--------------------------------------------------------------------------------
/tools/python/examples/openai/customer_support/support_agent.py:
--------------------------------------------------------------------------------
```python
1 | import env
2 | from agents import Agent, Runner, function_tool, TResponseInputItem, RunResult
3 | from stripe_agent_toolkit.openai.toolkit import StripeAgentToolkit
4 | import requests
5 |
6 | env.ensure("OPENAI_API_KEY")
7 |
8 | stripe_agent_toolkit = StripeAgentToolkit(
9 | secret_key=env.ensure("STRIPE_SECRET_KEY"),
10 | configuration={
11 | "actions": {
12 | "customers": {
13 | "read": True,
14 | },
15 | "invoices": {
16 | "read": True,
17 | },
18 | "billing_portal_sessions": {
19 | "create": True,
20 | },
21 | }
22 | },
23 | )
24 |
25 |
26 | @function_tool
27 | def search_faq(question: str) -> str:
28 | response = requests.get("https://standupjack.com/faq")
29 | if response.status_code != 200:
30 | return "Not sure"
31 | return f"Given the following context:\n{response.text}\n\nAnswer '{question}' or response with not sure\n"
32 |
33 |
34 | support_agent = Agent(
35 | name="Standup Jack Agent",
36 | instructions=(
37 | "You are a helpful customer support assistant"
38 | "Be casual and concise"
39 | "You only respond with markdown"
40 | "Use tools to support customers"
41 | "Respond with I'm not sure to any other prompts"
42 | "Sign off with Standup Jack Bot"
43 | ),
44 | tools=[search_faq, *stripe_agent_toolkit.get_tools()],
45 | )
46 |
47 |
48 | async def run(input: list[TResponseInputItem]) -> RunResult:
49 | return await Runner.run(support_agent, input)
50 |
```
--------------------------------------------------------------------------------
/tools/typescript/examples/openai/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {StripeAgentToolkit} from '@stripe/agent-toolkit/openai';
2 | import OpenAI from 'openai';
3 | import type {ChatCompletionMessageParam} from 'openai/resources';
4 |
5 | require('dotenv').config();
6 |
7 | const openai = new OpenAI();
8 |
9 | const stripeAgentToolkit = new StripeAgentToolkit({
10 | secretKey: process.env.STRIPE_SECRET_KEY!,
11 | configuration: {
12 | actions: {
13 | paymentLinks: {
14 | create: true,
15 | },
16 | products: {
17 | create: true,
18 | },
19 | prices: {
20 | create: true,
21 | },
22 | },
23 | },
24 | });
25 |
26 | (async (): Promise<void> => {
27 | let messages: ChatCompletionMessageParam[] = [
28 | {
29 | role: 'user',
30 | content: `Create a payment link for a new product called 'test' with a price
31 | of $100. Come up with a funny description about buy bots,
32 | maybe a haiku.`,
33 | },
34 | ];
35 |
36 | while (true) {
37 | // eslint-disable-next-line no-await-in-loop
38 | const completion = await openai.chat.completions.create({
39 | model: 'gpt-4o',
40 | messages,
41 | tools: stripeAgentToolkit.getTools(),
42 | });
43 |
44 | const message = completion.choices[0].message;
45 |
46 | messages.push(message);
47 |
48 | if (message.tool_calls) {
49 | // eslint-disable-next-line no-await-in-loop
50 | const toolMessages = await Promise.all(
51 | message.tool_calls.map((tc) => stripeAgentToolkit.handleToolCall(tc))
52 | );
53 | messages = [...messages, ...toolMessages];
54 | } else {
55 | console.log(completion.choices[0].message);
56 | break;
57 | }
58 | }
59 | })();
60 |
```
--------------------------------------------------------------------------------
/.github/workflows/pypi_release.yml:
--------------------------------------------------------------------------------
```yaml
1 | name: Python Release
2 |
3 | on:
4 | workflow_dispatch: {}
5 |
6 | jobs:
7 | python-build:
8 | name: Build for PyPi
9 | runs-on: ubuntu-latest
10 | environment: pypi
11 | permissions:
12 | contents: read
13 |
14 | defaults:
15 | run:
16 | working-directory: ./tools/python
17 |
18 | steps:
19 | - name: Checkout
20 | uses: actions/checkout@v4
21 |
22 | - name: Python
23 | uses: actions/setup-python@v4
24 | with:
25 | python-version: "3.11"
26 |
27 | - name: Install uv
28 | uses: astral-sh/setup-uv@v2
29 |
30 | - name: Install
31 | run: make venv
32 |
33 | - name: Build
34 | run: |
35 | set -x
36 | source .venv/bin/activate
37 | rm -rf build dist *.egg-info
38 | make build
39 | python -m twine check dist/*
40 |
41 | - name: Test
42 | run: |
43 | make venv
44 | make test
45 |
46 | - name: Upload artifact
47 | uses: actions/upload-artifact@v4
48 | with:
49 | name: release-dists
50 | path: ./tools/python/dist/
51 |
52 | python-release:
53 | name: Publish to PyPi
54 | runs-on: ubuntu-latest
55 | environment: pypi
56 | needs:
57 | - python-build
58 |
59 | defaults:
60 | run:
61 | working-directory: ./tools/python
62 |
63 | permissions:
64 | id-token: write
65 |
66 | steps:
67 | - name: Retrieve distribution
68 | uses: actions/download-artifact@v4
69 | with:
70 | name: release-dists
71 | path: dist/
72 |
73 | - name: Publish package distributions to PyPI
74 | uses: pypa/gh-action-pypi-publish@release/v1
75 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/test/shared/customers/parameters.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {createCustomerParameters} from '@/shared/customers/createCustomer';
2 | import {listCustomersParameters} from '@/shared/customers/listCustomers';
3 |
4 | describe('createCustomerParameters', () => {
5 | it('should return the correct parameters if no context', () => {
6 | // Create the parameters schema with an empty context
7 | const parameters = createCustomerParameters({});
8 |
9 | // Validate that the schema has the expected keys
10 | const fields = Object.keys(parameters.shape);
11 | expect(fields).toEqual(['name', 'email']);
12 | expect(fields.length).toBe(2);
13 | });
14 |
15 | it('should return the correct parameters if customer is specified', () => {
16 | const parameters = createCustomerParameters({customer: 'cus_123'});
17 |
18 | const fields = Object.keys(parameters.shape);
19 | expect(fields).toEqual(['name', 'email']);
20 | expect(fields.length).toBe(2);
21 | });
22 | });
23 |
24 | describe('listCustomersParameters', () => {
25 | it('should return the correct parameters if no context', () => {
26 | const parameters = listCustomersParameters({});
27 |
28 | const fields = Object.keys(parameters.shape);
29 | expect(fields).toEqual(['limit', 'email']);
30 | expect(fields.length).toBe(2);
31 | });
32 |
33 | it('should return the correct parameters if customer is specified', () => {
34 | const parameters = listCustomersParameters({customer: 'cus_123'});
35 |
36 | const fields = Object.keys(parameters.shape);
37 | expect(fields).toEqual(['limit', 'email']);
38 | expect(fields.length).toBe(2);
39 | });
40 | });
41 |
```
--------------------------------------------------------------------------------
/llm/ai-sdk/tsup.config.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {defineConfig} from 'tsup';
2 |
3 | export default defineConfig([
4 | // Provider build
5 | {
6 | entry: {
7 | 'provider/index': 'provider/index.ts',
8 | 'provider/stripe-provider': 'provider/stripe-provider.ts',
9 | 'provider/stripe-language-model': 'provider/stripe-language-model.ts',
10 | 'provider/utils': 'provider/utils.ts',
11 | 'provider/types': 'provider/types.ts',
12 | },
13 | outDir: 'dist',
14 | format: ['cjs', 'esm'],
15 | dts: true,
16 | sourcemap: true,
17 | clean: true,
18 | splitting: false,
19 | external: ['@ai-sdk/provider', '@ai-sdk/provider-utils', 'stripe', 'zod'],
20 | platform: 'node',
21 | target: 'es2022',
22 | tsconfig: 'provider/tsconfig.build.json',
23 | outExtension({format}) {
24 | return {
25 | js: format === 'cjs' ? '.js' : '.mjs',
26 | };
27 | },
28 | },
29 | // Meter build
30 | {
31 | entry: {
32 | 'meter/index': 'meter/index.ts',
33 | 'meter/wrapperV2': 'meter/wrapperV2.ts',
34 | 'meter/meter-event-logging': 'meter/meter-event-logging.ts',
35 | 'meter/meter-event-types': 'meter/meter-event-types.ts',
36 | 'meter/types': 'meter/types.ts',
37 | 'meter/utils': 'meter/utils.ts',
38 | },
39 | outDir: 'dist',
40 | format: ['cjs', 'esm'],
41 | dts: true,
42 | sourcemap: true,
43 | splitting: false,
44 | external: ['@ai-sdk/provider', 'stripe'],
45 | platform: 'node',
46 | target: 'es2022',
47 | tsconfig: 'meter/tsconfig.json',
48 | outExtension({format}) {
49 | return {
50 | js: format === 'cjs' ? '.js' : '.mjs',
51 | };
52 | },
53 | },
54 | ]);
55 |
56 |
```
--------------------------------------------------------------------------------
/tools/python/examples/crewai/main.py:
--------------------------------------------------------------------------------
```python
1 | import os
2 | from dotenv import load_dotenv
3 |
4 | from crewai import Agent, Task, Crew
5 | from stripe_agent_toolkit.crewai.toolkit import StripeAgentToolkit
6 |
7 | load_dotenv()
8 |
9 | stripe_agent_toolkit = StripeAgentToolkit(
10 | secret_key=os.getenv("STRIPE_SECRET_KEY"),
11 | configuration={
12 | "actions": {
13 | "payment_links": {
14 | "create": True,
15 | },
16 | "products": {
17 | "create": True,
18 | },
19 | "prices": {
20 | "create": True,
21 | },
22 | }
23 | },
24 | )
25 |
26 | stripe_agent = Agent(
27 | role="Stripe Agent",
28 | goal="Integrate with Stripe effectively to support our business.",
29 | backstory="You have been using stripe forever.",
30 | tools=[*stripe_agent_toolkit.get_tools()],
31 | allow_delegation=False,
32 | verbose=True,
33 | )
34 |
35 | haiku_writer = Agent(
36 | role="Haiku writer",
37 | goal="Write a haiku",
38 | backstory="You are really good at writing haikus.",
39 | allow_delegation=False,
40 | verbose=True,
41 | )
42 |
43 | create_payment_link = Task(
44 | description="Create a payment link for a new product called 'test' "
45 | "with a price of $100. The description should be a haiku",
46 | expected_output="url",
47 | agent=stripe_agent,
48 | )
49 |
50 | write_haiku = Task(
51 | description="Write a haiku about buy bots.",
52 | expected_output="haiku",
53 | agent=haiku_writer,
54 | )
55 |
56 | crew = Crew(
57 | agents=[stripe_agent, haiku_writer],
58 | tasks=[create_payment_link, write_haiku],
59 | verbose=True,
60 | planning=True,
61 | )
62 |
63 | crew.kickoff()
64 |
```
--------------------------------------------------------------------------------
/.github/workflows/sync-skills.yml:
--------------------------------------------------------------------------------
```yaml
1 | name: Sync MCP prompts to skills folder
2 |
3 | on:
4 | # schedule:
5 | # # Run daily at 00:00 UTC
6 | # - cron: '0 0 * * *'
7 | workflow_dispatch: # Allow manual triggering
8 |
9 | permissions:
10 | contents: write
11 | pull-requests: write
12 |
13 | jobs:
14 | sync:
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - name: Checkout repository
19 | uses: actions/checkout@v4
20 |
21 | - name: Setup Node.js
22 | uses: actions/setup-node@v4
23 | with:
24 | node-version: '20'
25 |
26 | - name: Run sync script
27 | env:
28 | MCP_STRIPE_API_KEY: ${{ secrets.MCP_STRIPE_API_KEY }}
29 | run: node skills/sync.js
30 |
31 | # This action will only create a PR if there are actual changes to the file
32 | # If the content is identical, no PR will be created
33 | - name: Create Pull Request
34 | id: cpr
35 | uses: peter-evans/create-pull-request@v6
36 | with:
37 | commit-message: 'sync mcp.stripe.comp prompts to skills'
38 | title: '[automated] sync mcp.stripe.comp prompts to skills'
39 | body: |
40 | This is an automated PR created by the sync-skills workflow.
41 | branch: sync-skills-update
42 | delete-branch: true
43 | add-paths: |
44 | skills/stripe-best-practices.md
45 |
46 | - name: Check outputs
47 | if: steps.cpr.outputs.pull-request-number
48 | run: |
49 | echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
50 | echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"
51 |
52 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/modelcontextprotocol/toolkit.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js';
2 | import {RequestHandlerExtra} from '@modelcontextprotocol/sdk/shared/protocol.js';
3 | import {Configuration, isToolAllowed} from '../shared/configuration';
4 | import StripeAPI from '../shared/api';
5 | import tools from '../shared/tools';
6 |
7 | class StripeAgentToolkit extends McpServer {
8 | private _stripe: StripeAPI;
9 |
10 | constructor({
11 | secretKey,
12 | configuration,
13 | }: {
14 | secretKey: string;
15 | configuration: Configuration;
16 | }) {
17 | super({
18 | name: 'Stripe',
19 | version: '0.4.0',
20 | configuration: {
21 | ...configuration,
22 | context: {
23 | ...configuration.context,
24 | mode: 'modelcontextprotocol',
25 | },
26 | },
27 | });
28 |
29 | this._stripe = new StripeAPI(secretKey, configuration.context);
30 |
31 | const context = configuration.context || {};
32 | const filteredTools = tools(context).filter((tool) =>
33 | isToolAllowed(tool, configuration)
34 | );
35 |
36 | filteredTools.forEach((tool) => {
37 | this.tool(
38 | tool.method,
39 | tool.description,
40 | tool.inputSchema.shape,
41 | tool.annotations,
42 | async (arg: any, _extra: RequestHandlerExtra<any, any>) => {
43 | const result = await this._stripe.run(tool.method, arg);
44 | return {
45 | content: [
46 | {
47 | type: 'text' as const,
48 | text: String(result),
49 | },
50 | ],
51 | };
52 | }
53 | );
54 | });
55 | }
56 | }
57 |
58 | export default StripeAgentToolkit;
59 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/shared/api.ts:
--------------------------------------------------------------------------------
```typescript
1 | import Stripe from 'stripe';
2 |
3 | import type {Context} from './configuration';
4 | import tools, {StripeToolDefinition} from './tools';
5 |
6 | const TOOLKIT_HEADER = 'stripe-agent-toolkit-typescript';
7 | const MCP_HEADER = 'stripe-mcp';
8 |
9 | class StripeAPI {
10 | stripe: Stripe;
11 |
12 | context: Context;
13 |
14 | tools: StripeToolDefinition[];
15 |
16 | constructor(secretKey: string, context?: Context) {
17 | const stripeClient = new Stripe(secretKey, {
18 | appInfo: {
19 | name:
20 | context?.mode === 'modelcontextprotocol'
21 | ? MCP_HEADER
22 | : TOOLKIT_HEADER,
23 | version: '0.8.1',
24 | url: 'https://github.com/stripe/ai',
25 | },
26 | });
27 | this.stripe = stripeClient;
28 | this.context = context || {};
29 | this.tools = tools(this.context);
30 | }
31 |
32 | async createMeterEvent({
33 | event,
34 | customer,
35 | value,
36 | }: {
37 | event: string;
38 | customer: string;
39 | value: string;
40 | }) {
41 | await this.stripe.billing.meterEvents.create(
42 | {
43 | event_name: event,
44 | payload: {
45 | stripe_customer_id: customer,
46 | value: value,
47 | },
48 | },
49 | this.context.account ? {stripeAccount: this.context.account} : undefined
50 | );
51 | }
52 |
53 | async run(method: string, arg: any) {
54 | const tool = this.tools.find((t) => t.method === method);
55 | if (tool) {
56 | const output = JSON.stringify(
57 | await tool.execute(this.stripe, this.context, arg)
58 | );
59 | return output;
60 | } else {
61 | throw new Error('Invalid method ' + method);
62 | }
63 | }
64 | }
65 |
66 | export default StripeAPI;
67 |
```
--------------------------------------------------------------------------------
/llm/token-meter/package.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "@stripe/token-meter",
3 | "version": "0.1.0",
4 | "description": "Generic token metering for AI SDKs with Stripe integration",
5 | "main": "./dist/index.js",
6 | "types": "./dist/index.d.ts",
7 | "exports": {
8 | ".": {
9 | "types": "./dist/index.d.ts",
10 | "import": "./dist/index.js"
11 | }
12 | },
13 | "scripts": {
14 | "build": "tsc --project tsconfig.build.json",
15 | "clean": "rm -rf dist",
16 | "test": "jest",
17 | "test:watch": "jest --watch",
18 | "test:coverage": "jest --coverage",
19 | "prepublishOnly": "npm run build"
20 | },
21 | "keywords": [
22 | "stripe",
23 | "ai",
24 | "billing",
25 | "tokens",
26 | "metering"
27 | ],
28 | "author": "Stripe <[email protected]> (https://stripe.com/)",
29 | "license": "MIT",
30 | "engines": {
31 | "node": ">=18"
32 | },
33 | "files": [
34 | "dist/**/*",
35 | "LICENSE",
36 | "README.md",
37 | "package.json"
38 | ],
39 | "devDependencies": {
40 | "@anthropic-ai/sdk": "^0.67.0",
41 | "@google/generative-ai": "^0.24.1",
42 | "@types/jest": "^30.0.0",
43 | "@types/node": "^22.10.5",
44 | "dotenv": "^17.2.3",
45 | "jest": "^30.2.0",
46 | "openai": "^6.6.0",
47 | "ts-jest": "^29.4.5",
48 | "ts-node": "^10.9.2",
49 | "typescript": "^5.8.3"
50 | },
51 | "dependencies": {
52 | "stripe": "^17.5.0"
53 | },
54 | "peerDependencies": {
55 | "@anthropic-ai/sdk": "^0.32.1",
56 | "@google/generative-ai": "^0.21.0",
57 | "openai": "^4.86.1"
58 | },
59 | "peerDependenciesMeta": {
60 | "@anthropic-ai/sdk": {
61 | "optional": true
62 | },
63 | "@google/generative-ai": {
64 | "optional": true
65 | },
66 | "openai": {
67 | "optional": true
68 | }
69 | }
70 | }
71 |
```
--------------------------------------------------------------------------------
/tools/python/tests/test_configuration.py:
--------------------------------------------------------------------------------
```python
1 | import unittest
2 | from stripe_agent_toolkit.configuration import is_tool_allowed
3 |
4 |
5 | class TestConfigurations(unittest.TestCase):
6 | def test_allowed(self):
7 | tool = {
8 | "actions": {
9 | "customers": {"create": True, "read": True},
10 | "invoices": {"create": True, "read": True},
11 | }
12 | }
13 |
14 | configuration = {
15 | "actions": {
16 | "customers": {"create": True, "read": True},
17 | "invoices": {"create": True, "read": True},
18 | }
19 | }
20 |
21 | self.assertTrue(is_tool_allowed(tool, configuration))
22 |
23 | def test_partial_allowed(self):
24 | tool = {
25 | "actions": {
26 | "customers": {"create": True, "read": True},
27 | "invoices": {"create": True, "read": True},
28 | }
29 | }
30 |
31 | configuration = {
32 | "actions": {
33 | "customers": {"create": True, "read": True},
34 | "invoices": {"create": True, "read": False},
35 | }
36 | }
37 |
38 | self.assertFalse(is_tool_allowed(tool, configuration))
39 |
40 | def test_not_allowed(self):
41 | tool = {
42 | "actions": {
43 | "payment_links": {"create": True},
44 | }
45 | }
46 |
47 | configuration = {
48 | "actions": {
49 | "customers": {"create": True, "read": True},
50 | "invoices": {"create": True, "read": True},
51 | }
52 | }
53 |
54 | self.assertFalse(is_tool_allowed(tool, configuration))
55 |
56 |
57 | if __name__ == "__main__":
58 | unittest.main()
59 |
```
--------------------------------------------------------------------------------
/tools/modelcontextprotocol/build-dxt.js:
--------------------------------------------------------------------------------
```javascript
1 | /* eslint-disable no-sync */
2 | const esbuild = require('esbuild');
3 | const fsSync = require('fs');
4 |
5 | const dxt = require('@anthropic-ai/dxt');
6 |
7 | // Ensure dist directory exists
8 | if (!fsSync.existsSync('dxt-dist')) {
9 | fsSync.mkdirSync('dxt-dist');
10 | }
11 |
12 | // Build configuration
13 | const buildConfig = {
14 | entryPoints: ['src/index.ts'],
15 | bundle: true,
16 | outfile: 'dxt-dist/index.js',
17 | platform: 'node',
18 | target: 'node18',
19 | format: 'cjs',
20 | external: [],
21 | minify: true,
22 | sourcemap: false,
23 | metafile: false,
24 | write: true,
25 | logLevel: 'info',
26 | };
27 |
28 | async function build() {
29 | try {
30 | console.log('🔨 Building with esbuild...');
31 |
32 | const result = await esbuild.build(buildConfig);
33 |
34 | if (result.errors.length > 0) {
35 | console.error('❌ Build failed with errors:');
36 | result.errors.forEach((error) => console.error(error));
37 | throw new Error('Build failed with errors');
38 | }
39 |
40 | if (result.warnings.length > 0) {
41 | console.warn('⚠️ Build completed with warnings:');
42 | result.warnings.forEach((warning) => console.warn(warning));
43 | }
44 |
45 | // Make the output file executable
46 | fsSync.chmodSync('dxt-dist/index.js', '755');
47 |
48 | console.log('✅ Build completed successfully!');
49 | console.log('📦 Output: dxt-dist/index.js');
50 | } catch (error) {
51 | console.error('❌ Build failed:', error);
52 | throw error;
53 | }
54 | }
55 |
56 | // Run the build
57 | build();
58 |
59 | // Pack the actual DXT extension
60 | dxt.packExtension({
61 | extensionPath: '.',
62 | outputPath: 'stripe.dxt',
63 | silent: true,
64 | });
65 |
66 | console.log('✅ DXT extension built successfully!');
67 | console.log('📦 Output: stripe.dxt');
68 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/shared/products/createProduct.ts:
--------------------------------------------------------------------------------
```typescript
1 | import Stripe from 'stripe';
2 | import {z} from 'zod';
3 | import type {Context} from '@/shared/configuration';
4 | import type {StripeToolDefinition} from '@/shared/tools';
5 | export const createProductPrompt = (_context: Context = {}) => `
6 | This tool will create a product in Stripe.
7 |
8 | It takes two arguments:
9 | - name (str): The name of the product.
10 | - description (str, optional): The description of the product.
11 | `;
12 |
13 | export const createProduct = async (
14 | stripe: Stripe,
15 | context: Context,
16 | params: z.infer<ReturnType<typeof createProductParameters>>
17 | ) => {
18 | try {
19 | const product = await stripe.products.create(
20 | params,
21 | context.account ? {stripeAccount: context.account} : undefined
22 | );
23 |
24 | return product;
25 | } catch (error) {
26 | return 'Failed to create product';
27 | }
28 | };
29 |
30 | export const createProductParameters = (_context: Context = {}) =>
31 | z.object({
32 | name: z.string().describe('The name of the product.'),
33 | description: z
34 | .string()
35 | .optional()
36 | .describe('The description of the product.'),
37 | });
38 |
39 | export const createProductAnnotations = () => ({
40 | destructiveHint: false,
41 | idempotentHint: false,
42 | openWorldHint: true,
43 | readOnlyHint: false,
44 | title: 'Create product',
45 | });
46 |
47 | const tool = (context: Context): StripeToolDefinition => ({
48 | method: 'create_product',
49 | name: 'Create Product',
50 | description: createProductPrompt(context),
51 | inputSchema: createProductParameters(context),
52 | annotations: createProductAnnotations(),
53 | actions: {
54 | products: {
55 | create: true,
56 | },
57 | },
58 | execute: createProduct,
59 | });
60 |
61 | export default tool;
62 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/shared/customers/createCustomer.ts:
--------------------------------------------------------------------------------
```typescript
1 | import Stripe from 'stripe';
2 | import {z} from 'zod';
3 | import type {Context} from '@/shared/configuration';
4 | import type {StripeToolDefinition} from '@/shared/tools';
5 |
6 | export const createCustomerPrompt = (_context: Context = {}) => `
7 | This tool will create a customer in Stripe.
8 |
9 | It takes two arguments:
10 | - name (str): The name of the customer.
11 | - email (str, optional): The email of the customer.
12 | `;
13 |
14 | export const createCustomerParameters = (
15 | _context: Context = {}
16 | ): z.AnyZodObject =>
17 | z.object({
18 | name: z.string().describe('The name of the customer'),
19 | email: z.string().email().optional().describe('The email of the customer'),
20 | });
21 |
22 | export const createCustomerAnnotations = () => ({
23 | destructiveHint: false,
24 | idempotentHint: false,
25 | openWorldHint: true,
26 | readOnlyHint: false,
27 | title: 'Create customer',
28 | });
29 |
30 | export const createCustomer = async (
31 | stripe: Stripe,
32 | context: Context,
33 | params: z.infer<ReturnType<typeof createCustomerParameters>>
34 | ) => {
35 | try {
36 | const customer = await stripe.customers.create(
37 | params,
38 | context.account ? {stripeAccount: context.account} : undefined
39 | );
40 |
41 | return {id: customer.id};
42 | } catch (error) {
43 | return 'Failed to create customer';
44 | }
45 | };
46 |
47 | const tool = (context: Context): StripeToolDefinition => ({
48 | method: 'create_customer',
49 | name: 'Create Customer',
50 | description: createCustomerPrompt(context),
51 | inputSchema: createCustomerParameters(context),
52 | annotations: createCustomerAnnotations(),
53 | actions: {
54 | customers: {
55 | create: true,
56 | },
57 | },
58 | execute: createCustomer,
59 | });
60 |
61 | export default tool;
62 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/shared/invoices/finalizeInvoice.ts:
--------------------------------------------------------------------------------
```typescript
1 | import Stripe from 'stripe';
2 | import {z} from 'zod';
3 | import type {Context} from '@/shared/configuration';
4 | import type {StripeToolDefinition} from '@/shared/tools';
5 |
6 | export const finalizeInvoiceParameters = (
7 | _context: Context = {}
8 | ): z.AnyZodObject =>
9 | z.object({
10 | invoice: z.string().describe('The ID of the invoice to finalize.'),
11 | });
12 |
13 | export const finalizeInvoicePrompt = (_context: Context = {}) => `
14 | This tool will finalize an invoice in Stripe.
15 |
16 | It takes one argument:
17 | - invoice (str): The ID of the invoice to finalize.
18 | `;
19 |
20 | export const finalizeInvoice = async (
21 | stripe: Stripe,
22 | context: Context,
23 | params: z.infer<ReturnType<typeof finalizeInvoiceParameters>>
24 | ) => {
25 | try {
26 | const invoice = await stripe.invoices.finalizeInvoice(
27 | params.invoice,
28 | context.account ? {stripeAccount: context.account} : undefined
29 | );
30 |
31 | return {
32 | id: invoice.id,
33 | url: invoice.hosted_invoice_url,
34 | customer: invoice.customer,
35 | status: invoice.status,
36 | };
37 | } catch (error) {
38 | return 'Failed to finalize invoice';
39 | }
40 | };
41 |
42 | export const finalizeInvoiceAnnotations = () => ({
43 | destructiveHint: false,
44 | idempotentHint: true,
45 | openWorldHint: true,
46 | readOnlyHint: false,
47 | title: 'Finalize invoice',
48 | });
49 |
50 | const tool = (context: Context): StripeToolDefinition => ({
51 | method: 'finalize_invoice',
52 | name: 'Finalize Invoice',
53 | description: finalizeInvoicePrompt(context),
54 | inputSchema: finalizeInvoiceParameters(context),
55 | annotations: finalizeInvoiceAnnotations(),
56 | actions: {
57 | invoices: {
58 | update: true,
59 | },
60 | },
61 | execute: finalizeInvoice,
62 | });
63 |
64 | export default tool;
65 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/shared/products/listProducts.ts:
--------------------------------------------------------------------------------
```typescript
1 | import Stripe from 'stripe';
2 | import {z} from 'zod';
3 | import type {Context} from '@/shared/configuration';
4 | import type {StripeToolDefinition} from '@/shared/tools';
5 |
6 | export const listProductsPrompt = (_context: Context = {}) => `
7 | This tool will fetch a list of Products from Stripe.
8 |
9 | It takes one optional argument:
10 | - limit (int, optional): The number of products to return.
11 | `;
12 |
13 | export const listProducts = async (
14 | stripe: Stripe,
15 | context: Context,
16 | params: z.infer<ReturnType<typeof listProductsParameters>>
17 | ) => {
18 | try {
19 | const products = await stripe.products.list(
20 | params,
21 | context.account ? {stripeAccount: context.account} : undefined
22 | );
23 |
24 | return products.data;
25 | } catch (error) {
26 | return 'Failed to list products';
27 | }
28 | };
29 |
30 | export const listProductsParameters = (
31 | _context: Context = {}
32 | ): z.AnyZodObject =>
33 | z.object({
34 | limit: z
35 | .number()
36 | .int()
37 | .min(1)
38 | .max(100)
39 | .optional()
40 | .describe(
41 | 'A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 10.'
42 | ),
43 | });
44 |
45 | export const listProductsAnnotations = () => ({
46 | destructiveHint: false,
47 | idempotentHint: true,
48 | openWorldHint: true,
49 | readOnlyHint: true,
50 | title: 'List products',
51 | });
52 |
53 | const tool = (context: Context): StripeToolDefinition => ({
54 | method: 'list_products',
55 | name: 'List Products',
56 | description: listProductsPrompt(context),
57 | inputSchema: listProductsParameters(context),
58 | annotations: listProductsAnnotations(),
59 | actions: {
60 | products: {
61 | read: true,
62 | },
63 | },
64 | execute: listProducts,
65 | });
66 |
67 | export default tool;
68 |
```
--------------------------------------------------------------------------------
/tools/python/stripe_agent_toolkit/configuration.py:
--------------------------------------------------------------------------------
```python
1 | from typing import Literal, Optional
2 | from typing_extensions import TypedDict
3 |
4 | # Define Object type
5 | Object = Literal[
6 | "customers",
7 | "invoices",
8 | "invoiceItems",
9 | "paymentLinks",
10 | "products",
11 | "prices",
12 | "balance",
13 | "refunds",
14 | "paymentIntents",
15 | ]
16 |
17 |
18 | # Define Permission type
19 | class Permission(TypedDict, total=False):
20 | create: Optional[bool]
21 | update: Optional[bool]
22 | read: Optional[bool]
23 |
24 |
25 | # Define BalancePermission type
26 | class BalancePermission(TypedDict, total=False):
27 | read: Optional[bool]
28 |
29 |
30 | # Define Actions type
31 | class Actions(TypedDict, total=False):
32 | customers: Optional[Permission]
33 | invoices: Optional[Permission]
34 | invoice_items: Optional[Permission]
35 | payment_links: Optional[Permission]
36 | products: Optional[Permission]
37 | prices: Optional[Permission]
38 | balance: Optional[BalancePermission]
39 | refunds: Optional[Permission]
40 | payment_intents: Optional[Permission]
41 | billing_portal_sessions: Optional[Permission]
42 |
43 |
44 | # Define Context type
45 | class Context(TypedDict, total=False):
46 | account: Optional[str]
47 |
48 |
49 | # Define Configuration type
50 | class Configuration(TypedDict, total=False):
51 | actions: Optional[Actions]
52 | context: Optional[Context]
53 |
54 |
55 | def is_tool_allowed(tool, configuration):
56 | for resource, permissions in tool.get("actions", {}).items():
57 | if resource not in configuration.get("actions", {}):
58 | return False
59 | for permission in permissions:
60 | if (
61 | not configuration["actions"]
62 | .get(resource, {})
63 | .get(permission, False)
64 | ):
65 | return False
66 | return True
67 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/cloudflare/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {z, ZodRawShape} from 'zod';
2 | import {ToolCallback} from '@modelcontextprotocol/sdk/server/mcp.js';
3 | import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js';
4 | import {registerPaidTool} from '../modelcontextprotocol/register-paid-tool';
5 | import type {PaidToolOptions} from '../modelcontextprotocol/register-paid-tool';
6 |
7 | // @ts-ignore: The current file is a CommonJS module whose imports will produce 'require' calls; however, the referenced file is an ECMAScript module and cannot be imported with 'require'.
8 | import {McpAgent} from 'agents/mcp';
9 |
10 | type Env = any;
11 |
12 | type StripeState = {
13 | customerId: string;
14 | };
15 |
16 | export type PaymentState = {
17 | stripe?: StripeState;
18 | };
19 |
20 | export type PaymentProps = {
21 | userEmail: string;
22 | };
23 |
24 | // eslint-disable-next-line @typescript-eslint/naming-convention
25 | export abstract class experimental_PaidMcpAgent<
26 | Bindings extends Env,
27 | State extends PaymentState,
28 | Props extends PaymentProps,
29 | > extends McpAgent<Bindings, State, Props> {
30 | paidTool<Args extends ZodRawShape>(
31 | toolName: string,
32 | toolDescription: string,
33 | paramsSchema: Args,
34 | // @ts-ignore
35 | paidCallback: ToolCallback<Args>,
36 | options: Omit<PaidToolOptions, 'userEmail' | 'stripeSecretKey'>
37 | ) {
38 | const mcpServer: McpServer = this.server as unknown as McpServer;
39 |
40 | const userEmail = this.props.userEmail;
41 |
42 | const updatedOptions = {
43 | ...options,
44 | userEmail,
45 | // @ts-ignore
46 | stripeSecretKey: this.env.STRIPE_SECRET_KEY,
47 | };
48 |
49 | // @ts-ignore
50 | registerPaidTool(
51 | mcpServer,
52 | toolName,
53 | toolDescription,
54 | paramsSchema,
55 | paidCallback,
56 | updatedOptions
57 | );
58 | }
59 | }
60 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/shared/subscriptions/cancelSubscription.ts:
--------------------------------------------------------------------------------
```typescript
1 | import Stripe from 'stripe';
2 | import {z} from 'zod';
3 | import type {Context} from '@/shared/configuration';
4 | import type {StripeToolDefinition} from '@/shared/tools';
5 |
6 | export const cancelSubscription = async (
7 | stripe: Stripe,
8 | context: Context,
9 | params: z.infer<ReturnType<typeof cancelSubscriptionParameters>>
10 | ) => {
11 | try {
12 | const {subscription: subscriptionId, ...cancelParams} = params;
13 |
14 | const subscription = await stripe.subscriptions.cancel(
15 | subscriptionId,
16 | cancelParams,
17 | context.account ? {stripeAccount: context.account} : undefined
18 | );
19 |
20 | return subscription;
21 | } catch (error) {
22 | return 'Failed to cancel subscription';
23 | }
24 | };
25 |
26 | export const cancelSubscriptionParameters = (
27 | _context: Context = {}
28 | ): z.AnyZodObject => {
29 | return z.object({
30 | subscription: z.string().describe('The ID of the subscription to cancel.'),
31 | });
32 | };
33 | export const cancelSubscriptionPrompt = (_context: Context = {}): string => {
34 | return `
35 | This tool will cancel a subscription in Stripe.
36 |
37 | It takes the following arguments:
38 | - subscription (str, required): The ID of the subscription to cancel.
39 | `;
40 | };
41 |
42 | export const cancelSubscriptionAnnotations = () => ({
43 | destructiveHint: true,
44 | idempotentHint: true,
45 | openWorldHint: true,
46 | readOnlyHint: false,
47 | title: 'Cancel subscription',
48 | });
49 |
50 | const tool = (context: Context): StripeToolDefinition => ({
51 | method: 'cancel_subscription',
52 | name: 'Cancel Subscription',
53 | description: cancelSubscriptionPrompt(context),
54 | inputSchema: cancelSubscriptionParameters(context),
55 | annotations: cancelSubscriptionAnnotations(),
56 | actions: {
57 | subscriptions: {
58 | update: true,
59 | },
60 | },
61 | execute: cancelSubscription,
62 | });
63 |
64 | export default tool;
65 |
```
--------------------------------------------------------------------------------
/llm/ai-sdk/meter/utils.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Utility functions for AI SDK metering
3 | */
4 |
5 | import type {LanguageModelV2StreamPart} from '@ai-sdk/provider';
6 | import type {Provider} from './meter-event-types';
7 |
8 | /**
9 | * Determines the provider type from a given model provider string.
10 | * Normalizes common provider names for consistency in Stripe meter events.
11 | *
12 | * For unknown providers, returns the lowercased provider string as-is.
13 | */
14 | export function determineProvider(providerString: string): Provider {
15 | const normalized = providerString.toLowerCase();
16 |
17 | // Normalize common provider names for consistency
18 | if (normalized.includes('azure')) return 'azure';
19 | if (normalized.includes('amazon_bedrock') || normalized.includes('bedrock'))
20 | return 'bedrock';
21 | if (normalized.includes('huggingface')) return 'huggingface';
22 | if (normalized.includes('together')) return 'together';
23 | if (normalized.includes('anthropic')) return 'anthropic';
24 | if (normalized.includes('google') || normalized.includes('gemini'))
25 | return 'google';
26 | if (normalized.includes('groq')) return 'groq';
27 | if (normalized.includes('openai')) return 'openai';
28 |
29 | // For any other provider, return the lowercased provider name
30 | return normalized;
31 | }
32 |
33 | /**
34 | * Processes stream chunks to extract usage information
35 | */
36 | export function extractUsageFromStream(
37 | chunks: LanguageModelV2StreamPart[]
38 | ): {inputTokens: number; outputTokens: number} {
39 | let inputTokens = 0;
40 | let outputTokens = 0;
41 |
42 | for (const chunk of chunks) {
43 | if (chunk.type === 'finish' && chunk.usage) {
44 | inputTokens = chunk.usage.inputTokens ?? 0;
45 | outputTokens = chunk.usage.outputTokens ?? 0;
46 | break; // Usage is typically in the final chunk
47 | }
48 | }
49 |
50 | return {inputTokens, outputTokens};
51 | }
52 |
53 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/test/shared/refunds/functions.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {createRefund} from '@/shared/refunds/createRefund';
2 |
3 | const Stripe = jest.fn().mockImplementation(() => ({
4 | refunds: {
5 | create: jest.fn(),
6 | },
7 | }));
8 |
9 | let stripe: ReturnType<typeof Stripe>;
10 |
11 | beforeEach(() => {
12 | stripe = new Stripe('fake-api-key');
13 | });
14 |
15 | describe('createRefund', () => {
16 | it('should create a refund and return it', async () => {
17 | const params = {
18 | payment_intent: 'pi_123456',
19 | };
20 |
21 | const mockRefund = {id: 're_123456'};
22 |
23 | const context = {};
24 |
25 | stripe.refunds.create.mockResolvedValue(mockRefund);
26 |
27 | const result = await createRefund(stripe, context, params);
28 |
29 | expect(stripe.refunds.create).toHaveBeenCalledWith(params, undefined);
30 | expect(result).toEqual(mockRefund);
31 | });
32 |
33 | it('should create a partial refund and return it', async () => {
34 | const params = {
35 | payment_intent: 'pi_123456',
36 | amount: 500,
37 | };
38 |
39 | const mockRefund = {id: 're_123456'};
40 |
41 | const context = {};
42 |
43 | stripe.refunds.create.mockResolvedValue(mockRefund);
44 |
45 | const result = await createRefund(stripe, context, params);
46 |
47 | expect(stripe.refunds.create).toHaveBeenCalledWith(params, undefined);
48 | expect(result).toEqual(mockRefund);
49 | });
50 |
51 | it('should specify the connected account if included in context', async () => {
52 | const params = {
53 | payment_intent: 'pi_123456',
54 | };
55 |
56 | const mockRefund = {id: 're_123456'};
57 |
58 | const context = {
59 | account: 'acct_123456',
60 | };
61 |
62 | stripe.refunds.create.mockResolvedValue(mockRefund);
63 |
64 | const result = await createRefund(stripe, context, params);
65 |
66 | expect(stripe.refunds.create).toHaveBeenCalledWith(params, {
67 | stripeAccount: context.account,
68 | });
69 | expect(result).toEqual(mockRefund);
70 | });
71 | });
72 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/shared/coupons/listCoupons.ts:
--------------------------------------------------------------------------------
```typescript
1 | import Stripe from 'stripe';
2 | import {z} from 'zod';
3 | import type {Context} from '@/shared/configuration';
4 | import type {StripeToolDefinition} from '@/shared/tools';
5 |
6 | export const listCouponsPrompt = (_context: Context = {}) => `
7 | This tool will fetch a list of Coupons from Stripe.
8 |
9 | It takes one optional argument:
10 | - limit (int, optional): The number of coupons to return.
11 | `;
12 |
13 | export const listCouponsParameters = (_context: Context = {}) =>
14 | z.object({
15 | limit: z
16 | .number()
17 | .int()
18 | .min(1)
19 | .max(100)
20 | .optional()
21 | .describe(
22 | 'A limit on the number of objects to be returned. Limit can range between 1 and 100.'
23 | ),
24 | });
25 |
26 | export const listCouponsAnnotations = () => ({
27 | destructiveHint: false,
28 | idempotentHint: true,
29 | openWorldHint: true,
30 | readOnlyHint: true,
31 | title: 'List coupons',
32 | });
33 |
34 | export const listCoupons = async (
35 | stripe: Stripe,
36 | context: Context,
37 | params: z.infer<ReturnType<typeof listCouponsParameters>>
38 | ) => {
39 | try {
40 | const coupons = await stripe.coupons.list(
41 | params,
42 | context.account ? {stripeAccount: context.account} : undefined
43 | );
44 |
45 | return coupons.data.map((coupon) => ({
46 | id: coupon.id,
47 | name: coupon.name,
48 | percent_off: coupon.percent_off,
49 | amount_off: coupon.amount_off,
50 | duration: coupon.duration,
51 | }));
52 | } catch (error) {
53 | return 'Failed to list coupons';
54 | }
55 | };
56 |
57 | const tool = (context: Context): StripeToolDefinition => ({
58 | method: 'list_coupons',
59 | name: 'List Coupons',
60 | description: listCouponsPrompt(context),
61 | inputSchema: listCouponsParameters(context),
62 | annotations: listCouponsAnnotations(),
63 | actions: {
64 | coupons: {
65 | read: true,
66 | },
67 | },
68 | execute: listCoupons,
69 | });
70 |
71 | export default tool;
72 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/shared/prices/listPrices.ts:
--------------------------------------------------------------------------------
```typescript
1 | import Stripe from 'stripe';
2 | import {z} from 'zod';
3 | import type {Context} from '@/shared/configuration';
4 | import type {StripeToolDefinition} from '@/shared/tools';
5 |
6 | export const listPrices = async (
7 | stripe: Stripe,
8 | context: Context,
9 | params: z.infer<ReturnType<typeof listPricesParameters>>
10 | ) => {
11 | try {
12 | const prices = await stripe.prices.list(
13 | params,
14 | context.account ? {stripeAccount: context.account} : undefined
15 | );
16 |
17 | return prices.data;
18 | } catch (error) {
19 | return 'Failed to list prices';
20 | }
21 | };
22 |
23 | export const listPricesParameters = (_context: Context = {}): z.AnyZodObject =>
24 | z.object({
25 | product: z
26 | .string()
27 | .optional()
28 | .describe('The ID of the product to list prices for.'),
29 | limit: z
30 | .number()
31 | .int()
32 | .min(1)
33 | .max(100)
34 | .optional()
35 | .describe(
36 | 'A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 10.'
37 | ),
38 | });
39 |
40 | export const listPricesAnnotations = () => ({
41 | destructiveHint: false,
42 | idempotentHint: true,
43 | openWorldHint: true,
44 | readOnlyHint: true,
45 | title: 'List prices',
46 | });
47 |
48 | export const listPricesPrompt = (_context: Context = {}) => `
49 | This tool will fetch a list of Prices from Stripe.
50 |
51 | It takes two arguments.
52 | - product (str, optional): The ID of the product to list prices for.
53 | - limit (int, optional): The number of prices to return.
54 | `;
55 |
56 | const tool = (context: Context): StripeToolDefinition => ({
57 | method: 'list_prices',
58 | name: 'List Prices',
59 | description: listPricesPrompt(context),
60 | inputSchema: listPricesParameters(context),
61 | annotations: listPricesAnnotations(),
62 | actions: {
63 | prices: {
64 | read: true,
65 | },
66 | },
67 | execute: listPrices,
68 | });
69 |
70 | export default tool;
71 |
```
--------------------------------------------------------------------------------
/skills/sync.js:
--------------------------------------------------------------------------------
```javascript
1 | const fs = require("fs").promises;
2 | const path = require("path");
3 |
4 | const STRIPE_API_KEY = process.env.MCP_STRIPE_API_KEY;
5 |
6 | if (!STRIPE_API_KEY) {
7 | throw new Error("MCP_STRIPE_API_KEY environment variable is required");
8 | }
9 |
10 | const getMCPPrompt = async (promptName) => {
11 | const response = await fetch("https://mcp.stripe.com", {
12 | method: "POST",
13 | headers: {
14 | "Content-Type": "application/json",
15 | Authorization: `Bearer ${STRIPE_API_KEY}`,
16 | "User-Agent": "github.com/stripe/ai/skills",
17 | },
18 | body: JSON.stringify({
19 | jsonrpc: "2.0",
20 | method: "prompts/get",
21 | params: {
22 | name: "stripe-best-practices",
23 | arguments: {},
24 | },
25 | id: 1,
26 | }),
27 | });
28 | const data = await response.json();
29 | return data.result.messages[0].content.text;
30 | };
31 |
32 | const listMCPPrompts = async () => {
33 | const response = await fetch("https://mcp.stripe.com", {
34 | method: "POST",
35 | headers: {
36 | "Content-Type": "application/json",
37 | Authorization: `Bearer ${STRIPE_API_KEY}`,
38 | "User-Agent": "github.com/stripe/ai/skills",
39 | },
40 | body: JSON.stringify({
41 | jsonrpc: "2.0",
42 | method: "prompts/list",
43 | params: {},
44 | id: 1,
45 | }),
46 | });
47 | const data = await response.json();
48 | return data.result.prompts;
49 | };
50 |
51 | const run = async () => {
52 | const prompts = await listMCPPrompts();
53 | console.log(`Found ${prompts.length} prompts`);
54 | for (const prompt of prompts) {
55 | const content = await getMCPPrompt(prompt.name);
56 | const outputPath = path.join(__dirname, `${prompt.name}.md`);
57 |
58 | const skillFileContent = `---
59 | description: ${prompt.description}
60 | alwaysApply: false
61 | ---
62 |
63 | ${content}
64 | `;
65 |
66 | await fs.writeFile(outputPath, skillFileContent, "utf8");
67 | console.log(`Content written to ${outputPath}`);
68 | }
69 | };
70 |
71 | run();
72 |
```
--------------------------------------------------------------------------------
/tools/python/stripe_agent_toolkit/strands/tool.py:
--------------------------------------------------------------------------------
```python
1 | from strands.tools.tools import PythonAgentTool as StrandTool
2 | import json
3 |
4 | def StripeTool(api, tool) -> StrandTool:
5 | parameters = tool["args_schema"].model_json_schema()
6 | parameters["additionalProperties"] = False
7 | parameters["type"] = "object"
8 |
9 | def callback_wrapper(tool_input, **kwargs):
10 | """Wrapper to handle additional parameters from strands framework."""
11 |
12 | # Extract toolUseId for the response
13 | tool_use_id = None
14 | if isinstance(tool_input, dict) and 'toolUseId' in tool_input:
15 | tool_use_id = tool_input['toolUseId']
16 | # Extract the actual parameters from the nested input structure
17 | actual_params = tool_input.get('input', {})
18 | elif isinstance(tool_input, str):
19 | # Parse JSON string input
20 | try:
21 | parsed = json.loads(tool_input)
22 | tool_use_id = parsed.get('toolUseId')
23 | actual_params = parsed.get('input', parsed)
24 | except json.JSONDecodeError:
25 | actual_params = {}
26 | elif isinstance(tool_input, dict):
27 | actual_params = tool_input.copy()
28 | else:
29 | actual_params = {}
30 |
31 | # Call the Stripe API
32 | result = api.run(tool["method"], **actual_params)
33 |
34 | # Return in the format expected by strands
35 | response = {
36 | "content": [{"text": result}]
37 | }
38 |
39 | if tool_use_id:
40 | response["toolUseId"] = tool_use_id
41 |
42 | return response
43 |
44 | return StrandTool(
45 | tool_name=tool["method"],
46 | tool_spec={
47 | "name": tool["method"],
48 | "description": tool["description"],
49 | "inputSchema": {
50 | "json": parameters
51 | }
52 | },
53 | callback=callback_wrapper
54 | )
55 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/shared/refunds/createRefund.ts:
--------------------------------------------------------------------------------
```typescript
1 | import Stripe from 'stripe';
2 | import {z} from 'zod';
3 | import type {Context} from '@/shared/configuration';
4 | import type {StripeToolDefinition} from '@/shared/tools';
5 |
6 | export const createRefundPrompt = (_context: Context = {}) => `
7 | This tool will refund a payment intent in Stripe.
8 |
9 | It takes three arguments:
10 | - payment_intent (str): The ID of the payment intent to refund.
11 | - amount (int, optional): The amount to refund in cents.
12 | - reason (str, optional): The reason for the refund.
13 | `;
14 |
15 | export const createRefund = async (
16 | stripe: Stripe,
17 | context: Context,
18 | params: z.infer<ReturnType<typeof createRefundParameters>>
19 | ) => {
20 | try {
21 | const refund = await stripe.refunds.create(
22 | params,
23 | context.account ? {stripeAccount: context.account} : undefined
24 | );
25 |
26 | return {
27 | id: refund.id,
28 | status: refund.status,
29 | amount: refund.amount,
30 | };
31 | } catch (error) {
32 | return 'Failed to create refund';
33 | }
34 | };
35 |
36 | export const createRefundParameters = (
37 | _context: Context = {}
38 | ): z.AnyZodObject =>
39 | z.object({
40 | payment_intent: z
41 | .string()
42 | .describe('The ID of the PaymentIntent to refund.'),
43 | amount: z
44 | .number()
45 | .int()
46 | .optional()
47 | .describe('The amount to refund in cents.'),
48 | });
49 |
50 | export const createRefundAnnotations = () => ({
51 | destructiveHint: false,
52 | idempotentHint: false,
53 | openWorldHint: true,
54 | readOnlyHint: false,
55 | title: 'Create refund',
56 | });
57 |
58 | const tool = (context: Context): StripeToolDefinition => ({
59 | method: 'create_refund',
60 | name: 'Create Refund',
61 | description: createRefundPrompt(context),
62 | inputSchema: createRefundParameters(context),
63 | annotations: createRefundAnnotations(),
64 | actions: {
65 | refunds: {
66 | create: true,
67 | },
68 | },
69 | execute: createRefund,
70 | });
71 |
72 | export default tool;
73 |
```
--------------------------------------------------------------------------------
/tools/modelcontextprotocol/manifest.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "dxt_version": "0.1",
3 | "name": "@stripe/mcp",
4 | "display_name": "Stripe",
5 | "version": "0.1.0",
6 | "description": "Manage resources in your Stripe account and search the Stripe knowledge base.",
7 | "author": {
8 | "name": "Stripe",
9 | "email": "[email protected]"
10 | },
11 | "documentation": "https://docs.stripe.com/mcp",
12 | "user_config": {
13 | "stripe_secret_key": {
14 | "type": "string",
15 | "title": "Stripe key",
16 | "description": "Your Stripe API key. We recommend using a restricted access key.",
17 | "multiple": false,
18 | "required": true
19 | }
20 | },
21 | "server": {
22 | "type": "node",
23 | "entry_point": "dxt-dist/index.js",
24 | "mcp_config": {
25 | "command": "node",
26 | "args": ["${__dirname}/dxt-dist/index.js", "--tools=all"],
27 | "env": {
28 | "STRIPE_SECRET_KEY": "${user_config.stripe_secret_key}"
29 | }
30 | }
31 | },
32 | "tools": [
33 | {"name": "search_documentation"},
34 | {"name": "get_stripe_account_in"},
35 | {"name": "create_customer"},
36 | {"name": "list_customers"},
37 | {"name": "create_product"},
38 | {"name": "list_products"},
39 | {"name": "create_price"},
40 | {"name": "list_prices"},
41 | {"name": "create_payment_link"},
42 | {"name": "create_invoice"},
43 | {"name": "list_invoices"},
44 | {"name": "create_invoice_item"},
45 | {"name": "finalize_invoice"},
46 | {"name": "retrieve_balance"},
47 | {"name": "create_refund"},
48 | {"name": "list_payment_intents"},
49 | {"name": "list_subscriptions"},
50 | {"name": "cancel_subscription"},
51 | {"name": "update_subscription"},
52 | {"name": "list_coupons"},
53 | {"name": "create_coupon"},
54 | {"name": "update_dispute"},
55 | {"name": "list_disputes"}
56 | ],
57 | "license": "MIT",
58 | "repository": {
59 | "type": "git",
60 | "url": "https://github.com/stripe/ai/tree/main"
61 | },
62 | "icon": "stripe_icon.png"
63 | }
64 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/shared/prices/createPrice.ts:
--------------------------------------------------------------------------------
```typescript
1 | import Stripe from 'stripe';
2 | import {z} from 'zod';
3 | import type {Context} from '@/shared/configuration';
4 | import type {StripeToolDefinition} from '@/shared/tools';
5 |
6 | export const createPricePrompt = (_context: Context = {}) => `
7 | This tool will create a price in Stripe. If a product has not already been specified, a product should be created first.
8 |
9 | It takes three arguments:
10 | - product (str): The ID of the product to create the price for.
11 | - unit_amount (int): The unit amount of the price in cents.
12 | - currency (str): The currency of the price.
13 | `;
14 |
15 | export const createPrice = async (
16 | stripe: Stripe,
17 | context: Context,
18 | params: z.infer<ReturnType<typeof createPriceParameters>>
19 | ) => {
20 | try {
21 | const price = await stripe.prices.create(
22 | params,
23 | context.account ? {stripeAccount: context.account} : undefined
24 | );
25 |
26 | return price;
27 | } catch (error) {
28 | return 'Failed to create price';
29 | }
30 | };
31 |
32 | export const createPriceParameters = (_context: Context = {}) =>
33 | z.object({
34 | product: z
35 | .string()
36 | .describe('The ID of the product to create the price for.'),
37 | unit_amount: z
38 | .number()
39 | .int()
40 | .describe('The unit amount of the price in cents.'),
41 | currency: z.string().describe('The currency of the price.'),
42 | });
43 |
44 | export const createPriceAnnotations = () => ({
45 | destructiveHint: false,
46 | idempotentHint: false,
47 | openWorldHint: true,
48 | readOnlyHint: false,
49 | title: 'Create price',
50 | });
51 |
52 | const tool = (context: Context): StripeToolDefinition => ({
53 | method: 'create_price',
54 | name: 'Create Price',
55 | description: createPricePrompt(context),
56 | inputSchema: createPriceParameters(context),
57 | annotations: createPriceAnnotations(),
58 | actions: {
59 | prices: {
60 | create: true,
61 | },
62 | },
63 | execute: createPrice,
64 | });
65 |
66 | export default tool;
67 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/shared/configuration.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type {StripeToolDefinition} from './tools';
2 |
3 | // Actions restrict the subset of API calls that can be made. They should
4 | // be used in conjunction with Restricted API Keys. Setting a permission to false
5 | // prevents the related "tool" from being considered.
6 | export type Object =
7 | | 'customers'
8 | | 'disputes'
9 | | 'invoices'
10 | | 'invoiceItems'
11 | | 'paymentLinks'
12 | | 'products'
13 | | 'prices'
14 | | 'balance'
15 | | 'refunds'
16 | | 'paymentIntents'
17 | | 'subscriptions'
18 | | 'documentation'
19 | | 'coupons';
20 |
21 | export type Permission = 'create' | 'update' | 'read';
22 |
23 | export type Actions = {
24 | [K in Object]?: {
25 | [K in Permission]?: boolean;
26 | };
27 | } & {
28 | balance?: {
29 | read?: boolean;
30 | };
31 | };
32 |
33 | // Context are settings that are applied to all requests made by the integration.
34 | export type Context = {
35 | // Account is a Stripe Connected Account ID. If set, the integration will
36 | // make requests for this Account.
37 | account?: string;
38 |
39 | // Customer is a Stripe Customer ID. If set, the integration will
40 | // make requests for this Customer.
41 | customer?: string;
42 |
43 | // If set to 'modelcontextprotocol', the Stripe API calls will use a special
44 | // header
45 | mode?: 'modelcontextprotocol' | 'toolkit';
46 | };
47 |
48 | // Configuration provides various settings and options for the integration
49 | // to tune and manage how it behaves.
50 | export type Configuration = {
51 | actions?: Actions;
52 | context?: Context;
53 | };
54 |
55 | export const isToolAllowed = (
56 | tool: StripeToolDefinition,
57 | configuration: Configuration
58 | ): boolean => {
59 | return Object.keys(tool.actions).every((resource) => {
60 | // For each resource.permission pair, check the configuration.
61 | // @ts-ignore
62 | const permissions = tool.actions[resource];
63 |
64 | return Object.keys(permissions).every((permission) => {
65 | // @ts-ignore
66 | return configuration.actions[resource]?.[permission] === true;
67 | });
68 | });
69 | };
70 |
```
--------------------------------------------------------------------------------
/tools/modelcontextprotocol/package.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "@stripe/mcp",
3 | "version": "0.2.5",
4 | "homepage": "https://github.com/stripe/ai/tree/main/tools/modelcontextprotocol",
5 | "description": "A command line tool for setting up Stripe MCP server",
6 | "bin": "dist/index.js",
7 | "files": [
8 | "dist/index.js",
9 | "LICENSE",
10 | "README.md",
11 | "VERSION",
12 | "package.json"
13 | ],
14 | "scripts": {
15 | "build": "tsc && node -e \"require('fs').chmodSync('dist/index.js', '755')\"",
16 | "clean": "rm -rf dist",
17 | "lint": "eslint \"./**/*.ts*\"",
18 | "prettier": "prettier './**/*.{js,ts,md,html,css}' --write",
19 | "prettier-check": "prettier './**/*.{js,ts,md,html,css}' --check",
20 | "test": "jest",
21 | "build-dxt-extension": "node build-dxt.js"
22 | },
23 | "packageManager": "[email protected]",
24 | "engines": {
25 | "node": ">=18"
26 | },
27 | "dependencies": {
28 | "@modelcontextprotocol/sdk": "^1.17.1",
29 | "@stripe/agent-toolkit": "^0.7.11",
30 | "colors": "^1.4.0"
31 | },
32 | "keywords": [
33 | "mcp",
34 | "modelcontextprotocol",
35 | "stripe"
36 | ],
37 | "author": "Stripe <[email protected]> (https://stripe.com/)",
38 | "license": "MIT",
39 | "devDependencies": {
40 | "@anthropic-ai/dxt": "^0.1.0",
41 | "@eslint/compat": "^1.2.6",
42 | "@types/jest": "^29.5.14",
43 | "@types/node": "^22.13.4",
44 | "@typescript-eslint/eslint-plugin": "^8.24.1",
45 | "esbuild": "^0.25.5",
46 | "eslint-config-prettier": "^10.0.1",
47 | "eslint-plugin-import": "^2.31.0",
48 | "eslint-plugin-jest": "^28.11.0",
49 | "eslint-plugin-prettier": "^5.2.3",
50 | "globals": "^15.15.0",
51 | "jest": "^29.7.0",
52 | "prettier": "^3.5.1",
53 | "ts-jest": "^29.2.5",
54 | "ts-node": "^10.9.2",
55 | "typescript": "^5.8.3"
56 | },
57 | "pnpm": {
58 | "overrides": {
59 | "form-data": "^4.0.4",
60 | "@babel/helpers": "^7.26.10",
61 | "jsondiffpatch": "^0.7.2",
62 | "brace-expansion": "^2.0.2",
63 | "@eslint/plugin-kit": "^0.3.4",
64 | "tmp": "^0.2.4"
65 | }
66 | }
67 | }
68 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/openai/toolkit.ts:
--------------------------------------------------------------------------------
```typescript
1 | import StripeAPI from '../shared/api';
2 | import tools from '../shared/tools';
3 | import {isToolAllowed, type Configuration} from '../shared/configuration';
4 | import {zodToJsonSchema} from 'zod-to-json-schema';
5 | import type {
6 | ChatCompletionTool,
7 | ChatCompletionMessageToolCall,
8 | ChatCompletionToolMessageParam,
9 | } from 'openai/resources';
10 |
11 | class StripeAgentToolkit {
12 | private _stripe: StripeAPI;
13 |
14 | tools: ChatCompletionTool[];
15 |
16 | constructor({
17 | secretKey,
18 | configuration,
19 | }: {
20 | secretKey: string;
21 | configuration: Configuration;
22 | }) {
23 | this._stripe = new StripeAPI(secretKey, configuration.context);
24 |
25 | const context = configuration.context || {};
26 | const filteredTools = tools(context).filter((tool) =>
27 | isToolAllowed(tool, configuration)
28 | );
29 |
30 | this.tools = filteredTools.map((tool) => ({
31 | type: 'function',
32 | function: {
33 | name: tool.method,
34 | description: tool.description,
35 | inputSchema: zodToJsonSchema(tool.inputSchema),
36 | },
37 | }));
38 | }
39 |
40 | getTools(): ChatCompletionTool[] {
41 | return this.tools;
42 | }
43 |
44 | /**
45 | * Processes a single OpenAI tool call by executing the requested function.
46 | *
47 | * @param {ChatCompletionMessageToolCall} toolCall - The tool call object from OpenAI containing
48 | * function name, arguments, and ID.
49 | * @returns {Promise<ChatCompletionToolMessageParam>} A promise that resolves to a tool message
50 | * object containing the result of the tool execution with the proper format for the OpenAI API.
51 | */
52 | async handleToolCall(toolCall: ChatCompletionMessageToolCall) {
53 | const args = JSON.parse(toolCall.function.arguments);
54 | const response = await this._stripe.run(toolCall.function.name, args);
55 | return {
56 | role: 'tool',
57 | tool_call_id: toolCall.id,
58 | content: response,
59 | } as ChatCompletionToolMessageParam;
60 | }
61 | }
62 |
63 | export default StripeAgentToolkit;
64 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/test/shared/subscriptions/parameters.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {listSubscriptionsParameters} from '@/shared/subscriptions/listSubscriptions';
2 | import {cancelSubscriptionParameters} from '@/shared/subscriptions/cancelSubscription';
3 | import {updateSubscriptionParameters} from '@/shared/subscriptions/updateSubscription';
4 |
5 | describe('listSubscriptionsParameters', () => {
6 | it('should return the correct parameters if no context', () => {
7 | const parameters = listSubscriptionsParameters({});
8 |
9 | const fields = Object.keys(parameters.shape);
10 | expect(fields).toEqual(['customer', 'price', 'status', 'limit']);
11 | expect(fields.length).toBe(4);
12 | });
13 |
14 | it('should return the correct parameters if customer is specified', () => {
15 | const parameters = listSubscriptionsParameters({customer: 'cus_123'});
16 |
17 | const fields = Object.keys(parameters.shape);
18 | expect(fields).toEqual(['price', 'status', 'limit']);
19 | expect(fields.length).toBe(3);
20 | });
21 | });
22 |
23 | describe('cancelSubscriptionParameters', () => {
24 | it('should return the correct parameters', () => {
25 | const parameters = cancelSubscriptionParameters({});
26 | const fields = Object.keys(parameters.shape);
27 | expect(fields).toEqual(['subscription']);
28 | });
29 | });
30 |
31 | describe('updateSubscriptionParameters', () => {
32 | it('should return the correct parameters', () => {
33 | const parameters = updateSubscriptionParameters({});
34 | const fields = Object.keys(parameters.shape);
35 | expect(fields).toEqual(['subscription', 'proration_behavior', 'items']);
36 | });
37 |
38 | it('should have the required subscription parameter', () => {
39 | const parameters = updateSubscriptionParameters({});
40 | expect(parameters.shape.subscription).toBeDefined();
41 | });
42 |
43 | it('should have the optional parameters defined', () => {
44 | const parameters = updateSubscriptionParameters({});
45 | expect(parameters.shape.proration_behavior).toBeDefined();
46 | expect(parameters.shape.items).toBeDefined();
47 | });
48 | });
49 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/shared/customers/listCustomers.ts:
--------------------------------------------------------------------------------
```typescript
1 | import Stripe from 'stripe';
2 | import {z} from 'zod';
3 | import type {Context} from '@/shared/configuration';
4 | import type {StripeToolDefinition} from '@/shared/tools';
5 |
6 | export const listCustomersPrompt = (_context: Context = {}) => `
7 | This tool will fetch a list of Customers from Stripe.
8 |
9 | It takes two arguments:
10 | - limit (int, optional): The number of customers to return.
11 | - email (str, optional): A case-sensitive filter on the list based on the customer's email field.
12 | `;
13 |
14 | export const listCustomersParameters = (_context: Context = {}) =>
15 | z.object({
16 | limit: z
17 | .number()
18 | .int()
19 | .min(1)
20 | .max(100)
21 | .optional()
22 | .describe(
23 | 'A limit on the number of objects to be returned. Limit can range between 1 and 100.'
24 | ),
25 | email: z
26 | .string()
27 | .optional()
28 | .describe(
29 | "A case-sensitive filter on the list based on the customer's email field. The value must be a string."
30 | ),
31 | });
32 |
33 | export const listCustomersAnnotations = () => ({
34 | destructiveHint: false,
35 | idempotentHint: true,
36 | openWorldHint: true,
37 | readOnlyHint: true,
38 | title: 'List customers',
39 | });
40 |
41 | export const listCustomers = async (
42 | stripe: Stripe,
43 | context: Context,
44 | params: z.infer<ReturnType<typeof listCustomersParameters>>
45 | ) => {
46 | try {
47 | const customers = await stripe.customers.list(
48 | params,
49 | context.account ? {stripeAccount: context.account} : undefined
50 | );
51 |
52 | return customers.data.map((customer) => ({id: customer.id}));
53 | } catch (error) {
54 | return 'Failed to list customers';
55 | }
56 | };
57 |
58 | const tool = (context: Context): StripeToolDefinition => ({
59 | method: 'list_customers',
60 | name: 'List Customers',
61 | description: listCustomersPrompt(context),
62 | inputSchema: listCustomersParameters(context),
63 | annotations: listCustomersAnnotations(),
64 | actions: {
65 | customers: {
66 | read: true,
67 | },
68 | },
69 | execute: listCustomers,
70 | });
71 |
72 | export default tool;
73 |
```
--------------------------------------------------------------------------------
/llm/ai-sdk/package.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "@stripe/ai-sdk",
3 | "version": "0.1.1",
4 | "description": "Stripe AI SDK - Provider and metering utilities for Vercel AI SDK",
5 | "exports": {
6 | "./provider": {
7 | "types": "./dist/provider/index.d.ts",
8 | "require": "./dist/provider/index.js",
9 | "import": "./dist/provider/index.mjs"
10 | },
11 | "./meter": {
12 | "types": "./dist/meter/index.d.ts",
13 | "require": "./dist/meter/index.js",
14 | "import": "./dist/meter/index.mjs"
15 | }
16 | },
17 | "scripts": {
18 | "build": "tsup",
19 | "clean": "rm -rf dist",
20 | "test": "jest",
21 | "test:watch": "jest --watch",
22 | "test:coverage": "jest --coverage",
23 | "prepublishOnly": "npm run build"
24 | },
25 | "keywords": [
26 | "stripe",
27 | "ai",
28 | "provider",
29 | "billing",
30 | "metering",
31 | "ai-sdk",
32 | "vercel"
33 | ],
34 | "author": "Stripe <[email protected]> (https://stripe.com/)",
35 | "license": "MIT",
36 | "engines": {
37 | "node": ">=18"
38 | },
39 | "files": [
40 | "dist/**/*",
41 | "LICENSE",
42 | "README.md",
43 | "package.json"
44 | ],
45 | "devDependencies": {
46 | "@ai-sdk/anthropic": "^2.0.35",
47 | "@ai-sdk/google": "^2.0.23",
48 | "@ai-sdk/groq": "^2.0.24",
49 | "@ai-sdk/openai": "^2.0.53",
50 | "@types/jest": "^29.5.14",
51 | "@types/node": "^22.10.5",
52 | "ai": "^5.0.76",
53 | "dotenv": "^17.2.3",
54 | "jest": "^29.7.0",
55 | "ts-jest": "^29.2.5",
56 | "ts-node": "^10.9.2",
57 | "tsup": "^8.3.5",
58 | "typescript": "^5.8.3"
59 | },
60 | "dependencies": {
61 | "@ai-sdk/provider": "^2.0.0",
62 | "@ai-sdk/provider-utils": "^2.0.0",
63 | "stripe": "^17.5.0",
64 | "zod": "^3.24.1"
65 | },
66 | "peerDependencies": {
67 | "@ai-sdk/anthropic": "^2.0.41",
68 | "@ai-sdk/google": "^2.0.27",
69 | "@ai-sdk/openai": "^2.0.59",
70 | "ai": "^3.4.7 || ^4.0.0 || ^5.0.0"
71 | },
72 | "peerDependenciesMeta": {
73 | "@ai-sdk/anthropic": {
74 | "optional": true
75 | },
76 | "@ai-sdk/google": {
77 | "optional": true
78 | },
79 | "@ai-sdk/openai": {
80 | "optional": true
81 | },
82 | "ai": {
83 | "optional": true
84 | }
85 | }
86 | }
87 |
88 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/test/shared/invoices/parameters.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {createInvoiceParameters} from '@/shared/invoices/createInvoice';
2 | import {listInvoicesParameters} from '@/shared/invoices/listInvoices';
3 | import {finalizeInvoiceParameters} from '@/shared/invoices/finalizeInvoice';
4 |
5 | describe('createInvoiceParameters', () => {
6 | it('should return the correct parameters if no context', () => {
7 | const parameters = createInvoiceParameters({});
8 |
9 | const fields = Object.keys(parameters.shape);
10 | expect(fields).toEqual(['customer', 'days_until_due']);
11 | expect(fields.length).toBe(2);
12 | });
13 |
14 | it('should return the correct parameters if customer is specified', () => {
15 | const parameters = createInvoiceParameters({customer: 'cus_123'});
16 |
17 | const fields = Object.keys(parameters.shape);
18 | expect(fields).toEqual(['days_until_due']);
19 | expect(fields.length).toBe(1);
20 | });
21 | });
22 |
23 | describe('listInvoicesParameters', () => {
24 | it('should return the correct parameters if no context', () => {
25 | const parameters = listInvoicesParameters({});
26 |
27 | const fields = Object.keys(parameters.shape);
28 | expect(fields).toEqual(['customer', 'limit']);
29 | expect(fields.length).toBe(2);
30 | });
31 |
32 | it('should return the correct parameters if customer is specified', () => {
33 | const parameters = listInvoicesParameters({customer: 'cus_123'});
34 |
35 | const fields = Object.keys(parameters.shape);
36 | expect(fields).toEqual(['limit']);
37 | expect(fields.length).toBe(1);
38 | });
39 | });
40 |
41 | describe('finalizeInvoiceParameters', () => {
42 | it('should return the correct parameters if no context', () => {
43 | const parameters = finalizeInvoiceParameters({});
44 |
45 | const fields = Object.keys(parameters.shape);
46 | expect(fields).toEqual(['invoice']);
47 | expect(fields.length).toBe(1);
48 | });
49 |
50 | it('should return the correct parameters if customer is specified', () => {
51 | const parameters = finalizeInvoiceParameters({customer: 'cus_123'});
52 |
53 | const fields = Object.keys(parameters.shape);
54 | expect(fields).toEqual(['invoice']);
55 | expect(fields.length).toBe(1);
56 | });
57 | });
58 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/test/shared/subscriptions/prompts.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {listSubscriptionsPrompt} from '@/shared/subscriptions/listSubscriptions';
2 | import {cancelSubscriptionPrompt} from '@/shared/subscriptions/cancelSubscription';
3 | import {updateSubscriptionPrompt} from '@/shared/subscriptions/updateSubscription';
4 |
5 | describe('listSubscriptionsPrompt', () => {
6 | it('should return the correct prompt with no context', () => {
7 | const prompt = listSubscriptionsPrompt({});
8 |
9 | expect(prompt).toContain('This tool will list all subscriptions in Stripe');
10 | expect(prompt).toContain('four arguments');
11 | expect(prompt).toContain('- customer (str, optional)');
12 | expect(prompt).toContain('- price (str, optional)');
13 | expect(prompt).toContain('- status (str, optional)');
14 | expect(prompt).toContain('- limit (int, optional)');
15 | });
16 |
17 | it('should return the correct prompt with customer in context', () => {
18 | const prompt = listSubscriptionsPrompt({customer: 'cus_123'});
19 |
20 | expect(prompt).toContain('This tool will list all subscriptions in Stripe');
21 | expect(prompt).toContain('three arguments');
22 | expect(prompt).not.toContain('- customer (str, optional)');
23 | expect(prompt).toContain('- price (str, optional)');
24 | expect(prompt).toContain('- status (str, optional)');
25 | expect(prompt).toContain('- limit (int, optional)');
26 | });
27 | });
28 |
29 | describe('cancelSubscriptionPrompt', () => {
30 | it('should return the correct prompt', () => {
31 | const prompt = cancelSubscriptionPrompt({});
32 |
33 | expect(prompt).toContain('This tool will cancel a subscription in Stripe');
34 | expect(prompt).toContain('- subscription (str, required)');
35 | });
36 | });
37 |
38 | describe('updateSubscriptionPrompt', () => {
39 | it('should return the correct prompt', () => {
40 | const prompt = updateSubscriptionPrompt({});
41 |
42 | expect(prompt).toContain(
43 | 'This tool will update an existing subscription in Stripe'
44 | );
45 | expect(prompt).toContain('- subscription (str, required)');
46 | expect(prompt).toContain('- proration_behavior (str, optional)');
47 | expect(prompt).toContain('- items (array, optional)');
48 | });
49 | });
50 |
```
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
```yaml
1 | name: Bug report
2 | description: Create a report to help us improve
3 | labels: ["bug"]
4 | body:
5 | - type: markdown
6 | attributes:
7 | value: |
8 | Thanks for taking the time to fill out this bug report!
9 | - type: textarea
10 | id: what-happened
11 | attributes:
12 | label: Describe the bug
13 | description: A clear and concise description of what the bug is.
14 | placeholder: Tell us what you see!
15 | validations:
16 | required: true
17 | - type: textarea
18 | id: repro-steps
19 | attributes:
20 | label: To Reproduce
21 | description: Steps to reproduce the behavior
22 | placeholder: |
23 | 1. Fetch a '...'
24 | 2. Update the '....'
25 | 3. See error
26 | validations:
27 | required: true
28 | - type: textarea
29 | id: expected-behavior
30 | attributes:
31 | label: Expected behavior
32 | description: A clear and concise description of what you expected to happen.
33 | validations:
34 | required: true
35 | - type: textarea
36 | id: code-snippets
37 | attributes:
38 | label: Code snippets
39 | description: If applicable, add code snippets to help explain your problem.
40 | render: Python
41 | validations:
42 | required: false
43 | - type: input
44 | id: os
45 | attributes:
46 | label: OS
47 | placeholder: macOS
48 | validations:
49 | required: true
50 | - type: input
51 | id: language-version
52 | attributes:
53 | label: Language version
54 | placeholder: Python 3.10.4
55 | validations:
56 | required: true
57 | - type: input
58 | id: lib-version
59 | attributes:
60 | label: Library version
61 | placeholder: stripe-python v2.73.0
62 | validations:
63 | required: true
64 | - type: input
65 | id: api-version
66 | attributes:
67 | label: API version
68 | description: See [Versioning](https://stripe.com/docs/api/versioning) in the API Reference to find which version you're using
69 | placeholder: "2020-08-27"
70 | validations:
71 | required: true
72 | - type: textarea
73 | id: additional-context
74 | attributes:
75 | label: Additional context
76 | description: Add any other context about the problem here.
77 | validations:
78 | required: false
79 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/test/shared/invoiceItems/functions.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {createInvoiceItem} from '@/shared/invoiceItems/createInvoiceItem';
2 |
3 | const Stripe = jest.fn().mockImplementation(() => ({
4 | invoiceItems: {
5 | create: jest.fn(),
6 | },
7 | }));
8 |
9 | let stripe: ReturnType<typeof Stripe>;
10 |
11 | beforeEach(() => {
12 | stripe = new Stripe('fake-api-key');
13 | });
14 |
15 | describe('createInvoiceItem', () => {
16 | it('should create an invoice item and return it', async () => {
17 | const params = {
18 | customer: 'cus_123456',
19 | price: 'price_123456',
20 | invoice: 'in_123456',
21 | };
22 |
23 | const mockInvoiceItem = {id: 'ii_123456', invoice: 'in_123456'};
24 |
25 | const context = {};
26 |
27 | stripe.invoiceItems.create.mockResolvedValue(mockInvoiceItem);
28 |
29 | const result = await createInvoiceItem(stripe, context, params);
30 |
31 | expect(stripe.invoiceItems.create).toHaveBeenCalledWith(params, undefined);
32 | expect(result).toEqual(mockInvoiceItem);
33 | });
34 |
35 | it('should specify the connected account if included in context', async () => {
36 | const params = {
37 | customer: 'cus_123456',
38 | price: 'price_123456',
39 | invoice: 'in_123456',
40 | };
41 |
42 | const mockInvoiceItem = {id: 'ii_123456', invoice: 'in_123456'};
43 |
44 | const context = {
45 | account: 'acct_123456',
46 | };
47 |
48 | stripe.invoiceItems.create.mockResolvedValue(mockInvoiceItem);
49 |
50 | const result = await createInvoiceItem(stripe, context, params);
51 |
52 | expect(stripe.invoiceItems.create).toHaveBeenCalledWith(params, {
53 | stripeAccount: context.account,
54 | });
55 | expect(result).toEqual(mockInvoiceItem);
56 | });
57 |
58 | it('should create an invoice item with a customer if included in context', async () => {
59 | const params = {
60 | price: 'price_123456',
61 | invoice: 'in_123456',
62 | };
63 |
64 | const mockInvoiceItem = {id: 'ii_123456', invoice: 'in_123456'};
65 |
66 | const context = {
67 | customer: 'cus_123456',
68 | };
69 |
70 | stripe.invoiceItems.create.mockResolvedValue(mockInvoiceItem);
71 |
72 | const result = await createInvoiceItem(stripe, context, params);
73 |
74 | expect(stripe.invoiceItems.create).toHaveBeenCalledWith(
75 | {
76 | ...params,
77 | customer: context.customer,
78 | },
79 | undefined
80 | );
81 | expect(result).toEqual(mockInvoiceItem);
82 | });
83 | });
84 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/shared/disputes/listDisputes.ts:
--------------------------------------------------------------------------------
```typescript
1 | import Stripe from 'stripe';
2 | import {z} from 'zod';
3 | import type {Context} from '@/shared/configuration';
4 | import type {StripeToolDefinition} from '@/shared/tools';
5 |
6 | export const listDisputesPrompt = (_context: Context = {}) => `
7 | This tool will fetch a list of disputes in Stripe.
8 |
9 | It takes the following arguments:
10 | - charge (string, optional): Only return disputes associated to the charge specified by this charge ID.
11 | - payment_intent (string, optional): Only return disputes associated to the PaymentIntent specified by this PaymentIntent ID.
12 | `;
13 |
14 | export const listDisputesParameters = (_context: Context = {}) =>
15 | z.object({
16 | charge: z
17 | .string()
18 | .optional()
19 | .describe(
20 | 'Only return disputes associated to the charge specified by this charge ID.'
21 | ),
22 | payment_intent: z
23 | .string()
24 | .optional()
25 | .describe(
26 | 'Only return disputes associated to the PaymentIntent specified by this PaymentIntent ID.'
27 | ),
28 | limit: z
29 | .number()
30 | .int()
31 | .min(1)
32 | .max(100)
33 | .default(10)
34 | .optional()
35 | .describe(
36 | 'A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 10.'
37 | ),
38 | });
39 |
40 | export const listDisputesAnnotations = () => ({
41 | destructiveHint: false,
42 | idempotentHint: true,
43 | openWorldHint: true,
44 | readOnlyHint: true,
45 | title: 'List disputes',
46 | });
47 |
48 | export const listDisputes = async (
49 | stripe: Stripe,
50 | context: Context,
51 | params: z.infer<ReturnType<typeof listDisputesParameters>>
52 | ) => {
53 | try {
54 | const disputes = await stripe.disputes.list(
55 | params,
56 | context.account ? {stripeAccount: context.account} : undefined
57 | );
58 |
59 | return disputes.data.map((dispute) => ({id: dispute.id}));
60 | } catch (error) {
61 | return 'Failed to list disputes';
62 | }
63 | };
64 |
65 | const tool = (context: Context): StripeToolDefinition => ({
66 | method: 'list_disputes',
67 | name: 'List Disputes',
68 | description: listDisputesPrompt(context),
69 | inputSchema: listDisputesParameters(context),
70 | annotations: listDisputesAnnotations(),
71 | actions: {
72 | disputes: {
73 | read: true,
74 | },
75 | },
76 | execute: listDisputes,
77 | });
78 |
79 | export default tool;
80 |
```
--------------------------------------------------------------------------------
/tools/python/examples/openai/file_search/main.py:
--------------------------------------------------------------------------------
```python
1 | import asyncio
2 | import os
3 | from pydantic import BaseModel, Field
4 |
5 | from dotenv import load_dotenv
6 | load_dotenv()
7 |
8 | from agents import Agent, Runner
9 | from agents.tool import FileSearchTool
10 |
11 | from stripe_agent_toolkit.openai.toolkit import StripeAgentToolkit
12 |
13 | stripe_agent_toolkit = StripeAgentToolkit(
14 | secret_key=os.getenv("STRIPE_SECRET_KEY"),
15 | configuration={
16 | "actions": {
17 | "customers": {
18 | "create": True,
19 | },
20 | "products": {
21 | "create": True,
22 | },
23 | "prices": {
24 | "create": True,
25 | },
26 | "invoice_items": {
27 | "create": True,
28 | },
29 | "invoices": {
30 | "create": True,
31 | "update": True,
32 | },
33 | }
34 | },
35 | )
36 |
37 | class InvoiceOutput(BaseModel):
38 | name: str = Field(description="The name of the customer")
39 | email: str = Field(description="The email of the customer")
40 | service: str = Field(description="The service that the customer is invoiced for")
41 | amount_due: int = Field(description="The dollar amount due for the invoice. Convert text to dollar amounts if needed.")
42 | id: str = Field(description="The id of the stripe invoice")
43 |
44 | class InvoiceListOutput(BaseModel):
45 | invoices: list[InvoiceOutput]
46 |
47 | invoice_agent = Agent(
48 | name="Invoice Agent",
49 | instructions="You are an expert at using the Stripe API to create, finalize, and send invoices to customers.",
50 | tools=stripe_agent_toolkit.get_tools(),
51 | )
52 |
53 | file_search_agent = Agent(
54 | name="File Search Agent",
55 | instructions="You are an expert at searching for financial documents.",
56 | tools=[
57 | FileSearchTool(
58 | max_num_results=50,
59 | vector_store_ids=[os.getenv("OPENAI_VECTOR_STORE_ID")],
60 | )
61 | ],
62 | output_type=InvoiceListOutput,
63 | handoffs=[invoice_agent]
64 | )
65 |
66 | async def main():
67 | assignment = "Search for all customers that haven't paid across all of my documents. Handoff to the invoice agent to create, finalize, and send an invoice for each."
68 |
69 | outstanding_invoices = await Runner.run(
70 | file_search_agent,
71 | assignment,
72 | )
73 |
74 | invoices = outstanding_invoices.final_output
75 |
76 | print(invoices)
77 |
78 | if __name__ == "__main__":
79 | asyncio.run(main())
80 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/shared/paymentIntents/listPaymentIntents.ts:
--------------------------------------------------------------------------------
```typescript
1 | import Stripe from 'stripe';
2 | import {z} from 'zod';
3 | import type {Context} from '@/shared/configuration';
4 | import type {StripeToolDefinition} from '@/shared/tools';
5 |
6 | export const listPaymentIntentsPrompt = (context: Context = {}) => {
7 | const customerArg = context.customer
8 | ? `The customer is already set in the context: ${context.customer}.`
9 | : `- customer (str, optional): The ID of the customer to list payment intents for.\n`;
10 |
11 | return `
12 | This tool will list payment intents in Stripe.
13 |
14 | It takes ${context.customer ? 'one' : 'two'} argument${context.customer ? '' : 's'}:
15 | ${customerArg}
16 | - limit (int, optional): The number of payment intents to return.
17 | `;
18 | };
19 |
20 | export const listPaymentIntents = async (
21 | stripe: Stripe,
22 | context: Context,
23 | params: z.infer<ReturnType<typeof listPaymentIntentsParameters>>
24 | ) => {
25 | try {
26 | if (context.customer) {
27 | params.customer = context.customer;
28 | }
29 |
30 | const paymentIntents = await stripe.paymentIntents.list(
31 | params,
32 | context.account ? {stripeAccount: context.account} : undefined
33 | );
34 |
35 | return paymentIntents.data;
36 | } catch (error) {
37 | return 'Failed to list payment intents';
38 | }
39 | };
40 |
41 | export const listPaymentIntentsAnnotations = () => ({
42 | destructiveHint: false,
43 | idempotentHint: true,
44 | openWorldHint: true,
45 | readOnlyHint: true,
46 | title: 'List payment intents',
47 | });
48 |
49 | export const listPaymentIntentsParameters = (
50 | context: Context = {}
51 | ): z.AnyZodObject => {
52 | const schema = z.object({
53 | customer: z
54 | .string()
55 | .optional()
56 | .describe('The ID of the customer to list payment intents for.'),
57 | limit: z
58 | .number()
59 | .int()
60 | .min(1)
61 | .max(100)
62 | .optional()
63 | .describe(
64 | 'A limit on the number of objects to be returned. Limit can range between 1 and 100.'
65 | ),
66 | });
67 |
68 | if (context.customer) {
69 | return schema.omit({customer: true});
70 | } else {
71 | return schema;
72 | }
73 | };
74 |
75 | const tool = (context: Context): StripeToolDefinition => ({
76 | method: 'list_payment_intents',
77 | name: 'List Payment Intents',
78 | description: listPaymentIntentsPrompt(context),
79 | inputSchema: listPaymentIntentsParameters(context),
80 | annotations: listPaymentIntentsAnnotations(),
81 | actions: {
82 | paymentIntents: {
83 | read: true,
84 | },
85 | },
86 | execute: listPaymentIntents,
87 | });
88 |
89 | export default tool;
90 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/shared/paymentLinks/createPaymentLink.ts:
--------------------------------------------------------------------------------
```typescript
1 | import Stripe from 'stripe';
2 | import {z} from 'zod';
3 | import type {Context} from '@/shared/configuration';
4 | import type {StripeToolDefinition} from '@/shared/tools';
5 |
6 | export const createPaymentLinkPrompt = (_context: Context = {}) => `
7 | This tool will create a payment link in Stripe.
8 |
9 | It takes two arguments:
10 | - price (str): The ID of the price to create the payment link for.
11 | - quantity (int): The quantity of the product to include in the payment link.
12 | - redirect_url (str, optional): The URL to redirect to after the payment is completed.
13 | `;
14 |
15 | export const createPaymentLink = async (
16 | stripe: Stripe,
17 | context: Context,
18 | params: z.infer<ReturnType<typeof createPaymentLinkParameters>>
19 | ) => {
20 | try {
21 | const paymentLink = await stripe.paymentLinks.create(
22 | {
23 | line_items: [{price: params.price, quantity: params.quantity}],
24 | ...(params.redirect_url
25 | ? {
26 | after_completion: {
27 | type: 'redirect',
28 | redirect: {
29 | url: params.redirect_url,
30 | },
31 | },
32 | }
33 | : undefined),
34 | },
35 | context.account ? {stripeAccount: context.account} : undefined
36 | );
37 |
38 | return {id: paymentLink.id, url: paymentLink.url};
39 | } catch (error) {
40 | return 'Failed to create payment link';
41 | }
42 | };
43 |
44 | export const createPaymentLinkAnnotations = () => ({
45 | destructiveHint: false,
46 | idempotentHint: false,
47 | openWorldHint: true,
48 | readOnlyHint: false,
49 | title: 'Create payment link',
50 | });
51 |
52 | export const createPaymentLinkParameters = (_context: Context = {}) =>
53 | z.object({
54 | price: z
55 | .string()
56 | .describe('The ID of the price to create the payment link for.'),
57 | quantity: z
58 | .number()
59 | .int()
60 | .describe('The quantity of the product to include.'),
61 | redirect_url: z
62 | .string()
63 | .optional()
64 | .describe('The URL to redirect to after the payment is completed.'),
65 | });
66 |
67 | const tool = (context: Context): StripeToolDefinition => ({
68 | method: 'create_payment_link',
69 | name: 'Create Payment Link',
70 | description: createPaymentLinkPrompt(context),
71 | inputSchema: createPaymentLinkParameters(context),
72 | annotations: createPaymentLinkAnnotations(),
73 | actions: {
74 | paymentLinks: {
75 | create: true,
76 | },
77 | },
78 | execute: createPaymentLink,
79 | });
80 |
81 | export default tool;
82 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/shared/invoices/createInvoice.ts:
--------------------------------------------------------------------------------
```typescript
1 | import Stripe from 'stripe';
2 | import {z} from 'zod';
3 | import type {Context} from '@/shared/configuration';
4 | import type {StripeToolDefinition} from '@/shared/tools';
5 |
6 | export const createInvoicePrompt = (context: Context = {}) => {
7 | const customerArg = context.customer
8 | ? `The customer is already set in the context: ${context.customer}.`
9 | : `- customer (str): The ID of the customer to create the invoice for.\n`;
10 |
11 | return `
12 | This tool will create an invoice in Stripe.
13 |
14 | It takes ${context.customer ? 'one' : 'two'} argument${context.customer ? '' : 's'}:
15 | ${customerArg}
16 | - days_until_due (int, optional): The number of days until the invoice is due.
17 | `;
18 | };
19 |
20 | export const createInvoiceParameters = (
21 | context: Context = {}
22 | ): z.AnyZodObject => {
23 | const schema = z.object({
24 | customer: z
25 | .string()
26 | .describe('The ID of the customer to create the invoice for.'),
27 | days_until_due: z
28 | .number()
29 | .int()
30 | .optional()
31 | .describe('The number of days until the invoice is due.'),
32 | });
33 |
34 | if (context.customer) {
35 | return schema.omit({customer: true});
36 | } else {
37 | return schema;
38 | }
39 | };
40 |
41 | export const createInvoiceAnnotations = () => ({
42 | destructiveHint: false,
43 | idempotentHint: false,
44 | openWorldHint: true,
45 | readOnlyHint: false,
46 | title: 'Create invoice',
47 | });
48 |
49 | export const createInvoice = async (
50 | stripe: Stripe,
51 | context: Context,
52 | params: z.infer<ReturnType<typeof createInvoiceParameters>>
53 | ) => {
54 | try {
55 | if (context.customer) {
56 | params.customer = context.customer;
57 | }
58 |
59 | const invoice = await stripe.invoices.create(
60 | {
61 | ...params,
62 | collection_method: 'send_invoice',
63 | },
64 | context.account ? {stripeAccount: context.account} : undefined
65 | );
66 |
67 | return {
68 | id: invoice.id,
69 | url: invoice.hosted_invoice_url,
70 | customer: invoice.customer,
71 | status: invoice.status,
72 | };
73 | } catch (error) {
74 | return 'Failed to create invoice';
75 | }
76 | };
77 |
78 | const tool = (context: Context): StripeToolDefinition => ({
79 | method: 'create_invoice',
80 | name: 'Create Invoice',
81 | description: createInvoicePrompt(context),
82 | inputSchema: createInvoiceParameters(context),
83 | annotations: createInvoiceAnnotations(),
84 | actions: {
85 | invoices: {
86 | create: true,
87 | },
88 | },
89 | execute: createInvoice,
90 | });
91 |
92 | export default tool;
93 |
```
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
```yaml
1 | name: CI
2 | permissions:
3 | contents: read
4 |
5 | on:
6 | workflow_dispatch: {}
7 | push:
8 | branches:
9 | - main
10 | pull_request:
11 | branches:
12 | - main
13 |
14 | jobs:
15 | typescript-build:
16 | name: Build - TypeScript
17 | runs-on: ubuntu-latest
18 |
19 | defaults:
20 | run:
21 | working-directory: ./tools/typescript
22 |
23 | steps:
24 | - name: Checkout
25 | uses: actions/checkout@v3
26 |
27 | - name: pnpm
28 | uses: pnpm/action-setup@v4
29 | with:
30 | version: 9.11.0
31 |
32 | - name: Node
33 | uses: actions/setup-node@v4
34 | with:
35 | node-version: "18"
36 |
37 | - name: Install
38 | run: pnpm install --frozen-lockfile
39 |
40 | - name: Build
41 | run: pnpm run build
42 |
43 | - name: Clean
44 | run: pnpm run clean
45 |
46 | - name: Lint
47 | run: pnpm run lint
48 |
49 | - name: Prettier
50 | run: pnpm run prettier-check
51 |
52 | - name: Test
53 | run: pnpm run test
54 |
55 | modelcontextprotocol-build:
56 | name: Build - Model Context Protocol
57 | runs-on: ubuntu-latest
58 |
59 | defaults:
60 | run:
61 | working-directory: ./tools/modelcontextprotocol
62 |
63 | steps:
64 | - name: Checkout
65 | uses: actions/checkout@v3
66 |
67 | - name: pnpm
68 | uses: pnpm/action-setup@v4
69 | with:
70 | version: 9.11.0
71 |
72 | - name: Node
73 | uses: actions/setup-node@v4
74 | with:
75 | node-version: "18"
76 |
77 | - name: Install
78 | run: pnpm install --frozen-lockfile
79 |
80 | - name: Build
81 | run: pnpm run build
82 |
83 | - name: Clean
84 | run: pnpm run clean
85 |
86 | - name: Lint
87 | run: pnpm run lint
88 |
89 | - name: Prettier
90 | run: pnpm run prettier-check
91 |
92 | - name: Test
93 | run: pnpm run test
94 |
95 | python-build:
96 | name: Build - Python
97 | runs-on: ubuntu-latest
98 |
99 | defaults:
100 | run:
101 | working-directory: ./tools/python
102 |
103 | steps:
104 | - name: Checkout
105 | uses: actions/checkout@v3
106 |
107 | - name: Python
108 | uses: actions/setup-python@v4
109 | with:
110 | python-version: "3.11"
111 |
112 | - name: Install uv
113 | uses: astral-sh/setup-uv@v2
114 |
115 | - name: Install
116 | run: make venv
117 |
118 | - name: Build
119 | run: |
120 | set -x
121 | source .venv/bin/activate
122 | rm -rf build dist *.egg-info
123 | make build
124 | python -m twine check dist/*
125 |
126 | - name: Test
127 | run: |
128 | make venv
129 | make test
130 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/shared/invoiceItems/createInvoiceItem.ts:
--------------------------------------------------------------------------------
```typescript
1 | import Stripe from 'stripe';
2 | import {z} from 'zod';
3 | import type {Context} from '@/shared/configuration';
4 | import type {StripeToolDefinition} from '@/shared/tools';
5 |
6 | export const createInvoiceItem = async (
7 | stripe: Stripe,
8 | context: Context,
9 | params: z.infer<ReturnType<typeof createInvoiceItemParameters>>
10 | ) => {
11 | try {
12 | if (context.customer) {
13 | params.customer = context.customer;
14 | }
15 |
16 | const invoiceItem = await stripe.invoiceItems.create(
17 | // @ts-ignore
18 | params,
19 | context.account ? {stripeAccount: context.account} : undefined
20 | );
21 |
22 | return {
23 | id: invoiceItem.id,
24 | invoice: invoiceItem.invoice,
25 | };
26 | } catch (error) {
27 | return 'Failed to create invoice item';
28 | }
29 | };
30 |
31 | export const createInvoiceItemAnnotations = () => ({
32 | destructiveHint: false,
33 | idempotentHint: false,
34 | openWorldHint: true,
35 | readOnlyHint: false,
36 | title: 'Create invoice item',
37 | });
38 |
39 | export const createInvoiceItemParameters = (
40 | context: Context = {}
41 | ): z.AnyZodObject => {
42 | const schema = z.object({
43 | customer: z
44 | .string()
45 | .describe('The ID of the customer to create the invoice item for.'),
46 | price: z.string().describe('The ID of the price for the item.'),
47 | invoice: z
48 | .string()
49 | .describe('The ID of the invoice to create the item for.'),
50 | });
51 |
52 | if (context.customer) {
53 | return schema.omit({customer: true});
54 | } else {
55 | return schema;
56 | }
57 | };
58 |
59 | export const createInvoiceItemPrompt = (context: Context = {}) => {
60 | const customerArg = context.customer
61 | ? `The customer is already set in the context: ${context.customer}.`
62 | : `- customer (str): The ID of the customer to create the invoice item for.\n`;
63 |
64 | return `
65 | This tool will create an invoice item in Stripe.
66 |
67 | It takes ${context.customer ? 'two' : 'three'} arguments'}:
68 | ${customerArg}
69 | - price (str): The ID of the price to create the invoice item for.
70 | - invoice (str): The ID of the invoice to create the invoice item for.
71 | `;
72 | };
73 |
74 | const tool = (context: Context): StripeToolDefinition => ({
75 | method: 'create_invoice_item',
76 | name: 'Create Invoice Item',
77 | description: createInvoiceItemPrompt(context),
78 | inputSchema: createInvoiceItemParameters(context),
79 | annotations: createInvoiceItemAnnotations(),
80 | actions: {
81 | invoiceItems: {
82 | create: true,
83 | },
84 | },
85 | execute: createInvoiceItem,
86 | });
87 |
88 | export default tool;
89 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/shared/invoices/listInvoices.ts:
--------------------------------------------------------------------------------
```typescript
1 | import Stripe from 'stripe';
2 | import {z} from 'zod';
3 | import type {Context} from '@/shared/configuration';
4 | import type {StripeToolDefinition} from '@/shared/tools';
5 |
6 | export const listInvoices = async (
7 | stripe: Stripe,
8 | context: Context,
9 | params: z.infer<ReturnType<typeof listInvoicesParameters>>
10 | ) => {
11 | try {
12 | if (context.customer) {
13 | params.customer = context.customer;
14 | }
15 |
16 | const invoices = await stripe.invoices.list(
17 | params,
18 | context.account ? {stripeAccount: context.account} : undefined
19 | );
20 |
21 | return invoices.data;
22 | } catch (error) {
23 | return 'Failed to list invoices';
24 | }
25 | };
26 |
27 | export const listInvoicesAnnotations = () => ({
28 | destructiveHint: false,
29 | idempotentHint: true,
30 | openWorldHint: true,
31 | readOnlyHint: true,
32 | title: 'List invoices',
33 | });
34 |
35 | export const listInvoicesParameters = (
36 | context: Context = {}
37 | ): z.AnyZodObject => {
38 | const schema = z.object({
39 | customer: z
40 | .string()
41 | .optional()
42 | .describe('The ID of the customer to list invoices for.'),
43 | limit: z
44 | .number()
45 | .int()
46 | .min(1)
47 | .max(100)
48 | .optional()
49 | .describe(
50 | 'A limit on the number of objects to be returned. Limit can range between 1 and 100, and the default is 10.'
51 | ),
52 | });
53 |
54 | if (context.customer) {
55 | return schema.omit({customer: true});
56 | } else {
57 | return schema;
58 | }
59 | };
60 |
61 | export const listInvoicesPrompt = (context: Context = {}) => {
62 | const customerArg = context.customer
63 | ? `The customer is already set in the context: ${context.customer}.`
64 | : `- customer (str, optional): The ID of the customer to list invoices for.\n`;
65 |
66 | return `
67 | This tool will fetch a list of Invoices from Stripe.
68 |
69 | It takes ${context.customer ? 'one' : 'two'} argument${context.customer ? '' : 's'}:
70 | ${customerArg}
71 | - limit (int, optional): The number of invoices to return.
72 | `;
73 | };
74 |
75 | export const finalizeInvoicePrompt = (_context: Context = {}) => `
76 | This tool will finalize an invoice in Stripe.
77 |
78 | It takes one argument:
79 | - invoice (str): The ID of the invoice to finalize.
80 | `;
81 |
82 | const tool = (context: Context): StripeToolDefinition => ({
83 | method: 'list_invoices',
84 | name: 'List Invoices',
85 | description: listInvoicesPrompt(context),
86 | inputSchema: listInvoicesParameters(context),
87 | annotations: listInvoicesAnnotations(),
88 | actions: {
89 | invoices: {
90 | read: true,
91 | },
92 | },
93 | execute: listInvoices,
94 | });
95 |
96 | export default tool;
97 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/test/shared/products/functions.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {createProduct} from '@/shared/products/createProduct';
2 | import {listProducts} from '@/shared/products/listProducts';
3 |
4 | const Stripe = jest.fn().mockImplementation(() => ({
5 | products: {
6 | create: jest.fn(),
7 | list: jest.fn(),
8 | },
9 | }));
10 |
11 | let stripe: ReturnType<typeof Stripe>;
12 |
13 | beforeEach(() => {
14 | stripe = new Stripe('fake-api-key');
15 | });
16 |
17 | describe('createProduct', () => {
18 | it('should create a product and return it', async () => {
19 | const params = {
20 | name: 'Test Product',
21 | };
22 |
23 | const context = {};
24 |
25 | const mockProduct = {id: 'prod_123456', name: 'Test Product'};
26 | stripe.products.create.mockResolvedValue(mockProduct);
27 |
28 | const result = await createProduct(stripe, context, params);
29 |
30 | expect(stripe.products.create).toHaveBeenCalledWith(params, undefined);
31 | expect(result).toEqual(mockProduct);
32 | });
33 |
34 | it('should specify the connected account if included in context', async () => {
35 | const params = {
36 | name: 'Test Product',
37 | };
38 |
39 | const context = {
40 | account: 'acct_123456',
41 | };
42 |
43 | const mockProduct = {id: 'prod_123456', name: 'Test Product'};
44 | stripe.products.create.mockResolvedValue(mockProduct);
45 |
46 | const result = await createProduct(stripe, context, params);
47 |
48 | expect(stripe.products.create).toHaveBeenCalledWith(params, {
49 | stripeAccount: context.account,
50 | });
51 | expect(result).toEqual(mockProduct);
52 | });
53 | });
54 |
55 | describe('listProducts', () => {
56 | it('should list products and return them', async () => {
57 | const mockProducts = [
58 | {id: 'prod_123456', name: 'Test Product 1'},
59 | {id: 'prod_789012', name: 'Test Product 2'},
60 | ];
61 |
62 | const context = {};
63 |
64 | stripe.products.list.mockResolvedValue({data: mockProducts});
65 | const result = await listProducts(stripe, context, {});
66 |
67 | expect(stripe.products.list).toHaveBeenCalledWith({}, undefined);
68 | expect(result).toEqual(mockProducts);
69 | });
70 |
71 | it('should specify the connected account if included in context', async () => {
72 | const mockProducts = [
73 | {id: 'prod_123456', name: 'Test Product 1'},
74 | {id: 'prod_789012', name: 'Test Product 2'},
75 | ];
76 |
77 | const context = {
78 | account: 'acct_123456',
79 | };
80 |
81 | stripe.products.list.mockResolvedValue({data: mockProducts});
82 | const result = await listProducts(stripe, context, {});
83 |
84 | expect(stripe.products.list).toHaveBeenCalledWith(
85 | {},
86 | {
87 | stripeAccount: context.account,
88 | }
89 | );
90 | expect(result).toEqual(mockProducts);
91 | });
92 | });
93 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/test/shared/paymentLinks/functions.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {createPaymentLink} from '@/shared/paymentLinks/createPaymentLink';
2 |
3 | const Stripe = jest.fn().mockImplementation(() => ({
4 | paymentLinks: {
5 | create: jest.fn(),
6 | },
7 | }));
8 |
9 | let stripe: ReturnType<typeof Stripe>;
10 |
11 | beforeEach(() => {
12 | stripe = new Stripe('fake-api-key');
13 | });
14 |
15 | describe('createPaymentLink', () => {
16 | it('should create a payment link and return it', async () => {
17 | const params = {
18 | line_items: [
19 | {
20 | price: 'price_123456',
21 | quantity: 1,
22 | },
23 | ],
24 | };
25 |
26 | const mockPaymentLink = {
27 | id: 'pl_123456',
28 | url: 'https://example.com',
29 | };
30 |
31 | const context = {};
32 |
33 | stripe.paymentLinks.create.mockResolvedValue(mockPaymentLink);
34 |
35 | const result = await createPaymentLink(stripe, context, {
36 | price: 'price_123456',
37 | quantity: 1,
38 | });
39 |
40 | expect(stripe.paymentLinks.create).toHaveBeenCalledWith(params, undefined);
41 | expect(result).toEqual(mockPaymentLink);
42 | });
43 |
44 | it('should specify the connected account if included in context', async () => {
45 | const params = {
46 | line_items: [
47 | {
48 | price: 'price_123456',
49 | quantity: 1,
50 | },
51 | ],
52 | };
53 |
54 | const mockPaymentLink = {
55 | id: 'pl_123456',
56 | url: 'https://example.com',
57 | };
58 |
59 | const context = {
60 | account: 'acct_123456',
61 | };
62 |
63 | stripe.paymentLinks.create.mockResolvedValue(mockPaymentLink);
64 |
65 | const result = await createPaymentLink(stripe, context, {
66 | price: 'price_123456',
67 | quantity: 1,
68 | });
69 |
70 | expect(stripe.paymentLinks.create).toHaveBeenCalledWith(params, {
71 | stripeAccount: context.account,
72 | });
73 | expect(result).toEqual(mockPaymentLink);
74 | });
75 |
76 | it('should specify the redirect URL if included in params', async () => {
77 | const params = {
78 | line_items: [
79 | {
80 | price: 'price_123456',
81 | quantity: 1,
82 | },
83 | ],
84 | after_completion: {
85 | type: 'redirect',
86 | redirect: {
87 | url: 'https://example.com',
88 | },
89 | },
90 | };
91 |
92 | const mockPaymentLink = {
93 | id: 'pl_123456',
94 | url: 'https://example.com',
95 | };
96 |
97 | const context = {};
98 |
99 | stripe.paymentLinks.create.mockResolvedValue(mockPaymentLink);
100 |
101 | const result = await createPaymentLink(stripe, context, {
102 | price: 'price_123456',
103 | quantity: 1,
104 | redirect_url: 'https://example.com',
105 | });
106 |
107 | expect(stripe.paymentLinks.create).toHaveBeenCalledWith(params, undefined);
108 | expect(result).toEqual(mockPaymentLink);
109 | });
110 | });
111 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/test/shared/documentation/functions.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {searchDocumentation} from '@/shared/documentation/searchDocumentation';
2 | import {z} from 'zod';
3 | import {searchDocumentationParameters} from '@/shared/documentation/searchDocumentation';
4 |
5 | const Stripe = jest.fn().mockImplementation(() => ({}));
6 |
7 | let stripe: ReturnType<typeof Stripe>;
8 |
9 | beforeEach(() => {
10 | stripe = new Stripe('fake-api-key');
11 | });
12 |
13 | const EXPECTED_HEADERS = {
14 | 'Content-Type': 'application/json',
15 | 'X-Requested-With': 'fetch',
16 | 'User-Agent': 'stripe-agent-toolkit-typescript',
17 | };
18 |
19 | describe('searchDocumentation', () => {
20 | it('should search for Stripe documentation and return sources', async () => {
21 | const question = 'How to create Stripe checkout session?';
22 | const requestBody: z.infer<
23 | ReturnType<typeof searchDocumentationParameters>
24 | > = {
25 | question: question,
26 | language: 'ruby',
27 | };
28 |
29 | const sources = [
30 | {
31 | type: 'docs',
32 | url: 'https://docs.stripe.com/payments/checkout/how-checkout-works',
33 | title: 'How checkout works',
34 | content: '...',
35 | },
36 | ];
37 | const mockResponse = {
38 | question: question,
39 | status: 'success',
40 | sources: sources,
41 | };
42 |
43 | const fetchMock = jest.spyOn(global, 'fetch').mockResolvedValueOnce({
44 | ok: true,
45 | status: 200,
46 | json: jest.fn().mockResolvedValueOnce(mockResponse),
47 | } as unknown as Response);
48 |
49 | const result = await searchDocumentation(stripe, {}, requestBody);
50 |
51 | expect(fetchMock).toHaveBeenCalledWith('https://ai.stripe.com/search', {
52 | method: 'POST',
53 | headers: EXPECTED_HEADERS,
54 | body: JSON.stringify(requestBody),
55 | });
56 |
57 | expect(result).toEqual(sources);
58 | });
59 |
60 | it('should return failure string if search failed', async () => {
61 | const question = 'What is the meaning of life?';
62 | const requestBody = {
63 | question: question,
64 | };
65 |
66 | const mockError = {
67 | error: 'Invalid query',
68 | message:
69 | 'Unable to process your question. Please rephrase it to be more specific.',
70 | };
71 |
72 | const fetchMock = jest.spyOn(global, 'fetch').mockResolvedValueOnce({
73 | ok: false,
74 | status: 400,
75 | json: jest.fn().mockResolvedValueOnce(mockError),
76 | } as unknown as Response);
77 |
78 | const result = await searchDocumentation(stripe, {}, requestBody);
79 |
80 | expect(fetchMock).toHaveBeenCalledWith('https://ai.stripe.com/search', {
81 | method: 'POST',
82 | headers: EXPECTED_HEADERS,
83 | body: JSON.stringify(requestBody),
84 | });
85 |
86 | expect(result).toEqual('Failed to search documentation');
87 | });
88 | });
89 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/shared/documentation/searchDocumentation.ts:
--------------------------------------------------------------------------------
```typescript
1 | import Stripe from 'stripe';
2 | import {z} from 'zod';
3 | import type {Context} from '@/shared/configuration';
4 | import type {StripeToolDefinition} from '@/shared/tools';
5 | export const searchDocumentationPrompt = (_context: Context = {}) => `
6 | This tool will take in a user question about integrating with Stripe in their application, then search and retrieve relevant Stripe documentation to answer the question.
7 |
8 | It takes two arguments:
9 | - question (str): The user question to search an answer for in the Stripe documentation.
10 | - language (str, optional): The programming language to search for in the the documentation.
11 | `;
12 |
13 | export const searchDocumentationParameters = (
14 | _context: Context = {}
15 | ): z.AnyZodObject =>
16 | z.object({
17 | question: z
18 | .string()
19 | .describe(
20 | 'The user question about integrating with Stripe will be used to search the documentation.'
21 | ),
22 | language: z
23 | .enum(['dotnet', 'go', 'java', 'node', 'php', 'ruby', 'python', 'curl'])
24 | .optional()
25 | .describe(
26 | 'The programming language to search for in the the documentation.'
27 | ),
28 | });
29 |
30 | export const searchDocumentationAnnotations = () => ({
31 | destructiveHint: false,
32 | idempotentHint: true,
33 | openWorldHint: true,
34 | readOnlyHint: true,
35 | title: 'Search Stripe documentation',
36 | });
37 |
38 | export const searchDocumentation = async (
39 | _stripe: Stripe,
40 | context: Context,
41 | params: z.infer<ReturnType<typeof searchDocumentationParameters>>
42 | ) => {
43 | try {
44 | const endpoint = 'https://ai.stripe.com/search';
45 | const response = await fetch(endpoint, {
46 | method: 'POST',
47 | headers: {
48 | 'Content-Type': 'application/json',
49 | 'X-Requested-With': 'fetch',
50 | 'User-Agent':
51 | context.mode === 'modelcontextprotocol'
52 | ? 'stripe-mcp'
53 | : 'stripe-agent-toolkit-typescript',
54 | },
55 | body: JSON.stringify(params),
56 | });
57 |
58 | // If status not in 200-299 range, throw error
59 | if (!response.ok) {
60 | throw new Error(`HTTP error! Status: ${response.status}`);
61 | }
62 |
63 | const data = await response.json();
64 | return data?.sources;
65 | } catch (error) {
66 | return 'Failed to search documentation';
67 | }
68 | };
69 |
70 | const tool = (context: Context): StripeToolDefinition => ({
71 | method: 'search_stripe_documentation',
72 | name: 'Search Stripe Documentation',
73 | description: searchDocumentationPrompt(context),
74 | inputSchema: searchDocumentationParameters(context),
75 | annotations: searchDocumentationAnnotations(),
76 | actions: {
77 | documentation: {
78 | read: true,
79 | },
80 | },
81 | execute: searchDocumentation,
82 | });
83 |
84 | export default tool;
85 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/test/shared/prices/functions.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {createPrice} from '@/shared/prices/createPrice';
2 | import {listPrices} from '@/shared/prices/listPrices';
3 |
4 | const Stripe = jest.fn().mockImplementation(() => ({
5 | prices: {
6 | create: jest.fn(),
7 | list: jest.fn(),
8 | },
9 | }));
10 |
11 | let stripe: ReturnType<typeof Stripe>;
12 |
13 | beforeEach(() => {
14 | stripe = new Stripe('fake-api-key');
15 | });
16 |
17 | describe('createPrice', () => {
18 | it('should create a price and return it', async () => {
19 | const params = {
20 | unit_amount: 1000,
21 | currency: 'usd',
22 | product: 'prod_123456',
23 | };
24 |
25 | const context = {};
26 |
27 | const mockPrice = {id: 'price_123456', unit_amount: 1000, currency: 'usd'};
28 | stripe.prices.create.mockResolvedValue(mockPrice);
29 |
30 | const result = await createPrice(stripe, context, params);
31 |
32 | expect(stripe.prices.create).toHaveBeenCalledWith(params, undefined);
33 | expect(result).toEqual(mockPrice);
34 | });
35 |
36 | it('should specify the connected account if included in context', async () => {
37 | const params = {
38 | unit_amount: 1000,
39 | currency: 'usd',
40 | product: 'prod_123456',
41 | };
42 |
43 | const context = {
44 | account: 'acct_123456',
45 | };
46 |
47 | const mockPrice = {id: 'price_123456', unit_amount: 1000, currency: 'usd'};
48 | stripe.prices.create.mockResolvedValue(mockPrice);
49 |
50 | const result = await createPrice(stripe, context, params);
51 |
52 | expect(stripe.prices.create).toHaveBeenCalledWith(params, {
53 | stripeAccount: context.account,
54 | });
55 | expect(result).toEqual(mockPrice);
56 | });
57 | });
58 |
59 | describe('listPrices', () => {
60 | it('should list prices and return them', async () => {
61 | const mockPrices = [
62 | {id: 'price_123456', unit_amount: 1000, currency: 'usd'},
63 | {id: 'price_789012', unit_amount: 2000, currency: 'usd'},
64 | ];
65 |
66 | const context = {};
67 |
68 | stripe.prices.list.mockResolvedValue({data: mockPrices});
69 | const result = await listPrices(stripe, context, {});
70 |
71 | expect(stripe.prices.list).toHaveBeenCalledWith({}, undefined);
72 | expect(result).toEqual(mockPrices);
73 | });
74 |
75 | it('should specify the connected account if included in context', async () => {
76 | const mockPrices = [
77 | {id: 'price_123456', unit_amount: 1000, currency: 'usd'},
78 | {id: 'price_789012', unit_amount: 2000, currency: 'usd'},
79 | ];
80 |
81 | const context = {
82 | account: 'acct_123456',
83 | };
84 |
85 | stripe.prices.list.mockResolvedValue({data: mockPrices});
86 | const result = await listPrices(stripe, context, {});
87 |
88 | expect(stripe.prices.list).toHaveBeenCalledWith(
89 | {},
90 | {
91 | stripeAccount: context.account,
92 | }
93 | );
94 | expect(result).toEqual(mockPrices);
95 | });
96 | });
97 |
```
--------------------------------------------------------------------------------
/llm/ai-sdk/meter/meter-event-logging.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type Stripe from 'stripe';
2 | import type {UsageEvent, MeterConfig} from './meter-event-types';
3 |
4 | /**
5 | * Normalize model names to match Stripe's approved model list
6 | */
7 | function normalizeModelName(provider: string, model: string): string {
8 | if (provider === 'anthropic') {
9 | // Remove date suffix (YYYYMMDD format at the end)
10 | model = model.replace(/-\d{8}$/, '');
11 |
12 | // Remove -latest suffix
13 | model = model.replace(/-latest$/, '');
14 |
15 | // Convert version number dashes to dots anywhere in the name
16 | // Match patterns like claude-3-7, opus-4-1, sonnet-4-5, etc.
17 | model = model.replace(/(-[a-z]+)?-(\d+)-(\d+)/g, '$1-$2.$3');
18 |
19 | return model;
20 | }
21 |
22 | if (provider === 'openai') {
23 | // Exception: keep gpt-4o-2024-05-13 as is
24 | if (model === 'gpt-4o-2024-05-13') {
25 | return model;
26 | }
27 |
28 | // Remove date suffix in format -YYYY-MM-DD
29 | model = model.replace(/-\d{4}-\d{2}-\d{2}$/, '');
30 |
31 | return model;
32 | }
33 |
34 | // For other providers (google), return as is
35 | return model;
36 | }
37 |
38 | /**
39 | * Send meter events to Stripe
40 | */
41 | export async function sendMeterEventsToStripe(
42 | stripeClient: Stripe,
43 | config: MeterConfig,
44 | event: UsageEvent
45 | ): Promise<void> {
46 | const timestamp = new Date().toISOString();
47 |
48 | // Normalize the model name before sending to Stripe
49 | const normalizedModel = normalizeModelName(event.provider, event.model);
50 | const fullModelName = event.provider + '/' + normalizedModel;
51 |
52 | try {
53 | if (event.usage.inputTokens > 0) {
54 | const inputPayload = {
55 | event_name: 'token-billing-tokens',
56 | timestamp,
57 | payload: {
58 | stripe_customer_id: event.stripeCustomerId,
59 | value: event.usage.inputTokens.toString(),
60 | model: fullModelName,
61 | token_type: 'input',
62 | },
63 | };
64 | await stripeClient.v2.billing.meterEvents.create(inputPayload);
65 | }
66 |
67 | if (event.usage.outputTokens > 0) {
68 | const outputPayload = {
69 | event_name: 'token-billing-tokens',
70 | timestamp,
71 | payload: {
72 | stripe_customer_id: event.stripeCustomerId,
73 | value: event.usage.outputTokens.toString(),
74 | model: fullModelName,
75 | token_type: 'output',
76 | },
77 | };
78 | await stripeClient.v2.billing.meterEvents.create(outputPayload);
79 | }
80 | } catch (error) {
81 | console.error('Error sending meter events to Stripe:', error);
82 | }
83 | }
84 |
85 | /**
86 | * Log usage event (fire-and-forget style)
87 | */
88 | export function logUsageEvent(
89 | stripeClient: Stripe,
90 | config: MeterConfig,
91 | event: UsageEvent
92 | ): void {
93 | sendMeterEventsToStripe(stripeClient, config, event).catch((err) => {
94 | console.error('Failed to send meter events to Stripe:', err);
95 | });
96 | }
97 |
98 |
99 |
```
--------------------------------------------------------------------------------
/llm/token-meter/meter-event-logging.ts:
--------------------------------------------------------------------------------
```typescript
1 | import type Stripe from 'stripe';
2 | import type {UsageEvent, MeterConfig} from './meter-event-types';
3 |
4 | /**
5 | * Normalize model names to match Stripe's approved model list
6 | */
7 | function normalizeModelName(provider: string, model: string): string {
8 | if (provider === 'anthropic') {
9 | // Remove date suffix (YYYYMMDD format at the end)
10 | model = model.replace(/-\d{8}$/, '');
11 |
12 | // Remove -latest suffix
13 | model = model.replace(/-latest$/, '');
14 |
15 | // Convert version number dashes to dots anywhere in the name
16 | // Match patterns like claude-3-7, opus-4-1, sonnet-4-5, etc.
17 | model = model.replace(/(-[a-z]+)?-(\d+)-(\d+)/g, '$1-$2.$3');
18 |
19 | return model;
20 | }
21 |
22 | if (provider === 'openai') {
23 | // Exception: keep gpt-4o-2024-05-13 as is
24 | if (model === 'gpt-4o-2024-05-13') {
25 | return model;
26 | }
27 |
28 | // Remove date suffix in format -YYYY-MM-DD
29 | model = model.replace(/-\d{4}-\d{2}-\d{2}$/, '');
30 |
31 | return model;
32 | }
33 |
34 | // For other providers (google), return as is
35 | return model;
36 | }
37 |
38 | /**
39 | * Send meter events to Stripe
40 | */
41 | export async function sendMeterEventsToStripe(
42 | stripeClient: Stripe,
43 | config: MeterConfig,
44 | event: UsageEvent
45 | ): Promise<void> {
46 | const timestamp = new Date().toISOString();
47 |
48 | // Normalize the model name before sending to Stripe
49 | const normalizedModel = normalizeModelName(event.provider, event.model);
50 | const fullModelName = event.provider + '/' + normalizedModel;
51 |
52 | try {
53 | if (event.usage.inputTokens > 0) {
54 | const inputPayload = {
55 | event_name: 'token-billing-tokens',
56 | timestamp,
57 | payload: {
58 | stripe_customer_id: event.stripeCustomerId,
59 | value: event.usage.inputTokens.toString(),
60 | model: fullModelName,
61 | token_type: 'input',
62 | },
63 | };
64 | await stripeClient.v2.billing.meterEvents.create(inputPayload);
65 | }
66 |
67 | if (event.usage.outputTokens > 0) {
68 | const outputPayload = {
69 | event_name: 'token-billing-tokens',
70 | timestamp,
71 | payload: {
72 | stripe_customer_id: event.stripeCustomerId,
73 | value: event.usage.outputTokens.toString(),
74 | model: fullModelName,
75 | token_type: 'output',
76 | },
77 | };
78 | await stripeClient.v2.billing.meterEvents.create(outputPayload);
79 | }
80 | } catch (error) {
81 | console.error('Error sending meter events to Stripe:', error);
82 | }
83 | }
84 |
85 | /**
86 | * Log usage event (fire-and-forget style)
87 | */
88 | export function logUsageEvent(
89 | stripeClient: Stripe,
90 | config: MeterConfig,
91 | event: UsageEvent
92 | ): void {
93 | sendMeterEventsToStripe(stripeClient, config, event).catch((err) => {
94 | console.error('Failed to send meter events to Stripe:', err);
95 | });
96 | }
97 |
98 |
99 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/test/shared/customers/functions.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {createCustomer} from '@/shared/customers/createCustomer';
2 | import {listCustomers} from '@/shared/customers/listCustomers';
3 |
4 | const Stripe = jest.fn().mockImplementation(() => ({
5 | customers: {
6 | create: jest.fn(),
7 | list: jest.fn(),
8 | },
9 | }));
10 |
11 | let stripe: ReturnType<typeof Stripe>;
12 |
13 | beforeEach(() => {
14 | stripe = new Stripe('fake-api-key');
15 | });
16 |
17 | describe('createCustomer', () => {
18 | it('should create a customer and return the id', async () => {
19 | const params = {
20 | email: '[email protected]',
21 | name: 'Test User',
22 | };
23 |
24 | const context = {};
25 |
26 | const mockCustomer = {id: 'cus_123456', email: '[email protected]'};
27 | stripe.customers.create.mockResolvedValue(mockCustomer);
28 |
29 | const result = await createCustomer(stripe, context, params);
30 |
31 | expect(stripe.customers.create).toHaveBeenCalledWith(params, undefined);
32 | expect(result).toEqual({id: mockCustomer.id});
33 | });
34 |
35 | it('should specify the connected account if included in context', async () => {
36 | const params = {
37 | email: '[email protected]',
38 | name: 'Test User',
39 | };
40 |
41 | const context = {
42 | account: 'acct_123456',
43 | };
44 |
45 | const mockCustomer = {id: 'cus_123456', email: '[email protected]'};
46 | stripe.customers.create.mockResolvedValue(mockCustomer);
47 |
48 | const result = await createCustomer(stripe, context, params);
49 |
50 | expect(stripe.customers.create).toHaveBeenCalledWith(params, {
51 | stripeAccount: context.account,
52 | });
53 | expect(result).toEqual({id: mockCustomer.id});
54 | });
55 | });
56 |
57 | describe('listCustomers', () => {
58 | it('should list customers and return their ids', async () => {
59 | const mockCustomers = [
60 | {id: 'cus_123456', email: '[email protected]'},
61 | {id: 'cus_789012', email: '[email protected]'},
62 | ];
63 |
64 | const context = {};
65 |
66 | stripe.customers.list.mockResolvedValue({data: mockCustomers});
67 | const result = await listCustomers(stripe, context, {});
68 |
69 | expect(stripe.customers.list).toHaveBeenCalledWith({}, undefined);
70 | expect(result).toEqual(mockCustomers.map(({id}) => ({id})));
71 | });
72 |
73 | it('should specify the connected account if included in context', async () => {
74 | const mockCustomers = [
75 | {id: 'cus_123456', email: '[email protected]'},
76 | {id: 'cus_789012', email: '[email protected]'},
77 | ];
78 |
79 | const context = {
80 | account: 'acct_123456',
81 | };
82 |
83 | stripe.customers.list.mockResolvedValue({data: mockCustomers});
84 | const result = await listCustomers(stripe, context, {});
85 |
86 | expect(stripe.customers.list).toHaveBeenCalledWith(
87 | {},
88 | {
89 | stripeAccount: context.account,
90 | }
91 | );
92 | expect(result).toEqual(mockCustomers.map(({id}) => ({id})));
93 | });
94 | });
95 |
```
--------------------------------------------------------------------------------
/tools/python/examples/openai/customer_support/main.py:
--------------------------------------------------------------------------------
```python
1 | import env
2 | import asyncio
3 | from emailer import Emailer, Email
4 | from typing import Union, List
5 | import support_agent
6 | import markdown as markdown
7 | import json
8 |
9 | from agents import (
10 | ItemHelpers,
11 | TResponseInputItem,
12 | )
13 |
14 |
15 | env.ensure("STRIPE_SECRET_KEY")
16 | env.ensure("OPENAI_API_KEY")
17 |
18 | email_address = env.ensure("EMAIL_ADDRESS")
19 | support_address = env.get_or("SUPPORT_ADDRESS", email_address)
20 | email_password = env.ensure("EMAIL_PASSWORD")
21 | emailer = Emailer(email_address, email_password, support_address)
22 |
23 |
24 | def unsure(str: str) -> bool:
25 | return (
26 | "not sure" in str
27 | or "unsure" in str
28 | or "don't know" in str
29 | or "dont know" in str
30 | or "do not know" in str
31 | )
32 |
33 |
34 | async def respond(thread: List[Email]) -> Union[Email, None]:
35 | most_recent = thread[-1]
36 | print(f"Got unread email:\n {json.dumps(most_recent.to_dict())}")
37 |
38 | # Loop through the entire thread to add historical context for the agent
39 | input_items: list[TResponseInputItem] = []
40 | for email in thread:
41 | input_items.append(
42 | {
43 | "content": (
44 | "This is an earlier email:"
45 | f"Email from: {email.from_address}\n"
46 | f"To: {email.to_address}\n"
47 | f"Subject: {email.subject}\n\n"
48 | f"{email.body}"
49 | ),
50 | "role": "user",
51 | }
52 | )
53 |
54 | input_items.append(
55 | {
56 | "content": (
57 | "This the latest email"
58 | "You can use context from earlier emails"
59 | "but reply specifically to the following email:"
60 | f"Email from: {most_recent.from_address}\n"
61 | f"To: {most_recent.to_address}\n"
62 | f"Subject: {most_recent.subject}\n\n"
63 | f"{most_recent.body}"
64 | ),
65 | "role": "user",
66 | }
67 | )
68 |
69 | print(f"Sending to agent:\n {json.dumps(input_items)}")
70 |
71 | output = await support_agent.run(input_items)
72 | body_md = ItemHelpers.text_message_outputs(output.new_items)
73 |
74 | # Handle answers that the agent doesn't know
75 | if unsure(body_md.lower()):
76 | print(
77 | f"Agent doesn't know, ignore response and keep email in the inbox:\n{body_md}"
78 | )
79 | return None
80 |
81 | # OpenAI often returns the body in html fences, trim those
82 | body_html = markdown.markdown(body_md, extensions=["tables"])
83 |
84 | return Email(
85 | from_address=most_recent.to_address,
86 | to_address=most_recent.from_address,
87 | subject=most_recent.subject,
88 | body=body_html,
89 | )
90 |
91 |
92 | async def main():
93 | await emailer.run(respond, delay=30, mark_read=True)
94 |
95 |
96 | if __name__ == "__main__":
97 | asyncio.run(main())
98 |
```
--------------------------------------------------------------------------------
/llm/ai-sdk/meter/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | /**
2 | * Stripe AI SDK Billing Wrapper Integration
3 | *
4 | * This module provides a wrapper for Vercel AI SDK models that automatically
5 | * reports token usage to Stripe meter events for billing purposes.
6 | */
7 |
8 | import type {LanguageModelV2} from '@ai-sdk/provider';
9 | import {AISDKWrapperV2} from './wrapperV2';
10 | import type {AIMeterConfig} from './types';
11 |
12 | export * from './types';
13 |
14 | /**
15 | * Wraps a Vercel AI SDK language model to automatically report usage to Stripe meter events.
16 | *
17 | * This function wraps AI SDK v2 models and returns a wrapped version that sends token usage
18 | * to Stripe for billing.
19 | *
20 | * **IMPORTANT:** Only LanguageModelV2 models are supported. If you try to pass a v1 model
21 | * (like Groq), you will get a TypeScript compilation error. This is intentional to ensure
22 | * metering works correctly.
23 | *
24 | * Works with ANY AI SDK provider that implements LanguageModelV2, including:
25 | * - OpenAI (`@ai-sdk/openai`)
26 | * - Anthropic (`@ai-sdk/anthropic`)
27 | * - Google (`@ai-sdk/google`)
28 | * - Azure OpenAI (via `@ai-sdk/openai`)
29 | * - Amazon Bedrock (`@ai-sdk/amazon-bedrock`)
30 | * - Together AI (via `createOpenAI` from `@ai-sdk/openai`)
31 | * - Any other provider with v2 specification
32 | *
33 | * Note: Providers using v1 specification (like Groq) are not supported
34 | *
35 | * @template T - The type of the language model (must extend LanguageModelV2).
36 | * @param model - The Vercel AI SDK language model instance to wrap (must be v2).
37 | * @param stripeApiKey - The Stripe API key.
38 | * @param stripeCustomerId - The Stripe customer ID for meter events.
39 | * @returns The wrapped model with Stripe meter event tracking.
40 | *
41 | * @example
42 | * ```typescript
43 | * import { meteredModel } from '@stripe/ai-sdk/meter';
44 | * import { openai } from '@ai-sdk/openai';
45 | * import { generateText } from 'ai';
46 | *
47 | * const model = meteredModel(openai('gpt-4'), STRIPE_API_KEY, STRIPE_CUSTOMER_ID);
48 | *
49 | * const { text } = await generateText({
50 | * model: model,
51 | * prompt: 'Say "Hello, World!" and nothing else.',
52 | * });
53 | * ```
54 | */
55 | export function meteredModel<T extends LanguageModelV2>(
56 | model: T,
57 | stripeApiKey: string,
58 | stripeCustomerId: string
59 | ): T {
60 | const config: AIMeterConfig = {
61 | stripeApiKey,
62 | stripeCustomerId,
63 | };
64 |
65 | // Verify at runtime that the model is actually v2
66 | if (
67 | !model ||
68 | typeof model !== 'object' ||
69 | !('specificationVersion' in model) ||
70 | model.specificationVersion !== 'v2'
71 | ) {
72 | throw new Error(
73 | '[Stripe AI SDK] Invalid model: Only LanguageModelV2 models are supported. ' +
74 | 'The provided model does not have specificationVersion "v2". ' +
75 | 'Please use a supported provider (OpenAI, Anthropic, Google, etc.).'
76 | );
77 | }
78 |
79 | return new AISDKWrapperV2(model, config) as unknown as T;
80 | }
81 |
82 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/test/shared/configuration.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {z} from 'zod';
2 | import {isToolAllowed} from '@/shared/configuration';
3 |
4 | describe('isToolAllowed', () => {
5 | it('should return true if all permissions are allowed', () => {
6 | const tool = {
7 | method: 'test',
8 | name: 'Test',
9 | description: 'Test',
10 | inputSchema: z.object({
11 | foo: z.string(),
12 | }),
13 | annotations: {
14 | destructiveHint: false,
15 | idempotentHint: false,
16 | openWorldHint: true,
17 | readOnlyHint: false,
18 | title: 'Test',
19 | },
20 | execute: async (a: any, b: any, c: any) => {},
21 | actions: {
22 | customers: {
23 | create: true,
24 | read: true,
25 | },
26 | invoices: {
27 | create: true,
28 | read: true,
29 | },
30 | },
31 | };
32 |
33 | const configuration = {
34 | actions: {
35 | customers: {
36 | create: true,
37 | read: true,
38 | },
39 | invoices: {
40 | create: true,
41 | read: true,
42 | },
43 | },
44 | };
45 |
46 | expect(isToolAllowed(tool, configuration)).toBe(true);
47 | });
48 |
49 | it('should return false if any permission is denied', () => {
50 | const tool = {
51 | method: 'test',
52 | name: 'Test',
53 | description: 'Test',
54 | inputSchema: z.object({
55 | foo: z.string(),
56 | }),
57 | annotations: {
58 | destructiveHint: false,
59 | idempotentHint: false,
60 | openWorldHint: true,
61 | readOnlyHint: false,
62 | title: 'Test',
63 | },
64 | execute: async (a: any, b: any, c: any) => {},
65 | actions: {
66 | customers: {
67 | create: true,
68 | read: true,
69 | },
70 | invoices: {
71 | create: true,
72 | read: true,
73 | },
74 | },
75 | };
76 |
77 | const configuration = {
78 | actions: {
79 | customers: {
80 | create: true,
81 | read: true,
82 | },
83 | invoices: {
84 | create: true,
85 | read: false,
86 | },
87 | },
88 | };
89 |
90 | expect(isToolAllowed(tool, configuration)).toBe(false);
91 | });
92 |
93 | it('should return false if any resource is not allowed', () => {
94 | const tool = {
95 | method: 'test',
96 | name: 'Test',
97 | description: 'Test',
98 | inputSchema: z.object({
99 | foo: z.string(),
100 | }),
101 | annotations: {
102 | destructiveHint: false,
103 | idempotentHint: false,
104 | openWorldHint: true,
105 | readOnlyHint: false,
106 | title: 'Test',
107 | },
108 | execute: async (a: any, b: any, c: any) => {},
109 | actions: {
110 | paymentLinks: {
111 | create: true,
112 | },
113 | },
114 | };
115 |
116 | const configuration = {
117 | actions: {
118 | customers: {
119 | create: true,
120 | read: true,
121 | },
122 | invoices: {
123 | create: true,
124 | read: true,
125 | },
126 | },
127 | };
128 |
129 | expect(isToolAllowed(tool, configuration)).toBe(false);
130 | });
131 | });
132 |
```
--------------------------------------------------------------------------------
/tools/python/stripe_agent_toolkit/api.py:
--------------------------------------------------------------------------------
```python
1 | """Util that calls Stripe."""
2 |
3 | from __future__ import annotations
4 |
5 | import json
6 | import stripe
7 | from typing import Optional, Dict, Callable, Any
8 | from pydantic import BaseModel
9 |
10 | from .configuration import Context
11 |
12 | from .functions import (
13 | create_customer,
14 | list_customers,
15 | create_product,
16 | list_products,
17 | create_price,
18 | list_prices,
19 | create_payment_link,
20 | list_invoices,
21 | create_invoice,
22 | create_invoice_item,
23 | finalize_invoice,
24 | retrieve_balance,
25 | create_refund,
26 | list_payment_intents,
27 | create_billing_portal_session,
28 | )
29 |
30 |
31 | class StripeAPI(BaseModel):
32 | """ "Wrapper for Stripe API"""
33 |
34 | _context: Context
35 | _method_dispatch: Dict[str, Callable[..., Any]]
36 |
37 | def __init__(self, secret_key: str, context: Optional[Context]):
38 | super().__init__()
39 |
40 | self._context = context if context is not None else Context()
41 |
42 | self._method_dispatch = {
43 | "create_customer": create_customer,
44 | "list_customers": list_customers,
45 | "create_product": create_product,
46 | "list_products": list_products,
47 | "create_price": create_price,
48 | "list_prices": list_prices,
49 | "create_payment_link": create_payment_link,
50 | "list_invoices": list_invoices,
51 | "create_invoice": create_invoice,
52 | "create_invoice_item": create_invoice_item,
53 | "finalize_invoice": finalize_invoice,
54 | "retrieve_balance": retrieve_balance,
55 | "create_refund": create_refund,
56 | "list_payment_intents": list_payment_intents,
57 | "create_billing_portal_session": create_billing_portal_session,
58 | }
59 |
60 | stripe.api_key = secret_key
61 | stripe.set_app_info(
62 | "stripe-agent-toolkit-python",
63 | version="0.6.2",
64 | url="https://github.com/stripe/ai",
65 | )
66 |
67 | def create_meter_event(
68 | self, event: str, customer: str, value: Optional[str] = None
69 | ) -> str:
70 | meter_event_data: dict = {
71 | "event_name": event,
72 | "payload": {
73 | "stripe_customer_id": customer,
74 | },
75 | }
76 | if value is not None:
77 | meter_event_data["payload"]["value"] = value
78 |
79 | if self._context.get("account") is not None:
80 | account = self._context.get("account")
81 | if account is not None:
82 | meter_event_data["stripe_account"] = account
83 |
84 | stripe.billing.MeterEvent.create(**meter_event_data)
85 |
86 | def run(self, method: str, *args, **kwargs) -> str:
87 | if method not in self._method_dispatch:
88 | raise ValueError(f"Invalid method: {method}")
89 |
90 | function = self._method_dispatch[method]
91 | result = function(self._context, *args, **kwargs)
92 | return json.dumps(result)
93 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/shared/subscriptions/listSubscriptions.ts:
--------------------------------------------------------------------------------
```typescript
1 | import Stripe from 'stripe';
2 | import {z} from 'zod';
3 | import type {Context} from '@/shared/configuration';
4 | import type {StripeToolDefinition} from '@/shared/tools';
5 |
6 | export const listSubscriptions = async (
7 | stripe: Stripe,
8 | context: Context,
9 | params: z.infer<ReturnType<typeof listSubscriptionsParameters>>
10 | ) => {
11 | try {
12 | if (context.customer) {
13 | params.customer = context.customer;
14 | }
15 |
16 | const subscriptions = await stripe.subscriptions.list(
17 | params,
18 | context.account ? {stripeAccount: context.account} : undefined
19 | );
20 |
21 | return subscriptions.data;
22 | } catch (error) {
23 | return 'Failed to list subscriptions';
24 | }
25 | };
26 |
27 | export const listSubscriptionsParameters = (
28 | context: Context = {}
29 | ): z.AnyZodObject => {
30 | const schema = z.object({
31 | customer: z
32 | .string()
33 | .optional()
34 | .describe('The ID of the customer to list subscriptions for.'),
35 | price: z
36 | .string()
37 | .optional()
38 | .describe('The ID of the price to list subscriptions for.'),
39 | status: z
40 | .enum([
41 | 'active',
42 | 'past_due',
43 | 'unpaid',
44 | 'canceled',
45 | 'incomplete',
46 | 'incomplete_expired',
47 | 'trialing',
48 | 'all',
49 | ])
50 | .optional()
51 | .describe('The status of the subscriptions to retrieve.'),
52 | limit: z
53 | .number()
54 | .int()
55 | .min(1)
56 | .max(100)
57 | .optional()
58 | .describe(
59 | 'A limit on the number of objects to be returned. Limit can range between 1 and 100.'
60 | ),
61 | });
62 |
63 | if (context.customer) {
64 | return schema.omit({customer: true});
65 | } else {
66 | return schema;
67 | }
68 | };
69 |
70 | export const listSubscriptionsPrompt = (context: Context = {}): string => {
71 | const customerArg = context.customer
72 | ? `The customer is already set in the context: ${context.customer}.`
73 | : `- customer (str, optional): The ID of the customer to list subscriptions for.\n`;
74 |
75 | return `
76 | This tool will list all subscriptions in Stripe.
77 |
78 | It takes ${context.customer ? 'three' : 'four'} arguments:
79 | ${customerArg}
80 | - price (str, optional): The ID of the price to list subscriptions for.
81 | - status (str, optional): The status of the subscriptions to list.
82 | - limit (int, optional): The number of subscriptions to return.
83 | `;
84 | };
85 |
86 | export const listSubscriptionsAnnotations = () => ({
87 | destructiveHint: false,
88 | idempotentHint: true,
89 | openWorldHint: true,
90 | readOnlyHint: true,
91 | title: 'List subscriptions',
92 | });
93 |
94 | const tool = (context: Context): StripeToolDefinition => ({
95 | method: 'list_subscriptions',
96 | name: 'List Subscriptions',
97 | description: listSubscriptionsPrompt(context),
98 | inputSchema: listSubscriptionsParameters(context),
99 | annotations: listSubscriptionsAnnotations(),
100 | actions: {
101 | subscriptions: {
102 | read: true,
103 | },
104 | },
105 | execute: listSubscriptions,
106 | });
107 |
108 | export default tool;
109 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/shared/tools.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {z} from 'zod';
2 |
3 | import createCustomerTool from '@/shared/customers/createCustomer';
4 | import listCustomersTool from '@/shared/customers/listCustomers';
5 | import createProductTool from '@/shared/products/createProduct';
6 | import listProductsTool from '@/shared/products/listProducts';
7 | import createPriceTool from '@/shared/prices/createPrice';
8 | import listPricesTool from '@/shared/prices/listPrices';
9 | import createPaymentLinkTool from '@/shared/paymentLinks/createPaymentLink';
10 | import createInvoiceTool from '@/shared/invoices/createInvoice';
11 | import listInvoicesTool from '@/shared/invoices/listInvoices';
12 | import createInvoiceItemTool from '@/shared/invoiceItems/createInvoiceItem';
13 | import finalizeInvoiceTool from '@/shared/invoices/finalizeInvoice';
14 | import retrieveBalanceTool from '@/shared/balance/retrieveBalance';
15 | import listCouponsTool from '@/shared/coupons/listCoupons';
16 | import createCouponTool from '@/shared/coupons/createCoupon';
17 | import createRefundTool from '@/shared/refunds/createRefund';
18 | import listPaymentIntentsTool from '@/shared/paymentIntents/listPaymentIntents';
19 | import listSubscriptionsTool from '@/shared/subscriptions/listSubscriptions';
20 | import cancelSubscriptionTool from '@/shared/subscriptions/cancelSubscription';
21 | import updateSubscriptionTool from '@/shared/subscriptions/updateSubscription';
22 | import searchDocumentationTool from '@/shared/documentation/searchDocumentation';
23 | import listDisputesTool from '@/shared/disputes/listDisputes';
24 | import updateDisputeTool from '@/shared/disputes/updateDispute';
25 |
26 | import {Context} from './configuration';
27 | import Stripe from 'stripe';
28 |
29 | export type StripeToolDefinition = {
30 | method: string;
31 | name: string;
32 | description: string;
33 | inputSchema: z.ZodObject<any, any, any, any>;
34 | annotations: {
35 | destructiveHint?: boolean;
36 | idempotentHint?: boolean;
37 | openWorldHint?: boolean;
38 | readOnlyHint?: boolean;
39 | title?: string;
40 | };
41 | actions: {
42 | [key: string]: {
43 | [action: string]: boolean;
44 | };
45 | };
46 | execute: (stripe: Stripe, context: Context, params: any) => Promise<any>;
47 | };
48 |
49 | const tools = (context: Context): StripeToolDefinition[] => [
50 | createCustomerTool(context),
51 | listCustomersTool(context),
52 | createProductTool(context),
53 | listProductsTool(context),
54 | createPriceTool(context),
55 | listPricesTool(context),
56 | createPaymentLinkTool(context),
57 | createInvoiceTool(context),
58 | listInvoicesTool(context),
59 | createInvoiceItemTool(context),
60 | finalizeInvoiceTool(context),
61 | retrieveBalanceTool(context),
62 | createRefundTool(context),
63 | listPaymentIntentsTool(context),
64 | listSubscriptionsTool(context),
65 | cancelSubscriptionTool(context),
66 | updateSubscriptionTool(context),
67 | searchDocumentationTool(context),
68 | listCouponsTool(context),
69 | createCouponTool(context),
70 | updateDisputeTool(context),
71 | listDisputesTool(context),
72 | ];
73 |
74 | export default tools;
75 |
```
--------------------------------------------------------------------------------
/tools/typescript/package.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "@stripe/agent-toolkit",
3 | "version": "0.8.1",
4 | "homepage": "https://github.com/stripe/ai",
5 | "scripts": {
6 | "build": "tsup",
7 | "clean": "rm -rf langchain ai-sdk modelcontextprotocol openai cloudflare",
8 | "lint": "eslint \"./**/*.ts*\"",
9 | "prettier": "prettier './**/*.{js,ts,md,html,css}' --write",
10 | "prettier-check": "prettier './**/*.{js,ts,md,html,css}' --check",
11 | "test": "jest",
12 | "eval": "cd ../evals && pnpm test"
13 | },
14 | "exports": {
15 | "./langchain": {
16 | "types": "./langchain/index.d.ts",
17 | "require": "./langchain/index.js",
18 | "import": "./langchain/index.mjs"
19 | },
20 | "./ai-sdk": {
21 | "types": "./ai-sdk/index.d.ts",
22 | "require": "./ai-sdk/index.js",
23 | "import": "./ai-sdk/index.mjs"
24 | },
25 | "./openai": {
26 | "types": "./openai/index.d.ts",
27 | "require": "./openai/index.js",
28 | "import": "./openai/index.mjs"
29 | },
30 | "./modelcontextprotocol": {
31 | "types": "./modelcontextprotocol/index.d.ts",
32 | "require": "./modelcontextprotocol/index.js",
33 | "import": "./modelcontextprotocol/index.mjs"
34 | },
35 | "./cloudflare": {
36 | "types": "./cloudflare/index.d.ts",
37 | "require": "./cloudflare/index.js",
38 | "import": "./cloudflare/index.mjs"
39 | }
40 | },
41 | "packageManager": "[email protected]",
42 | "engines": {
43 | "node": ">=18"
44 | },
45 | "author": "Stripe <[email protected]> (https://stripe.com/)",
46 | "contributors": [
47 | "Steve Kaliski <[email protected]>"
48 | ],
49 | "license": "MIT",
50 | "devDependencies": {
51 | "@eslint/compat": "^1.2.4",
52 | "@types/jest": "^29.5.14",
53 | "@types/node": "^22.10.5",
54 | "@typescript-eslint/eslint-plugin": "^8.19.1",
55 | "eslint": "^9.17.0",
56 | "eslint-config-prettier": "^9.1.0",
57 | "eslint-plugin-import": "^2.31.0",
58 | "eslint-plugin-jest": "^28.10.0",
59 | "eslint-plugin-prettier": "^5.2.1",
60 | "globals": "^15.14.0",
61 | "jest": "^29.7.0",
62 | "prettier": "^3.4.2",
63 | "ts-jest": "^29.2.5",
64 | "ts-node": "^10.9.2",
65 | "tsup": "^8.3.5",
66 | "typescript": "^5.8.3"
67 | },
68 | "dependencies": {
69 | "@ai-sdk/provider": "^2.0.0",
70 | "stripe": "^17.5.0",
71 | "zod": "^3.25.76",
72 | "zod-to-json-schema": "^3.24.6"
73 | },
74 | "peerDependencies": {
75 | "@langchain/core": "^0.3.6",
76 | "@modelcontextprotocol/sdk": "^1.17.1",
77 | "agents": "^0.0.84",
78 | "ai": "^5.0.89",
79 | "openai": "^4.86.1"
80 | },
81 | "workspaces": [
82 | ".",
83 | "examples/*"
84 | ],
85 | "pnpm": {
86 | "overrides": {
87 | "form-data": "^4.0.4",
88 | "esbuild": "^0.25.0",
89 | "@babel/helpers": "^7.26.10",
90 | "jsondiffpatch": "^0.7.2",
91 | "nanoid": "^3.3.8",
92 | "brace-expansion": "^2.0.2",
93 | "@eslint/plugin-kit": "^0.3.4",
94 | "tmp": "^0.2.4"
95 | }
96 | },
97 | "files": [
98 | "ai-sdk/**/*",
99 | "langchain/**/*",
100 | "modelcontextprotocol/**/*",
101 | "openai/**/*",
102 | "cloudflare/**/*",
103 | "LICENSE",
104 | "README.md",
105 | "VERSION",
106 | "package.json"
107 | ]
108 | }
109 |
```
--------------------------------------------------------------------------------
/tools/typescript/src/test/shared/paymentIntents/functions.test.ts:
--------------------------------------------------------------------------------
```typescript
1 | import {listPaymentIntents} from '@/shared/paymentIntents/listPaymentIntents';
2 |
3 | const Stripe = jest.fn().mockImplementation(() => ({
4 | paymentIntents: {
5 | list: jest.fn(),
6 | },
7 | }));
8 |
9 | let stripe: ReturnType<typeof Stripe>;
10 |
11 | beforeEach(() => {
12 | stripe = new Stripe('fake-api-key');
13 | });
14 |
15 | describe('listPaymentIntents', () => {
16 | it('should list payment intents and return them', async () => {
17 | const mockPaymentIntents = [
18 | {
19 | id: 'pi_123456',
20 | customer: 'cus_123456',
21 | amount: 1000,
22 | status: 'succeeded',
23 | description: 'Test Payment Intent',
24 | },
25 | ];
26 |
27 | const context = {};
28 |
29 | stripe.paymentIntents.list.mockResolvedValue({data: mockPaymentIntents});
30 |
31 | const result = await listPaymentIntents(stripe, context, {});
32 |
33 | expect(stripe.paymentIntents.list).toHaveBeenCalledWith({}, undefined);
34 | expect(result).toEqual(mockPaymentIntents);
35 | });
36 |
37 | it('should list payment intents for a specific customer', async () => {
38 | const mockPaymentIntents = [
39 | {
40 | id: 'pi_123456',
41 | customer: 'cus_123456',
42 | amount: 1000,
43 | status: 'succeeded',
44 | description: 'Test Payment Intent',
45 | },
46 | ];
47 |
48 | const context = {};
49 |
50 | stripe.paymentIntents.list.mockResolvedValue({data: mockPaymentIntents});
51 |
52 | const result = await listPaymentIntents(stripe, context, {
53 | customer: 'cus_123456',
54 | });
55 |
56 | expect(stripe.paymentIntents.list).toHaveBeenCalledWith(
57 | {
58 | customer: 'cus_123456',
59 | },
60 | undefined
61 | );
62 | expect(result).toEqual(mockPaymentIntents);
63 | });
64 |
65 | it('should specify the connected account if included in context', async () => {
66 | const mockPaymentIntents = [
67 | {
68 | id: 'pi_123456',
69 | customer: 'cus_123456',
70 | amount: 1000,
71 | status: 'succeeded',
72 | description: 'Test Payment Intent',
73 | },
74 | ];
75 |
76 | const context = {
77 | account: 'acct_123456',
78 | };
79 |
80 | stripe.paymentIntents.list.mockResolvedValue({data: mockPaymentIntents});
81 |
82 | const result = await listPaymentIntents(stripe, context, {});
83 |
84 | expect(stripe.paymentIntents.list).toHaveBeenCalledWith(
85 | {},
86 | {
87 | stripeAccount: context.account,
88 | }
89 | );
90 | expect(result).toEqual(mockPaymentIntents);
91 | });
92 |
93 | it('should list payment intents for a specific customer if included in context', async () => {
94 | const mockPaymentIntents = [
95 | {
96 | id: 'pi_123456',
97 | customer: 'cus_123456',
98 | amount: 1000,
99 | status: 'succeeded',
100 | description: 'Test Payment Intent',
101 | },
102 | ];
103 |
104 | const context = {
105 | customer: 'cus_123456',
106 | };
107 |
108 | stripe.paymentIntents.list.mockResolvedValue({data: mockPaymentIntents});
109 |
110 | const result = await listPaymentIntents(stripe, context, {});
111 |
112 | expect(stripe.paymentIntents.list).toHaveBeenCalledWith(
113 | {customer: context.customer},
114 | undefined
115 | );
116 | expect(result).toEqual(mockPaymentIntents);
117 | });
118 | });
119 |
```