refactor(mcp-deribit): localize env_validation as service-internal util

This commit is contained in:
AdrianoDev
2026-04-27 17:45:08 +02:00
parent 37e5024acc
commit b16b26e2e3
3 changed files with 152 additions and 1 deletions
@@ -5,7 +5,7 @@ import os
import uvicorn
from mcp_common.auth import load_token_store_from_files
from mcp_common.env_validation import (
from mcp_deribit.env_validation import (
fail_fast_if_missing,
require_env,
summarize,
@@ -0,0 +1,80 @@
"""CER-P5-010: env validation policy — fail-fast per mandatory, soft per optional.
Usage al boot di ogni mcp `__main__.py`:
from option_mcp_common.env_validation import require_env, optional_env, summarize
creds_file = require_env("CREDENTIALS_FILE", "deribit credentials JSON path")
host = optional_env("HOST", default="0.0.0.0")
summarize(["CREDENTIALS_FILE", "HOST", "PORT"])
"""
from __future__ import annotations
import logging
import os
import sys
logger = logging.getLogger(__name__)
class MissingEnvError(RuntimeError):
"""Mandatory env var absent or empty."""
def require_env(name: str, description: str = "") -> str:
"""Fail-fast: raise MissingEnvError se name non presente o vuoto.
Uscita dal processo con codice 2 se chiamato dal main(). Comporta
logging chiaro del missing var prima dell'exit.
"""
val = (os.environ.get(name) or "").strip()
if not val:
msg = f"missing mandatory env var: {name}"
if description:
msg += f" ({description})"
logger.error(msg)
raise MissingEnvError(msg)
return val
def optional_env(name: str, *, default: str = "") -> str:
"""Soft: ritorna env o default. Log INFO se default usato."""
val = (os.environ.get(name) or "").strip()
if not val:
if default:
logger.info("env %s not set, using default=%r", name, default)
return default
return val
def summarize(names: list[str]) -> None:
"""Log INFO di tutti gli env rilevanti con presenza (mask se SECRET/KEY/TOKEN)."""
sensitive_tokens = ("SECRET", "KEY", "TOKEN", "PASSWORD", "CREDENTIAL", "WALLET")
for n in names:
val = os.environ.get(n)
if val is None:
logger.info("env[%s]: <unset>", n)
continue
if any(t in n.upper() for t in sensitive_tokens):
logger.info("env[%s]: <set, %d chars>", n, len(val))
else:
logger.info("env[%s]: %s", n, val)
def fail_fast_if_missing(names: list[str]) -> None:
"""Verifica lista di nomi mandatory al boot. Exit 2 se uno solo manca.
Uso preferito: early call in main() per bloccare boot se config incompleta.
"""
missing: list[str] = []
for n in names:
if not (os.environ.get(n) or "").strip():
missing.append(n)
if missing:
logger.error("boot aborted: missing mandatory env vars: %s", missing)
print(
f"FATAL: missing mandatory env vars: {missing}",
file=sys.stderr,
)
sys.exit(2)