Skip to content

Commit 618a583

Browse files
committed
feat(catalog): implement meta tool filtering in catalog processing
- Introduced a new function `is_meta_tool` to identify and filter out meta tools from various catalog processing functions. - Updated `_normalize_tool`, `_normalize_tools`, `record_tool_attempt_feedback`, and `filter_tools_for_tier_tracking` to skip processing for tools identified as meta tools. - Enhanced `build_catalog_from_tools` and `_catalog_entries_from_dicts` to prevent meta tools from being included in the catalog. - Improved overall catalog management by ensuring that meta tools do not interfere with standard tool operations.
1 parent 4740fe1 commit 618a583

7 files changed

Lines changed: 38 additions & 25 deletions

File tree

‎src/cyt/cyt_mcp/catalog.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,9 +192,13 @@ def _normalize_catalog_scope(raw: object) -> str | None:
192192

193193

194194
def _normalize_tool(tool: dict[str, Any]) -> dict[str, Any] | None:
195+
from cyt_mcp.search import is_meta_tool
196+
195197
name = str(tool.get("name") or "").strip()
196198
if not name:
197199
return None
200+
if is_meta_tool(name):
201+
return None
198202
schema = tool.get("input_schema")
199203
if not isinstance(schema, dict):
200204
schema = tool.get("inputSchema")

‎src/cyt/hook/catalog_registry.py‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,16 @@ def _registry_key(
111111
def _normalize_tools(raw: object) -> list[dict[str, Any]]:
112112
if not isinstance(raw, list):
113113
return []
114+
from cyt_mcp.search import is_meta_tool
115+
114116
tools: list[dict[str, Any]] = []
115117
for item in raw:
116-
if isinstance(item, dict):
117-
tools.append(copy.deepcopy(item))
118+
if not isinstance(item, dict):
119+
continue
120+
name = str(item.get("name") or "").strip()
121+
if name and is_meta_tool(name):
122+
continue
123+
tools.append(copy.deepcopy(item))
118124
return tools
119125

120126

‎src/cyt/tiers/adapters/tools.py‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,14 @@ def filter_tools_for_tier_tracking(
5858
effective = resolve_effective_permissions(config=config)
5959
deny_entries = effective.mcp.deny
6060
tracked: list[dict[str, Any]] = []
61+
from cyt_mcp.search import is_meta_tool
62+
6163
for tool in tools:
6264
if not isinstance(tool, dict):
6365
continue
66+
catalog_name = str(tool.get("name") or "").strip()
67+
if catalog_name and is_meta_tool(catalog_name):
68+
continue
6469
stamped = stamp_tool_catalog_source(tool)
6570
if resolve_tool_catalog_source(stamped) not in allowed:
6671
continue

‎src/cyt/tiers/feedback.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,17 @@ def record_tool_attempt_feedback(
6666
tool_tracked_for_config,
6767
)
6868

69+
from cyt_mcp.search import is_meta_tool
70+
71+
if is_meta_tool(tool_name):
72+
return
6973
canonical_name = resolve_canonical_tool_name_for_tiers(
7074
tool_name,
7175
config=cfg,
7276
catalog=catalog,
7377
)
78+
if is_meta_tool(canonical_name):
79+
return
7480
tool = stamp_tool_catalog_source(
7581
{
7682
"name": canonical_name,

‎src/cyt_mcp/catalog_build.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
from cyt_mcp.config import AggregatorConfig
1414
from cyt_mcp.runtime_cache import RuntimeToolCache
15-
from cyt_mcp.search import MCP_WIRE_SEARCH_TOOL_NAME, refresh_search_tool_schema
15+
from cyt_mcp.search import MCP_WIRE_SEARCH_TOOL_NAME, is_meta_tool, refresh_search_tool_schema
1616
from cyt_mcp.tool_identity import enrich_tool_identity, tool_name_allowed_for_servers
1717

1818
JsonScalar = str | int | float | bool | None
@@ -86,7 +86,7 @@ def build_catalog_from_tools(
8686
for tool in tools:
8787
mcp_tool = tool.to_mcp_tool()
8888
name = str(mcp_tool.name)
89-
if name == MCP_WIRE_SEARCH_TOOL_NAME:
89+
if is_meta_tool(name):
9090
continue
9191
if server_keys and not tool_name_allowed_for_servers(name, server_keys):
9292
continue
@@ -244,7 +244,7 @@ def _catalog_entries_from_dicts(
244244
search_index: dict[str, dict[str, Any]] = {}
245245
for raw in tools:
246246
name = str(raw.get("name") or "").strip()
247-
if not name or name == MCP_WIRE_SEARCH_TOOL_NAME:
247+
if not name or is_meta_tool(name):
248248
continue
249249
if server_keys and not tool_name_allowed_for_servers(name, server_keys):
250250
continue

‎src/cyt_mcp/search.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@
2929
_EXCLUDED_LOOKUP_NAMES = frozenset({SEARCH_TOOL_NAME, MCP_WIRE_SEARCH_TOOL_NAME})
3030

3131

32+
def is_meta_tool(tool_name: str) -> bool:
33+
"""True for cyt-mcp frontend meta tools that must not enter hook/tier catalogs."""
34+
normalized = str(tool_name or "").strip()
35+
if not normalized:
36+
return True
37+
if normalized in _EXCLUDED_LOOKUP_NAMES:
38+
return True
39+
return normalized.endswith(("_get-tool-definitions", "__get-tool-definitions"))
40+
41+
3242
def search_tool_description(*, agent: str | None) -> str:
3343
description = _SEARCH_TOOL_BASE_DESCRIPTION
3444
if agent == "cursor":

‎src/cyt_mcp/tool_use_feedback_middleware.py‎

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,13 @@
1414
from cyt_mcp.config import AggregatorConfig, load_known_mcp_server_keys
1515
from cyt_mcp.config_holder import ConfigHolder
1616
from cyt_mcp.runtime_cache import RuntimeToolCache
17-
from cyt_mcp.search import MCP_WIRE_SEARCH_TOOL_NAME, SEARCH_TOOL_NAME
17+
from cyt_mcp.search import is_meta_tool
1818
from cyt_mcp.session_context import get_current_session_runtime
1919
from cyt_mcp.tier_feedback_push import schedule_tool_use_feedback
2020
from cyt_mcp.tool_identity import canonical_backend_identity, resolve_backend_identity
2121

2222
logger = logging.getLogger(__name__)
2323

24-
_META_TOOL_NAMES = frozenset(
25-
{
26-
SEARCH_TOOL_NAME,
27-
MCP_WIRE_SEARCH_TOOL_NAME,
28-
"cyt-mcp_get-tool-definitions",
29-
},
30-
)
31-
32-
33-
def _is_meta_tool(tool_name: str) -> bool:
34-
normalized = tool_name.strip()
35-
if not normalized:
36-
return True
37-
if normalized in _META_TOOL_NAMES:
38-
return True
39-
return normalized.endswith(("_get-tool-definitions", "__get-tool-definitions"))
40-
41-
4224
def _tool_call_succeeded(result: ToolResult) -> bool:
4325
if getattr(result, "is_error", False):
4426
return False
@@ -105,7 +87,7 @@ async def on_call_tool(
10587
arguments = getattr(params, "arguments", None)
10688
args = dict(arguments) if isinstance(arguments, dict) else {}
10789

108-
if _is_meta_tool(tool_name):
90+
if is_meta_tool(tool_name):
10991
return await call_next(context)
11092

11193
success = False

0 commit comments

Comments
 (0)