Files
mcp-synology-filestation/CLAUDE.md
T
marcus b88677a20c docs: document Search::start folder_path array format + Extract::start file_path key
Hard-won DSM quirks confirmed by live testing:
- Search::start folder_path must be json.dumps([path]) — plain string or
  json.dumps(path) is silently ignored, causing empty results
- Extract::start source archive key is file_path (not path); both
  file_path and dest_folder_path require json.dumps()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 21:40:21 +02:00

9.3 KiB
Raw Blame History

CLAUDE.md — mcp-synology-filestation

Development context for this project.

Project

mcp-synology-filestation — MCP server exposing Synology FileStation as Claude tools. Sibling project to mcp-synology-container.

Infrastructure

  • NAS: https://dsm.gecheckt.de
  • Config file: ~/.config/mcp-synology-filestation/config.yaml
  • Credentials: OS keyring, service name mcp-synology-filestation
  • Gitea: https://gitea.gecheckt.de/marcus/mcp-synology-filestation
  • Local code: D:\Dev\Projects\mcp-synology-filestation
  • Runtime: Python 3.12+, uv, MCP SDK (mcp>=1.0.0), httpx, keyring, click, rich
  • Test share on NAS: /test-mcp — only for MCP tests, no production data
  • DSM test user: Testuser / Lasdas1234 (read-only, for browser tests)

Deploy Workflow

  1. Commit and push via Claude Code.
  2. Close Claude Desktop first (Windows holds file locks — reinstall fails otherwise).
  3. Install:
    uv tool install --reinstall dist\mcp_synology_filestation-X.Y.Z-py3-none-any.whl
    
    Or directly from Gitea:
    uv tool install --reinstall git+https://gitea.gecheckt.de/marcus/mcp-synology-filestation.git
    
  4. Restart Claude Desktop.
  5. Verify: uv tool list shows the expected version.

Versioning

Bump the patch version on every commit in both files:

  • pyproject.tomlversion = "X.Y.Z"
  • src/mcp_synology_filestation/__init__.py__version__ = "X.Y.Z"

Current series: 0.3.x

Toolchain

Task Command
Format ruff format src/ tests/
Lint ruff check src/ tests/
Tests pytest
Install dev uv sync --dev
Run server uv run mcp-synology-filestation serve
Setup uv run mcp-synology-filestation setup

Code Standards

  • Type hints on all public functions and methods.
  • Docstrings (English, 1 line max for @mcp.tool() functions) on all public modules, classes, and functions.
  • Async-first: use httpx.AsyncClient throughout; never requests.
  • Formatter: ruff format (line length 100).
  • Linter: ruff check — fix all warnings before committing.
  • No -> str on @mcp.tool() functions. FastMCP generates outputSchema from return annotations, which bloats the tools/list payload and causes Claude Desktop to truncate the tool list. Omit return type annotations on all tool functions.
  • Short docstrings on tools. Keep tool docstrings to 1 line. Long docstrings also bloat the tools/list payload.

Security Rules

  • Never log or write credentials, session IDs, or passwords to disk or stderr.
  • Never store passwords in the config YAML — OS keyring only.
  • Mask sensitive query parameters (_sid, passwd) before logging request URLs.

Destructive Operation Rules

  • delete, move with overwrite=True, copy with overwrite=True, and upload with overwrite=True are considered destructive.
  • The delete tool MUST require confirmed=True to proceed. Without it, return a preview message that describes exactly what would be deleted — never silently proceed.
  • For overwrite scenarios in move/copy/upload, include a warning in the tool description and default overwrite to False.

Error Handling Rules

  • DSM error codes must be mapped to human-readable messages. See SPEC.md for the full map.
  • Never surface raw Python stack traces in tool return values.
  • Session expiry (codes 106, 107, 119): transparent single retry with re-login.
  • Network errors: return "Cannot reach NAS at {host} — check connectivity."
  • Auth failures (400403): return message + "Run mcp-synology-filestation setup to reconfigure credentials."

Tool Return Format

  • All tools return str.
  • Use rich.table.Table (rendered to string) for tabular data.
  • Include item counts and pagination hints where relevant.
  • Error messages are prefixed with Error: for easy recognition by Claude.

DSM Quirks (hard-won knowledge)

  • JSON-encode path parameters: path, dest_file_path, dest_folder_path, name etc. must be passed as json.dumps("/path") or json.dumps(["/path1", "/path2"]). Plain strings or Python lists are silently ignored by DSM.
  • Share paths only: always use /docker, /homes/marcus — never /volume1/docker. Volume paths cause DSM error 408.
  • additional field: must be json.dumps(["size","time"]) — comma-separated string does not work.
  • List::list additional: only ["size","time"] confirmed working on this firmware. real_path, perm, type cause error 408.
  • SYNO.FileStation.Stat: not in API registry — use List::getinfo instead.
  • SYNO.FileStation.CheckExist: returns error 400 — use List::getinfo instead (entries with name=null do not exist).
  • DirSize / MD5 one-shot: finished=true is returned exactly once, then the task is deleted. Implemented via _start_and_poll_oneshot() — never poll again after finished=true.
  • DirSize cold start: after inactivity, DSM's background service needs ~68 s to initialise. During this window every status poll returns 599. Fix: restart the task (new start call) — up to 6 restarts within a 60 s budget.
  • DirSize status version: must use version=1. Version 2 always returns 599.
  • Sharing::delete id parameter: must be json.dumps(link_id).
  • CheckPermission::write: path and filename are plain strings (no json.dumps). Returns {"blSkip": false} on success.
  • Search::start folder_path: must be a JSON array: json.dumps([path]). A plain string or json.dumps(path) is silently ignored by DSM — it starts an empty search and immediately returns finished=true, files=[].
  • Extract::start parameter name: the source archive key is file_path (not path). Both path parameters need json.dumps(): file_path=json.dumps(...) and dest_folder_path=json.dumps(...).
  • Error 599: means "background service not ready / task not found" for DirSize/MD5. Handled by _start_and_poll_oneshot().

Module Structure

src/mcp_synology_filestation/
├── __init__.py          # __version__
├── __main__.py          # entry point
├── server.py            # create_server(config, client) → FastMCP
├── client.py            # FileStationClient (async httpx wrapper)
├── auth.py              # AuthManager: keyring, env vars, 2FA, login/logout
├── config.py            # AppConfig, ConnectionConfig, load_config, save_config
├── cli.py               # click: setup / check / serve
└── tools/
    ├── __init__.py
    └── filestation.py   # register_filestation(mcp, config, client)

Implemented Tools (v0.2.10 — 20 tools)

Tool Description
list_shares List all shared folders with volume usage
list_dir List directory contents with pagination and sorting
get_info Get detailed metadata for one or more paths
check_exist Check if one or more paths exist (Yes/No table)
search Search for files by glob pattern with async polling
download Download a file as base64 (max 10 MB)
create_folder Create a new folder (optionally with parent dirs)
rename Rename a file or folder
copy Copy a file or folder (async polling, overwrite=False)
move Move a file or folder (async polling, overwrite=False)
delete Delete a file or folder — requires confirmed=True
upload Upload base64-encoded content to a path (max 50 MB)
compress Compress paths into a ZIP or 7z archive
extract Extract a ZIP or 7z archive to a destination folder
dir_size Total size, file count, folder count for directories
get_md5 Compute MD5 checksum of a file
check_permission Check write permission for a filename in a directory
create_sharing_link Create a public sharing link (optional password + expiry)
list_sharing_links List all sharing links (paginated table)
delete_sharing_link Delete a sharing link by ID

See SPEC.md for full tool specifications and DSM call details.

Roadmap

v0.3 — next

Tool API
get_thumbnail SYNO.FileStation.Thumb
list_favorites SYNO.FileStation.Favorite
add_favorite SYNO.FileStation.Favorite
delete_favorite SYNO.FileStation.Favorite
list_snapshots SYNO.FileStation.Snapshot
background_tasks SYNO.FileStation.BackgroundTask