Skip to content

Commit 5ef52c9

Browse files
committed
mcp(feat[resources]): add tmux://reference/format-strings cheat sheet
Static markdown reference resource cataloging the tmux format strings agents most commonly encounter via ``display_message`` and friends: pane / window / session / server fields, plus the conditional and string-operation forms (``#{?cond,then,else}``, ``#{=N:expr}``, ``#{s/from/to/:expr}``, etc.). Why a resource and not a tool: tmux format strings are a closed catalog, not a query. Agents that hit a ``#{...}`` field they don't recognize need a fixed lookup, not a server round-trip. Pulling the reference is cheaper than guessing a name and burning a ``display_message`` call to confirm it. The new module follows the ``hierarchy.register(mcp)`` shape: closures decorated with ``@mcp.resource(...)`` and a trailing ``_ = (fn,)`` tuple to silence type-checker unused-name warnings without exporting the local name. Concrete URI (no template params), so it registers under ``mcp.list_resources()`` rather than ``list_resource_templates()``. Tests cover both layers: a closure-capture fixture verifies the body contains the spot-check format strings (``#{pane_id}``, ``#{window_id}``, ``#{session_id}``, ``#{?cond,then,else}``), and a real-FastMCP test verifies the registered resource advertises ``text/markdown`` so clients render it correctly.
1 parent 393cef4 commit 5ef52c9

3 files changed

Lines changed: 181 additions & 1 deletion

File tree

src/libtmux_mcp/resources/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
def register_resources(mcp: FastMCP) -> None:
1212
"""Register all resource modules with the FastMCP instance."""
13-
from libtmux_mcp.resources import hierarchy
13+
from libtmux_mcp.resources import hierarchy, reference
1414

1515
hierarchy.register(mcp)
16+
reference.register(mcp)
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
"""Static reference resources for tmux primitives.
2+
3+
Why a resource and not a tool: tmux format strings (``#{pane_id}``,
4+
``#{pane_in_mode}``, ``#{?cond,then,else}``) are a closed reference
5+
catalog, not a query. Agents that hit a ``#{...}`` field they don't
6+
recognize need a fixed lookup, not a tool round-trip. Exposing this as
7+
an MCP resource lets clients pull it on demand and lets the agent
8+
recover from an unknown-format-name guess without paying a
9+
``display_message`` round-trip just to discover what's available.
10+
"""
11+
12+
from __future__ import annotations
13+
14+
import textwrap
15+
import typing as t
16+
17+
if t.TYPE_CHECKING:
18+
from fastmcp import FastMCP
19+
20+
21+
#: MIME type for the format-string reference resource. Markdown gives
22+
#: clients enough structure to render headings and code spans without
23+
#: requiring a richer content type.
24+
_MARKDOWN_MIME = "text/markdown"
25+
26+
27+
_FORMAT_STRING_REFERENCE = textwrap.dedent("""\
28+
# tmux format strings
29+
30+
Pass via ``display_message(format_string="#{...}")`` or any other
31+
tool that accepts a tmux format expression.
32+
33+
## Pane
34+
35+
- ``#{pane_id}`` — globally unique pane identifier (e.g. ``%1``)
36+
- ``#{pane_index}`` — index within the window
37+
- ``#{pane_current_command}`` — foreground command name
38+
- ``#{pane_current_path}`` — current working directory
39+
- ``#{pane_pid}`` — pane process PID
40+
- ``#{pane_dead}`` — ``1`` when the pane's process has exited
41+
- ``#{pane_in_mode}`` — ``1`` when the pane is in copy/scroll mode
42+
- ``#{pane_mode}`` — current pane mode name when in mode
43+
- ``#{pane_active}`` — ``1`` for the active pane in its window
44+
- ``#{pane_width}`` / ``#{pane_height}`` — pane dimensions in cells
45+
- ``#{cursor_x}`` / ``#{cursor_y}`` — cursor position within the pane
46+
- ``#{scroll_position}`` — scrollback position when in copy mode
47+
48+
## Window
49+
50+
- ``#{window_id}`` — globally unique window identifier (e.g. ``@1``)
51+
- ``#{window_index}`` — window index within the session
52+
- ``#{window_name}`` — window name
53+
- ``#{window_zoomed_flag}`` — ``1`` when a pane is zoomed
54+
- ``#{window_layout}`` — current layout string
55+
- ``#{window_panes}`` — number of panes in the window
56+
- ``#{window_active}`` — ``1`` for the active window in its session
57+
58+
## Session
59+
60+
- ``#{session_id}`` — globally unique session identifier (e.g. ``$1``)
61+
- ``#{session_name}`` — session name
62+
- ``#{session_attached}`` — ``1`` when at least one client is attached
63+
- ``#{session_windows}`` — number of windows in the session
64+
65+
## Server / client
66+
67+
- ``#{host}`` — hostname running the tmux server
68+
- ``#{client_tty}`` — TTY of the client (when evaluated client-side)
69+
- ``#{socket_path}`` — server socket path
70+
71+
## Conditionals and string operations
72+
73+
- ``#{?cond,then,else}`` — emit ``then`` if ``cond`` is truthy, else
74+
``else``
75+
- ``#{C/i:pattern}`` — case-insensitive search inside the result
76+
- ``#{=N:expr}`` — truncate ``expr`` to ``N`` characters
77+
- ``#{s/from/to/:expr}`` — substitution
78+
- ``#{T:expr}`` — recursively expand format strings within ``expr``
79+
80+
See ``man tmux`` (FORMATS section) for the complete catalog.
81+
""")
82+
83+
84+
def register(mcp: FastMCP) -> None:
85+
"""Register reference resources with the FastMCP instance."""
86+
87+
@mcp.resource(
88+
"tmux://reference/format-strings",
89+
title="tmux Format String Reference",
90+
mime_type=_MARKDOWN_MIME,
91+
)
92+
def get_format_string_reference() -> str:
93+
"""Return the tmux format-string cheat sheet as Markdown.
94+
95+
Static reference content. Use this when an agent encounters
96+
an unfamiliar ``#{...}`` field — pulling the resource is
97+
cheaper than a ``display_message`` round-trip and avoids
98+
hallucinated format names.
99+
"""
100+
return _FORMAT_STRING_REFERENCE
101+
102+
# Type checkers: list the function to silence unused-name warnings
103+
# without exposing it outside this closure.
104+
_ = (get_format_string_reference,)
105+
106+
107+
__all__ = ["register"]

tests/test_resources.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import pytest
99

1010
from libtmux_mcp.resources.hierarchy import register
11+
from libtmux_mcp.resources.reference import register as register_reference
1112

1213
if t.TYPE_CHECKING:
1314
from libtmux.pane import Pane
@@ -184,3 +185,74 @@ def test_hierarchy_resources_advertise_mime_type(uri: str, expected_mime: str) -
184185
candidate = by_uri.get(uri)
185186
assert candidate is not None, f"resource {uri!r} not registered"
186187
assert candidate.mime_type == expected_mime
188+
189+
190+
# ---------------------------------------------------------------------------
191+
# Reference resources (static catalogs, no tmux server interaction)
192+
# ---------------------------------------------------------------------------
193+
194+
195+
@pytest.fixture
196+
def reference_resource_functions() -> dict[str, t.Any]:
197+
"""Capture reference-module resource closures by URI.
198+
199+
Mirrors ``resource_functions`` but registers
200+
:mod:`libtmux_mcp.resources.reference` instead of ``hierarchy``.
201+
Reference resources are static and need no tmux fixtures.
202+
"""
203+
functions: dict[str, t.Any] = {}
204+
205+
class MockMCP:
206+
def resource(self, uri: str, **kwargs: t.Any) -> t.Any:
207+
def decorator(fn: t.Any) -> t.Any:
208+
functions[uri] = fn
209+
return fn
210+
211+
return decorator
212+
213+
register_reference(MockMCP()) # type: ignore[arg-type]
214+
return functions
215+
216+
217+
def test_format_string_reference_returns_markdown(
218+
reference_resource_functions: dict[str, t.Any],
219+
) -> None:
220+
"""tmux://reference/format-strings returns non-empty Markdown.
221+
222+
The agent's reason for pulling this resource is to recover from an
223+
unfamiliar ``#{...}`` token without burning a ``display_message``
224+
round-trip. The body must therefore (a) be present and (b) name
225+
the format strings most likely to confuse — pane / window /
226+
session ID forms and the ``#{?cond,then,else}`` conditional.
227+
"""
228+
fn = reference_resource_functions["tmux://reference/format-strings"]
229+
body = fn()
230+
assert isinstance(body, str)
231+
assert body.strip(), "format-string reference body is empty"
232+
# Spot-check the highest-traffic catalog entries — if any of these
233+
# vanish, the reference is failing at its job.
234+
assert "#{pane_id}" in body
235+
assert "#{window_id}" in body
236+
assert "#{session_id}" in body
237+
assert "#{?cond,then,else}" in body
238+
239+
240+
def test_format_string_reference_advertises_markdown_mime() -> None:
241+
"""tmux://reference/format-strings is registered with text/markdown.
242+
243+
Concrete URIs (no ``{...}`` template params) register as resources,
244+
not resource templates — they show up under ``mcp.list_resources()``
245+
rather than ``mcp.list_resource_templates()``.
246+
"""
247+
import asyncio
248+
249+
from fastmcp import FastMCP
250+
251+
mcp = FastMCP(name="test-reference-mime")
252+
register_reference(mcp)
253+
254+
resources = asyncio.run(mcp.list_resources())
255+
by_uri = {str(getattr(r, "uri", "")): r for r in resources}
256+
target = by_uri.get("tmux://reference/format-strings")
257+
assert target is not None, "format-strings reference not registered"
258+
assert target.mime_type == "text/markdown"

0 commit comments

Comments
 (0)