Skip to content

Make OAuth state storage backend configurable (DiskStore/SQLite corrupts on network filesystems) #184

@mikhail-zhadanov

Description

@mikhail-zhadanov

Problem

The MCP server's OAuth state storage uses DiskStore (SQLite via diskcache), which is hardcoded in FastMCP 2.14.x's OAuthProxy.__init__. SQLite is not safe on network filesystems (AWS EFS, NFS). A container restart that races with unflushed NFS writes corrupts the database, causing an unrecoverable crash loop:

File "fastmcp/server/auth/oauth_proxy.py", line 822, in __init__
    key_value=DiskStore(directory=settings.home / "oauth-proxy"),
sqlite3.DatabaseError: database disk image is malformed

The only recovery is manually deleting the corrupted cache.db from the volume, destroying all OAuth sessions.

Why this happens

The storage backend is not configurable. When using FASTMCP_SERVER_AUTH, the auth provider is instantiated by FastMCP with no arguments, so client_storage=None, and OAuthProxy falls back to DiskStore:

FASTMCP_SERVER_AUTH env var
  -> FastMCP instantiates AzureProvider() with no args
    -> AzureProvider passes client_storage=None to OAuthProxy
      -> OAuthProxy creates DiskStore(directory=settings.home / "oauth-proxy")  <- hardcoded

FastMCP 3.x already solved this by switching the default to FileTreeStore (one JSON file per key, NFS-safe). The OAuthProxy in both 2.14.x and 3.x accepts a client_storage parameter, but the Exasol MCP server has no way to pass it through since auth is configured entirely via environment variables.

Proposed solution

Add an environment variable to select the storage backend. The store directory is already configurable via FASTMCP_HOME, so no additional path variable is needed.

EXA_MCP_STORAGE_BACKEND=filetree   # default, NFS-safe, persistent
EXA_MCP_STORAGE_BACKEND=memory     # no persistence, no corruption risk
EXA_MCP_STORAGE_BACKEND=disk       # current behavior (SQLite, local FS only)

filetree should be the new default. It is persistent (same as current disk), safe on network filesystems, and aligns with FastMCP 3.x's own default. It uses FileTreeStore from py-key-value-aio >=0.3.0, which is already a dependency of FastMCP 2.14.x. The store reuses the existing path at $FASTMCP_HOME/oauth-proxy/ and needs the documented sanitization strategies:

from key_value.aio.stores.filetree import (
    FileTreeStore,
    FileTreeV1KeySanitizationStrategy,
    FileTreeV1CollectionSanitizationStrategy,
)

storage_dir = settings.home / "oauth-proxy"
store = FileTreeStore(
    data_directory=storage_dir,
    key_sanitization_strategy=FileTreeV1KeySanitizationStrategy(storage_dir),
    collection_sanitization_strategy=FileTreeV1CollectionSanitizationStrategy(storage_dir),
)

memory is useful for deployments where OAuth session loss on restart is acceptable and no persistent volume is needed.

Ideally, all backends from py-key-value-aio (Redis, DynamoDB, etc.) would be supported for production distributed deployments, but filetree and memory cover the most common cases.

Note: The filetree extra of py-key-value-aio (providing the aiofile runtime dependency) is not currently installed by FastMCP 2.14.x, which only declares [disk,keyring,memory]. The Docker image build would need to include py-key-value-aio[filetree], or alternatively, the minimum FastMCP version should be bumped to ^3.0.0 where [filetree] is a declared dependency and FileTreeStore is the default.

Environment

  • exadockerci4/exasol-mcp-server:1.6.0 (FastMCP 2.14.5)
  • AWS Fargate Spot with EFS at /root/.local/share/fastmcp
  • Auth: fastmcp.server.auth.providers.azure.AzureProvider
  • Crash triggered by Fargate Spot interruption corrupting SQLite on EFS

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions