generated from personal-projects/leo-claude-mktplace
Implement tool filtering module
This commit implements a flexible tool filtering system for Claude Desktop compatibility. Features: - Whitelist mode: Only enable specified tools - Blacklist mode: Disable specified tools (default enables all) - Passthrough mode: No filtering (default if no lists provided) - Validation: Prevents conflicting enabled/disabled lists Implementation: - ToolFilter class with three filtering modes - should_include_tool() for individual tool checks - filter_tools_list() for filtering tool definition lists - filter_tools_response() for filtering MCP list_tools responses - get_filter_stats() for observability and debugging This module integrates with the configuration loader (#11) and will be used by the HTTP MCP server (#14) to ensure only compatible tools are exposed to Claude Desktop. Closes #12 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
"""Tool filtering module for Claude Desktop compatibility."""
|
||||
|
||||
__all__ = []
|
||||
from .filter import ToolFilter
|
||||
|
||||
__all__ = ["ToolFilter"]
|
||||
|
||||
108
src/gitea_http_wrapper/filtering/filter.py
Normal file
108
src/gitea_http_wrapper/filtering/filter.py
Normal file
@@ -0,0 +1,108 @@
|
||||
"""Tool filtering for Claude Desktop compatibility."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
|
||||
class ToolFilter:
|
||||
"""
|
||||
Filter MCP tools based on enabled/disabled lists.
|
||||
|
||||
This class handles tool filtering to ensure only compatible tools are exposed
|
||||
to Claude Desktop, preventing crashes from unsupported tool schemas.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
enabled_tools: list[str] | None = None,
|
||||
disabled_tools: list[str] | None = None,
|
||||
):
|
||||
"""
|
||||
Initialize tool filter.
|
||||
|
||||
Args:
|
||||
enabled_tools: List of tool names to enable. If None, all tools are enabled.
|
||||
disabled_tools: List of tool names to disable. Takes precedence over enabled_tools.
|
||||
|
||||
Raises:
|
||||
ValueError: If both enabled_tools and disabled_tools are specified.
|
||||
"""
|
||||
if enabled_tools is not None and disabled_tools is not None:
|
||||
raise ValueError(
|
||||
"Cannot specify both enabled_tools and disabled_tools. Choose one filtering mode."
|
||||
)
|
||||
|
||||
self.enabled_tools = set(enabled_tools) if enabled_tools else None
|
||||
self.disabled_tools = set(disabled_tools) if disabled_tools else None
|
||||
|
||||
def should_include_tool(self, tool_name: str) -> bool:
|
||||
"""
|
||||
Determine if a tool should be included based on filter rules.
|
||||
|
||||
Args:
|
||||
tool_name: Name of the tool to check.
|
||||
|
||||
Returns:
|
||||
True if tool should be included, False otherwise.
|
||||
"""
|
||||
# If disabled list is specified, exclude disabled tools
|
||||
if self.disabled_tools is not None:
|
||||
return tool_name not in self.disabled_tools
|
||||
|
||||
# If enabled list is specified, only include enabled tools
|
||||
if self.enabled_tools is not None:
|
||||
return tool_name in self.enabled_tools
|
||||
|
||||
# If no filters specified, include all tools
|
||||
return True
|
||||
|
||||
def filter_tools_list(self, tools: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
"""
|
||||
Filter a list of tool definitions.
|
||||
|
||||
Args:
|
||||
tools: List of tool definitions (dicts with at least a 'name' field).
|
||||
|
||||
Returns:
|
||||
Filtered list of tool definitions.
|
||||
"""
|
||||
return [tool for tool in tools if self.should_include_tool(tool.get("name", ""))]
|
||||
|
||||
def filter_tools_response(self, response: dict[str, Any]) -> dict[str, Any]:
|
||||
"""
|
||||
Filter tools from an MCP list_tools response.
|
||||
|
||||
Args:
|
||||
response: MCP response dict containing 'tools' list.
|
||||
|
||||
Returns:
|
||||
Filtered response with tools list updated.
|
||||
"""
|
||||
if "tools" in response and isinstance(response["tools"], list):
|
||||
response = response.copy()
|
||||
response["tools"] = self.filter_tools_list(response["tools"])
|
||||
return response
|
||||
|
||||
def get_filter_stats(self) -> dict[str, Any]:
|
||||
"""
|
||||
Get statistics about the filter configuration.
|
||||
|
||||
Returns:
|
||||
Dict containing filter mode and tool counts.
|
||||
"""
|
||||
if self.disabled_tools is not None:
|
||||
return {
|
||||
"mode": "blacklist",
|
||||
"disabled_count": len(self.disabled_tools),
|
||||
"disabled_tools": sorted(self.disabled_tools),
|
||||
}
|
||||
elif self.enabled_tools is not None:
|
||||
return {
|
||||
"mode": "whitelist",
|
||||
"enabled_count": len(self.enabled_tools),
|
||||
"enabled_tools": sorted(self.enabled_tools),
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"mode": "passthrough",
|
||||
"message": "All tools enabled",
|
||||
}
|
||||
Reference in New Issue
Block a user