This is page 1 of 4. 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_agent_toolkit_release.yml │ ├── npm_mcp_release.yml │ └── pypi_release.yml ├── .gitignore ├── .vscode │ ├── extensions.json │ ├── launch.json │ └── settings.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── evals │ ├── .env.example │ ├── .gitignore │ ├── braintrust_openai.ts │ ├── cases.ts │ ├── eval.ts │ ├── package.json │ ├── pnpm-lock.yaml │ ├── README.md │ ├── scorer.ts │ └── tsconfig.json ├── gemini-extension.json ├── LICENSE ├── modelcontextprotocol │ ├── .dxtignore │ ├── .gitignore │ ├── .node-version │ ├── .prettierrc │ ├── build-dxt.js │ ├── Dockerfile │ ├── eslint.config.mjs │ ├── jest.config.ts │ ├── manifest.json │ ├── package.json │ ├── pnpm-lock.yaml │ ├── README.md │ ├── 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 ├── SECURITY.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 ├── 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 -------------------------------------------------------------------------------- /modelcontextprotocol/.node-version: -------------------------------------------------------------------------------- ``` 1 | 22.14.0 2 | ``` -------------------------------------------------------------------------------- /evals/.gitignore: -------------------------------------------------------------------------------- ``` 1 | node_modules 2 | .env 3 | ``` -------------------------------------------------------------------------------- /modelcontextprotocol/.gitignore: -------------------------------------------------------------------------------- ``` 1 | dist/ 2 | node_modules/ 3 | *.dxt 4 | dxt-dist/ ``` -------------------------------------------------------------------------------- /python/examples/openai/.env.template: -------------------------------------------------------------------------------- ``` 1 | OPENAI_API_KEY="" 2 | STRIPE_SECRET_KEY="" ``` -------------------------------------------------------------------------------- /typescript/examples/openai/.env.template: -------------------------------------------------------------------------------- ``` 1 | OPENAI_API_KEY="" 2 | STRIPE_SECRET_KEY="" ``` -------------------------------------------------------------------------------- /typescript/examples/cloudflare/.gitignore: -------------------------------------------------------------------------------- ``` 1 | # wrangler project 2 | .dev.vars 3 | .wrangler/ 4 | ``` -------------------------------------------------------------------------------- /typescript/examples/langchain/.env.template: -------------------------------------------------------------------------------- ``` 1 | LANGSMITH_API_KEY="" 2 | STRIPE_SECRET_KEY="" 3 | ``` -------------------------------------------------------------------------------- /modelcontextprotocol/.dxtignore: -------------------------------------------------------------------------------- ``` 1 | node_modules/ 2 | node_modules/**/*.* 3 | tsconfig.json 4 | Dockerfile ``` -------------------------------------------------------------------------------- /python/examples/openai/web_search/.env.template: -------------------------------------------------------------------------------- ``` 1 | OPENAI_API_KEY="" 2 | STRIPE_SECRET_KEY="" 3 | STRIPE_CUSTOMER_ID="" 4 | STRIPE_METER="" 5 | ``` -------------------------------------------------------------------------------- /modelcontextprotocol/.prettierrc: -------------------------------------------------------------------------------- ``` 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5", 4 | "bracketSpacing": false 5 | } 6 | ``` -------------------------------------------------------------------------------- /typescript/.prettierrc: -------------------------------------------------------------------------------- ``` 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5", 4 | "bracketSpacing": false 5 | } 6 | ``` -------------------------------------------------------------------------------- /python/examples/crewai/.env.template: -------------------------------------------------------------------------------- ``` 1 | STRIPE_SECRET_KEY="" 2 | OPENAI_API_BASE="" 3 | OPENAI_MODEL_NAME="gpt-4o" 4 | OPENAI_API_KEY="" 5 | ``` -------------------------------------------------------------------------------- /typescript/examples/ai-sdk/.env.template: -------------------------------------------------------------------------------- ``` 1 | STRIPE_SECRET_KEY="" 2 | STRIPE_CUSTOMER_ID="" 3 | STRIPE_METER_INPUT="" 4 | STRIPE_METER_OUTPUT="" 5 | ``` -------------------------------------------------------------------------------- /python/examples/langchain/.env.template: -------------------------------------------------------------------------------- ``` 1 | LANGSMITH_API_KEY="" 2 | STRIPE_SECRET_KEY="" 3 | OPENAI_API_BASE="" 4 | OPENAI_MODEL_NAME="gpt-4o" 5 | OPENAI_API_KEY="" 6 | ``` -------------------------------------------------------------------------------- /evals/.env.example: -------------------------------------------------------------------------------- ``` 1 | BRAINTRUST_API_KEY=... 2 | STRIPE_SECRET_KEY=... 3 | 4 | # Enable if your OpenAI API is behind a unix socket proxy: 5 | # SOCKET_PROXY_PATH="" 6 | OPENAI_BASE_URL=http://0.0.0.0:8000/v1 7 | OPENAI_API_KEY=EMPTY 8 | 9 | 10 | 11 | ``` -------------------------------------------------------------------------------- /typescript/examples/cloudflare/.dev.vars.example: -------------------------------------------------------------------------------- ``` 1 | STRIPE_SECRET_KEY=sk_test_...... 2 | STRIPE_PRICE_ID_ONE_TIME_PAYMENT=price_1RJJwjR1b4cWtS0UCIDTSU3V 3 | STRIPE_PRICE_ID_SUBSCRIPTION=price_1RJJwjR1bGyW9S0UCIDTSU3V 4 | STRIPE_PRICE_ID_USAGE_BASED_SUBSCRIPTION=price_1RJdGWR1bGyW9S0UucbYBFBZ ``` -------------------------------------------------------------------------------- /python/.editorconfig: -------------------------------------------------------------------------------- ``` 1 | ; https://editorconfig.org/ 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | end_of_line = lf 11 | charset = utf-8 12 | 13 | [*.{cfg,ini,json,toml,yml}] 14 | indent_size = 2 15 | 16 | [Makefile] 17 | indent_style = tab 18 | ``` -------------------------------------------------------------------------------- /python/examples/openai/customer_support/.env.template: -------------------------------------------------------------------------------- ``` 1 | OPENAI_API_KEY=your_openai_api_key 2 | STRIPE_SECRET_KEY=your_stripe_secret_key 3 | 4 | # Your email address and app password 5 | # https://support.google.com/accounts/answer/185833?hl=en 6 | [email protected] 7 | EMAIL_PASSWORD=your_app_specific_password 8 | # Only respond to emails send to this address (defaults to $EMAIL_ADDRESS) 9 | SUPPORT_ADDRESS="[email protected]" 10 | 11 | # If your connecting to gmail, you don't need to customize these 12 | IMAP_SERVER=imap.gmail.com 13 | IMAP_PORT=993 14 | SMTP_SERVER=smtp.gmail.com 15 | SMTP_PORT=587 16 | ``` -------------------------------------------------------------------------------- /python/.flake8: -------------------------------------------------------------------------------- ``` 1 | [flake8] 2 | # E501 is the "Line too long" error. We disable it because we use Black for 3 | # code formatting. Black makes a best effort to keep lines under the max 4 | # length, but can go over in some cases. 5 | # W503 goes against PEP8 rules. It's disabled by default, but must be disabled 6 | # explicitly when using `ignore`. 7 | # E704 is disabled in the default configuration, but by specifying `ignore`, we wipe that out. 8 | # ruff formatting creates code that violates it, so we have to disable it manually 9 | ignore = E501, W503, E704 10 | per-file-ignores = 11 | # setup.py is required for tooling 12 | setup.py: IMP102 13 | ``` -------------------------------------------------------------------------------- /typescript/.gitignore: -------------------------------------------------------------------------------- ``` 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | junk/ 45 | 46 | # Snowpack dependency directory (https://snowpack.dev/) 47 | web_modules/ 48 | 49 | # TypeScript cache 50 | *.tsbuildinfo 51 | 52 | # Optional npm cache directory 53 | .npm 54 | 55 | # Optional eslint cache 56 | .eslintcache 57 | 58 | # Optional stylelint cache 59 | .stylelintcache 60 | 61 | # Microbundle cache 62 | .rpt2_cache/ 63 | .rts2_cache_cjs/ 64 | .rts2_cache_es/ 65 | .rts2_cache_umd/ 66 | 67 | # Optional REPL history 68 | .node_repl_history 69 | 70 | # Output of 'npm pack' 71 | *.tgz 72 | 73 | # Yarn Integrity file 74 | .yarn-integrity 75 | 76 | # dotenv environment variable files 77 | .env 78 | .env.development.local 79 | .env.test.local 80 | .env.production.local 81 | .env.local 82 | 83 | # parcel-bundler cache (https://parceljs.org/) 84 | .cache 85 | .parcel-cache 86 | 87 | # Next.js build output 88 | .next 89 | out 90 | 91 | # Nuxt.js build / generate output 92 | .nuxt 93 | dist 94 | 95 | # Gatsby files 96 | .cache/ 97 | # Comment in the public line in if your project uses Gatsby and not Next.js 98 | # https://nextjs.org/blog/next-9-1#public-directory-support 99 | # public 100 | 101 | # vuepress build output 102 | .vuepress/dist 103 | 104 | # vuepress v2.x temp and cache directory 105 | .temp 106 | .cache 107 | 108 | # Docusaurus cache and generated files 109 | .docusaurus 110 | 111 | # Serverless directories 112 | .serverless/ 113 | 114 | # FuseBox cache 115 | .fusebox/ 116 | 117 | # DynamoDB Local files 118 | .dynamodb/ 119 | 120 | # TernJS port file 121 | .tern-port 122 | 123 | # Stores VSCode versions used for testing VSCode extensions 124 | .vscode-test 125 | 126 | # yarn v2 127 | .yarn/cache 128 | .yarn/unplugged 129 | .yarn/build-state.yml 130 | .yarn/install-state.gz 131 | .pnp.* 132 | 133 | .turbo 134 | 135 | 136 | /cloudflare/ 137 | /openai/ 138 | /ai-sdk/ 139 | /langchain/ 140 | /modelcontextprotocol/ ``` -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Node modules 10 | node_modules/ 11 | junk/ 12 | 13 | # Distribution / packaging 14 | .Python 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | share/python-wheels/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | MANIFEST 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .nox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *.cover 53 | *.py,cover 54 | .hypothesis/ 55 | .pytest_cache/ 56 | cover/ 57 | 58 | # Translations 59 | *.mo 60 | *.pot 61 | 62 | # Django stuff: 63 | *.log 64 | local_settings.py 65 | db.sqlite3 66 | db.sqlite3-journal 67 | 68 | # Flask stuff: 69 | instance/ 70 | .webassets-cache 71 | 72 | # Scrapy stuff: 73 | .scrapy 74 | 75 | # Sphinx documentation 76 | docs/_build/ 77 | 78 | # PyBuilder 79 | .pybuilder/ 80 | target/ 81 | 82 | # Jupyter Notebook 83 | .ipynb_checkpoints 84 | 85 | # IPython 86 | profile_default/ 87 | ipython_config.py 88 | 89 | # pyenv 90 | # For a library or package, you might want to ignore these files since the code is 91 | # intended to run in multiple environments; otherwise, check them in: 92 | # .python-version 93 | 94 | # pipenv 95 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 96 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 97 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 98 | # install all needed dependencies. 99 | #Pipfile.lock 100 | 101 | # poetry 102 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 103 | # This is especially recommended for binary packages to ensure reproducibility, and is more 104 | # commonly ignored for libraries. 105 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 106 | #poetry.lock 107 | 108 | # pdm 109 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 110 | #pdm.lock 111 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 112 | # in version control. 113 | # https://pdm.fming.dev/#use-with-ide 114 | .pdm.toml 115 | 116 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 117 | __pypackages__/ 118 | 119 | # Celery stuff 120 | celerybeat-schedule 121 | celerybeat.pid 122 | 123 | # SageMath parsed files 124 | *.sage.py 125 | 126 | # Environments 127 | .env 128 | .venv 129 | env/ 130 | venv/ 131 | ENV/ 132 | env.bak/ 133 | venv.bak/ 134 | 135 | # Spyder project settings 136 | .spyderproject 137 | .spyproject 138 | 139 | # Rope project settings 140 | .ropeproject 141 | 142 | # mkdocs documentation 143 | /site 144 | 145 | # mypy 146 | .mypy_cache/ 147 | .dmypy.json 148 | dmypy.json 149 | 150 | # Pyre type checker 151 | .pyre/ 152 | 153 | # pytype static type analyzer 154 | .pytype/ 155 | 156 | # Cython debug symbols 157 | cython_debug/ 158 | 159 | # PyCharm 160 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 161 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 162 | # and can be added to the global gitignore or merged into this file. For a more nuclear 163 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 164 | #.idea/ 165 | ``` -------------------------------------------------------------------------------- /python/examples/crewai/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # CrewAI Example 2 | 3 | ## Setup 4 | 5 | Copy the `.env.template` and populate with your values. 6 | 7 | ``` 8 | cp .env.template .env 9 | ``` 10 | 11 | ## Usage 12 | 13 | ``` 14 | python main.py 15 | ``` 16 | ``` -------------------------------------------------------------------------------- /python/examples/langchain/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # LangChain Example 2 | 3 | ## Setup 4 | 5 | Copy the `.env.template` and populate with your values. 6 | 7 | ``` 8 | cp .env.template .env 9 | ``` 10 | 11 | ## Usage 12 | 13 | ``` 14 | python main.py 15 | ``` 16 | ``` -------------------------------------------------------------------------------- /typescript/examples/ai-sdk/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # AI SDK Example 2 | 3 | ## Setup 4 | 5 | Copy the `.env.template` and populate with your values. 6 | 7 | ``` 8 | cp .env.template .env 9 | ``` 10 | 11 | ## Usage 12 | 13 | ``` 14 | npx ts-node index.ts --env 15 | ``` 16 | ``` -------------------------------------------------------------------------------- /typescript/examples/openai/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # OpenAI Example 2 | 3 | ## Setup 4 | 5 | Copy the `.env.template` and populate with your values. 6 | 7 | ``` 8 | cp .env.template .env 9 | ``` 10 | 11 | ## Usage 12 | 13 | ``` 14 | npx ts-node index.ts --env 15 | ``` 16 | ``` -------------------------------------------------------------------------------- /typescript/examples/langchain/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # LangChain Example 2 | 3 | ## Setup 4 | 5 | Copy the `.env.template` and populate with your values. 6 | 7 | ``` 8 | cp .env.template .env 9 | ``` 10 | 11 | ## Usage 12 | 13 | ``` 14 | npx ts-node index.ts --env 15 | ``` 16 | ``` -------------------------------------------------------------------------------- /python/examples/openai/file_search/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # Web Search Example 2 | 3 | This example shows how to use the Stripe Agent Toolkit with OpenAI to create an agent that can search the web and charge for outcomes. 4 | 5 | ## Setup 6 | 7 | 1. Create a OpenAI Vector Store following the [OpenAI documentation](https://platform.openai.com/docs/api-reference/vector-stores-files) and add the files you want to search. 8 | 9 | 2. Copy `.env.template` to `.env` populate with the relevant values. 10 | 11 | ```bash 12 | OPENAI_API_KEY=your_openai_api_key 13 | OPENAI_VECTOR_STORE_ID=your_openai_vector_store_id 14 | STRIPE_SECRET_KEY=your_stripe_secret_key 15 | ``` 16 | 17 | ## Usage 18 | 19 | ```bash 20 | python main.py 21 | ``` 22 | 23 | You can see the invoices created in the Stripe Dashboard. 24 | ``` -------------------------------------------------------------------------------- /python/examples/openai/web_search/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # Web Search Example 2 | 3 | This example shows how to use the Stripe Agent Toolkit with OpenAI to create an agent that can search the web and charge for outcomes. 4 | 5 | ## Setup 6 | 7 | 1. Create a Stripe Billing Meter and Stripe Customer following the [Stripe documentation](https://docs.stripe.com/billing/subscriptions/usage-based/implementation-guide). 8 | 9 | 2. Copy `.env.template` to `.env` populate with the relevant values. 10 | 11 | ```bash 12 | OPENAI_API_KEY=your_openai_api_key 13 | STRIPE_SECRET_KEY=your_stripe_secret_key 14 | STRIPE_CUSTOMER_ID=your_stripe_customer_id 15 | STRIPE_METER=your_stripe_meter 16 | ``` 17 | 18 | ## Usage 19 | 20 | ```bash 21 | python main.py 22 | ``` 23 | 24 | You can see the usage in the Stripe Dashboard. 25 | ``` -------------------------------------------------------------------------------- /typescript/src/modelcontextprotocol/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # MCP Payments 2 | 3 | A simple MCP server helper to require payment to use tools, whether subscription or usage-based. 4 | 5 | This implementation works on Vercel with a standard MCP server. 6 | 7 | ## Usage Instructions for `registerPaidTool` 8 | 9 | 1. Import the `registerPaidTool` function from this package. 10 | 2. Call `registerPaidTool` with your MCP server, tool name, description, params schema, callback, and payment options. 11 | 3. Example usage: 12 | 13 | ```ts 14 | import {registerPaidTool} from './register-paid-tool'; 15 | import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js'; 16 | 17 | const server = new McpServer({ 18 | name: 'mcp-typescript server', 19 | version: '0.1.0', 20 | }); 21 | 22 | registerPaidTool( 23 | server, 24 | 'add_numbers', 25 | { 26 | a: z.number(), 27 | b: z.number(), 28 | }, 29 | ({a, b}) => { 30 | return { 31 | content: [{type: 'text', text: `Result: ${a + b}`}], 32 | }; 33 | }, 34 | { 35 | priceId: '{{PRICE_ID}}', 36 | successUrl: '{{CALLBACK_URL}}', 37 | email: '{{EMAIL}}', 38 | paymentReason: 39 | 'You must pay a subscription to add two big numbers together.', 40 | stripeSecretKey: '{{SECRET_KEY}}', 41 | } 42 | ); 43 | ``` 44 | ``` -------------------------------------------------------------------------------- /typescript/examples/cloudflare/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # PaidMcpAgent 2 | 3 | An example of how to monetize an MCP server with Stripe. 4 | 5 | ## Setup 6 | 7 | 1. Copy `.dev.vars.example` to `.dev.vars` and add your Stripe API key. 8 | 2. Configure the required Stripe environment variables: 9 | - `STRIPE_SECRET_KEY`: Your Stripe secret key 10 | - `STRIPE_ONETIME_SUBSCRIPTION_PRICE_ID`: Price ID for one-time payment 11 | - `STRIPE_PRICE_ID_USAGE_BASED_SUBSCRIPTION`: Price ID for usage-based subscription 12 | - `STRIPE_METER_EVENT_NAME`: Event name for usage metering 13 | 3. This demo uses an example fake OAuth implementation for the MCP server. We recommend following the [authorization](https://developers.cloudflare.com/agents/model-context-protocol/authorization/) Cloudflare docs. 14 | 15 | ## Development 16 | 17 | ``` 18 | pnpm i 19 | pnpm dev 20 | ``` 21 | 22 | ## Testing 23 | 24 | Open up the inspector and connect to your MCP server. 25 | 26 | ``` 27 | npx @modelcontextprotocol/inspector@latest http://localhost:4242/sse 28 | ``` 29 | 30 | ### Deploy 31 | 32 | ``` 33 | npx wrangler secret put STRIPE_SECRET_KEY 34 | npx wrangler secret put STRIPE_PRICE_ID_ONE_TIME_PAYMENT 35 | npx wrangler secret put STRIPE_ONETIME_SUBSCRIPTION_PRICE_ID 36 | npx wrangler secret put STRIPE_PRICE_ID_USAGE_BASED_SUBSCRIPTION 37 | ``` 38 | 39 | ### Feedback 40 | 41 | Please leave feedback throught the GitHub issues and discussions on how you 42 | would use the `PaidMcpAgent`! 43 | ``` -------------------------------------------------------------------------------- /evals/README.md: -------------------------------------------------------------------------------- ```markdown 1 | ## Evals 2 | 3 | Set up `.env` file with the following (see `.env.example` for an example): 4 | 5 | ``` 6 | BRAINTRUST_API_KEY=... 7 | STRIPE_SECRET_KEY=sk_test_.... 8 | OPENAI_BASE_URL=http://0.0.0.0:8000/v1 9 | OPENAI_API_KEY=EMPTY 10 | ``` 11 | 12 | To run: 13 | 14 | ``` 15 | tsx eval.ts 16 | ``` 17 | 18 | We are using [Braintrust](https://www.braintrust.dev/) to run the evals. 19 | 20 | ## Framework 21 | 22 | There is a very lightweight built-in testing framework that wraps Braintrust to make adding new test cases easy. 23 | 24 | Add a new test case to `cases.ts`: 25 | 26 | ```javascript 27 | test({ 28 | prompt: 29 | "Create a product called 'Test Product' with a description 'A test product for evaluation'", 30 | fn: ({ toolCalls, messages }) => [ 31 | expectToolCall(toolCalls, ["create_product"]), 32 | llmCriteriaMet( 33 | messages, 34 | "The message should include a successful production creation response" 35 | ), 36 | ], 37 | }); 38 | ``` 39 | 40 | The Typescript type defintions has documentation to help you. The `fn` function 41 | will be called with the resulting output of your prompt. This should return an array of "assertions." These are like `expect` in Jest. 42 | 43 | This can be as simple as noting a tool was called or as complex as asking an LLM to do semantic similarities. See `scorer.ts` for a list of assertions. 44 | 45 | Override the toolkit config by passing a `toolkitConfig` object. 46 | 47 | If your test case needs some set up, for example, if it needs to set up some state in the Stripe account or load data, you can pass an async function. 48 | 49 | ```javascript 50 | test(async () => { 51 | const customers = await stripe.customers.list(); 52 | 53 | return { 54 | prompt: "What are my payments", 55 | toolkitConfig: { 56 | context: { 57 | customer: customers.data[0].id, 58 | }, 59 | }, 60 | fn: ({ toolCalls, messages }) => [], 61 | }; 62 | }); 63 | ``` 64 | ``` -------------------------------------------------------------------------------- /python/examples/openai/customer_support/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # Email Support Agent 2 | 3 | Sample app to help you automate your email support. Powered by OpenAI's Agent SDK and [Stripe Agent Toolkit](https://github.com/stripe/agent-toolkit). 4 | 5 | Customize this agent to fit your own needs by cloning and modifying [support_agent.py](./support_agent.py). 6 | 7 | ## Features 8 | 9 | The support agent currently can: 10 | 11 | - Answer FAQ questions 12 | - Update billing information through Customer Portal 13 | - Send any missing invoices 14 | 15 | We also support a REPL to help you test your agent without sending a gazillion emails. 16 | 17 | ## How it Works 18 | 19 | The support agent will: 20 | 21 | - Connects to your email using an IMAP client 22 | - Checks for unread emails every 30 seconds 23 | - Generates and sends a reply 24 | - Marks the emails as read 25 | 26 | If it doesn't know how to answer the question, it will not respond and ignore the email. 27 | 28 | ## Setup 29 | 30 | 1. Install uv (if not already installed): 31 | 32 | ```bash 33 | curl -LsSf https://astral.sh/uv/install.sh | sh 34 | ``` 35 | 36 | 2. Clone this repository 37 | 3. Create and activate a virtual environment: 38 | 39 | ```bash 40 | uv venv 41 | source .venv/bin/activate # On Unix/macOS 42 | # or 43 | .venv\Scripts\activate # On Windows 44 | ``` 45 | 46 | 4. Install dependencies: 47 | 48 | ```bash 49 | uv sync 50 | ``` 51 | 52 | 5. Copy `.env.example` to `.env`: 53 | 54 | ```bash 55 | cp .env.example .env 56 | ``` 57 | 58 | 6. Configure your `.env` file with: 59 | - Email credentials (create an [app-specific password](https://support.google.com/accounts/answer/185833) for Gmail) 60 | - IMAP/SMTP server details (defaults for Gmail provided in .env.example) 61 | - OpenAI API key 62 | - Stripe Secret Key 63 | 64 | ## Usage 65 | 66 | Run the agent: 67 | 68 | ```bash 69 | python main.py 70 | ``` 71 | 72 | Run the REPL with: 73 | 74 | ```bash 75 | python repl.py 76 | ``` 77 | 78 | ## Customize for your App 79 | 80 | This repository is just a sample app tailored to our [example website](http://standupjack.com). 81 | 82 | We recommend cloning this repository and customizing the system prompt and tools in [support_agent.py](./support_agent.py). It's very easy to add new capabilities. 83 | ``` -------------------------------------------------------------------------------- /typescript/src/cloudflare/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # MCP Payments 2 | 3 | `PaidMcpAgent` extends [Cloudflare's `McpAgent`](https://github.com/cloudflare/agents) to make it simple to require payment to use tools, whether subscription or usage-based. For a full end-to-end example, see [/examples/cloudflare](../../examples/cloudflare/). 4 | 5 | ## Usage 6 | 7 | ### Setup 8 | 9 | ``` 10 | npm install @stripe/agent-toolkit 11 | ``` 12 | 13 | Modify your existing MCP server by extending with `PaidMcpAgent` instead of `McpAgent`. 14 | 15 | ```ts 16 | import { 17 | PaymentState, 18 | experimental_PaidMcpAgent as PaidMcpAgent, 19 | } from '@stripe/agent-toolkit/cloudflare'; 20 | 21 | type Props = { 22 | userEmail: string; 23 | }; 24 | 25 | type State = PaymentState & {}; 26 | 27 | export class MyMCP extends PaidMcpAgent<Bindings, State, Props> {} 28 | ``` 29 | 30 | Lastly, set your `STRIPE_SECRET_KEY` in `.dev.vars` to test, and then `npx wrangler secret put STRIPE_SECRET_KEY` when ready for production. 31 | 32 | ### Monetizing a tool 33 | 34 | Consider a basic tool that can add two numbers together: 35 | 36 | ```ts 37 | this.server.tool('add', {a: z.number(), b: z.number()}, ({a, b}) => { 38 | return { 39 | content: [{type: 'text', text: `Result: ${a + b}`}], 40 | }; 41 | }); 42 | ``` 43 | 44 | To make this paid using a subscription, first create a product and price in the Stripe Dashboard. 45 | 46 | Then, replace `this.server.tool` with `this.paidTool` and add your payment config: `priceId`, `paymentReason`, and `successUrl`. 47 | 48 | ```ts 49 | this.paidTool( 50 | 'add_numbers', 51 | { 52 | a: z.number(), 53 | b: z.number(), 54 | }, 55 | ({a, b}) => { 56 | return { 57 | content: [{type: 'text', text: `Result: ${a + b}`}], 58 | }; 59 | }, 60 | { 61 | priceId: '{{PRICE_ID}}', 62 | successUrl: 'https://mcp.mysite.com/success', 63 | paymentReason: 64 | 'You must pay a subscription to add two big numbers together.', 65 | } 66 | ); 67 | ``` 68 | 69 | ## Authentication 70 | 71 | `PaidMcp` relies on `props.userEmail` to identify (or create) a Stripe customer. You can prepopulate this directly, or integrate with `OAuthProvider` from `@cloudflare/workers-oauth-provider` to set the prop on succesful authentication. 72 | ``` -------------------------------------------------------------------------------- /python/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # Stripe Agent Toolkit - Python 2 | 3 | The Stripe Agent Toolkit library enables popular agent frameworks including OpenAI's Agent SDK, LangChain, and CrewAI to integrate with Stripe APIs through function calling. The 4 | library is not exhaustive of the entire Stripe API. It is built directly on top 5 | of the [Stripe Python SDK][python-sdk]. 6 | 7 | ## Installation 8 | 9 | You don't need this source code unless you want to modify the package. If you just 10 | want to use the package, just run: 11 | 12 | ```sh 13 | pip install stripe-agent-toolkit 14 | ``` 15 | 16 | ### Requirements 17 | 18 | - Python 3.11+ 19 | 20 | ## Usage 21 | 22 | The library needs to be configured with your account's secret key which is 23 | available in your [Stripe Dashboard][api-keys]. 24 | 25 | ```python 26 | from stripe_agent_toolkit.openai.toolkit import StripeAgentToolkit 27 | 28 | stripe_agent_toolkit = StripeAgentToolkit( 29 | secret_key="sk_test_...", 30 | configuration={ 31 | "actions": { 32 | "payment_links": { 33 | "create": True, 34 | }, 35 | } 36 | }, 37 | ) 38 | ``` 39 | 40 | The toolkit works with OpenAI's Agent SDK, LangChain, and CrewAI and can be passed as a list of tools. For example: 41 | 42 | ```python 43 | from agents import Agent 44 | 45 | stripe_agent = Agent( 46 | name="Stripe Agent", 47 | instructions="You are an expert at integrating with Stripe", 48 | tools=stripe_agent_toolkit.get_tools() 49 | ) 50 | ``` 51 | 52 | Examples for OpenAI's Agent SDK,LangChain, and CrewAI are included in [/examples](/examples). 53 | 54 | [python-sdk]: https://github.com/stripe/stripe-python 55 | [api-keys]: https://dashboard.stripe.com/account/apikeys 56 | 57 | #### Context 58 | 59 | In some cases you will want to provide values that serve as defaults when making requests. Currently, the `account` context value enables you to make API calls for your [connected accounts](https://docs.stripe.com/connect/authentication). 60 | 61 | ```python 62 | stripe_agent_toolkit = StripeAgentToolkit( 63 | secret_key="sk_test_...", 64 | configuration={ 65 | "context": { 66 | "account": "acct_123" 67 | } 68 | } 69 | ) 70 | ``` 71 | 72 | ## Development 73 | 74 | ``` 75 | python3 -m venv venv 76 | source venv/bin/activate 77 | pip install -r requirements.txt 78 | ``` 79 | ``` -------------------------------------------------------------------------------- /typescript/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # Stripe Agent Toolkit - TypeScript 2 | 3 | The Stripe Agent Toolkit enables popular agent frameworks including LangChain and Vercel's AI SDK to integrate with Stripe APIs through function calling. It also provides tooling to quickly integrate metered billing for prompt and completion token usage. 4 | 5 | ## Installation 6 | 7 | You don't need this source code unless you want to modify the package. If you just 8 | want to use the package run: 9 | 10 | ``` 11 | npm install @stripe/agent-toolkit 12 | ``` 13 | 14 | ### Requirements 15 | 16 | - Node 18+ 17 | 18 | ## Usage 19 | 20 | The library needs to be configured with your account's secret key which is available in your [Stripe Dashboard][api-keys]. Additionally, `configuration` enables you to specify the types of actions that can be taken using the toolkit. 21 | 22 | ```typescript 23 | import {StripeAgentToolkit} from '@stripe/agent-toolkit/langchain'; 24 | 25 | const stripeAgentToolkit = new StripeAgentToolkit({ 26 | secretKey: process.env.STRIPE_SECRET_KEY!, 27 | configuration: { 28 | actions: { 29 | paymentLinks: { 30 | create: true, 31 | }, 32 | }, 33 | }, 34 | }); 35 | ``` 36 | 37 | ### Tools 38 | 39 | The toolkit works with LangChain and Vercel's AI SDK and can be passed as a list of tools. For example: 40 | 41 | ```typescript 42 | import {AgentExecutor, createStructuredChatAgent} from 'langchain/agents'; 43 | 44 | const tools = stripeAgentToolkit.getTools(); 45 | 46 | const agent = await createStructuredChatAgent({ 47 | llm, 48 | tools, 49 | prompt, 50 | }); 51 | 52 | const agentExecutor = new AgentExecutor({ 53 | agent, 54 | tools, 55 | }); 56 | ``` 57 | 58 | #### Context 59 | 60 | In some cases you will want to provide values that serve as defaults when making requests. Currently, the `account` context value enables you to make API calls for your [connected accounts](https://docs.stripe.com/connect/authentication). 61 | 62 | ```typescript 63 | const stripeAgentToolkit = new StripeAgentToolkit({ 64 | secretKey: process.env.STRIPE_SECRET_KEY!, 65 | configuration: { 66 | context: { 67 | account: 'acct_123', 68 | }, 69 | }, 70 | }); 71 | ``` 72 | 73 | ### Metered billing 74 | 75 | For Vercel's AI SDK, you can use middleware to submit billing events for usage. All that is required is the customer ID and the input/output meters to bill. 76 | 77 | ```typescript 78 | import {StripeAgentToolkit} from '@stripe/agent-toolkit/ai-sdk'; 79 | import {openai} from '@ai-sdk/openai'; 80 | import { 81 | generateText, 82 | experimental_wrapLanguageModel as wrapLanguageModel, 83 | } from 'ai'; 84 | 85 | const stripeAgentToolkit = new StripeAgentToolkit({ 86 | secretKey: process.env.STRIPE_SECRET_KEY!, 87 | configuration: { 88 | actions: { 89 | paymentLinks: { 90 | create: true, 91 | }, 92 | }, 93 | }, 94 | }); 95 | 96 | const model = wrapLanguageModel({ 97 | model: openai('gpt-4o'), 98 | middleware: stripeAgentToolkit.middleware({ 99 | billing: { 100 | customer: 'cus_123', 101 | meters: { 102 | input: 'input_tokens', 103 | output: 'output_tokens', 104 | }, 105 | }, 106 | }), 107 | }); 108 | ``` 109 | 110 | This works with both `generateText` and `generateStream` from the Vercel AI SDK. 111 | 112 | ## Model Context Protocol 113 | 114 | The Stripe Agent Toolkit also supports the [Model Context Protocol (MCP)](https://modelcontextprotocol.com/). See `/examples/modelcontextprotocol` for an example. The same configuration options are available, and the server can be run with all supported transports. 115 | 116 | ```typescript 117 | import {StripeAgentToolkit} from '@stripe/agent-toolkit/modelcontextprotocol'; 118 | import {StdioServerTransport} from '@modelcontextprotocol/sdk/server/stdio.js'; 119 | 120 | const server = new StripeAgentToolkit({ 121 | secretKey: process.env.STRIPE_SECRET_KEY!, 122 | configuration: { 123 | actions: { 124 | paymentLinks: { 125 | create: true, 126 | }, 127 | products: { 128 | create: true, 129 | }, 130 | prices: { 131 | create: true, 132 | }, 133 | }, 134 | }, 135 | }); 136 | 137 | async function main() { 138 | const transport = new StdioServerTransport(); 139 | await server.connect(transport); 140 | console.error('Stripe MCP Server running on stdio'); 141 | } 142 | 143 | main().catch((error) => { 144 | console.error('Fatal error in main():', error); 145 | process.exit(1); 146 | }); 147 | ``` 148 | 149 | [node-sdk]: https://github.com/stripe/stripe-node 150 | [api-keys]: https://dashboard.stripe.com/account/apikeys 151 | ``` -------------------------------------------------------------------------------- /modelcontextprotocol/README.md: -------------------------------------------------------------------------------- ```markdown 1 | # Stripe Model Context Protocol 2 | 3 | The Stripe [Model Context Protocol](https://modelcontextprotocol.com/) server allows you to integrate with Stripe APIs through function calling. This protocol supports various tools to interact with different Stripe services. 4 | 5 | ## Setup 6 | 7 | Stripe hosts a remote MCP server at https://mcp.stripe.com. View the docs [here](https://docs.stripe.com/mcp). To run the Stripe MCP server locally using npx, use the following command: 8 | 9 | ```bash 10 | # To set up all available tools 11 | npx -y @stripe/mcp --tools=all --api-key=YOUR_STRIPE_SECRET_KEY 12 | 13 | # To set up specific tools 14 | npx -y @stripe/mcp --tools=customers.create,customers.read,products.create --api-key=YOUR_STRIPE_SECRET_KEY 15 | 16 | # To configure a Stripe connected account 17 | npx -y @stripe/mcp --tools=all --api-key=YOUR_STRIPE_SECRET_KEY --stripe-account=CONNECTED_ACCOUNT_ID 18 | ``` 19 | 20 | Make sure to replace `YOUR_STRIPE_SECRET_KEY` with your actual Stripe secret key. Alternatively, you could set the STRIPE_SECRET_KEY in your environment variables. 21 | 22 | ### Usage with Claude Desktop 23 | 24 | Add the following to your `claude_desktop_config.json`. See [here](https://modelcontextprotocol.io/quickstart/user) for more details. 25 | 26 | ``` 27 | { 28 | "mcpServers": { 29 | "stripe": { 30 | "command": "npx", 31 | "args": [ 32 | "-y", 33 | "@stripe/mcp", 34 | "--tools=all", 35 | "--api-key=STRIPE_SECRET_KEY" 36 | ] 37 | } 38 | } 39 | } 40 | ``` 41 | 42 | of if you're using Docker 43 | 44 | ``` 45 | { 46 | “mcpServers”: { 47 | “stripe”: { 48 | “command”: “docker", 49 | “args”: [ 50 | “run”, 51 | "--rm", 52 | "-i", 53 | “mcp/stripe”, 54 | “--tools=all”, 55 | “--api-key=STRIPE_SECRET_KEY” 56 | ] 57 | } 58 | } 59 | } 60 | 61 | ``` 62 | 63 | ### Usage with Gemini CLI 64 | 65 | 1. Install [Gemini CLI](https://google-gemini.github.io/gemini-cli/#-installation) through your preferred method. 66 | 2. Install the Stripe MCP extension: `gemini extensions install https://github.com/stripe/agent-toolkit`. 67 | 3. Start Gemini CLI: `gemini`. 68 | 4. Go through the OAUTH flow: `/mcp auth stripe`. 69 | 70 | ## Available tools 71 | 72 | | Tool | Description | 73 | | ---------------------- | ------------------------------- | 74 | | `balance.read` | Retrieve balance information | 75 | | `coupons.create` | Create a new coupon | 76 | | `coupons.read` | Read coupon information | 77 | | `customers.create` | Create a new customer | 78 | | `customers.read` | Read customer information | 79 | | `disputes.read` | Read disputes information | 80 | | `disputes.update` | Update an existing dispute | 81 | | `documentation.read` | Search Stripe documentation | 82 | | `invoiceItems.create` | Create a new invoice item | 83 | | `invoices.create` | Create a new invoice | 84 | | `invoices.read` | Read invoice information | 85 | | `invoices.update` | Update an existing invoice | 86 | | `paymentIntents.read` | Read payment intent information | 87 | | `paymentLinks.create` | Create a new payment link | 88 | | `prices.create` | Create a new price | 89 | | `prices.read` | Read price information | 90 | | `products.create` | Create a new product | 91 | | `products.read` | Read product information | 92 | | `refunds.create` | Create a new refund | 93 | | `subscriptions.read` | Read subscription information | 94 | | `subscriptions.update` | Update subscription information | 95 | 96 | ## Debugging the Server 97 | 98 | To debug your server, you can use the [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector). 99 | 100 | First build the server 101 | 102 | ``` 103 | npm run build 104 | ``` 105 | 106 | Run the following command in your terminal: 107 | 108 | ```bash 109 | # Start MCP Inspector and server with all tools 110 | npx @modelcontextprotocol/inspector node dist/index.js --tools=all --api-key=YOUR_STRIPE_SECRET_KEY 111 | ``` 112 | 113 | ### Build using Docker 114 | 115 | First build the server 116 | 117 | ``` 118 | docker build -t mcp/stripe . 119 | ``` 120 | 121 | Run the following command in your terminal: 122 | 123 | ```bash 124 | docker run -p 3000:3000 -p 5173:5173 -v /var/run/docker.sock:/var/run/docker.sock mcp/inspector docker run --rm -i mcp/stripe --tools=all --api-key=YOUR_STRIPE_SECRET_KEY 125 | 126 | ``` 127 | 128 | ### Instructions 129 | 130 | 1. Replace `YOUR_STRIPE_SECRET_KEY` with your actual Stripe API secret key. 131 | 2. Run the command to start the MCP Inspector. 132 | 3. Open the MCP Inspector UI in your browser and click Connect to start the MCP server. 133 | 4. You can see the list of tools you selected and test each tool individually. 134 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown 1 | # Stripe Agent Toolkit 2 | 3 | The Stripe Agent Toolkit enables popular agent frameworks including Model Context Protocol (MCP), OpenAI's Agent SDK, LangChain, CrewAI, and Vercel's AI SDK to integrate with Stripe APIs through function calling. The 4 | library is not exhaustive of the entire Stripe API. It includes support for MCP, Python, and TypeScript and is built directly on top of the Stripe [Python][python-sdk] and [Node][node-sdk] SDKs. 5 | 6 | Included below are basic instructions, but refer to the [MCP](/modelcontextprotocol) [Python](/python), [TypeScript](/typescript) packages for more information. 7 | 8 | ## Model Context Protocol 9 | 10 | Stripe hosts a remote MCP server at `https://mcp.stripe.com`. This allows secure MCP client access via OAuth. View the docs [here](https://docs.stripe.com/mcp#remote). 11 | 12 | The Stripe Agent Toolkit also exposes tools in the [Model Context Protocol (MCP)](https://modelcontextprotocol.com/) format. Or, to run a local Stripe MCP server using npx, use the following command: 13 | 14 | ```bash 15 | npx -y @stripe/mcp --tools=all --api-key=YOUR_STRIPE_SECRET_KEY 16 | ``` 17 | 18 | ## Python 19 | 20 | ### Installation 21 | 22 | You don't need this source code unless you want to modify the package. If you just 23 | want to use the package run: 24 | 25 | ```sh 26 | pip install stripe-agent-toolkit 27 | ``` 28 | 29 | #### Requirements 30 | 31 | - Python 3.11+ 32 | 33 | ### Usage 34 | 35 | The library needs to be configured with your account's secret key which is 36 | available in your [Stripe Dashboard][api-keys]. 37 | 38 | ```python 39 | from stripe_agent_toolkit.openai.toolkit import StripeAgentToolkit 40 | 41 | stripe_agent_toolkit = StripeAgentToolkit( 42 | secret_key="sk_test_...", 43 | configuration={ 44 | "actions": { 45 | "payment_links": { 46 | "create": True, 47 | }, 48 | } 49 | }, 50 | ) 51 | ``` 52 | 53 | The toolkit works with OpenAI's Agent SDK, LangChain, and CrewAI and can be passed as a list of tools. For example: 54 | 55 | ```python 56 | from agents import Agent 57 | 58 | stripe_agent = Agent( 59 | name="Stripe Agent", 60 | instructions="You are an expert at integrating with Stripe", 61 | tools=stripe_agent_toolkit.get_tools() 62 | ) 63 | ``` 64 | 65 | Examples for OpenAI's Agent SDK,LangChain, and CrewAI are included in [/examples](/python/examples). 66 | 67 | #### Context 68 | 69 | In some cases you will want to provide values that serve as defaults when making requests. Currently, the `account` context value enables you to make API calls for your [connected accounts](https://docs.stripe.com/connect/authentication). 70 | 71 | ```python 72 | stripe_agent_toolkit = StripeAgentToolkit( 73 | secret_key="sk_test_...", 74 | configuration={ 75 | "context": { 76 | "account": "acct_123" 77 | } 78 | } 79 | ) 80 | ``` 81 | 82 | ## TypeScript 83 | 84 | ### Installation 85 | 86 | You don't need this source code unless you want to modify the package. If you just 87 | want to use the package run: 88 | 89 | ``` 90 | npm install @stripe/agent-toolkit 91 | ``` 92 | 93 | #### Requirements 94 | 95 | - Node 18+ 96 | 97 | ### Usage 98 | 99 | The library needs to be configured with your account's secret key which is available in your [Stripe Dashboard][api-keys]. Additionally, `configuration` enables you to specify the types of actions that can be taken using the toolkit. 100 | 101 | ```typescript 102 | import { StripeAgentToolkit } from "@stripe/agent-toolkit/langchain"; 103 | 104 | const stripeAgentToolkit = new StripeAgentToolkit({ 105 | secretKey: process.env.STRIPE_SECRET_KEY!, 106 | configuration: { 107 | actions: { 108 | paymentLinks: { 109 | create: true, 110 | }, 111 | }, 112 | }, 113 | }); 114 | ``` 115 | 116 | #### Tools 117 | 118 | The toolkit works with LangChain and Vercel's AI SDK and can be passed as a list of tools. For example: 119 | 120 | ```typescript 121 | import { AgentExecutor, createStructuredChatAgent } from "langchain/agents"; 122 | 123 | const tools = stripeAgentToolkit.getTools(); 124 | 125 | const agent = await createStructuredChatAgent({ 126 | llm, 127 | tools, 128 | prompt, 129 | }); 130 | 131 | const agentExecutor = new AgentExecutor({ 132 | agent, 133 | tools, 134 | }); 135 | ``` 136 | 137 | #### Context 138 | 139 | In some cases you will want to provide values that serve as defaults when making requests. Currently, the `account` context value enables you to make API calls for your [connected accounts](https://docs.stripe.com/connect/authentication). 140 | 141 | ```typescript 142 | const stripeAgentToolkit = new StripeAgentToolkit({ 143 | secretKey: process.env.STRIPE_SECRET_KEY!, 144 | configuration: { 145 | context: { 146 | account: "acct_123", 147 | }, 148 | }, 149 | }); 150 | ``` 151 | 152 | #### Metered billing 153 | 154 | For Vercel's AI SDK, you can use middleware to submit billing events for usage. All that is required is the customer ID and the input/output meters to bill. 155 | 156 | ```typescript 157 | import { StripeAgentToolkit } from "@stripe/agent-toolkit/ai-sdk"; 158 | import { openai } from "@ai-sdk/openai"; 159 | import { 160 | generateText, 161 | experimental_wrapLanguageModel as wrapLanguageModel, 162 | } from "ai"; 163 | 164 | const stripeAgentToolkit = new StripeAgentToolkit({ 165 | secretKey: process.env.STRIPE_SECRET_KEY!, 166 | configuration: { 167 | actions: { 168 | paymentLinks: { 169 | create: true, 170 | }, 171 | }, 172 | }, 173 | }); 174 | 175 | const model = wrapLanguageModel({ 176 | model: openai("gpt-4o"), 177 | middleware: stripeAgentToolkit.middleware({ 178 | billing: { 179 | customer: "cus_123", 180 | meters: { 181 | input: "input_tokens", 182 | output: "output_tokens", 183 | }, 184 | }, 185 | }), 186 | }); 187 | ``` 188 | 189 | 190 | 191 | ## Supported API methods 192 | 193 | - [Cancel a subscription](https://docs.stripe.com/api/subscriptions/cancel) 194 | - [Create a coupon](https://docs.stripe.com/api/coupons/create) 195 | - [Create a customer](https://docs.stripe.com/api/customers/create) 196 | - [Create a payment link](https://docs.stripe.com/api/payment-link/create) 197 | - [Create a price](https://docs.stripe.com/api/prices/create) 198 | - [Create a product](https://docs.stripe.com/api/products/create) 199 | - [Create a refund](https://docs.stripe.com/api/refunds/create) 200 | - [Create an invoice item](https://docs.stripe.com/api/invoiceitems/create) 201 | - [Create an invoice](https://docs.stripe.com/api/invoices/create) 202 | - [Finalize an invoice](https://docs.stripe.com/api/invoices/finalize) 203 | - [List all coupons](https://docs.stripe.com/api/coupons/list) 204 | - [List all customers](https://docs.stripe.com/api/customers/list) 205 | - [List all disputes](https://docs.stripe.com/api/disputes/list) 206 | - [List all prices](https://docs.stripe.com/api/prices/list) 207 | - [List all products](https://docs.stripe.com/api/products/list) 208 | - [List all subscriptions](https://docs.stripe.com/api/subscriptions/list) 209 | - [Retrieve balance](https://docs.stripe.com/api/balance/balance_retrieve) 210 | - [Update a dispute](https://docs.stripe.com/api/disputes/update) 211 | - [Update a subscription](https://docs.stripe.com/api/subscriptions/update) 212 | 213 | [python-sdk]: https://github.com/stripe/stripe-python 214 | [node-sdk]: https://github.com/stripe/stripe-node 215 | [api-keys]: https://dashboard.stripe.com/account/apikeys 216 | ``` -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- ```markdown 1 | # Security Policy 2 | 3 | ### Reporting a vulnerability 4 | 5 | Please do not open GitHub issues or pull requests - this makes the problem immediately visible to everyone, including malicious actors. 6 | 7 | Security issues in this open-source project can be safely reported to Stripe's [Vulnerability Disclosure and Reward Program](https://stripe.com/docs/security/stripe#disclosure-and-reward-program). 8 | Stripe's security team will triage your report and respond according to its impact on Stripe users and systems. 9 | ``` -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- ```markdown 1 | # Contributing 2 | 3 | Contributions of any kind are welcome! If you've found a bug or have a feature request, please feel free to [open an issue](/issues). 4 | 5 | <!-- We will try and respond to your issue or pull request within a week. --> 6 | 7 | To make changes yourself, follow these steps: 8 | 9 | 1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository and [clone](https://help.github.com/articles/cloning-a-repository/) it locally. 10 | <!-- 1. TODO add install step(s), e.g. "Run `npm install`" --> 11 | <!-- 1. TODO add build step(s), e.g. "Build the library using `npm run build`" --> 12 | 2. Make your changes 13 | <!-- 1. TODO add test step(s), e.g. "Test your changes with `npm test`" --> 14 | 3. Submit a [pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) 15 | 16 | ## Contributor License Agreement ([CLA](https://en.wikipedia.org/wiki/Contributor_License_Agreement)) 17 | 18 | Once you have submitted a pull request, sign the CLA by clicking on the badge in the comment from [@CLAassistant](https://github.com/CLAassistant). 19 | 20 | <img width="910" alt="image" src="https://user-images.githubusercontent.com/62121649/198740836-70aeb322-5755-49fc-af55-93c8e8a39058.png"> 21 | 22 | <br /> 23 | Thanks for contributing to Stripe! :sparkles: 24 | ``` -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- ```markdown 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies within all project spaces, and it also applies when 49 | an individual is representing the project or its community in public spaces. 50 | Examples of representing a project or community include using an official 51 | project e-mail address, posting via an official social media account, or acting 52 | as an appointed representative at an online or offline event. Representation of 53 | a project may be further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at [email protected]. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | ``` -------------------------------------------------------------------------------- /python/examples/langchain/__init__.py: -------------------------------------------------------------------------------- ```python 1 | ``` -------------------------------------------------------------------------------- /python/stripe_agent_toolkit/__init__.py: -------------------------------------------------------------------------------- ```python 1 | ``` -------------------------------------------------------------------------------- /python/tests/__init__.py: -------------------------------------------------------------------------------- ```python 1 | ``` -------------------------------------------------------------------------------- /typescript/pnpm-workspace.yaml: -------------------------------------------------------------------------------- ```yaml 1 | packages: 2 | - '.' 3 | - 'examples/*' 4 | ``` -------------------------------------------------------------------------------- /typescript/src/ai-sdk/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | import StripeAgentToolkit from './toolkit'; 2 | export {StripeAgentToolkit}; 3 | ``` -------------------------------------------------------------------------------- /typescript/src/langchain/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | import StripeAgentToolkit from './toolkit'; 2 | export {StripeAgentToolkit}; 3 | ``` -------------------------------------------------------------------------------- /typescript/src/openai/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | import StripeAgentToolkit from './toolkit'; 2 | export {StripeAgentToolkit}; 3 | ``` -------------------------------------------------------------------------------- /python/stripe_agent_toolkit/strands/__init__.py: -------------------------------------------------------------------------------- ```python 1 | """Stripe Agent Toolkit for Strands.""" 2 | 3 | from .toolkit import StripeAgentToolkit 4 | 5 | __all__ = ["StripeAgentToolkit"] 6 | ``` -------------------------------------------------------------------------------- /typescript/src/modelcontextprotocol/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | import StripeAgentToolkit from './toolkit'; 2 | import {registerPaidTool} from './register-paid-tool'; 3 | export {StripeAgentToolkit, registerPaidTool}; 4 | ``` -------------------------------------------------------------------------------- /typescript/examples/ai-sdk/tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | }, 6 | "include": ["index.ts"], 7 | "exclude": ["node_modules", "dist"] 8 | } 9 | ``` -------------------------------------------------------------------------------- /typescript/examples/langchain/tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | }, 6 | "include": ["index.ts"], 7 | "exclude": ["node_modules", "dist"] 8 | } 9 | ``` -------------------------------------------------------------------------------- /typescript/examples/openai/tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist" 5 | }, 6 | "include": ["index.ts"], 7 | "exclude": ["node_modules", "dist"] 8 | } 9 | ``` -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "recommendations": [ 3 | "EditorConfig.editorconfig", // default 4 | "ms-python.python", // intellisense 5 | "ms-python.flake8", // linting 6 | "charliermarsh.ruff" // formatting 7 | ] 8 | } 9 | ``` -------------------------------------------------------------------------------- /gemini-extension.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "stripe-gemini-mcp-extension", 3 | "version": "0.1.0", 4 | "mcpServers": { 5 | "stripe": { 6 | "httpUrl": "https://mcp.stripe.com", 7 | "oauth": { 8 | "enabled": true 9 | } 10 | } 11 | } 12 | } 13 | ``` -------------------------------------------------------------------------------- /python/requirements.txt: -------------------------------------------------------------------------------- ``` 1 | twine 2 | crewai==0.76.2 3 | crewai-tools===0.13.2 4 | flake8 5 | langchain==0.3.4 6 | langchain-openai==0.2.2 7 | mypy==1.7.0 8 | pydantic>=2.10 9 | pyright==1.1.350 10 | python-dotenv==1.0.1 11 | ruff==0.4.4 12 | stripe==11.0.0 13 | openai==1.66.0 14 | openai-agents==0.0.2 15 | ``` -------------------------------------------------------------------------------- /typescript/examples/cloudflare/src/imageGenerator.ts: -------------------------------------------------------------------------------- ```typescript 1 | // @ts-ignore 2 | import emojiFromText from 'emoji-from-text'; 3 | 4 | export function generateImage(description: string) { 5 | const emoji = emojiFromText(description); 6 | try { 7 | return emoji[0].match.emoji.char; 8 | } catch (e) { 9 | return '⚠️ (Error generating image)'; 10 | } 11 | } 12 | ``` -------------------------------------------------------------------------------- /modelcontextprotocol/jest.config.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type {Config} from 'jest'; 2 | 3 | const config: Config = { 4 | preset: 'ts-jest', 5 | testEnvironment: 'node', 6 | roots: ['<rootDir>/src'], 7 | testMatch: ['**/test/**/*.ts?(x)'], 8 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 9 | }; 10 | 11 | export default config; 12 | ``` -------------------------------------------------------------------------------- /typescript/jest.config.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type {Config} from 'jest'; 2 | 3 | const config: Config = { 4 | preset: 'ts-jest', 5 | testEnvironment: 'node', 6 | roots: ['<rootDir>/src'], 7 | testMatch: ['**/test/**/*.ts?(x)'], 8 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 9 | moduleNameMapper: { 10 | '^@/(.*)$': '<rootDir>/src/$1', 11 | }, 12 | }; 13 | 14 | export default config; 15 | ``` -------------------------------------------------------------------------------- /modelcontextprotocol/tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "Node16", 5 | "moduleResolution": "Node16", 6 | "outDir": "./dist", 7 | "rootDir": "./src", 8 | "strict": true, 9 | "esModuleInterop": true, 10 | "skipLibCheck": true, 11 | "forceConsistentCasingInFileNames": true 12 | }, 13 | "include": ["src/**/*"], 14 | "exclude": ["node_modules"] 15 | } 16 | ``` -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- ```yaml 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Stripe support 4 | url: https://support.stripe.com/ 5 | about: | 6 | Please only file issues here that you believe represent actual bugs or feature requests for the Stripe Agent Tools library. 7 | 8 | If you're having general trouble with your Stripe integration, please reach out to support. 9 | ``` -------------------------------------------------------------------------------- /python/examples/openai/customer_support/env.py: -------------------------------------------------------------------------------- ```python 1 | from os import getenv 2 | 3 | from dotenv import load_dotenv 4 | 5 | # Load the environment 6 | load_dotenv() 7 | 8 | 9 | def ensure(name: str) -> str: 10 | var = getenv(name) 11 | if not var: 12 | raise ValueError(f"Missing '{name}' environment variable") 13 | return var 14 | 15 | 16 | def get_or(name: str, default: str) -> str: 17 | var = getenv(name) 18 | if not var: 19 | return default 20 | return var 21 | ``` -------------------------------------------------------------------------------- /evals/tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "compilerOptions": { 4 | "target": "es2022", 5 | "module": "NodeNext", 6 | "moduleResolution": "NodeNext", 7 | "esModuleInterop": true, 8 | "skipLibCheck": true, 9 | "strict": true, 10 | "baseUrl": ".", 11 | "paths": { 12 | "@/*": ["../typescript/src/*"] 13 | } 14 | }, 15 | "include": ["**/*.ts"], 16 | "exclude": ["node_modules"] 17 | } 18 | ``` -------------------------------------------------------------------------------- /typescript/examples/openai/package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "stripe-agent-toolkit-examples-openai", 3 | "version": "0.1.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "MIT", 11 | "dependencies": { 12 | "dotenv": "^16.4.5", 13 | "openai": "^4.86.1", 14 | "@stripe/agent-toolkit": "latest" 15 | }, 16 | "devDependencies": { 17 | "@types/node": "^22.7.4" 18 | } 19 | } 20 | ``` -------------------------------------------------------------------------------- /modelcontextprotocol/Dockerfile: -------------------------------------------------------------------------------- ```dockerfile 1 | # syntax=docker/dockerfile:1 2 | # check=experimental=all 3 | 4 | FROM node:22-alpine@sha256:9bef0ef1e268f60627da9ba7d7605e8831d5b56ad07487d24d1aa386336d1944 5 | RUN npm install -g typescript pnpm 6 | RUN addgroup -S group && adduser -S user -G group 7 | WORKDIR /app 8 | COPY . . 9 | RUN --mount=type=cache,target=/root/.local \ 10 | pnpm install --frozen-lockfile && pnpm run build 11 | 12 | USER user 13 | ENTRYPOINT ["node", "/app/dist/index.js"] 14 | ``` -------------------------------------------------------------------------------- /typescript/examples/ai-sdk/package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "stripe-agent-toolkit-examples-ai-sdk", 3 | "version": "0.1.0", 4 | "description": "", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "author": "", 9 | "license": "MIT", 10 | "dependencies": { 11 | "@ai-sdk/openai": "^0.0.63", 12 | "@stripe/agent-toolkit": "latest", 13 | "ai": "^3.4.7 || ^4.0.0", 14 | "dotenv": "^16.4.5" 15 | }, 16 | "devDependencies": { 17 | "@types/node": "^22.7.4" 18 | } 19 | } 20 | ``` -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "editor.formatOnSave": true, 3 | "python.defaultInterpreterPath": "./venv/bin/python", 4 | "[python]": { 5 | "editor.defaultFormatter": "charliermarsh.ruff", 6 | "editor.codeActionsOnSave": { 7 | "source.organizeImports": "never" 8 | } 9 | }, 10 | "[typescript]": { 11 | "editor.defaultFormatter": "esbenp.prettier-vscode" 12 | }, 13 | "[json]": { 14 | "editor.defaultFormatter": "esbenp.prettier-vscode" 15 | }, 16 | "ruff.lint.enable": false 17 | } 18 | ``` -------------------------------------------------------------------------------- /python/examples/openai/customer_support/pyproject.toml: -------------------------------------------------------------------------------- ```toml 1 | [project] 2 | name = "email-agent" 3 | version = "0.1.0" 4 | description = "An automated email support agent that uses AI to respond to customer support emails" 5 | requires-python = ">=3.9" 6 | dependencies = [ 7 | "python-dotenv>=1.0.0", 8 | "imaplib2==3.6", 9 | "python-decouple==3.8", 10 | "openai-agents==0.0.2", 11 | "stripe-agent-toolkit>=0.6.0", 12 | "stripe>=7.0.0", 13 | "urllib3<2.0.0", 14 | "markdown==3.7" 15 | ] 16 | 17 | [tool.hatch.metadata] 18 | allow-direct-references = true 19 | ``` -------------------------------------------------------------------------------- /typescript/tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Default", 4 | "compilerOptions": { 5 | "outDir": "./dist", 6 | "target": "es2022", 7 | "moduleDetection": "force", 8 | "esModuleInterop": true, 9 | "skipLibCheck": true, 10 | "strict": true, 11 | "module": "NodeNext", 12 | "baseUrl": ".", 13 | "paths": { 14 | "@/*": [ 15 | "src/*" 16 | ] 17 | } 18 | }, 19 | "include": [ 20 | "**/*.ts" 21 | ], 22 | "exclude": [ 23 | "node_modules", 24 | "examples" 25 | ] 26 | } ``` -------------------------------------------------------------------------------- /typescript/src/ai-sdk/tool.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type {CoreTool} from 'ai'; 2 | import {tool} from 'ai'; 3 | import {z} from 'zod'; 4 | import StripeAPI from '../shared/api'; 5 | 6 | export default function StripeTool( 7 | stripeAPI: StripeAPI, 8 | method: string, 9 | description: string, 10 | schema: z.ZodObject<any, any, any, any, {[x: string]: any}> 11 | ): CoreTool { 12 | return tool({ 13 | description: description, 14 | parameters: schema, 15 | execute: (arg: z.output<typeof schema>) => { 16 | return stripeAPI.run(method, arg); 17 | }, 18 | }); 19 | } 20 | ``` -------------------------------------------------------------------------------- /typescript/examples/langchain/package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "stripe-agent-toolkit-examples-langchain", 3 | "version": "0.1.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "MIT", 11 | "dependencies": { 12 | "@langchain/core": "^0.3.6", 13 | "@langchain/openai": "^0.3.5", 14 | "@stripe/agent-toolkit": "latest", 15 | "dotenv": "^16.4.5", 16 | "langchain": "^0.3.2" 17 | }, 18 | "devDependencies": { 19 | "@types/node": "^22.7.4" 20 | } 21 | } 22 | ``` -------------------------------------------------------------------------------- /typescript/examples/cloudflare/biome.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", 3 | "vcs": { 4 | "enabled": false, 5 | "clientKind": "git", 6 | "useIgnoreFile": false 7 | }, 8 | "files": { 9 | "ignoreUnknown": false, 10 | "ignore": ["worker-configuration.d.ts"] 11 | }, 12 | "formatter": { 13 | "enabled": true, 14 | "indentStyle": "tab" 15 | }, 16 | "organizeImports": { 17 | "enabled": true 18 | }, 19 | "linter": { 20 | "enabled": true, 21 | "rules": { 22 | "recommended": true 23 | } 24 | }, 25 | "javascript": { 26 | "formatter": { 27 | "quoteStyle": "double" 28 | } 29 | } 30 | } 31 | ``` -------------------------------------------------------------------------------- /typescript/examples/cloudflare/tsconfig.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "compilerOptions": { 3 | "target": "es2021", 4 | "lib": [ 5 | "es2021" 6 | ], 7 | "jsx": "react-jsx", 8 | "module": "es2022", 9 | "moduleResolution": "Bundler", 10 | "resolveJsonModule": true, 11 | "allowJs": true, 12 | "checkJs": false, 13 | "noEmit": true, 14 | "isolatedModules": true, 15 | "allowSyntheticDefaultImports": true, 16 | "forceConsistentCasingInFileNames": true, 17 | "allowImportingTsExtensions": true, 18 | "strict": true, 19 | "skipLibCheck": true, 20 | "types": [] 21 | }, 22 | "include": [ 23 | "worker-configuration.d.ts", 24 | "src/**/*.ts" 25 | ] 26 | } ``` -------------------------------------------------------------------------------- /typescript/src/test/shared/invoiceItems/prompts.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import {createInvoiceItemPrompt} from '@/shared/invoiceItems/createInvoiceItem'; 2 | 3 | describe('createInvoiceItemPrompt', () => { 4 | it('should return the correct prompt when no customer is specified', () => { 5 | const prompt = createInvoiceItemPrompt({}); 6 | expect(prompt).toContain('- customer (str)'); 7 | }); 8 | 9 | it('should return the correct prompt when a customer is specified', () => { 10 | const prompt = createInvoiceItemPrompt({customer: 'cus_123'}); 11 | expect(prompt).toContain('context: cus_123'); 12 | expect(prompt).not.toContain('- customer (str)'); 13 | }); 14 | }); 15 | ``` -------------------------------------------------------------------------------- /evals/package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "evals", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "tsx eval.ts" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@ai-sdk/openai": "^0.0.63", 14 | "@stripe/agent-toolkit": "file:../typescript", 15 | "ai": "^4.0.28", 16 | "autoevals": "^0.0.124", 17 | "braintrust": "^0.0.185", 18 | "dotenv": "^16.4.7", 19 | "lodash": "^4.17.21", 20 | "openai": "^4.91.1", 21 | "stripe": "^17.7.0", 22 | "zod": "^3.24.2" 23 | }, 24 | "devDependencies": { 25 | "@types/lodash": "^4.17.20", 26 | "tsx": "^4.19.3" 27 | } 28 | } 29 | ``` -------------------------------------------------------------------------------- /python/examples/openai/customer_support/repl.py: -------------------------------------------------------------------------------- ```python 1 | import asyncio 2 | 3 | from agents import ItemHelpers, TResponseInputItem 4 | 5 | import support_agent 6 | 7 | 8 | async def main(): 9 | """Simple REPL for testing your support agent""" 10 | input_items: list[TResponseInputItem] = [] 11 | while True: 12 | user_input = input("Enter your message: ") 13 | input_items.append({"content": user_input, "role": "user"}) 14 | result = await support_agent.run(input_items) 15 | output = ItemHelpers.text_message_outputs(result.new_items) 16 | print(f"Assistant: {output}") 17 | input_items = result.to_input_list() 18 | 19 | 20 | if __name__ == "__main__": 21 | asyncio.run(main()) 22 | ``` -------------------------------------------------------------------------------- /evals/braintrust_openai.ts: -------------------------------------------------------------------------------- ```typescript 1 | require("dotenv").config(); 2 | 3 | import { wrapOpenAI } from "braintrust"; 4 | import OpenAI from "openai"; 5 | import * as http from "http"; 6 | import * as net from "net"; 7 | 8 | const httpAgent = process.env.SOCKET_PROXY_PATH 9 | ? new (class extends http.Agent { 10 | createConnection = (_: any, callback: Function) => 11 | net.createConnection(process.env.SOCKET_PROXY_PATH!, () => callback()); 12 | })() 13 | : undefined; 14 | 15 | // This wrap function adds useful tracing in Braintrust 16 | const openai: any = wrapOpenAI( 17 | new OpenAI({ 18 | baseURL: process.env.OPENAI_BASE_URL, 19 | apiKey: process.env.OPENAI_API_KEY, 20 | httpAgent, 21 | }) 22 | ); 23 | 24 | export default openai; 25 | ``` -------------------------------------------------------------------------------- /typescript/src/test/shared/balance/parameters.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import {retrieveBalanceParameters} from '@/shared/balance/retrieveBalance'; 2 | 3 | describe('retrieveBalanceParameters', () => { 4 | it('should return the correct parameters if no context', () => { 5 | const parameters = retrieveBalanceParameters({}); 6 | 7 | const fields = Object.keys(parameters.shape); 8 | expect(fields).toEqual([]); 9 | expect(fields.length).toBe(0); 10 | }); 11 | 12 | it('should return the correct parameters if customer is specified', () => { 13 | const parameters = retrieveBalanceParameters({customer: 'cus_123'}); 14 | 15 | const fields = Object.keys(parameters.shape); 16 | expect(fields).toEqual([]); 17 | expect(fields.length).toBe(0); 18 | }); 19 | }); 20 | ``` -------------------------------------------------------------------------------- /python/stripe_agent_toolkit/crewai/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 typing import Any, Optional, Type 8 | from pydantic import BaseModel 9 | 10 | from crewai_tools import BaseTool 11 | 12 | from ..api import StripeAPI 13 | 14 | 15 | class StripeTool(BaseTool): 16 | """Tool for interacting with the Stripe API.""" 17 | 18 | stripe_api: StripeAPI 19 | method: str 20 | name: str = "" 21 | description: str = "" 22 | args_schema: Optional[Type[BaseModel]] = None 23 | 24 | def _run( 25 | self, 26 | *args: Any, 27 | **kwargs: Any, 28 | ) -> str: 29 | """Use the Stripe API to run an operation.""" 30 | return self.stripe_api.run(self.method, *args, **kwargs) 31 | ``` -------------------------------------------------------------------------------- /python/stripe_agent_toolkit/langchain/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 typing import Any, Optional, Type 8 | from pydantic import BaseModel 9 | 10 | from langchain.tools import BaseTool 11 | 12 | from ..api import StripeAPI 13 | 14 | 15 | class StripeTool(BaseTool): 16 | """Tool for interacting with the Stripe API.""" 17 | 18 | stripe_api: StripeAPI 19 | method: str 20 | name: str = "" 21 | description: str = "" 22 | args_schema: Optional[Type[BaseModel]] = None 23 | 24 | def _run( 25 | self, 26 | *args: Any, 27 | **kwargs: Any, 28 | ) -> str: 29 | """Use the Stripe API to run an operation.""" 30 | return self.stripe_api.run(self.method, *args, **kwargs) 31 | ``` -------------------------------------------------------------------------------- /typescript/src/test/shared/refunds/parameters.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import {createRefundParameters} from '@/shared/refunds/createRefund'; 2 | 3 | describe('createRefundParameters', () => { 4 | it('should return the correct parameters if no context', () => { 5 | const parameters = createRefundParameters({}); 6 | 7 | const fields = Object.keys(parameters.shape); 8 | expect(fields).toEqual(['payment_intent', 'amount']); 9 | expect(fields.length).toBe(2); 10 | }); 11 | 12 | it('should return the correct parameters if customer is specified', () => { 13 | const parameters = createRefundParameters({customer: 'cus_123'}); 14 | 15 | const fields = Object.keys(parameters.shape); 16 | expect(fields).toEqual(['payment_intent', 'amount']); 17 | expect(fields.length).toBe(2); 18 | }); 19 | }); 20 | ``` -------------------------------------------------------------------------------- /typescript/src/test/shared/paymentIntents/parameters.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import {listPaymentIntentsParameters} from '@/shared/paymentIntents/listPaymentIntents'; 2 | 3 | describe('listPaymentIntentsParameters', () => { 4 | it('should return the correct parameters if no context', () => { 5 | const parameters = listPaymentIntentsParameters({}); 6 | 7 | const fields = Object.keys(parameters.shape); 8 | expect(fields).toEqual(['customer', 'limit']); 9 | expect(fields.length).toBe(2); 10 | }); 11 | 12 | it('should return the correct parameters if customer is specified', () => { 13 | const parameters = listPaymentIntentsParameters({customer: 'cus_123'}); 14 | 15 | const fields = Object.keys(parameters.shape); 16 | expect(fields).toEqual(['limit']); 17 | expect(fields.length).toBe(1); 18 | }); 19 | }); 20 | ``` -------------------------------------------------------------------------------- /typescript/src/test/shared/paymentLinks/parameters.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import {createPaymentLinkParameters} from '@/shared/paymentLinks/createPaymentLink'; 2 | 3 | describe('createPaymentLinkParameters', () => { 4 | it('should return the correct parameters if no context', () => { 5 | const parameters = createPaymentLinkParameters({}); 6 | 7 | const fields = Object.keys(parameters.shape); 8 | expect(fields).toEqual(['price', 'quantity', 'redirect_url']); 9 | expect(fields.length).toBe(3); 10 | }); 11 | 12 | it('should return the correct parameters if customer is specified', () => { 13 | const parameters = createPaymentLinkParameters({customer: 'cus_123'}); 14 | 15 | const fields = Object.keys(parameters.shape); 16 | expect(fields).toEqual(['price', 'quantity', 'redirect_url']); 17 | }); 18 | }); 19 | ``` -------------------------------------------------------------------------------- /typescript/src/test/shared/invoiceItems/parameters.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import {createInvoiceItemParameters} from '@/shared/invoiceItems/createInvoiceItem'; 2 | 3 | describe('createInvoiceItemParameters', () => { 4 | it('should return the correct parameters if no context', () => { 5 | const parameters = createInvoiceItemParameters({}); 6 | 7 | const fields = Object.keys(parameters.shape); 8 | expect(fields).toEqual(['customer', 'price', 'invoice']); 9 | expect(fields.length).toBe(3); 10 | }); 11 | 12 | it('should return the correct parameters if customer is specified', () => { 13 | const parameters = createInvoiceItemParameters({customer: 'cus_123'}); 14 | 15 | const fields = Object.keys(parameters.shape); 16 | expect(fields).toEqual(['price', 'invoice']); 17 | expect(fields.length).toBe(2); 18 | }); 19 | }); 20 | ``` -------------------------------------------------------------------------------- /typescript/src/test/shared/documentation/parameters.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import {searchDocumentationParameters} from '@/shared/documentation/searchDocumentation'; 2 | 3 | describe('searchDocumentationParameters', () => { 4 | it('should return the correct parameters if no context', () => { 5 | const parameters = searchDocumentationParameters({}); 6 | 7 | const fields = Object.keys(parameters.shape); 8 | expect(fields).toEqual(['question', 'language']); 9 | expect(fields.length).toBe(2); 10 | }); 11 | 12 | it('should return the correct parameters if customer is specified', () => { 13 | const parameters = searchDocumentationParameters({customer: 'cus_123'}); 14 | 15 | const fields = Object.keys(parameters.shape); 16 | expect(fields).toEqual(['question', 'language']); 17 | expect(fields.length).toBe(2); 18 | }); 19 | }); 20 | ``` -------------------------------------------------------------------------------- /typescript/src/test/shared/paymentIntents/prompts.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import {listPaymentIntentsPrompt} from '@/shared/paymentIntents/listPaymentIntents'; 2 | 3 | describe('listPaymentIntentsPrompt', () => { 4 | it('should return the correct prompt', () => { 5 | const prompt = listPaymentIntentsPrompt(); 6 | expect(prompt).toContain('customer'); 7 | }); 8 | 9 | it('should return the correct prompt when no customer is specified', () => { 10 | const prompt = listPaymentIntentsPrompt({}); 11 | expect(prompt).toContain('- customer (str, optional)'); 12 | }); 13 | 14 | it('should return the correct prompt when a customer is specified', () => { 15 | const prompt = listPaymentIntentsPrompt({customer: 'cus_123'}); 16 | expect(prompt).toContain('context: cus_123'); 17 | expect(prompt).not.toContain('- customer (str, optional)'); 18 | }); 19 | }); 20 | ``` -------------------------------------------------------------------------------- /.github/workflows/npm_mcp_release.yml: -------------------------------------------------------------------------------- ```yaml 1 | name: NPM Release @stripe/mcp 2 | 3 | on: 4 | workflow_dispatch: {} 5 | 6 | jobs: 7 | mcp-release: 8 | runs-on: ubuntu-latest 9 | defaults: 10 | run: 11 | working-directory: ./modelcontextprotocol 12 | permissions: 13 | contents: read 14 | id-token: write 15 | steps: 16 | - uses: actions/checkout@v4 17 | 18 | - name: pnpm 19 | uses: pnpm/action-setup@v4 20 | with: 21 | version: 9.11.0 22 | 23 | # Setup .npmrc file to publish to npm 24 | - uses: actions/setup-node@v4 25 | with: 26 | node-version: "20.x" 27 | registry-url: "https://registry.npmjs.org" 28 | - run: pnpm install 29 | - run: pnpm run test 30 | - run: pnpm run build 31 | - run: npm publish --ignore-scripts --access public 32 | env: 33 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 34 | ``` -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Jest Current File", 11 | "runtimeExecutable": "sh", 12 | "program": "${workspaceFolder}/typescript/node_modules/.bin/jest", 13 | "args": [ 14 | "${relativeFile}", 15 | "--config", 16 | "${workspaceFolder}/typescript/jest.config.ts" 17 | ], 18 | "console": "integratedTerminal", 19 | "internalConsoleOptions": "openOnFirstSessionStart" 20 | } 21 | ] 22 | } ``` -------------------------------------------------------------------------------- /.github/workflows/npm_agent_toolkit_release.yml: -------------------------------------------------------------------------------- ```yaml 1 | name: NPM Release @stripe/agent-toolkit 2 | 3 | on: 4 | workflow_dispatch: {} 5 | 6 | jobs: 7 | agent-toolkit-release: 8 | runs-on: ubuntu-latest 9 | defaults: 10 | run: 11 | working-directory: ./typescript 12 | permissions: 13 | contents: read 14 | id-token: write 15 | steps: 16 | - uses: actions/checkout@v4 17 | # Setup .npmrc file to publish to npm 18 | 19 | - name: pnpm 20 | uses: pnpm/action-setup@v4 21 | with: 22 | version: 9.11.0 23 | 24 | - uses: actions/setup-node@v4 25 | with: 26 | node-version: "20.x" 27 | registry-url: "https://registry.npmjs.org" 28 | - run: pnpm install 29 | - run: pnpm run test 30 | - run: pnpm run build 31 | - run: npm publish --ignore-scripts --access public 32 | env: 33 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 34 | ``` -------------------------------------------------------------------------------- /typescript/tsup.config.ts: -------------------------------------------------------------------------------- ```typescript 1 | import {defineConfig} from 'tsup'; 2 | 3 | export default defineConfig([ 4 | { 5 | entry: ['src/langchain/index.ts'], 6 | outDir: 'langchain', 7 | format: ['cjs', 'esm'], 8 | dts: true, 9 | sourcemap: true, 10 | }, 11 | { 12 | entry: ['src/ai-sdk/index.ts'], 13 | outDir: 'ai-sdk', 14 | format: ['cjs', 'esm'], 15 | dts: true, 16 | sourcemap: true, 17 | }, 18 | { 19 | entry: ['src/modelcontextprotocol/index.ts'], 20 | outDir: 'modelcontextprotocol', 21 | format: ['cjs', 'esm'], 22 | dts: true, 23 | sourcemap: true, 24 | }, 25 | { 26 | entry: ['src/openai/index.ts'], 27 | outDir: 'openai', 28 | format: ['cjs', 'esm'], 29 | dts: true, 30 | sourcemap: true, 31 | }, 32 | { 33 | entry: ['src/cloudflare/index.ts'], 34 | outDir: 'cloudflare', 35 | format: ['cjs', 'esm'], 36 | dts: true, 37 | sourcemap: true, 38 | external: ['cloudflare:workers'], 39 | }, 40 | ]); 41 | ``` -------------------------------------------------------------------------------- /python/examples/openai/web_search/main.py: -------------------------------------------------------------------------------- ```python 1 | import asyncio 2 | import os 3 | 4 | from dotenv import load_dotenv 5 | load_dotenv() 6 | 7 | from agents import Agent, Runner, WebSearchTool 8 | from stripe_agent_toolkit.openai.toolkit import StripeAgentToolkit 9 | 10 | stripe_agent_toolkit = StripeAgentToolkit( 11 | secret_key=os.getenv("STRIPE_SECRET_KEY"), 12 | configuration={}, 13 | ) 14 | 15 | research_agent = Agent( 16 | name="Research Agent", 17 | instructions="You are an expert at research.", 18 | tools=[WebSearchTool()], 19 | hooks=stripe_agent_toolkit.billing_hook( 20 | type="outcome", 21 | customer=os.getenv("STRIPE_CUSTOMER_ID"), 22 | meter=os.getenv("STRIPE_METER"), 23 | ), 24 | ) 25 | 26 | async def main(): 27 | result = await Runner.run( 28 | research_agent, 29 | "search the web for 'global gdp' and give me the latest data.", 30 | ) 31 | print(result.final_output) 32 | 33 | if __name__ == "__main__": 34 | asyncio.run(main()) 35 | ``` -------------------------------------------------------------------------------- /typescript/examples/cloudflare/package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "remote-mcp-server", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "deploy": "wrangler deploy", 7 | "dev": "wrangler dev", 8 | "format": "biome format --write", 9 | "lint:fix": "biome lint --fix", 10 | "start": "wrangler dev", 11 | "cf-typegen": "wrangler types" 12 | }, 13 | "devDependencies": { 14 | "@biomejs/biome": "1.9.4", 15 | "@cloudflare/workers-types": "^4.20250515.0", 16 | "typescript": "^5.8.3", 17 | "workers-mcp": "0.1.0-3", 18 | "wrangler": "^4.15.2" 19 | }, 20 | "dependencies": { 21 | "@cloudflare/workers-oauth-provider": "^0.0.5", 22 | "@modelcontextprotocol/sdk": "1.11.3", 23 | "@stripe/agent-toolkit": "latest", 24 | "@types/node": "^22.15.18", 25 | "add": "^2.0.6", 26 | "agents": "^0.0.84", 27 | "dotenv": "^16.5.0", 28 | "emoji-from-text": "^1.1.13", 29 | "hono": "^4.7.9", 30 | "stripe": "^18.1.0", 31 | "zod": "^3.24.4" 32 | }, 33 | "pnpm": {} 34 | } 35 | ``` -------------------------------------------------------------------------------- /python/examples/strands/main.py: -------------------------------------------------------------------------------- ```python 1 | import os 2 | from dotenv import load_dotenv 3 | 4 | from strands import Agent 5 | from stripe_agent_toolkit.strands.toolkit import StripeAgentToolkit 6 | 7 | load_dotenv() 8 | 9 | # Initialize the Stripe Agent Toolkit 10 | stripe_agent_toolkit = StripeAgentToolkit( 11 | secret_key=os.getenv("STRIPE_SECRET_KEY"), 12 | configuration={ 13 | "actions": { 14 | "payment_links": { 15 | "create": True, 16 | }, 17 | "products": { 18 | "create": True, 19 | }, 20 | "prices": { 21 | "create": True, 22 | }, 23 | } 24 | }, 25 | ) 26 | 27 | # Get the Stripe tools 28 | tools = stripe_agent_toolkit.get_tools() 29 | 30 | # Create agent with Stripe tools 31 | agent = Agent( 32 | tools=tools 33 | ) 34 | 35 | # Test the agent 36 | response = agent(""" 37 | Create a payment link for a new product called 'test' with a price 38 | of $100. Come up with a funny description about buy bots, 39 | maybe a haiku. 40 | """) 41 | 42 | print(response) 43 | ``` -------------------------------------------------------------------------------- /python/stripe_agent_toolkit/openai/hooks.py: -------------------------------------------------------------------------------- ```python 1 | from typing import Any 2 | from agents import AgentHooks, RunContextWrapper, Agent, Tool 3 | from ..api import StripeAPI 4 | 5 | class BillingHooks(AgentHooks): 6 | def __init__(self, stripe: StripeAPI, type: str, customer: str, meter: str = None, meters: dict[str, str] = None): 7 | self.type = type 8 | self.stripe = stripe 9 | self.customer = customer 10 | self.meter = meter 11 | self.meters = meters 12 | 13 | async def on_end(self, context: RunContextWrapper, agent: Agent, output: Any) -> None: 14 | if self.type == "outcome": 15 | self.stripe.create_meter_event(self.meter, self.customer) 16 | 17 | if self.type == "token": 18 | if self.meters["input"]: 19 | self.stripe.create_meter_event(self.meters["input"], self.customer, context.usage.input_tokens) 20 | if self.meters["output"]: 21 | self.stripe.create_meter_event(self.meters["output"], self.customer, context.usage.output_tokens) 22 | ``` -------------------------------------------------------------------------------- /typescript/src/langchain/tool.ts: -------------------------------------------------------------------------------- ```typescript 1 | import {z} from 'zod'; 2 | import {StructuredTool} from '@langchain/core/tools'; 3 | import {CallbackManagerForToolRun} from '@langchain/core/callbacks/manager'; 4 | import {RunnableConfig} from '@langchain/core/runnables'; 5 | import StripeAPI from '../shared/api'; 6 | 7 | class StripeTool extends StructuredTool { 8 | stripeAPI: StripeAPI; 9 | 10 | method: string; 11 | 12 | name: string; 13 | 14 | description: string; 15 | 16 | schema: z.ZodObject<any, any, any, any>; 17 | 18 | constructor( 19 | StripeAPI: StripeAPI, 20 | method: string, 21 | description: string, 22 | schema: z.ZodObject<any, any, any, any, {[x: string]: any}> 23 | ) { 24 | super(); 25 | 26 | this.stripeAPI = StripeAPI; 27 | this.method = method; 28 | this.name = method; 29 | this.description = description; 30 | this.schema = schema; 31 | } 32 | 33 | _call( 34 | arg: z.output<typeof this.schema>, 35 | _runManager?: CallbackManagerForToolRun, 36 | _parentConfig?: RunnableConfig 37 | ): Promise<any> { 38 | return this.stripeAPI.run(this.method, arg); 39 | } 40 | } 41 | 42 | export default StripeTool; 43 | ``` -------------------------------------------------------------------------------- /typescript/src/langchain/toolkit.ts: -------------------------------------------------------------------------------- ```typescript 1 | import {BaseToolkit} from '@langchain/core/tools'; 2 | import StripeTool from './tool'; 3 | import StripeAPI from '../shared/api'; 4 | import tools from '../shared/tools'; 5 | import {isToolAllowed, type Configuration} from '../shared/configuration'; 6 | 7 | class StripeAgentToolkit implements BaseToolkit { 8 | private _stripe: StripeAPI; 9 | 10 | tools: StripeTool[]; 11 | 12 | constructor({ 13 | secretKey, 14 | configuration, 15 | }: { 16 | secretKey: string; 17 | configuration: Configuration; 18 | }) { 19 | this._stripe = new StripeAPI(secretKey, configuration.context); 20 | 21 | const context = configuration.context || {}; 22 | const filteredTools = tools(context).filter((tool) => 23 | isToolAllowed(tool, configuration) 24 | ); 25 | 26 | this.tools = filteredTools.map( 27 | (tool) => 28 | new StripeTool( 29 | this._stripe, 30 | tool.method, 31 | tool.description, 32 | tool.parameters 33 | ) 34 | ); 35 | } 36 | 37 | getTools(): StripeTool[] { 38 | return this.tools; 39 | } 40 | } 41 | 42 | export default StripeAgentToolkit; 43 | ``` -------------------------------------------------------------------------------- /python/pyproject.toml: -------------------------------------------------------------------------------- ```toml 1 | [project] 2 | name = "stripe-agent-toolkit" 3 | version = "0.6.1" 4 | description = "Stripe Agent Toolkit" 5 | readme = "README.md" 6 | license = {file = "LICENSE"} 7 | authors = [ 8 | {name = "Stripe", email = "[email protected]"} 9 | ] 10 | keywords = ["stripe", "api", "payments"] 11 | 12 | [project.urls] 13 | "Bug Tracker" = "https://github.com/stripe/agent-toolkit/issues" 14 | "Source Code" = "https://github.com/stripe/agent-toolkit" 15 | 16 | [tool.setuptools.packages.find] 17 | include = ["stripe_agent_toolkit*"] 18 | exclude = ["tests*", "examples*"] 19 | 20 | [tool.ruff] 21 | # same as our black config 22 | line-length = 79 23 | extend-exclude = ["build"] 24 | 25 | [tool.ruff.format] 26 | # currently the default value, but opt-out in the future 27 | docstring-code-format = false 28 | 29 | [tool.pyright] 30 | include = [ 31 | "*", 32 | ] 33 | exclude = ["build", "**/__pycache__"] 34 | reportMissingTypeArgument = true 35 | reportUnnecessaryCast = true 36 | reportUnnecessaryComparison = true 37 | reportUnnecessaryContains = true 38 | reportUnnecessaryIsInstance = true 39 | reportPrivateImportUsage = true 40 | reportUnnecessaryTypeIgnoreComment = true 41 | ``` -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- ```yaml 1 | name: Feature request 2 | description: Suggest an idea for this library 3 | labels: ["feature-request"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | Thanks for taking the time to fill out this feature request! 9 | - type: textarea 10 | id: problem 11 | attributes: 12 | label: Is your feature request related to a problem? Please describe. 13 | description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 14 | - type: textarea 15 | id: solution 16 | attributes: 17 | label: Describe the solution you'd like 18 | description: A clear and concise description of what you want to happen. 19 | - type: textarea 20 | id: alternatives 21 | attributes: 22 | label: Describe alternatives you've considered 23 | description: A clear and concise description of any alternative solutions or features you've considered. 24 | - type: textarea 25 | id: context 26 | attributes: 27 | label: Additional context 28 | description: Add any other context about the feature request here. 29 | ``` -------------------------------------------------------------------------------- /python/examples/langchain/main.py: -------------------------------------------------------------------------------- ```python 1 | import os 2 | from dotenv import load_dotenv 3 | 4 | from langchain import hub 5 | from langchain_openai import ChatOpenAI 6 | 7 | from langgraph.prebuilt import create_react_agent 8 | 9 | from stripe_agent_toolkit.langchain.toolkit import StripeAgentToolkit 10 | 11 | load_dotenv() 12 | 13 | llm = ChatOpenAI( 14 | model="gpt-4o", 15 | ) 16 | 17 | stripe_agent_toolkit = StripeAgentToolkit( 18 | secret_key=os.getenv("STRIPE_SECRET_KEY"), 19 | configuration={ 20 | "actions": { 21 | "payment_links": { 22 | "create": True, 23 | }, 24 | "products": { 25 | "create": True, 26 | }, 27 | "prices": { 28 | "create": True, 29 | }, 30 | } 31 | }, 32 | ) 33 | 34 | tools = [] 35 | tools.extend(stripe_agent_toolkit.get_tools()) 36 | 37 | langgraph_agent_executor = create_react_agent(llm, tools) 38 | 39 | input_state = { 40 | "messages": """ 41 | Create a payment link for a new product called 'test' with a price 42 | of $100. Come up with a funny description about buy bots, 43 | maybe a haiku. 44 | """, 45 | } 46 | 47 | output_state = langgraph_agent_executor.invoke(input_state) 48 | 49 | print(output_state["messages"][-1].content) 50 | ``` -------------------------------------------------------------------------------- /python/stripe_agent_toolkit/crewai/toolkit.py: -------------------------------------------------------------------------------- ```python 1 | """Stripe Agent Toolkit.""" 2 | 3 | from typing import List, Optional 4 | from pydantic import PrivateAttr 5 | 6 | from ..api import StripeAPI 7 | from ..tools import tools 8 | from ..configuration import Configuration, is_tool_allowed 9 | from .tool import StripeTool 10 | 11 | 12 | class StripeAgentToolkit: 13 | _tools: List = PrivateAttr(default=[]) 14 | 15 | def __init__( 16 | self, secret_key: str, configuration: Optional[Configuration] = None 17 | ): 18 | super().__init__() 19 | 20 | context = configuration.get("context") if configuration else None 21 | 22 | stripe_api = StripeAPI(secret_key=secret_key, context=context) 23 | 24 | filtered_tools = [ 25 | tool for tool in tools if is_tool_allowed(tool, configuration) 26 | ] 27 | 28 | self._tools = [ 29 | StripeTool( 30 | name=tool["method"], 31 | description=tool["description"], 32 | method=tool["method"], 33 | stripe_api=stripe_api, 34 | args_schema=tool.get("args_schema", None), 35 | ) 36 | for tool in filtered_tools 37 | ] 38 | 39 | def get_tools(self) -> List: 40 | """Get the tools in the toolkit.""" 41 | return self._tools 42 | ``` -------------------------------------------------------------------------------- /python/stripe_agent_toolkit/langchain/toolkit.py: -------------------------------------------------------------------------------- ```python 1 | """Stripe Agent Toolkit.""" 2 | 3 | from typing import List, Optional 4 | from pydantic import PrivateAttr 5 | 6 | from ..api import StripeAPI 7 | from ..tools import tools 8 | from ..configuration import Configuration, Context, is_tool_allowed 9 | from .tool import StripeTool 10 | 11 | 12 | class StripeAgentToolkit: 13 | _tools: List = PrivateAttr(default=[]) 14 | 15 | def __init__( 16 | self, secret_key: str, configuration: Optional[Configuration] = None 17 | ): 18 | super().__init__() 19 | 20 | context = configuration.get("context") if configuration else None 21 | 22 | stripe_api = StripeAPI(secret_key=secret_key, context=context) 23 | 24 | filtered_tools = [ 25 | tool for tool in tools if is_tool_allowed(tool, configuration) 26 | ] 27 | 28 | self._tools = [ 29 | StripeTool( 30 | name=tool["method"], 31 | description=tool["description"], 32 | method=tool["method"], 33 | stripe_api=stripe_api, 34 | args_schema=tool.get("args_schema", None), 35 | ) 36 | for tool in filtered_tools 37 | ] 38 | 39 | def get_tools(self) -> List: 40 | """Get the tools in the toolkit.""" 41 | return self._tools 42 | ``` -------------------------------------------------------------------------------- /typescript/examples/ai-sdk/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | import {StripeAgentToolkit} from '@stripe/agent-toolkit/ai-sdk'; 2 | import {openai} from '@ai-sdk/openai'; 3 | import { 4 | generateText, 5 | experimental_wrapLanguageModel as wrapLanguageModel, 6 | } from 'ai'; 7 | 8 | require('dotenv').config(); 9 | 10 | const stripeAgentToolkit = new StripeAgentToolkit({ 11 | secretKey: process.env.STRIPE_SECRET_KEY!, 12 | configuration: { 13 | actions: { 14 | paymentLinks: { 15 | create: true, 16 | }, 17 | products: { 18 | create: true, 19 | }, 20 | prices: { 21 | create: true, 22 | }, 23 | }, 24 | }, 25 | }); 26 | 27 | const model = wrapLanguageModel({ 28 | model: openai('gpt-4o'), 29 | middleware: stripeAgentToolkit.middleware({ 30 | billing: { 31 | customer: process.env.STRIPE_CUSTOMER_ID!, 32 | meters: { 33 | input: process.env.STRIPE_METER_INPUT!, 34 | output: process.env.STRIPE_METER_OUTPUT!, 35 | }, 36 | }, 37 | }), 38 | }); 39 | 40 | (async () => { 41 | const result = await generateText({ 42 | model: model, 43 | tools: { 44 | ...stripeAgentToolkit.getTools(), 45 | }, 46 | maxSteps: 5, 47 | prompt: 48 | 'Create a payment link for a new product called "test" with a price of $100. Come up with a funny description about buy bots, maybe a haiku.', 49 | }); 50 | 51 | console.log(result); 52 | })(); 53 | ``` -------------------------------------------------------------------------------- /python/stripe_agent_toolkit/strands/toolkit.py: -------------------------------------------------------------------------------- ```python 1 | """Stripe Agent Toolkit.""" 2 | 3 | from typing import List, Optional, Dict 4 | from strands.tools.tools import PythonAgentTool as StrandTool 5 | 6 | from ..api import StripeAPI 7 | from ..tools import tools 8 | from ..configuration import Configuration, is_tool_allowed 9 | from .tool import StripeTool 10 | from .hooks import BillingHooks 11 | 12 | 13 | class StripeAgentToolkit: 14 | def __init__( 15 | self, secret_key: str, configuration: Optional[Configuration] = None 16 | ): 17 | context = configuration.get("context") if configuration else None 18 | 19 | self._stripe_api = StripeAPI(secret_key=secret_key, context=context) 20 | 21 | filtered_tools = [ 22 | tool for tool in tools if is_tool_allowed(tool, configuration) 23 | ] 24 | 25 | self._tools = [ 26 | StripeTool(self._stripe_api, tool) 27 | for tool in filtered_tools 28 | ] 29 | 30 | def get_tools(self) -> List[StrandTool]: 31 | """Get the tools in the toolkit.""" 32 | return self._tools 33 | 34 | def billing_hook( 35 | self, 36 | type: Optional[str] = None, 37 | customer: Optional[str] = None, 38 | meter: Optional[str] = None, 39 | meters: Optional[Dict[str, str]] = None 40 | ) -> BillingHooks: 41 | return BillingHooks(self._stripe_api, type, customer, meter, meters) 42 | ``` -------------------------------------------------------------------------------- /typescript/src/test/shared/balance/functions.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import {retrieveBalance} from '@/shared/balance/retrieveBalance'; 2 | 3 | const Stripe = jest.fn().mockImplementation(() => ({ 4 | balance: { 5 | retrieve: 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('retrieveBalance', () => { 16 | it('should retrieve the balance and return it', async () => { 17 | const mockBalance = {available: [{amount: 1000, currency: 'usd'}]}; 18 | 19 | const context = {}; 20 | 21 | stripe.balance.retrieve.mockResolvedValue(mockBalance); 22 | 23 | const result = await retrieveBalance(stripe, context, {}); 24 | 25 | expect(stripe.balance.retrieve).toHaveBeenCalledWith({}, undefined); 26 | expect(result).toEqual(mockBalance); 27 | }); 28 | 29 | it('should specify the connected account if included in context', async () => { 30 | const mockBalance = {available: [{amount: 1000, currency: 'usd'}]}; 31 | 32 | const context = { 33 | account: 'acct_123456', 34 | }; 35 | 36 | stripe.balance.retrieve.mockResolvedValue(mockBalance); 37 | 38 | const result = await retrieveBalance(stripe, context, {}); 39 | 40 | expect(stripe.balance.retrieve).toHaveBeenCalledWith( 41 | {}, 42 | { 43 | stripeAccount: context.account, 44 | } 45 | ); 46 | expect(result).toEqual(mockBalance); 47 | }); 48 | }); 49 | ``` -------------------------------------------------------------------------------- /typescript/src/test/shared/paymentLinks/prompts.test.ts: -------------------------------------------------------------------------------- ```typescript 1 | import {createPaymentLinkPrompt} from '@/shared/paymentLinks/createPaymentLink'; 2 | 3 | describe('createPaymentLinkPrompt', () => { 4 | it('should return the correct prompt', () => { 5 | const prompt = createPaymentLinkPrompt({}); 6 | 7 | expect(prompt).toContain('This tool will create a payment link in Stripe.'); 8 | expect(prompt).toContain( 9 | 'price (str): The ID of the price to create the payment link for.' 10 | ); 11 | expect(prompt).toContain( 12 | 'quantity (int): The quantity of the product to include in the payment link.' 13 | ); 14 | expect(prompt).toContain( 15 | 'redirect_url (str, optional): The URL to redirect to after the payment is completed.' 16 | ); 17 | }); 18 | 19 | it('should return the correct prompt with customer context', () => { 20 | const prompt = createPaymentLinkPrompt({customer: 'cus_123'}); 21 | 22 | expect(prompt).toContain('This tool will create a payment link in Stripe.'); 23 | expect(prompt).toContain( 24 | 'price (str): The ID of the price to create the payment link for.' 25 | ); 26 | expect(prompt).toContain( 27 | 'quantity (int): The quantity of the product to include in the payment link.' 28 | ); 29 | expect(prompt).toContain( 30 | 'redirect_url (str, optional): The URL to redirect to after the payment is completed.' 31 | ); 32 | }); 33 | }); 34 | ``` -------------------------------------------------------------------------------- /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 | ``` -------------------------------------------------------------------------------- /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 {Tool} 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): Tool => ({ 40 | method: 'retrieve_balance', 41 | name: 'Retrieve Balance', 42 | description: retrieveBalancePrompt(context), 43 | parameters: retrieveBalanceParameters(context), 44 | annotations: retrieveBalanceAnnotations(), 45 | actions: { 46 | balance: { 47 | read: true, 48 | }, 49 | }, 50 | execute: retrieveBalance, 51 | }); 52 | 53 | export default tool; 54 | ``` -------------------------------------------------------------------------------- /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 | ``` -------------------------------------------------------------------------------- /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 | ``` -------------------------------------------------------------------------------- /.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 | 12 | defaults: 13 | run: 14 | working-directory: ./python 15 | 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | 20 | - name: Python 21 | uses: actions/setup-python@v4 22 | with: 23 | python-version: "3.11" 24 | 25 | - name: Install 26 | run: make venv 27 | 28 | - name: Build 29 | run: | 30 | set -x 31 | source venv/bin/activate 32 | rm -rf build dist *.egg-info 33 | make build 34 | python -m twine check dist/* 35 | 36 | - name: Test 37 | run: | 38 | make venv 39 | make test 40 | 41 | - name: Upload artifact 42 | uses: actions/upload-artifact@v4 43 | with: 44 | name: release-dists 45 | path: ./python/dist/ 46 | 47 | python-release: 48 | name: Publish to PyPi 49 | runs-on: ubuntu-latest 50 | environment: pypi 51 | needs: 52 | - python-build 53 | 54 | defaults: 55 | run: 56 | working-directory: ./python 57 | 58 | permissions: 59 | id-token: write 60 | 61 | steps: 62 | - name: Retrieve distribution 63 | uses: actions/download-artifact@v4 64 | with: 65 | name: release-dists 66 | path: dist/ 67 | 68 | - name: Publish package distributions to PyPI 69 | uses: pypa/gh-action-pypi-publish@release/v1 70 | ``` -------------------------------------------------------------------------------- /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 | ``` -------------------------------------------------------------------------------- /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 | ``` -------------------------------------------------------------------------------- /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 | ``` -------------------------------------------------------------------------------- /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 | ``` -------------------------------------------------------------------------------- /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 | ``` -------------------------------------------------------------------------------- /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 | ``` -------------------------------------------------------------------------------- /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 | ``` -------------------------------------------------------------------------------- /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.parameters.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 | ``` -------------------------------------------------------------------------------- /typescript/src/shared/api.ts: -------------------------------------------------------------------------------- ```typescript 1 | import Stripe from 'stripe'; 2 | 3 | import type {Context} from './configuration'; 4 | import tools, {Tool} 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: Tool[]; 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.7.11', 24 | url: 'https://github.com/stripe/agent-toolkit', 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 | ``` -------------------------------------------------------------------------------- /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 {Tool} 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): Tool => ({ 48 | method: 'create_product', 49 | name: 'Create Product', 50 | description: createProductPrompt(context), 51 | parameters: createProductParameters(context), 52 | annotations: createProductAnnotations(), 53 | actions: { 54 | products: { 55 | create: true, 56 | }, 57 | }, 58 | execute: createProduct, 59 | }); 60 | 61 | export default tool; 62 | ``` -------------------------------------------------------------------------------- /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 | ``` -------------------------------------------------------------------------------- /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 {Tool} 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): Tool => ({ 48 | method: 'create_customer', 49 | name: 'Create Customer', 50 | description: createCustomerPrompt(context), 51 | parameters: createCustomerParameters(context), 52 | annotations: createCustomerAnnotations(), 53 | actions: { 54 | customers: { 55 | create: true, 56 | }, 57 | }, 58 | execute: createCustomer, 59 | }); 60 | 61 | export default tool; 62 | ``` -------------------------------------------------------------------------------- /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 | ``` -------------------------------------------------------------------------------- /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 {Tool} 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): Tool => ({ 51 | method: 'finalize_invoice', 52 | name: 'Finalize Invoice', 53 | description: finalizeInvoicePrompt(context), 54 | parameters: finalizeInvoiceParameters(context), 55 | annotations: finalizeInvoiceAnnotations(), 56 | actions: { 57 | invoices: { 58 | update: true, 59 | }, 60 | }, 61 | execute: finalizeInvoice, 62 | }); 63 | 64 | export default tool; 65 | ``` -------------------------------------------------------------------------------- /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 {Tool} 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): Tool => ({ 54 | method: 'list_products', 55 | name: 'List Products', 56 | description: listProductsPrompt(context), 57 | parameters: listProductsParameters(context), 58 | annotations: listProductsAnnotations(), 59 | actions: { 60 | products: { 61 | read: true, 62 | }, 63 | }, 64 | execute: listProducts, 65 | }); 66 | 67 | export default tool; 68 | ``` -------------------------------------------------------------------------------- /modelcontextprotocol/package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "@stripe/mcp", 3 | "version": "0.2.4", 4 | "homepage": "https://github.com/stripe/agent-toolkit/tree/main/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 | } 58 | ``` -------------------------------------------------------------------------------- /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 | ``` -------------------------------------------------------------------------------- /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 | ``` -------------------------------------------------------------------------------- /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 {Tool} 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): Tool => ({ 51 | method: 'cancel_subscription', 52 | name: 'Cancel Subscription', 53 | description: cancelSubscriptionPrompt(context), 54 | parameters: cancelSubscriptionParameters(context), 55 | annotations: cancelSubscriptionAnnotations(), 56 | actions: { 57 | subscriptions: { 58 | update: true, 59 | }, 60 | }, 61 | execute: cancelSubscription, 62 | }); 63 | 64 | export default tool; 65 | ``` -------------------------------------------------------------------------------- /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 {Tool} 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): Tool => ({ 58 | method: 'list_coupons', 59 | name: 'List Coupons', 60 | description: listCouponsPrompt(context), 61 | parameters: listCouponsParameters(context), 62 | annotations: listCouponsAnnotations(), 63 | actions: { 64 | coupons: { 65 | read: true, 66 | }, 67 | }, 68 | execute: listCoupons, 69 | }); 70 | 71 | export default tool; 72 | ``` -------------------------------------------------------------------------------- /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 {Tool} 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): Tool => ({ 57 | method: 'list_prices', 58 | name: 'List Prices', 59 | description: listPricesPrompt(context), 60 | parameters: listPricesParameters(context), 61 | annotations: listPricesAnnotations(), 62 | actions: { 63 | prices: { 64 | read: true, 65 | }, 66 | }, 67 | execute: listPrices, 68 | }); 69 | 70 | export default tool; 71 | ``` -------------------------------------------------------------------------------- /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 | ``` -------------------------------------------------------------------------------- /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 {Tool} 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): Tool => ({ 59 | method: 'create_refund', 60 | name: 'Create Refund', 61 | description: createRefundPrompt(context), 62 | parameters: createRefundParameters(context), 63 | annotations: createRefundAnnotations(), 64 | actions: { 65 | refunds: { 66 | create: true, 67 | }, 68 | }, 69 | execute: createRefund, 70 | }); 71 | 72 | export default tool; 73 | ``` -------------------------------------------------------------------------------- /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 {Tool} 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): Tool => ({ 53 | method: 'create_price', 54 | name: 'Create Price', 55 | description: createPricePrompt(context), 56 | parameters: createPriceParameters(context), 57 | annotations: createPriceAnnotations(), 58 | actions: { 59 | prices: { 60 | create: true, 61 | }, 62 | }, 63 | execute: createPrice, 64 | }); 65 | 66 | export default tool; 67 | ``` -------------------------------------------------------------------------------- /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 | ``` -------------------------------------------------------------------------------- /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/agent-toolkit/tree/main" 61 | }, 62 | "icon": "stripe_icon.png" 63 | } 64 | ``` -------------------------------------------------------------------------------- /typescript/src/shared/configuration.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type {Tool} 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: Tool, 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 | ``` -------------------------------------------------------------------------------- /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 | parameters: zodToJsonSchema(tool.parameters), 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 | ``` -------------------------------------------------------------------------------- /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 {Tool} 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): Tool => ({ 59 | method: 'list_customers', 60 | name: 'List Customers', 61 | description: listCustomersPrompt(context), 62 | parameters: listCustomersParameters(context), 63 | annotations: listCustomersAnnotations(), 64 | actions: { 65 | customers: { 66 | read: true, 67 | }, 68 | }, 69 | execute: listCustomers, 70 | }); 71 | 72 | export default tool; 73 | ``` -------------------------------------------------------------------------------- /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 | ``` -------------------------------------------------------------------------------- /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 | ```