A Model Context Protocol server for Synology DSM 7.x. Read-only by default; write tools opt in via env var. One Go binary, stdio transport, no inbound network surface, OS-keyring password storage, 2FA with device-token persistence.
Built with the official MCP Go SDK, matching the conventions established in bold-minds/mcp-ynab.
Ships:
- DSM session auth (SID cookie) with device-token 2FA support and silent re-login on session expiry
- Host-locked outbound HTTP, TLS-verify on by default
- Read tools:
synology_info— model / firmware / uptime / CPU / RAMsynology_utilization— live CPU + memorysynology_list_shares_admin— admin view of configured shares (SYNO.Core.Share)synology_list_users— DSM accounts (SYNO.Core.User)filestation_list_shares/filestation_list_folder/filestation_statdownloads_list— DownloadStation tasksbackup_list_tasks— HyperBackup last-run statedocker_list_containers— Container Manager containers
- Write tools (
SYNOLOGY_ALLOW_WRITES=1):filestation_create_folder,filestation_delete(needsconfirm=true),filestation_renamedownloads_create,downloads_pause,downloads_resume,downloads_delete(needsconfirm=true)
Not yet covered — Photos, VMM, Surveillance, Certificates, Task Scheduler, Snapshot, SecurityAdvisor. The subpackage layout under internal/dsm/api/* is shaped for these to land incrementally.
cmd/mcp-synology/ main, subcommand dispatch
internal/config env + keyring loader
internal/secret redacting credential type
internal/dsm HTTP client, auth, device-token persistence, errors
internal/dsm/api/core SYNO.Core.* bindings
internal/dsm/api/filestation SYNO.FileStation.* bindings
internal/tools MCP tool registrations
Adding a new DSM namespace = new subpackage under internal/dsm/api/ + new file under internal/tools/. No flat-file growth.
SYNOLOGY_BASE_URL https://nas.lan:5001 (required)
SYNOLOGY_USERNAME service-account-name (required)
SYNOLOGY_PASSWORD <raw> (or PASSWORD_FILE, or keyring)
SYNOLOGY_PASSWORD_FILE /run/secrets/dsm
SYNOLOGY_OTP 123456 (first 2FA login only)
SYNOLOGY_STATE_DIR ~/.local/state/mcp-synology (device-token cache)
SYNOLOGY_INSECURE_SKIP_VERIFY=1 (LAN self-signed certs)
SYNOLOGY_ALLOW_WRITES=1 (register write tools)
Store password in the OS keyring:
echo -n 'your-dsm-password' | SYNOLOGY_USERNAME=claude mcp-synology store-passwordFirst run: set SYNOLOGY_OTP=<code from your authenticator>. DSM returns a device_id which is persisted to SYNOLOGY_STATE_DIR; subsequent runs re-authenticate silently without an OTP.
Use a dedicated DSM service account (separate from the admin you use interactively). Grant only the shares it needs.
go build -o mcp-synology ./cmd/mcp-synology
./mcp-synologyOr via Docker:
docker build -t mcp-synology .
docker run --rm -i \
-e SYNOLOGY_BASE_URL=https://nas.lan:5001 \
-e SYNOLOGY_USERNAME=claude \
-e SYNOLOGY_PASSWORD \
-e SYNOLOGY_INSECURE_SKIP_VERIFY=1 \
-v mcp-synology-state:/home/nonroot/.local/state/mcp-synology \
mcp-synology- Password wrapped in
secret.Secret— everyfmt/json/slogpath returns[REDACTED]. Raw value reached only viasecret.Reveal()(grep in review). dsm.Sanitize()scrubspasswd=,otp_code=,_sid=,device_id=,SynoToken=from any string before it leaves the process.- Login params are POSTed in the form body — never on the query string.
- Host-lock
http.RoundTripperrefuses any request to a host other than the configured DSM. - Write tools are not registered unless
SYNOLOGY_ALLOW_WRITES=1, so they don't appear intools/list. filestation_deleteadditionally requiresconfirm=truein the call arguments.
MIT. See LICENSE.