Files
mcp-synology-filestation/CLAUDE.md
T
marcus f2abf2af1e docs: rewrite README, mark v0.3 complete in CLAUDE.md (v0.3.6)
- README.md: replace v0.1 placeholder with full v0.3.5 reference docs
  (all 26 tools documented with parameters, return values, caveats)
- CLAUDE.md: bump implemented-tools header to v0.3.5; roadmap updated
  (v0.3 complete, v0.4 candidates: none planned)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 06:46:45 +02:00

203 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.toml` → `version = "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.3.5 — 26 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 |
| `get_thumbnail` | Fetch a thumbnail for an image/video file as base64 |
| `list_favorites` | List all FileStation user favorites |
| `add_favorite` | Pin a path as a FileStation favorite |
| `delete_favorite` | Remove a path from FileStation favorites |
| `background_tasks` | List FileStation background tasks (copy, move, delete, etc.) |
| `list_snapshots` | List Btrfs snapshots for a share (requires Btrfs volume) |
See [SPEC.md](SPEC.md) for full tool specifications and DSM call details.
## DSM Quirks (continued)
- **`SYNO.FileStation.Snapshot::list`:** Returns error 400 when the share is not on a
Btrfs-formatted volume. Confirmed: this NAS has no Btrfs volumes — `list_snapshots`
maps error 400 to a clear "requires Btrfs-formatted volume" message.
- **`SYNO.FileStation.BackgroundTask`:** Only the `list` method is available (v1v3).
No stop/cancel/clear methods exist on this firmware.
- **`SYNO.FileStation.Thumb` — `quality` parameter ignored:** DSM accepts the `quality`
field in the POST body but always returns the same JPEG regardless of the value.
No server-side quality control is available.
- **`SYNO.FileStation.Thumb` — size limits:** `small` thumbnails range from ~5 KB to
~548 KB raw depending on source image resolution. `medium`/`large` can exceed 380 KB
raw. `original` reflects the full stored image and may be several MB.
The `get_thumbnail` tool enforces: abort >1.5 MB raw (≈2 MB base64), warning >375 KB
raw (≈500 KB base64). Default changed from `large` to `small` to avoid MCP buffer
overflows.
## Roadmap
### v0.4 — candidates
No further tools currently planned.