""" Pydantic models for read_delta MCP tool input validation. Validates Delta Lake table path, cloud provider, and optional query/limit. """ import re from typing import Literal, Optional from pydantic import BaseModel, Field, field_validator, model_validator Vendor = Literal["aws", "alicloud", "gcp", "local"] # Path scheme patterns per cloud (path part must be non-empty) SCHEMES = { "local": (r"^file:///", "file:///absolute/path/to/delta-table"), "aws": (r"^s3://", "s3://bucket-name/path/to/delta-table"), "alicloud": (r"^s3://", "s3://bucket-name/path/to/delta-table"), "gcp": (r"^gs://", "gs://bucket-name/path/to/delta-table"), } class ReadDeltaRequest(BaseModel): """ Input model for read_delta tool. Validates table_path format and optional limit/query. """ table_path: str = Field( ..., description=( "URI to the Delta table " "(e.g. s3://bucket/path, gs://bucket/path, file:///local/path)" ), min_length=1, ) query: Optional[str] = Field( default=None, description="Optional SQL over the table; must reference the table as `__t__`.", ) limit: Optional[int] = Field( default=None, ge=1, le=100_000, description="Optional row limit (1–100000). Applied if query is not provided.", ) cluster_id: str = Field( ..., description="Cluster identifier.", ) region: Optional[str] = Field( default=None, description="Cloud region (e.g. us-west-2)", ) vendor: Vendor = Field( ..., description="Cloud vendor (aws, alicloud, gcp, local)", ) @field_validator("table_path") @classmethod def validate_table_path_not_empty(cls, v: str) -> str: v = v.strip() if not v: raise ValueError("table_path cannot be empty") return v @model_validator(mode="after") def validate_table_path_scheme(self) -> "ReadDeltaRequest": v = self.table_path vendor = self.vendor pattern, example = SCHEMES[vendor] if not re.search(pattern, v): raise ValueError( f"table_path for cloud '{vendor}' must match scheme. Example: {example}" ) # Ensure path part exists (e.g. s3://bucket/ok, file:///a) no_trailing = v.rstrip("/") if no_trailing.endswith(":"): raise ValueError("table_path must include a path (e.g. bucket and key or path)") if vendor != "local" and no_trailing.count("/") < 3: raise ValueError("table_path must include a path (e.g. bucket and key)") return self