#
tokens: 49253/50000 69/256 files (page 2/7)
lines: on (toggle) GitHub
raw markdown copy reset
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 | 
```
Page 2/7FirstPrevNextLast