mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-31 19:16:29 +00:00
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:
parent
ce0defe4d8
commit
58c97b9ddd
6 changed files with 295 additions and 14 deletions
|
|
@ -92,6 +92,7 @@ Thread safety:
|
|||
import asyncio
|
||||
import contextvars
|
||||
import concurrent.futures
|
||||
import fnmatch
|
||||
import inspect
|
||||
import json
|
||||
import logging
|
||||
|
|
@ -4862,7 +4863,7 @@ def _build_utility_schemas(server_name: str) -> List[dict]:
|
|||
|
||||
|
||||
def _normalize_name_filter(value: Any, label: str) -> set[str]:
|
||||
"""Normalize include/exclude config to a set of tool names."""
|
||||
"""Normalize include/exclude config to a set of tool names/patterns."""
|
||||
if value is None:
|
||||
return set()
|
||||
if isinstance(value, str):
|
||||
|
|
@ -4873,6 +4874,23 @@ def _normalize_name_filter(value: Any, label: str) -> set[str]:
|
|||
return set()
|
||||
|
||||
|
||||
def _name_filter_matches(name: str, filter_set: set) -> bool:
|
||||
"""True if *name* matches any entry in a normalized include/exclude set.
|
||||
|
||||
Entries containing a glob metacharacter (``*``, ``?``, ``[``) are matched
|
||||
with :func:`fnmatch.fnmatchcase`; plain entries are exact names. Globs let
|
||||
huge auto-generated surfaces (e.g. an OpenAPI-derived MCP exposing
|
||||
thousands of endpoint tools) be filtered by product family
|
||||
(``*_accounts_magic_*``) instead of thousand-line literal lists.
|
||||
"""
|
||||
if name in filter_set:
|
||||
return True
|
||||
for pat in filter_set:
|
||||
if ("*" in pat or "?" in pat or "[" in pat) and fnmatch.fnmatchcase(name, pat):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _parse_boolish(value: Any, default: bool = True) -> bool:
|
||||
"""Parse a bool-like config value with safe fallback."""
|
||||
if value is None:
|
||||
|
|
@ -5047,9 +5065,9 @@ def _register_server_tools(name: str, server: MCPServerTask, config: dict) -> Li
|
|||
|
||||
def _should_register(tool_name: str) -> bool:
|
||||
if include_set:
|
||||
return tool_name in include_set
|
||||
return _name_filter_matches(tool_name, include_set)
|
||||
if exclude_set:
|
||||
return tool_name not in exclude_set
|
||||
return not _name_filter_matches(tool_name, exclude_set)
|
||||
return True
|
||||
|
||||
for mcp_tool in server._tools:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue