Properties
category: reference tags: - vs-2 - oauth - mcp - infrastructure last_updated: 2026-03-15
VS-2: Persistent MCP OAuth for Claude.ai
Problem
The otterwiki-mcp server used FastMCP's InMemoryOAuthProvider for OAuth 2.1. All OAuth state (registered clients, authorization codes, access/refresh tokens) was lost on every server restart, forcing Claude.ai to re-authorize each time.
Solution
Replaced InMemoryOAuthProvider with SQLiteOAuthProvider — a drop-in persistent provider backed by a local SQLite database.
Changes
| File | Description |
|---|---|
otterwiki_mcp/oauth_store.py |
New SQLiteOAuthProvider implementing FastMCP's OAuthProvider interface |
otterwiki_mcp/server.py |
Swapped InMemoryOAuthProvider for SQLiteOAuthProvider |
otterwiki_mcp/config.py |
Added MCP_OAUTH_DB env var (default: mcp_oauth.db) |
tests/test_oauth_store.py |
20 tests covering registration, auth flow, tokens, refresh, revocation, persistence |
tests/test_server_auth.py |
Updated to expect SQLiteOAuthProvider; uses :memory: DB for test isolation |
.gitignore |
Added *.db pattern |
Configuration
MCP_OAUTH_DB— Path to the SQLite database file. Default:mcp_oauth.dbin the working directory. Set to:memory:for ephemeral (test) usage.
Token Lifetimes
| Token Type | Expiry |
|---|---|
| Authorization code | 10 minutes |
| Access token | 1 hour |
| Refresh token | 30 days |
Schema
Three tables, auto-created on first use:
- oauth_clients — Registered OAuth clients (client_id, full client JSON, created_at)
- oauth_codes — Authorization codes (code, client_id, redirect_uri, PKCE challenge, scopes, expiry, resource)
- oauth_tokens — Access and refresh tokens stored as paired rows (token, client_id, scopes, expiry, type, paired counterpart, resource)
Token pairs are linked: revoking either token in a pair removes both.
Branch
feat/persistent-oauth on otterwiki-mcp repo. Not pushed, not merged.
Test Results
All 133 tests pass (20 new + 113 existing).