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 +4 -4
- data/CHANGELOG.md +68 -0
- data/README.md +20 -3
- data/lib/libtmux/mcp/cli.rb +32 -11
- data/lib/libtmux/mcp/version.rb +1 -1
- data/sig/libtmux-mcp.rbs +1 -1
- metadata +8 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e455168a6f0c00704e8728c52c33cd6cd0ae786648a9be633aafd5ff469e1b24
|
|
4
|
+
data.tar.gz: 836e5048e210aed6fabb35d5ca57e031eb9a9a6adb1bb736877df77f47393aa6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
+
[](https://libtmux.org/en/ruby/latest/mcp/)
|
|
24
|
+
[](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
|
-
|
|
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.
|
|
354
|
-
|
|
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
|
|
data/lib/libtmux/mcp/cli.rb
CHANGED
|
@@ -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 =
|
|
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
|
|
189
|
-
options.on("--socket PATH", "Borrow
|
|
190
|
-
|
|
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
|
|
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?
|
data/lib/libtmux/mcp/version.rb
CHANGED
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|