schwab-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@schwab-mcpShow me my current account balances"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
schwab-mcp
A private, self-hosted Schwab MCP server for Cloudflare Workers. It uses the current stateless Streamable HTTP transport at /mcp, Cloudflare Access for owner authentication, and a small first-party Schwab HTTP client.
This project is not affiliated with or endorsed by Charles Schwab, Cloudflare, or OpenAI. Brokerage activity involves risk. Review every live order yourself.
Security model
ChatGPT/Codex
│ MCP OAuth 2.1 + PKCE
▼
Cloudflare Worker ── Cloudflare Access OIDC ── exact owner email
│
├── /mcp (authenticated Streamable HTTP)
├── OAuth provider metadata (Cloudflare KV)
└── encrypted flow state and Schwab tokens (single Durable Object)
│
▼
Schwab Trader/Market APIsThe repository contains no credentials. The Worker checks the configured email after cryptographically verifying the Cloudflare Access ID token, including its signature, issuer, audience, authorized party, nonce, and lifetime. OAuth and approval state is encrypted and atomically consumed in Durable Object storage. Schwab tokens are encrypted with AES-256-GCM before Durable Object storage. Raw account numbers are redacted from MCP responses.
OAuth clients must use a Client ID Metadata Document (CIMD). Anonymous dynamic client registration is intentionally disabled, so arbitrary visitors cannot create persistent OAuth clients in the deployment. The consent page displays the client ID, exact redirect URI, and requested scopes before authentication. MCP access tokens last one hour and refresh tokens last seven days.
Cloudflare Access must also have an Allow policy containing only the owner's exact email. The code-level email check is a second independent guard.
Related MCP server: Schwab MCP Server
Trading modes
Mode | Preparation tools | Schwab write requests |
| Not registered | Impossible through MCP |
| Registered | Never sent |
| Requires | Only after one-time browser approval |
The checked-in default is disabled. In live mode, preparing an order creates a ten-minute approval URL. Opening it requires a fresh Cloudflare Access login; the page shows the exact action and digest. Approval is one-time. The execution tool remains guarded by a second runtime mode check.
Use separate Worker names, KV namespaces, and Schwab applications for read-only, preview, and live deployments. The example configurations intentionally use placeholders:
wrangler.jsonc— read-only defaultwrangler.preview.jsonc— dry-run order preparationwrangler.live.jsonc— live order support
Implemented tools
The disabled/read-only deployment registers 19 tools:
connection status and allowed account hashes/fingerprints
accounts, balances, and optional positions
bulk and single-symbol quotes, including extended and fundamental fields
price history with validated period/frequency combinations
paged or raw option chains, preserving Schwab-provided Greeks, implied volatility, theoretical values, and market timestamps; plus expiration calendars
market movers and market hours
paged instrument search and CUSIP lookup
account-scoped order lists and individual orders
account-scoped transaction lists and individual transactions
sanitized market-data permissions and streaming availability
Cross-account reads are implemented as separate account-scoped Schwab requests for only the hashes admitted by the deployment allowlist. User preferences omit account numbers, nicknames, streamer URLs, customer IDs, and correlation IDs.
Large quotes, candles, positions, orders, transactions, instrument searches, and option chains use bounded output pages. Pass each opaque nextCursor into the next call until it is null; cursor keys are based on stable Schwab identifiers instead of array offsets and are bound to the exact tool and normalized query. A cursor from another query is rejected. Each page is still a fresh Schwab request, so fetchedAt and Schwab-provided timestamps identify when values were observed. Records created after a traversal starts may appear on a later page, but inserting them cannot shift the continuation point. An individually oversized record is omitted with its stable key and size in oversizedItems, while the cursor advances so later records remain readable. outputMode=raw is available for queries already narrowed enough to remain below the MCP response limit.
When enabled, three trading tools prepare place, replace, and cancel actions. The typed order schema supports common equity, mutual-fund, option, multi-leg, trailing-stop, OCO, and trigger fields. Only a live deployment with the mcp:trade scope registers schwab_execute_approved_order, for a total of 23 tools.
Continuous Schwab Streamer subscriptions are intentionally not included. Cloudflare cannot hibernate a Durable Object that holds an outbound WebSocket, so a persistent market-data connection consumes duration even while idle and can exhaust a free-plan allowance. The sanitized user-preference tool reports whether streaming is available without exposing the connection credentials.
Deploy the read-only version
These steps work with Cloudflare's free tier, including SQLite-backed Durable Objects. You need a Schwab developer application and a Cloudflare account with a Workers subdomain.
Install and verify the project.
npm ci --ignore-scripts npm run check npm run supply-chainSign in to Cloudflare and create a KV namespace.
npx wrangler login npx wrangler kv namespace create OAUTH_KVCopy the public template to the ignored local deployment configuration:
cp wrangler.jsonc wrangler.local.jsoncPut the returned namespace ID into
wrangler.local.jsonc. ReplaceREPLACE_WITH_WORKER_HOSTNAMEwith the deployment's Worker or custom-domain hostname. Keepwrangler.local.jsoncprivate; Git ignores it by default.Create a Cloudflare Access SaaS application using generic OIDC.
Redirect URL:
https://<worker-hostname>/callbackScopes:
openid,email,profileEnable PKCE.
Add one Access policy:
Allow→Emails→ your exact email.Do not add broad domains,
Everyone, bypass rules, or service-token rules.Record the team domain, OIDC client ID, and OIDC client secret.
Configure the Schwab developer application.
Callback URL:
https://<worker-hostname>/schwab/callbackRecord the Schwab app key and secret.
Callback URLs must match exactly, including HTTPS and path.
Add Worker secrets interactively. None of these values belong in
wrangler.jsoncor Git.npx wrangler secret put OWNER_EMAIL npx wrangler secret put ACCESS_TEAM_DOMAIN npx wrangler secret put ACCESS_CLIENT_ID npx wrangler secret put ACCESS_CLIENT_SECRET npx wrangler secret put SCHWAB_CLIENT_ID npx wrangler secret put SCHWAB_CLIENT_SECRET npx wrangler secret put TOKEN_ENCRYPTION_KEY npx wrangler secret put STATE_SIGNING_KEYGenerate the two random values locally:
openssl rand -base64 32 openssl rand -base64 48Use the first output for
TOKEN_ENCRYPTION_KEYand the second forSTATE_SIGNING_KEY.Deploy.
npm run deployAdd
https://<worker-hostname>/mcpas a remote MCP server in ChatGPT/Codex. The client must support Client ID Metadata Documents. The first authorization runs through both Cloudflare Access and Schwab. The MCP endpoint uses Streamable HTTP; do not append/sse.
Optional account allowlist
After the first connection, call schwab_list_account_hashes. It returns Schwab account hashes plus non-reversible base64url SHA-256 fingerprints, never raw account numbers. To restrict a deployment to selected accounts, store a comma-separated fingerprint list as another Worker secret:
npx wrangler secret put OWNER_ACCOUNT_FINGERPRINTSThe restriction is applied on subsequent tool calls. To expand the list beyond hashes captured during the original authorization, clear the deployment's Durable Object storage and reconnect Schwab.
Local development
Copy .dev.vars.example to .dev.vars and fill it locally. .dev.vars is ignored by Git. Local OAuth callbacks must also be registered with Cloudflare Access and Schwab, so a temporary HTTPS Worker deployment is usually simpler than localhost for end-to-end testing.
npm run devSupply chain
This repository does not import @sudowealth/schwab-api, workers-mcp, Clearbit, or another runtime API proxy. The Worker makes outbound calls only to the configured Cloudflare Access OIDC endpoints and official Schwab API hosts.
All npm versions are exact and package-lock.json is committed. CI installs the lockfile without lifecycle scripts, runs tests/types/lint/format checks, checks production vulnerabilities, and verifies npm registry signatures. See SUPPLY_CHAIN.md and SECURITY.md.
References
License
MIT
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
No tool schema history has been recorded yet.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Multi-tenant FastMCP server for Charles Schwab brokerage data, monetized via DPYC Tollbooth
Hosted MCP for stocks, options, Greeks, brokers, order previews, alerts, and workflows.
Hosted remote MCP server for YNAB on Cloudflare Workers with OAuth
Hosted MCP server with managed OAuth for 15+ toolkits: Google Workspace, Fitbit, Oura, Kalshi, etc.
Related MCP Servers
- AlicenseAqualityDmaintenanceA read-only MCP server that provides access to Charles Schwab account data and market information, including portfolio positions, real-time quotes, options chains, price history, and account balances through AI assistants.9MIT
- AlicenseNot gradedqualityDmaintenanceA read-only Model Context Protocol server that connects your Schwab brokerage account to LLM applications for portfolio monitoring and market data retrieval.MIT
- AlicenseNot gradedqualityAmaintenanceExposes Charles Schwab brokerage data to AI assistants via MCP, enabling natural language queries about positions, option chains, account balances, and trade audits with Lightning micropayment monetization.Apache 2.0
- FlicenseNot gradedqualityCmaintenanceA local MCP server that exposes the Charles Schwab Individual Trader API (accounts, quotes, orders, transactions) as MCP tools.1-