mirror of
https://github.com/hanzoai/python-sdk.git
synced 2026-10-23 20:23:00 +00:00
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.
265 lines
8.0 KiB
TOML
265 lines
8.0 KiB
TOML
[build-system]
|
|
requires = ["setuptools>=61.0.0", "wheel"]
|
|
build-backend = "setuptools.build_meta"
|
|
|
|
[project]
|
|
name = "hanzo-mcp"
|
|
version = "0.15.21"
|
|
description = "The Zen of Hanzo MCP: One server to rule them all. The ultimate MCP that orchestrates all others."
|
|
readme = "README.md"
|
|
requires-python = ">=3.12"
|
|
license = { text = "MIT" }
|
|
authors = [{ name = "Hanzo Industries Inc", email = "[email protected]" }]
|
|
classifiers = [
|
|
"Programming Language :: Python :: 3",
|
|
"License :: OSI Approved :: MIT License",
|
|
"Operating System :: OS Independent",
|
|
]
|
|
keywords = ["mcp", "claude", "hanzo", "code", "agent"]
|
|
dependencies = [
|
|
# Hanzo AI core SDK (canonical types, config, session, MCP client)
|
|
# 2.2.1 is the first release shipping hanzoai.protocols (permissions.py imports it);
|
|
# 8.5.624 the first carrying hanzoai.usage, which the llm tools read refusals with
|
|
"hanzoai>=8.5.624",
|
|
# Core MCP dependencies
|
|
"mcp>=1.25.0,<2",
|
|
"fastmcp>=2.14.4",
|
|
"pydantic>=2.12.5",
|
|
"pydantic-settings>=2.12.0",
|
|
"typing-extensions>=4.13.0",
|
|
# Unified async I/O (aiofiles + uvloop auto-installed on Linux/macOS, graceful fallback)
|
|
"hanzo-async>=0.1.3",
|
|
"uvloop>=0.22.1; sys_platform != 'win32'",
|
|
# Hanzo tool infrastructure (all tools)
|
|
"hanzo-tools>=0.3.9", # 0.3.9: a cloud refusal carries its body and headers, send() returns headers
|
|
"hanzo-tools-fs>=0.3.4", # 0.3.4 returns image files as native MCP ImageContent
|
|
"hanzo-tools-shell>=0.6.6", # 0.6.6 bounds a foreground command's captured output; 0.6.5 pairs with hanzo-tools>=0.3.0 (ToolError)
|
|
"hanzo-tools-core>=0.3.1", # 0.3.1 is the EMPTY shim — no longer shadows canonical hanzo-tools.core
|
|
"hanzo-tools-browser[playwright]>=0.5.12", # 0.5.12 reaches the browser over the embedded ZAP router
|
|
"hanzo-tools-memory>=0.2.0",
|
|
"hanzo-tools-todo>=0.1.0",
|
|
"hanzo-tools-reasoning>=0.1.0",
|
|
"hanzo-tools-lsp>=0.1.0",
|
|
"hanzo-tools-computer>=0.5.5", # 0.5.5 screenshots downscale before they reach the wire
|
|
# 0.2.1 fixes ConfigTool passing permission_manager positionally into BaseTool.__init__
|
|
"hanzo-tools-config>=0.2.1",
|
|
"hanzo-tools-refactor>=0.1.0",
|
|
"hanzo-tools-llm>=0.2.2", # 0.2.2: llm limits, searched models, served/paid-by, typed plan refusals
|
|
"hanzo-tools-code>=0.1.0",
|
|
"hanzo-tools-vcs>=0.1.3", # 0.1.3 adds repo: repositories on Hanzo git (list, sync now)
|
|
"hanzo-tools-net>=0.1.0",
|
|
"hanzo-tools-agent>=0.3.1",
|
|
"hanzo-tools-api>=0.3.2", # 0.3.2 is the unified `hanzo` tool projected from cloud's OpenAPI registry
|
|
# The fleet is reached through the unified `hanzo` tool (hanzo-tools-api),
|
|
# which offers what cloud offers an AGENT — it withholds an operation whose
|
|
# name discloses a bearer secret, or whose mutating verb acts on identity or
|
|
# authority. The per-service packages that used to be installed here carried
|
|
# no such filter: hanzo-tools-kms handed a model `inject`, which reads every
|
|
# secret at a path, and hanzo-tools-iam handed it create_user/delete_user and
|
|
# the token and session reads. They remain installable for a human at a CLI.
|
|
"hanzo-tools-auth>=0.1.0",
|
|
"hanzo-tools-mpc>=0.1.0",
|
|
"hanzo-tools-ui>=0.1.0",
|
|
"hanzo-tools-devserver>=0.1.0", # running dev servers as MCP nodes: index, call, errors
|
|
"hanzo-persona>=1.0.0",
|
|
]
|
|
|
|
# For local development, install hanzo-tools-* packages from pkg/
|
|
# pip install -e ../hanzo-tools ../hanzo-tools-fs etc.
|
|
# Or when published, add them back as dependencies
|
|
|
|
[project.urls]
|
|
"Homepage" = "https://github.com/hanzoai/mcp"
|
|
"Bug Tracker" = "https://github.com/hanzoai/mcp/issues"
|
|
"Documentation" = "https://mcp.hanzo.ai"
|
|
|
|
[project.optional-dependencies]
|
|
# NOTE: hanzo-tools-* packages are installed separately from pkg/
|
|
# Use: pip install -e ../hanzo-tools -e ../hanzo-tools-fs etc.
|
|
# When published to PyPI, these extras will work:
|
|
# filesystem = ["hanzo-tools-fs>=0.1.0"]
|
|
# shell = ["hanzo-tools-shell>=0.1.0"]
|
|
# etc.
|
|
|
|
# Memory tools with full backend support
|
|
memory = [
|
|
"hanzo-tools-memory[full]>=0.2.2",
|
|
"sqlite-vec>=0.1.0",
|
|
"fastembed>=0.4.0",
|
|
]
|
|
|
|
# Interactive REPL with multi-language Jupyter kernels
|
|
repl = [
|
|
"hanzo-tools-repl>=0.1.0",
|
|
"jupyter-client>=8.6.0",
|
|
"ipykernel>=6.29.0",
|
|
]
|
|
|
|
# IDE integration (VS Code, Cursor, JetBrains)
|
|
ide = [
|
|
"hanzo-tools-ide>=0.1.0",
|
|
]
|
|
|
|
# Full interactive suite (REPL + IDE + Browser)
|
|
interactive = [
|
|
"hanzo-mcp[repl,ide]",
|
|
"hanzo-tools-browser>=0.2.1",
|
|
]
|
|
|
|
# Development
|
|
dev = [
|
|
"watchdog>=3.0.0",
|
|
"pytest>=7.0.0",
|
|
"pytest-cov>=4.1.0",
|
|
"ruff>=0.14.0",
|
|
"black>=23.3.0",
|
|
"mypy>=1.10.0",
|
|
"types-aiofiles>=23.2.0",
|
|
"types-psutil>=5.9.5",
|
|
"types-setuptools>=69.5.0",
|
|
]
|
|
docs = ["sphinx>=8.0.0", "sphinx-rtd-theme>=3.0.0", "myst-parser>=4.0.0", "sphinx-copybutton>=0.5.0"]
|
|
test = [
|
|
"pytest>=7.0.0",
|
|
"pytest-cov>=4.1.0",
|
|
"pytest-mock>=3.10.0",
|
|
"pytest-asyncio>=0.26.0,<1.0.0",
|
|
]
|
|
performance = [
|
|
"ujson>=5.7.0",
|
|
"orjson>=3.9.0",
|
|
]
|
|
publish = ["twine>=4.0.2", "build>=1.0.3"]
|
|
|
|
[project.scripts]
|
|
hanzo-mcp = "hanzo_mcp.cli:main"
|
|
hanzo-mcp-dev = "hanzo_mcp.dev_server:run_dev_server"
|
|
|
|
[tool.setuptools.packages.find]
|
|
where = ["."]
|
|
include = ["hanzo_mcp*"]
|
|
|
|
[tool.setuptools.package-data]
|
|
hanzo_mcp = ["py.typed"]
|
|
|
|
[tool.basedpyright]
|
|
include = ["hanzo_mcp"]
|
|
|
|
[tool.pytest.ini_options]
|
|
# Configuration for pytest
|
|
addopts = "--no-header --no-summary -p asyncio"
|
|
asyncio_mode = "auto"
|
|
asyncio_default_fixture_loop_scope = "function"
|
|
# Note: --cov-fail-under=96.3 will be added after achieving target coverage
|
|
markers = [
|
|
"asyncio: mark test as using asyncio",
|
|
"requires_hanzo_agents: mark test as requiring hanzo-agents SDK",
|
|
"requires_memory_tools: mark test as requiring hanzo-memory package",
|
|
"slow: mark test as slow running",
|
|
"integration: mark test as integration test",
|
|
]
|
|
testpaths = ["tests"]
|
|
python_files = ["test_*.py", "*_test.py"]
|
|
python_classes = ["Test*"]
|
|
python_functions = ["test_*"]
|
|
|
|
[tool.pytest_asyncio]
|
|
mode = "auto"
|
|
default_loop_scope = "function"
|
|
|
|
[tool.mypy]
|
|
python_version = "3.12"
|
|
strict = true
|
|
warn_return_any = true
|
|
warn_unused_configs = true
|
|
disallow_untyped_defs = true
|
|
disallow_any_generics = true
|
|
check_untyped_defs = true
|
|
no_implicit_optional = true
|
|
warn_redundant_casts = true
|
|
warn_unused_ignores = true
|
|
warn_no_return = true
|
|
warn_unreachable = true
|
|
strict_equality = true
|
|
show_error_codes = true
|
|
show_column_numbers = true
|
|
pretty = true
|
|
|
|
# Per-module options
|
|
[[tool.mypy.overrides]]
|
|
module = [
|
|
"bashlex.*",
|
|
"grep_ast.*",
|
|
"libtmux.*",
|
|
"llm.*",
|
|
"nbformat.*",
|
|
"ffind.*",
|
|
"fastmcp.*",
|
|
"mcp.*",
|
|
"hanzo_agents.*",
|
|
"hanzo_memory.*",
|
|
"personalities.*",
|
|
]
|
|
ignore_missing_imports = true
|
|
|
|
[tool.coverage.run]
|
|
source = ["hanzo_mcp"]
|
|
omit = [
|
|
"*/tests/*",
|
|
"*/__pycache__/*",
|
|
"*/site-packages/*",
|
|
"*/venv/*",
|
|
"*/migrations/*",
|
|
]
|
|
|
|
[tool.coverage.report]
|
|
precision = 2
|
|
show_missing = true
|
|
skip_covered = false
|
|
fail_under = 96.3
|
|
exclude_lines = [
|
|
"pragma: no cover",
|
|
"def __repr__",
|
|
"if self.debug:",
|
|
"if __name__ == .__main__.:",
|
|
"raise AssertionError",
|
|
"raise NotImplementedError",
|
|
"if TYPE_CHECKING:",
|
|
"@abstractmethod",
|
|
]
|
|
|
|
[tool.coverage.html]
|
|
directory = "htmlcov"
|
|
|
|
[tool.ruff]
|
|
exclude = [
|
|
".git",
|
|
".venv",
|
|
"venv",
|
|
"__pycache__",
|
|
"build",
|
|
"dist",
|
|
"*.egg-info",
|
|
"node_modules",
|
|
"vscode-extension",
|
|
]
|
|
|
|
[tool.ruff.lint]
|
|
select = ["E", "F", "I", "B"]
|
|
ignore = [
|
|
"E501", # Line too long
|
|
"B017", # Blind exception test
|
|
"B023", # Function definition loop variable
|
|
"B904", # Raise without from in except
|
|
"E741", # Ambiguous variable name
|
|
"E402", # Module level import not at top
|
|
"F401", # Unused imports (handled by tests themselves)
|
|
"F821", # Undefined name (tests may have conditional imports)
|
|
"F841", # Local variable assigned but never used
|
|
"B007", # Loop control variable not used
|
|
]
|
|
|
|
[tool.ruff.lint.per-file-ignores]
|
|
"tests/*" = ["F401", "F821", "F841", "B007"] # Relax rules for test files
|
|
"*/__init__.py" = ["F401"] # Allow unused imports in __init__.py
|