from __future__ import annotations
from dataclasses import dataclass
from functools import lru_cache
import os
from typing import Optional
class ConfigurationError(RuntimeError):
"""Raised when required Microsoft Graph configuration is missing."""
@dataclass(frozen=True)
class GraphSettings:
tenant_id: Optional[str]
client_id: Optional[str]
client_secret: Optional[str]
default_sender: Optional[str] = None
delegated_token: Optional[str] = None
@classmethod
def load(cls) -> "GraphSettings":
tenant_id = os.environ.get("GRAPH_TENANT_ID", "").strip() or None
client_id = os.environ.get("GRAPH_CLIENT_ID", "").strip() or None
client_secret = os.environ.get("GRAPH_CLIENT_SECRET", "").strip() or None
default_sender = os.environ.get("GRAPH_DEFAULT_SENDER", "").strip() or None
delegated_token = os.environ.get("GRAPH_USER_ACCESS_TOKEN", "").strip() or None
# For multi-tenant support, environment variables are optional.
# Users can provide credentials as tool parameters instead.
return cls(
tenant_id=tenant_id,
client_id=client_id,
client_secret=client_secret,
default_sender=default_sender,
delegated_token=delegated_token,
)
@lru_cache(maxsize=1)
def get_graph_settings() -> GraphSettings:
"""
Retrieve cached Microsoft Graph settings.
Raises:
ConfigurationError: if required environment variables are missing.
"""
return GraphSettings.load()