plaid-devx-mcp
# plaid-devx-mcp
A sandbox-first [Model Context Protocol](https://modelcontextprotocol.io) server for developers integrating [Plaid](https://plaid.com). Twenty tools in two groups:
- **13 sandbox integration tools.** Create a test Item, read accounts, balances, auth numbers, identity, sync transactions with cursor paging, break and repair the login, fire webhooks, search institutions, create Link tokens. Built on the official `plaid` Node SDK. Access tokens stay in a server-side vault; the model only ever sees an `item_id`.
- **7 docs-quality tools.** Search and describe every endpoint from the official OpenAPI spec, generate minimal curl and Node requests, search and fetch plaid.com/docs as Markdown, and run a **drift check** that cross-references the OpenAPI spec, the official Postman collection and the docs site. No credentials needed for this group.
No keys are stored in this repo. The server reads `PLAID_CLIENT_ID` and `PLAID_SECRET` from the environment and refuses `PLAID_ENV=production` unless you also set `PLAID_ALLOW_PRODUCTION=1`.
## Why this exists
Plaid's Developer Relations team owns the docs, the sample apps, the Postman collection and the feedback loop from developers back to Product. Every one of those is a thing that drifts: an endpoint ships in the OpenAPI spec before its docs anchor exists, a Postman request keeps calling an endpoint after it is deprecated, a `externalDocs` link points at a page that now 404s. Plaid's own MCP server (the [Dashboard MCP](https://plaid.com/docs/resources/mcp/)) covers production diagnostics; the twenty-odd community `plaid-mcp` repos all do the same thing, connect my bank and read my transactions. Nothing covered the developer's first hour in sandbox, and nothing treated the docs surface as something you can test. This server does both, and the drift check is the release checklist I would run at Plaid every week.
## Install
Requires Node 20+.
```sh
git clone https://github.com/ExpertVagabond/plaid-devx-mcp
cd plaid-devx-mcp
npm install
npm run build
```
Free sandbox credentials: sign up at [dashboard.plaid.com/signup](https://dashboard.plaid.com/signup), open **Developers > Keys**, copy the `client_id` and the **Sandbox** secret. Sandbox is free and needs no production approval.
Claude Desktop / Claude Code / Cursor config:
```json
{
"mcpServers": {
"plaid-devx": {
"command": "node",
"args": ["/absolute/path/to/plaid-devx-mcp/dist/index.js"],
"env": {
"PLAID_CLIENT_ID": "your_client_id",
"PLAID_SECRET": "your_sandbox_secret",
"PLAID_ENV": "sandbox"
}
}
}
}
```
Leave the `env` block out and the seven docs tools still work; the sandbox tools return a message telling you where to get keys.
Environment variables:
| variable | default | purpose |
|---|---|---|
| `PLAID_CLIENT_ID`, `PLAID_SECRET` | unset | Plaid API keys. Sandbox secret recommended. |
| `PLAID_ENV` | `sandbox` | `sandbox` or `production`. Production also needs `PLAID_ALLOW_PRODUCTION=1`. |
| `PLAID_DEVX_CACHE` | `~/.cache/plaid-devx-mcp` | Disk cache for the OpenAPI spec (3 MB), Postman collection (1.6 MB) and llms-full.txt (6.5 MB); 24 h TTL, stale copy used if offline. |
## Tools
### Sandbox integration (credentials required)
| tool | Plaid endpoint(s) | what it does |
|---|---|---|
| `plaid_sandbox_item_create` | `/sandbox/public_token/create` + `/item/public_token/exchange` | One call to a working Item. Supports `override_username` (`user_custom`, `user_transactions_dynamic`), products, webhook URL. Returns `item_id`; the access token stays in the vault. |
| `plaid_items_list` | (local) | Items created this session, no tokens printed. |
| `plaid_accounts_get` | `/accounts/get` | Accounts with type, subtype, mask, balances. |
| `plaid_balance_get` | `/accounts/balance/get` | Real-time balances, optional `account_ids` filter. |
| `plaid_transactions_sync` | `/transactions/sync` | Follows `has_more` until complete, returns added/modified/removed plus `next_cursor`. `max_pages` safety cap. |
| `plaid_auth_get` | `/auth/get` | ACH, wire, EFT, BACS numbers. |
| `plaid_identity_get` | `/identity/get` | Account-holder names, emails, phones, addresses. |
| `plaid_item_get` | `/item/get` | Item status, products, consent expiry, current error. |
| `plaid_item_remove` | `/item/remove` | Invalidates the token and forgets it. |
| `plaid_sandbox_fire_webhook` | `/sandbox/item/fire_webhook` | Fire `SYNC_UPDATES_AVAILABLE`, `DEFAULT_UPDATE`, etc. at your webhook. |
| `plaid_sandbox_reset_login` | `/sandbox/item/reset_login` | Force `ITEM_LOGIN_REQUIRED` to test update mode. |
| `plaid_institutions_search` | `/institutions/search` | Find `institution_id`, OAuth flag, supported products. |
| `plaid_link_token_create` | `/link/token/create` | For the real Link UI flow (web/mobile). |
Every Plaid error comes back as `error_type` / `error_code` / `error_message` / `request_id` with a link to the errors reference, not as a stack trace.
### Docs quality (no credentials)
| tool | source | what it does |
|---|---|---|
| `plaid_api_search` | OpenAPI | Keyword search across path, summary, description. Deprecated hidden by default. |
| `plaid_api_describe` | OpenAPI | Request fields with required flag, type, enum; 200 example; docs link; deprecation. |
| `plaid_api_request_example` | OpenAPI | Minimal valid body from the required fields, rendered as curl and as `plaid` Node code against sandbox. |
| `plaid_api_stats` | OpenAPI | Spec version, live/deprecated/hidden counts, all 31 sandbox-only endpoints. |
| `plaid_docs_search` | plaid.com/docs/llms.txt | Search the page index Plaid publishes for LLMs. |
| `plaid_docs_fetch` | plaid.com/docs/*/index.html.md | Fetch any docs page as Markdown, paged by offset. |
| `plaid_drift_check` | OpenAPI + Postman + docs | Endpoints missing from Postman, Postman hitting deprecated or unknown endpoints, placeholder or missing `externalDocs`, endpoints absent from llms-full.txt, and with `live: true` the 404 pages and missing anchors. Markdown or JSON. |
Plus one MCP prompt (`plaid-quickstart`) and one resource (`plaid-devx://quickstart`).
## Five-prompt walkthrough
With the server connected and sandbox keys set, ask your MCP client:
1. **"Create a sandbox Item at First Platypus Bank with transactions and auth."**
`plaid_sandbox_item_create` runs `/sandbox/public_token/create` then `/item/public_token/exchange` and returns `item_id`.
2. **"List its accounts and the routing numbers for the checking account."**
`plaid_accounts_get` then `plaid_auth_get`. Sandbox routing number is `011401533`.
3. **"Sync all transactions and total the spend by category."**
`plaid_transactions_sync` pages through `has_more`; the model sums `personal_finance_category.primary`. On a brand-new Item the first sync can be empty for a few seconds while Plaid fetches history; ask again.
4. **"Simulate the user changing their bank password, then show me the Item error and what my app should do."**
`plaid_sandbox_reset_login` then `plaid_item_get` shows `ITEM_LOGIN_REQUIRED`; the fix is Link update mode with a new `link_token` (`plaid_link_token_create` with an `access_token`).
5. **"Run the docs drift check and give me the three fixes you would ship first."**
`plaid_drift_check` with `live: true`. See the findings below for what it returns today.
Or use the `plaid-quickstart` prompt, which sends all five in order.
## What the drift check found (2026-09-14, OpenAPI 2020-09-14_1.740.1)
Full report: [`reports/drift-2026-09-14.md`](reports/drift-2026-09-14.md). Highlights:
- **92 live endpoints have no request in the official Postman collection** (262 requests, 210 unique paths, against 296 live endpoints). The whole `/cashflow_report/*`, `/protect/*`, `/cra/loans/*` and `/fdx/*` families are absent.
- **5 Postman requests call endpoints the spec marks deprecated** (`/categories/get`, three `/cra/monitoring_insights/*`, `/credit/bank_income/refresh`), and **1 Postman request calls a path that is not in the spec at all** (`/transfer/platform/document/submit`).
- **15 live endpoints have `externalDocs: none`** as a placeholder and **18 have no `externalDocs`** at all, so SDKs generated from the spec ship without a docs link for 33 endpoints.
- **4 `externalDocs` pages return 404** (`/api/products/beta/`, `/api/products/issues`, `/api/profile/`), covering 10 endpoints.
- **36 `externalDocs` anchors do not exist on the page they point at.** The four `/cashflow_report/*` endpoints use camelCase anchors (`#cashflowReportGet`) while every other anchor on that page is lowercase; `/cashflow_report` does not appear anywhere in docs/llms-full.txt either.
- **76 live endpoints are never mentioned in `docs/llms-full.txt`**, the file Plaid publishes for LLM consumption.
- Three docs pages are referenced both with and without a trailing slash (`/api/products/auth`, `/api/products/issues`, `/api/products/signal`).
Method note: "anchor missing" means the `id` is absent from the server-rendered HTML of a page that does contain ids for its other endpoints. Deprecated and `x-hidden-from-docs` endpoints are excluded from every count.
## Tests
```
$ npm test
✓ tests/spec-units.test.ts (7 tests) 8ms
✓ tests/sandbox-tools.test.ts (21 tests) 132ms
✓ tests/docs-tools.test.ts (12 tests) 123ms
Test Files 3 passed (3)
Tests 40 passed (40)
$ npx tsc --noEmit # clean
```
The sandbox tools are tested end to end through a real MCP client over an in-memory transport against a local `node:http` mock of sandbox.plaid.com (`tests/helpers/mock-plaid.ts`), so request bodies, headers and error mapping are asserted, not stubbed. The docs tools run against a 14-endpoint subset of the real spec (`tests/fixtures/mini-openapi.yml`, generated by `scripts/make-fixture.py`) and a fake fetcher.
Live sandbox: `PLAID_CLIENT_ID=... PLAID_SECRET=... npm run smoke` runs the five-prompt walkthrough against sandbox.plaid.com and prints PASS/FAIL per step. `npm run drift` regenerates the report from live sources.
## Security notes
- The access token is never returned in a tool result. Tools take `item_id`; the vault is process memory and dies with the server.
- `plaid_docs_fetch` only fetches `https://plaid.com/` URLs.
- Production is opt-in twice (`PLAID_ENV=production` and `PLAID_ALLOW_PRODUCTION=1`).
- Balance calls are billable in production; the tool description says so.
## License
MIT. Not affiliated with Plaid Inc. Plaid, the OpenAPI spec and the Postman collection are Plaid's; this project only reads them.
TDQS
Scored across 20 tools
Each tool targets a distinct resource and action: core data endpoints (accounts, balance, transactions, auth, identity), item lifecycle (create, get, list, remove), sandbox testing controls (fire webhook, reset login), API spec exploration (search, describe, request example, stats), docs access, and a drift check. Even similar tools like accounts_get vs balance_get are clearly differentiated by purpose and description.
All tools consistently use the 'plaid_' prefix followed by a noun phrase and an action verb (e.g., plaid_accounts_get, plaid_transactions_sync, plaid_sandbox_item_create). The naming pattern is uniform and predictable, making it easy to infer tool purpose from the name alone.
With 20 tools, the server covers a broad set of Plaid development features without feeling bloated. Each tool has a clear role in the developer workflow—from item creation to sandbox controls, API exploration, and documentation—so the count is well-scoped and appropriate for the server's purpose.
The tool surface covers the full lifecycle: item creation (via sandbox or Link), data retrieval for accounts, balances, transactions, auth, and identity, item management (get, list, remove), sandbox testing (webhook firing, login reset), API spec introspection, docs access, and release-readiness drift checking. No obvious gaps are apparent for commonly used Plaid endpoints.