fix: enable uvx/PyPI deployments without Docker assumptions

Two bugs made `uvx --from . nextcloud-mcp-server run` (and any pip install)
unusable outside Docker:

1. Dynaconf was configured with ignore_unknown_envvars=True and relied on
   settings.toml to declare the key schema. With no settings.toml in a wheel
   install, every env var (NEXTCLOUD_HOST, MCP_DEPLOYMENT_MODE, ...) was
   silently dropped. Moved the schema into a Python _DEFAULTS dict passed
   directly to Dynaconf, kept settings.toml as an optional external override
   (renamed to settings.toml.example, gitignored), and pointed docker-compose
   at the example file.

2. Token SQLite DB defaulted to /app/data/tokens.db in multiple places
   (auth/storage.py, migrations.py, alembic/env.py, cli.py db subcommands),
   which blew up at uvicorn startup with FileNotFoundError on non-Docker
   hosts. Replaced with a new config.get_token_db_path() helper that
   resolves TOKEN_STORAGE_DB if explicitly set, otherwise allocates a
   per-process tempfile cleaned up at interpreter exit via atexit — mirroring
   the "ephemeral by default" pattern used for QDRANT_LOCATION=:memory:.

Containers are unaffected: docker-compose services now explicitly set
TOKEN_STORAGE_DB=/app/data/tokens.db (the fourth service that was missing
this pin has been brought in line with the other three).

Verified end-to-end in an isolated /tmp venv: env-var-only startup, Alembic
migrations run against the tempfile, Application startup complete, /health/live
returns 200, tempfile deleted on SIGTERM. Unit tests (464) + ruff + ty pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2026-04-14 20:11:23 +02:00
co-authored by Claude Opus 4.6
parent fd1846de03
commit 146b622ebf
9 changed files with 242 additions and 40 deletions
+6 -4
View File
@@ -13,6 +13,7 @@ from alembic.config import Config
import nextcloud_mcp_server.alembic as alembic_package
from alembic import command
from nextcloud_mcp_server.config import get_token_db_path
logger = logging.getLogger(__name__)
@@ -25,8 +26,9 @@ def get_alembic_config(database_path: str | Path | None = None) -> Config:
package location instead of alembic.ini file.
Args:
database_path: Path to SQLite database file. If None, uses default
(/app/data/tokens.db for Docker)
database_path: Path to SQLite database file. If None, resolves via
config.get_token_db_path() (ephemeral tempfile unless
TOKEN_STORAGE_DB is set).
Returns:
Alembic Config object configured for the specified database
@@ -45,7 +47,7 @@ def get_alembic_config(database_path: str | Path | None = None) -> Config:
if database_path:
db_path = Path(database_path).resolve()
else:
db_path = Path("/app/data/tokens.db") # Default for Docker
db_path = Path(get_token_db_path()).resolve()
url = f"sqlite+aiosqlite:///{db_path}"
config.set_main_option("sqlalchemy.url", url)
@@ -100,7 +102,7 @@ def get_current_revision(database_path: str | Path | None = None) -> str | None:
"""
if database_path is None:
database_path = "/app/data/tokens.db"
database_path = get_token_db_path()
db_path = Path(database_path).resolve()