#
tokens: 49514/50000 121/259 files (page 1/10)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 1 of 10. Use http://codebase.md/dodopayments/dodopayments-node?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .devcontainer
│   └── devcontainer.json
├── .github
│   └── workflows
│       ├── ci.yml
│       ├── docker-mcp.yml
│       ├── publish-npm.yml
│       └── release-doctor.yml
├── .gitignore
├── .prettierignore
├── .prettierrc.json
├── .release-please-manifest.json
├── .stats.yml
├── api.md
├── bin
│   ├── check-release-environment
│   ├── cli
│   ├── docker-tags
│   ├── migration-config.json
│   └── publish-npm
├── Brewfile
├── CHANGELOG.md
├── CONTRIBUTING.md
├── eslint.config.mjs
├── examples
│   └── .keep
├── jest.config.ts
├── LICENSE
├── MIGRATION.md
├── package.json
├── packages
│   └── mcp-server
│       ├── .dockerignore
│       ├── build
│       ├── cloudflare-worker
│       │   ├── .gitignore
│       │   ├── biome.json
│       │   ├── package.json
│       │   ├── README.md
│       │   ├── src
│       │   │   ├── app.ts
│       │   │   ├── index.ts
│       │   │   └── utils.ts
│       │   ├── static
│       │   │   └── home.md
│       │   ├── tsconfig.json
│       │   ├── worker-configuration.d.ts
│       │   └── wrangler.jsonc
│       ├── Dockerfile
│       ├── jest.config.ts
│       ├── manifest.json
│       ├── package.json
│       ├── README.md
│       ├── scripts
│       │   ├── copy-bundle-files.cjs
│       │   └── postprocess-dist-package-json.cjs
│       ├── src
│       │   ├── code-tool-paths.cts
│       │   ├── code-tool-types.ts
│       │   ├── code-tool-worker.ts
│       │   ├── code-tool.ts
│       │   ├── compat.ts
│       │   ├── docs-search-tool.ts
│       │   ├── dynamic-tools.ts
│       │   ├── filtering.ts
│       │   ├── headers.ts
│       │   ├── http.ts
│       │   ├── index.ts
│       │   ├── options.ts
│       │   ├── server.ts
│       │   ├── stdio.ts
│       │   ├── tools
│       │   │   ├── addons
│       │   │   │   ├── create-addons.ts
│       │   │   │   ├── list-addons.ts
│       │   │   │   ├── retrieve-addons.ts
│       │   │   │   ├── update-addons.ts
│       │   │   │   └── update-images-addons.ts
│       │   │   ├── brands
│       │   │   │   ├── create-brands.ts
│       │   │   │   ├── list-brands.ts
│       │   │   │   ├── retrieve-brands.ts
│       │   │   │   ├── update-brands.ts
│       │   │   │   └── update-images-brands.ts
│       │   │   ├── checkout-sessions
│       │   │   │   └── create-checkout-sessions.ts
│       │   │   ├── customers
│       │   │   │   ├── create-customers.ts
│       │   │   │   ├── customer-portal
│       │   │   │   │   └── create-customers-customer-portal.ts
│       │   │   │   ├── list-customers.ts
│       │   │   │   ├── retrieve-customers.ts
│       │   │   │   ├── update-customers.ts
│       │   │   │   └── wallets
│       │   │   │       ├── ledger-entries
│       │   │   │       │   ├── create-wallets-customers-ledger-entries.ts
│       │   │   │       │   └── list-wallets-customers-ledger-entries.ts
│       │   │   │       └── list-customers-wallets.ts
│       │   │   ├── discounts
│       │   │   │   ├── create-discounts.ts
│       │   │   │   ├── delete-discounts.ts
│       │   │   │   ├── list-discounts.ts
│       │   │   │   ├── retrieve-discounts.ts
│       │   │   │   └── update-discounts.ts
│       │   │   ├── disputes
│       │   │   │   ├── list-disputes.ts
│       │   │   │   └── retrieve-disputes.ts
│       │   │   ├── index.ts
│       │   │   ├── invoices
│       │   │   │   └── payments
│       │   │   │       ├── retrieve-invoices-payments.ts
│       │   │   │       └── retrieve-refund-invoices-payments.ts
│       │   │   ├── license-key-instances
│       │   │   │   ├── list-license-key-instances.ts
│       │   │   │   ├── retrieve-license-key-instances.ts
│       │   │   │   └── update-license-key-instances.ts
│       │   │   ├── license-keys
│       │   │   │   ├── list-license-keys.ts
│       │   │   │   ├── retrieve-license-keys.ts
│       │   │   │   └── update-license-keys.ts
│       │   │   ├── licenses
│       │   │   │   ├── activate-licenses.ts
│       │   │   │   ├── deactivate-licenses.ts
│       │   │   │   └── validate-licenses.ts
│       │   │   ├── meters
│       │   │   │   ├── archive-meters.ts
│       │   │   │   ├── create-meters.ts
│       │   │   │   ├── list-meters.ts
│       │   │   │   ├── retrieve-meters.ts
│       │   │   │   └── unarchive-meters.ts
│       │   │   ├── misc
│       │   │   │   └── list-supported-countries-misc.ts
│       │   │   ├── payments
│       │   │   │   ├── create-payments.ts
│       │   │   │   ├── list-payments.ts
│       │   │   │   ├── retrieve-line-items-payments.ts
│       │   │   │   └── retrieve-payments.ts
│       │   │   ├── payouts
│       │   │   │   └── list-payouts.ts
│       │   │   ├── products
│       │   │   │   ├── archive-products.ts
│       │   │   │   ├── create-products.ts
│       │   │   │   ├── images
│       │   │   │   │   └── update-products-images.ts
│       │   │   │   ├── list-products.ts
│       │   │   │   ├── retrieve-products.ts
│       │   │   │   ├── unarchive-products.ts
│       │   │   │   ├── update-files-products.ts
│       │   │   │   └── update-products.ts
│       │   │   ├── refunds
│       │   │   │   ├── create-refunds.ts
│       │   │   │   ├── list-refunds.ts
│       │   │   │   └── retrieve-refunds.ts
│       │   │   ├── subscriptions
│       │   │   │   ├── change-plan-subscriptions.ts
│       │   │   │   ├── charge-subscriptions.ts
│       │   │   │   ├── create-subscriptions.ts
│       │   │   │   ├── list-subscriptions.ts
│       │   │   │   ├── retrieve-subscriptions.ts
│       │   │   │   ├── retrieve-usage-history-subscriptions.ts
│       │   │   │   └── update-subscriptions.ts
│       │   │   ├── types.ts
│       │   │   ├── usage-events
│       │   │   │   ├── ingest-usage-events.ts
│       │   │   │   ├── list-usage-events.ts
│       │   │   │   └── retrieve-usage-events.ts
│       │   │   └── webhooks
│       │   │       ├── create-webhooks.ts
│       │   │       ├── delete-webhooks.ts
│       │   │       ├── headers
│       │   │       │   ├── retrieve-webhooks-headers.ts
│       │   │       │   └── update-webhooks-headers.ts
│       │   │       ├── list-webhooks.ts
│       │   │       ├── retrieve-secret-webhooks.ts
│       │   │       ├── retrieve-webhooks.ts
│       │   │       └── update-webhooks.ts
│       │   └── tools.ts
│       ├── tests
│       │   ├── compat.test.ts
│       │   ├── dynamic-tools.test.ts
│       │   ├── options.test.ts
│       │   └── tools.test.ts
│       ├── tsc-multi.json
│       ├── tsconfig.build.json
│       ├── tsconfig.dist-src.json
│       ├── tsconfig.json
│       └── yarn.lock
├── README.md
├── release-please-config.json
├── scripts
│   ├── bootstrap
│   ├── build
│   ├── build-all
│   ├── fast-format
│   ├── format
│   ├── lint
│   ├── mock
│   ├── publish-packages.ts
│   ├── test
│   └── utils
│       ├── attw-report.cjs
│       ├── check-is-in-git-install.sh
│       ├── check-version.cjs
│       ├── fix-index-exports.cjs
│       ├── git-swap.sh
│       ├── make-dist-package-json.cjs
│       ├── postprocess-files.cjs
│       └── upload-artifact.sh
├── SECURITY.md
├── src
│   ├── api-promise.ts
│   ├── client.ts
│   ├── core
│   │   ├── api-promise.ts
│   │   ├── error.ts
│   │   ├── pagination.ts
│   │   ├── README.md
│   │   ├── resource.ts
│   │   └── uploads.ts
│   ├── error.ts
│   ├── index.ts
│   ├── internal
│   │   ├── builtin-types.ts
│   │   ├── detect-platform.ts
│   │   ├── errors.ts
│   │   ├── headers.ts
│   │   ├── parse.ts
│   │   ├── README.md
│   │   ├── request-options.ts
│   │   ├── shim-types.ts
│   │   ├── shims.ts
│   │   ├── to-file.ts
│   │   ├── types.ts
│   │   ├── uploads.ts
│   │   ├── utils
│   │   │   ├── base64.ts
│   │   │   ├── bytes.ts
│   │   │   ├── env.ts
│   │   │   ├── log.ts
│   │   │   ├── path.ts
│   │   │   ├── sleep.ts
│   │   │   ├── uuid.ts
│   │   │   └── values.ts
│   │   └── utils.ts
│   ├── lib
│   │   └── .keep
│   ├── pagination.ts
│   ├── resource.ts
│   ├── resources
│   │   ├── addons.ts
│   │   ├── brands.ts
│   │   ├── checkout-sessions.ts
│   │   ├── customers
│   │   │   ├── customer-portal.ts
│   │   │   ├── customers.ts
│   │   │   ├── index.ts
│   │   │   ├── wallets
│   │   │   │   ├── index.ts
│   │   │   │   ├── ledger-entries.ts
│   │   │   │   └── wallets.ts
│   │   │   └── wallets.ts
│   │   ├── customers.ts
│   │   ├── discounts.ts
│   │   ├── disputes.ts
│   │   ├── index.ts
│   │   ├── invoices
│   │   │   ├── index.ts
│   │   │   ├── invoices.ts
│   │   │   └── payments.ts
│   │   ├── invoices.ts
│   │   ├── license-key-instances.ts
│   │   ├── license-keys.ts
│   │   ├── licenses.ts
│   │   ├── meters.ts
│   │   ├── misc.ts
│   │   ├── payments.ts
│   │   ├── payouts.ts
│   │   ├── products
│   │   │   ├── images.ts
│   │   │   ├── index.ts
│   │   │   └── products.ts
│   │   ├── products.ts
│   │   ├── refunds.ts
│   │   ├── subscriptions.ts
│   │   ├── usage-events.ts
│   │   ├── webhook-events.ts
│   │   ├── webhooks
│   │   │   ├── headers.ts
│   │   │   ├── index.ts
│   │   │   └── webhooks.ts
│   │   └── webhooks.ts
│   ├── resources.ts
│   ├── uploads.ts
│   └── version.ts
├── tests
│   ├── api-resources
│   │   ├── addons.test.ts
│   │   ├── brands.test.ts
│   │   ├── checkout-sessions.test.ts
│   │   ├── customers
│   │   │   ├── customer-portal.test.ts
│   │   │   ├── customers.test.ts
│   │   │   └── wallets
│   │   │       ├── ledger-entries.test.ts
│   │   │       └── wallets.test.ts
│   │   ├── discounts.test.ts
│   │   ├── disputes.test.ts
│   │   ├── license-key-instances.test.ts
│   │   ├── license-keys.test.ts
│   │   ├── licenses.test.ts
│   │   ├── meters.test.ts
│   │   ├── misc.test.ts
│   │   ├── payments.test.ts
│   │   ├── payouts.test.ts
│   │   ├── products
│   │   │   ├── images.test.ts
│   │   │   └── products.test.ts
│   │   ├── refunds.test.ts
│   │   ├── subscriptions.test.ts
│   │   ├── usage-events.test.ts
│   │   └── webhooks
│   │       ├── headers.test.ts
│   │       └── webhooks.test.ts
│   ├── base64.test.ts
│   ├── buildHeaders.test.ts
│   ├── form.test.ts
│   ├── index.test.ts
│   ├── path.test.ts
│   ├── stringifyQuery.test.ts
│   └── uploads.test.ts
├── tsc-multi.json
├── tsconfig.build.json
├── tsconfig.deno.json
├── tsconfig.dist-src.json
├── tsconfig.json
└── yarn.lock
```

# Files

--------------------------------------------------------------------------------
/.release-please-manifest.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   ".": "2.3.1"
3 | }
4 | 
```

--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------

```
1 | CHANGELOG.md
2 | /ecosystem-tests/*/**
3 | /node_modules
4 | /deno
5 | 
6 | # don't format tsc output, will break source maps
7 | dist
8 | 
```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
 1 | .prism.log
 2 | node_modules
 3 | yarn-error.log
 4 | codegen.log
 5 | Brewfile.lock.json
 6 | dist
 7 | dist-deno
 8 | /*.tgz
 9 | .idea/
10 | .eslintcache
11 | dist-bundle
12 | *.mcpb
13 | 
```

--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "arrowParens": "always",
3 |   "experimentalTernaries": true,
4 |   "printWidth": 110,
5 |   "singleQuote": true,
6 |   "trailingComma": "all"
7 | }
8 | 
```

--------------------------------------------------------------------------------
/src/lib/.keep:
--------------------------------------------------------------------------------

```
1 | File generated from our OpenAPI spec by Stainless.
2 | 
3 | This directory can be used to store custom files to expand the SDK.
4 | It is ignored by Stainless code generation and its content (other than this keep file) won't be touched.
5 | 
```

--------------------------------------------------------------------------------
/examples/.keep:
--------------------------------------------------------------------------------

```
1 | File generated from our OpenAPI spec by Stainless.
2 | 
3 | This directory can be used to store example files demonstrating usage of this SDK.
4 | It is ignored by Stainless code generation and its content (other than this keep file) won't be touched.
5 | 
```

--------------------------------------------------------------------------------
/.stats.yml:
--------------------------------------------------------------------------------

```yaml
1 | configured_endpoints: 77
2 | openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/dodo-payments%2Fdodo-payments-233d6735a99febbd67b26d1682829a9958a94aeb99afb13d9a0f7ed2bbf760e7.yml
3 | openapi_spec_hash: 15ae965d2fa82590457ccf0ed7d83a30
4 | config_hash: 4e66056c26dbceea9d8c234d58c45669
5 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/.dockerignore:
--------------------------------------------------------------------------------

```
 1 | # Dependencies
 2 | node_modules/
 3 | **/node_modules/
 4 | 
 5 | # Build outputs
 6 | dist/
 7 | **/dist/
 8 | build/
 9 | **/build/
10 | 
11 | # Git
12 | .git/
13 | .gitignore
14 | 
15 | # CI/CD
16 | .github/
17 | .gitlab-ci.yml
18 | .travis.yml
19 | 
20 | # IDE
21 | .vscode/
22 | .idea/
23 | *.swp
24 | *.swo
25 | *~
26 | 
27 | # OS
28 | .DS_Store
29 | Thumbs.db
30 | 
31 | # Documentation
32 | *.md
33 | docs/
34 | LICENSE
35 | 
36 | # Testing
37 | test/
38 | tests/
39 | __tests__/
40 | *.test.js
41 | *.spec.js
42 | coverage/
43 | .nyc_output/
44 | 
45 | # Logs
46 | *.log
47 | npm-debug.log*
48 | yarn-debug.log*
49 | yarn-error.log*
50 | 
51 | # Environment
52 | .env
53 | .env.*
54 | 
55 | # Temporary files
56 | *.tmp
57 | *.temp
58 | .cache/
59 | 
60 | # Examples and scripts
61 | examples/
62 | bin/
63 | 
64 | # Other packages (we only need mcp-server)
65 | packages/*/
66 | !packages/mcp-server/
67 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/cloudflare-worker/.gitignore:
--------------------------------------------------------------------------------

```
  1 | node_modules
  2 | 
  3 | .nx
  4 | .idea
  5 | .vscode
  6 | .zed
  7 | # Logs
  8 | 
  9 | logs
 10 | _.log
 11 | npm-debug.log_
 12 | yarn-debug.log*
 13 | yarn-error.log*
 14 | lerna-debug.log*
 15 | .pnpm-debug.log*
 16 | 
 17 | # Diagnostic reports (https://nodejs.org/api/report.html)
 18 | 
 19 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
 20 | 
 21 | # Runtime data
 22 | 
 23 | pids
 24 | _.pid
 25 | _.seed
 26 | \*.pid.lock
 27 | 
 28 | # Directory for instrumented libs generated by jscoverage/JSCover
 29 | 
 30 | lib-cov
 31 | 
 32 | # Coverage directory used by tools like istanbul
 33 | 
 34 | coverage
 35 | \*.lcov
 36 | 
 37 | # nyc test coverage
 38 | 
 39 | .nyc_output
 40 | 
 41 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
 42 | 
 43 | .grunt
 44 | 
 45 | # Bower dependency directory (https://bower.io/)
 46 | 
 47 | bower_components
 48 | 
 49 | # node-waf configuration
 50 | 
 51 | .lock-wscript
 52 | 
 53 | # Compiled binary addons (https://nodejs.org/api/addons.html)
 54 | 
 55 | build/Release
 56 | 
 57 | # Dependency directories
 58 | 
 59 | node_modules/
 60 | jspm_packages/
 61 | 
 62 | # Snowpack dependency directory (https://snowpack.dev/)
 63 | 
 64 | web_modules/
 65 | 
 66 | # TypeScript cache
 67 | 
 68 | \*.tsbuildinfo
 69 | 
 70 | # Optional npm cache directory
 71 | 
 72 | .npm
 73 | 
 74 | # Optional eslint cache
 75 | 
 76 | .eslintcache
 77 | 
 78 | # Optional stylelint cache
 79 | 
 80 | .stylelintcache
 81 | 
 82 | # Microbundle cache
 83 | 
 84 | .rpt2_cache/
 85 | .rts2_cache_cjs/
 86 | .rts2_cache_es/
 87 | .rts2_cache_umd/
 88 | 
 89 | # Optional REPL history
 90 | 
 91 | .node_repl_history
 92 | 
 93 | # Output of 'npm pack'
 94 | 
 95 | \*.tgz
 96 | 
 97 | # Yarn Integrity file
 98 | 
 99 | .yarn-integrity
100 | 
101 | # dotenv environment variable files
102 | 
103 | .env
104 | .env.development.local
105 | .env.test.local
106 | .env.production.local
107 | .env.local
108 | 
109 | # parcel-bundler cache (https://parceljs.org/)
110 | 
111 | .cache
112 | .parcel-cache
113 | 
114 | # Next.js build output
115 | 
116 | .next
117 | out
118 | 
119 | # Nuxt.js build / generate output
120 | 
121 | .nuxt
122 | dist
123 | 
124 | # Gatsby files
125 | 
126 | .cache/
127 | 
128 | # Comment in the public line in if your project uses Gatsby and not Next.js
129 | 
130 | # https://nextjs.org/blog/next-9-1#public-directory-support
131 | 
132 | # public
133 | 
134 | # vuepress build output
135 | 
136 | .vuepress/dist
137 | 
138 | # vuepress v2.x temp and cache directory
139 | 
140 | .temp
141 | .cache
142 | 
143 | # Docusaurus cache and generated files
144 | 
145 | .docusaurus
146 | 
147 | # Serverless directories
148 | 
149 | .serverless/
150 | 
151 | # FuseBox cache
152 | 
153 | .fusebox/
154 | 
155 | # DynamoDB Local files
156 | 
157 | .dynamodb/
158 | 
159 | # TernJS port file
160 | 
161 | .tern-port
162 | 
163 | # Stores VSCode versions used for testing VSCode extensions
164 | 
165 | .vscode-test
166 | 
167 | # yarn v2
168 | 
169 | .yarn/cache
170 | .yarn/unplugged
171 | .yarn/build-state.yml
172 | .yarn/install-state.gz
173 | .pnp.\*
174 | 
175 | # wrangler project
176 | 
177 | .dev.vars
178 | .wrangler/
179 | 
```

--------------------------------------------------------------------------------
/src/core/README.md:
--------------------------------------------------------------------------------

```markdown
1 | # `core`
2 | 
3 | This directory holds public modules implementing non-resource-specific SDK functionality.
4 | 
```

--------------------------------------------------------------------------------
/src/internal/README.md:
--------------------------------------------------------------------------------

```markdown
1 | # `internal`
2 | 
3 | The modules in this directory are not importable outside this package and will change between releases.
4 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/cloudflare-worker/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Remote MCP Server on Cloudflare with Stainless
  2 | 
  3 | Remote MCP servers require OAuth, so this flow implements a local version of the OAuth redirects, but instead accepts the
  4 | API token and any other client configuration options that you'd need to instantiate your TypeScript client.
  5 | 
  6 | ## Usage
  7 | 
  8 | The recommended way to use this project is to use the below "deploy to cloudflare" button to use this repo as a template for generating a server.
  9 | 
 10 | [![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/dodopayments/dodopayments-typescript/tree/main/packages/mcp-server/cloudflare-worker)
 11 | 
 12 | ## Develop locally
 13 | 
 14 | ```bash
 15 | # install dependencies
 16 | npm install
 17 | 
 18 | # run locally
 19 | npm run dev
 20 | ```
 21 | 
 22 | You should be able to open [`http://localhost:8787/`](http://localhost:8787/) in your browser
 23 | 
 24 | ## Connect the MCP inspector to your server
 25 | 
 26 | To explore your new MCP api, you can use the [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector).
 27 | 
 28 | - Start it with `npx @modelcontextprotocol/inspector`
 29 | - [Within the inspector](http://localhost:5173), switch the Transport Type to `SSE` and enter `http://localhost:8787/sse` as the URL of the MCP server to connect to, and click "Connect"
 30 | - You will navigate to a (mock) user/password login screen. Input any email and pass to login.
 31 | - You should be redirected back to the MCP Inspector and you can now list and call any defined tools!
 32 | 
 33 | ## Connect Claude Desktop to your local MCP server
 34 | 
 35 | The MCP inspector is great, but we really want to connect this to Claude! Follow [Anthropic's Quickstart](https://modelcontextprotocol.io/quickstart/user) and within Claude Desktop go to Settings > Developer > Edit Config to find your configuration file.
 36 | 
 37 | Open the file in your text editor and replace it with this configuration:
 38 | 
 39 | ```json
 40 | {
 41 |   "mcpServers": {
 42 |     "dodopayments_api": {
 43 |       "command": "npx",
 44 |       "args": ["mcp-remote", "http://localhost:8787/sse"]
 45 |     }
 46 |   }
 47 | }
 48 | ```
 49 | 
 50 | This will run a local proxy and let Claude talk to your MCP server over HTTP
 51 | 
 52 | When you open Claude a browser window should open and allow you to login. You should see the tools available in the bottom right. Given the right prompt Claude should ask to call the tool.
 53 | 
 54 | ## Deploy to Cloudflare
 55 | 
 56 | If you want to manually deploy this server (e.g. without the "deploy to cloudflare" button)
 57 | 
 58 | 1. `npx wrangler@latest kv namespace create remote-mcp-server-oauth-kv`
 59 | 2. Follow the guidance to add the kv namespace ID to `wrangler.jsonc`
 60 | 3. `npm run deploy`
 61 | 
 62 | ## Call your newly deployed remote MCP server from a remote MCP client
 63 | 
 64 | Just like you did above in "Develop locally", run the MCP inspector:
 65 | 
 66 | `npx @modelcontextprotocol/inspector@latest`
 67 | 
 68 | Then enter the `workers.dev` URL (ex: `worker-name.account-name.workers.dev/sse`) of your Worker in the inspector as the URL of the MCP server to connect to, and click "Connect".
 69 | 
 70 | You've now connected to your MCP server from a remote MCP client.
 71 | 
 72 | ## Connect Claude Desktop to your remote MCP server
 73 | 
 74 | Update the Claude configuration file to point to your `workers.dev` URL (ex: `worker-name.account-name.workers.dev/sse`) and restart Claude
 75 | 
 76 | ```json
 77 | {
 78 |   "mcpServers": {
 79 |     "dodopayments_api": {
 80 |       "command": "npx",
 81 |       "args": ["mcp-remote", "https://worker-name.account-name.workers.dev/sse"]
 82 |     }
 83 |   }
 84 | }
 85 | ```
 86 | 
 87 | ## Debugging
 88 | 
 89 | Should anything go wrong it can be helpful to restart Claude, or to try connecting directly to your
 90 | MCP server on the command line with the following command.
 91 | 
 92 | ```bash
 93 | npx mcp-remote http://localhost:8787/sse
 94 | ```
 95 | 
 96 | In some rare cases it may help to clear the files added to `~/.mcp-auth`
 97 | 
 98 | ```bash
 99 | rm -rf ~/.mcp-auth
100 | ```
101 | 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Dodo Payments TypeScript API Library
  2 | 
  3 | [![NPM version](<https://img.shields.io/npm/v/dodopayments.svg?label=npm%20(stable)>)](https://npmjs.org/package/dodopayments) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/dodopayments)
  4 | 
  5 | This library provides convenient access to the [Dodo Payments](https://dodopayments.com) REST API from server-side TypeScript or JavaScript.
  6 | 
  7 | The REST API documentation can be found on [docs.dodopayments.com](https://docs.dodopayments.com/api-reference/introduction). The full API of this library can be found in [api.md](api.md).
  8 | 
  9 | It is generated with [Stainless](https://www.stainless.com/).
 10 | 
 11 | ## Installation
 12 | 
 13 | ```sh
 14 | npm install dodopayments
 15 | ```
 16 | 
 17 | ## Usage
 18 | 
 19 | The full API of this library can be found in [api.md](api.md).
 20 | 
 21 | <!-- prettier-ignore -->
 22 | ```js
 23 | import DodoPayments from 'dodopayments';
 24 | 
 25 | const client = new DodoPayments({
 26 |   bearerToken: process.env['DODO_PAYMENTS_API_KEY'], // This is the default and can be omitted
 27 |   environment: 'test_mode', // defaults to 'live_mode'
 28 | });
 29 | 
 30 | const checkoutSessionResponse = await client.checkoutSessions.create({
 31 |   product_cart: [{ product_id: 'product_id', quantity: 0 }],
 32 | });
 33 | 
 34 | console.log(checkoutSessionResponse.session_id);
 35 | ```
 36 | 
 37 | ### Request & Response types
 38 | 
 39 | This library includes TypeScript definitions for all request params and response fields. You may import and use them like so:
 40 | 
 41 | <!-- prettier-ignore -->
 42 | ```ts
 43 | import DodoPayments from 'dodopayments';
 44 | 
 45 | const client = new DodoPayments({
 46 |   bearerToken: process.env['DODO_PAYMENTS_API_KEY'], // This is the default and can be omitted
 47 |   environment: 'test_mode', // defaults to 'live_mode'
 48 | });
 49 | 
 50 | const params: DodoPayments.CheckoutSessionCreateParams = {
 51 |   product_cart: [{ product_id: 'product_id', quantity: 0 }],
 52 | };
 53 | const checkoutSessionResponse: DodoPayments.CheckoutSessionResponse = await client.checkoutSessions.create(
 54 |   params,
 55 | );
 56 | ```
 57 | 
 58 | Documentation for each method, request param, and response field are available in docstrings and will appear on hover in most modern editors.
 59 | 
 60 | ## Handling errors
 61 | 
 62 | When the library is unable to connect to the API,
 63 | or if the API returns a non-success status code (i.e., 4xx or 5xx response),
 64 | a subclass of `APIError` will be thrown:
 65 | 
 66 | <!-- prettier-ignore -->
 67 | ```ts
 68 | const checkoutSessionResponse = await client.checkoutSessions
 69 |   .create({ product_cart: [{ product_id: 'product_id', quantity: 0 }] })
 70 |   .catch(async (err) => {
 71 |     if (err instanceof DodoPayments.APIError) {
 72 |       console.log(err.status); // 400
 73 |       console.log(err.name); // BadRequestError
 74 |       console.log(err.headers); // {server: 'nginx', ...}
 75 |     } else {
 76 |       throw err;
 77 |     }
 78 |   });
 79 | ```
 80 | 
 81 | Error codes are as follows:
 82 | 
 83 | | Status Code | Error Type                 |
 84 | | ----------- | -------------------------- |
 85 | | 400         | `BadRequestError`          |
 86 | | 401         | `AuthenticationError`      |
 87 | | 403         | `PermissionDeniedError`    |
 88 | | 404         | `NotFoundError`            |
 89 | | 422         | `UnprocessableEntityError` |
 90 | | 429         | `RateLimitError`           |
 91 | | >=500       | `InternalServerError`      |
 92 | | N/A         | `APIConnectionError`       |
 93 | 
 94 | ### Retries
 95 | 
 96 | Certain errors will be automatically retried 2 times by default, with a short exponential backoff.
 97 | Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict,
 98 | 429 Rate Limit, and >=500 Internal errors will all be retried by default.
 99 | 
100 | You can use the `maxRetries` option to configure or disable this:
101 | 
102 | <!-- prettier-ignore -->
103 | ```js
104 | // Configure the default for all requests:
105 | const client = new DodoPayments({
106 |   maxRetries: 0, // default is 2
107 | });
108 | 
109 | // Or, configure per-request:
110 | await client.checkoutSessions.create({ product_cart: [{ product_id: 'product_id', quantity: 0 }] }, {
111 |   maxRetries: 5,
112 | });
113 | ```
114 | 
115 | ### Timeouts
116 | 
117 | Requests time out after 1 minute by default. You can configure this with a `timeout` option:
118 | 
119 | <!-- prettier-ignore -->
120 | ```ts
121 | // Configure the default for all requests:
122 | const client = new DodoPayments({
123 |   timeout: 20 * 1000, // 20 seconds (default is 1 minute)
124 | });
125 | 
126 | // Override per-request:
127 | await client.checkoutSessions.create({ product_cart: [{ product_id: 'product_id', quantity: 0 }] }, {
128 |   timeout: 5 * 1000,
129 | });
130 | ```
131 | 
132 | On timeout, an `APIConnectionTimeoutError` is thrown.
133 | 
134 | Note that requests which time out will be [retried twice by default](#retries).
135 | 
136 | ## Auto-pagination
137 | 
138 | List methods in the DodoPayments API are paginated.
139 | You can use the `for await … of` syntax to iterate through items across all pages:
140 | 
141 | ```ts
142 | async function fetchAllPaymentListResponses(params) {
143 |   const allPaymentListResponses = [];
144 |   // Automatically fetches more pages as needed.
145 |   for await (const paymentListResponse of client.payments.list()) {
146 |     allPaymentListResponses.push(paymentListResponse);
147 |   }
148 |   return allPaymentListResponses;
149 | }
150 | ```
151 | 
152 | Alternatively, you can request a single page at a time:
153 | 
154 | ```ts
155 | let page = await client.payments.list();
156 | for (const paymentListResponse of page.items) {
157 |   console.log(paymentListResponse);
158 | }
159 | 
160 | // Convenience methods are provided for manually paginating:
161 | while (page.hasNextPage()) {
162 |   page = await page.getNextPage();
163 |   // ...
164 | }
165 | ```
166 | 
167 | ## Advanced Usage
168 | 
169 | ### Accessing raw Response data (e.g., headers)
170 | 
171 | The "raw" `Response` returned by `fetch()` can be accessed through the `.asResponse()` method on the `APIPromise` type that all methods return.
172 | This method returns as soon as the headers for a successful response are received and does not consume the response body, so you are free to write custom parsing or streaming logic.
173 | 
174 | You can also use the `.withResponse()` method to get the raw `Response` along with the parsed data.
175 | Unlike `.asResponse()` this method consumes the body, returning once it is parsed.
176 | 
177 | <!-- prettier-ignore -->
178 | ```ts
179 | const client = new DodoPayments();
180 | 
181 | const response = await client.checkoutSessions
182 |   .create({ product_cart: [{ product_id: 'product_id', quantity: 0 }] })
183 |   .asResponse();
184 | console.log(response.headers.get('X-My-Header'));
185 | console.log(response.statusText); // access the underlying Response object
186 | 
187 | const { data: checkoutSessionResponse, response: raw } = await client.checkoutSessions
188 |   .create({ product_cart: [{ product_id: 'product_id', quantity: 0 }] })
189 |   .withResponse();
190 | console.log(raw.headers.get('X-My-Header'));
191 | console.log(checkoutSessionResponse.session_id);
192 | ```
193 | 
194 | ### Logging
195 | 
196 | > [!IMPORTANT]
197 | > All log messages are intended for debugging only. The format and content of log messages
198 | > may change between releases.
199 | 
200 | #### Log levels
201 | 
202 | The log level can be configured in two ways:
203 | 
204 | 1. Via the `DODO_PAYMENTS_LOG` environment variable
205 | 2. Using the `logLevel` client option (overrides the environment variable if set)
206 | 
207 | ```ts
208 | import DodoPayments from 'dodopayments';
209 | 
210 | const client = new DodoPayments({
211 |   logLevel: 'debug', // Show all log messages
212 | });
213 | ```
214 | 
215 | Available log levels, from most to least verbose:
216 | 
217 | - `'debug'` - Show debug messages, info, warnings, and errors
218 | - `'info'` - Show info messages, warnings, and errors
219 | - `'warn'` - Show warnings and errors (default)
220 | - `'error'` - Show only errors
221 | - `'off'` - Disable all logging
222 | 
223 | At the `'debug'` level, all HTTP requests and responses are logged, including headers and bodies.
224 | Some authentication-related headers are redacted, but sensitive data in request and response bodies
225 | may still be visible.
226 | 
227 | #### Custom logger
228 | 
229 | By default, this library logs to `globalThis.console`. You can also provide a custom logger.
230 | Most logging libraries are supported, including [pino](https://www.npmjs.com/package/pino), [winston](https://www.npmjs.com/package/winston), [bunyan](https://www.npmjs.com/package/bunyan), [consola](https://www.npmjs.com/package/consola), [signale](https://www.npmjs.com/package/signale), and [@std/log](https://jsr.io/@std/log). If your logger doesn't work, please open an issue.
231 | 
232 | When providing a custom logger, the `logLevel` option still controls which messages are emitted, messages
233 | below the configured level will not be sent to your logger.
234 | 
235 | ```ts
236 | import DodoPayments from 'dodopayments';
237 | import pino from 'pino';
238 | 
239 | const logger = pino();
240 | 
241 | const client = new DodoPayments({
242 |   logger: logger.child({ name: 'DodoPayments' }),
243 |   logLevel: 'debug', // Send all messages to pino, allowing it to filter
244 | });
245 | ```
246 | 
247 | ### Making custom/undocumented requests
248 | 
249 | This library is typed for convenient access to the documented API. If you need to access undocumented
250 | endpoints, params, or response properties, the library can still be used.
251 | 
252 | #### Undocumented endpoints
253 | 
254 | To make requests to undocumented endpoints, you can use `client.get`, `client.post`, and other HTTP verbs.
255 | Options on the client, such as retries, will be respected when making these requests.
256 | 
257 | ```ts
258 | await client.post('/some/path', {
259 |   body: { some_prop: 'foo' },
260 |   query: { some_query_arg: 'bar' },
261 | });
262 | ```
263 | 
264 | #### Undocumented request params
265 | 
266 | To make requests using undocumented parameters, you may use `// @ts-expect-error` on the undocumented
267 | parameter. This library doesn't validate at runtime that the request matches the type, so any extra values you
268 | send will be sent as-is.
269 | 
270 | ```ts
271 | client.checkoutSessions.create({
272 |   // ...
273 |   // @ts-expect-error baz is not yet public
274 |   baz: 'undocumented option',
275 | });
276 | ```
277 | 
278 | For requests with the `GET` verb, any extra params will be in the query, all other requests will send the
279 | extra param in the body.
280 | 
281 | If you want to explicitly send an extra argument, you can do so with the `query`, `body`, and `headers` request
282 | options.
283 | 
284 | #### Undocumented response properties
285 | 
286 | To access undocumented response properties, you may access the response object with `// @ts-expect-error` on
287 | the response object, or cast the response object to the requisite type. Like the request params, we do not
288 | validate or strip extra properties from the response from the API.
289 | 
290 | ### Customizing the fetch client
291 | 
292 | By default, this library expects a global `fetch` function is defined.
293 | 
294 | If you want to use a different `fetch` function, you can either polyfill the global:
295 | 
296 | ```ts
297 | import fetch from 'my-fetch';
298 | 
299 | globalThis.fetch = fetch;
300 | ```
301 | 
302 | Or pass it to the client:
303 | 
304 | ```ts
305 | import DodoPayments from 'dodopayments';
306 | import fetch from 'my-fetch';
307 | 
308 | const client = new DodoPayments({ fetch });
309 | ```
310 | 
311 | ### Fetch options
312 | 
313 | If you want to set custom `fetch` options without overriding the `fetch` function, you can provide a `fetchOptions` object when instantiating the client or making a request. (Request-specific options override client options.)
314 | 
315 | ```ts
316 | import DodoPayments from 'dodopayments';
317 | 
318 | const client = new DodoPayments({
319 |   fetchOptions: {
320 |     // `RequestInit` options
321 |   },
322 | });
323 | ```
324 | 
325 | #### Configuring proxies
326 | 
327 | To modify proxy behavior, you can provide custom `fetchOptions` that add runtime-specific proxy
328 | options to requests:
329 | 
330 | <img src="https://raw.githubusercontent.com/stainless-api/sdk-assets/refs/heads/main/node.svg" align="top" width="18" height="21"> **Node** <sup>[[docs](https://github.com/nodejs/undici/blob/main/docs/docs/api/ProxyAgent.md#example---proxyagent-with-fetch)]</sup>
331 | 
332 | ```ts
333 | import DodoPayments from 'dodopayments';
334 | import * as undici from 'undici';
335 | 
336 | const proxyAgent = new undici.ProxyAgent('http://localhost:8888');
337 | const client = new DodoPayments({
338 |   fetchOptions: {
339 |     dispatcher: proxyAgent,
340 |   },
341 | });
342 | ```
343 | 
344 | <img src="https://raw.githubusercontent.com/stainless-api/sdk-assets/refs/heads/main/bun.svg" align="top" width="18" height="21"> **Bun** <sup>[[docs](https://bun.sh/guides/http/proxy)]</sup>
345 | 
346 | ```ts
347 | import DodoPayments from 'dodopayments';
348 | 
349 | const client = new DodoPayments({
350 |   fetchOptions: {
351 |     proxy: 'http://localhost:8888',
352 |   },
353 | });
354 | ```
355 | 
356 | <img src="https://raw.githubusercontent.com/stainless-api/sdk-assets/refs/heads/main/deno.svg" align="top" width="18" height="21"> **Deno** <sup>[[docs](https://docs.deno.com/api/deno/~/Deno.createHttpClient)]</sup>
357 | 
358 | ```ts
359 | import DodoPayments from 'npm:dodopayments';
360 | 
361 | const httpClient = Deno.createHttpClient({ proxy: { url: 'http://localhost:8888' } });
362 | const client = new DodoPayments({
363 |   fetchOptions: {
364 |     client: httpClient,
365 |   },
366 | });
367 | ```
368 | 
369 | ## Frequently Asked Questions
370 | 
371 | ## Semantic versioning
372 | 
373 | This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:
374 | 
375 | 1. Changes that only affect static types, without breaking runtime behavior.
376 | 2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_
377 | 3. Changes that we do not expect to impact the vast majority of users in practice.
378 | 
379 | We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
380 | 
381 | We are keen for your feedback; please open an [issue](https://www.github.com/dodopayments/dodopayments-typescript/issues) with questions, bugs, or suggestions.
382 | 
383 | ## Requirements
384 | 
385 | TypeScript >= 4.9 is supported.
386 | 
387 | The following runtimes are supported:
388 | 
389 | - Web browsers (Up-to-date Chrome, Firefox, Safari, Edge, and more)
390 | - Node.js 20 LTS or later ([non-EOL](https://endoflife.date/nodejs)) versions.
391 | - Deno v1.28.0 or higher.
392 | - Bun 1.0 or later.
393 | - Cloudflare Workers.
394 | - Vercel Edge Runtime.
395 | - Jest 28 or greater with the `"node"` environment (`"jsdom"` is not supported at this time).
396 | - Nitro v2.6 or greater.
397 | 
398 | Note that React Native is not supported at this time.
399 | 
400 | If you are interested in other runtime environments, please open or upvote an issue on GitHub.
401 | 
402 | ## Contributing
403 | 
404 | See [the contributing documentation](./CONTRIBUTING.md).
405 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Dodo Payments TypeScript MCP Server
  2 | 
  3 | It is generated with [Stainless](https://www.stainless.com/).
  4 | 
  5 | ## Installation
  6 | 
  7 | ### Direct invocation
  8 | 
  9 | You can run the MCP Server directly via `npx`:
 10 | 
 11 | ```sh
 12 | export DODO_PAYMENTS_API_KEY="My Bearer Token"
 13 | export DODO_PAYMENTS_WEBHOOK_KEY="My Webhook Key"
 14 | export DODO_PAYMENTS_ENVIRONMENT="live_mode"
 15 | npx -y dodopayments-mcp@latest
 16 | ```
 17 | 
 18 | ### Via MCP Client
 19 | 
 20 | There is a partial list of existing clients at [modelcontextprotocol.io](https://modelcontextprotocol.io/clients). If you already
 21 | have a client, consult their documentation to install the MCP server.
 22 | 
 23 | For clients with a configuration JSON, it might look something like this:
 24 | 
 25 | ```json
 26 | {
 27 |   "mcpServers": {
 28 |     "dodopayments_api": {
 29 |       "command": "npx",
 30 |       "args": ["-y", "dodopayments-mcp", "--client=claude", "--tools=dynamic"],
 31 |       "env": {
 32 |         "DODO_PAYMENTS_API_KEY": "My Bearer Token",
 33 |         "DODO_PAYMENTS_WEBHOOK_KEY": "My Webhook Key",
 34 |         "DODO_PAYMENTS_ENVIRONMENT": "live_mode"
 35 |       }
 36 |     }
 37 |   }
 38 | }
 39 | ```
 40 | 
 41 | ## Exposing endpoints to your MCP Client
 42 | 
 43 | There are two ways to expose endpoints as tools in the MCP server:
 44 | 
 45 | 1. Exposing one tool per endpoint, and filtering as necessary
 46 | 2. Exposing a set of tools to dynamically discover and invoke endpoints from the API
 47 | 
 48 | ### Filtering endpoints and tools
 49 | 
 50 | You can run the package on the command line to discover and filter the set of tools that are exposed by the
 51 | MCP Server. This can be helpful for large APIs where including all endpoints at once is too much for your AI's
 52 | context window.
 53 | 
 54 | You can filter by multiple aspects:
 55 | 
 56 | - `--tool` includes a specific tool by name
 57 | - `--resource` includes all tools under a specific resource, and can have wildcards, e.g. `my.resource*`
 58 | - `--operation` includes just read (get/list) or just write operations
 59 | 
 60 | ### Dynamic tools
 61 | 
 62 | If you specify `--tools=dynamic` to the MCP server, instead of exposing one tool per endpoint in the API, it will
 63 | expose the following tools:
 64 | 
 65 | 1. `list_api_endpoints` - Discovers available endpoints, with optional filtering by search query
 66 | 2. `get_api_endpoint_schema` - Gets detailed schema information for a specific endpoint
 67 | 3. `invoke_api_endpoint` - Executes any endpoint with the appropriate parameters
 68 | 
 69 | This allows you to have the full set of API endpoints available to your MCP Client, while not requiring that all
 70 | of their schemas be loaded into context at once. Instead, the LLM will automatically use these tools together to
 71 | search for, look up, and invoke endpoints dynamically. However, due to the indirect nature of the schemas, it
 72 | can struggle to provide the correct properties a bit more than when tools are imported explicitly. Therefore,
 73 | you can opt-in to explicit tools, the dynamic tools, or both.
 74 | 
 75 | See more information with `--help`.
 76 | 
 77 | All of these command-line options can be repeated, combined together, and have corresponding exclusion versions (e.g. `--no-tool`).
 78 | 
 79 | Use `--list` to see the list of available tools, or see below.
 80 | 
 81 | ### Specifying the MCP Client
 82 | 
 83 | Different clients have varying abilities to handle arbitrary tools and schemas.
 84 | 
 85 | You can specify the client you are using with the `--client` argument, and the MCP server will automatically
 86 | serve tools and schemas that are more compatible with that client.
 87 | 
 88 | - `--client=<type>`: Set all capabilities based on a known MCP client
 89 | 
 90 |   - Valid values: `openai-agents`, `claude`, `claude-code`, `cursor`
 91 |   - Example: `--client=cursor`
 92 | 
 93 | Additionally, if you have a client not on the above list, or the client has gotten better
 94 | over time, you can manually enable or disable certain capabilities:
 95 | 
 96 | - `--capability=<name>`: Specify individual client capabilities
 97 |   - Available capabilities:
 98 |     - `top-level-unions`: Enable support for top-level unions in tool schemas
 99 |     - `valid-json`: Enable JSON string parsing for arguments
100 |     - `refs`: Enable support for $ref pointers in schemas
101 |     - `unions`: Enable support for union types (anyOf) in schemas
102 |     - `formats`: Enable support for format validations in schemas (e.g. date-time, email)
103 |     - `tool-name-length=N`: Set maximum tool name length to N characters
104 |   - Example: `--capability=top-level-unions --capability=tool-name-length=40`
105 |   - Example: `--capability=top-level-unions,tool-name-length=40`
106 | 
107 | ### Examples
108 | 
109 | 1. Filter for read operations on cards:
110 | 
111 | ```bash
112 | --resource=cards --operation=read
113 | ```
114 | 
115 | 2. Exclude specific tools while including others:
116 | 
117 | ```bash
118 | --resource=cards --no-tool=create_cards
119 | ```
120 | 
121 | 3. Configure for Cursor client with custom max tool name length:
122 | 
123 | ```bash
124 | --client=cursor --capability=tool-name-length=40
125 | ```
126 | 
127 | 4. Complex filtering with multiple criteria:
128 | 
129 | ```bash
130 | --resource=cards,accounts --operation=read --tag=kyc --no-tool=create_cards
131 | ```
132 | 
133 | ## Running remotely
134 | 
135 | Launching the client with `--transport=http` launches the server as a remote server using Streamable HTTP transport. The `--port` setting can choose the port it will run on, and the `--socket` setting allows it to run on a Unix socket.
136 | 
137 | Authorization can be provided via the `Authorization` header using the Bearer scheme.
138 | 
139 | Additionally, authorization can be provided via the following headers:
140 | | Header | Equivalent client option | Security scheme |
141 | | ------------------------- | ------------------------ | --------------- |
142 | | `x-dodo-payments-api-key` | `bearerToken` | API_KEY |
143 | 
144 | A configuration JSON for this server might look like this, assuming the server is hosted at `http://localhost:3000`:
145 | 
146 | ```json
147 | {
148 |   "mcpServers": {
149 |     "dodopayments_api": {
150 |       "url": "http://localhost:3000",
151 |       "headers": {
152 |         "Authorization": "Bearer <auth value>"
153 |       }
154 |     }
155 |   }
156 | }
157 | ```
158 | 
159 | The command-line arguments for filtering tools and specifying clients can also be used as query parameters in the URL.
160 | For example, to exclude specific tools while including others, use the URL:
161 | 
162 | ```
163 | http://localhost:3000?resource=cards&resource=accounts&no_tool=create_cards
164 | ```
165 | 
166 | Or, to configure for the Cursor client, with a custom max tool name length, use the URL:
167 | 
168 | ```
169 | http://localhost:3000?client=cursor&capability=tool-name-length%3D40
170 | ```
171 | 
172 | ## Importing the tools and server individually
173 | 
174 | ```js
175 | // Import the server, generated endpoints, or the init function
176 | import { server, endpoints, init } from "dodopayments-mcp/server";
177 | 
178 | // import a specific tool
179 | import createCheckoutSessions from "dodopayments-mcp/tools/checkout-sessions/create-checkout-sessions";
180 | 
181 | // initialize the server and all endpoints
182 | init({ server, endpoints });
183 | 
184 | // manually start server
185 | const transport = new StdioServerTransport();
186 | await server.connect(transport);
187 | 
188 | // or initialize your own server with specific tools
189 | const myServer = new McpServer(...);
190 | 
191 | // define your own endpoint
192 | const myCustomEndpoint = {
193 |   tool: {
194 |     name: 'my_custom_tool',
195 |     description: 'My custom tool',
196 |     inputSchema: zodToJsonSchema(z.object({ a_property: z.string() })),
197 |   },
198 |   handler: async (client: client, args: any) => {
199 |     return { myResponse: 'Hello world!' };
200 |   })
201 | };
202 | 
203 | // initialize the server with your custom endpoints
204 | init({ server: myServer, endpoints: [createCheckoutSessions, myCustomEndpoint] });
205 | ```
206 | 
207 | ## Available Tools
208 | 
209 | The following tools are available in this MCP server.
210 | 
211 | ### Resource `checkout_sessions`:
212 | 
213 | - `create_checkout_sessions` (`write`):
214 | 
215 | ### Resource `payments`:
216 | 
217 | - `create_payments` (`write`):
218 | - `retrieve_payments` (`read`):
219 | - `list_payments` (`read`):
220 | - `retrieve_line_items_payments` (`read`):
221 | 
222 | ### Resource `subscriptions`:
223 | 
224 | - `create_subscriptions` (`write`):
225 | - `retrieve_subscriptions` (`read`):
226 | - `update_subscriptions` (`write`):
227 | - `list_subscriptions` (`read`):
228 | - `change_plan_subscriptions` (`write`):
229 | - `charge_subscriptions` (`write`):
230 | - `retrieve_usage_history_subscriptions` (`read`): Get detailed usage history for a subscription that includes usage-based billing (metered components).
231 |   This endpoint provides insights into customer usage patterns and billing calculations over time.
232 | 
233 |   ## What You'll Get:
234 | 
235 |   - **Billing periods**: Each item represents a billing cycle with start and end dates
236 |   - **Meter usage**: Detailed breakdown of usage for each meter configured on the subscription
237 |   - **Usage calculations**: Total units consumed, free threshold units, and chargeable units
238 |   - **Historical tracking**: Complete audit trail of usage-based charges
239 | 
240 |   ## Use Cases:
241 | 
242 |   - **Customer support**: Investigate billing questions and usage discrepancies
243 |   - **Usage analytics**: Analyze customer consumption patterns over time
244 |   - **Billing transparency**: Provide customers with detailed usage breakdowns
245 |   - **Revenue optimization**: Identify usage trends to optimize pricing strategies
246 | 
247 |   ## Filtering Options:
248 | 
249 |   - **Date range filtering**: Get usage history for specific time periods
250 |   - **Meter-specific filtering**: Focus on usage for a particular meter
251 |   - **Pagination**: Navigate through large usage histories efficiently
252 | 
253 |   ## Important Notes:
254 | 
255 |   - Only returns data for subscriptions with usage-based (metered) components
256 |   - Usage history is organized by billing periods (subscription cycles)
257 |   - Free threshold units are calculated and displayed separately from chargeable units
258 |   - Historical data is preserved even if meter configurations change
259 | 
260 |   ## Example Query Patterns:
261 | 
262 |   - Get last 3 months: `?start_date=2024-01-01T00:00:00Z&end_date=2024-03-31T23:59:59Z`
263 |   - Filter by meter: `?meter_id=mtr_api_requests`
264 |   - Paginate results: `?page_size=20&page_number=1`
265 |   - Recent usage: `?start_date=2024-03-01T00:00:00Z` (from March 1st to now)
266 | 
267 | ### Resource `invoices.payments`:
268 | 
269 | - `retrieve_invoices_payments` (`read`):
270 | - `retrieve_refund_invoices_payments` (`read`):
271 | 
272 | ### Resource `licenses`:
273 | 
274 | - `activate_licenses` (`write`):
275 | - `deactivate_licenses` (`write`):
276 | - `validate_licenses` (`write`):
277 | 
278 | ### Resource `license_keys`:
279 | 
280 | - `retrieve_license_keys` (`read`):
281 | - `update_license_keys` (`write`):
282 | - `list_license_keys` (`read`):
283 | 
284 | ### Resource `license_key_instances`:
285 | 
286 | - `retrieve_license_key_instances` (`read`):
287 | - `update_license_key_instances` (`write`):
288 | - `list_license_key_instances` (`read`):
289 | 
290 | ### Resource `customers`:
291 | 
292 | - `create_customers` (`write`):
293 | - `retrieve_customers` (`read`):
294 | - `update_customers` (`write`):
295 | - `list_customers` (`read`):
296 | 
297 | ### Resource `customers.customer_portal`:
298 | 
299 | - `create_customers_customer_portal` (`write`):
300 | 
301 | ### Resource `customers.wallets`:
302 | 
303 | - `list_customers_wallets` (`read`):
304 | 
305 | ### Resource `customers.wallets.ledger_entries`:
306 | 
307 | - `create_wallets_customers_ledger_entries` (`write`):
308 | - `list_wallets_customers_ledger_entries` (`read`):
309 | 
310 | ### Resource `refunds`:
311 | 
312 | - `create_refunds` (`write`):
313 | - `retrieve_refunds` (`read`):
314 | - `list_refunds` (`read`):
315 | 
316 | ### Resource `disputes`:
317 | 
318 | - `retrieve_disputes` (`read`):
319 | - `list_disputes` (`read`):
320 | 
321 | ### Resource `payouts`:
322 | 
323 | - `list_payouts` (`read`):
324 | 
325 | ### Resource `products`:
326 | 
327 | - `create_products` (`write`):
328 | - `retrieve_products` (`read`):
329 | - `update_products` (`write`):
330 | - `list_products` (`read`):
331 | - `archive_products` (`write`):
332 | - `unarchive_products` (`write`):
333 | - `update_files_products` (`write`):
334 | 
335 | ### Resource `products.images`:
336 | 
337 | - `update_products_images` (`write`):
338 | 
339 | ### Resource `misc`:
340 | 
341 | - `list_supported_countries_misc` (`read`):
342 | 
343 | ### Resource `discounts`:
344 | 
345 | - `create_discounts` (`write`): POST /discounts
346 |   If `code` is omitted or empty, a random 16-char uppercase code is generated.
347 | - `retrieve_discounts` (`read`): GET /discounts/{discount_id}
348 | - `update_discounts` (`write`): PATCH /discounts/{discount_id}
349 | - `list_discounts` (`read`): GET /discounts
350 | - `delete_discounts` (`write`): DELETE /discounts/{discount_id}
351 | 
352 | ### Resource `addons`:
353 | 
354 | - `create_addons` (`write`):
355 | - `retrieve_addons` (`read`):
356 | - `update_addons` (`write`):
357 | - `list_addons` (`read`):
358 | - `update_images_addons` (`write`):
359 | 
360 | ### Resource `brands`:
361 | 
362 | - `create_brands` (`write`):
363 | - `retrieve_brands` (`read`): Thin handler just calls `get_brand` and wraps in `Json(...)`
364 | - `update_brands` (`write`):
365 | - `list_brands` (`read`):
366 | - `update_images_brands` (`write`):
367 | 
368 | ### Resource `webhooks`:
369 | 
370 | - `create_webhooks` (`write`): Create a new webhook
371 | - `retrieve_webhooks` (`read`): Get a webhook by id
372 | - `update_webhooks` (`write`): Patch a webhook by id
373 | - `list_webhooks` (`read`): List all webhooks
374 | - `delete_webhooks` (`write`): Delete a webhook by id
375 | - `retrieve_secret_webhooks` (`read`): Get webhook secret by id
376 | 
377 | ### Resource `webhooks.headers`:
378 | 
379 | - `retrieve_webhooks_headers` (`read`): Get a webhook by id
380 | - `update_webhooks_headers` (`write`): Patch a webhook by id
381 | 
382 | ### Resource `usage_events`:
383 | 
384 | - `retrieve_usage_events` (`read`): Fetch detailed information about a single event using its unique event ID. This endpoint is useful for:
385 | 
386 |   - Debugging specific event ingestion issues
387 |   - Retrieving event details for customer support
388 |   - Validating that events were processed correctly
389 |   - Getting the complete metadata for an event
390 | 
391 |   ## Event ID Format:
392 | 
393 |   The event ID should be the same value that was provided during event ingestion via the `/events/ingest` endpoint.
394 |   Event IDs are case-sensitive and must match exactly.
395 | 
396 |   ## Response Details:
397 | 
398 |   The response includes all event data including:
399 | 
400 |   - Complete metadata key-value pairs
401 |   - Original timestamp (preserved from ingestion)
402 |   - Customer and business association
403 |   - Event name and processing information
404 | 
405 |   ## Example Usage:
406 | 
407 |   ```text
408 |   GET /events/api_call_12345
409 |   ```
410 | 
411 | - `list_usage_events` (`read`): Fetch events from your account with powerful filtering capabilities. This endpoint is ideal for:
412 | 
413 |   - Debugging event ingestion issues
414 |   - Analyzing customer usage patterns
415 |   - Building custom analytics dashboards
416 |   - Auditing billing-related events
417 | 
418 |   ## Filtering Options:
419 | 
420 |   - **Customer filtering**: Filter by specific customer ID
421 |   - **Event name filtering**: Filter by event type/name
422 |   - **Meter-based filtering**: Use a meter ID to apply the meter's event name and filter criteria automatically
423 |   - **Time range filtering**: Filter events within a specific date range
424 |   - **Pagination**: Navigate through large result sets
425 | 
426 |   ## Meter Integration:
427 | 
428 |   When using `meter_id`, the endpoint automatically applies:
429 | 
430 |   - The meter's configured `event_name` filter
431 |   - The meter's custom filter criteria (if any)
432 |   - If you also provide `event_name`, it must match the meter's event name
433 | 
434 |   ## Example Queries:
435 | 
436 |   - Get all events for a customer: `?customer_id=cus_abc123`
437 |   - Get API request events: `?event_name=api_request`
438 |   - Get events from last 24 hours: `?start=2024-01-14T10:30:00Z&end=2024-01-15T10:30:00Z`
439 |   - Get events with meter filtering: `?meter_id=mtr_xyz789`
440 |   - Paginate results: `?page_size=50&page_number=2`
441 | 
442 | - `ingest_usage_events` (`write`): This endpoint allows you to ingest custom events that can be used for:
443 | 
444 |   - Usage-based billing and metering
445 |   - Analytics and reporting
446 |   - Customer behavior tracking
447 | 
448 |   ## Important Notes:
449 | 
450 |   - **Duplicate Prevention**:
451 |     - Duplicate `event_id` values within the same request are rejected (entire request fails)
452 |     - Subsequent requests with existing `event_id` values are ignored (idempotent behavior)
453 |   - **Rate Limiting**: Maximum 1000 events per request
454 |   - **Time Validation**: Events with timestamps older than 1 hour or more than 5 minutes in the future will be rejected
455 |   - **Metadata Limits**: Maximum 50 key-value pairs per event, keys max 100 chars, values max 500 chars
456 | 
457 |   ## Example Usage:
458 | 
459 |   ```json
460 |   {
461 |     "events": [
462 |       {
463 |         "event_id": "api_call_12345",
464 |         "customer_id": "cus_abc123",
465 |         "event_name": "api_request",
466 |         "timestamp": "2024-01-15T10:30:00Z",
467 |         "metadata": {
468 |           "endpoint": "/api/v1/users",
469 |           "method": "GET",
470 |           "tokens_used": "150"
471 |         }
472 |       }
473 |     ]
474 |   }
475 |   ```
476 | 
477 | ### Resource `meters`:
478 | 
479 | - `create_meters` (`write`):
480 | - `retrieve_meters` (`read`):
481 | - `list_meters` (`read`):
482 | - `archive_meters` (`write`):
483 | - `unarchive_meters` (`write`):
484 | 
```

--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Security Policy
 2 | 
 3 | ## Reporting Security Issues
 4 | 
 5 | This SDK is generated by [Stainless Software Inc](http://stainless.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken.
 6 | 
 7 | To report a security issue, please contact the Stainless team at [email protected].
 8 | 
 9 | ## Responsible Disclosure
10 | 
11 | We appreciate the efforts of security researchers and individuals who help us maintain the security of
12 | SDKs we generate. If you believe you have found a security vulnerability, please adhere to responsible
13 | disclosure practices by allowing us a reasonable amount of time to investigate and address the issue
14 | before making any information public.
15 | 
16 | ## Reporting Non-SDK Related Security Issues
17 | 
18 | If you encounter security issues that are not directly related to SDKs but pertain to the services
19 | or products provided by Dodo Payments, please follow the respective company's security reporting guidelines.
20 | 
21 | ### Dodo Payments Terms and Policies
22 | 
23 | Please contact [email protected] for any questions or concerns regarding the security of our services.
24 | 
25 | ---
26 | 
27 | Thank you for helping us keep the SDKs and systems they interact with secure.
28 | 
```

--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------

```markdown
  1 | ## Setting up the environment
  2 | 
  3 | This repository uses [`yarn@v1`](https://classic.yarnpkg.com/lang/en/docs/install).
  4 | Other package managers may work but are not officially supported for development.
  5 | 
  6 | To set up the repository, run:
  7 | 
  8 | ```sh
  9 | $ yarn
 10 | $ yarn build
 11 | ```
 12 | 
 13 | This will install all the required dependencies and build output files to `dist/`.
 14 | 
 15 | ## Modifying/Adding code
 16 | 
 17 | Most of the SDK is generated code. Modifications to code will be persisted between generations, but may
 18 | result in merge conflicts between manual patches and changes from the generator. The generator will never
 19 | modify the contents of the `src/lib/` and `examples/` directories.
 20 | 
 21 | ## Adding and running examples
 22 | 
 23 | All files in the `examples/` directory are not modified by the generator and can be freely edited or added to.
 24 | 
 25 | ```ts
 26 | // add an example to examples/<your-example>.ts
 27 | 
 28 | #!/usr/bin/env -S npm run tsn -T
 29 | …
 30 | ```
 31 | 
 32 | ```sh
 33 | $ chmod +x examples/<your-example>.ts
 34 | # run the example against your api
 35 | $ yarn tsn -T examples/<your-example>.ts
 36 | ```
 37 | 
 38 | ## Using the repository from source
 39 | 
 40 | If you’d like to use the repository from source, you can either install from git or link to a cloned repository:
 41 | 
 42 | To install via git:
 43 | 
 44 | ```sh
 45 | $ npm install git+ssh://[email protected]:dodopayments/dodopayments-typescript.git
 46 | ```
 47 | 
 48 | Alternatively, to link a local copy of the repo:
 49 | 
 50 | ```sh
 51 | # Clone
 52 | $ git clone https://www.github.com/dodopayments/dodopayments-typescript
 53 | $ cd dodopayments-typescript
 54 | 
 55 | # With yarn
 56 | $ yarn link
 57 | $ cd ../my-package
 58 | $ yarn link dodopayments
 59 | 
 60 | # With pnpm
 61 | $ pnpm link --global
 62 | $ cd ../my-package
 63 | $ pnpm link -—global dodopayments
 64 | ```
 65 | 
 66 | ## Running tests
 67 | 
 68 | Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests.
 69 | 
 70 | ```sh
 71 | $ npx prism mock path/to/your/openapi.yml
 72 | ```
 73 | 
 74 | ```sh
 75 | $ yarn run test
 76 | ```
 77 | 
 78 | ## Linting and formatting
 79 | 
 80 | This repository uses [prettier](https://www.npmjs.com/package/prettier) and
 81 | [eslint](https://www.npmjs.com/package/eslint) to format the code in the repository.
 82 | 
 83 | To lint:
 84 | 
 85 | ```sh
 86 | $ yarn lint
 87 | ```
 88 | 
 89 | To format and fix all lint issues automatically:
 90 | 
 91 | ```sh
 92 | $ yarn fix
 93 | ```
 94 | 
 95 | ## Publishing and releases
 96 | 
 97 | Changes made to this repository via the automated release PR pipeline should publish to npm automatically. If
 98 | the changes aren't made through the automated pipeline, you may want to make releases manually.
 99 | 
100 | ### Publish with a GitHub workflow
101 | 
102 | You can release to package managers by using [the `Publish NPM` GitHub action](https://www.github.com/dodopayments/dodopayments-typescript/actions/workflows/publish-npm.yml). This requires a setup organization or repository secret to be set up.
103 | 
104 | ### Publish manually
105 | 
106 | If you need to manually release a package, you can run the `bin/publish-npm` script with an `NPM_TOKEN` set on
107 | the environment.
108 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools.ts:
--------------------------------------------------------------------------------

```typescript
1 | export * from './tools/index';
2 | 
```

--------------------------------------------------------------------------------
/src/resources.ts:
--------------------------------------------------------------------------------

```typescript
1 | export * from './resources/index';
2 | 
```

--------------------------------------------------------------------------------
/src/version.ts:
--------------------------------------------------------------------------------

```typescript
1 | export const VERSION = '2.3.1'; // x-release-please-version
2 | 
```

--------------------------------------------------------------------------------
/src/error.ts:
--------------------------------------------------------------------------------

```typescript
1 | /** @deprecated Import from ./core/error instead */
2 | export * from './core/error';
3 | 
```

--------------------------------------------------------------------------------
/src/uploads.ts:
--------------------------------------------------------------------------------

```typescript
1 | /** @deprecated Import from ./core/uploads instead */
2 | export * from './core/uploads';
3 | 
```

--------------------------------------------------------------------------------
/src/resource.ts:
--------------------------------------------------------------------------------

```typescript
1 | /** @deprecated Import from ./core/resource instead */
2 | export * from './core/resource';
3 | 
```

--------------------------------------------------------------------------------
/src/pagination.ts:
--------------------------------------------------------------------------------

```typescript
1 | /** @deprecated Import from ./core/pagination instead */
2 | export * from './core/pagination';
3 | 
```

--------------------------------------------------------------------------------
/src/api-promise.ts:
--------------------------------------------------------------------------------

```typescript
1 | /** @deprecated Import from ./core/api-promise instead */
2 | export * from './core/api-promise';
3 | 
```

--------------------------------------------------------------------------------
/src/core/uploads.ts:
--------------------------------------------------------------------------------

```typescript
1 | export { type Uploadable } from '../internal/uploads';
2 | export { toFile, type ToFileInput } from '../internal/to-file';
3 | 
```

--------------------------------------------------------------------------------
/src/resources/customers/wallets.ts:
--------------------------------------------------------------------------------

```typescript
1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2 | 
3 | export * from './wallets/index';
4 | 
```

--------------------------------------------------------------------------------
/src/resources/invoices.ts:
--------------------------------------------------------------------------------

```typescript
1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2 | 
3 | export * from './invoices/index';
4 | 
```

--------------------------------------------------------------------------------
/src/resources/products.ts:
--------------------------------------------------------------------------------

```typescript
1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2 | 
3 | export * from './products/index';
4 | 
```

--------------------------------------------------------------------------------
/src/resources/webhooks.ts:
--------------------------------------------------------------------------------

```typescript
1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2 | 
3 | export * from './webhooks/index';
4 | 
```

--------------------------------------------------------------------------------
/src/resources/customers.ts:
--------------------------------------------------------------------------------

```typescript
1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2 | 
3 | export * from './customers/index';
4 | 
```

--------------------------------------------------------------------------------
/bin/migration-config.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "pkg": "dodopayments",
3 |   "githubRepo": "https://github.com/dodopayments/dodopayments-typescript",
4 |   "clientClass": "DodoPayments",
5 |   "methods": []
6 | }
7 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/tsc-multi.json:
--------------------------------------------------------------------------------

```json
1 | {
2 |   "targets": [
3 |     { "extname": ".js", "module": "commonjs" },
4 |     { "extname": ".mjs", "module": "esnext" }
5 |   ],
6 |   "projects": ["tsconfig.build.json"]
7 | }
8 | 
```

--------------------------------------------------------------------------------
/src/resources/invoices/index.ts:
--------------------------------------------------------------------------------

```typescript
1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2 | 
3 | export { Invoices } from './invoices';
4 | export { Payments } from './payments';
5 | 
```

--------------------------------------------------------------------------------
/src/internal/utils/sleep.ts:
--------------------------------------------------------------------------------

```typescript
1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2 | 
3 | export const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));
4 | 
```

--------------------------------------------------------------------------------
/src/internal/utils.ts:
--------------------------------------------------------------------------------

```typescript
1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
2 | 
3 | export * from './utils/values';
4 | export * from './utils/base64';
5 | export * from './utils/env';
6 | export * from './utils/log';
7 | export * from './utils/uuid';
8 | export * from './utils/sleep';
9 | 
```

--------------------------------------------------------------------------------
/tsc-multi.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "targets": [
 3 |     {
 4 |       "extname": ".js",
 5 |       "module": "commonjs",
 6 |       "shareHelpers": "internal/tslib.js"
 7 |     },
 8 |     {
 9 |       "extname": ".mjs",
10 |       "module": "esnext",
11 |       "shareHelpers": "internal/tslib.mjs"
12 |     }
13 |   ],
14 |   "projects": ["tsconfig.build.json"]
15 | }
16 | 
```

--------------------------------------------------------------------------------
/src/core/resource.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import type { DodoPayments } from '../client';
 4 | 
 5 | export abstract class APIResource {
 6 |   protected _client: DodoPayments;
 7 | 
 8 |   constructor(client: DodoPayments) {
 9 |     this._client = client;
10 |   }
11 | }
12 | 
```

--------------------------------------------------------------------------------
/tsconfig.deno.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "extends": "./tsconfig.json",
 3 |   "include": ["dist-deno"],
 4 |   "exclude": [],
 5 |   "compilerOptions": {
 6 |     "rootDir": "./dist-deno",
 7 |     "lib": ["es2020", "DOM"],
 8 |     "noEmit": true,
 9 |     "declaration": true,
10 |     "declarationMap": true,
11 |     "outDir": "dist-deno",
12 |     "pretty": true,
13 |     "sourceMap": true
14 |   }
15 | }
16 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/tsconfig.dist-src.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   // this config is included in the published src directory to prevent TS errors
 3 |   // from appearing when users go to source, and VSCode opens the source .ts file
 4 |   // via declaration maps
 5 |   "include": ["index.ts"],
 6 |   "compilerOptions": {
 7 |     "target": "es2015",
 8 |     "lib": ["DOM"],
 9 |     "moduleResolution": "node"
10 |   }
11 | }
12 | 
```

--------------------------------------------------------------------------------
/tsconfig.dist-src.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   // this config is included in the published src directory to prevent TS errors
 3 |   // from appearing when users go to source, and VSCode opens the source .ts file
 4 |   // via declaration maps
 5 |   "include": ["index.ts"],
 6 |   "compilerOptions": {
 7 |     "target": "ES2015",
 8 |     "lib": ["DOM", "DOM.Iterable", "ES2018"],
 9 |     "moduleResolution": "node"
10 |   }
11 | }
12 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/code-tool-types.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { ClientOptions } from 'dodopayments';
 4 | 
 5 | export type WorkerInput = {
 6 |   opts: ClientOptions;
 7 |   code: string;
 8 | };
 9 | export type WorkerSuccess = {
10 |   result: unknown | null;
11 |   logLines: string[];
12 |   errLines: string[];
13 | };
14 | export type WorkerError = { message: string | undefined };
15 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/filtering.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // @ts-nocheck
 2 | import initJq from 'jq-web';
 3 | 
 4 | export async function maybeFilter(jqFilter: unknown | undefined, response: any): Promise<any> {
 5 |   if (jqFilter && typeof jqFilter === 'string') {
 6 |     return await jq(response, jqFilter);
 7 |   } else {
 8 |     return response;
 9 |   }
10 | }
11 | 
12 | async function jq(json: any, jqFilter: string) {
13 |   return (await initJq).json(json, jqFilter);
14 | }
15 | 
```

--------------------------------------------------------------------------------
/src/resources/customers/wallets/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | export {
 4 |   LedgerEntries,
 5 |   type CustomerWalletTransaction,
 6 |   type LedgerEntryCreateParams,
 7 |   type LedgerEntryListParams,
 8 |   type CustomerWalletTransactionsDefaultPageNumberPagination,
 9 | } from './ledger-entries';
10 | export { Wallets, type CustomerWallet, type WalletListResponse } from './wallets';
11 | 
```

--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "extends": "./tsconfig.json",
 3 |   "include": ["dist/src"],
 4 |   "exclude": [],
 5 |   "compilerOptions": {
 6 |     "rootDir": "./dist/src",
 7 |     "paths": {
 8 |       "dodopayments/*": ["./dist/src/*"],
 9 |       "dodopayments": ["./dist/src/index.ts"]
10 |     },
11 |     "noEmit": false,
12 |     "declaration": true,
13 |     "declarationMap": true,
14 |     "outDir": "dist",
15 |     "pretty": true,
16 |     "sourceMap": true
17 |   }
18 | }
19 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/tsconfig.build.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "extends": "./tsconfig.json",
 3 |   "include": ["dist/src"],
 4 |   "exclude": [],
 5 |   "compilerOptions": {
 6 |     "rootDir": "./dist/src",
 7 |     "paths": {
 8 |       "dodopayments-mcp/*": ["./dist/src/*"],
 9 |       "dodopayments-mcp": ["./dist/src/index.ts"]
10 |     },
11 |     "noEmit": false,
12 |     "declaration": true,
13 |     "declarationMap": true,
14 |     "outDir": "dist",
15 |     "pretty": true,
16 |     "sourceMap": true
17 |   }
18 | }
19 | 
```

--------------------------------------------------------------------------------
/scripts/utils/git-swap.sh:
--------------------------------------------------------------------------------

```bash
 1 | #!/usr/bin/env bash
 2 | set -exuo pipefail
 3 | # the package is published to NPM from ./dist
 4 | # we want the final file structure for git installs to match the npm installs, so we
 5 | 
 6 | # delete everything except ./dist and ./node_modules
 7 | find . -maxdepth 1 -mindepth 1 ! -name 'dist' ! -name 'node_modules' -exec rm -rf '{}' +
 8 | 
 9 | # move everything from ./dist to .
10 | mv dist/* .
11 | 
12 | # delete the now-empty ./dist
13 | rmdir dist
14 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/scripts/postprocess-dist-package-json.cjs:
--------------------------------------------------------------------------------

```
 1 | const fs = require('fs');
 2 | const pkgJson = require('../dist/package.json');
 3 | const parentPkgJson = require('../../../package.json');
 4 | 
 5 | for (const dep in pkgJson.dependencies) {
 6 |   // ensure we point to NPM instead of a local directory
 7 |   if (dep === 'dodopayments') {
 8 |     pkgJson.dependencies[dep] = '^' + parentPkgJson.version;
 9 |   }
10 | }
11 | 
12 | fs.writeFileSync('dist/package.json', JSON.stringify(pkgJson, null, 2));
13 | 
```

--------------------------------------------------------------------------------
/src/resources/invoices/invoices.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { APIResource } from '../../core/resource';
 4 | import * as PaymentsAPI from './payments';
 5 | import { Payments } from './payments';
 6 | 
 7 | export class Invoices extends APIResource {
 8 |   payments: PaymentsAPI.Payments = new PaymentsAPI.Payments(this._client);
 9 | }
10 | 
11 | Invoices.Payments = Payments;
12 | 
13 | export declare namespace Invoices {
14 |   export { Payments as Payments };
15 | }
16 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/stdio.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
 2 | import { initMcpServer, newMcpServer } from './server';
 3 | import { McpOptions } from './options';
 4 | 
 5 | export const launchStdioServer = async (options: McpOptions) => {
 6 |   const server = newMcpServer();
 7 | 
 8 |   initMcpServer({ server, mcpOptions: options });
 9 | 
10 |   const transport = new StdioServerTransport();
11 |   await server.connect(transport);
12 |   console.error('MCP Server running on stdio');
13 | };
14 | 
```

--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------

```json
 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the
 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/debian
 3 | {
 4 |   "name": "Development",
 5 |   "image": "mcr.microsoft.com/devcontainers/typescript-node:latest",
 6 |   "features": {
 7 |     "ghcr.io/devcontainers/features/node:1": {}
 8 |   },
 9 |   "postCreateCommand": "yarn install",
10 |   "customizations": {
11 |     "vscode": {
12 |       "extensions": ["esbenp.prettier-vscode"]
13 |     }
14 |   }
15 | }
16 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/cloudflare-worker/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "es2021",
 4 |     "lib": ["es2021"],
 5 |     "jsx": "react-jsx",
 6 |     "module": "es2022",
 7 |     "moduleResolution": "Bundler",
 8 |     "resolveJsonModule": true,
 9 |     "allowJs": true,
10 |     "checkJs": false,
11 |     "noEmit": true,
12 |     "isolatedModules": true,
13 |     "allowSyntheticDefaultImports": true,
14 |     "forceConsistentCasingInFileNames": true,
15 |     "strict": true,
16 |     "skipLibCheck": true
17 |   },
18 |   "include": ["worker-configuration.d.ts", "src/**/*.ts"]
19 | }
20 | 
```

--------------------------------------------------------------------------------
/scripts/utils/check-is-in-git-install.sh:
--------------------------------------------------------------------------------

```bash
 1 | #!/usr/bin/env bash
 2 | # Check if you happen to call prepare for a repository that's already in node_modules.
 3 | [ "$(basename "$(dirname "$PWD")")" = 'node_modules' ] ||
 4 | # The name of the containing directory that 'npm` uses, which looks like
 5 | # $HOME/.npm/_cacache/git-cloneXXXXXX
 6 | [ "$(basename "$(dirname "$PWD")")" = 'tmp' ] ||
 7 | # The name of the containing directory that 'yarn` uses, which looks like
 8 | # $(yarn cache dir)/.tmp/XXXXX
 9 | [ "$(basename "$(dirname "$PWD")")" = '.tmp' ]
10 | 
```

--------------------------------------------------------------------------------
/src/resources/customers/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | export { CustomerPortal, type CustomerPortalCreateParams } from './customer-portal';
 4 | export {
 5 |   Customers,
 6 |   type Customer,
 7 |   type CustomerPortalSession,
 8 |   type CustomerCreateParams,
 9 |   type CustomerUpdateParams,
10 |   type CustomerListParams,
11 |   type CustomersDefaultPageNumberPagination,
12 | } from './customers';
13 | export { Wallets, type CustomerWallet, type WalletListResponse } from './wallets/index';
14 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/jest.config.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import type { JestConfigWithTsJest } from 'ts-jest';
 2 | 
 3 | const config: JestConfigWithTsJest = {
 4 |   preset: 'ts-jest/presets/default-esm',
 5 |   testEnvironment: 'node',
 6 |   transform: {
 7 |     '^.+\\.(t|j)sx?$': ['@swc/jest', { sourceMaps: 'inline' }],
 8 |   },
 9 |   moduleNameMapper: {
10 |     '^dodopayments-mcp$': '<rootDir>/src/index.ts',
11 |     '^dodopayments-mcp/(.*)$': '<rootDir>/src/$1',
12 |   },
13 |   modulePathIgnorePatterns: ['<rootDir>/dist/'],
14 |   testPathIgnorePatterns: ['scripts'],
15 | };
16 | 
17 | export default config;
18 | 
```

--------------------------------------------------------------------------------
/src/resources/products/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | export { Images, type ImageUpdateResponse, type ImageUpdateParams } from './images';
 4 | export {
 5 |   Products,
 6 |   type AddMeterToPrice,
 7 |   type LicenseKeyDuration,
 8 |   type Price,
 9 |   type Product,
10 |   type ProductListResponse,
11 |   type ProductUpdateFilesResponse,
12 |   type ProductCreateParams,
13 |   type ProductUpdateParams,
14 |   type ProductListParams,
15 |   type ProductUpdateFilesParams,
16 |   type ProductListResponsesDefaultPageNumberPagination,
17 | } from './products';
18 | 
```

--------------------------------------------------------------------------------
/scripts/utils/fix-index-exports.cjs:
--------------------------------------------------------------------------------

```
 1 | const fs = require('fs');
 2 | const path = require('path');
 3 | 
 4 | const indexJs =
 5 |   process.env['DIST_PATH'] ?
 6 |     path.resolve(process.env['DIST_PATH'], 'index.js')
 7 |   : path.resolve(__dirname, '..', '..', 'dist', 'index.js');
 8 | 
 9 | let before = fs.readFileSync(indexJs, 'utf8');
10 | let after = before.replace(
11 |   /^(\s*Object\.defineProperty\s*\(exports,\s*["']__esModule["'].+)$/m,
12 |   `exports = module.exports = function (...args) {
13 |     return new exports.default(...args)
14 |   }
15 |   $1`.replace(/^  /gm, ''),
16 | );
17 | fs.writeFileSync(indexJs, after, 'utf8');
18 | 
```

--------------------------------------------------------------------------------
/src/internal/utils/uuid.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | /**
 4 |  * https://stackoverflow.com/a/2117523
 5 |  */
 6 | export let uuid4 = function () {
 7 |   const { crypto } = globalThis as any;
 8 |   if (crypto?.randomUUID) {
 9 |     uuid4 = crypto.randomUUID.bind(crypto);
10 |     return crypto.randomUUID();
11 |   }
12 |   const u8 = new Uint8Array(1);
13 |   const randomByte = crypto ? () => crypto.getRandomValues(u8)[0]! : () => (Math.random() * 0xff) & 0xff;
14 |   return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, (c) =>
15 |     (+c ^ (randomByte() & (15 >> (+c / 4)))).toString(16),
16 |   );
17 | };
18 | 
```

--------------------------------------------------------------------------------
/jest.config.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import type { JestConfigWithTsJest } from 'ts-jest';
 2 | 
 3 | const config: JestConfigWithTsJest = {
 4 |   preset: 'ts-jest/presets/default-esm',
 5 |   testEnvironment: 'node',
 6 |   transform: {
 7 |     '^.+\\.(t|j)sx?$': ['@swc/jest', { sourceMaps: 'inline' }],
 8 |   },
 9 |   moduleNameMapper: {
10 |     '^dodopayments$': '<rootDir>/src/index.ts',
11 |     '^dodopayments/(.*)$': '<rootDir>/src/$1',
12 |   },
13 |   modulePathIgnorePatterns: [
14 |     '<rootDir>/ecosystem-tests/',
15 |     '<rootDir>/dist/',
16 |     '<rootDir>/deno/',
17 |     '<rootDir>/deno_tests/',
18 |     '<rootDir>/packages/',
19 |   ],
20 |   testPathIgnorePatterns: ['scripts'],
21 | };
22 | 
23 | export default config;
24 | 
```

--------------------------------------------------------------------------------
/src/internal/utils/env.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | /**
 4 |  * Read an environment variable.
 5 |  *
 6 |  * Trims beginning and trailing whitespace.
 7 |  *
 8 |  * Will return undefined if the environment variable doesn't exist or cannot be accessed.
 9 |  */
10 | export const readEnv = (env: string): string | undefined => {
11 |   if (typeof (globalThis as any).process !== 'undefined') {
12 |     return (globalThis as any).process.env?.[env]?.trim() ?? undefined;
13 |   }
14 |   if (typeof (globalThis as any).Deno !== 'undefined') {
15 |     return (globalThis as any).Deno.env?.get?.(env)?.trim();
16 |   }
17 |   return undefined;
18 | };
19 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/cloudflare-worker/biome.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "$schema": "https://biomejs.dev/schemas/1.6.2/schema.json",
 3 |   "organizeImports": {
 4 |     "enabled": true
 5 |   },
 6 |   "files": {
 7 |     "ignore": ["worker-configuration.d.ts"]
 8 |   },
 9 |   "vcs": {
10 |     "enabled": true,
11 |     "clientKind": "git",
12 |     "useIgnoreFile": true
13 |   },
14 |   "linter": {
15 |     "enabled": true,
16 |     "rules": {
17 |       "recommended": true,
18 |       "suspicious": {
19 |         "noExplicitAny": "off",
20 |         "noDebugger": "off",
21 |         "noConsoleLog": "off",
22 |         "noConfusingVoidType": "off"
23 |       },
24 |       "style": {
25 |         "noNonNullAssertion": "off"
26 |       }
27 |     }
28 |   },
29 |   "formatter": {
30 |     "enabled": true,
31 |     "indentWidth": 4,
32 |     "lineWidth": 100
33 |   }
34 | }
35 | 
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | export { DodoPayments as default } from './client';
 4 | 
 5 | export { type Uploadable, toFile } from './core/uploads';
 6 | export { APIPromise } from './core/api-promise';
 7 | export { DodoPayments, type ClientOptions } from './client';
 8 | export { PagePromise } from './core/pagination';
 9 | export {
10 |   DodoPaymentsError,
11 |   APIError,
12 |   APIConnectionError,
13 |   APIConnectionTimeoutError,
14 |   APIUserAbortError,
15 |   NotFoundError,
16 |   ConflictError,
17 |   RateLimitError,
18 |   BadRequestError,
19 |   AuthenticationError,
20 |   InternalServerError,
21 |   PermissionDeniedError,
22 |   UnprocessableEntityError,
23 | } from './core/error';
24 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/cloudflare-worker/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "remote-mcp-server-with-stainless",
 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 |     "marked": "^15.0.11",
15 |     "typescript": "^5.8.3",
16 |     "workers-mcp": "0.1.0-3",
17 |     "wrangler": "^4.15.2"
18 |   },
19 |   "dependencies": {
20 |     "@cloudflare/workers-oauth-provider": "^0.0.5",
21 |     "@modelcontextprotocol/sdk": "^1.11.4",
22 |     "agents": "^0.0.88",
23 |     "hono": "^4.7.9",
24 |     "dodopayments-mcp": "latest",
25 |     "zod": "^3.24.4"
26 |   }
27 | }
28 | 
```

--------------------------------------------------------------------------------
/scripts/utils/check-version.cjs:
--------------------------------------------------------------------------------

```
 1 | const fs = require('fs');
 2 | const path = require('path');
 3 | 
 4 | const main = () => {
 5 |   const pkg = require('../../package.json');
 6 |   const version = pkg['version'];
 7 |   if (!version) throw 'The version property is not set in the package.json file';
 8 |   if (typeof version !== 'string') {
 9 |     throw `Unexpected type for the package.json version field; got ${typeof version}, expected string`;
10 |   }
11 | 
12 |   const versionFile = path.resolve(__dirname, '..', '..', 'src', 'version.ts');
13 |   const contents = fs.readFileSync(versionFile, 'utf8');
14 |   const output = contents.replace(/(export const VERSION = ')(.*)(')/g, `$1${version}$3`);
15 |   fs.writeFileSync(versionFile, output);
16 | };
17 | 
18 | if (require.main === module) {
19 |   main();
20 | }
21 | 
```

--------------------------------------------------------------------------------
/.github/workflows/release-doctor.yml:
--------------------------------------------------------------------------------

```yaml
 1 | name: Release Doctor
 2 | on:
 3 |   pull_request:
 4 |     branches:
 5 |       - main
 6 |   workflow_dispatch:
 7 | 
 8 | jobs:
 9 |   release_doctor:
10 |     name: release doctor
11 |     runs-on: ubuntu-latest
12 |     if: github.repository == 'dodopayments/dodopayments-typescript' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next')
13 | 
14 |     steps:
15 |       - uses: actions/checkout@v4
16 | 
17 |       - name: Check release environment
18 |         run: |
19 |           bash ./bin/check-release-environment
20 |         env:
21 |           NPM_TOKEN: ${{ secrets.DODO_PAYMENTS_NPM_TOKEN || secrets.NPM_TOKEN }}
22 |           DOCKERHUB_TOKEN: ${{ secrets.DODO_PAYMENTS_DOCKERHUB_TOKEN || secrets.DOCKERHUB_TOKEN }}
23 | 
24 | 
```

--------------------------------------------------------------------------------
/scripts/utils/upload-artifact.sh:
--------------------------------------------------------------------------------

```bash
 1 | #!/usr/bin/env bash
 2 | set -exuo pipefail
 3 | 
 4 | RESPONSE=$(curl -X POST "$URL" \
 5 |   -H "Authorization: Bearer $AUTH" \
 6 |   -H "Content-Type: application/json")
 7 | 
 8 | SIGNED_URL=$(echo "$RESPONSE" | jq -r '.url')
 9 | 
10 | if [[ "$SIGNED_URL" == "null" ]]; then
11 |   echo -e "\033[31mFailed to get signed URL.\033[0m"
12 |   exit 1
13 | fi
14 | 
15 | TARBALL=$(cd dist && npm pack --silent)
16 | 
17 | UPLOAD_RESPONSE=$(curl -v -X PUT \
18 |   -H "Content-Type: application/gzip" \
19 |   --data-binary "@dist/$TARBALL" "$SIGNED_URL" 2>&1)
20 | 
21 | if echo "$UPLOAD_RESPONSE" | grep -q "HTTP/[0-9.]* 200"; then
22 |   echo -e "\033[32mUploaded build to Stainless storage.\033[0m"
23 |   echo -e "\033[32mInstallation: npm install 'https://pkg.stainless.com/s/dodo-payments-typescript/$SHA'\033[0m"
24 | else
25 |   echo -e "\033[31mFailed to upload artifact.\033[0m"
26 |   exit 1
27 | fi
28 | 
```

--------------------------------------------------------------------------------
/tests/api-resources/customers/wallets/wallets.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import DodoPayments from 'dodopayments';
 4 | 
 5 | const client = new DodoPayments({
 6 |   bearerToken: 'My Bearer Token',
 7 |   baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
 8 | });
 9 | 
10 | describe('resource wallets', () => {
11 |   test('list', async () => {
12 |     const responsePromise = client.customers.wallets.list('customer_id');
13 |     const rawResponse = await responsePromise.asResponse();
14 |     expect(rawResponse).toBeInstanceOf(Response);
15 |     const response = await responsePromise;
16 |     expect(response).not.toBeInstanceOf(Response);
17 |     const dataAndResponse = await responsePromise.withResponse();
18 |     expect(dataAndResponse.data).toBe(response);
19 |     expect(dataAndResponse.response).toBe(rawResponse);
20 |   });
21 | });
22 | 
```

--------------------------------------------------------------------------------
/tests/api-resources/misc.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import DodoPayments from 'dodopayments';
 4 | 
 5 | const client = new DodoPayments({
 6 |   bearerToken: 'My Bearer Token',
 7 |   baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
 8 | });
 9 | 
10 | describe('resource misc', () => {
11 |   test('listSupportedCountries', async () => {
12 |     const responsePromise = client.misc.listSupportedCountries();
13 |     const rawResponse = await responsePromise.asResponse();
14 |     expect(rawResponse).toBeInstanceOf(Response);
15 |     const response = await responsePromise;
16 |     expect(response).not.toBeInstanceOf(Response);
17 |     const dataAndResponse = await responsePromise.withResponse();
18 |     expect(dataAndResponse.data).toBe(response);
19 |     expect(dataAndResponse.response).toBe(rawResponse);
20 |   });
21 | });
22 | 
```

--------------------------------------------------------------------------------
/src/internal/utils/bytes.ts:
--------------------------------------------------------------------------------

```typescript
 1 | export function concatBytes(buffers: Uint8Array[]): Uint8Array {
 2 |   let length = 0;
 3 |   for (const buffer of buffers) {
 4 |     length += buffer.length;
 5 |   }
 6 |   const output = new Uint8Array(length);
 7 |   let index = 0;
 8 |   for (const buffer of buffers) {
 9 |     output.set(buffer, index);
10 |     index += buffer.length;
11 |   }
12 | 
13 |   return output;
14 | }
15 | 
16 | let encodeUTF8_: (str: string) => Uint8Array;
17 | export function encodeUTF8(str: string) {
18 |   let encoder;
19 |   return (
20 |     encodeUTF8_ ??
21 |     ((encoder = new (globalThis as any).TextEncoder()), (encodeUTF8_ = encoder.encode.bind(encoder)))
22 |   )(str);
23 | }
24 | 
25 | let decodeUTF8_: (bytes: Uint8Array) => string;
26 | export function decodeUTF8(bytes: Uint8Array) {
27 |   let decoder;
28 |   return (
29 |     decodeUTF8_ ??
30 |     ((decoder = new (globalThis as any).TextDecoder()), (decodeUTF8_ = decoder.decode.bind(decoder)))
31 |   )(bytes);
32 | }
33 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/headers.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { IncomingMessage } from 'node:http';
 4 | import { ClientOptions } from 'dodopayments';
 5 | 
 6 | export const parseAuthHeaders = (req: IncomingMessage): Partial<ClientOptions> => {
 7 |   if (req.headers.authorization) {
 8 |     const scheme = req.headers.authorization.split(' ')[0]!;
 9 |     const value = req.headers.authorization.slice(scheme.length + 1);
10 |     switch (scheme) {
11 |       case 'Bearer':
12 |         return { bearerToken: req.headers.authorization.slice('Bearer '.length) };
13 |       default:
14 |         throw new Error(`Unsupported authorization scheme`);
15 |     }
16 |   }
17 | 
18 |   const bearerToken =
19 |     Array.isArray(req.headers['x-dodo-payments-api-key']) ?
20 |       req.headers['x-dodo-payments-api-key'][0]
21 |     : req.headers['x-dodo-payments-api-key'];
22 |   return { bearerToken };
23 | };
24 | 
```

--------------------------------------------------------------------------------
/scripts/utils/make-dist-package-json.cjs:
--------------------------------------------------------------------------------

```
 1 | const pkgJson = require(process.env['PKG_JSON_PATH'] || '../../package.json');
 2 | 
 3 | function processExportMap(m) {
 4 |   for (const key in m) {
 5 |     const value = m[key];
 6 |     if (typeof value === 'string') m[key] = value.replace(/^\.\/dist\//, './');
 7 |     else processExportMap(value);
 8 |   }
 9 | }
10 | processExportMap(pkgJson.exports);
11 | 
12 | for (const key of ['types', 'main', 'module']) {
13 |   if (typeof pkgJson[key] === 'string') pkgJson[key] = pkgJson[key].replace(/^(\.\/)?dist\//, './');
14 | }
15 | // Fix bin paths if present
16 | if (pkgJson.bin) {
17 |   for (const key in pkgJson.bin) {
18 |     if (typeof pkgJson.bin[key] === 'string') {
19 |       pkgJson.bin[key] = pkgJson.bin[key].replace(/^(\.\/)?dist\//, './');
20 |     }
21 |   }
22 | }
23 | 
24 | delete pkgJson.devDependencies;
25 | delete pkgJson.scripts.prepack;
26 | delete pkgJson.scripts.prepublishOnly;
27 | delete pkgJson.scripts.prepare;
28 | 
29 | console.log(JSON.stringify(pkgJson, null, 2));
30 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "include": ["src", "tests", "examples"],
 3 |   "exclude": [],
 4 |   "compilerOptions": {
 5 |     "target": "es2020",
 6 |     "lib": ["es2020"],
 7 |     "module": "commonjs",
 8 |     "moduleResolution": "node",
 9 |     "esModuleInterop": true,
10 |     "paths": {
11 |       "dodopayments-mcp/*": ["./src/*"],
12 |       "dodopayments-mcp": ["./src/index.ts"]
13 |     },
14 |     "noEmit": true,
15 | 
16 |     "resolveJsonModule": true,
17 | 
18 |     "forceConsistentCasingInFileNames": true,
19 | 
20 |     "strict": true,
21 |     "noImplicitAny": true,
22 |     "strictNullChecks": true,
23 |     "strictFunctionTypes": true,
24 |     "strictBindCallApply": true,
25 |     "strictPropertyInitialization": true,
26 |     "noImplicitThis": true,
27 |     "noImplicitReturns": true,
28 |     "alwaysStrict": true,
29 |     "exactOptionalPropertyTypes": true,
30 |     "noUncheckedIndexedAccess": true,
31 |     "noImplicitOverride": true,
32 |     "noPropertyAccessFromIndexSignature": true,
33 | 
34 |     "skipLibCheck": true
35 |   }
36 | }
37 | 
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "include": ["src", "tests", "examples"],
 3 |   "exclude": [],
 4 |   "compilerOptions": {
 5 |     "target": "es2020",
 6 |     "lib": ["es2020"],
 7 |     "module": "commonjs",
 8 |     "moduleResolution": "node",
 9 |     "esModuleInterop": true,
10 |     "paths": {
11 |       "dodopayments/*": ["./src/*"],
12 |       "dodopayments": ["./src/index.ts"]
13 |     },
14 |     "noEmit": true,
15 | 
16 |     "resolveJsonModule": true,
17 | 
18 |     "forceConsistentCasingInFileNames": true,
19 | 
20 |     "strict": true,
21 |     "noImplicitAny": true,
22 |     "strictNullChecks": true,
23 |     "strictFunctionTypes": true,
24 |     "strictBindCallApply": true,
25 |     "strictPropertyInitialization": true,
26 |     "noImplicitThis": true,
27 |     "noImplicitReturns": true,
28 |     "alwaysStrict": true,
29 |     "exactOptionalPropertyTypes": true,
30 |     "noUncheckedIndexedAccess": true,
31 |     "noImplicitOverride": true,
32 |     "noPropertyAccessFromIndexSignature": true,
33 |     "isolatedModules": false,
34 | 
35 |     "skipLibCheck": true
36 |   }
37 | }
38 | 
```

--------------------------------------------------------------------------------
/src/internal/shim-types.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | /**
 4 |  * Shims for types that we can't always rely on being available globally.
 5 |  *
 6 |  * Note: these only exist at the type-level, there is no corresponding runtime
 7 |  * version for any of these symbols.
 8 |  */
 9 | 
10 | type NeverToAny<T> = T extends never ? any : T;
11 | 
12 | /** @ts-ignore */
13 | type _DOMReadableStream<R = any> = globalThis.ReadableStream<R>;
14 | 
15 | /** @ts-ignore */
16 | type _NodeReadableStream<R = any> = import('stream/web').ReadableStream<R>;
17 | 
18 | type _ConditionalNodeReadableStream<R = any> =
19 |   typeof globalThis extends { ReadableStream: any } ? never : _NodeReadableStream<R>;
20 | 
21 | type _ReadableStream<R = any> = NeverToAny<
22 |   | ([0] extends [1 & _DOMReadableStream<R>] ? never : _DOMReadableStream<R>)
23 |   | ([0] extends [1 & _ConditionalNodeReadableStream<R>] ? never : _ConditionalNodeReadableStream<R>)
24 | >;
25 | 
26 | export type { _ReadableStream as ReadableStream };
27 | 
```

--------------------------------------------------------------------------------
/src/resources/products/images.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { APIResource } from '../../core/resource';
 4 | import { APIPromise } from '../../core/api-promise';
 5 | import { RequestOptions } from '../../internal/request-options';
 6 | import { path } from '../../internal/utils/path';
 7 | 
 8 | export class Images extends APIResource {
 9 |   update(
10 |     id: string,
11 |     params: ImageUpdateParams | null | undefined = {},
12 |     options?: RequestOptions,
13 |   ): APIPromise<ImageUpdateResponse> {
14 |     const { force_update } = params ?? {};
15 |     return this._client.put(path`/products/${id}/images`, { query: { force_update }, ...options });
16 |   }
17 | }
18 | 
19 | export interface ImageUpdateResponse {
20 |   url: string;
21 | 
22 |   image_id?: string | null;
23 | }
24 | 
25 | export interface ImageUpdateParams {
26 |   force_update?: boolean;
27 | }
28 | 
29 | export declare namespace Images {
30 |   export { type ImageUpdateResponse as ImageUpdateResponse, type ImageUpdateParams as ImageUpdateParams };
31 | }
32 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/meters/retrieve-meters.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';
 4 | 
 5 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
 6 | import DodoPayments from 'dodopayments';
 7 | 
 8 | export const metadata: Metadata = {
 9 |   resource: 'meters',
10 |   operation: 'read',
11 |   tags: [],
12 |   httpMethod: 'get',
13 |   httpPath: '/meters/{id}',
14 |   operationId: 'get_meter_handler',
15 | };
16 | 
17 | export const tool: Tool = {
18 |   name: 'retrieve_meters',
19 |   description: '',
20 |   inputSchema: {
21 |     type: 'object',
22 |     properties: {
23 |       id: {
24 |         type: 'string',
25 |       },
26 |     },
27 |     required: ['id'],
28 |   },
29 |   annotations: {
30 |     readOnlyHint: true,
31 |   },
32 | };
33 | 
34 | export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
35 |   const { id, ...body } = args as any;
36 |   return asTextContentResult(await client.meters.retrieve(id));
37 | };
38 | 
39 | export default { metadata, tool, handler };
40 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/products/retrieve-products.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';
 4 | 
 5 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
 6 | import DodoPayments from 'dodopayments';
 7 | 
 8 | export const metadata: Metadata = {
 9 |   resource: 'products',
10 |   operation: 'read',
11 |   tags: [],
12 |   httpMethod: 'get',
13 |   httpPath: '/products/{id}',
14 |   operationId: 'get_product_handler',
15 | };
16 | 
17 | export const tool: Tool = {
18 |   name: 'retrieve_products',
19 |   description: '',
20 |   inputSchema: {
21 |     type: 'object',
22 |     properties: {
23 |       id: {
24 |         type: 'string',
25 |       },
26 |     },
27 |     required: ['id'],
28 |   },
29 |   annotations: {
30 |     readOnlyHint: true,
31 |   },
32 | };
33 | 
34 | export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
35 |   const { id, ...body } = args as any;
36 |   return asTextContentResult(await client.products.retrieve(id));
37 | };
38 | 
39 | export default { metadata, tool, handler };
40 | 
```

--------------------------------------------------------------------------------
/tests/stringifyQuery.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { DodoPayments } from 'dodopayments';
 4 | 
 5 | const { stringifyQuery } = DodoPayments.prototype as any;
 6 | 
 7 | describe(stringifyQuery, () => {
 8 |   for (const [input, expected] of [
 9 |     [{ a: '1', b: 2, c: true }, 'a=1&b=2&c=true'],
10 |     [{ a: null, b: false, c: undefined }, 'a=&b=false'],
11 |     [{ 'a/b': 1.28341 }, `${encodeURIComponent('a/b')}=1.28341`],
12 |     [
13 |       { 'a/b': 'c/d', 'e=f': 'g&h' },
14 |       `${encodeURIComponent('a/b')}=${encodeURIComponent('c/d')}&${encodeURIComponent(
15 |         'e=f',
16 |       )}=${encodeURIComponent('g&h')}`,
17 |     ],
18 |   ]) {
19 |     it(`${JSON.stringify(input)} -> ${expected}`, () => {
20 |       expect(stringifyQuery(input)).toEqual(expected);
21 |     });
22 |   }
23 | 
24 |   for (const value of [[], {}, new Date()]) {
25 |     it(`${JSON.stringify(value)} -> <error>`, () => {
26 |       expect(() => stringifyQuery({ value })).toThrow(`Cannot stringify type ${typeof value}`);
27 |     });
28 |   }
29 | });
30 | 
```

--------------------------------------------------------------------------------
/src/resources/invoices/payments.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { APIResource } from '../../core/resource';
 4 | import { APIPromise } from '../../core/api-promise';
 5 | import { buildHeaders } from '../../internal/headers';
 6 | import { RequestOptions } from '../../internal/request-options';
 7 | import { path } from '../../internal/utils/path';
 8 | 
 9 | export class Payments extends APIResource {
10 |   retrieve(paymentID: string, options?: RequestOptions): APIPromise<Response> {
11 |     return this._client.get(path`/invoices/payments/${paymentID}`, {
12 |       ...options,
13 |       headers: buildHeaders([{ Accept: 'application/pdf' }, options?.headers]),
14 |       __binaryResponse: true,
15 |     });
16 |   }
17 | 
18 |   retrieveRefund(refundID: string, options?: RequestOptions): APIPromise<Response> {
19 |     return this._client.get(path`/invoices/refunds/${refundID}`, {
20 |       ...options,
21 |       headers: buildHeaders([{ Accept: 'application/pdf' }, options?.headers]),
22 |       __binaryResponse: true,
23 |     });
24 |   }
25 | }
26 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/meters/unarchive-meters.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';
 4 | 
 5 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
 6 | import DodoPayments from 'dodopayments';
 7 | 
 8 | export const metadata: Metadata = {
 9 |   resource: 'meters',
10 |   operation: 'write',
11 |   tags: [],
12 |   httpMethod: 'post',
13 |   httpPath: '/meters/{id}/unarchive',
14 |   operationId: 'unarchive_meter',
15 | };
16 | 
17 | export const tool: Tool = {
18 |   name: 'unarchive_meters',
19 |   description: '',
20 |   inputSchema: {
21 |     type: 'object',
22 |     properties: {
23 |       id: {
24 |         type: 'string',
25 |       },
26 |     },
27 |     required: ['id'],
28 |   },
29 |   annotations: {},
30 | };
31 | 
32 | export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
33 |   const { id, ...body } = args as any;
34 |   const response = await client.meters.unarchive(id).asResponse();
35 |   return asTextContentResult(await response.text());
36 | };
37 | 
38 | export default { metadata, tool, handler };
39 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/products/unarchive-products.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';
 4 | 
 5 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
 6 | import DodoPayments from 'dodopayments';
 7 | 
 8 | export const metadata: Metadata = {
 9 |   resource: 'products',
10 |   operation: 'write',
11 |   tags: [],
12 |   httpMethod: 'post',
13 |   httpPath: '/products/{id}/unarchive',
14 |   operationId: 'undelete_product',
15 | };
16 | 
17 | export const tool: Tool = {
18 |   name: 'unarchive_products',
19 |   description: '',
20 |   inputSchema: {
21 |     type: 'object',
22 |     properties: {
23 |       id: {
24 |         type: 'string',
25 |       },
26 |     },
27 |     required: ['id'],
28 |   },
29 |   annotations: {},
30 | };
31 | 
32 | export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
33 |   const { id, ...body } = args as any;
34 |   const response = await client.products.unarchive(id).asResponse();
35 |   return asTextContentResult(await response.text());
36 | };
37 | 
38 | export default { metadata, tool, handler };
39 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/payments/retrieve-payments.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';
 4 | 
 5 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
 6 | import DodoPayments from 'dodopayments';
 7 | 
 8 | export const metadata: Metadata = {
 9 |   resource: 'payments',
10 |   operation: 'read',
11 |   tags: [],
12 |   httpMethod: 'get',
13 |   httpPath: '/payments/{payment_id}',
14 |   operationId: 'get_payment_handler',
15 | };
16 | 
17 | export const tool: Tool = {
18 |   name: 'retrieve_payments',
19 |   description: '',
20 |   inputSchema: {
21 |     type: 'object',
22 |     properties: {
23 |       payment_id: {
24 |         type: 'string',
25 |       },
26 |     },
27 |     required: ['payment_id'],
28 |   },
29 |   annotations: {
30 |     readOnlyHint: true,
31 |   },
32 | };
33 | 
34 | export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
35 |   const { payment_id, ...body } = args as any;
36 |   return asTextContentResult(await client.payments.retrieve(payment_id));
37 | };
38 | 
39 | export default { metadata, tool, handler };
40 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/meters/archive-meters.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';
 4 | 
 5 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
 6 | import DodoPayments from 'dodopayments';
 7 | 
 8 | export const metadata: Metadata = {
 9 |   resource: 'meters',
10 |   operation: 'write',
11 |   tags: [],
12 |   httpMethod: 'delete',
13 |   httpPath: '/meters/{id}',
14 |   operationId: 'delete_meter',
15 | };
16 | 
17 | export const tool: Tool = {
18 |   name: 'archive_meters',
19 |   description: '',
20 |   inputSchema: {
21 |     type: 'object',
22 |     properties: {
23 |       id: {
24 |         type: 'string',
25 |       },
26 |     },
27 |     required: ['id'],
28 |   },
29 |   annotations: {
30 |     idempotentHint: true,
31 |   },
32 | };
33 | 
34 | export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
35 |   const { id, ...body } = args as any;
36 |   const response = await client.meters.archive(id).asResponse();
37 |   return asTextContentResult(await response.text());
38 | };
39 | 
40 | export default { metadata, tool, handler };
41 | 
```

--------------------------------------------------------------------------------
/src/resources/customers/customer-portal.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { APIResource } from '../../core/resource';
 4 | import * as CustomersAPI from './customers';
 5 | import { APIPromise } from '../../core/api-promise';
 6 | import { RequestOptions } from '../../internal/request-options';
 7 | import { path } from '../../internal/utils/path';
 8 | 
 9 | export class CustomerPortal extends APIResource {
10 |   create(
11 |     customerID: string,
12 |     params: CustomerPortalCreateParams | null | undefined = {},
13 |     options?: RequestOptions,
14 |   ): APIPromise<CustomersAPI.CustomerPortalSession> {
15 |     const { send_email } = params ?? {};
16 |     return this._client.post(path`/customers/${customerID}/customer-portal/session`, {
17 |       query: { send_email },
18 |       ...options,
19 |     });
20 |   }
21 | }
22 | 
23 | export interface CustomerPortalCreateParams {
24 |   /**
25 |    * If true, will send link to user.
26 |    */
27 |   send_email?: boolean;
28 | }
29 | 
30 | export declare namespace CustomerPortal {
31 |   export { type CustomerPortalCreateParams as CustomerPortalCreateParams };
32 | }
33 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/products/archive-products.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';
 4 | 
 5 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
 6 | import DodoPayments from 'dodopayments';
 7 | 
 8 | export const metadata: Metadata = {
 9 |   resource: 'products',
10 |   operation: 'write',
11 |   tags: [],
12 |   httpMethod: 'delete',
13 |   httpPath: '/products/{id}',
14 |   operationId: 'delete_product',
15 | };
16 | 
17 | export const tool: Tool = {
18 |   name: 'archive_products',
19 |   description: '',
20 |   inputSchema: {
21 |     type: 'object',
22 |     properties: {
23 |       id: {
24 |         type: 'string',
25 |       },
26 |     },
27 |     required: ['id'],
28 |   },
29 |   annotations: {
30 |     idempotentHint: true,
31 |   },
32 | };
33 | 
34 | export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
35 |   const { id, ...body } = args as any;
36 |   const response = await client.products.archive(id).asResponse();
37 |   return asTextContentResult(await response.text());
38 | };
39 | 
40 | export default { metadata, tool, handler };
41 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/invoices/payments/retrieve-invoices-payments.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { Metadata, asBinaryContentResult } from 'dodopayments-mcp/tools/types';
 4 | 
 5 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
 6 | import DodoPayments from 'dodopayments';
 7 | 
 8 | export const metadata: Metadata = {
 9 |   resource: 'invoices.payments',
10 |   operation: 'read',
11 |   tags: [],
12 |   httpMethod: 'get',
13 |   httpPath: '/invoices/payments/{payment_id}',
14 |   operationId: 'get_payment_invoice_no_auth',
15 | };
16 | 
17 | export const tool: Tool = {
18 |   name: 'retrieve_invoices_payments',
19 |   description: '',
20 |   inputSchema: {
21 |     type: 'object',
22 |     properties: {
23 |       payment_id: {
24 |         type: 'string',
25 |       },
26 |     },
27 |     required: ['payment_id'],
28 |   },
29 |   annotations: {
30 |     readOnlyHint: true,
31 |   },
32 | };
33 | 
34 | export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
35 |   const { payment_id, ...body } = args as any;
36 |   return asBinaryContentResult(await client.invoices.payments.retrieve(payment_id));
37 | };
38 | 
39 | export default { metadata, tool, handler };
40 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/subscriptions/retrieve-subscriptions.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';
 4 | 
 5 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
 6 | import DodoPayments from 'dodopayments';
 7 | 
 8 | export const metadata: Metadata = {
 9 |   resource: 'subscriptions',
10 |   operation: 'read',
11 |   tags: [],
12 |   httpMethod: 'get',
13 |   httpPath: '/subscriptions/{subscription_id}',
14 |   operationId: 'get_subscription_handler',
15 | };
16 | 
17 | export const tool: Tool = {
18 |   name: 'retrieve_subscriptions',
19 |   description: '',
20 |   inputSchema: {
21 |     type: 'object',
22 |     properties: {
23 |       subscription_id: {
24 |         type: 'string',
25 |       },
26 |     },
27 |     required: ['subscription_id'],
28 |   },
29 |   annotations: {
30 |     readOnlyHint: true,
31 |   },
32 | };
33 | 
34 | export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
35 |   const { subscription_id, ...body } = args as any;
36 |   return asTextContentResult(await client.subscriptions.retrieve(subscription_id));
37 | };
38 | 
39 | export default { metadata, tool, handler };
40 | 
```

--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------

```
 1 | // @ts-check
 2 | import tseslint from 'typescript-eslint';
 3 | import unusedImports from 'eslint-plugin-unused-imports';
 4 | import prettier from 'eslint-plugin-prettier';
 5 | 
 6 | export default tseslint.config(
 7 |   {
 8 |     languageOptions: {
 9 |       parser: tseslint.parser,
10 |       parserOptions: { sourceType: 'module' },
11 |     },
12 |     files: ['**/*.ts', '**/*.mts', '**/*.cts', '**/*.js', '**/*.mjs', '**/*.cjs'],
13 |     ignores: ['dist/'],
14 |     plugins: {
15 |       '@typescript-eslint': tseslint.plugin,
16 |       'unused-imports': unusedImports,
17 |       prettier,
18 |     },
19 |     rules: {
20 |       'no-unused-vars': 'off',
21 |       'prettier/prettier': 'error',
22 |       'unused-imports/no-unused-imports': 'error',
23 |       'no-restricted-imports': [
24 |         'error',
25 |         {
26 |           patterns: [
27 |             {
28 |               regex: '^dodopayments(/.*)?',
29 |               message: 'Use a relative import, not a package import.',
30 |             },
31 |           ],
32 |         },
33 |       ],
34 |     },
35 |   },
36 |   {
37 |     files: ['tests/**', 'examples/**', 'packages/**'],
38 |     rules: {
39 |       'no-restricted-imports': 'off',
40 |     },
41 |   },
42 | );
43 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/invoices/payments/retrieve-refund-invoices-payments.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { Metadata, asBinaryContentResult } from 'dodopayments-mcp/tools/types';
 4 | 
 5 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
 6 | import DodoPayments from 'dodopayments';
 7 | 
 8 | export const metadata: Metadata = {
 9 |   resource: 'invoices.payments',
10 |   operation: 'read',
11 |   tags: [],
12 |   httpMethod: 'get',
13 |   httpPath: '/invoices/refunds/{refund_id}',
14 |   operationId: 'get_refund_invoice_no_auth',
15 | };
16 | 
17 | export const tool: Tool = {
18 |   name: 'retrieve_refund_invoices_payments',
19 |   description: '',
20 |   inputSchema: {
21 |     type: 'object',
22 |     properties: {
23 |       refund_id: {
24 |         type: 'string',
25 |       },
26 |     },
27 |     required: ['refund_id'],
28 |   },
29 |   annotations: {
30 |     readOnlyHint: true,
31 |   },
32 | };
33 | 
34 | export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
35 |   const { refund_id, ...body } = args as any;
36 |   return asBinaryContentResult(await client.invoices.payments.retrieveRefund(refund_id));
37 | };
38 | 
39 | export default { metadata, tool, handler };
40 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/webhooks/delete-webhooks.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';
 4 | 
 5 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
 6 | import DodoPayments from 'dodopayments';
 7 | 
 8 | export const metadata: Metadata = {
 9 |   resource: 'webhooks',
10 |   operation: 'write',
11 |   tags: [],
12 |   httpMethod: 'delete',
13 |   httpPath: '/webhooks/{webhook_id}',
14 |   operationId: 'delete_webhook',
15 | };
16 | 
17 | export const tool: Tool = {
18 |   name: 'delete_webhooks',
19 |   description: 'Delete a webhook by id',
20 |   inputSchema: {
21 |     type: 'object',
22 |     properties: {
23 |       webhook_id: {
24 |         type: 'string',
25 |       },
26 |     },
27 |     required: ['webhook_id'],
28 |   },
29 |   annotations: {
30 |     idempotentHint: true,
31 |   },
32 | };
33 | 
34 | export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
35 |   const { webhook_id, ...body } = args as any;
36 |   const response = await client.webhooks.delete(webhook_id).asResponse();
37 |   return asTextContentResult(await response.text());
38 | };
39 | 
40 | export default { metadata, tool, handler };
41 | 
```

--------------------------------------------------------------------------------
/scripts/utils/attw-report.cjs:
--------------------------------------------------------------------------------

```
 1 | const fs = require('fs');
 2 | const problems = Object.values(JSON.parse(fs.readFileSync('.attw.json', 'utf-8')).problems)
 3 |   .flat()
 4 |   .filter(
 5 |     (problem) =>
 6 |       !(
 7 |         // This is intentional, if the user specifies .mjs they get ESM.
 8 |         (
 9 |           (problem.kind === 'CJSResolvesToESM' && problem.entrypoint.endsWith('.mjs')) ||
10 |           // This is intentional for backwards compat reasons.
11 |           (problem.kind === 'MissingExportEquals' && problem.implementationFileName.endsWith('/index.js')) ||
12 |           // this is intentional, we deliberately attempt to import types that may not exist from parent node_modules
13 |           // folders to better support various runtimes without triggering automatic type acquisition.
14 |           (problem.kind === 'InternalResolutionError' && problem.moduleSpecifier.includes('node_modules'))
15 |         )
16 |       ),
17 |   );
18 | fs.unlinkSync('.attw.json');
19 | if (problems.length) {
20 |   process.stdout.write('The types are wrong!\n' + JSON.stringify(problems, null, 2) + '\n');
21 |   process.exitCode = 1;
22 | } else {
23 |   process.stdout.write('Types ok!\n');
24 | }
25 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/discounts/delete-discounts.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';
 4 | 
 5 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
 6 | import DodoPayments from 'dodopayments';
 7 | 
 8 | export const metadata: Metadata = {
 9 |   resource: 'discounts',
10 |   operation: 'write',
11 |   tags: [],
12 |   httpMethod: 'delete',
13 |   httpPath: '/discounts/{discount_id}',
14 |   operationId: 'delete_discount_handler',
15 | };
16 | 
17 | export const tool: Tool = {
18 |   name: 'delete_discounts',
19 |   description: 'DELETE /discounts/{discount_id}',
20 |   inputSchema: {
21 |     type: 'object',
22 |     properties: {
23 |       discount_id: {
24 |         type: 'string',
25 |       },
26 |     },
27 |     required: ['discount_id'],
28 |   },
29 |   annotations: {
30 |     idempotentHint: true,
31 |   },
32 | };
33 | 
34 | export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
35 |   const { discount_id, ...body } = args as any;
36 |   const response = await client.discounts.delete(discount_id).asResponse();
37 |   return asTextContentResult(await response.text());
38 | };
39 | 
40 | export default { metadata, tool, handler };
41 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/licenses/deactivate-licenses.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';
 4 | 
 5 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
 6 | import DodoPayments from 'dodopayments';
 7 | 
 8 | export const metadata: Metadata = {
 9 |   resource: 'licenses',
10 |   operation: 'write',
11 |   tags: [],
12 |   httpMethod: 'post',
13 |   httpPath: '/licenses/deactivate',
14 |   operationId: 'deactivate_license_key',
15 | };
16 | 
17 | export const tool: Tool = {
18 |   name: 'deactivate_licenses',
19 |   description: '',
20 |   inputSchema: {
21 |     type: 'object',
22 |     properties: {
23 |       license_key: {
24 |         type: 'string',
25 |       },
26 |       license_key_instance_id: {
27 |         type: 'string',
28 |       },
29 |     },
30 |     required: ['license_key', 'license_key_instance_id'],
31 |   },
32 |   annotations: {},
33 | };
34 | 
35 | export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
36 |   const body = args as any;
37 |   const response = await client.licenses.deactivate(body).asResponse();
38 |   return asTextContentResult(await response.text());
39 | };
40 | 
41 | export default { metadata, tool, handler };
42 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/scripts/copy-bundle-files.cjs:
--------------------------------------------------------------------------------

```
 1 | const fs = require('fs');
 2 | const path = require('path');
 3 | const pkgJson = require('../dist-bundle/package.json');
 4 | 
 5 | const distDir = path.resolve(__dirname, '..', 'dist');
 6 | const distBundleDir = path.resolve(__dirname, '..', 'dist-bundle');
 7 | const distBundlePkgJson = path.join(distBundleDir, 'package.json');
 8 | 
 9 | async function* walk(dir) {
10 |   for await (const d of await fs.promises.opendir(dir)) {
11 |     const entry = path.join(dir, d.name);
12 |     if (d.isDirectory()) yield* walk(entry);
13 |     else if (d.isFile()) yield entry;
14 |   }
15 | }
16 | 
17 | async function copyFiles() {
18 |   // copy runtime files
19 |   for await (const file of walk(distDir)) {
20 |     if (!/[cm]?js$/.test(file)) continue;
21 |     const dest = path.join(distBundleDir, path.relative(distDir, file));
22 |     await fs.promises.mkdir(path.dirname(dest), { recursive: true });
23 |     await fs.promises.copyFile(file, dest);
24 |   }
25 | 
26 |   // replace package.json reference with local reference
27 |   for (const dep in pkgJson.dependencies) {
28 |     if (dep === 'dodopayments') {
29 |       pkgJson.dependencies[dep] = 'file:../../../dist/';
30 |     }
31 |   }
32 | 
33 |   await fs.promises.writeFile(distBundlePkgJson, JSON.stringify(pkgJson, null, 2));
34 | }
35 | 
36 | copyFiles();
37 | 
```

--------------------------------------------------------------------------------
/tests/api-resources/products/images.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import DodoPayments from 'dodopayments';
 4 | 
 5 | const client = new DodoPayments({
 6 |   bearerToken: 'My Bearer Token',
 7 |   baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
 8 | });
 9 | 
10 | describe('resource images', () => {
11 |   test('update', async () => {
12 |     const responsePromise = client.products.images.update('id');
13 |     const rawResponse = await responsePromise.asResponse();
14 |     expect(rawResponse).toBeInstanceOf(Response);
15 |     const response = await responsePromise;
16 |     expect(response).not.toBeInstanceOf(Response);
17 |     const dataAndResponse = await responsePromise.withResponse();
18 |     expect(dataAndResponse.data).toBe(response);
19 |     expect(dataAndResponse.response).toBe(rawResponse);
20 |   });
21 | 
22 |   test('update: request options and params are passed correctly', async () => {
23 |     // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error
24 |     await expect(
25 |       client.products.images.update('id', { force_update: true }, { path: '/_stainless_unknown_path' }),
26 |     ).rejects.toThrow(DodoPayments.NotFoundError);
27 |   });
28 | });
29 | 
```

--------------------------------------------------------------------------------
/src/internal/errors.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | export function isAbortError(err: unknown) {
 4 |   return (
 5 |     typeof err === 'object' &&
 6 |     err !== null &&
 7 |     // Spec-compliant fetch implementations
 8 |     (('name' in err && (err as any).name === 'AbortError') ||
 9 |       // Expo fetch
10 |       ('message' in err && String((err as any).message).includes('FetchRequestCanceledException')))
11 |   );
12 | }
13 | 
14 | export const castToError = (err: any): Error => {
15 |   if (err instanceof Error) return err;
16 |   if (typeof err === 'object' && err !== null) {
17 |     try {
18 |       if (Object.prototype.toString.call(err) === '[object Error]') {
19 |         // @ts-ignore - not all envs have native support for cause yet
20 |         const error = new Error(err.message, err.cause ? { cause: err.cause } : {});
21 |         if (err.stack) error.stack = err.stack;
22 |         // @ts-ignore - not all envs have native support for cause yet
23 |         if (err.cause && !error.cause) error.cause = err.cause;
24 |         if (err.name) error.name = err.name;
25 |         return error;
26 |       }
27 |     } catch {}
28 |     try {
29 |       return new Error(JSON.stringify(err));
30 |     } catch {}
31 |   }
32 |   return new Error(err);
33 | };
34 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/code-tool-worker.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import util from 'node:util';
 4 | import { WorkerInput, WorkerSuccess, WorkerError } from './code-tool-types';
 5 | import { DodoPayments } from 'dodopayments';
 6 | 
 7 | const fetch = async (req: Request): Promise<Response> => {
 8 |   const { opts, code } = (await req.json()) as WorkerInput;
 9 |   const client = new DodoPayments({
10 |     ...opts,
11 |   });
12 | 
13 |   const logLines: string[] = [];
14 |   const errLines: string[] = [];
15 |   const console = {
16 |     log: (...args: unknown[]) => {
17 |       logLines.push(util.format(...args));
18 |     },
19 |     error: (...args: unknown[]) => {
20 |       errLines.push(util.format(...args));
21 |     },
22 |   };
23 |   try {
24 |     let run_ = async (client: any) => {};
25 |     eval(`
26 |       ${code}
27 |       run_ = run;
28 |     `);
29 |     const result = await run_(client);
30 |     return Response.json({
31 |       result,
32 |       logLines,
33 |       errLines,
34 |     } satisfies WorkerSuccess);
35 |   } catch (e) {
36 |     const message = e instanceof Error ? e.message : undefined;
37 |     return Response.json(
38 |       {
39 |         message,
40 |       } satisfies WorkerError,
41 |       { status: 400, statusText: 'Code execution error' },
42 |     );
43 |   }
44 | };
45 | 
46 | export default { fetch };
47 | 
```

--------------------------------------------------------------------------------
/src/resources/webhooks/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | export { Headers, type HeaderRetrieveResponse, type HeaderUpdateParams } from './headers';
 4 | export {
 5 |   Webhooks,
 6 |   type WebhookDetails,
 7 |   type WebhookRetrieveSecretResponse,
 8 |   type DisputeAcceptedWebhookEvent,
 9 |   type DisputeCancelledWebhookEvent,
10 |   type DisputeChallengedWebhookEvent,
11 |   type DisputeExpiredWebhookEvent,
12 |   type DisputeLostWebhookEvent,
13 |   type DisputeOpenedWebhookEvent,
14 |   type DisputeWonWebhookEvent,
15 |   type LicenseKeyCreatedWebhookEvent,
16 |   type PaymentCancelledWebhookEvent,
17 |   type PaymentFailedWebhookEvent,
18 |   type PaymentProcessingWebhookEvent,
19 |   type PaymentSucceededWebhookEvent,
20 |   type RefundFailedWebhookEvent,
21 |   type RefundSucceededWebhookEvent,
22 |   type SubscriptionActiveWebhookEvent,
23 |   type SubscriptionCancelledWebhookEvent,
24 |   type SubscriptionExpiredWebhookEvent,
25 |   type SubscriptionFailedWebhookEvent,
26 |   type SubscriptionOnHoldWebhookEvent,
27 |   type SubscriptionPlanChangedWebhookEvent,
28 |   type SubscriptionRenewedWebhookEvent,
29 |   type UnsafeUnwrapWebhookEvent,
30 |   type UnwrapWebhookEvent,
31 |   type WebhookCreateParams,
32 |   type WebhookUpdateParams,
33 |   type WebhookListParams,
34 |   type WebhookDetailsCursorPagePagination,
35 | } from './webhooks';
36 | 
```

--------------------------------------------------------------------------------
/tests/api-resources/customers/customer-portal.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import DodoPayments from 'dodopayments';
 4 | 
 5 | const client = new DodoPayments({
 6 |   bearerToken: 'My Bearer Token',
 7 |   baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
 8 | });
 9 | 
10 | describe('resource customerPortal', () => {
11 |   test('create', async () => {
12 |     const responsePromise = client.customers.customerPortal.create('customer_id');
13 |     const rawResponse = await responsePromise.asResponse();
14 |     expect(rawResponse).toBeInstanceOf(Response);
15 |     const response = await responsePromise;
16 |     expect(response).not.toBeInstanceOf(Response);
17 |     const dataAndResponse = await responsePromise.withResponse();
18 |     expect(dataAndResponse.data).toBe(response);
19 |     expect(dataAndResponse.response).toBe(rawResponse);
20 |   });
21 | 
22 |   test('create: request options and params are passed correctly', async () => {
23 |     // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error
24 |     await expect(
25 |       client.customers.customerPortal.create(
26 |         'customer_id',
27 |         { send_email: true },
28 |         { path: '/_stainless_unknown_path' },
29 |       ),
30 |     ).rejects.toThrow(DodoPayments.NotFoundError);
31 |   });
32 | });
33 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/meters/list-meters.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';
 4 | 
 5 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
 6 | import DodoPayments from 'dodopayments';
 7 | 
 8 | export const metadata: Metadata = {
 9 |   resource: 'meters',
10 |   operation: 'read',
11 |   tags: [],
12 |   httpMethod: 'get',
13 |   httpPath: '/meters',
14 |   operationId: 'list_meters_handler',
15 | };
16 | 
17 | export const tool: Tool = {
18 |   name: 'list_meters',
19 |   description: '',
20 |   inputSchema: {
21 |     type: 'object',
22 |     properties: {
23 |       archived: {
24 |         type: 'boolean',
25 |         description: 'List archived meters',
26 |       },
27 |       page_number: {
28 |         type: 'integer',
29 |         description: 'Page number default is 0',
30 |       },
31 |       page_size: {
32 |         type: 'integer',
33 |         description: 'Page size default is 10 max is 100',
34 |       },
35 |     },
36 |     required: [],
37 |   },
38 |   annotations: {
39 |     readOnlyHint: true,
40 |   },
41 | };
42 | 
43 | export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
44 |   const body = args as any;
45 |   const response = await client.meters.list(body).asResponse();
46 |   return asTextContentResult(await response.json());
47 | };
48 | 
49 | export default { metadata, tool, handler };
50 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/webhooks/headers/update-webhooks-headers.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';
 4 | 
 5 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
 6 | import DodoPayments from 'dodopayments';
 7 | 
 8 | export const metadata: Metadata = {
 9 |   resource: 'webhooks.headers',
10 |   operation: 'write',
11 |   tags: [],
12 |   httpMethod: 'patch',
13 |   httpPath: '/webhooks/{webhook_id}/headers',
14 |   operationId: 'patch_webhook_headers',
15 | };
16 | 
17 | export const tool: Tool = {
18 |   name: 'update_webhooks_headers',
19 |   description: 'Patch a webhook by id',
20 |   inputSchema: {
21 |     type: 'object',
22 |     properties: {
23 |       webhook_id: {
24 |         type: 'string',
25 |       },
26 |       headers: {
27 |         type: 'object',
28 |         description: 'Object of header-value pair to update or add',
29 |         additionalProperties: true,
30 |       },
31 |     },
32 |     required: ['webhook_id', 'headers'],
33 |   },
34 |   annotations: {},
35 | };
36 | 
37 | export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
38 |   const { webhook_id, ...body } = args as any;
39 |   const response = await client.webhooks.headers.update(webhook_id, body).asResponse();
40 |   return asTextContentResult(await response.text());
41 | };
42 | 
43 | export default { metadata, tool, handler };
44 | 
```

--------------------------------------------------------------------------------
/src/internal/utils/base64.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { DodoPaymentsError } from '../../core/error';
 4 | import { encodeUTF8 } from './bytes';
 5 | 
 6 | export const toBase64 = (data: string | Uint8Array | null | undefined): string => {
 7 |   if (!data) return '';
 8 | 
 9 |   if (typeof (globalThis as any).Buffer !== 'undefined') {
10 |     return (globalThis as any).Buffer.from(data).toString('base64');
11 |   }
12 | 
13 |   if (typeof data === 'string') {
14 |     data = encodeUTF8(data);
15 |   }
16 | 
17 |   if (typeof btoa !== 'undefined') {
18 |     return btoa(String.fromCharCode.apply(null, data as any));
19 |   }
20 | 
21 |   throw new DodoPaymentsError('Cannot generate base64 string; Expected `Buffer` or `btoa` to be defined');
22 | };
23 | 
24 | export const fromBase64 = (str: string): Uint8Array => {
25 |   if (typeof (globalThis as any).Buffer !== 'undefined') {
26 |     const buf = (globalThis as any).Buffer.from(str, 'base64');
27 |     return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
28 |   }
29 | 
30 |   if (typeof atob !== 'undefined') {
31 |     const bstr = atob(str);
32 |     const buf = new Uint8Array(bstr.length);
33 |     for (let i = 0; i < bstr.length; i++) {
34 |       buf[i] = bstr.charCodeAt(i);
35 |     }
36 |     return buf;
37 |   }
38 | 
39 |   throw new DodoPaymentsError('Cannot decode base64 string; Expected `Buffer` or `atob` to be defined');
40 | };
41 | 
```

--------------------------------------------------------------------------------
/tests/api-resources/payouts.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import DodoPayments from 'dodopayments';
 4 | 
 5 | const client = new DodoPayments({
 6 |   bearerToken: 'My Bearer Token',
 7 |   baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
 8 | });
 9 | 
10 | describe('resource payouts', () => {
11 |   test('list', async () => {
12 |     const responsePromise = client.payouts.list();
13 |     const rawResponse = await responsePromise.asResponse();
14 |     expect(rawResponse).toBeInstanceOf(Response);
15 |     const response = await responsePromise;
16 |     expect(response).not.toBeInstanceOf(Response);
17 |     const dataAndResponse = await responsePromise.withResponse();
18 |     expect(dataAndResponse.data).toBe(response);
19 |     expect(dataAndResponse.response).toBe(rawResponse);
20 |   });
21 | 
22 |   test('list: request options and params are passed correctly', async () => {
23 |     // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error
24 |     await expect(
25 |       client.payouts.list(
26 |         {
27 |           created_at_gte: '2019-12-27T18:11:19.117Z',
28 |           created_at_lte: '2019-12-27T18:11:19.117Z',
29 |           page_number: 0,
30 |           page_size: 0,
31 |         },
32 |         { path: '/_stainless_unknown_path' },
33 |       ),
34 |     ).rejects.toThrow(DodoPayments.NotFoundError);
35 |   });
36 | });
37 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
 1 | # Build stage
 2 | FROM node:20-alpine AS builder
 3 | 
 4 | # Install bash for build script
 5 | RUN apk add --no-cache bash openssl
 6 | 
 7 | # Set working directory
 8 | WORKDIR /build
 9 | 
10 | # Copy entire repository
11 | COPY . .
12 | 
13 | # Install all dependencies and build everything
14 | RUN yarn install --frozen-lockfile && \
15 |     yarn build
16 | 
17 | # Production stage
18 | 
19 |         FROM denoland/deno:alpine
20 |         RUN apk add --no-cache npm
21 | 
22 | # Add non-root user
23 | RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -u 1001
24 | 
25 | # Set working directory
26 | WORKDIR /app
27 | 
28 | # Copy the built mcp-server dist directory
29 | COPY --from=builder /build/packages/mcp-server/dist ./
30 | 
31 | # Copy node_modules from mcp-server (includes all production deps)
32 | COPY --from=builder /build/packages/mcp-server/node_modules ./node_modules
33 | 
34 | # Copy the built dodopayments into node_modules
35 | COPY --from=builder /build/dist ./node_modules/dodopayments
36 | 
37 | # Change ownership to nodejs user
38 | RUN chown -R nodejs:nodejs /app
39 | 
40 | # Switch to non-root user
41 | USER nodejs
42 | 
43 | # The MCP server uses stdio transport by default
44 | # No exposed ports needed for stdio communication
45 | 
46 |     # This is needed for node to run on the deno:alpine image.
47 |     # See <https://github.com/denoland/deno_docker/issues/373>.
48 |     ENV LD_LIBRARY_PATH=/usr/lib:/usr/local/lib
49 | 
50 | # Set the entrypoint to the MCP server
51 | ENTRYPOINT ["node", "index.js"]
52 | 
53 | # Allow passing arguments to the MCP server
54 | CMD []
55 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/manifest.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "dxt_version": "0.2",
 3 |   "name": "dodopayments-mcp",
 4 |   "version": "1.52.5",
 5 |   "description": "The official MCP Server for the Dodo Payments API",
 6 |   "author": {
 7 |     "name": "Dodo Payments",
 8 |     "email": "[email protected]"
 9 |   },
10 |   "repository": {
11 |     "type": "git",
12 |     "url": "git+https://github.com/dodopayments/dodopayments-typescript.git"
13 |   },
14 |   "homepage": "https://github.com/dodopayments/dodopayments-typescript/tree/main/packages/mcp-server#readme",
15 |   "documentation": "https://docs.dodopayments.com/api-reference/introduction",
16 |   "server": {
17 |     "type": "node",
18 |     "entry_point": "index.js",
19 |     "mcp_config": {
20 |       "command": "node",
21 |       "args": ["${__dirname}/index.js"],
22 |       "env": {
23 |         "DODO_PAYMENTS_API_KEY": "${user_config.DODO_PAYMENTS_API_KEY}",
24 |         "DODO_PAYMENTS_WEBHOOK_KEY": "${user_config.DODO_PAYMENTS_WEBHOOK_KEY}"
25 |       }
26 |     }
27 |   },
28 |   "user_config": {
29 |     "DODO_PAYMENTS_API_KEY": {
30 |       "title": "bearer_token",
31 |       "description": "Bearer Token for API authentication",
32 |       "required": true,
33 |       "type": "string"
34 |     },
35 |     "DODO_PAYMENTS_WEBHOOK_KEY": {
36 |       "title": "webhook_key",
37 |       "description": "",
38 |       "required": false,
39 |       "type": "string"
40 |     }
41 |   },
42 |   "tools": [],
43 |   "tools_generated": true,
44 |   "compatibility": {
45 |     "runtimes": {
46 |       "node": ">=18.0.0"
47 |     }
48 |   },
49 |   "keywords": ["api"]
50 | }
51 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/docs-search-tool.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { Metadata, asTextContentResult } from './tools/types';
 4 | 
 5 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
 6 | 
 7 | export const metadata: Metadata = {
 8 |   resource: 'all',
 9 |   operation: 'read',
10 |   tags: [],
11 |   httpMethod: 'get',
12 | };
13 | 
14 | export const tool: Tool = {
15 |   name: 'search_docs',
16 |   description:
17 |     'Search for documentation for how to use the client to interact with the API.\nThe tool will return an array of Markdown-formatted documentation pages.',
18 |   inputSchema: {
19 |     type: 'object',
20 |     properties: {
21 |       query: {
22 |         type: 'string',
23 |         description: 'The query to search for.',
24 |       },
25 |       language: {
26 |         type: 'string',
27 |         description: 'The language for the SDK to search for.',
28 |         enum: ['http', 'python', 'go', 'typescript', 'terraform', 'ruby', 'java', 'kotlin'],
29 |       },
30 |     },
31 |     required: ['query', 'language'],
32 |   },
33 |   annotations: {
34 |     readOnlyHint: true,
35 |   },
36 | };
37 | 
38 | const docsSearchURL =
39 |   process.env['DOCS_SEARCH_URL'] || 'https://api.stainless.com/api/projects/dodo-payments/docs/search';
40 | 
41 | export const handler = async (_: unknown, args: Record<string, unknown> | undefined) => {
42 |   const body = args as any;
43 |   const query = new URLSearchParams(body).toString();
44 |   const result = await fetch(`${docsSearchURL}?${query}`);
45 |   return asTextContentResult(await result.json());
46 | };
47 | 
48 | export default { metadata, tool, handler };
49 | 
```

--------------------------------------------------------------------------------
/release-please-config.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "packages": {
 3 |     ".": {}
 4 |   },
 5 |   "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json",
 6 |   "include-v-in-tag": true,
 7 |   "include-component-in-tag": false,
 8 |   "versioning": "prerelease",
 9 |   "prerelease": true,
10 |   "bump-minor-pre-major": true,
11 |   "bump-patch-for-minor-pre-major": false,
12 |   "pull-request-header": "Automated Release PR",
13 |   "pull-request-title-pattern": "release: ${version}",
14 |   "changelog-sections": [
15 |     {
16 |       "type": "feat",
17 |       "section": "Features"
18 |     },
19 |     {
20 |       "type": "fix",
21 |       "section": "Bug Fixes"
22 |     },
23 |     {
24 |       "type": "perf",
25 |       "section": "Performance Improvements"
26 |     },
27 |     {
28 |       "type": "revert",
29 |       "section": "Reverts"
30 |     },
31 |     {
32 |       "type": "chore",
33 |       "section": "Chores"
34 |     },
35 |     {
36 |       "type": "docs",
37 |       "section": "Documentation"
38 |     },
39 |     {
40 |       "type": "style",
41 |       "section": "Styles"
42 |     },
43 |     {
44 |       "type": "refactor",
45 |       "section": "Refactors"
46 |     },
47 |     {
48 |       "type": "test",
49 |       "section": "Tests",
50 |       "hidden": true
51 |     },
52 |     {
53 |       "type": "build",
54 |       "section": "Build System"
55 |     },
56 |     {
57 |       "type": "ci",
58 |       "section": "Continuous Integration",
59 |       "hidden": true
60 |     }
61 |   ],
62 |   "release-type": "node",
63 |   "extra-files": [
64 |     "src/version.ts",
65 |     "README.md",
66 |     "packages/mcp-server/yarn.lock",
67 |     {
68 |       "type": "json",
69 |       "path": "packages/mcp-server/package.json",
70 |       "jsonpath": "$.version"
71 |     }
72 |   ]
73 | }
74 | 
```

--------------------------------------------------------------------------------
/tests/api-resources/webhooks/headers.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import DodoPayments from 'dodopayments';
 4 | 
 5 | const client = new DodoPayments({
 6 |   bearerToken: 'My Bearer Token',
 7 |   baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
 8 | });
 9 | 
10 | describe('resource headers', () => {
11 |   test('retrieve', async () => {
12 |     const responsePromise = client.webhooks.headers.retrieve('webhook_id');
13 |     const rawResponse = await responsePromise.asResponse();
14 |     expect(rawResponse).toBeInstanceOf(Response);
15 |     const response = await responsePromise;
16 |     expect(response).not.toBeInstanceOf(Response);
17 |     const dataAndResponse = await responsePromise.withResponse();
18 |     expect(dataAndResponse.data).toBe(response);
19 |     expect(dataAndResponse.response).toBe(rawResponse);
20 |   });
21 | 
22 |   test('update: only required params', async () => {
23 |     const responsePromise = client.webhooks.headers.update('webhook_id', { headers: { foo: 'string' } });
24 |     const rawResponse = await responsePromise.asResponse();
25 |     expect(rawResponse).toBeInstanceOf(Response);
26 |     const response = await responsePromise;
27 |     expect(response).not.toBeInstanceOf(Response);
28 |     const dataAndResponse = await responsePromise.withResponse();
29 |     expect(dataAndResponse.data).toBe(response);
30 |     expect(dataAndResponse.response).toBe(rawResponse);
31 |   });
32 | 
33 |   test('update: required and optional params', async () => {
34 |     const response = await client.webhooks.headers.update('webhook_id', { headers: { foo: 'string' } });
35 |   });
36 | });
37 | 
```

--------------------------------------------------------------------------------
/.github/workflows/publish-npm.yml:
--------------------------------------------------------------------------------

```yaml
 1 | # This workflow is triggered when a GitHub release is created.
 2 | # It can also be run manually to re-publish to NPM in case it failed for some reason.
 3 | # You can run this workflow by navigating to https://www.github.com/dodopayments/dodopayments-typescript/actions/workflows/publish-npm.yml
 4 | name: Publish NPM
 5 | on:
 6 |   workflow_dispatch:
 7 |     inputs:
 8 |       path:
 9 |         description: The path to run the release in, e.g. '.' or 'packages/mcp-server'
10 |         required: true
11 | 
12 |   release:
13 |     types: [published]
14 | 
15 | jobs:
16 |   publish:
17 |     name: publish
18 |     runs-on: ubuntu-latest
19 |     permissions:
20 |       contents: write
21 | 
22 |     steps:
23 |       - uses: actions/checkout@v4
24 | 
25 |       - name: Set up Node
26 |         uses: actions/setup-node@v3
27 |         with:
28 |           node-version: '20'
29 | 
30 |       - name: Install dependencies
31 |         run: |
32 |           yarn install
33 | 
34 |       - name: Publish to NPM
35 |         run: |
36 |           if [ -n "${{ github.event.inputs.path }}" ]; then
37 |             PATHS_RELEASED='[\"${{ github.event.inputs.path }}\"]'
38 |           else
39 |             PATHS_RELEASED='[\".\", \"packages/mcp-server\"]'
40 |           fi
41 |           yarn tsn scripts/publish-packages.ts "{ \"paths_released\": \"$PATHS_RELEASED\" }"
42 |         env:
43 |           NPM_TOKEN: ${{ secrets.DODO_PAYMENTS_NPM_TOKEN || secrets.NPM_TOKEN }}
44 | 
45 |       - name: Upload MCP Server DXT GitHub release asset
46 |         run: |
47 |           gh release upload ${{ github.event.release.tag_name }} \
48 |             packages/mcp-server/dodopayments_api.mcpb
49 |         env:
50 |           GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
51 | 
```

--------------------------------------------------------------------------------
/src/internal/parse.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import type { FinalRequestOptions } from './request-options';
 4 | import { type DodoPayments } from '../client';
 5 | import { formatRequestDetails, loggerFor } from './utils/log';
 6 | 
 7 | export type APIResponseProps = {
 8 |   response: Response;
 9 |   options: FinalRequestOptions;
10 |   controller: AbortController;
11 |   requestLogID: string;
12 |   retryOfRequestLogID: string | undefined;
13 |   startTime: number;
14 | };
15 | 
16 | export async function defaultParseResponse<T>(client: DodoPayments, props: APIResponseProps): Promise<T> {
17 |   const { response, requestLogID, retryOfRequestLogID, startTime } = props;
18 |   const body = await (async () => {
19 |     // fetch refuses to read the body when the status code is 204.
20 |     if (response.status === 204) {
21 |       return null as T;
22 |     }
23 | 
24 |     if (props.options.__binaryResponse) {
25 |       return response as unknown as T;
26 |     }
27 | 
28 |     const contentType = response.headers.get('content-type');
29 |     const mediaType = contentType?.split(';')[0]?.trim();
30 |     const isJSON = mediaType?.includes('application/json') || mediaType?.endsWith('+json');
31 |     if (isJSON) {
32 |       const json = await response.json();
33 |       return json as T;
34 |     }
35 | 
36 |     const text = await response.text();
37 |     return text as unknown as T;
38 |   })();
39 |   loggerFor(client).debug(
40 |     `[${requestLogID}] response parsed`,
41 |     formatRequestDetails({
42 |       retryOfRequestLogID,
43 |       url: response.url,
44 |       status: response.status,
45 |       body,
46 |       durationMs: Date.now() - startTime,
47 |     }),
48 |   );
49 |   return body;
50 | }
51 | 
```

--------------------------------------------------------------------------------
/src/resources/webhooks/headers.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { APIResource } from '../../core/resource';
 4 | import { APIPromise } from '../../core/api-promise';
 5 | import { buildHeaders } from '../../internal/headers';
 6 | import { RequestOptions } from '../../internal/request-options';
 7 | import { path } from '../../internal/utils/path';
 8 | 
 9 | export class Headers extends APIResource {
10 |   /**
11 |    * Get a webhook by id
12 |    */
13 |   retrieve(webhookID: string, options?: RequestOptions): APIPromise<HeaderRetrieveResponse> {
14 |     return this._client.get(path`/webhooks/${webhookID}/headers`, options);
15 |   }
16 | 
17 |   /**
18 |    * Patch a webhook by id
19 |    */
20 |   update(webhookID: string, body: HeaderUpdateParams, options?: RequestOptions): APIPromise<void> {
21 |     return this._client.patch(path`/webhooks/${webhookID}/headers`, {
22 |       body,
23 |       ...options,
24 |       headers: buildHeaders([{ Accept: '*/*' }, options?.headers]),
25 |     });
26 |   }
27 | }
28 | 
29 | /**
30 |  * The value of the headers is returned in the `headers` field.
31 |  *
32 |  * Sensitive headers that have been redacted are returned in the sensitive field.
33 |  */
34 | export interface HeaderRetrieveResponse {
35 |   /**
36 |    * List of headers configured
37 |    */
38 |   headers: { [key: string]: string };
39 | 
40 |   /**
41 |    * Sensitive headers without the value
42 |    */
43 |   sensitive: Array<string>;
44 | }
45 | 
46 | export interface HeaderUpdateParams {
47 |   /**
48 |    * Object of header-value pair to update or add
49 |    */
50 |   headers: { [key: string]: string };
51 | }
52 | 
53 | export declare namespace Headers {
54 |   export {
55 |     type HeaderRetrieveResponse as HeaderRetrieveResponse,
56 |     type HeaderUpdateParams as HeaderUpdateParams,
57 |   };
58 | }
59 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/products/list-products.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';
 4 | 
 5 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
 6 | import DodoPayments from 'dodopayments';
 7 | 
 8 | export const metadata: Metadata = {
 9 |   resource: 'products',
10 |   operation: 'read',
11 |   tags: [],
12 |   httpMethod: 'get',
13 |   httpPath: '/products',
14 |   operationId: 'list_products_handler',
15 | };
16 | 
17 | export const tool: Tool = {
18 |   name: 'list_products',
19 |   description: '',
20 |   inputSchema: {
21 |     type: 'object',
22 |     properties: {
23 |       archived: {
24 |         type: 'boolean',
25 |         description: 'List archived products',
26 |       },
27 |       brand_id: {
28 |         type: 'string',
29 |         description: 'filter by Brand id',
30 |       },
31 |       page_number: {
32 |         type: 'integer',
33 |         description: 'Page number default is 0',
34 |       },
35 |       page_size: {
36 |         type: 'integer',
37 |         description: 'Page size default is 10 max is 100',
38 |       },
39 |       recurring: {
40 |         type: 'boolean',
41 |         description:
42 |           'Filter products by pricing type:\n- `true`: Show only recurring pricing products (e.g. subscriptions)\n- `false`: Show only one-time price products\n- `null` or absent: Show both types of products',
43 |       },
44 |     },
45 |     required: [],
46 |   },
47 |   annotations: {
48 |     readOnlyHint: true,
49 |   },
50 | };
51 | 
52 | export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
53 |   const body = args as any;
54 |   const response = await client.products.list(body).asResponse();
55 |   return asTextContentResult(await response.json());
56 | };
57 | 
58 | export default { metadata, tool, handler };
59 | 
```

--------------------------------------------------------------------------------
/src/resources/customers/wallets/wallets.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { APIResource } from '../../../core/resource';
 4 | import * as MiscAPI from '../../misc';
 5 | import * as LedgerEntriesAPI from './ledger-entries';
 6 | import {
 7 |   CustomerWalletTransaction,
 8 |   CustomerWalletTransactionsDefaultPageNumberPagination,
 9 |   LedgerEntries,
10 |   LedgerEntryCreateParams,
11 |   LedgerEntryListParams,
12 | } from './ledger-entries';
13 | import { APIPromise } from '../../../core/api-promise';
14 | import { RequestOptions } from '../../../internal/request-options';
15 | import { path } from '../../../internal/utils/path';
16 | 
17 | export class Wallets extends APIResource {
18 |   ledgerEntries: LedgerEntriesAPI.LedgerEntries = new LedgerEntriesAPI.LedgerEntries(this._client);
19 | 
20 |   list(customerID: string, options?: RequestOptions): APIPromise<WalletListResponse> {
21 |     return this._client.get(path`/customers/${customerID}/wallets`, options);
22 |   }
23 | }
24 | 
25 | export interface CustomerWallet {
26 |   balance: number;
27 | 
28 |   created_at: string;
29 | 
30 |   currency: MiscAPI.Currency;
31 | 
32 |   customer_id: string;
33 | 
34 |   updated_at: string;
35 | }
36 | 
37 | export interface WalletListResponse {
38 |   items: Array<CustomerWallet>;
39 | 
40 |   /**
41 |    * Sum of all wallet balances converted to USD (in smallest unit)
42 |    */
43 |   total_balance_usd: number;
44 | }
45 | 
46 | Wallets.LedgerEntries = LedgerEntries;
47 | 
48 | export declare namespace Wallets {
49 |   export { type CustomerWallet as CustomerWallet, type WalletListResponse as WalletListResponse };
50 | 
51 |   export {
52 |     LedgerEntries as LedgerEntries,
53 |     type CustomerWalletTransaction as CustomerWalletTransaction,
54 |     type CustomerWalletTransactionsDefaultPageNumberPagination as CustomerWalletTransactionsDefaultPageNumberPagination,
55 |     type LedgerEntryCreateParams as LedgerEntryCreateParams,
56 |     type LedgerEntryListParams as LedgerEntryListParams,
57 |   };
58 | }
59 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/subscriptions/list-subscriptions.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';
 4 | 
 5 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
 6 | import DodoPayments from 'dodopayments';
 7 | 
 8 | export const metadata: Metadata = {
 9 |   resource: 'subscriptions',
10 |   operation: 'read',
11 |   tags: [],
12 |   httpMethod: 'get',
13 |   httpPath: '/subscriptions',
14 |   operationId: 'list_subscriptions_handler',
15 | };
16 | 
17 | export const tool: Tool = {
18 |   name: 'list_subscriptions',
19 |   description: '',
20 |   inputSchema: {
21 |     type: 'object',
22 |     properties: {
23 |       brand_id: {
24 |         type: 'string',
25 |         description: 'filter by Brand id',
26 |       },
27 |       created_at_gte: {
28 |         type: 'string',
29 |         description: 'Get events after this created time',
30 |         format: 'date-time',
31 |       },
32 |       created_at_lte: {
33 |         type: 'string',
34 |         description: 'Get events created before this time',
35 |         format: 'date-time',
36 |       },
37 |       customer_id: {
38 |         type: 'string',
39 |         description: 'Filter by customer id',
40 |       },
41 |       page_number: {
42 |         type: 'integer',
43 |         description: 'Page number default is 0',
44 |       },
45 |       page_size: {
46 |         type: 'integer',
47 |         description: 'Page size default is 10 max is 100',
48 |       },
49 |       status: {
50 |         type: 'string',
51 |         description: 'Filter by status',
52 |         enum: ['pending', 'active', 'on_hold', 'cancelled', 'failed', 'expired'],
53 |       },
54 |     },
55 |     required: [],
56 |   },
57 |   annotations: {
58 |     readOnlyHint: true,
59 |   },
60 | };
61 | 
62 | export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
63 |   const body = args as any;
64 |   const response = await client.subscriptions.list(body).asResponse();
65 |   return asTextContentResult(await response.json());
66 | };
67 | 
68 | export default { metadata, tool, handler };
69 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/cloudflare-worker/static/home.md:
--------------------------------------------------------------------------------

```markdown
 1 | # DodoPayments Remote MCP Server
 2 | 
 3 | This MCP server is available at:
 4 | 
 5 | ```
 6 | {{cloudflareWorkerUrl}}
 7 | ```
 8 | 
 9 | ### Claude.ai
10 | 
11 | To connect Claude Web to this MCP server:
12 | 
13 | 1. Open Claude Web
14 | 2. Go to Settings -> Connectors
15 | 3. Click "+ Add Custom Connector"
16 | 4. Add the MCP server URL: `{{cloudflareWorkerUrl}}`
17 | 
18 | ### Claude Desktop
19 | 
20 | Claude Desktop requires using the `mcp-remote` package to connect to remote MCP servers:
21 | 
22 | 1. Edit your Claude Desktop configuration file:
23 |    - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
24 |    - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
25 | 2. Add the following configuration:
26 | 
27 | ```json
28 | {
29 |   "mcpServers": {
30 |     "dodopayments_api": {
31 |       "command": "npx",
32 |       "args": ["-y", "mcp-remote@latest", "{{cloudflareWorkerUrl}}"]
33 |     }
34 |   }
35 | }
36 | ```
37 | 
38 | 3. Restart Claude Desktop to see the MCP server connection (look for the hammer icon)
39 | 
40 | ### Cursor
41 | 
42 | 1. Edit your Cursor MCP configuration file at `~/.cursor/mcp.json`
43 | 2. Add the following configuration:
44 | 
45 | ```json
46 | {
47 |   "mcpServers": {
48 |     "dodopayments_api": {
49 |       "command": "npx",
50 |       "args": ["-y", "mcp-remote@latest", "{{cloudflareWorkerUrl}}"]
51 |     }
52 |   }
53 | }
54 | ```
55 | 
56 | ### Windsurf
57 | 
58 | 1. Edit your Windsurf configuration file at `~/.codeium/windsurf/mcp_config.json`
59 | 2. Add the following configuration:
60 | 
61 | ```json
62 | {
63 |   "mcpServers": {
64 |     "dodopayments_api": {
65 |       "command": "npx",
66 |       "args": ["-y", "mcp-remote@latest", "{{cloudflareWorkerUrl}}"]
67 |     }
68 |   }
69 | }
70 | ```
71 | 
72 | ## Troubleshooting
73 | 
74 | If you encounter issues connecting:
75 | 
76 | 1. Ensure you have Node.js 18 or higher installed
77 | 2. Try clearing MCP authentication cache: `rm -rf ~/.mcp-auth`
78 | 3. Restart your MCP client application
79 | 4. Check client logs for error messages
80 | 
81 | ## Learn More
82 | 
83 | For more information about MCP:
84 | 
85 | - [MCP Introduction](https://modelcontextprotocol.io/introduction)
86 | - [mcp-remote package](https://www.npmjs.com/package/mcp-remote)
87 | 
```

--------------------------------------------------------------------------------
/tests/form.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { multipartFormRequestOptions, createForm } from 'dodopayments/internal/uploads';
 2 | import { toFile } from 'dodopayments/core/uploads';
 3 | 
 4 | describe('form data validation', () => {
 5 |   test('valid values do not error', async () => {
 6 |     await multipartFormRequestOptions(
 7 |       {
 8 |         body: {
 9 |           foo: 'foo',
10 |           string: 1,
11 |           bool: true,
12 |           file: await toFile(Buffer.from('some-content')),
13 |           blob: new Blob(['Some content'], { type: 'text/plain' }),
14 |         },
15 |       },
16 |       fetch,
17 |     );
18 |   });
19 | 
20 |   test('null', async () => {
21 |     await expect(() =>
22 |       multipartFormRequestOptions(
23 |         {
24 |           body: {
25 |             null: null,
26 |           },
27 |         },
28 |         fetch,
29 |       ),
30 |     ).rejects.toThrow(TypeError);
31 |   });
32 | 
33 |   test('undefined is stripped', async () => {
34 |     const form = await createForm(
35 |       {
36 |         foo: undefined,
37 |         bar: 'baz',
38 |       },
39 |       fetch,
40 |     );
41 |     expect(form.has('foo')).toBe(false);
42 |     expect(form.get('bar')).toBe('baz');
43 |   });
44 | 
45 |   test('nested undefined property is stripped', async () => {
46 |     const form = await createForm(
47 |       {
48 |         bar: {
49 |           baz: undefined,
50 |         },
51 |       },
52 |       fetch,
53 |     );
54 |     expect(Array.from(form.entries())).toEqual([]);
55 | 
56 |     const form2 = await createForm(
57 |       {
58 |         bar: {
59 |           foo: 'string',
60 |           baz: undefined,
61 |         },
62 |       },
63 |       fetch,
64 |     );
65 |     expect(Array.from(form2.entries())).toEqual([['bar[foo]', 'string']]);
66 |   });
67 | 
68 |   test('nested undefined array item is stripped', async () => {
69 |     const form = await createForm(
70 |       {
71 |         bar: [undefined, undefined],
72 |       },
73 |       fetch,
74 |     );
75 |     expect(Array.from(form.entries())).toEqual([]);
76 | 
77 |     const form2 = await createForm(
78 |       {
79 |         bar: [undefined, 'foo'],
80 |       },
81 |       fetch,
82 |     );
83 |     expect(Array.from(form2.entries())).toEqual([['bar[]', 'foo']]);
84 |   });
85 | });
86 | 
```

--------------------------------------------------------------------------------
/tests/api-resources/disputes.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import DodoPayments from 'dodopayments';
 4 | 
 5 | const client = new DodoPayments({
 6 |   bearerToken: 'My Bearer Token',
 7 |   baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
 8 | });
 9 | 
10 | describe('resource disputes', () => {
11 |   test('retrieve', async () => {
12 |     const responsePromise = client.disputes.retrieve('dispute_id');
13 |     const rawResponse = await responsePromise.asResponse();
14 |     expect(rawResponse).toBeInstanceOf(Response);
15 |     const response = await responsePromise;
16 |     expect(response).not.toBeInstanceOf(Response);
17 |     const dataAndResponse = await responsePromise.withResponse();
18 |     expect(dataAndResponse.data).toBe(response);
19 |     expect(dataAndResponse.response).toBe(rawResponse);
20 |   });
21 | 
22 |   test('list', async () => {
23 |     const responsePromise = client.disputes.list();
24 |     const rawResponse = await responsePromise.asResponse();
25 |     expect(rawResponse).toBeInstanceOf(Response);
26 |     const response = await responsePromise;
27 |     expect(response).not.toBeInstanceOf(Response);
28 |     const dataAndResponse = await responsePromise.withResponse();
29 |     expect(dataAndResponse.data).toBe(response);
30 |     expect(dataAndResponse.response).toBe(rawResponse);
31 |   });
32 | 
33 |   test('list: request options and params are passed correctly', async () => {
34 |     // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error
35 |     await expect(
36 |       client.disputes.list(
37 |         {
38 |           created_at_gte: '2019-12-27T18:11:19.117Z',
39 |           created_at_lte: '2019-12-27T18:11:19.117Z',
40 |           customer_id: 'customer_id',
41 |           dispute_stage: 'pre_dispute',
42 |           dispute_status: 'dispute_opened',
43 |           page_number: 0,
44 |           page_size: 0,
45 |         },
46 |         { path: '/_stainless_unknown_path' },
47 |       ),
48 |     ).rejects.toThrow(DodoPayments.NotFoundError);
49 |   });
50 | });
51 | 
```

--------------------------------------------------------------------------------
/tests/base64.test.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { fromBase64, toBase64 } from 'dodopayments/internal/utils/base64';
 2 | 
 3 | describe.each(['Buffer', 'atob'])('with %s', (mode) => {
 4 |   let originalBuffer: BufferConstructor;
 5 |   beforeAll(() => {
 6 |     if (mode === 'atob') {
 7 |       originalBuffer = globalThis.Buffer;
 8 |       // @ts-expect-error Can't assign undefined to BufferConstructor
 9 |       delete globalThis.Buffer;
10 |     }
11 |   });
12 |   afterAll(() => {
13 |     if (mode === 'atob') {
14 |       globalThis.Buffer = originalBuffer;
15 |     }
16 |   });
17 |   test('toBase64', () => {
18 |     const testCases = [
19 |       {
20 |         input: 'hello world',
21 |         expected: 'aGVsbG8gd29ybGQ=',
22 |       },
23 |       {
24 |         input: new Uint8Array([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]),
25 |         expected: 'aGVsbG8gd29ybGQ=',
26 |       },
27 |       {
28 |         input: undefined,
29 |         expected: '',
30 |       },
31 |       {
32 |         input: new Uint8Array([
33 |           229, 102, 215, 230, 65, 22, 46, 87, 243, 176, 99, 99, 31, 174, 8, 242, 83, 142, 169, 64, 122, 123,
34 |           193, 71,
35 |         ]),
36 |         expected: '5WbX5kEWLlfzsGNjH64I8lOOqUB6e8FH',
37 |       },
38 |       {
39 |         input: '✓',
40 |         expected: '4pyT',
41 |       },
42 |       {
43 |         input: new Uint8Array([226, 156, 147]),
44 |         expected: '4pyT',
45 |       },
46 |     ];
47 | 
48 |     testCases.forEach(({ input, expected }) => {
49 |       expect(toBase64(input)).toBe(expected);
50 |     });
51 |   });
52 | 
53 |   test('fromBase64', () => {
54 |     const testCases = [
55 |       {
56 |         input: 'aGVsbG8gd29ybGQ=',
57 |         expected: new Uint8Array([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]),
58 |       },
59 |       {
60 |         input: '',
61 |         expected: new Uint8Array([]),
62 |       },
63 |       {
64 |         input: '5WbX5kEWLlfzsGNjH64I8lOOqUB6e8FH',
65 |         expected: new Uint8Array([
66 |           229, 102, 215, 230, 65, 22, 46, 87, 243, 176, 99, 99, 31, 174, 8, 242, 83, 142, 169, 64, 122, 123,
67 |           193, 71,
68 |         ]),
69 |       },
70 |       {
71 |         input: '4pyT',
72 |         expected: new Uint8Array([226, 156, 147]),
73 |       },
74 |     ];
75 | 
76 |     testCases.forEach(({ input, expected }) => {
77 |       expect(fromBase64(input)).toEqual(expected);
78 |     });
79 |   });
80 | });
81 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/licenses/validate-licenses.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { maybeFilter } from 'dodopayments-mcp/filtering';
 4 | import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';
 5 | 
 6 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
 7 | import DodoPayments from 'dodopayments';
 8 | 
 9 | export const metadata: Metadata = {
10 |   resource: 'licenses',
11 |   operation: 'write',
12 |   tags: [],
13 |   httpMethod: 'post',
14 |   httpPath: '/licenses/validate',
15 |   operationId: 'validate_license_key',
16 | };
17 | 
18 | export const tool: Tool = {
19 |   name: 'validate_licenses',
20 |   description:
21 |     "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\n\n\n# Response Schema\n```json\n{\n  $ref: '#/$defs/license_validate_response',\n  $defs: {\n    license_validate_response: {\n      type: 'object',\n      properties: {\n        valid: {\n          type: 'boolean'\n        }\n      },\n      required: [        'valid'\n      ]\n    }\n  }\n}\n```",
22 |   inputSchema: {
23 |     type: 'object',
24 |     properties: {
25 |       license_key: {
26 |         type: 'string',
27 |       },
28 |       license_key_instance_id: {
29 |         type: 'string',
30 |       },
31 |       jq_filter: {
32 |         type: 'string',
33 |         title: 'jq Filter',
34 |         description:
35 |           'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).',
36 |       },
37 |     },
38 |     required: ['license_key'],
39 |   },
40 |   annotations: {},
41 | };
42 | 
43 | export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
44 |   const { jq_filter, ...body } = args as any;
45 |   return asTextContentResult(await maybeFilter(jq_filter, await client.licenses.validate(body)));
46 | };
47 | 
48 | export default { metadata, tool, handler };
49 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/addons/update-images-addons.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { maybeFilter } from 'dodopayments-mcp/filtering';
 4 | import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';
 5 | 
 6 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
 7 | import DodoPayments from 'dodopayments';
 8 | 
 9 | export const metadata: Metadata = {
10 |   resource: 'addons',
11 |   operation: 'write',
12 |   tags: [],
13 |   httpMethod: 'put',
14 |   httpPath: '/addons/{id}/images',
15 |   operationId: 'update_addon_image',
16 | };
17 | 
18 | export const tool: Tool = {
19 |   name: 'update_images_addons',
20 |   description:
21 |     "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\n\n\n# Response Schema\n```json\n{\n  $ref: '#/$defs/addon_update_images_response',\n  $defs: {\n    addon_update_images_response: {\n      type: 'object',\n      properties: {\n        image_id: {\n          type: 'string'\n        },\n        url: {\n          type: 'string'\n        }\n      },\n      required: [        'image_id',\n        'url'\n      ]\n    }\n  }\n}\n```",
22 |   inputSchema: {
23 |     type: 'object',
24 |     properties: {
25 |       id: {
26 |         type: 'string',
27 |       },
28 |       jq_filter: {
29 |         type: 'string',
30 |         title: 'jq Filter',
31 |         description:
32 |           'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).',
33 |       },
34 |     },
35 |     required: ['id'],
36 |   },
37 |   annotations: {
38 |     idempotentHint: true,
39 |   },
40 | };
41 | 
42 | export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
43 |   const { id, jq_filter, ...body } = args as any;
44 |   return asTextContentResult(await maybeFilter(jq_filter, await client.addons.updateImages(id)));
45 | };
46 | 
47 | export default { metadata, tool, handler };
48 | 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "dodopayments",
 3 |   "version": "2.3.1",
 4 |   "description": "The official TypeScript library for the Dodo Payments API",
 5 |   "author": "Dodo Payments <[email protected]>",
 6 |   "types": "dist/index.d.ts",
 7 |   "main": "dist/index.js",
 8 |   "type": "commonjs",
 9 |   "repository": "github:dodopayments/dodopayments-typescript",
10 |   "license": "Apache-2.0",
11 |   "packageManager": "[email protected]",
12 |   "files": [
13 |     "**/*"
14 |   ],
15 |   "private": false,
16 |   "publishConfig": {
17 |     "access": "public"
18 |   },
19 |   "scripts": {
20 |     "test": "./scripts/test",
21 |     "build": "./scripts/build",
22 |     "prepublishOnly": "echo 'to publish, run yarn build && (cd dist; yarn publish)' && exit 1",
23 |     "format": "./scripts/format",
24 |     "prepare": "if ./scripts/utils/check-is-in-git-install.sh; then ./scripts/build && ./scripts/utils/git-swap.sh; fi",
25 |     "tsn": "ts-node -r tsconfig-paths/register",
26 |     "lint": "./scripts/lint",
27 |     "fix": "./scripts/format"
28 |   },
29 |   "dependencies": {
30 |     "standardwebhooks": "^1.0.0"
31 |   },
32 |   "devDependencies": {
33 |     "@arethetypeswrong/cli": "^0.17.0",
34 |     "@swc/core": "^1.3.102",
35 |     "@swc/jest": "^0.2.29",
36 |     "@types/jest": "^29.4.0",
37 |     "@types/node": "^20.17.6",
38 |     "@typescript-eslint/eslint-plugin": "8.31.1",
39 |     "@typescript-eslint/parser": "8.31.1",
40 |     "eslint": "^9.20.1",
41 |     "eslint-plugin-prettier": "^5.4.1",
42 |     "eslint-plugin-unused-imports": "^4.1.4",
43 |     "iconv-lite": "^0.6.3",
44 |     "jest": "^29.4.0",
45 |     "prettier": "^3.0.0",
46 |     "publint": "^0.2.12",
47 |     "ts-jest": "^29.1.0",
48 |     "ts-node": "^10.5.0",
49 |     "tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.9/tsc-multi.tgz",
50 |     "tsconfig-paths": "^4.0.0",
51 |     "tslib": "^2.8.1",
52 |     "typescript": "5.8.3",
53 |     "typescript-eslint": "8.31.1"
54 |   },
55 |   "bin": {
56 |     "dodopayments": "bin/cli"
57 |   },
58 |   "exports": {
59 |     ".": {
60 |       "import": "./dist/index.mjs",
61 |       "require": "./dist/index.js"
62 |     },
63 |     "./*.mjs": {
64 |       "default": "./dist/*.mjs"
65 |     },
66 |     "./*.js": {
67 |       "default": "./dist/*.js"
68 |     },
69 |     "./*": {
70 |       "import": "./dist/*.mjs",
71 |       "require": "./dist/*.js"
72 |     }
73 |   }
74 | }
75 | 
```

--------------------------------------------------------------------------------
/packages/mcp-server/src/tools/webhooks/retrieve-secret-webhooks.ts:
--------------------------------------------------------------------------------

```typescript
 1 | // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
 2 | 
 3 | import { maybeFilter } from 'dodopayments-mcp/filtering';
 4 | import { Metadata, asTextContentResult } from 'dodopayments-mcp/tools/types';
 5 | 
 6 | import { Tool } from '@modelcontextprotocol/sdk/types.js';
 7 | import DodoPayments from 'dodopayments';
 8 | 
 9 | export const metadata: Metadata = {
10 |   resource: 'webhooks',
11 |   operation: 'read',
12 |   tags: [],
13 |   httpMethod: 'get',
14 |   httpPath: '/webhooks/{webhook_id}/secret',
15 |   operationId: 'get_webhook_secret',
16 | };
17 | 
18 | export const tool: Tool = {
19 |   name: 'retrieve_secret_webhooks',
20 |   description:
21 |     "When using this tool, always use the `jq_filter` parameter to reduce the response size and improve performance.\n\nOnly omit if you're sure you don't need the data.\n\nGet webhook secret by id\n\n# Response Schema\n```json\n{\n  $ref: '#/$defs/webhook_retrieve_secret_response',\n  $defs: {\n    webhook_retrieve_secret_response: {\n      type: 'object',\n      properties: {\n        secret: {\n          type: 'string'\n        }\n      },\n      required: [        'secret'\n      ]\n    }\n  }\n}\n```",
22 |   inputSchema: {
23 |     type: 'object',
24 |     properties: {
25 |       webhook_id: {
26 |         type: 'string',
27 |       },
28 |       jq_filter: {
29 |         type: 'string',
30 |         title: 'jq Filter',
31 |         description:
32 |           'A jq filter to apply to the response to include certain fields. Consult the output schema in the tool description to see the fields that are available.\n\nFor example: to include only the `name` field in every object of a results array, you can provide ".results[].name".\n\nFor more information, see the [jq documentation](https://jqlang.org/manual/).',
33 |       },
34 |     },
35 |     required: ['webhook_id'],
36 |   },
37 |   annotations: {
38 |     readOnlyHint: true,
39 |   },
40 | };
41 | 
42 | export const handler = async (client: DodoPayments, args: Record<string, unknown> | undefined) => {
43 |   const { webhook_id, jq_filter, ...body } = args as any;
44 |   return asTextContentResult(await maybeFilter(jq_filter, await client.webhooks.retrieveSecret(webhook_id)));
45 | };
46 | 
47 | export default { metadata, tool, handler };
48 | 
```
Page 1/10FirstPrevNextLast