diff --git a/src/personal_mcp/config.py b/src/personal_mcp/config.py new file mode 100644 index 0000000..49dd389 --- /dev/null +++ b/src/personal_mcp/config.py @@ -0,0 +1,42 @@ +from functools import cache +from pathlib import Path + +from pydantic import BaseModel +from pydantic import DirectoryPath +from pydantic import Field +from pydantic_settings import BaseSettings +from pydantic_settings import SettingsConfigDict + +DEFAULT_ENV_FILE = Path(".env").resolve() +_REPO_ROOT = Path(__file__).resolve().parents[2] + + +class Mounts(BaseModel): + docs: str = "/docs" + docs_dir: DirectoryPath = Field(default=_REPO_ROOT / "docs") + mcp: str = "/mcp" + + +class Settings(BaseSettings): + """Runtime settings for the HTTP MCP and docs server.""" + + model_config = SettingsConfigDict( + env_file=DEFAULT_ENV_FILE, + env_prefix="PERSONAL_MCP_", + extra="ignore", + ) + + debug: bool = False + log_level: str = "info" + mounts: Mounts = Field(default_factory=Mounts) + site_dir: DirectoryPath = Field(default=_REPO_ROOT / "site") + + +@cache +def get_settings(**overrides) -> Settings: + return Settings(**overrides) + + +def refresh_settings(**overrides): + get_settings.cache_clear() + return get_settings(**overrides) diff --git a/src/personal_mcp/web/app.py b/src/personal_mcp/web/app.py index 2f4353c..b1aecc0 100644 --- a/src/personal_mcp/web/app.py +++ b/src/personal_mcp/web/app.py @@ -1,8 +1,8 @@ from fastapi import FastAPI +from personal_mcp.config import Settings +from personal_mcp.config import get_settings from personal_mcp.mcp import mcp -from personal_mcp.web.config import Settings -from personal_mcp.web.config import get_settings from personal_mcp.web.docs_mount import mount_docs_static from personal_mcp.web.health import router as health_router @@ -10,7 +10,7 @@ from personal_mcp.web.health import router as health_router def create_app(settings: Settings | None = None) -> FastAPI: runtime_settings = settings or get_settings() mcp_app = mcp.http_app( - path=runtime_settings.mcp_route, + path=runtime_settings.mounts.mcp, json_response=True, stateless_http=True, transport="http", @@ -27,7 +27,7 @@ def create_app(settings: Settings | None = None) -> FastAPI: app.include_router(health_router) mount_docs_static( app, - docs_route=runtime_settings.docs_route, + docs_route=runtime_settings.mounts.docs, site_dir=runtime_settings.site_dir, ) app.mount("/", mcp_app, name="mcp") diff --git a/src/personal_mcp/web/config.py b/src/personal_mcp/web/config.py deleted file mode 100644 index 5a55245..0000000 --- a/src/personal_mcp/web/config.py +++ /dev/null @@ -1,38 +0,0 @@ -from contextvars import ContextVar -from pathlib import Path - -from pydantic import Field -from pydantic_settings import BaseSettings -from pydantic_settings import SettingsConfigDict - -DEFAULT_ENV_FILE = Path(".env").resolve() -_REPO_ROOT = Path(__file__).resolve().parents[3] - - -class Settings(BaseSettings): - """Runtime settings for the HTTP MCP and docs server.""" - - model_config = SettingsConfigDict( - env_file=DEFAULT_ENV_FILE, - env_prefix="PERSONAL_MCP_", - extra="ignore", - ) - - host: str = "127.0.0.1" - port: int = 8000 - debug: bool = False - log_level: str = "info" - docs_route: str = "/docs" - mcp_route: str = "/mcp" - site_dir: Path = Field(default=_REPO_ROOT / "site") - - -_settings: ContextVar[Settings | None] = ContextVar("settings", default=None) - - -def get_settings() -> Settings: - settings = _settings.get() - if settings is None: - settings = Settings() - _settings.set(settings) - return settings