decomplect: HIP-0300 wire surface is the only public contract

The MCP wire (what tools/list reports) ships exactly one tool per axis:
fs, exec, code, git, fetch, plan — action-routed. The per-action classes
(ReadTool, WriteTool, EditTool, …) stay importable for in-process
read-only sandboxing (swarm sub-agents) but are NOT on the wire.

PyPI 0.3.1 of hanzo-tools-fs shipped the legacy split (7 tools) while
the local 0.3.1 source had already migrated to TOOLS=[FsTool]. Same
version number, two different contracts — the source of the
'Python MCP surfaces 32 tools, Node MCP surfaces 13' divergence
that breaks contract parity. Bumping to 0.3.2 publishes the
HIP-0300 surface for the first time.

- pkg/hanzo-tools-fs 0.3.1 → 0.3.2: TOOLS=[FsTool]; legacy classes
  importable; dropped 'Edit = EditTool' alias; cleaner __init__.
- pkg/hanzo-tools-shell 0.6.3 → 0.6.4: bump only (source already
  TOOLS=_get_detected_shell_tools()).
- pkg/hanzo-tools-code 0.1.0 → 0.1.1: bump only (TOOLS=[CodeTool]).
- pkg/hanzo-tools-vcs  0.1.0 → 0.1.1: bump only (TOOLS=[GitTool]).
- pkg/hanzo-tools-net  0.1.0 → 0.1.1: bump only (TOOLS=[FetchTool]).
- pkg/hanzo-tools-plan 0.1.0 → 0.1.1: bump only (TOOLS=[PlanTool]).
- pkg/hanzo-tools-agent 0.3.1 → 0.3.2: 'Edit' alias is gone — call
  EditTool by its real name in swarm_tool.py.
- pkg/hanzo-mcp 0.15.3 → 0.15.4: gets the HIP-0300-clean wire when
  consumed via 'uvx --refresh-package hanzo-tools-fs hanzo-mcp serve'.
This commit is contained in:
2026-05-13 13:51:48 -07:00
parent c66e0302cd
commit 572dcd68f2
10 changed files with 48 additions and 119 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "hanzo-mcp"
version = "0.15.3"
version = "0.15.4"
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"
@@ -154,7 +154,7 @@ except ImportError:
pass
from hanzo_tools.fs import Edit, get_read_only_filesystem_tools
from hanzo_tools.fs import EditTool, get_read_only_filesystem_tools
from hanzo_tools.core import BaseTool, PermissionManager, create_tool_context
from hanzo_tools.jupyter import get_read_only_jupyter_tools
@@ -518,8 +518,8 @@ Models can be specified as:
get_read_only_jupyter_tools(self.permission_manager)
)
# Add edit tools
self.available_tools.append(Edit(self.permission_manager))
# Add edit tool
self.available_tools.append(EditTool(self.permission_manager))
@override
@auto_timeout("swarm")
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "hanzo-tools-agent"
version = "0.3.1"
version = "0.3.2"
description = "Agent tools for Hanzo AI - multi-agent orchestration, CLI agents, swarms"
readme = "README.md"
requires-python = ">=3.12"
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "hanzo-tools-code"
version = "0.1.0"
version = "0.1.1"
description = "Code semantic tools for Hanzo AI (HIP-0300)"
readme = "README.md"
license = "MIT"
+37 -108
View File
@@ -1,76 +1,38 @@
"""Filesystem tools for Hanzo AI (HIP-0300).
Tools:
- fs: Unified filesystem tool (HIP-0300)
- read: Read file contents (returns hash)
- write: Create new files
- stat: File metadata
- list: Directory listing
- apply_patch: Edit with base_hash precondition
- search_text: Text search
- mkdir: Create directory
- rm: Remove (requires confirm=true)
The MCP wire surface is a single action-routed tool: `fs`.
IMPORTANT: apply_patch is the ONLY way to edit existing files.
It requires base_hash from a previous read to prevent stale edits.
fs.read fs.write fs.stat fs.list
fs.apply_patch fs.search_text
fs.mkdir fs.rm
Install:
pip install hanzo-tools-fs
`apply_patch` is the only way to edit an existing file and requires
`base_hash` from a prior `read` so stale edits are impossible.
Usage:
from hanzo_tools.fs import register_tools, TOOLS
# Register with MCP server
register_tools(mcp_server, permission_manager)
# Or access the unified tool
from hanzo_tools.fs import FsTool
The per-action classes (ReadTool, WriteTool, …) are kept importable for
in-process consumers that need read-only sandboxes — they are NOT
registered with the MCP server. The wire contract is `fs` only.
"""
# HIP-0300: Unified fs tool
# Legacy tools (backward compatibility)
from hanzo_tools.fs.ast import ASTTool
from hanzo_tools.fs.edit import EditTool
from hanzo_tools.fs.find import FindTool
from hanzo_tools.fs.fs_tool import FsTool, fs_tool
from hanzo_tools.fs.read import ReadTool
from hanzo_tools.fs.search import SearchTool
from hanzo_tools.fs.tree import TreeTool
from hanzo_tools.fs.write import WriteTool
from hanzo_tools.fs.search import SearchTool
from hanzo_tools.fs.fs_tool import FsTool, fs_tool
# Backwards compatibility aliases
Edit = EditTool
Read = ReadTool
Write = WriteTool
# HIP-0300 wire surface — what entry-point discovery registers with MCP.
TOOLS: list[type] = [FsTool]
# Read-only tools (for agent sandboxing)
READ_ONLY_TOOLS = [
ReadTool,
TreeTool,
FindTool,
SearchTool,
ASTTool,
]
# Export list for tool discovery - HIP-0300 unified tool
TOOLS = [FsTool]
# Legacy tools list (for backward compatibility)
LEGACY_TOOLS = [
ReadTool,
WriteTool,
EditTool,
TreeTool,
FindTool,
SearchTool,
ASTTool,
]
# In-process read-only set, used by sandboxed agents (e.g. swarm subagents).
# NOT on the MCP wire.
READ_ONLY_TOOLS: list[type] = [ReadTool, TreeTool, FindTool, SearchTool, ASTTool]
__all__ = [
# HIP-0300 unified tool
"FsTool",
"fs_tool",
# Legacy tool classes
"ReadTool",
"WriteTool",
"EditTool",
@@ -78,78 +40,45 @@ __all__ = [
"FindTool",
"SearchTool",
"ASTTool",
# Aliases
"Edit",
"Read",
"Write",
# Registration
"READ_ONLY_TOOLS",
"TOOLS",
"register_tools",
"get_read_only_filesystem_tools",
"TOOLS",
"LEGACY_TOOLS",
"READ_ONLY_TOOLS",
]
def get_read_only_filesystem_tools(permission_manager) -> list:
"""Get read-only filesystem tools for sandboxed agents.
"""Instantiate the read-only filesystem tool set for sandboxed agents.
Returns tools that can only read files, not modify them:
- read: Read file contents
- tree: View directory structure
- find: Find files by pattern
- search: Search file contents
- ast: Code structure analysis
Args:
permission_manager: PermissionManager instance
Returns:
List of instantiated read-only tools
These are NOT exposed on the MCP wire — they're used in-process by
agent runtimes that need to grant a sub-agent read-only file access
without giving it `fs.write` / `fs.apply_patch` / `fs.rm`.
"""
tools = []
for tool_class in READ_ONLY_TOOLS:
out = []
for cls in READ_ONLY_TOOLS:
try:
tools.append(tool_class(permission_manager))
out.append(cls(permission_manager))
except TypeError:
tools.append(tool_class())
return tools
out.append(cls())
return out
def register_tools(
mcp_server, permission_manager, enabled_tools: dict[str, bool] | None = None
):
"""Register all filesystem tools with the MCP server.
Args:
mcp_server: FastMCP server instance
permission_manager: PermissionManager for access control
enabled_tools: Dict of tool_name -> enabled state
Returns:
List of registered tool instances
"""
"""Register the unified `fs` tool with the MCP server."""
from hanzo_tools.core import ToolRegistry
enabled = enabled_tools or {}
registered = []
for tool_class in TOOLS:
tool_name = (
tool_class.name
if hasattr(tool_class, "name")
else tool_class.__name__.lower()
)
if enabled.get(tool_name, True): # Enabled by default
try:
tool = tool_class(permission_manager)
ToolRegistry.register_tool(mcp_server, tool)
registered.append(tool)
except TypeError:
# Tool doesn't need permission_manager
tool = tool_class()
ToolRegistry.register_tool(mcp_server, tool)
registered.append(tool)
name = getattr(tool_class, "name", tool_class.__name__.lower())
if not enabled.get(name, True):
continue
try:
tool = tool_class(permission_manager)
except TypeError:
tool = tool_class()
ToolRegistry.register_tool(mcp_server, tool)
registered.append(tool)
return registered
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "hanzo-tools-fs"
version = "0.3.1"
version = "0.3.2"
description = "Filesystem tools for Hanzo AI - read, write, edit, search, find"
readme = "README.md"
requires-python = ">=3.12"
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "hanzo-tools-net"
version = "0.1.0"
version = "0.1.1"
description = "Network tools for Hanzo AI (HIP-0300)"
readme = "README.md"
license = "MIT"
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "hanzo-tools-plan"
version = "0.1.0"
version = "0.1.1"
description = "Plan orchestration tools for Hanzo AI (HIP-0300)"
readme = "README.md"
license = "MIT"
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "hanzo-tools-shell"
version = "0.6.3"
version = "0.6.4"
description = "Shell/command execution tools with auto-detection - only exposes your active shell (zsh, bash, fish, dash, ksh, tcsh, csh)"
readme = "README.md"
requires-python = ">=3.12"
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "hanzo-tools-vcs"
version = "0.1.0"
version = "0.1.1"
description = "Unified version control tool for Hanzo AI (HIP-0300)"
readme = "README.md"
license = "MIT"