[eric] tools_lib: leading-_ -> p_/public across tools_lib/mcp_config/mcp_discovery/oauth_tokens/tool_taxonomy; promote cross-file public (load_all_tools/sanitize_server_name/discover_mcp_tools_*), fix re-export + attr test refs

This commit is contained in:
ciregenz
2026-06-23 20:30:14 -07:00
parent deeab52ed4
commit 71b2b33fae
16 changed files with 187 additions and 187 deletions
+17 -17
View File
@@ -25,8 +25,8 @@ def tools_tmp(tmp_path, monkeypatch):
d = tmp_path / "tools"
d.mkdir()
monkeypatch.setattr(tools_lib, "DATA_DIR", str(d))
monkeypatch.setattr(tools_lib, "_tools_cache", None)
monkeypatch.setattr(tools_lib, "_tools_cache_sig", None)
monkeypatch.setattr(tools_lib, "p_tools_cache", None)
monkeypatch.setattr(tools_lib, "p_tools_cache_sig", None)
return d
@@ -71,40 +71,40 @@ def test_settings_file_deleted_falls_back_to_defaults(settings_tmp):
def test_tools_write_then_list_is_fresh(tools_tmp):
assert tools_lib._load_all() == []
assert tools_lib.load_all_tools() == []
t = ToolDefinition(name="Alpha", description="a")
tools_lib._save(t)
tools_lib.save(t)
_bump_mtime(tools_tmp / f"{t.id}.json")
names = [x.name for x in tools_lib._load_all()]
names = [x.name for x in tools_lib.load_all_tools()]
assert names == ["Alpha"]
t2 = ToolDefinition(name="Beta", description="b")
tools_lib._save(t2)
assert sorted(x.name for x in tools_lib._load_all()) == ["Alpha", "Beta"]
tools_lib.save(t2)
assert sorted(x.name for x in tools_lib.load_all_tools()) == ["Alpha", "Beta"]
def test_tools_delete_detected(tools_tmp):
t = ToolDefinition(name="Gone", description="g")
tools_lib._save(t)
assert [x.name for x in tools_lib._load_all()] == ["Gone"]
tools_lib.save(t)
assert [x.name for x in tools_lib.load_all_tools()] == ["Gone"]
os.remove(tools_tmp / f"{t.id}.json")
assert tools_lib._load_all() == []
assert tools_lib.load_all_tools() == []
def test_tools_in_place_rewrite_detected(tools_tmp):
t = ToolDefinition(name="Old", description="x")
tools_lib._save(t)
assert [x.name for x in tools_lib._load_all()] == ["Old"]
tools_lib.save(t)
assert [x.name for x in tools_lib.load_all_tools()] == ["Old"]
t.name = "New"
tools_lib._save(t)
tools_lib.save(t)
_bump_mtime(tools_tmp / f"{t.id}.json")
assert [x.name for x in tools_lib._load_all()] == ["New"]
assert [x.name for x in tools_lib.load_all_tools()] == ["New"]
def test_tools_cached_hit_skips_reparse(tools_tmp, monkeypatch):
tools_lib._save(ToolDefinition(name="Once", description="o"))
tools_lib._load_all()
tools_lib.save(ToolDefinition(name="Once", description="o"))
tools_lib.load_all_tools()
def boom(*a, **k):
raise AssertionError("disk re-parse on unchanged dir")
monkeypatch.setattr(json, "load", boom)
assert [x.name for x in tools_lib._load_all()] == ["Once"]
assert [x.name for x in tools_lib.load_all_tools()] == ["Once"]
+11 -11
View File
@@ -11,7 +11,7 @@ tool-name shape, including the round-trip that the old code failed.
"""
from backend.apps.tools_lib.tools_lib import resolve_policy_slot, PolicySlot
from backend.apps.tools_lib.mcp_config import _sanitize_server_name
from backend.apps.tools_lib.mcp_config import sanitize_server_name
from backend.apps.tools_lib.models import ToolDefinition
@@ -34,7 +34,7 @@ def test_slot_for_our_browser_and_invoke_agents_uses_inner_name():
def test_slot_for_community_mcp_points_at_the_owning_tool():
tool = _mcp_tool("My Notion Server")
slug = _sanitize_server_name(tool.name)
slug = sanitize_server_name(tool.name)
assert resolve_policy_slot(f"mcp__{slug}__notion-fetch", [tool]) == \
PolicySlot("mcp", tool.id, "notion-fetch")
@@ -73,7 +73,7 @@ def test_always_approve_round_trips_for_every_tool_shape():
"""The invariant the old code violated: after WRITE(always_allow), the very
next READ returns always_allow, for builtin, our agents, and community MCP."""
notion = _mcp_tool("Notion")
slug = _sanitize_server_name("Notion")
slug = sanitize_server_name("Notion")
tools = [notion]
builtin_perms: dict[str, str] = {}
@@ -94,7 +94,7 @@ def test_always_approve_round_trips_for_every_tool_shape():
def test_two_actions_on_the_same_mcp_server_are_independent():
"""Approving one action must not silently approve a sibling action."""
tool = _mcp_tool("Notion")
slug = _sanitize_server_name("Notion")
slug = sanitize_server_name("Notion")
tools = [tool]
bp: dict[str, str] = {}
_write(f"mcp__{slug}__notion-fetch", "always_allow", bp, tools)
@@ -121,21 +121,21 @@ def test_builtin_policy_survives_a_real_file_reload(tmp_path, monkeypatch):
def test_mcp_policy_survives_a_real_tool_file_reload(tmp_path, monkeypatch):
monkeypatch.setattr(tl, "DATA_DIR", str(tmp_path))
monkeypatch.setattr(tl, "_tools_cache", None)
monkeypatch.setattr(tl, "_tools_cache_sig", None)
tl._save(_mcp_tool("Notion"))
slug = _sanitize_server_name("Notion")
monkeypatch.setattr(tl, "p_tools_cache", None)
monkeypatch.setattr(tl, "p_tools_cache_sig", None)
tl.save(_mcp_tool("Notion"))
slug = sanitize_server_name("Notion")
name = f"mcp__{slug}__notion-fetch"
# WRITE via the resolver against the freshly loaded tool, then persist.
tools = tl._load_all()
tools = tl.load_all_tools()
slot = tl.resolve_policy_slot(name, tools)
target = next(t for t in tools if t.id == slot.key)
target.tool_permissions[slot.action] = "always_allow"
tl._save(target)
tl.save(target)
# RELOAD from disk and read via the resolver: the policy is there.
tools2 = tl._load_all()
tools2 = tl.load_all_tools()
rslot = tl.resolve_policy_slot(name, tools2)
got = next(t for t in tools2 if t.id == rslot.key)
assert got.tool_permissions.get(rslot.action) == "always_allow"
+16 -16
View File
@@ -641,7 +641,7 @@ async def test_mcp_gate_only_forwards_activated_servers():
auth_status="configured", auth_type="apikey") for n in names]
# allowed_tools == get_all_tool_names() bypasses the (separate) permission
# gate so we isolate the ACTIVATION gate. _sanitize_server_name -> identity.
# gate so we isolate the ACTIVATION gate. sanitize_server_name -> identity.
with patch("backend.apps.agents.manager.RunSupportMixin.load_all_tools", side_effect=installed), \
patch("backend.apps.agents.manager.RunSupportMixin.get_all_tool_names", return_value=["__ALL__"]), \
patch("backend.apps.agents.manager.RunSupportMixin.sanitize_server_name", side_effect=lambda n: n), \
@@ -810,9 +810,9 @@ def test_mcp_brand_covers_curated_servers():
"google-workspace", "microsoft-365", "slack", "discord",
"notion", "airtable", "hubspot", "reddit", "youtube",
}
from backend.apps.tools_lib.tools_lib import _sanitize_server_name
from backend.apps.tools_lib.tools_lib import sanitize_server_name
for slug in curated:
assert _sanitize_server_name(slug) == slug, (
assert sanitize_server_name(slug) == slug, (
f"curated slug {slug!r} is not in sanitized form"
)
@@ -828,30 +828,30 @@ def test_curated_server_aliases_in_main():
def test_sanitize_server_name_idempotent():
"""_sanitize_server_name must be idempotent (sanitize twice = sanitize once)."""
from backend.apps.tools_lib.tools_lib import _sanitize_server_name
"""sanitize_server_name must be idempotent (sanitize twice = sanitize once)."""
from backend.apps.tools_lib.tools_lib import sanitize_server_name
test_inputs = [
"Google Workspace", "Microsoft 365", "Slack", "Discord",
"Notion", "Airtable", "HubSpot", "Reddit", "YouTube",
"GitHub", "GitLab", "Jira",
]
for raw in test_inputs:
once = _sanitize_server_name(raw)
twice = _sanitize_server_name(once)
once = sanitize_server_name(raw)
twice = sanitize_server_name(once)
assert once == twice, f"{raw}: sanitize not idempotent ({once} != {twice})"
def test_sanitize_server_name_lowercase():
from backend.apps.tools_lib.tools_lib import _sanitize_server_name
assert _sanitize_server_name("Gmail") == "gmail"
assert _sanitize_server_name("UPPERCASE") == "uppercase"
from backend.apps.tools_lib.tools_lib import sanitize_server_name
assert sanitize_server_name("Gmail") == "gmail"
assert sanitize_server_name("UPPERCASE") == "uppercase"
def test_sanitize_server_name_strips_special_chars():
from backend.apps.tools_lib.tools_lib import _sanitize_server_name
assert _sanitize_server_name("Foo Bar!") == "foo-bar"
assert _sanitize_server_name("@x/y") == "x-y"
assert _sanitize_server_name("a__b") == "a-b"
from backend.apps.tools_lib.tools_lib import sanitize_server_name
assert sanitize_server_name("Foo Bar!") == "foo-bar"
assert sanitize_server_name("@x/y") == "x-y"
assert sanitize_server_name("a__b") == "a-b"
# ===========================================================================
@@ -864,10 +864,10 @@ def test_mcp_activate_handler_unknown_server():
# We test the response shape independently of the FastAPI plumbing.
# The handler is a closure inside main.py:mcp_meta_handler, so we
# instead exercise the contract: invalid name surfaces alternatives.
from backend.apps.tools_lib.tools_lib import _sanitize_server_name
from backend.apps.tools_lib.tools_lib import sanitize_server_name
valid = {"gmail", "slack", "google-workspace"}
requested = "Gmail" # raw, needs sanitize
sanitized = _sanitize_server_name(requested)
sanitized = sanitize_server_name(requested)
if sanitized in valid:
status = "would_activate"
else: