feat(mcp): curated exclude list for cloudflare + glob tool filters + default_excluded manifests

The cloudflare entry's 3,320-endpoint surface is ~43% product families a
personal/dev account never touches (Zero Trust org-fleet suite, Magic
Transit/WAN, Cloudforce One, Radar analytics, API Shield, legacy
migration surfaces). Ship a 34-pattern curated exclude list in the
manifest: 3,320 -> 1,905 tools kept, and everything Cloudflare adds
later stays enabled by default.

Mechanism, two small extensions:
- tools/mcp_tool.py: tools.include/exclude entries containing glob
  metacharacters now match via fnmatch (plain names stay exact-match),
  so a product family is one pattern instead of hundreds of stale
  literals.
- hermes_cli/mcp_catalog.py: manifests may declare
  tools.default_excluded (mutually exclusive with default_enabled);
  install writes it to tools.exclude and skips the probe/checklist —
  a 3,320-row curses checklist is not a UX. Prior user include
  selections still win on reinstall.

Verified by replaying the real filter functions over the live-probed
3,320-tool list: 1,415 excluded, zero overmatch against a per-product
target audit; DNS/Workers/R2/D1/tunnels/Access/AI kept.
This commit is contained in:
Teknium 2026-07-20 08:51:47 -07:00
parent ce0defe4d8
commit 58c97b9ddd
No known key found for this signature in database
6 changed files with 295 additions and 14 deletions

View file

@ -112,6 +112,14 @@ class ToolsSpec:
# pre-checked (or no filter is written when probe fails).
default_enabled: Optional[List[str]] = None
# Exclude-mode counterpart: tool names/glob patterns written to
# ``mcp_servers.<name>.tools.exclude`` at install time. Everything NOT
# matching stays enabled — including tools the server adds later. Use for
# huge auto-generated surfaces (OpenAPI-derived MCPs) where an include
# list would be thousands of lines and freeze out new endpoints.
# Mutually exclusive with ``default_enabled``.
default_excluded: Optional[List[str]] = None
@dataclass
class CatalogEntry:
@ -241,7 +249,22 @@ def _parse_manifest(path: Path) -> CatalogEntry:
raise CatalogError(
f"{path}: tools.default_enabled must be a list of strings"
)
tools_spec = ToolsSpec(default_enabled=default_enabled)
default_excluded = tools_raw.get("default_excluded")
if default_excluded is not None:
if not isinstance(default_excluded, list) or not all(
isinstance(t, str) for t in default_excluded
):
raise CatalogError(
f"{path}: tools.default_excluded must be a list of strings"
)
if default_enabled is not None and default_excluded is not None:
raise CatalogError(
f"{path}: tools.default_enabled and tools.default_excluded are "
"mutually exclusive"
)
tools_spec = ToolsSpec(
default_enabled=default_enabled, default_excluded=default_excluded
)
install: Optional[InstallSpec] = None
install_raw = data.get("install")
@ -555,6 +578,22 @@ def _write_tools_include(name: str, include: Optional[List[str]]) -> None:
save_config(cfg)
def _write_tools_exclude(name: str, exclude: List[str]) -> None:
"""Persist ``mcp_servers.<name>.tools.exclude`` (names or glob patterns)."""
cfg = load_config()
servers = cfg.setdefault("mcp_servers", {})
server_entry = servers.get(name) or {}
tools_block = server_entry.get("tools") or {}
if not isinstance(tools_block, dict):
tools_block = {}
tools_block["exclude"] = list(exclude)
tools_block.pop("include", None)
server_entry["tools"] = tools_block
servers[name] = server_entry
cfg["mcp_servers"] = servers
save_config(cfg)
def _apply_tool_selection(
entry: CatalogEntry, *, prior_selection: Optional[List[str]]
) -> None:
@ -576,6 +615,23 @@ def _apply_tool_selection(
"""
print()
print(color(f" Probing '{entry.name}' for available tools...", Colors.CYAN))
# Exclude-mode manifests short-circuit the checklist entirely: the curated
# exclude list (names or glob patterns) is written as-is, everything else
# stays enabled — including tools the server adds later. A reinstall with
# a prior include selection still honours the user's own choice below.
if entry.tools.default_excluded and prior_selection is None:
_write_tools_exclude(entry.name, entry.tools.default_excluded)
print(color(
f" Applied manifest exclude list "
f"({len(entry.tools.default_excluded)} entries); everything else "
f"stays enabled. Edit mcp_servers.{entry.name}.tools.exclude in "
"config.yaml or run "
f"`hermes mcp configure {entry.name}` to change.",
Colors.GREEN,
))
return
probed = _probe_tools(entry.name)
# Probe failure path