nothingworry's picture
Update the backend
e44e5dd
raw
history blame
1.76 kB
from __future__ import annotations
import re
from dataclasses import dataclass
from typing import Any, Mapping, Optional
class TenantValidationError(ValueError):
"""Raised when tenant metadata is missing or malformed."""
TENANT_ID_PATTERN = re.compile(r"^[A-Za-z0-9_\-.:/]{3,128}$")
@dataclass(slots=True)
class TenantContext:
tenant_id: str
user_id: Optional[str] = None
metadata: Optional[dict[str, Any]] = None
def _extract_tenant_id(payload: Mapping[str, Any]) -> str:
for key in ("tenant_id", "tenantId", "tenant"):
if key in payload:
value = payload[key]
if isinstance(value, str):
return value.strip()
raise TenantValidationError("tenant_id is required for every MCP tool call")
def _normalize_tenant_id(raw_value: str) -> str:
normalized = raw_value.strip()
if not normalized:
raise TenantValidationError("tenant_id cannot be empty")
if not TENANT_ID_PATTERN.match(normalized):
raise TenantValidationError(
"tenant_id must be 3-128 chars and may only contain letters, numbers, '.', '-', '_', or ':'"
)
return normalized
def build_tenant_context(payload: Mapping[str, Any]) -> TenantContext:
tenant_id = _normalize_tenant_id(_extract_tenant_id(payload))
user_id: Optional[str] = None
metadata: Optional[dict[str, Any]] = None
for key in ("user_id", "userId"):
if key in payload and isinstance(payload[key], str):
user_id = payload[key].strip() or None
break
meta_candidate = payload.get("metadata")
if isinstance(meta_candidate, dict):
metadata = meta_candidate
return TenantContext(tenant_id=tenant_id, user_id=user_id, metadata=metadata)