Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 54 additions & 54 deletions src/mcp_server_docker/input_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@


class JSONParsingModel(BaseModel):
"""
\"\"\"
A base Pydantic model that attempts to parse JSON strings for non-primitive fields.
If a string is provided for a field that expects a complex type (dict, list, or another model),
it will attempt to parse it as JSON.

Claude appears to not understand that a nested field shouldn't be a JSON-encoded string...
But it does send valid JSON!
"""
\"\"\"

@field_validator("*", mode="before")
@field_validator(\"*\", mode=\"before\")
@classmethod
def _try_parse_json(cls, value: Any, info: ValidationInfo):
if not isinstance(value, str):
Expand Down Expand Up @@ -57,153 +57,153 @@ def _try_parse_json(cls, value: Any, info: ValidationInfo):


class FetchContainerLogsInput(JSONParsingModel):
container_id: str = Field(..., description="Container ID or name")
tail: int | Literal["all"] = Field(
100, description="Number of lines to show from the end"
container_id: str = Field(..., description=\"Container ID or name\")
tail: int | Literal[\"all\"] = Field(
100, description=\"Number of lines to show from the end\"
)


class ListContainersFilters(JSONParsingModel):
label: list[str] | None = Field(
None, description="Filter by label, either `key` or `key=value` format"
None, description=\"Filter by label, either `key` or `key=value` format\"
)


class ListContainersInput(JSONParsingModel):
all: bool = Field(
False, description="Show all containers (default shows just running)"
False, description=\"Show all containers (default shows just running)\"
)
filters: ListContainersFilters | None = Field(None, description="Filter containers")
filters: ListContainersFilters | None = Field(None, description=\"Filter containers\")


class CreateContainerInput(JSONParsingModel):
"""
\"\"\"
Schema for creating a new container.

This is passed to the Python Docker SDK directly, so the fields are the same
as the `docker.containers.create` method.
"""
\"\"\"

detach: bool = Field(
True,
description="Run container in the background. Should be True for long-running containers, can be false for short-lived containers",
description=\"Run container in the background. Should be True for long-running containers, can be false for short-lived containers\",
)
image: str = Field(..., description="Docker image name")
name: str | None = Field(None, description="Container name")
entrypoint: str | None = Field(None, description="Entrypoint to run in container")
command: str | None = Field(None, description="Command to run in container")
network: str | None = Field(None, description="Network to attach the container to")
environment: dict[str, str] | None = Field(
None, description="Environment variables dictionary"
image: str = Field(..., description=\"Docker image name\")
name: str | None = Field(None, description=\"Container name\")
entrypoint: str | None = Field(None, description=\"Entrypoint to run in container\")
command: str | None = Field(None, description=\"Command to run in container\")
network: str | None = Field(None, description=\"Network to attach the container to\")
environment: dict[str, str] | list[str] | None = Field(
None, description=\"Environment variables dictionary or list of strings in KEY=VALUE format\"
)
ports: dict[str, int | list[int] | tuple[str, int] | None] | None = Field(
ports: dict[str, int | list[int] | list[str | int] | None] | None = Field(
None,
description="A map whose keys are the container port, and the values are the host port(s) to bind to.",
description=\"A map whose keys are the container port, and the values are the host port(s) to bind to.\",
)
volumes: dict[str, dict[str, str]] | list[str] | None = Field(
None, description="Volume mappings"
None, description=\"Volume mappings\"
)
labels: dict[str, str] | list[str] | None = Field(
None,
description="Container labels, either as a dictionary or a list of key=value strings",
description=\"Container labels, either as a dictionary or a list of key=value strings\",
)
auto_remove: bool = Field(False, description="Automatically remove the container")
auto_remove: bool = Field(False, description=\"Automatically remove the container\")


class RecreateContainerInput(CreateContainerInput):
container_id: str | None = Field(
None,
description="Container ID to recreate. The `name` parameter will be used if this is not provided",
description=\"Container ID to recreate. The `name` parameter will be used if this is not provided\",
)

@computed_field
@property
def resolved_container_id(self) -> str:
return self.container_id or self.name # pyright: ignore

@model_validator(mode="after")
@model_validator(mode=\"after\")
def validate_container_id(self):
if self.container_id is None and self.name is None:
raise ValueError(
"container_id or name is required for identifying the container to stop+remove"
\"container_id or name is required for identifying the container to stop+remove\"
)
return self


class ContainerActionInput(JSONParsingModel):
container_id: str = Field(..., description="Container ID or name")
container_id: str = Field(..., description=\"Container ID or name\")


class RemoveContainerInput(JSONParsingModel):
container_id: str = Field(..., description="Container ID or name")
force: bool = Field(False, description="Force remove the container")
container_id: str = Field(..., description=\"Container ID or name\")
force: bool = Field(False, description=\"Force remove the container\")


class ListImagesFilters(JSONParsingModel):
dangling: bool | None = Field(None, description="Show dangling images")
dangling: bool | None = Field(None, description=\"Show dangling images\")
label: list[str] | None = Field(
None, description="Filter by label, either `key` or `key=value` format"
None, description=\"Filter by label, either `key` or `key=value` format\"
)


class ListImagesInput(JSONParsingModel):
name: str | None = Field(
None, description="Filter images by repository name, if desired"
None, description=\"Filter images by repository name, if desired\"
)
all: bool = Field(False, description="Show all images (default hides intermediate)")
filters: ListImagesFilters | None = Field(None, description="Filter images")
all: bool = Field(False, description=\"Show all images (default hides intermediate)\")
filters: ListImagesFilters | None = Field(None, description=\"Filter images\")


class PullPushImageInput(JSONParsingModel):
repository: str = Field(..., description="Image repository")
tag: str | None = Field("latest", description="Image tag")
repository: str = Field(..., description=\"Image repository\")
tag: str | None = Field(\"latest\", description=\"Image tag\")


class BuildImageInput(JSONParsingModel):
path: str = Field(..., description="Path to build context")
tag: str = Field(..., description="Image tag")
dockerfile: str | None = Field(None, description="Path to Dockerfile")
path: str = Field(..., description=\"Path to build context\")
tag: str = Field(..., description=\"Image tag\")
dockerfile: str | None = Field(None, description=\"Path to Dockerfile\")


class RemoveImageInput(JSONParsingModel):
image: str = Field(..., description="Image ID or name")
force: bool = Field(False, description="Force remove the image")
image: str = Field(..., description=\"Image ID or name\")
force: bool = Field(False, description=\"Force remove the image\")


class ListNetworksFilter(JSONParsingModel):
label: list[str] | None = Field(
None, description="Filter by label, either `key` or `key=value` format"
None, description=\"Filter by label, either `key` or `key=value` format\"
)


class ListNetworksInput(JSONParsingModel):
filters: ListNetworksFilter | None = Field(None, description="Filter networks")
filters: ListNetworksFilter | None = Field(None, description=\"Filter networks\")


class CreateNetworkInput(JSONParsingModel):
name: str = Field(..., description="Network name")
driver: str | None = Field("bridge", description="Network driver")
internal: bool = Field(False, description="Create an internal network")
labels: dict[str, str] | None = Field(None, description="Network labels")
name: str = Field(..., description=\"Network name\")
driver: str | None = Field(\"bridge\", description=\"Network driver\")
internal: bool = Field(False, description=\"Create an internal network\")
labels: dict[str, str] | None = Field(None, description=\"Network labels\")


class RemoveNetworkInput(JSONParsingModel):
network_id: str = Field(..., description="Network ID or name")
network_id: str = Field(..., description=\"Network ID or name\")


class ListVolumesInput(JSONParsingModel):
pass


class CreateVolumeInput(JSONParsingModel):
name: str = Field(..., description="Volume name")
driver: str | None = Field("local", description="Volume driver")
labels: dict[str, str] | None = Field(None, description="Volume labels")
name: str = Field(..., description=\"Volume name\")
driver: str | None = Field(\"local\", description=\"Volume driver\")
labels: dict[str, str] | None = Field(None, description=\"Volume labels\")


class RemoveVolumeInput(JSONParsingModel):
volume_name: str = Field(..., description="Volume name")
force: bool = Field(False, description="Force remove the volume")
volume_name: str = Field(..., description=\"Volume name\")
force: bool = Field(False, description=\"Force remove the volume\")


class DockerComposePromptInput(BaseModel):
Expand Down