Skip to content

Commit 7b6abc5

Browse files
committed
mcp(feat[session_tools]): add get_session_info for single-session metadata
why: completes the symmetry started by get_window_info. Agents given a session_id and asked "how many windows does this session have?" no longer need to call list_sessions and filter. The bounded-to-core-hierarchy rule from get_window_info applies here too — inline comment memorializes it. what: - Add get_session_info(session_id, session_name, socket_name) returning SessionInfo. Reuses _resolve_session and _serialize_session; no new helpers. - Register with ANNOTATIONS_RO + TAG_READONLY, placed next to list_windows in the Session tool group. - Add test_get_session_info (by id) and test_get_session_info_by_name mirroring the test_list_windows style. - Add docs/tools/session/get-session-info.md modeled after the peer get-window-info page; insert the new page into the Session index grid and toctree. - Append get_session_info to the README tool catalog Session row.
1 parent 34af705 commit 7b6abc5

5 files changed

Lines changed: 116 additions & 1 deletion

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Give your AI agent hands inside the terminal — create sessions, run commands,
1616
| Module | Tools |
1717
|--------|-------|
1818
| **Server** | `list_sessions`, `create_session`, `kill_server`, `get_server_info` |
19-
| **Session** | `list_windows`, `create_window`, `rename_session`, `select_window`, `kill_session` |
19+
| **Session** | `list_windows`, `get_session_info`, `create_window`, `rename_session`, `select_window`, `kill_session` |
2020
| **Window** | `list_panes`, `get_window_info`, `split_window`, `rename_window`, `select_layout`, `resize_window`, `move_window`, `kill_window` |
2121
| **Pane** | `send_keys`, `paste_text`, `capture_pane`, `snapshot_pane`, `search_panes`, `get_pane_info`, `wait_for_text`, `wait_for_content_change`, `display_message`, `select_pane`, `swap_pane`, `resize_pane`, `set_pane_title`, `clear_pane`, `pipe_pane`, `enter_copy_mode`, `exit_copy_mode`, `kill_pane` |
2222
| **Options** | `show_option`, `set_option` |
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Get session info
2+
3+
```{fastmcp-tool} session_tools.get_session_info
4+
```
5+
6+
**Use when** you need metadata for a single session (ID, name, window
7+
count, attachment status, activity timestamp) and you already know its
8+
`session_id` or `session_name`. Avoids the `list_sessions` + filter dance.
9+
10+
**Avoid when** you need every session — call `list_sessions` or iterate
11+
via the `tmux://sessions` resource.
12+
13+
**Side effects:** None. Readonly.
14+
15+
**Example:**
16+
17+
```json
18+
{
19+
"tool": "get_session_info",
20+
"arguments": {
21+
"session_id": "$0"
22+
}
23+
}
24+
```
25+
26+
Response:
27+
28+
```json
29+
{
30+
"session_id": "$0",
31+
"session_name": "dev",
32+
"window_count": 3,
33+
"session_attached": "1",
34+
"session_created": "1713600000",
35+
"active_pane_id": "%0"
36+
}
37+
```
38+
39+
Resolve by name when only the session_name is known:
40+
41+
```json
42+
{
43+
"tool": "get_session_info",
44+
"arguments": {
45+
"session_name": "dev"
46+
}
47+
}
48+
```
49+
50+
```{fastmcp-tool-input} session_tools.get_session_info
51+
```

docs/tools/session/index.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ Session-scoped tools — enumerate windows, rename or kill a session, switch win
99
Enumerate windows inside a session.
1010
:::
1111

12+
:::{grid-item-card} {tooliconl}`get-session-info`
13+
Read metadata for one session.
14+
:::
15+
1216
:::{grid-item-card} {tooliconl}`select-window`
1317
Switch to a window by id, index, or direction.
1418
:::
@@ -32,6 +36,7 @@ Terminate a session. Destructive.
3236
:maxdepth: 1
3337
3438
list-windows
39+
get-session-info
3540
select-window
3641
create-window
3742
rename-session

src/libtmux_mcp/tools/session_tools.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,40 @@ def list_windows(
7171
return _apply_filters(windows, filters, _serialize_window)
7272

7373

74+
# get_session_info completes the core-tmux-hierarchy symmetry alongside
75+
# get_window_info / get_pane_info / get_server_info. Bounded to the four
76+
# hierarchy levels — see the same note in window_tools.get_window_info.
77+
@handle_tool_errors
78+
def get_session_info(
79+
session_id: str | None = None,
80+
session_name: str | None = None,
81+
socket_name: str | None = None,
82+
) -> SessionInfo:
83+
"""Return metadata for a single tmux session (ID, name, window count, activity).
84+
85+
Use this instead of list_sessions + filter when you only need one
86+
session's info. Resolves by session_id first; falls back to
87+
session_name.
88+
89+
Parameters
90+
----------
91+
session_id : str, optional
92+
Session ID (e.g. '$0').
93+
session_name : str, optional
94+
Session name.
95+
socket_name : str, optional
96+
tmux socket name.
97+
98+
Returns
99+
-------
100+
SessionInfo
101+
Serialized session metadata.
102+
"""
103+
server = _get_server(socket_name=socket_name)
104+
session = _resolve_session(server, session_name=session_name, session_id=session_id)
105+
return _serialize_session(session)
106+
107+
74108
@handle_tool_errors
75109
def create_window(
76110
session_name: str | None = None,
@@ -298,6 +332,9 @@ def register(mcp: FastMCP) -> None:
298332
mcp.tool(title="List Windows", annotations=ANNOTATIONS_RO, tags={TAG_READONLY})(
299333
list_windows
300334
)
335+
mcp.tool(title="Get Session Info", annotations=ANNOTATIONS_RO, tags={TAG_READONLY})(
336+
get_session_info
337+
)
301338
mcp.tool(
302339
title="Create Window", annotations=ANNOTATIONS_CREATE, tags={TAG_MUTATING}
303340
)(create_window)

tests/test_session_tools.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from libtmux_mcp.tools.session_tools import (
1111
create_window,
12+
get_session_info,
1213
kill_session,
1314
list_windows,
1415
rename_session,
@@ -40,6 +41,27 @@ def test_list_windows_by_id(mcp_server: Server, mcp_session: Session) -> None:
4041
assert len(result) >= 1
4142

4243

44+
def test_get_session_info(mcp_server: Server, mcp_session: Session) -> None:
45+
"""get_session_info returns a SessionInfo for a single session."""
46+
result = get_session_info(
47+
session_id=mcp_session.session_id,
48+
socket_name=mcp_server.socket_name,
49+
)
50+
assert result.session_id == mcp_session.session_id
51+
assert result.session_name == mcp_session.session_name
52+
assert result.window_count >= 1
53+
54+
55+
def test_get_session_info_by_name(mcp_server: Server, mcp_session: Session) -> None:
56+
"""get_session_info resolves by session_name when no ID is given."""
57+
assert mcp_session.session_name is not None
58+
result = get_session_info(
59+
session_name=mcp_session.session_name,
60+
socket_name=mcp_server.socket_name,
61+
)
62+
assert result.session_id == mcp_session.session_id
63+
64+
4365
def test_create_window(mcp_server: Server, mcp_session: Session) -> None:
4466
"""create_window creates a new window in a session."""
4567
result = create_window(

0 commit comments

Comments
 (0)