[0.8.29] - 2026-10-25
Added
mcptoon upgrade— update the installed CLI itself.mcptoon updaterefreshes
the tool surface captured from your servers; nothing told you when the mcptoon
program on disk was behind.upgradeasks PyPI for the latest release, compares it
with the installed version, and — after a confirmation — upgrades in place using the
installer it detects (uvwhen the interpreter lives under a uv tool dir, else
pip;MCPTOON_UPGRADE_CMDoverrides).upgrade --checkis the scriptable probe:
exit 1 when a newer release exists, 0 when current, 2 when PyPI is unreachable, so a
shell profile or CI job can surface it.--yesskips the prompt;--jsonis
machine-readable. Zero dependencies — a singleurllibGET and a hand-rolled
version compare (nopackaging). The footer picks it up too:upgrade --check
records its result to a cache, andfooter-factsappends a one-line "new version
available" hint read straight from that cache — never the network — while a
throttled, detached refresh keeps the cache current without ever blocking the turn.mcptoon serve --background— a server that outlives the terminal. The
foreground server dies when the shell closes, which is why a one-off trial rarely
becomes a daily install.--backgrounddetaches the HTTP server (pid + log under the
mcptoon state dir) and refuses to start if the port is already taken;serve --status
reports the live instance and its last log lines,serve --stopends it. For
auto-start,serve --install-autostartregisters a logon entry (a per-user Windows
Scheduled Task, or asystemd --userunit on POSIX) and--uninstall-autostart
removes it — both explicit, opt-in and reversible.
Security
mcptoon serve --stopcould kill an unrelated process (fixed before release).
The background-instance bookkeeping trusted a bare pid and probed liveness with
os.kill(pid, 0)— but on Windows that call is not a probe: it invokes
TerminateProcessand ends the process, so even a--statusliveness check could
terminate an unrelated process whose pid had been recycled (observed during
development: it took down an unrelated node process)._pid_alivenow uses a
query-only handle (OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION)+
GetExitCodeProcess) on Windows and the harmlesskill(pid, 0)only on POSIX, and
the pid file records the process creation time so--stoprefuses to signal a
pid whose identity no longer matches the one it spawned.mcptoon installcould write arbitrary code into a generated handler file.
installer._generate_http_handler_fileand_generate_handler_filebuild a
Python module that is later imported (from router import registerat the top,
@register(...)), but interpolatedserver_name,url,transport,command
and each arg as raw f-string text inside"..."literals. A value like
install --url 'http://x/sse";import os;os.system("id");_="'therefore produced
a syntactically valid handler that ran the injected statement on import —
verified: such a file compiles. These inputs routinely arrive from a pasted
link, an issue, or an agent, so this was a code-execution primitive. Every one
of them is now emitted as arepr()/json.dumps()literal (likeheadersand
SCHEMAalready were), so no value can escape its string. The docstring, which
cannot be a repr, gets a sanitised display copy.