|
| 1 | +(adr-capability-model)= |
| 2 | + |
| 3 | +# ADR 0001: Capability boundaries for tmux MCP implementations |
| 4 | + |
| 5 | +## Abstract |
| 6 | + |
| 7 | +This record defines the shared capability contract for tmux Model Context |
| 8 | +Protocol (MCP) implementations. It specifies how implementations describe |
| 9 | +available operations, how a tmux socket limits the structured tool namespace, |
| 10 | +and what MCP annotations do and do not express. It does not prescribe an |
| 11 | +implementation language, internal class design, or rollout sequence. |
| 12 | + |
| 13 | +## Status |
| 14 | + |
| 15 | +Proposed. Supersedes the former three-level capability model. |
| 16 | + |
| 17 | +## Context and problem |
| 18 | + |
| 19 | +A tmux MCP gives an agent a terminal. The implementation must describe that |
| 20 | +authority without confusing tmux object selection, process execution, client |
| 21 | +consent, and operating-system confinement. |
| 22 | + |
| 23 | +One ordered safety scale cannot describe those independent concerns. Running a |
| 24 | +shell command and deleting a tmux window are different capabilities, not |
| 25 | +different amounts of one capability. MCP also defines |
| 26 | +[`readOnlyHint`, `destructiveHint`, `idempotentHint`, and `openWorldHint`](https://github.com/modelcontextprotocol/python-sdk/blob/v1.29.1/src/mcp/types.py#L1247-L1294) |
| 27 | +for client presentation. Reusing those annotations as severity labels would |
| 28 | +change their protocol meaning. |
| 29 | + |
| 30 | +## Scope and non-goals |
| 31 | + |
| 32 | +This record applies to MCP servers that expose structured tmux operations. It |
| 33 | +defines their shared capability vocabulary, tool-surface behavior, annotation |
| 34 | +semantics, execution boundary, and disclosure obligations. |
| 35 | + |
| 36 | +This record does not define operating-system isolation, decide which shell |
| 37 | +commands an operator permits, or make the model an enforcement boundary. It |
| 38 | +does not track implementation progress or prescribe a programming language, |
| 39 | +framework, storage format, or build system. |
| 40 | + |
| 41 | +## Terminology |
| 42 | + |
| 43 | +**tmux MCP implementation** |
| 44 | +: An MCP server implementation that exposes structured tools for tmux. |
| 45 | + |
| 46 | +**server process** |
| 47 | +: One running instance of a tmux MCP implementation. |
| 48 | + |
| 49 | +**structured tool** |
| 50 | +: A typed MCP operation that targets tmux or a program running in a tmux pane. |
| 51 | + |
| 52 | +**selected socket** |
| 53 | +: The single tmux server socket chosen by a server process at startup. |
| 54 | + |
| 55 | +**direct operation** |
| 56 | +: The operation a structured tool requests, excluding behavior already |
| 57 | + configured in tmux, such as command aliases and hooks. |
| 58 | + |
| 59 | +**workload process** |
| 60 | +: A pane or host process whose behavior can be influenced by caller input. A |
| 61 | + control-plane process used only to issue a tmux request is not a workload |
| 62 | + process. |
| 63 | + |
| 64 | +**whole-call MCP annotation** |
| 65 | +: A standard MCP annotation describing the observable tool call as a whole, |
| 66 | + including configured tmux behavior activated by the direct operation. |
| 67 | + |
| 68 | +**structured tool surface** |
| 69 | +: The fixed set of tools a server process advertises and accepts. |
| 70 | + |
| 71 | +**host command** |
| 72 | +: Client-authored executable input handed directly to a process outside a tmux |
| 73 | + pane. |
| 74 | + |
| 75 | +**minimal tmux configuration** |
| 76 | +: Configuration supplied by the implementation that loads no user configuration, |
| 77 | + plugin, hook, or status job. |
| 78 | + |
| 79 | +## Conformance |
| 80 | + |
| 81 | +In this record, uppercase **MUST**, **MUST NOT**, **SHOULD**, **SHOULD NOT**, and |
| 82 | +**MAY** use the meanings defined by |
| 83 | +[BCP 14](https://datatracker.ietf.org/doc/html/rfc8174). Lowercase forms have |
| 84 | +their ordinary English meanings. |
| 85 | + |
| 86 | +A **conforming tmux MCP implementation** satisfies every applicable **MUST** and |
| 87 | +**MUST NOT** requirement in this record. The Terminology, Conformance, |
| 88 | +Architectural decisions, and Security and reliability considerations sections |
| 89 | +are normative. Sections and paragraphs labeled informative do not define |
| 90 | +conformance requirements. |
| 91 | + |
| 92 | +## Architectural decisions |
| 93 | + |
| 94 | +### CM-1: A tmux socket limits addressable tmux objects; it is not a security boundary |
| 95 | + |
| 96 | +A server process **MUST** select exactly one tmux socket before exposing |
| 97 | +structured tools. The server process **MUST** retain that socket for its |
| 98 | +lifetime. A structured tool **MUST NOT** accept a per-call socket selector. A |
| 99 | +structured tool **MUST NOT** address tmux objects outside the selected socket. |
| 100 | + |
| 101 | +The default configuration **MUST** select a product-scoped dedicated socket. A |
| 102 | +server process that creates the tmux server on that socket **MUST** use a minimal |
| 103 | +tmux configuration. Selecting an inherited or user-configured socket **MUST** |
| 104 | +require explicit operator configuration. A server process that finds an existing |
| 105 | +server on the selected socket **MUST NOT** claim that server has minimal |
| 106 | +configuration provenance. |
| 107 | + |
| 108 | +The selected socket limits which tmux sessions, windows, and panes structured |
| 109 | +tools can name. It does not restrict filesystem access, process execution, |
| 110 | +network access, credentials, or access through commands running in a pane. |
| 111 | + |
| 112 | +**Rationale, informative.** Socket pinning prevents accidental cross-server |
| 113 | +object selection. It does not provide operating-system isolation and must not be |
| 114 | +described as a sandbox. |
| 115 | + |
| 116 | +### CM-2: Independent properties describe each tool's direct capability |
| 117 | + |
| 118 | +Every structured tool **MUST** declare its direct process reach, direct tmux |
| 119 | +effect, and output classes. An implementation **MUST NOT** collapse those |
| 120 | +properties into an ordered safety level. |
| 121 | + |
| 122 | +Direct process reach **MUST** use one of these values: |
| 123 | + |
| 124 | +- `none`: starts no workload process and delivers no client-controlled input to |
| 125 | + one. |
| 126 | +- `configured-process`: starts a pane workload process without accepting a |
| 127 | + command payload or client-controlled tmux-format input. |
| 128 | +- `pane-input`: delivers client-controlled keys or text to a pane program. |
| 129 | +- `pane-command`: runs a client-authored shell command in a pane. |
| 130 | + |
| 131 | +A conforming implementation **MUST** reserve `host-command` to describe a |
| 132 | +prohibited public capability. |
| 133 | + |
| 134 | +Direct tmux effect **MUST** be a set containing one or more of `observe`, |
| 135 | +`change`, and `delete`. Output classes **MUST** be a set drawn from |
| 136 | +`tmux-metadata`, `terminal-content`, `process-environment`, and |
| 137 | +`configured-command`. An implementation **MUST** separately record whether a |
| 138 | +tool may expose secrets or return untrusted content. |
| 139 | + |
| 140 | +These properties describe the direct operation. Whole-call MCP annotations |
| 141 | +separately account for execution or mutation added by configured aliases and |
| 142 | +hooks. |
| 143 | + |
| 144 | +**Example, informative.** `rename_window` changes tmux state without accepting |
| 145 | +executable input. `run_shell_command` also changes tmux state and accepts a |
| 146 | +client-authored pane command. Their tmux effects overlap; their process reach |
| 147 | +does not. |
| 148 | + |
| 149 | +### CM-3: One startup decision defines the advertised and callable tool surface |
| 150 | + |
| 151 | +Every structured tool **MUST** belong to exactly one unordered toolset: |
| 152 | +`inspect`, `manage`, `execute`, or `teardown`. |
| 153 | + |
| 154 | +On the default dedicated socket with minimal tmux configuration, an |
| 155 | +implementation **MUST** enable all four toolsets by default. On an inherited or |
| 156 | +user-configured socket, an implementation **MUST** require explicit operator |
| 157 | +selection before enabling `teardown`. It **MUST** apply the same requirement to |
| 158 | +an existing server whose configuration provenance is unknown. |
| 159 | + |
| 160 | +At startup, an implementation **MUST** expand the selected toolsets. It **MUST** |
| 161 | +then add named inclusions. It **MUST** then remove named exclusions. A named |
| 162 | +exclusion **MUST** win over every inclusion path. |
| 163 | + |
| 164 | +An implementation **MUST** reject unknown toolset and tool names at startup. It |
| 165 | +**MUST** freeze the effective structured tool surface for the server process's |
| 166 | +lifetime. A tool outside that surface **MUST NOT** be advertised. A tool outside |
| 167 | +that surface **MUST NOT** be callable by name. |
| 168 | + |
| 169 | +An aggregate tool **MAY** invoke a nested operation that is not separately |
| 170 | +advertised. The aggregate tool **MUST** declare that nested operation as part of |
| 171 | +its own authority. A named exclusion **MUST** remove an operation from every |
| 172 | +aggregate tool's nested authority. |
| 173 | + |
| 174 | +The toolsets support inventory configuration, context reduction, model routing, |
| 175 | +and documentation navigation. They do not restrict what an enabled pane-input |
| 176 | +or pane-command tool can cause a pane program to do. |
| 177 | + |
| 178 | +**Rationale, informative.** Unordered toolsets permit combinations such as |
| 179 | +`inspect,teardown`. Applying the same effective surface to discovery and |
| 180 | +invocation prevents a hidden tool from remaining callable. |
| 181 | + |
| 182 | +### CM-4: MCP annotations describe an entire tool call |
| 183 | + |
| 184 | +An implementation **MUST** use standard MCP annotations only with their protocol |
| 185 | +meanings. A whole-call MCP annotation **MUST** account for the direct operation |
| 186 | +and configured tmux behavior that the operation activates. An implementation |
| 187 | +**MUST NOT** use standard annotations as authorization decisions or project |
| 188 | +severity labels. |
| 189 | + |
| 190 | +Direct process reach, direct tmux effect, and output classes **MUST** remain |
| 191 | +separate from standard MCP annotations. Without evidence for the whole-call |
| 192 | +claim, an implementation **MUST** set `readOnlyHint` to `false`. Without evidence |
| 193 | +for the whole-call claim, an implementation **MUST** set `destructiveHint` to |
| 194 | +`true`. Without evidence for the whole-call claim, an implementation **MUST** set |
| 195 | +`idempotentHint` to `false`. Without evidence for the whole-call claim, an |
| 196 | +implementation **MUST** set `openWorldHint` to `true`. |
| 197 | + |
| 198 | +An implementation **MUST** set `readOnlyHint` to `true` only when the whole call |
| 199 | +cannot modify its environment. It **MUST** set `destructiveHint` to `false` only |
| 200 | +when the whole call performs additive updates at most. It **MUST** set |
| 201 | +`idempotentHint` to `true` only when repeating the call with the same arguments |
| 202 | +has no additional effect. It **MUST** set `openWorldHint` to `false` only when the |
| 203 | +whole call cannot interact with external entities. |
| 204 | + |
| 205 | +MCP annotations support client consent interfaces. They do not enforce tool |
| 206 | +authorization, operating-system confinement, or command policy. |
| 207 | + |
| 208 | +### CM-5: One authoritative capability definition governs every public claim |
| 209 | + |
| 210 | +An implementation **MUST** maintain one authoritative, machine-readable |
| 211 | +capability definition for its structured tools. Tool registration, the effective |
| 212 | +structured tool surface, tool descriptions, documentation, and capability |
| 213 | +reporting **MUST** agree with that definition. |
| 214 | + |
| 215 | +The capability definition **MUST** classify every caller-controlled input by the |
| 216 | +interpreter boundary it reaches. An implementation **MUST** validate capability |
| 217 | +claims against those input sinks rather than infer behavior from parameter |
| 218 | +names. |
| 219 | + |
| 220 | +**Rationale, informative.** tmux expands formats in argument positions whose |
| 221 | +names do not reveal the interpreter boundary. A shared manifest generated or |
| 222 | +validated during CI is one implementation approach, not a required storage |
| 223 | +format. |
| 224 | + |
| 225 | +### CM-6: Public tools do not execute client-authored host commands |
| 226 | + |
| 227 | +A conforming implementation **MUST NOT** expose a structured tool with |
| 228 | +`host-command` process reach. It **MUST NOT** hand caller text directly to a |
| 229 | +host-side shell. Client-authored shell commands **MAY** run through a |
| 230 | +`pane-command` tool, where the process is represented by a tmux pane. |
| 231 | + |
| 232 | +This rule constrains the direct MCP surface. It does not prevent a pane command |
| 233 | +from invoking tmux's |
| 234 | +[`run-shell`](https://github.com/tmux/tmux/blob/3.2a/cmd-run-shell.c#L177-L181), |
| 235 | +installing a |
| 236 | +[`#()` status job](https://github.com/tmux/tmux/blob/3.2a/format.c#L392-L399), |
| 237 | +or starting any process available to the tmux user's account. |
| 238 | + |
| 239 | +### CM-7: Capability disclosure is part of the product contract |
| 240 | + |
| 241 | +Every structured tool description **MUST** begin with a plain-language statement |
| 242 | +of its direct process reach and tmux effect. An implementation **MUST** publish |
| 243 | +its effective structured tool surface and selected socket. Installation and |
| 244 | +trust documentation **MUST** state that execute tools run with the tmux user's |
| 245 | +authority. |
| 246 | + |
| 247 | +Documentation **MUST** distinguish socket-scoped object selection from |
| 248 | +operating-system confinement. Documentation **MUST** distinguish tool-surface |
| 249 | +filtering from authorization. Documentation **MUST** describe whole-call MCP |
| 250 | +annotations as consent metadata rather than enforcement. |
| 251 | + |
| 252 | +An implementation **MUST NOT** describe a dedicated socket, restricted tool |
| 253 | +surface, payload filter, or MCP annotation as a sandbox or security boundary. |
| 254 | + |
| 255 | +## Consequences |
| 256 | + |
| 257 | +Conservative whole-call annotations may cause clients to prompt more often. |
| 258 | +That cost preserves the protocol meaning of the annotations. |
| 259 | + |
| 260 | +Pinning one socket per server process requires separate configured MCP entries |
| 261 | +to control separate tmux sockets. The selected socket reduces accidental object |
| 262 | +selection without claiming exclusive ownership. |
| 263 | + |
| 264 | +Maintaining one authoritative capability definition adds review and validation |
| 265 | +work. It also makes capability drift detectable across registration, |
| 266 | +documentation, and runtime disclosure. |
| 267 | + |
| 268 | +Stable tool names become part of the client-consent surface. Renaming a public |
| 269 | +tool therefore requires an explicit migration rather than an internal refactor. |
| 270 | + |
| 271 | +## Rejected alternatives |
| 272 | + |
| 273 | +**An ordered safety scale.** One rank cannot represent independent process |
| 274 | +reach, tmux effect, and output sensitivity without hiding one of them. |
| 275 | + |
| 276 | +**Tool filtering as authorization.** An enabled pane-input or pane-command tool |
| 277 | +can express operations omitted from the structured tool surface. Filtering is |
| 278 | +still useful for inventory control and accident reduction. |
| 279 | + |
| 280 | +**Payload blocklists.** Shell and terminal input are composable. A filter that |
| 281 | +recognizes selected strings cannot establish a command boundary and would invite |
| 282 | +operators to rely on incomplete protection. |
| 283 | + |
| 284 | +**Per-call socket selection.** A caller-selected socket expands every tool's |
| 285 | +object namespace and makes one server process represent several trust contexts. |
| 286 | + |
| 287 | +**Public host-command tools.** Host-side commands would bypass the observable |
| 288 | +pane process and its completion boundary. |
| 289 | + |
| 290 | +**Generic mutating or destructive aggregates.** A wrapper hides the names on |
| 291 | +which client consent policies depend and turns one approval into authority over |
| 292 | +unrelated operations. |
| 293 | + |
| 294 | +## Security and reliability considerations |
| 295 | + |
| 296 | +### Ambient tmux behavior |
| 297 | + |
| 298 | +**Background, informative.** tmux |
| 299 | +[expands command aliases](https://github.com/tmux/tmux/blob/3.2a/cmd-parse.y#L698-L715) |
| 300 | +before dispatch and |
| 301 | +[runs after-hooks](https://github.com/tmux/tmux/blob/3.2a/cmd-queue.c#L617-L627) |
| 302 | +after many commands. A nominally observational direct operation can therefore |
| 303 | +execute or mutate through existing configuration. Independent pane processes, |
| 304 | +plugins, event hooks, and status jobs can also run without an MCP call. |
| 305 | + |
| 306 | +A conforming implementation **MUST NOT** claim that it prevents all subprocess |
| 307 | +execution. A conforming implementation **MUST** describe ambient tmux behavior |
| 308 | +separately from direct process reach. |
| 309 | + |
| 310 | +### Shared sockets |
| 311 | + |
| 312 | +**Boundary, informative.** Every process with access to the selected socket can |
| 313 | +create or alter objects on it. A dedicated socket separates the structured |
| 314 | +namespace from another tmux server; it does not establish exclusive ownership. |
| 315 | + |
| 316 | +An implementation **MUST NOT** claim ownership of every object on a shared |
| 317 | +socket. A self-kill guard **MUST** be described as protection against direct |
| 318 | +teardown tools only, not against equivalent pane commands or other clients. |
| 319 | + |
| 320 | +**Shutdown behavior, informative.** With the default `exit-empty` enabled and |
| 321 | +`exit-unattached` disabled |
| 322 | +([tmux option defaults](https://github.com/tmux/tmux/blob/3.2a/options-table.c#L256-L268)), |
| 323 | +tmux waits for every session to be removed and for clients to disconnect before |
| 324 | +exiting |
| 325 | +([tmux exit logic](https://github.com/tmux/tmux/blob/3.2a/server.c#L268-L286)). |
| 326 | +Removing one MCP instance's sessions does not stop a shared server while another |
| 327 | +session remains. Socket-wide termination remains an operator action because it |
| 328 | +may destroy sessions the caller does not own. |
| 329 | + |
| 330 | +### Untrusted and sensitive output |
| 331 | + |
| 332 | +**Background, informative.** Terminal output, environment values, configured |
| 333 | +commands, and tmux metadata can contain secrets or untrusted instructions. |
| 334 | + |
| 335 | +An `inspect` tool **MUST NOT** be described as safe merely because its direct |
| 336 | +operation is observational. An implementation **MUST** disclose when a tool may |
| 337 | +return untrusted content. It **MUST** disclose when a tool may expose secrets. |
| 338 | + |
| 339 | +### Aggregate authority |
| 340 | + |
| 341 | +An aggregate tool **MUST** disclose the complete set of nested tools it can |
| 342 | +invoke. Its effective authority **MUST NOT** exceed its advertised nested tool |
| 343 | +set. A conforming implementation **MUST NOT** expose a generic mutating or |
| 344 | +destructive aggregate. |
| 345 | + |
| 346 | +### Bounded operations |
| 347 | + |
| 348 | +An implementation that claims bounded pattern matching **MUST** bound both input |
| 349 | +size and matching execution time. A capture or transport deadline **MUST NOT** be |
| 350 | +misreported as a matching timeout. |
| 351 | + |
| 352 | +### Redaction and history suppression |
| 353 | + |
| 354 | +**Boundary, informative.** Audit redaction applies only to the audit record. |
| 355 | +History suppression is best-effort shell hygiene. |
| 356 | + |
| 357 | +An implementation **MUST NOT** claim that either mechanism removes data from |
| 358 | +client transcripts, pane scrollback, process arguments, shell history outside |
| 359 | +its control, or operating-system observation surfaces. |
| 360 | + |
| 361 | +## When to reconsider |
| 362 | + |
| 363 | +Reconsider this decision if MCP gains enforceable authorization or nested-call |
| 364 | +annotation semantics, if tmux exposes a public command mode that suppresses all |
| 365 | +relevant ambient behavior, or if deployments require a stronger multi-user |
| 366 | +boundary than one operating-system account and socket can provide. |
| 367 | + |
| 368 | +Reconsider the shared vocabulary if an implementation cannot express a required |
| 369 | +capability without weakening an existing term. Add a new independent property |
| 370 | +instead of stretching an old one into a severity rank. |
| 371 | + |
| 372 | +## References |
| 373 | + |
| 374 | +- [Issue #127: tool visibility is not operating-system confinement](https://github.com/tmux-python/libtmux-mcp/issues/127) |
| 375 | +- [Source decision for ADR 0001](https://github.com/tmux-python/libtmux-mcp/issues/127#issuecomment-5463431049) |
| 376 | +- [Detailed target-state capability model](https://github.com/tmux-python/libtmux-mcp/issues/127#issuecomment-5463166342) |
0 commit comments