Keepspace Clean
Keep what matters. Clear what doesn't.
Keepspace is a local-first AI disk organizer. Keepspace Clean is its first stage: a safety-first, whitelist-based disk cache cleanup server for the Model Context Protocol (MCP), so an AI assistant can explain where your disk space went before anything is touched. Supports macOS and Windows only.
Status: public Beta. 中文文档
At a glance:
- Analyzes disk usage, scans a built-in cache whitelist, and explains findings.
- Read-only by default — execution must be switched on deliberately.
- Never permanently deletes; approved items only go to the system Trash.
- Never scans your personal files — no Desktop / Downloads / Documents, no document contents, nothing leaves your machine.
Default read-only → strict whitelist → scan → plan → client-side tool approval by the user → move to Trash → auditable. Everything you delete goes to the system Trash / Recycle Bin via
send2trash; nothing is ever permanently deleted.
Design principles
- Default read-only. 5 of 6 tools never modify anything.
execute_cleanup_planis disabled by default and only becomes enabled when the environment variableDISK_CLEANER_ENABLE_EXECUTIONis explicitly set (see "Execution gate" below). - Strict whitelist. Cleanup is limited to low-risk, rebuildable cache
directories declared in
rules.py. Callers can never pass a path. - Scan → plan → approve → execute.
create_cleanup_planproduces a one-time, 10-minute-expiring plan with a randomconfirmation_token; onlyexecute_cleanup_plancan move files. Theconfirmation_tokenonly binds the call to one valid plan — it does not prove the user saw anything. User consent is delegated to MCP tool approval (or the client's own confirmation UI, which must usereadOnlyHint/destructiveHintannotations): the client must show the pending destructive action to the user and obtain explicit approval before each execution. - Trash only. Everything goes through
send2trash. No permanent delete, norm -rffallback, no auto-emptying of the Trash. Disk space is only freed after the user empties the Trash manually. - Auditable. Every tool call is appended to a local audit log readable via
the read-only
get_operation_historytool. No sensitive content is logged.
Tools
All tools return structured JSON and accept an optional lang parameter
("zh" / "en" / "auto", default follows the system locale).
| Tool | Read-only | Description |
|---|---|---|
get_disk_usage |
✅ | Capacity / used / free / free % for a local volume or drive letter. |
scan_cleanable_items |
✅ | Scans built-in whitelist caches above a size threshold. Returns per-item item_id, size, risk, requires_app_closed, … Scanning is resumable: per-directory failures are reported, never fatal. |
create_cleanup_plan |
✅ | Builds a one-time plan from whitelist item_ids (max 20). Re-validates every item (existence, size, whitelist root, no symlink). |
execute_cleanup_plan |
❌ | The only mutating tool, disabled by default (see "Execution gate"). Requires plan_id + confirmation_token; marked destructiveHint + readOnlyHint=false so tool-approval clients must ask the user before calling. Re-validates paths, sizes, file counts, mtimes and related app processes, then moves to Trash via send2trash. |
get_trash_usage |
✅ | Total size of the system Trash / Recycle Bin. No emptying. |
get_operation_history |
✅ | Last limit audit records. |
Example flow
get_disk_usage() # what's the state of the disk
scan_cleanable_items(min_size_mb=50, max_items=30) # what is reclaimable (whitelist; size estimation must be complete)
create_cleanup_plan(item_ids=["pip_cache_macos", …])# plan + confirmation_token (read-only)
# → client shows the pending destructive action to the user and gets approval
execute_cleanup_plan(plan_id="…", confirmation_token="…") # needs DISK_CLEANER_ENABLE_EXECUTION=1
get_operation_history(limit=20) # what happened
execute_cleanup_plan returns bytes_actually_freed: 0 and a message about
emptying the Trash — moving to Trash does not free disk space.
Platforms & whitelist
- macOS: pip, npm, yarn, Homebrew download cache, CocoaPods, Playwright browsers, Xcode DerivedData, Chrome cache, Edge cache.
- Windows: pip, npm, yarn, Playwright, Chrome cache, Edge cache.
Everything else is out of scope for V1: whole Temp, whole Chrome user-data directory, chat software, browser profiles, Downloads / Desktop / Documents / Photos, cloud sync directories, Git repos, AI model directories, system directories.
Execution gate
execute_cleanup_plan is the only tool that can move files, and it is disabled
by default. The server runs pure read-only unless the operator explicitly sets:
export DISK_CLEANER_ENABLE_EXECUTION=1 # or true / yes / on
Without the variable (or with 0 / false / off), execute_cleanup_plan
returns the structured error EXECUTION_DISABLED. Even when enabled, every
execution still requires both:
- a valid, unexpired, unused plan bound to the submitted
confirmation_token(this token proves the plan once existed, not that the user approved it), and - client-side tool approval: a tool-approval-capable MCP client must present
the destructive action (via
readOnlyHint=false/destructiveHint=true) to the user and obtain explicit consent before the call is sent.
Security model
Path safety is enforced centrally in disk_cleaner/safety.py
(validate_cleanup_path) at scan, plan, and execute time:
- symlinks rejected (checked before resolving),
- rule roots and
allowed_rootescapes rejected, - protected directories rejected (system dirs as subtrees; user home and sensitive folders via exact/ancestor checks),
- re-validation immediately before any move.
execute_cleanup_plan never falls back to permanent deletion if
send2trash fails; the item is simply reported as failed.
See SECURITY.md for the full model.
Structured error codes
Failures look like:
{"success": false,
"error": {"code": "PLAN_EXPIRED", "message": "...", "recoverable": true}}
| Code | Meaning |
|---|---|
UNSUPPORTED_PLATFORM |
Linux or other unsupported OS. |
INVALID_ARGUMENT |
Bad parameter (range, type, …). |
INVALID_ITEM_ID |
item_id not in the whitelist. |
PATH_NOT_ALLOWED |
Target outside whitelist root / protected dir. |
SYMLINK_REJECTED |
Target is a symlink. |
SCAN_PERMISSION_DENIED |
Cannot read a location without elevation; no auto-elevation. |
PLAN_NOT_FOUND |
Unknown plan_id. |
PLAN_EXPIRED |
Plan older than 10 minutes. |
PLAN_ALREADY_USED |
Plan already executed. |
CONFIRMATION_FAILED |
confirmation_token mismatch. |
ITEM_CHANGED |
Item changed (size / file count / mtime / path) or disappeared since planning. |
APP_RUNNING |
A related app process is still running (or its state is undetectable) for a requires_app_closed item; close it and retry. |
SIZE_ESTIMATION_INCOMPLETE |
Some entries could not be read, so the size estimate is incomplete; item is not put into a plan. |
EXECUTION_DISABLED |
DISK_CLEANER_ENABLE_EXECUTION is not set; execution is off in pure read-only mode. |
MOVE_TO_TRASH_FAILED |
send2trash failed. |
PARTIAL_SUCCESS |
Some items succeeded, some failed. |
Python exceptions or absolute local paths are never exposed to the AI client.
Installation
Requires Python 3.10+.
pip install keepspace-clean
Or from a checkout, for development:
python3 -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e .
Dependencies are pinned in pyproject.toml (tested with Python 3.11.9,
mcp 2.1.1, send2trash 2.1.0; see requirements.txt):
mcp>=2.0,<3.0send2trash>=1.8,<3.0
Note: the server uses the official high-level entry
from mcp.server.mcpserver import MCPServer(verified importable and runnable with mcp 2.1.1).mcp.server.fastmcp.FastMCPis not available in this environment and is not used.
MCP client configuration
Static example connection configuration lives in
examples/client-config.json. The root
server.json is not a client config anymore: it is the MCP Registry
publishing manifest placeholder (see below).
{
"mcpServers": {
"keepspace-clean": {
"command": "/absolute/path/to/.venv/bin/python",
"args": ["-m", "disk_cleaner.server"],
"env": {
"DISK_CLEANER_ENABLE_EXECUTION": "0"
}
}
}
}
Set DISK_CLEANER_ENABLE_EXECUTION to 1 only if you deliberately want to
allow cleanup execution (see "Execution gate"). Leaving it unset defaults to
pure read-only.
MCP Registry publishing
server.json is the executable MCP Registry publishing manifest for this
project ($schema, name: io.github.d7huang/keepspace-disk-cleaner-mcp,
title, description, version, repository, packages …). It is already
filled with a pypi package entry (keepspace-clean 1.1.0, stdio
transport) and validates against the 2025-12-11 registry schema. It is
not used for local client configuration (see the example above).
The Registry name, the repository URL and the <!-- mcp-name: ... --> comment
at the top of this README must stay identical:
io.github.d7huang/keepspace-disk-cleaner-mcp.
To publish (not done yet):
- Publish the
keepspace-cleanpackage to PyPI (version must matchserver.json/pyproject.toml, currently1.1.0). - Run
mcp-publisher publish server.json.
Audit log location
- macOS:
~/Library/Application Support/keepspace-clean/audit.jsonl - Windows:
%APPDATA%\keepspace-clean\audit.jsonl
Development & tests
pip install -e ".[dev]"
python -m pytest tests/ -v
The suite covers rules (whitelist), path safety (symlink / escape / protected
dirs), scanning, the full plan lifecycle (availability, expiry, one-time use,
confirmation), and execution (success / ITEM_CHANGED / symlink rejection /
partial success). Run with pytest from the project root.
Roadmap
Keepspace Clean is the first stage of Keepspace. Later stages (a desktop app, inbox-style file organization, information distillation) are not implemented today and are not part of this release.
Open core
Keepspace is an open core project. The safety foundation — the protocol layer, whitelist rules, path validation, planning and the Trash executor — is open source, because a tool that asks for your trust must be auditable. AI judgement, habit models, knowledge distillation and the desktop app are not part of this repository. See docs/OPEN-CORE.md.
Contributing
See CONTRIBUTING.md. New cache locations are welcome; changes that weaken the safety rules are not.
License
MIT © 2026 d7huang
The Keepspace name is a mark of d7huang — see TRADEMARK.md. Forks are welcome under the MIT licence, but please choose your own name.
Release files for keepspace-clean 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| keepspace_clean-1.1.0.tar.gz | 82.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| keepspace_clean-1.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 118.0 kB
Release files / keepspace_clean-1.1.0.tar.gz
| Download URL | keepspace_clean-1.1.0.tar.gz |
|---|---|
| Size | 82.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5ce6862435a974a7cc1653b2ad759c9ca8e65a8b7b7d87c7b11fdf506e2782a9
|
|
BLAKE2b-256 checksum How to use checksums |
32994c6b155f6e6291fb3920088a3b06af2e30f2c58ce08659525105f0887308
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.15
|
Release files / keepspace_clean-1.1.0-py3-none-any.whl
| Download URL | keepspace_clean-1.1.0-py3-none-any.whl |
|---|---|
| Size | 35.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a4cc3f49e61786007db789b21343b22c881aa5c87f24c01fd0b56ce63f644f0b
|
|
BLAKE2b-256 checksum How to use checksums |
7d38bbfb239db4c470eb4a087f511273328717c99b4c40a17c2786e396a8d450
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.15
|