oauthProxy needs one store that every instance of your MCP server shares. Pass it as the storage option. Adapters for common databases are in mcp-use/oauth/storage; each one takes a client or binding that your application already has, so mcp-use adds no database dependency.
What the proxy stores
The proxy encrypts every value with a key derived from yoursecret before the store sees it. Keys hold SHA-256 hashes of tokens, never the tokens themselves, so a leaked store holds no usable token without the secret.
One completed sign-in writes these records:
Every key starts with
mcp-use:oauth: and a hash of the MCP resource URL, so several MCP servers can share one database without seeing each other’s records. A client: record exists only for clients that registered through dynamic client registration. The proxy also writes short-lived markers and locks (codeused:, ustate:, revoked:, lock:), which expire within an hour.
Adapter requirements
A store must:- be shared by every instance of the server, because a sign-in can start on one instance and finish on another;
- expire each value after the
ttlSecondsit was written with; - implement
take(read and delete) andadd(write if absent) atomically. One-time codes and sign-in states depend ontake, and refresh locks depend onadd.
Upstash
upstashStorage needs only fetch. Pass a fetch option to use a custom implementation.
Redis
Pass a connected node-redis or ioredis client, or a function that connects one:GETDEL and SET NX EX, which need Redis 6.2 or later.
Postgres
take uses DELETE … RETURNING. Expired rows are deleted in the background on about 1% of writes. table must be a plain SQL identifier.
Firestore
value field and an expiresAt timestamp. take and add run in transactions. The adapter ignores expired documents, but Firestore keeps them until they are deleted: add a TTL policy on the expiresAt field of the collection.
firestoreStorage uses the server SDK, which needs Node.js. It does not run on Cloudflare Workers or other edge runtimes. The Admin SDK bypasses security rules, so the collection needs no rules of its own.
Cloudflare
D1
Durable Object
A Durable Object processes one request at a time, which makes every operation atomic. ExportOAuthProxyDurableObject from your Worker and bind it:
src/worker.ts
wrangler.jsonc
mcp-use-oauth. The object deletes expired entries in pages of 1,000 every 200 writes.
Memory
memoryStorage() keeps values in process memory. Its state is lost on restart and is not shared between instances, so oauthProxy accepts it only when the MCP resource is on localhost. A listen() on a loopback address uses it by default when you pass no storage. Under mcp-use dev, pass it explicitly:
mcp-use dev starts a new process, so sign-ins end on each file change. Use Redis locally to keep them.
Write your own adapter
ImplementOAuthProxyStorage. Values and keys are strings, and ttlSeconds is always a positive integer:
take and add under concurrency before you deploy.
Next steps
OAuth Proxy
Configure the upstream provider, clients, and consent page.
API reference
Every adapter, option, and client interface.