Skip to main content

tmux_mcp/
views.rs

1//! What the tools answer with.
2//!
3//! Every tool returns one of these rather than a string, so the shape it
4//! promises is published as an output schema and the value arrives as
5//! structured content. An agent reads fields instead of parsing prose, and the
6//! doc comments here become the descriptions it reads while doing it.
7//!
8//! Lists are wrapped in a named object rather than returned bare. The protocol
9//! says structured content is an object, and a wrapper leaves somewhere to put
10//! a count or a cursor later without changing a shape callers already read.
11
12use serde::Serialize;
13
14use crate::caller::Relation;
15use crate::schema::ChannelWaitOutcomeSchema;
16
17/// One session, as the protocol sees it.
18#[derive(Debug, Serialize, schemars::JsonSchema)]
19pub struct SessionView {
20    /// The `$`-prefixed tmux identity.
21    pub id: String,
22    /// The session name, absent when tmux reported none.
23    pub name: String,
24    /// How many windows the session holds.
25    pub windows: u32,
26    /// Whether any client is attached.
27    pub attached: bool,
28}
29
30/// One window, as the protocol sees it.
31#[derive(Debug, Serialize, schemars::JsonSchema)]
32pub struct WindowView {
33    /// The `@`-prefixed tmux identity.
34    pub id: String,
35    /// The session this window was reached through.
36    pub session_id: String,
37    /// The window's index within that session.
38    pub index: i32,
39    /// The window name.
40    pub name: String,
41    /// How many panes the window holds.
42    pub panes: u32,
43    /// Whether this is the session's active window.
44    pub active: bool,
45    /// Whether more than one session links this window.
46    pub linked: bool,
47}
48
49/// One pane, as the protocol sees it.
50#[derive(Debug, Serialize, schemars::JsonSchema)]
51pub struct PaneView {
52    /// The `%`-prefixed tmux identity.
53    pub id: String,
54    /// The window that contains the pane.
55    pub window_id: String,
56    /// The command currently running.
57    pub command: Option<String>,
58    /// The pane's working directory.
59    pub path: Option<String>,
60    /// Whether this is the window's active pane.
61    pub active: bool,
62    /// Whether this is the pane the MCP server itself runs in.
63    ///
64    /// `self` only on a confirmed match of both socket and pane id; `other`
65    /// for every pane that is not, including one this crate cannot prove
66    /// either way; `unknown` when the server is not running inside tmux, so
67    /// the question has no answer.
68    pub caller: Relation,
69}
70
71/// Every session on the server.
72#[derive(Debug, Serialize, schemars::JsonSchema)]
73pub struct Sessions {
74    /// The sessions, in tmux's own order.
75    pub sessions: Vec<SessionView>,
76}
77
78/// Every window asked for.
79#[derive(Debug, Serialize, schemars::JsonSchema)]
80pub struct Windows {
81    /// The windows, in tmux's own order.
82    pub windows: Vec<WindowView>,
83}
84
85/// Every pane asked for.
86#[derive(Debug, Serialize, schemars::JsonSchema)]
87pub struct Panes {
88    /// The panes, in tmux's own order.
89    pub panes: Vec<PaneView>,
90}
91
92/// One pane inside a described window.
93#[derive(Debug, Serialize, schemars::JsonSchema)]
94pub struct BranchPane {
95    /// The `%`-prefixed tmux identity.
96    pub id: String,
97    /// The command currently running.
98    pub command: Option<String>,
99    /// Whether this is the window's active pane.
100    pub active: bool,
101}
102
103/// One window inside a described session.
104#[derive(Debug, Serialize, schemars::JsonSchema)]
105pub struct BranchWindow {
106    /// The `@`-prefixed tmux identity.
107    pub id: String,
108    /// The window's index within its session.
109    pub index: i32,
110    /// The window name.
111    pub name: String,
112    /// Whether this is the session's active window.
113    pub active: bool,
114    /// Whether more than one session links this window.
115    pub linked: bool,
116    /// The panes it holds.
117    pub panes: Vec<BranchPane>,
118}
119
120/// One session with everything under it.
121#[derive(Debug, Serialize, schemars::JsonSchema)]
122pub struct Branch {
123    /// The `$`-prefixed tmux identity.
124    pub id: String,
125    /// The session name.
126    pub name: String,
127    /// Whether any client is attached.
128    pub attached: bool,
129    /// The windows it holds.
130    pub windows: Vec<BranchWindow>,
131}
132
133/// The whole hierarchy, in one answer.
134#[derive(Debug, Serialize, schemars::JsonSchema)]
135pub struct Tree {
136    /// Every session, with its windows and their panes.
137    pub sessions: Vec<Branch>,
138}
139
140/// Whether tmux knew where the last command's output began.
141///
142/// Reported rather than inferred: an answer that fell back to the whole
143/// screen looks exactly like a command that printed a great deal.
144#[derive(Debug, Serialize, schemars::JsonSchema)]
145#[serde(rename_all = "snake_case")]
146pub enum Marks {
147    /// The prompt marks were there, so the text is one command's output.
148    Present,
149    /// tmux has the marks but this pane has none, because its shell does not
150    /// emit OSC 133. fish does; bash and zsh need shell integration. The
151    /// visible screen came back instead -- not the history, which would
152    /// answer a request for one command with everything the pane ever wrote.
153    Absent,
154    /// This tmux predates `capture-pane -F`, which arrived in 3.7. The
155    /// visible screen came back instead.
156    Unsupported,
157    /// The caller did not ask for the last command, so nothing was looked up.
158    NotAsked,
159}
160
161/// What a pane is showing.
162#[derive(Debug, Serialize, schemars::JsonSchema)]
163pub struct Capture {
164    /// The pane that was read.
165    pub pane: String,
166    /// The text, with lines separated by newlines.
167    pub text: String,
168    /// How many lines that is.
169    pub lines: usize,
170    /// Whether the text is one command's output, and why it is not.
171    pub marks: Marks,
172}
173
174/// A pane's contents and the state a capture leaves out.
175#[derive(Debug, Serialize, schemars::JsonSchema)]
176pub struct Snapshot {
177    /// The pane itself.
178    pub pane: PaneView,
179    /// How wide it is, in columns.
180    pub width: u32,
181    /// How tall it is, in rows.
182    pub height: u32,
183    /// Where the cursor sits, counting from the left.
184    pub cursor_x: Option<u32>,
185    /// Where the cursor sits, counting from the top.
186    pub cursor_y: Option<u32>,
187    /// Whether the pane is in a mode, where keys navigate rather than type.
188    pub in_mode: bool,
189    /// Which mode, when it is in one.
190    pub mode: Option<String>,
191    /// How far it is scrolled back, when it is in copy mode.
192    pub scroll_position: Option<i32>,
193    /// Whether the process in it has ended.
194    pub dead: bool,
195    /// What the pane is showing.
196    pub content: String,
197    /// How many lines of it are reported.
198    pub lines: usize,
199    /// How many older lines were dropped to honour `max_lines`.
200    pub dropped: usize,
201}
202
203/// One line of one pane that matched a search.
204#[derive(Debug, Serialize, schemars::JsonSchema)]
205pub struct MatchView {
206    /// The pane the line was found in.
207    pub pane: String,
208    /// The window that pane belongs to.
209    pub window_id: String,
210    /// Which line of the capture matched, counting from the top.
211    pub line: usize,
212    /// The line itself.
213    pub text: String,
214}
215
216/// Where a pattern was found.
217#[derive(Debug, Serialize, schemars::JsonSchema)]
218pub struct Matches {
219    /// Every matching line, in the order the panes were read.
220    pub matches: Vec<MatchView>,
221    /// How many panes were read to answer this.
222    pub panes_searched: usize,
223    /// Whether the match ceiling was reached, so there may be more.
224    pub capped: bool,
225}
226
227/// What a pane wrote since a cursor.
228#[derive(Debug, Serialize, schemars::JsonSchema)]
229pub struct Since {
230    /// The pane that was read.
231    pub pane: String,
232    /// The text, with escape sequences removed.
233    ///
234    /// After the first answer this is the raw output stream, not the rendered
235    /// screen: a line redrawn in place repeats. `capture_pane` shows the
236    /// screen.
237    pub text: String,
238    /// The cursor to pass back next time.
239    pub cursor: String,
240    /// Whether output between the previous cursor and this text was lost.
241    pub missed: bool,
242    /// Whether the pane has stopped writing for good.
243    pub closed: bool,
244    /// Whether this is the first answer, which reports the visible screen
245    /// rather than what is new.
246    pub first: bool,
247}
248
249/// The value of one tmux option.
250#[derive(Debug, Serialize, schemars::JsonSchema)]
251pub struct OptionValue {
252    /// The option name.
253    pub name: String,
254    /// Its value, absent when it has never been set at that scope.
255    pub value: Option<String>,
256}
257
258/// How a wait on a channel finished.
259#[derive(Debug, Serialize, schemars::JsonSchema)]
260pub struct ChannelWait {
261    /// The channel that was waited on.
262    pub channel: String,
263    /// `signalled` or `deadline`.
264    #[schemars(with = "ChannelWaitOutcomeSchema")]
265    pub outcome: String,
266}
267
268/// A channel that was signalled.
269#[derive(Debug, Serialize, schemars::JsonSchema)]
270pub struct ChannelSignal {
271    /// The channel that was signalled.
272    pub channel: String,
273}
274
275/// Keys that were sent.
276#[derive(Debug, Serialize, schemars::JsonSchema)]
277pub struct Sent {
278    /// The pane the caller targeted.
279    pub pane: String,
280    /// The effective configured recipient IDs observed before dispatch.
281    ///
282    /// Membership does not confirm delivery and may change after observation.
283    pub panes: Vec<String>,
284}
285
286/// A pane's size after being resized.
287#[derive(Debug, Serialize, schemars::JsonSchema)]
288pub struct Size {
289    /// The pane that was resized.
290    pub pane: String,
291    /// How wide it is now, in columns.
292    pub width: u32,
293    /// How tall it is now, in rows.
294    pub height: u32,
295}
296
297/// An object that was destroyed.
298#[derive(Debug, Serialize, schemars::JsonSchema)]
299pub struct Killed {
300    /// The id of what was destroyed.
301    pub id: String,
302}
303
304/// What tmux holds under one environment name.
305#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, schemars::JsonSchema)]
306#[serde(rename_all = "snake_case")]
307pub enum EnvironmentState {
308    /// tmux holds a value and hands it to processes it starts.
309    Set,
310    /// tmux removes the name from the environment of processes it starts.
311    Removed,
312}
313
314/// One tmux environment entry.
315#[derive(Debug, Serialize, schemars::JsonSchema)]
316pub struct EnvironmentEntry {
317    /// The variable name.
318    pub name: String,
319    /// Whether the name holds a value or is marked for removal.
320    pub state: EnvironmentState,
321    /// The value, only for a set variable whose name the operator allowed in
322    /// `LIBTMUX_ENVIRONMENT_VALUES` at startup.
323    pub value: Option<String>,
324    /// True for a set variable whose value was not returned because the
325    /// operator did not allow its name.
326    pub withheld: bool,
327}
328
329/// A tmux environment, server-wide or for one session.
330#[derive(Debug, Serialize, schemars::JsonSchema)]
331pub struct Environment {
332    /// The entries, in tmux's own order.
333    pub entries: Vec<EnvironmentEntry>,
334    /// The session the environment belongs to, or absent for the server's.
335    pub session: Option<String>,
336}
337
338/// One tmux hook.
339#[derive(Debug, Serialize, schemars::JsonSchema)]
340pub struct Hook {
341    /// The hook name, such as `pane-exited`.
342    pub name: String,
343    /// Its index, when the hook is an array.
344    pub index: Option<u32>,
345    /// The command tmux runs.
346    pub command: String,
347}
348
349/// The hooks set at one scope.
350#[derive(Debug, Serialize, schemars::JsonSchema)]
351pub struct Hooks {
352    /// The hooks, in tmux's own order.
353    pub hooks: Vec<Hook>,
354}
355
356/// A window whose layout was set.
357#[derive(Debug, Serialize, schemars::JsonSchema)]
358pub struct Layout {
359    /// The window that was arranged.
360    pub window: String,
361    /// The layout it now has, in tmux's own syntax.
362    pub layout: String,
363}
364
365/// A pane that was cleared, respawned, or retitled.
366#[derive(Debug, Serialize, schemars::JsonSchema)]
367pub struct PaneChanged {
368    /// The pane that changed.
369    pub pane: String,
370}
371
372/// Text that was pasted into a pane.
373#[derive(Debug, Serialize, schemars::JsonSchema)]
374pub struct Pasted {
375    /// The pane it went into.
376    pub pane: String,
377    /// How many bytes came from `text`, excluding optional Enter.
378    pub bytes: usize,
379}