libtmux-mcp 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a25cba0abc2b1a7f75709296e55caf0eecebcc0f73e9f1f17b35425f6f2779d2
4
- data.tar.gz: 18773f3f297f70ec862a674841e297d757c25045975133a1770f6d76e9fca750
3
+ metadata.gz: e455168a6f0c00704e8728c52c33cd6cd0ae786648a9be633aafd5ff469e1b24
4
+ data.tar.gz: 836e5048e210aed6fabb35d5ca57e031eb9a9a6adb1bb736877df77f47393aa6
5
5
  SHA512:
6
- metadata.gz: 3268471fc74ba19a01585df6ff499fa608d9abbecc69d3a059b9b733c9c646c03c3562d724904de5cfae59632e142a21435af9c6d59357b41eda6e9cf26688d6
7
- data.tar.gz: 1591c3f7db07082e5d926412a4aa06cae2dff75a4499f589b1b611395f16bdc31472d53a35b75a41ee0337348910af37f41aed19d2acac19d57e1c13fe90cf91
6
+ metadata.gz: 2d72cb649f7ddd031b3aeae5dea7d390fbb641b6ba33fcba233058fc370fdb3b807ee910daf8264638f6de2de1634f01c1a2c93222f6716d35f4fb94268e4de2
7
+ data.tar.gz: dcf5d44e2e4acf56a7155c654a52d214761fa3fb456c4d2678c2c001c2869f3828fcaec6f56881d6688c390f7b7c31078c82784aeb5ef4393d96b879c1a8ff8a
data/CHANGELOG.md ADDED
@@ -0,0 +1,68 @@
1
+ # Changelog
2
+
3
+ Notable changes to `libtmux-mcp`.
4
+
5
+ Releases are numbered `0.1.N`, and each release increments `N`. The API is not
6
+ settled: pin an exact version to avoid a break.
7
+
8
+ The gems are released together at one version, so every release has a section
9
+ here. Changes that span gems, such as documentation, CI and release tooling, are
10
+ in the [main changelog](../../CHANGELOG.md).
11
+
12
+ ## 0.1.3 (unreleased)
13
+
14
+ ## 0.1.2 (2026-11-04)
15
+
16
+ No entries for `libtmux-mcp`.
17
+
18
+ ## 0.1.1 (2026-10-31)
19
+
20
+ - **Behavior change:** `libtmux-mcp` no longer needs `--socket` or
21
+ `--socket-name`, and still refuses both together. Without a flag it serves
22
+ the same choice as `Server.new`, so start it with `--socket-name` or
23
+ `LIBTMUX_SOCKET` when an agent needs a server of its own. (#71)
24
+
25
+ ## 0.1.0 (2026-10-25)
26
+
27
+ No entries for `libtmux-mcp`.
28
+
29
+ ## 0.1.0.alpha.3 (2026-10-24)
30
+
31
+ ### libtmux-mcp
32
+
33
+ - `tmux_wait` with `screen_contains` polls the pane screen on tmux older than
34
+ 3.7 instead of attaching a control client, and uses control events from
35
+ 3.7. (#37)
36
+ - `tmux_wait` with `screen_contains` joins wrapped rows, so text that the pane
37
+ width split across rows now matches. (#37)
38
+ - `tmux_wait` fails with the new `pane_died` code, `delivery: observed` and
39
+ the last rows read in `error.capture`, when the pane or its tmux server ends
40
+ before the text appears. That case used to return `stale_target`, or
41
+ `observation_lost` after a killed server. A control stream that ends while
42
+ the pane lives, such as after a detach, returns `observation_lost` instead
43
+ of raising `NoMethodError`. (#37)
44
+
45
+ ## 0.1.0.alpha.2 (2026-10-18)
46
+
47
+ ### libtmux-mcp
48
+
49
+ - `libtmux-mcp` reports an unsupported tmux daemon as refused to start,
50
+ before any session exists. If the daemon changes while the server runs, a
51
+ tool call returns an `unsupported` result with `delivery: not_sent` and a
52
+ fixed line that names the floor and the remedy. (#36)
53
+ - Declare object roots in tool output schemas so clients using the
54
+ 2025-11-25 protocol accept the `tools/list` response with MCP SDK 1.5.1.
55
+
56
+ ## 0.1.0.alpha.1 (2026-09-20)
57
+
58
+ ### libtmux-mcp
59
+
60
+ - Serve an explicit tmux endpoint over MCP stdio with `libtmux-mcp`, or embed
61
+ `Application` and `StdioTransport` in Ruby. Capability discovery and snapshot
62
+ queries are enabled by default. [Server setup](../../gems/libtmux-mcp/README.md#quick-start).
63
+ - Opt into screen captures, event-driven waits, creation, input and close
64
+ tools. Snapshot cursors page one retained capture; resources expose metadata
65
+ and pane screens. [Protocol example](../../examples/mcp_protocol.rb).
66
+ - Run scripts through `tmux_run` after explicitly enabling the tool and
67
+ enrolling a zsh shell. Results distinguish stdout, stderr and observed
68
+ completion. [Shell enrollment](../../gems/libtmux-mcp/README.md#run-a-script-in-a-pane).
data/README.md CHANGED
@@ -20,6 +20,9 @@ tool at a time.
20
20
  [Guides](https://libtmux.org/en/ruby/latest/mcp/guides/) ·
21
21
  [RubyGems](https://rubygems.org/gems/libtmux-mcp)
22
22
 
23
+ [![libtmux.org](https://img.shields.io/badge/docs-libtmux.org-0b7285)](https://libtmux.org/en/ruby/latest/mcp/)
24
+ [![libtmux-mcp](https://img.shields.io/gem/v/libtmux-mcp?label=libtmux-mcp)](https://rubygems.org/gems/libtmux-mcp)
25
+
23
26
  </div>
24
27
 
25
28
  Part of [libtmux for Ruby](https://github.com/libtmux/libtmux-ruby#readme).
@@ -122,6 +125,15 @@ $ libtmux-mcp \
122
125
  --socket-name agent
123
126
  ```
124
127
 
128
+ - With neither flag, the server borrows the socket in `LIBTMUX_SOCKET_PATH`,
129
+ else the name in `LIBTMUX_SOCKET`, else the server `TMUX` names when it runs
130
+ inside tmux, else tmux's default socket. When both variables are set, the
131
+ path wins, and an empty variable counts as unset. A flag ignores both
132
+ variables, and the two flags are refused together.
133
+ - The server never starts a tmux server. When none answers at the socket it
134
+ chose, it exits with status 1.
135
+ - Started from a tmux pane with no flag or variable, the server drives that
136
+ pane's server. Pass `--socket-name` to give an agent a server of its own.
125
137
  - EOF retires the clients the server owns and leaves the daemon running.
126
138
  - `--endpoint` sets the public alias in discovery and resource URIs, and
127
139
  defaults to `local`.
@@ -336,7 +348,10 @@ pass the invitation to [`accept_shell`][mcp-application-accept_shell].
336
348
 
337
349
  Releases are numbered `0.1.N`, and each release increments `N`. The API is not
338
350
  settled: any release may change or remove exported identifiers, and the
339
- changelog marks each break. Pin an exact version to avoid one.
351
+ changelogs mark each break. Pin an exact version to avoid one.
352
+ See the [changelog](CHANGELOG.md), and the [main
353
+ changelog](https://github.com/libtmux/libtmux-ruby/blob/master/CHANGELOG.md)
354
+ for changes that span gems.
340
355
 
341
356
  - **Ruby 3.3 or newer.** The gem depends on `libtmux` and `libtmux-async` at
342
357
  the same version, the `mcp` SDK 1.5, `digest` and `optparse`.
@@ -350,8 +365,10 @@ changelog marks each break. Pin an exact version to avoid one.
350
365
  client.
351
366
  - **Linux, including WSL, and macOS.** The core
352
367
  [compatibility workflow][compat] runs Ruby 3.3, 3.4 and 4.0 with tmux 3.3a
353
- through 3.8 on Ubuntu 24.04. macOS is supported on a best-effort basis, and
354
- CI coverage for it is highly limited.
368
+ through 3.8 on Ubuntu 24.04. A push and a pull request run the oldest and
369
+ newest of those releases, 3.3a and 3.8, and a release runs all of them. macOS
370
+ is supported on a best-effort basis, and CI coverage for it is highly
371
+ limited.
355
372
 
356
373
  ## Documentation
357
374
 
@@ -6,12 +6,12 @@ require "optparse"
6
6
  module LibTmux
7
7
  module MCP
8
8
  class CLI
9
- def self.run(arguments, input: $stdin, out: $stdout, err: $stderr)
10
- new(arguments, input, out, err).run
9
+ def self.run(arguments, input: $stdin, out: $stdout, err: $stderr, environment: ENV)
10
+ new(arguments, input, out, err, environment).run
11
11
  end
12
12
 
13
- def initialize(arguments, input, output, error)
14
- @arguments, @input, @output, @error = arguments.dup, input, output, error
13
+ def initialize(arguments, input, output, error, environment = ENV)
14
+ @arguments, @input, @output, @error, @environment = arguments.dup, input, output, error, environment
15
15
  @options = {endpoint_name: "local", executable: "tmux", tools: Catalog::READ_ONLY.dup,
16
16
  timeout: 5.0, concurrency: 4, max_requests: 32, max_frame_bytes: 1 << 20,
17
17
  max_output_bytes: 1 << 22, max_response_bytes: 1 << 20,
@@ -27,8 +27,7 @@ module LibTmux
27
27
  return 0
28
28
  end
29
29
  validate_arguments
30
- endpoint = Endpoint.new(socket_path: @options[:socket_path], socket_name: @options[:socket_name],
31
- executable: @options.fetch(:executable))
30
+ endpoint = build_endpoint
32
31
  Async do |task|
33
32
  begin
34
33
  serve(endpoint, task)
@@ -44,7 +43,7 @@ module LibTmux
44
43
  end
45
44
  end.wait
46
45
  rescue OptionParser::ParseError, ArgumentError => error
47
- report("Invalid arguments; use --help for supported options.", 2, failure: error)
46
+ report(@invalid_endpoint || "Invalid arguments; use --help for supported options.", 2, failure: error)
48
47
  rescue Interrupt, LibTmux::Cancelled => error
49
48
  report("MCP interrupted; dispatched effects may remain.", 130, failure: error)
50
49
  rescue StandardError => error
@@ -53,6 +52,25 @@ module LibTmux
53
52
 
54
53
  private
55
54
 
55
+ # --socket and --socket-name win; otherwise the core reads the variables,
56
+ # TMUX and tmux's default.
57
+ def build_endpoint
58
+ socket_path = @options[:socket_path]
59
+ socket_path = expand_socket(socket_path) unless socket_path.nil? || socket_path.empty?
60
+ Endpoint.new(socket_path: socket_path, socket_name: @options[:socket_name],
61
+ executable: @options.fetch(:executable), environment: @environment)
62
+ rescue ArgumentError => error
63
+ @invalid_endpoint = "Invalid arguments: #{error.message}."
64
+ raise
65
+ end
66
+
67
+ # File.expand_path puts the user it cannot find in its message.
68
+ def expand_socket(socket)
69
+ File.expand_path(socket)
70
+ rescue ArgumentError
71
+ raise ArgumentError, "--socket cannot be expanded to a path; its leading ~ names no home directory"
72
+ end
73
+
56
74
  def serve(endpoint, task)
57
75
  Server.open(endpoint: endpoint) do |server|
58
76
  LibTmux::Async.open(server: server, parent: task) do |scope|
@@ -185,9 +203,12 @@ module LibTmux
185
203
 
186
204
  def parser
187
205
  @parser ||= OptionParser.new do |options|
188
- options.banner = "Usage: libtmux-mcp (--socket PATH | --socket-name NAME) [options]"
189
- options.on("--socket PATH", "Borrow this existing tmux Unix socket") { |value| @options[:socket_path] = value }
190
- options.on("--socket-name NAME", "Borrow this explicit tmux socket name") { |value| @options[:socket_name] = value }
206
+ options.banner = "Usage: libtmux-mcp [--socket PATH | --socket-name NAME] [options]"
207
+ options.on("--socket PATH", "Borrow the tmux server at this Unix socket",
208
+ "Default: LIBTMUX_SOCKET_PATH, then LIBTMUX_SOCKET, then the",
209
+ "server TMUX names, then tmux's default socket") { |value| @options[:socket_path] = value }
210
+ options.on("--socket-name NAME", "Borrow the tmux server with this socket name",
211
+ "Not with --socket") { |value| @options[:socket_name] = value }
191
212
  options.on("--endpoint NAME", "Public endpoint alias (default: local)") { |value| @options[:endpoint_name] = value }
192
213
  options.on("--tmux EXECUTABLE", "Resolve and pin this tmux executable at startup") { |value| @options[:executable] = value }
193
214
  options.on("--enable-tool NAME", "Enable one additional tool; repeat as needed") { |value| @options.fetch(:tools) << value }
@@ -211,7 +232,7 @@ module LibTmux
211
232
 
212
233
  def validate_arguments
213
234
  raise ArgumentError unless @arguments.empty?
214
- raise ArgumentError unless [@options[:socket_path], @options[:socket_name]].compact.length == 1
235
+ raise ArgumentError if @options[:socket_path] && @options[:socket_name]
215
236
  raise ArgumentError unless @options.fetch(:tools).all? { |name| Catalog::NAMES.include?(name) }
216
237
  raise ArgumentError unless /\A[A-Za-z0-9][A-Za-z0-9_.-]{0,127}\z/.match?(@options.fetch(:endpoint_name))
217
238
  raise ArgumentError unless @options.fetch(:timeout).finite? && @options.fetch(:timeout).positive?
@@ -2,6 +2,6 @@
2
2
 
3
3
  module LibTmux
4
4
  module MCP
5
- VERSION = "0.1.0"
5
+ VERSION = "0.1.2"
6
6
  end
7
7
  end
data/sig/libtmux-mcp.rbs CHANGED
@@ -10,7 +10,7 @@ module LibTmux
10
10
  class CLI
11
11
  private def self.new: (*untyped, **untyped) -> instance
12
12
  def run: () -> Integer
13
- def self.run: (Array[String], ?input: IO, ?out: untyped, ?err: untyped) -> Integer
13
+ def self.run: (Array[String], ?input: IO, ?out: untyped, ?err: untyped, ?environment: untyped) -> Integer
14
14
  end
15
15
  class Application
16
16
  attr_reader tools: Array[untyped]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: libtmux-mcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - libtmux contributors
@@ -15,28 +15,28 @@ dependencies:
15
15
  requirements:
16
16
  - - '='
17
17
  - !ruby/object:Gem::Version
18
- version: 0.1.0
18
+ version: 0.1.2
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - '='
24
24
  - !ruby/object:Gem::Version
25
- version: 0.1.0
25
+ version: 0.1.2
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: libtmux-async
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - '='
31
31
  - !ruby/object:Gem::Version
32
- version: 0.1.0
32
+ version: 0.1.2
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - '='
38
38
  - !ruby/object:Gem::Version
39
- version: 0.1.0
39
+ version: 0.1.2
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: mcp
42
42
  requirement: !ruby/object:Gem::Requirement
@@ -92,6 +92,7 @@ executables:
92
92
  extensions: []
93
93
  extra_rdoc_files: []
94
94
  files:
95
+ - CHANGELOG.md
95
96
  - LICENSE
96
97
  - README.md
97
98
  - assets/logo.png
@@ -118,7 +119,8 @@ licenses:
118
119
  - MIT
119
120
  metadata:
120
121
  source_code_uri: https://github.com/libtmux/libtmux-ruby
121
- documentation_uri: https://libtmux.org/en/ruby/v0.1.0/mcp/
122
+ changelog_uri: https://github.com/libtmux/libtmux-ruby/blob/master/gems/libtmux-mcp/CHANGELOG.md
123
+ documentation_uri: https://libtmux.org/en/ruby/v0.1.2/mcp/
122
124
  rdoc_options: []
123
125
  require_paths:
124
126
  - lib