merge upstream

This commit is contained in:
Taylor Wilsdon
2026-01-31 13:14:27 -05:00
43 changed files with 6531 additions and 481 deletions

View File

@@ -8,6 +8,8 @@ based on tier configuration, replacing direct @server.tool() decorators.
import logging
from typing import Set, Optional, Callable
from auth.oauth_config import is_oauth21_enabled
logger = logging.getLogger(__name__)
# Global registry of enabled tools
@@ -79,7 +81,8 @@ def wrap_server_tool_method(server):
def filter_server_tools(server):
"""Remove disabled tools from the server after registration."""
enabled_tools = get_enabled_tools()
if enabled_tools is None:
oauth21_enabled = is_oauth21_enabled()
if enabled_tools is None and not oauth21_enabled:
return
tools_removed = 0
@@ -95,30 +98,40 @@ def filter_server_tools(server):
read_only_mode = is_read_only_mode()
allowed_scopes = set(get_all_read_only_scopes()) if read_only_mode else None
tools_to_remove = []
for tool_name, tool_func in tool_registry.items():
# 1. Tier filtering
if not is_tool_enabled(tool_name):
tools_to_remove.append(tool_name)
continue
tools_to_remove = set()
# 1. Tier filtering
if enabled_tools is not None:
for tool_name in list(tool_registry.keys()):
if not is_tool_enabled(tool_name):
tools_to_remove.add(tool_name)
# 2. OAuth 2.1 filtering
if oauth21_enabled and "start_google_auth" in tool_registry:
tools_to_remove.add("start_google_auth")
logger.info("OAuth 2.1 enabled: disabling start_google_auth tool")
# 3. Read-only mode filtering
if read_only_mode:
for tool_name, tool_func in tool_registry.items():
if tool_name in tools_to_remove:
continue
# 2. Read-only filtering
if read_only_mode:
# Check if tool has required scopes attached (from @require_google_service)
# Note: FastMCP wraps functions in Tool objects, so we need to check .fn if available
func_to_check = tool_func
if hasattr(tool_func, "fn"):
func_to_check = tool_func.fn
required_scopes = getattr(func_to_check, "_required_google_scopes", [])
if required_scopes:
# If ANY required scope is not in the allowed read-only scopes, disable the tool
if not all(scope in allowed_scopes for scope in required_scopes):
logger.info(
f"Read-only mode: Disabling tool '{tool_name}' (requires write scopes: {required_scopes})"
)
tools_to_remove.append(tool_name)
tools_to_remove.add(tool_name)
for tool_name in tools_to_remove:
if tool_name in tool_registry:
@@ -126,6 +139,10 @@ def filter_server_tools(server):
tools_removed += 1
if tools_removed > 0:
from auth.scopes import is_read_only_mode
enabled_count = len(enabled_tools) if enabled_tools is not None else "all"
mode = "Read-Only" if is_read_only_mode() else "Full"
logger.info(
f"Tool filtering: removed {tools_removed} tools. Mode: {'Read-Only' if is_read_only_mode() else 'Full'}"
f"Tool filtering: removed {tools_removed} tools, {enabled_count} enabled. Mode: {mode}"
)