Skip to content

Commit 38f6a8e

Browse files
committed
mcp(feat[tools]): Use Literal types for enum parameters in JSON schema
why: Parameters like `direction` and `scope` were typed as `str | None`, so the MCP input schema showed `{"type": "string"}` — LLMs had to read descriptions to discover valid values. Pydantic generates `{"enum": ["above", "below", ...]}` from `Literal` types, putting valid values directly in the JSON schema where LLMs can see them. what: - Use `t.Literal["above", "below", "left", "right"]` for split direction - Use `t.Literal["before", "after"]` for window placement direction - Use `t.Literal["server", "session", "window", "pane"]` for option scope - Keep manual validation as safety net for direct callers (belt-and-suspenders)
1 parent cbb548f commit 38f6a8e

File tree

6 files changed

+14
-14
lines changed

6 files changed

+14
-14
lines changed

src/libtmux/mcp/tools/option_tools.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
def _resolve_option_target(
3131
socket_name: str | None,
32-
scope: str | None,
32+
scope: t.Literal["server", "session", "window", "pane"] | None,
3333
target: str | None,
3434
) -> tuple[OptionsMixin, OptionScope | None]:
3535
"""Resolve the target object and scope for option operations."""
@@ -62,7 +62,7 @@ def _resolve_option_target(
6262
@handle_tool_errors
6363
def show_option(
6464
option: str,
65-
scope: str | None = None,
65+
scope: t.Literal["server", "session", "window", "pane"] | None = None,
6666
target: str | None = None,
6767
global_: bool = False,
6868
socket_name: str | None = None,
@@ -74,7 +74,7 @@ def show_option(
7474
option : str
7575
The tmux option name to query.
7676
scope : str, optional
77-
Option scope: "server", "session", "window", or "pane".
77+
Option scope.
7878
target : str, optional
7979
Target identifier. For session scope: session name
8080
(e.g. 'mysession'). For window scope: window ID (e.g. '@1').
@@ -98,7 +98,7 @@ def show_option(
9898
def set_option(
9999
option: str,
100100
value: str,
101-
scope: str | None = None,
101+
scope: t.Literal["server", "session", "window", "pane"] | None = None,
102102
target: str | None = None,
103103
global_: bool = False,
104104
socket_name: str | None = None,
@@ -112,7 +112,7 @@ def set_option(
112112
value : str
113113
The value to set.
114114
scope : str, optional
115-
Option scope: "server", "session", "window", or "pane".
115+
Option scope.
116116
target : str, optional
117117
Target identifier. For session scope: session name
118118
(e.g. 'mysession'). For window scope: window ID (e.g. '@1').

src/libtmux/mcp/tools/session_tools.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def create_window(
6464
window_name: str | None = None,
6565
start_directory: str | None = None,
6666
attach: bool = False,
67-
direction: str | None = None,
67+
direction: t.Literal["before", "after"] | None = None,
6868
socket_name: str | None = None,
6969
) -> str:
7070
"""Create a new window in a tmux session.
@@ -82,7 +82,7 @@ def create_window(
8282
attach : bool, optional
8383
Whether to make the new window active.
8484
direction : str, optional
85-
Window placement direction: "before" or "after".
85+
Window placement direction.
8686
socket_name : str, optional
8787
tmux socket name. Defaults to LIBTMUX_SOCKET env var.
8888
@@ -104,7 +104,7 @@ def create_window(
104104
"before": WindowDirection.Before,
105105
"after": WindowDirection.After,
106106
}
107-
resolved = direction_map.get(direction.lower())
107+
resolved = direction_map.get(direction)
108108
if resolved is None:
109109
from fastmcp.exceptions import ToolError
110110

src/libtmux/mcp/tools/window_tools.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def split_window(
9090
session_id: str | None = None,
9191
window_id: str | None = None,
9292
window_index: str | None = None,
93-
direction: str | None = None,
93+
direction: t.Literal["above", "below", "left", "right"] | None = None,
9494
size: str | int | None = None,
9595
start_directory: str | None = None,
9696
shell: str | None = None,
@@ -111,7 +111,7 @@ def split_window(
111111
window_index : str, optional
112112
Window index within the session.
113113
direction : str, optional
114-
Split direction: 'above', 'below', 'left', or 'right'.
114+
Split direction.
115115
size : str or int, optional
116116
Size of the new pane. Use a string with '%%' suffix for
117117
percentage (e.g. '50%%') or an integer for lines/columns.
@@ -131,7 +131,7 @@ def split_window(
131131

132132
pane_dir: PaneDirection | None = None
133133
if direction is not None:
134-
pane_dir = _DIRECTION_MAP.get(direction.lower())
134+
pane_dir = _DIRECTION_MAP.get(direction)
135135
if pane_dir is None:
136136
from fastmcp.exceptions import ToolError
137137

tests/mcp/test_option_tools.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def test_show_option_invalid_scope(mcp_server: Server, mcp_session: Session) ->
3333
with pytest.raises(ToolError, match="Invalid scope"):
3434
show_option(
3535
option="base-index",
36-
scope="global",
36+
scope="global", # type: ignore[arg-type]
3737
socket_name=mcp_server.socket_name,
3838
)
3939

tests/mcp/test_session_tools.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def test_create_window_invalid_direction(
6161
create_window(
6262
session_name=mcp_session.session_name,
6363
window_name="bad_dir",
64-
direction="sideways",
64+
direction="sideways", # type: ignore[arg-type]
6565
socket_name=mcp_server.socket_name,
6666
)
6767

tests/mcp/test_window_tools.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def test_split_window_invalid_direction(
6868
with pytest.raises(ToolError, match="Invalid direction"):
6969
split_window(
7070
window_id=window.window_id,
71-
direction="diagonal",
71+
direction="diagonal", # type: ignore[arg-type]
7272
socket_name=mcp_server.socket_name,
7373
)
7474

0 commit comments

Comments
 (0)