Repository navigation
v0.1.2
This release adds a shared MCP port, categories with search/filter, a read-only guest view, and hardens the MCP runner. It also contains everything from v0.1.1, which was never tagged.
Highlights
One port for all MCPs — a streaming reverse proxy serves every instance as /mcp/, so only the UI port and the shared port stay open.
Categories, search and filter — group instances with a free-text label; the dashboard filters over ID, name, description and category.
Guest mode — visitors see which tools exist and whether they run, but never ports, URLs or actions.
Control tool v0.0.6 — categories over MCP, plus documentation of what a locked instance refuses.
New
Shared MCP port — all instances can be served through a single port as /mcp/ via a streaming reverse proxy; instances then bind to 127.0.0.1 only, so exactly two ports stay open.
Categories — instances carry a free-text category set on upload, in the editor or in Edit; the dashboard shows it as a column and offers a category filter plus a search box over ID, name, description and category. The label lives in the MCP config only, so setting it never rewrites the tool JSON/code or restarts the instance.
Guest mode — with auth enabled, the login screen can be dismissed with Continue as guest; GET /api/instances and GET /api/instances/{id} then return only id, name, description, category, status and version, and the UI hides ports, URLs, venv and all actions.
Control tool v0.0.6 — upload_tool and create_tool accept manager categories; update_instance_category can change or clear them without modifying tool JSON/code or restarting the instance. The write actions now document the lock: code edit, config/values/dependencies/venv changes, restart, reinstall and delete state that a locked instance answers 403, and export_tool states that it needs code-read permission. The shipped specs are regenerated from the code, so the descriptions an agent sees can no longer drift from the docstrings.
Version badge — the spawner's own version is shown next to the title in the header.
Security
Only advertised tools are callable — the MCP runner checks the requested name against the tool list built from the Tools class instead of resolving it with getattr alone, which also exposed private helpers and inherited methods.
Subresource integrity for CDN assets — the CodeMirror <script>/ tags carry SRI hashes, so a compromised CDN cannot inject code into the admin UI.
CLI edit mode is locked — a mode set via --no-edit / --no-code-edit can no longer be lifted at runtime through PUT /api/settings. Modes set via the Settings page remain changeable as before.
Lock/unlock respects edit mode — both routes now require upload or full edit mode and return 403 under --no-edit.
Fixed
Dependencies starting with http were rejected — the package-spec validator matched the prefix http, so httpx, httpcore and friends failed validation; it now only rejects http:// and https:// URLs.
Bearer scheme is matched case-insensitively — per RFC 7235 a client sending authorization: bearer is no longer turned away by the MCP endpoint.
Stop never lies about the outcome — a runner that survives SIGTERM and SIGKILL is reported as still running instead of being marked stopped while the orphan keeps its port; killed children are reaped, and restart aborts when the stop failed instead of starting a second process on the same port.
Stop works during startup — the runner PID is registered as soon as the process spawns, so stopping an instance in its startup window actually kills it.
Port race on concurrent installs fixed — auto-assigned ports are held for the duration of the install, so two parallel uploads can no longer receive the same port; the shared proxy's port is reserved during allocation as well.
ID example is reserved — creating a tool with this ID used to overwrite the shipped configs/example.json and produce an instance that never showed up in the list.
Atomic file writes — configs, pids.json and settings.json are written via temp file + rename; a crash mid-write can no longer corrupt them.
Robust request validation — malformed JSON bodies return 400/422 instead of 500.
Settings save is change-aware — an unchanged edit mode is no longer persisted to settings.json (which used to freeze a CLI-flag default).
Readable error messages — upload and config-edit dialogs render structured 422 errors (e.g. a failed pip install) as text instead of [object Object].
Reinstall visible in readonly mode — matching the documented API behaviour (it only re-runs pip for the already-pinned dependency list).
Log panel handles auth errors — an expired session shows the login dialog instead of dumping a JSON error into the log view; GET /api/instances/{id} now includes the version field like the list endpoint.
MCP token hint — changing the MCP Bearer token in Settings notes that running instances keep the previous token until restarted.
Internal
API split into route modules — admin_server.py shrank from ~1200 to ~140 lines; the endpoints now live in app/routes/ with shared guards and serialization in app/api_helpers.py.
Frontend split into ES modules — web/app.js is a bootstrap; instances, config, upload, editor, logs and settings each own a module.
Faster instance list — one directory scan feeds states and enrichment, tool versions are cached by mtime, and the disk walk runs off the event loop.
Server-classified secrets — GET /api/instances/{id}/config ships a secret_fields list so the edit dialog no longer has to guess which values are credentials.
Test suite — tests/ covers the API contract, auth and guest exposure, locking, schema/package validation, port allocation, the control tool's specs, and a full e2e lifecycle over HTTP and MCP.
Also included since v0.1.0 (v0.1.1, never tagged)
Secrets survive config round-trips — PUT /api/instances/{id} ignores values equal to the ******** mask, so a client that echoes back a fetched config no longer overwrites real API keys.
Non-blocking delete — deleting a running instance stops it in a worker thread instead of blocking the event loop for up to ~5 s.
Locked instances can be stopped again — lock means "don't modify", but a misbehaving instance must always be stoppable.
Honest upload errors — an MCP config whose dependencies fail to install returns 422 instead of reporting success.
Validation in the instance venv — POST /api/tools/validate accepts an optional instance_id, so already-installed third-party imports no longer false-fail.
pip timeout raised to 600 s per package (was 120 s).
Control tool v0.0.5 — trailing-slash tolerance in manager_url, 600 s timeouts for install-heavy calls, optional instance_id for validate_tool_code.
Upgrading
No new dependencies and no config migration. Pull, restart the spawner, and restart your instances so they pick up the new runner — with the shared port enabled, running instances keep their public binding until they are restarted.