Files
python-sdk/pkg/hanzo-mcp/tests/test_default_surface.py
zeekay 7374804405 hanzo-mcp 0.15.21: the active mode is the whole manifest, and a flood of output has a ceiling
The default surface was 34 tools, 61 KB, ~15,300 tokens of schema in every
prompt of every agent. The mode system already names what a session should
carry, but only the tools register_all_tools enumerates obeyed it: zsh,
curl, playwright and the helpers (ps, npx, uvx, jq, wget, cdp, open) stayed
on in every mode. A mode now decides every discovered tool, the `hanzo`
mode lists what a coding agent calls every few steps (18 tools, 32 KB,
~8,000 tokens), and the rest is a name away: HANZO_MCP_ENABLED_TOOLS=a,b
or =all, `tool enable`, or `mode`.

hanzo-tools-shell 0.6.6: run_shell kept every byte a foreground command
wrote until the command ended. `yes` for thirty seconds is gigabytes. It
keeps the first 16 MiB per stream and drains the rest.

The tests' session-wide patch of asyncio.create_subprocess_exec handed
every shell command a stream that never reached EOF; the first test to run
one read it in a loop until the machine was out of memory (22 GB, killed).
The patch is now opt-in and its process has exited.

bench/latency.py measures the cold start, the tools/list bill and the common
calls through a real MCP client over stdio. Interleaved rounds, wall and CPU
seconds, p50 over 6 cold starts:

                         cold wall  cold CPU  tools  bytes   ~tokens
  installed 0.15.18      9.34 s     10.46 s   30     52,601  13,150
  main 0.15.20           1.52 s      1.13 s   34     61,287  15,321
  this                   0.96 s      1.06 s   18     32,222   8,055

The installed environment's 9 s is its hanzoai 8.5.156, whose package init
imports the whole generated cloud client; hanzo-mcp >= 0.15.20 requires
hanzoai >= 8.5.624, which loads it lazily. Per-call latency is spawn-bound
(zsh 17.6 ms, git status 33 ms, fs.read 2.5 ms at p50) and moves with the
machine's load; no call path changed.
2026-10-12 08:47:57 -07:00

62 lines
2.3 KiB
Python

"""The default surface is what every prompt of an agent carries: tools/list is its schema bill.
The active mode is the whole manifest. A tool it does not list is off, whichever package ships it (before,
only the names `register_all_tools` enumerated obeyed the mode, so the shell and browser helpers stayed on
in every mode); what the user asks for by name stands over the mode.
"""
import asyncio
import json
import pytest
from mcp.server.fastmcp import FastMCP
from hanzo_mcp.tools import register_all_tools
from hanzo_mcp.tools.common.permissions import PermissionManager
from hanzo_mcp.tools.common.mode import ModeRegistry
from hanzo_mcp.tools.common.mode_loader import ModeLoader
from hanzo_mcp.tools.common.personality import DEFAULT_TOOLS
# Tools registered outside any mode: the way to a wider surface.
SYSTEM = {"tool", "version", "stats", "mode"}
BUDGET_BYTES = 36_000 # the default surface at 0.15.20 was 61,000
@pytest.fixture(autouse=True)
def _isolated(monkeypatch, tmp_path):
monkeypatch.setenv("HOME", str(tmp_path))
monkeypatch.delenv("HANZO_MCP_ENABLED_TOOLS", raising=False)
monkeypatch.delenv("HANZO_MCP_MODE", raising=False)
ModeRegistry.clear() if hasattr(ModeRegistry, "clear") else None
ModeLoader.initialize_modes()
def listed():
server = FastMCP("surface")
register_all_tools(server, PermissionManager())
return asyncio.run(server.list_tools())
def test_the_default_lists_the_default_mode_and_nothing_else():
names = {t.name for t in listed()}
assert names <= set(DEFAULT_TOOLS) | SYSTEM, sorted(names - set(DEFAULT_TOOLS) - SYSTEM)
assert {"fs", "zsh", "git", "browser", "playwright"} <= names
def test_the_default_stays_inside_its_schema_budget():
size = sum(len(json.dumps(t.model_dump(exclude_none=True))) for t in listed())
assert size < BUDGET_BYTES, size
def test_a_name_asked_for_stands_over_the_mode(monkeypatch):
assert "memory" not in {t.name for t in listed()}
monkeypatch.setenv("HANZO_MCP_ENABLED_TOOLS", "memory,critic")
assert {"memory", "critic"} <= {t.name for t in listed()}
def test_all_restores_every_tool(monkeypatch):
base = {t.name for t in listed()}
monkeypatch.setenv("HANZO_MCP_ENABLED_TOOLS", "all")
names = {t.name for t in listed()}
assert base < names and {"memory", "computer", "wget"} <= names