Files
mcp-synology-container/CHANGELOG.md
T
marcus 3f73ed0aef feat: v0.3.2 — delete_project tool
Closes the project lifecycle (create → start/stop/redeploy → delete).
The tool calls SYNO.Docker.Project/delete with the UUID JSON-encoded
as the `id` parameter (per DSM convention) and removes only the
Container Manager registration — the project folder and compose
file remain on the NAS. This mirrors DSM's own "Delete project"
behaviour, not a bug; the success message states the folder was
preserved so the user is not surprised.

Safety:
- Project-name validation runs before any I/O.
- A `_find_project` pre-flight returns "not found" with a clear
  message rather than letting DSM reject an unknown UUID.
- No automatic stop. If the project is RUNNING and DSM rejects
  the delete, the response tells the user to `stop_project` first
  rather than silently halting containers under the guise of a
  "delete" call.
- Requires confirmed=True; preview shows name, UUID, status, full
  path, and share path so the user can verify before deleting.

Tests cover preview-only, not-found, invalid-name, happy path
(verifies the UUID is JSON-encoded in the delete call), and the
running-project rejection path that surfaces the stop_project hint.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 11:29:09 +02:00

14 KiB

Changelog

All notable changes to this project will be documented in this file.

[0.3.2] - 2026-05-18

Added

  • delete_project — remove a Container Manager project's registration via SYNO.Docker.Project/delete with the UUID JSON-encoded as the id parameter (per DSM convention). Mirrors the "Delete project" action in Container Manager: only the registration is removed; the project folder and compose file remain on the NAS. The success message explicitly states the folder was preserved so the user is not surprised. Closes the project lifecycle (create → start/stop/redeploy → delete). Safety:
    • Project-name validation runs before any I/O.
    • A _find_project pre-flight returns "not found" with a clear message rather than letting DSM reject an unknown UUID.
    • The tool deliberately does NOT auto-stop a running project. If DSM rejects the delete on a RUNNING project, the response tells the user to stop_project first rather than silently halting containers under the guise of a "delete" call.
    • Requires confirmed=True; the preview shows name, UUID, status, full path, and share path so the user can verify before deleting.

Added

  • create_project — register a new Container Manager project from a compose YAML string. Three-step flow:
    1. Create the target folder via SYNO.FileStation.CreateFolder with force_parent=true (idempotent — does not fail if the folder already exists, and creates missing intermediate directories). Without this step, SYNO.Docker.Project/create fails with DSM error code 2100.
    2. SYNO.Docker.Project/create (form-encoded POST, JSON-encoded string parameters per DSM convention) returns the new project's UUID.
    3. trigger_build_stream + _wait_for_project_running — reuses the existing image-pull / start / poll machinery (including the BUILD_FAILED early-exit from welle 2). Defaults: share_path is derived from compose_base_path (e.g. /volume1/docker + myapp/docker/myapp). The compose content is validated as YAML before any side effects. A pre-flight list_projects check rejects duplicate names with a clear message rather than leaving an orphaned folder on the NAS. Requires confirmed=True; the preview shows the resolved share path and the service count parsed from the compose content.

Fixed

  • DsmClient.trigger_build_stream: broaden transport-error handling (M-4). httpx.ReadTimeout is still treated as a success signal (the request was sent; DSM is processing it server-side), but all other httpx.HTTPError subclasses (ConnectError, ConnectTimeout, WriteError, RemoteProtocolError, …) are now converted into a SynologyError with a clear message. Previously these propagated as raw httpx exceptions and left callers with a stack trace.
  • redeploy_project: when build_stream (or the polling step) fails after the project has already been stopped, the response now explicitly tells the user that the project is in STOPPED state and must be recovered with start_project or another redeploy_project call. Previously the response suggested "use stop + start separately", which was misleading because stop had already happened.
  • _wait_for_project_running: exit the polling loop early on BUILD_FAILED / ERROR (M-5). DSM signals these statuses within seconds of a failed image pull; the old polling loop kept waiting up to 5 minutes for RUNNING. redeploy_project surfaces the terminal status with a hint to update_image_tag and retry when the cause is BUILD_FAILED.
  • system_prune preview: count unused user-created networks alongside dangling images and stopped containers (M-6). Built-in networks (bridge, host, none) are excluded because Docker never prunes them. Previously the preview noted "Unused networks: (not counted)", even though the underlying SYNO.Docker.Utils/prune call deletes them — users could lose networks they had not been warned about.

Changed

  • Minor version bump because redeploy_project and system_prune return different strings (and redeploy_project returns earlier on BUILD_FAILED). No tool signatures changed.

[0.2.9] - 2026-05-18

Fixed

  • __version__ is now derived from package metadata via importlib.metadata.version(), eliminating the drift between pyproject.toml and src/mcp_synology_container/__init__.py (was stuck at 0.1.0 since the initial release).
  • compose.py: reject project names that contain path separators or other unsafe characters before they reach _find_compose_path. Previously a name like "../../etc" could traverse out of compose_base_path when the project was not yet registered with Container Manager. The new _validate_project_name helper enforces ^[a-zA-Z0-9_-]+$ and is applied to read_compose, update_compose, update_image_tag, and update_env_var. Addresses M-3 from the v0.2.8 review.

Docs

  • CHANGELOG.md backfilled for releases 0.2.7 and 0.2.8 (entries had been missed during those releases).

[0.2.8] - 2026-04-21

Added

  • tests/test_dsm_client.py — comprehensive offline test suite for DsmClient:
    • _scrub_url and _error_message pure helpers.
    • request() happy-path, API-not-cached, _sid scrubbing in HTTPStatusError, sensitive-param log masking.
    • Session re-auth retry: single-retry semantics, auth-manager-absent path, re-auth failure path, thundering-herd (login called once under concurrent 106 responses).
    • trigger_build_stream: SSE fire-and-forget, JSON error detection, ReadTimeout swallowing, HTTP-error scrubbing.
    • upload_text and download_text happy-path + error-response branches.
    • _ensure_initialized double-checked locking and negative-cache cooldown behavior.

Fixed

  • DsmClient._ensure_initialized: cache failed init outcomes for 60 s so that repeated tool calls during a credential outage (wrong password, IP-blocked 407, DNS failure) do not keep hammering DSM. Each caller receives the cached exception until the cooldown window expires, after which a fresh attempt is made. Adds INIT_ERROR_COOLDOWN module constant and _init_error / _init_error_until state. Addresses M4 from the 0.2.7 review.

[0.2.7] - 2026-04-21

Fixed

  • DsmClient: scrub _sid query-parameter values from URLs embedded in httpx.HTTPStatusError messages so the raw DSM session ID never reaches log output or MCP tool responses (C1).
  • DsmClient: re-auth lock now snapshots _sid before entering the lock and skips the redundant login if another task has already refreshed the session, eliminating duplicate logins on concurrent 106/107/119 responses (M3).
  • DsmClient.trigger_build_stream: re-instates immediate JSON-error detection (regression from 0.2.6). Inspects the Content-Type header and reads a small capped prefix of the body for application/json responses to surface DSM error codes without forcing the caller into a multi-minute polling timeout. SSE responses remain fire-and-forget (C2/M8).
  • compose.update_env_var: parenthesise the apply-branch match condition so (isinstance AND startswith) OR (entry == var_name) no longer evaluates the equality branch for non-string entries — aligns the apply side with the preview-side detection logic (M1).

Changed

  • All 23 @mcp.tool() functions: strip -> str return annotations and trim docstrings to a single line (≤100 chars). FastMCP generates an outputSchema entry for every annotated tool, which roughly doubles the tools/list payload size; multi-line docstrings with Args:/Returns: sections add further bulk that Claude Desktop must parse on every connection.

Chore

  • uv.lock resynced (was stale at 0.2.2).
  • .gitignore: exclude .claude/ per-user Claude Code settings.
  • Mechanical ruff check --fix + ruff format cleanup (import sorting, unused-import removal). No functional change.

[0.2.6] - 2026-04-21

Fixed

  • DsmClient.trigger_build_stream: Claude Desktop aborts tool calls after ~4 minutes. The previous implementation read the first SSE chunk before returning, which could block for the entire duration of an image pull. Fixed by making the call truly fire-and-forget: the HTTP request is sent, response headers are received (HTTP status check only), then the connection is closed immediately without reading any SSE events. DSM continues the build server-side regardless. The _json import added in 0.2.5 is removed.

[0.2.5] - 2026-04-21

Changed

  • redeploy_project: Replaced the delete-before-start image workaround with SYNO.Docker.Project/build_stream — the programmatic equivalent of the DSM "Erstellen" (Build) button, confirmed via browser DevTools capture. New 3-step flow for all project states:
    1. Stop (skipped for STOPPED; error-suppressed for BUILD_FAILED)
    2. build_stream — DSM pulls updated images and starts the project via SSE
    3. Poll for RUNNING (timeout raised from 30 s to 5 min to accommodate image pulls) build_stream errors are now fatal (abort the redeploy with a clear message).

Added

  • DsmClient.trigger_build_stream(project_id) — fires a streaming GET to SYNO.Docker.Project/build_stream, reads the first SSE chunk to confirm DSM accepted the request, then closes the connection. The build continues server-side. Handles immediate JSON error responses; swallows ReadTimeout (stream still open = build running).

[0.2.4] - 2026-04-21

Changed

  • redeploy_project: Replaced broken SYNO.Docker.Image/pull with a delete-before-start workaround. The tool now reads image tags from the project's compose file via FileStation, deletes each cached image before calling start (so DSM auto-pulls the latest version), then polls for RUNNING. Image deletion is non-fatal — if it fails the project still starts. Unified 4-step flow for all project states (RUNNING, STOPPED, BUILD_FAILED).

Added

  • update_image_tag: Auto-updates environment variables whose value equals the numeric version prefix of the old tag when the new tag shares the same <digits>-<suffix> pattern. For example, changing 2.558-jdk212.560-jdk21 automatically updates JENKINS_VERSION=2.558 to JENKINS_VERSION=2.560. The preview (unconfirmed call) now lists which env vars will be updated. Only triggers when the variable exists and the pattern matches; no change for plain tags like latest.

[0.2.3] - 2026-04-21

Changed

  • CLAUDE.md rewritten: removed all operator-specific infrastructure details (hostnames, container names, image tags, personal notes). Kept DSM API quirks, implementation rules, and tool inventory.

[0.2.2] - 2026-04-21

Fixed

  • redeploy_project (BUILD_FAILED): Pull errors are no longer silently suppressed. If the image pull fails (e.g. the tag in compose.yaml does not exist on the registry), redeploy aborts immediately with a clear message pointing to update_image_tag.

[0.2.1] - 2026-04-21

Fixed

  • redeploy_project: After issuing start, the tool now polls the project status every 2 seconds for up to 30 seconds until the project reaches RUNNING. Previously DSM returned immediately while containers were still starting, causing the project to appear as exited when checked right after redeploy. On timeout a warning is returned instead of an error.
  • delete_image: Now distinguishes between running and stopped container references. A stopped container holding the image produces a clear hint to use delete_container or system_prune instead of a generic "in use" error.
  • redeploy_project (BUILD_FAILED path): Added explicit image pull step before restart (stop → pull → start). Previously the old cached image could be reused.

Added

  • delete_container — delete a stopped container by name; refuses if container is still running; requires confirmed=True.

[0.2.0] - 2026-04-14

Added

Images

  • list_images — list local images sorted by size; marks images in use and with available updates
  • delete_image — delete image by name:tag or hash; refuses if image is used by a container

Container

  • container_stats — live CPU %, RAM used/limit, network I/O, block I/O

System

  • system_df — Docker disk usage: image count/size, running/stopped containers, reclaimable space
  • system_prune — remove dangling images, stopped containers, and unused networks

Networks

  • list_networks — list all Docker networks with driver, subnet, gateway, attached containers
  • create_network — create a new bridge (or other driver) network
  • delete_network — delete a network; refuses if any container is still attached

Fixed

  • get_container_status now reads the correct DSM response fields (details.State for status/running, profile.image for image name) and displays IP addresses, port bindings, and mounts
  • redeploy_project is now status-aware: RUNNING → stop + start; STOPPED → start directly; BUILD_FAILED → force-stop + start; unknown status returns a clear error with workaround hint
  • Container names with hash prefix (e.g. f93cb8b504f7_jenkins) are now transparently stripped in list_containers, container_stats, get_container_status, get_container_logs, and exec_in_container

Changed

  • DsmClient gained a post_request() method for form-encoded POST operations required by image delete, network create/delete

[0.1.0] - 2026-04-13

Added

  • Initial implementation
  • Projects: list_projects, get_project_status, start_project, stop_project, redeploy_project
  • Containers: list_containers, get_container_status, get_container_logs, exec_in_container
  • Compose: read_compose, update_compose, update_image_tag, update_env_var
  • Images: check_image_updates
  • DSM session management with auto re-authentication on session timeout
  • OS keyring integration for secure credential storage
  • 2FA / device token support
  • Interactive setup wizard and check connectivity command