github activeing123/mcptoon v0.8.0
v0.8.0 — installed now means saving tokens

latest releases: v0.8.36, v0.8.35, v0.8.34...
one month ago

The release that makes "installed" mean "saving tokens". A mounted gateway used to
add itself alongside a host's servers and save nothing; now it withholds the
upstream tool list by default, sync --takeover makes it the connection instead of
another server, and quickstart offers that switch at the one moment the tradeoff
is legible. Measured against a real serve handshake on a 12-server / 96-tool
machine (tiktoken cl100k_base): 18,686 → 2,607 tokens per turn (−86.0%).

Changed

  • The gateway withholds the upstream tool list by default. serve used to
    enumerate every upstream tool with a simplified schema — the cost that made a
    mounted gateway save ~0 on this machine once the host also listed its servers
    directly. tools/list now returns only mcptoon's own tools; upstream tools stay
    fully callable (mcptoon_manifest names them, mcptoon_inspect shows one
    schema, mcptoon_call runs it) but are no longer enumerated. Measured against a
    real mcptoon serve handshake on a 12-server / 96-tool machine, tiktoken cl100k_base: 18,686 → 2,607 tokens per turn (−86.0%). This is the "compact
    manifest" ADR 0004 originally promised ("Claude Code 只看到 1 个 MCP server")
    and ADR 0006's layered schemas had not delivered.
  • The handshake now explains where the tools went, in a second directive
    independent of the savings line: mcptoon config set footer off no longer also
    hides the tool-location note, since under the new default that note is the only
    thing telling the model the user's MCP servers still exist.
  • Roll back with one command — no host config edit, no reinstall:
    mcptoon config set exposure full. Both the setting's value and its default are
    validated, so a typo fails loudly instead of persisting a mode nothing honours.

Added

  • Both legs now land on a host that can carry them. sync --self already
    mounted mcptoon serve in every agent whose config holds MCP servers, and wrote
    a skill pointer for Codex. Claude Code can do both — it mounts servers in
    ~/.claude.json and reads ~/.claude/CLAUDE.md as machine-wide context on
    every session — so it now gets the mount and the pointer. The two are
    independent: the mount gives the host native tools, the pointer gives it the
    catalog in words, and neither is as good alone. mcptoon off removes both,
    restoring the memory file byte for byte (the block is heading-anchored and cut at
    the next ## ).
    A host is included only on evidence that its file is read every session:
    Codex (AGENTS.md) and Claude Code (CLAUDE.md). Cursor is deliberately left
    out — ~/.cursorrules is the legacy form, so a pointer there could be a silent
    no-op, which is worse than not writing one. Windsurf, Cline and VS Code Copilot
    expose no dedicated global instruction file and stay MCP-only rather than risk
    editing a file the user shares with everything else.

  • mcptoon's own skill now ships in the wheel and installs itself. The skill
    that explains the exposure presets, and what to do when a tool panel looks
    empty, existed in the repo but never reached a pip install: every builtin pack
    carried "skills": [], install_pack only wrote PROMPT.md, and the only
    channel that shipped it was the Claude Code plugin. quickstart now copies it
    into each agent's skill folder (best-effort — never fails onboarding), and
    mcptoon skills install-self [--view DIR] [--dry] repairs a machine where it is
    missing. Only a file whose frontmatter declares name: mcptoon is treated as
    ours: a missing one is created, and our own stale one is refreshed — otherwise
    an upgrading user keeps the copy from the release they first installed and never
    reads the new defaults. A view holding a different skill is still left
    completely alone, because another manager may own that folder and two managers
    writing one view is how a catalog gets clobbered.
    tests/test_skill_package.py pins the three distribution copies byte-identical
    and pins skill/SKILL.md into package-data.

  • The first command on a machine finishes the install pip could not. A
    pip install runs no code, so it landed the package and nothing else: the skill
    sat in site-packages where no agent reads it, and no skill index existed, so a
    user who never ran quickstart stayed half-installed while the docs promised
    "pip install, then just use it". Now the first real command (anything but
    serve/off/uninstall/demo) installs the agent-visible skill and builds
    the skill index, then stops. It is the additive half of the write-consent
    rule: it creates a missing skill, refreshes our own stale copy, and never touches
    a skill that is not ours; it is silent for pipes and machine formats, like the
    welcome it rides beside. The marker records the installed skill's fingerprint, so
    a release that ships a new skill heals once more instead of being suppressed by
    the previous release's marker. uninstall clears the marker, so a clean
    reinstall self-heals again.

  • exposure setting (compact | full). compact is the default and the
    cheaper preset; full restores the previous enumeration for a host whose tool
    panel reads tools/list, or a model that will not ask for the manifest first.
    Switching takes effect on the host's next connection.

  • sync --takeover: the gateway becomes the connection, not another server.
    quickstart and sync --self registered mcptoon serve alongside every
    upstream server, so a host that already listed its servers kept loading all of
    their tool schemas directly — the gateway added a line and saved nothing, which
    is the opposite of what the tool exists for. mcptoon sync --takeover removes
    each server mcptoon manages from the host config and reaches it through the
    gateway instead (a server mcptoon does not manage is left alone, since the
    gateway cannot serve it). Implies --self. The change is reversible: the
    existing <config>.bak written on first change is the undo, and mcptoon off
    still removes only the gateway entry. sync --dry and the sync report now name
    how many direct entries were replaced (−N direct, now via gateway).

    Measured on a real 12-server / 96-tool catalog (this repo's own dev box, same
    tiktoken caliber status and bench use): mounting the upstreams directly
    costs 21,074 tokens of tool schemas, and the gateway entry added on top
    costs another 2,607 under the default compact exposure (18,686 if
    exposure=full) — the same tools reaching the agent twice. Takeover drops the
    direct half, so the host's tool context goes 23,681 → 2,607 instead of
    growing. That is the difference between "installed" and "installed and worth
    installing".
    Takeover is a subtraction — it removes server entries the user wrote into a
    host config — so it now lists exactly what it will drop and asks once before
    doing it. --dry prints the plan and stops, --yes skips the prompt for
    scripted runs, and a declined or unanswerable prompt writes nothing. The
    additive --self stays silent: additions are reversible and lose nothing,
    removals are not.

  • quickstart now offers takeover instead of quietly saving nothing. The
    additive default above is safe but near-worthless on a machine whose servers are
    already registered directly — the host keeps loading every upstream schema, so the
    install that promised savings delivers ~0. quickstart therefore ends onboarding
    with a red warning that says so, lists exactly which direct entries it would
    replace, and asks once: answering yes routes them through the gateway (via
    sync --takeover --yes), anything else keeps the safe default. It is an offer,
    never a default flip — the additive path is unchanged — and it obeys the same
    consent bounds as every other prompt here: silent under --dry, machine formats,
    --no-self, an empty removal plan, and MCPTOON_NO_TAKEOVER_OFFER; a
    non-interactive stdin is never asked and is pointed at mcptoon sync --takeover
    instead. The colour comes from one new choke point, welcome.paint, which returns
    plain text for a pipe or NO_COLOR, so no escape codes reach a captured
    transcript.

  • The skill catalog is now reachable over MCP. serve published tools and
    mcptoon skills managed skills, but an agent connected to the gateway could not
    see or resolve a skill without shelling out to the CLI. Two first-party tools close
    that gap: mcptoon_skills lists the catalog (slug + one-line description), and
    mcptoon_resolve_skills takes a task and returns the best-matching skills from the
    same offline BM25 the CLI uses. Both are read-only and need no upstream servers, so a
    gateway that only ever proxied tools can now answer "which skill should I load?".
    The projection lives in skills.catalog_rows / skills.resolve_shortlist, shared by
    the CLI and the tools, so the two surfaces cannot rank differently; resolve also
    records the same usage counts, so mcptoon skills list --usage reflects MCP calls.

Fixed

  • mcptoon_inspect and mcptoon_call now actually exist. The compact
    exposure's whole safety argument is that withholding the tool list withholds
    visibility, not capability — "mcptoon_manifest names them,
    mcptoon_inspect shows one schema, and mcptoon_call runs it". The last two
    were named in the handshake directive, in the skill, and in ADR 0012, but had
    never been implemented: a real tools/call for either returned Unknown tool.
    Under the default preset an agent could therefore learn an upstream tool's
    name but not its parameters, so "the tools still work" held only if it
    guessed the arguments. Both are implemented now — mcptoon_inspect returns one
    upstream tool's full input schema, and mcptoon_call routes {name, arguments}
    back through the bridge's own call path, so it inherits schema validation,
    danger checks, output compression and usage tracking rather than forking them.
    The lesson is the test that was missing, not the code: the old test asserted the
    tool index still held the tool and never called one, so a green suite proved
    nothing about callability. tests/test_native_tools.py::TestCallBridge now
    performs real calls. The two tools cost 469 tokens per turn on the default
    path (2,138 → 2,607), which is why the skill and the ADR quote the new number.

  • mcptoon config set now exits non-zero when it rejects a value. A bad value
    left the setting unchanged but still exited 0, so
    mcptoon config set exposure compakt && restart read as a successful preset
    change. A refusal is a failure and now reports like one.

  • sync verifies the gateway entry survived its own write. A successful
    write_text is not proof the entry is there: Claude Code rewrites
    ~/.claude.json on its own schedule, and an observed race dropped the
    mcptoon entry back out seconds after a sync that reported success. The write
    is now read back, and a lost gateway entry returns written: False with a
    warning naming the file — a silent loss only surfaces later, as a user
    wondering why their tools vanished.

  • skills sync no longer mistakes an agent view for the catalog. With no
    source argument the command took the first default root (~/.claude/skills and
    friends) as its source — but those paths are exactly where the agent views
    live. On a machine whose view is a real directory rather than a link back to the
    catalog, a sync would read the view and republish it into every view, freezing a
    copy as if it were the truth, with nothing in the output to say so. A default
    root now counts as a source only when it is a link to the real catalog; a plain
    directory there is refused, and the caller is asked to name the source.

  • 21 skills were invisible to the catalog, and a deleted skill never left it.
    Found by running the new staleness probe against this machine's real 407-skill
    catalog rather than the fixtures:

    • The walk matched the manifest as child / "SKILL.md". On Windows that found
      the 21 skills that spell it skill.md — but only because NTFS folds case;
      the same code on Linux would have dropped every one of them, and a stricter
      == "SKILL.md" comparison drops them everywhere. The manifest name is now
      matched case-insensitively, so the catalog is the same on every platform.
    • Staleness was "is any SKILL.md newer than the index file", which cannot see a
      deletion (the files that remain are not newer) — a hand-removed skill kept
      answering from the index. The index now stores a signature over the whole
      listing (path, size, mtime of every manifest) and compares against it, so an
      add, an edit and a delete are all detected. Verified against the real
      catalog: deleting a skill and re-resolving removed it (408 → 407) with no
      manual skills index.
  • Concurrent skill resolves lost their usage counts. record_skill_use was a
    bare read-modify-write, and two callers reach it now — the CLI (resolve/route)
    and the MCP tool (mcptoon_resolve_skills). Measured 2026-09-24: 30 concurrent
    resolves recorded a count of 1, each thread having read the old file and written
    back the same +1. It is now serialized in-process and written atomically (unique
    tmp name + os.replace), the same discipline usage._save_usage already used, so a
    reader can never catch a torn file either. The count is what list --usage shows,
    so losing 29 of 30 was a visible lie about which skills earn their keep.

  • The skill index no longer needs a manual step, and the CLI-only hosts now
    actually get the pointer.
    Four gaps that together meant "installed" did not
    mean "working":

    • mcptoon skills index was required before resolve/route/the MCP tools
      would answer anything, and a skill added afterwards stayed invisible until it
      was re-run. The index now builds itself on first use and rebuilds when the
      catalog on disk no longer matches the signature it was built from — a
      stat-only probe (measured 0.25s cold, 0.03s warm for this machine's 1,217
      manifests, against 0.4s for a full parse), so it rides every resolve.
      list/stats stay strict readers and never build one as a side effect.
      mcptoon quickstart also builds it at the end of onboarding, so the catalog
      is answerable the moment the install finishes.
    • The codex sync target wrote to Path.cwd()/AGENTS.md — the pointer landed
      in whichever project the user happened to be in, or nowhere — and its text
      named only mcptoon manifest, never the catalog. It now targets
      ~/.codex/AGENTS.md, writes a skill pointer, is idempotent, and is gated on
      --self like the gateway registration. The undo (mcptoon off) removes
      exactly that block, byte-for-byte.
    • codex was not in detect_installed_agents, so the automatic path never
      reached it. It is now detected by the presence of its AGENTS.md.
    • Claude Code was not a sync target at all, though it is the host where the
      MCP handshake was verified working. ~/.claude.json (flat mcpServers, the
      same shape as Cursor) now syncs, and it is detected when the file exists.

Changed

  • The handshake now points at the skill tool, and costs less. The instructions
    field of the initialize result is the one channel that reaches the model without a
    tool call, and it talked only about the savings line. It now opens by naming
    mcptoon_resolve_skills: a list of tools says nothing about when to reach for one,
    so an agent that never sees a skill list in its prompt had no reason to call a
    resolver. The same edit tightened the rest of the paragraph, so the whole block went
    from 351 to 314 tokens — the pointer was paid for out of prose, not added on top. The
    directive names the mounted tool rather than the shell command, because the reader
    always has this gateway and may have no shell.

Don't miss a new mcptoon release

NewReleases is sending notifications on new releases.