---
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.db` in 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).
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9