ugreen-nas-mcp
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| UNC_NAS_URL | No | The NAS address. Passed through to the unc CLI. Only https:// URLs are supported; LAN addresses usually need port 9443. | |
| UNC_NAS_TOKEN | No | The NAS access token. Passed through to the unc CLI. If not set, the local cache from 'unc auth login' is used. | |
| UGREEN_MCP_ACCESS | No | Access level: read, write, or admin. | read |
| UGREEN_MCP_UNC_BIN | No | Full path to the unc binary. If unset, the server searches PATH and then ~/.local/bin/unc. | |
| UGREEN_MCP_TOOLSETS | No | Enabled toolsets as a comma-separated list: file,search,docker,download,app,storage,ctl,network,taskmgr. Defaults to all. | all |
| UGREEN_MCP_LOCAL_DIR | No | Local directory that upload and download tools may read from or write to. If unset, upload and download tools are not available. | |
| UGREEN_MCP_ALLOWED_PATHS | No | Comma-separated NAS folders that file and search tools may access, for example /volume1/docs,/home/alice. Defaults to unrestricted. | |
| UGREEN_MCP_MAX_OUTPUT_CHARS | No | Maximum number of characters returned in a single response. Output beyond this is truncated. | 60000 |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server manages the user's own UGREEN NAS through the official unc CLI.
If a call fails with an authentication or connection error, call nas_status.
File paths start at a shared folder or the personal folder. Volume roots such as /volume1 are not accessible. If you do not know a path, call nas_file_roots (shared folders) or nas_file_list with no path (personal folder).
Never invent IDs, paths, task IDs or app IDs. Take them from tool results.
64-bit integers (sizes, timestamps, task IDs) are JSON strings. Convert them before comparing.
Some operations are asynchronous: success means "accepted". Verify with the matching list tool before reporting completion.
After a failed write, check the current state before trying again. Do not retry automatically.
Tools that delete, overwrite, stop services or change accounts first run as a preview and return a confirm_token. Show the preview to the user, then call the same tool with identical arguments plus confirm_token.
If server policy rejects a call (access level, allowed paths, local directory), tell the user which environment variable to change. Do not look for another tool to get around it.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| nas_statusA | Check whether this server can reach the NAS, and what it is allowed to do. Returns the login state (NAS address and user name, never the token), the unc CLI version, and the server policy: access level, enabled toolsets, allowed NAS paths and whether local file transfer is enabled. Call it first when another tool fails with an authentication, connection or "not available" error. Set doctor to true for a full health report with firmware and component versions and the methods this NAS does not support; that report takes longer, so do not request it before every call. |
| nas_file_rootsA | List the shared folders this NAS account can access. This is the starting point whenever you do not know a path: file access is granted per shared folder, and volume roots such as /volume1 cannot be listed directly. Use the returned paths with the other nas_file_* tools. For the personal folder, call nas_file_list with no path instead. |
| nas_file_listA | List the entries of a folder on the NAS. Omit path to list the personal folder of the logged-in user. Volume roots such as /volume1 are not accessible; start from a shared folder (see nas_file_roots). Entries are in items[0].entries[]; size and the time fields are strings holding 64-bit integers, and the mode field is unreliable, so do not use it to judge permissions. Recursive listing of a large folder can take a minute or more: prefer a deeper path, or nas_search_files when looking for a file by name. |
| nas_file_readA | Read bytes from a file on the NAS without saving it locally. Returns { path, offset, bytes, encoding, content }: encoding is "utf8" when the bytes are valid UTF-8 text, otherwise "base64". By default the first 64 KiB are read and at most 1 MiB per call; for a longer file call again with offset increased by the bytes already read. A result with fewer bytes than requested means the end of the file was reached. To copy a whole file to this computer use nas_file_download. |
| nas_file_statA | Get type, size and timestamps for one or more paths. The result always has items[], one per path in the order given; check each item for success, because one missing path does not stop the others. size and the time fields are strings; times are Unix seconds. The mode field is unreliable on existing paths, so do not use it to decide whether something is readable or writable. |
| nas_file_findA | Walk one or more folders and return entries matching the filters. kind selects plain files, folders, or a media category (image, video, audio, archive, doc, font, app). Sizes take a unit suffix such as 10K or 4M. since and until are Unix seconds and need time_field to say which timestamp they apply to. Roots must be shared folders or folders inside them; a volume root is reported as an invalid root. All roots are searched in one request, so matches are in the top-level entries[] of the result, and that key is absent when nothing matched. For finding files by name across a large area, nas_search_files is faster. |
| nas_file_usageA | kind "du" reports how much space folders occupy (items[].entries[] with size, dir_count and file_count), optionally broken down to a given depth; it walks the tree and can be slow. kind "quota" reports the size limit set on a personal or shared folder: the value is in MB as a string, and "0" means no quota is set, not a quota of zero. Quotas exist only at the level of a personal or shared folder, not on subfolders or volume roots. For free space on a whole volume use nas_storage_info with kind "volumes". |
| nas_file_checksumA | Compute the MD5 or SHA-256 checksum of files on the NAS, for example to verify a transfer. The NAS reads each whole file, so large files take a while: timeout_seconds applies to each file, and a file that needs longer fails with "context deadline exceeded". The result has items[], one per path; check each for success. |
| nas_file_tagsA | Query the tag system of the file manager. query "list_tags" returns all tags with their IDs. "tags_of_paths" returns the tags on the given paths (needs paths). "files_by_tag" returns the files that carry the given tags (needs tag_ids from list_tags; optionally limit it to scope_path, with recursive to include subfolders). Tag IDs are numbers taken from list_tags; never guess them. |
| nas_search_filesA | Search the NAS index for files and folders whose name contains the query (case-insensitive). It matches names, not file content. Give paths to limit the search to folders; subfolders are included unless recursive is false. The call waits for the search to finish and returns the first sorted page in data.results.ordered_items[], plus data.task_id. When data.next_offset is present there are more results: page through them with nas_search_results using the same sort_by and order. When data.task_removed is not true the search task is still stored on the NAS; release it with nas_search_results action "remove" when you are done. file_size and the time fields are strings. A search without paths or query can take minutes; narrow it first. |
| nas_search_resultsA | Work with a search started by nas_search_files, identified by its task_id. action "page" returns another page: entries are in data.ordered_items[] (there is no results wrapper here, unlike nas_search_files). Pass the offset from the previous next_offset and the same sort_by and order as the original search, otherwise pages overlap or skip entries. The search is exhausted only when a page has no next_offset; do not use total_count to decide. "cancel" stops a search that is still running and keeps what was found. "remove" discards the stored search task and its results; it never touches any file. A task that was already removed reports "not found", which is not a failure worth retrying. |
| nas_docker_containersA | List containers with their state, image, CPU and memory use, and published ports (data.result[]). This is the place to read a container's CPU and memory; mem_used is a string holding bytes. The status filter takes "running" or "stopped", while the status field in the response uses a different set (CONTAINER_DETAIL_STATUS_RUNNING, _EXITED, _RESTARTING, _UPDATING), so do not feed response values back into the filter. port_mappings is only filled for running containers, so an empty list does not prove that no ports are configured. Mounts, environment variables, restart policy, network mode and health checks cannot be read through this server; point the user to the Docker app on the NAS for those. |
| nas_docker_imagesA | source "local" (default) lists the images stored on the NAS (data.result[]), optionally filtered by name or status and sorted; image_size is a string holding bytes. To find what can be cleaned up, filter status "unused" and sort by size. source "registry" searches the remote image registry for name (data.search_image[]) and supports page and page_size. Images cannot be deleted through this server; the user has to do that in the Docker app on the NAS. |
| nas_docker_projectsA | List Docker Compose projects (data.projects[]) with status, path, container counts and whether image updates are available. Projects that carry an application_id are managed by the app center: they cannot be deleted here and must be uninstalled with nas_app_manage. update_available is three-state: omit it for no filter, true for projects with updates, false for projects without. A status ending in _OPERATING means an operation is still in progress. data.original_total is the unfiltered project count. |
| nas_docker_logsA | Fetch recent log output (data.logs, one multi-line string). target "container" takes the full container_id from nas_docker_containers and returns the latest 200 entries. target "project" takes a project_name from nas_docker_projects and returns the combined log of all its containers, 200 lines in total, possibly with ANSI colour codes. There is no way to get older lines, follow the log or filter by time; for that the user needs the Docker app on the NAS. |
| nas_docker_checkA | Check for conflicts before deploying. kind "port" tells whether a host port is taken by a container (needs port; protocol optional). kind "dir" tells whether a folder is mounted by a container (needs dir). kind "gpu" tells whether an app is using the discrete GPU of the given vendor (needs vendor; ask the user if unknown). These checks only see Docker containers: occupied: false does not guarantee that the port is free, because other services on the NAS are not covered. |
| nas_docker_app_usageA | Report CPU, memory, network and disk rates for Docker-based apps installed from the app center (data.usages, keyed by app ID). It only covers app-center apps: a plain container or a self-made Compose project, or a misspelled ID, comes back as all zeros without an error, so zeros do not mean idle. App IDs are the application field of nas_docker_containers. For any container's CPU and memory use nas_docker_containers instead. Rates are strings in bytes per second. |
| nas_download_listA | Show the tasks of the NAS download center with their progress. Without task_ids it lists every task, including finished ones; with task_ids it returns just those, and fails as a whole if any ID does not exist. Tasks are in data.tasks[]. Use progress_percent (a number from 0 to 100) rather than dividing bytes: total_bytes can be negative while the size is unknown. saved_path and file_name are empty until task_status is TASK_STATUS_COMPLETED, which does not mean failure. When waiting for a task, check a few seconds apart and stop at COMPLETED, FAILED or PAUSED; the access token has a per-minute request quota. |
| nas_app_listA | List the apps of the NAS app center, or search them by keyword. The output is a compact summary per app (app_id, name, install_status, installed_version, available_version); for full details and install parameters call nas_app_get. filter narrows the list to installed, not installed, updatable or broken apps. keyword (at most 20 characters) searches by name; a search can be combined only with filter "installed", and it does not find port apps or portal apps, so if nothing matches, list without keyword and look through the names. Always take app IDs from this output and confirm the choice with the user. |
| nas_app_getA | view "detail" (default) returns everything about one app, including install_parameters, whose keys are what nas_app_manage expects in params; the values still have to come from the user. An unknown app ID is an error. view "status" returns the install, running and auto-start state for one or more apps in data.status keyed by app ID: decide from is_installed and is_running. An empty run_state, or one resting at APP_RUNNER_STAGE_FREE, just means no workflow is in progress and is not a failure. |
| nas_storage_infoA | Read storage state. kind "disks" lists internal disks with health and temperature; "volumes" lists storage volumes with total and used space, mount point and warnings; "pools" lists storage pools with RAID level, status and member disks. To answer "are my disks healthy" check both disks and pools; for "how much space is left" use volumes (free = total - used). Sizes are strings in bytes, and a total of "0" means not mounted, so do not compute usage from it. A temperature of 0 means unreadable, not zero degrees. Disk status UNSUPPORTED means health cannot be determined, which is not the same as healthy. A pool reported as GOOD can still contain a missing or unhealthy disk, so also read disks[].status; a missing disk does not make the call fail. kind "smart" returns raw SMART attributes for every disk: this is sensitive diagnostic data, needs admin access and a confirmation, and should be requested only when the user explicitly asks for it. Serial numbers are masked unless show_serial is true, which is likewise only for an explicit request. Pools and volumes cannot be created, expanded or deleted through this server. |
| nas_usersA | kind "users" (default) lists NAS accounts (data.users[]): uid is a number, role is in user_base.role, group membership in user_base.groups[], and an expired account has user_base.is_expired true (there is no server-side filter for expiry, so list all and check the field). names matches exactly and is case sensitive. kind "groups" lists user-created groups with their members (data.groups[]); gid there is a string. The built-in groups admin, users and ughomeusers are not listed and cannot take new members. Passwords are never returned. Users cannot be modified or deleted and groups cannot be created through this server; that is done in the NAS control panel. |
| nas_system_infoA | Read one group of system settings. "identity": device model and system version. "update_status": whether a system update exists (read-only; it never starts a check or an update, and UNSPECIFIED means unknown, not up to date). "fan", "beep", "cpu", "led", "zram", "power" and "ups": the corresponding control panel settings. For cpu, when is_support says frequency control is unsupported, the frequency value is meaningless. For ups, read right_now first (true means act immediately); otherwise a standby_time of "-1" means "when the UPS battery is low", not "unset". "identity", "update_status" and "zram" are missing on older control panel versions and then report "not available"; do not retry, and use nas_status with doctor: true for the firmware version instead. Nothing here can be changed through this server. |
| nas_network_infoA | kind "interfaces" (default) lists network interfaces (data.ifaces[]) with link state, speed, MAC and IPv4/IPv6 settings; pass iface to see only one. To find the NAS address, take an interface whose connection is CONNECT_STATUS_CONNECTED and whose ipv4.ip_addr is not empty: decide by ip_addr, not by ipv4.status, which reads "enabled" even without an address. A speed of -1 or 0 means no link. An unknown interface name returns an empty list rather than an error. kind "general" returns the default gateway, DNS mode and servers, and proxy settings; the manual DNS list there only applies in manual mode and may differ from the per-interface DNS, so report both when asked about DNS. Network settings cannot be changed through this server. |
| nas_performanceA | Read the task manager. kind "overview" (default) returns CPU, GPU, memory, fans, network and disk activity in one call and is the right first step for "is the NAS slow". "cpu", "gpu", "mem", "net", "disk" and "volume" return one metric each; in net, disk and volume the entry named "overview" is the total. "processes" and "services" list resource use per process or service; sort by cpu, mem or name (pid for processes only) and top keeps the first N (0 for all). Every value is a single instant: to judge a trend, call again a few seconds later. Mind the units: network and per-process disk rates are bytes per second, whole-disk and volume rates are kilobytes per second, memory is bytes, and most numbers are strings. A temperature of 0 means unreadable. On older task manager versions every kind reports "not available"; do not retry. Processes cannot be ended and services cannot be stopped here. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 25 tools
Each tool maps to a distinct subsystem (files, docker, apps, storage, system, network, performance), and the longer descriptions explicitly cross-reference siblings to prevent misselection (e.g. nas_search_files vs nas_file_find vs nas_file_list, nas_file_usage vs nas_storage_info, nas_docker_containers vs nas_docker_app_usage). The remaining overlap among the three file-finding tools and among the read status tools is handled well by the descriptions, though an agent could still hesitate briefly.
Every tool uses the nas_ prefix with snake_case and a grouped domain sub-prefix (nas_file_*, nas_docker_*, nas_search_*, nas_app_*). Read-only aggregators consistently use an _info/_status noun form, giving a predictable and readable pattern throughout.
25 tools is at the upper edge, but they span many genuinely distinct NAS subsystems (files, Docker, app center, storage, users, system, network, performance, downloads), so the breadth is justified rather than duplicative. It is slightly heavy but each tool has a defensible role.
The surface is broad but deliberately read/monitor-heavy, with obvious gaps: no file write/upload tool, and several descriptions reference tools that do not exist (nas_file_download, nas_app_manage), which will dead-end agents trying to follow the guidance. User/group creation, image deletion, and pool/volume management are also unavailable, so lifecycle coverage is partial.