Skip to content

Commit 58cb9ea

Browse files
committed
Add short tool aliases
Default registry now also exposes a curated set of model-friendly aliases (click, type, screenshot, drag, find_image, shell, ...) that point at the canonical ac_* tools. Reduces prompt verbosity without renaming the underlying API. Toggle off via JE_AUTOCONTROL_MCP_ALIASES=0 or build_default_tool_registry( aliases=False); read-only mode automatically filters destructive aliases out before expansion.
1 parent 97e7075 commit 58cb9ea

2 files changed

Lines changed: 88 additions & 2 deletions

File tree

je_auto_control/utils/mcp_server/tools/__init__.py

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
``_factories`` (per-domain ``MCPTool`` builders). Public consumers
66
should import only the names re-exported here.
77
"""
8-
from typing import List, Optional
8+
import os
9+
from dataclasses import replace
10+
from typing import Dict, List, Optional
911

1012
from je_auto_control.utils.mcp_server.tools._base import (
1113
MCPContent, MCPTool, MCPToolAnnotations, read_only_env_flag,
@@ -16,14 +18,65 @@
1618
)
1719

1820

19-
def build_default_tool_registry(read_only: Optional[bool] = None
21+
# Short, model-friendly aliases for the most-used tools. Each alias is
22+
# registered as an additional MCPTool entry pointing at the same handler.
23+
_DEFAULT_ALIASES: Dict[str, str] = {
24+
"click": "ac_click_mouse",
25+
"move_mouse": "ac_set_mouse_position",
26+
"mouse_pos": "ac_get_mouse_position",
27+
"scroll": "ac_mouse_scroll",
28+
"type": "ac_type_text",
29+
"press": "ac_press_key",
30+
"hotkey": "ac_hotkey",
31+
"screenshot": "ac_screenshot",
32+
"screen_size": "ac_screen_size",
33+
"find_image": "ac_locate_image_center",
34+
"find_text": "ac_locate_text",
35+
"click_text": "ac_click_text",
36+
"drag": "ac_drag",
37+
"list_windows": "ac_list_windows",
38+
"focus_window": "ac_focus_window",
39+
"wait_image": "ac_wait_for_image",
40+
"wait_pixel": "ac_wait_for_pixel",
41+
"diff_screens": "ac_diff_screenshots",
42+
"shell": "ac_shell",
43+
}
44+
45+
46+
def _aliases_enabled(explicit: Optional[bool]) -> bool:
47+
if explicit is not None:
48+
return bool(explicit)
49+
raw = os.environ.get("JE_AUTOCONTROL_MCP_ALIASES", "1")
50+
return raw.strip().lower() in {"1", "true", "yes", "on"}
51+
52+
53+
def _make_aliases(tools: List[MCPTool]) -> List[MCPTool]:
54+
by_name: Dict[str, MCPTool] = {tool.name: tool for tool in tools}
55+
aliases: List[MCPTool] = []
56+
for short, canonical in _DEFAULT_ALIASES.items():
57+
target = by_name.get(canonical)
58+
if target is None:
59+
continue
60+
aliases.append(replace(
61+
target, name=short,
62+
description=f"Alias for {canonical}: {target.description}",
63+
))
64+
return aliases
65+
66+
67+
def build_default_tool_registry(read_only: Optional[bool] = None,
68+
aliases: Optional[bool] = None,
2069
) -> List[MCPTool]:
2170
"""Return the full set of tools the MCP server exposes by default.
2271
2372
:param read_only: when True, drop every tool whose annotations
2473
indicate it can mutate state. When None (default), the value
2574
of ``JE_AUTOCONTROL_MCP_READONLY`` is consulted, so deployments
2675
can pin the server in safe mode without code changes.
76+
:param aliases: when True, also register short model-friendly
77+
aliases (``click``, ``type``, ``screenshot`` ...) pointing at
78+
the canonical ``ac_*`` tools. Defaults to True; honour
79+
``JE_AUTOCONTROL_MCP_ALIASES=0`` to disable globally.
2780
"""
2881
enforce_read_only = (
2982
read_only_env_flag() if read_only is None else bool(read_only)
@@ -33,6 +86,8 @@ def build_default_tool_registry(read_only: Optional[bool] = None
3386
tools.extend(factory())
3487
if enforce_read_only:
3588
tools = [tool for tool in tools if tool.annotations.read_only]
89+
if _aliases_enabled(aliases):
90+
tools.extend(_make_aliases(tools))
3691
return tools
3792

3893

test/unit_test/headless/test_mcp_server.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,6 +1473,37 @@ def boom(x):
14731473
assert saved_paths
14741474

14751475

1476+
def test_default_registry_includes_short_aliases():
1477+
names = {tool.name for tool in build_default_tool_registry(aliases=True)}
1478+
assert {"click", "type", "screenshot", "find_image",
1479+
"drag", "shell"}.issubset(names)
1480+
1481+
1482+
def test_alias_handler_dispatches_to_canonical_tool():
1483+
by_name = {tool.name: tool
1484+
for tool in build_default_tool_registry(aliases=True)}
1485+
canonical = by_name["ac_get_mouse_position"]
1486+
alias = by_name["mouse_pos"]
1487+
assert alias.handler is canonical.handler
1488+
1489+
1490+
def test_aliases_env_flag_disables_them(monkeypatch):
1491+
monkeypatch.setenv("JE_AUTOCONTROL_MCP_ALIASES", "0")
1492+
names = {tool.name for tool in build_default_tool_registry()}
1493+
assert "click" not in names
1494+
assert "ac_click_mouse" in names
1495+
1496+
1497+
def test_aliases_excluded_from_read_only_registry():
1498+
names = {tool.name
1499+
for tool in build_default_tool_registry(read_only=True,
1500+
aliases=True)}
1501+
# Read-only filter runs before alias expansion, so destructive aliases drop.
1502+
assert "click" not in names
1503+
# Read-only canonical tools get their aliases.
1504+
assert "mouse_pos" in names
1505+
1506+
14761507
def test_default_registry_lists_core_automation_tools():
14771508
names = {tool.name for tool in build_default_tool_registry()}
14781509
expected = {

0 commit comments

Comments
 (0)