MCP conceptExamples

Choose documentation 3

Concept and alpha: example support scope and release maturity.

v0.1.0.alpha.2

Current version

v0.1.0.alpha.2
English

Prerelease This site documents an alpha of libtmux. Its structure, URLs and APIs are subject to change.

Edit this page on GitHub

Page session metadata from Ruby

libtmux-mcp 0.1.0.alpha.2 · Source

Use the MCP application directly when a Ruby host needs the same tool policy and result envelopes as its MCP clients. This complete example creates two sessions, selects one row per page, and prints both names from one retained metadata capture.

It calls the application in process. For a client that launches a stdio server, use the client guide.

Prepare the project

Use Ruby 4.0.7 with its development headers, Bundler, Git, a C compiler and Make, and tmux 3.2a or newer on a Unix host. The locked bundle includes native extensions. Start with an empty directory:

Terminal window
$ mkdir ruby-mcp-pages && cd ruby-mcp-pages

Fetch the source:

Terminal window
$ git clone https://github.com/libtmux/libtmux-ruby libtmux-source

Select the documented revision:

Terminal window
$ git -C libtmux-source checkout 9b1545562a112353c2c893a1d3e8c0d9b4b51f8d

Install the locked dependencies:

Terminal window
$ BUNDLE_FROZEN=true BUNDLE_GEMFILE=./libtmux-source/Gemfile bundle install

Run the complete example

Save this file beside the source checkout:

snapshot-sessions.rb
# frozen_string_literal: true
ENV["BUNDLE_GEMFILE"] = File.expand_path("libtmux-source/Gemfile", __dir__)
require "bundler/setup"
require "libtmux/mcp"
def describe_failure(error)
details = ["#{error.class}: #{error.message}"]
if error.is_a?(LibTmux::Error)
details.concat(error.cleanup_errors.map { |message| "Cleanup: #{message}" })
end
if error.respond_to?(:async_cleanup_errors)
details.concat(error.async_cleanup_errors.map { |message| "Cleanup: #{message}" })
end
details.join("\n")
end
def tool_data(application, name, arguments = {})
result = application.call(name, arguments).structured_content
return result.fetch("data") if result.fetch("ok")
error = result.fetch("error")
raise "#{name}: #{error.fetch('code')} (#{error.fetch('delivery')}): " \
"#{error.fetch('message')}"
end
server = nil
failures = []
begin
server = LibTmux::Server.start(timeout: 5.0)
%w[build editor].each do |name|
server.new_session(name: name, command: ["/bin/cat"], timeout: 5.0)
end
Async do |parent|
LibTmux::Async.open(parent: parent, server: server) do |scope|
application = nil
begin
application = LibTmux::MCP::Application.new(
server: scope.server, endpoint_name: "example", request_timeout: 5.0
)
capabilities = tool_data(application, "tmux_capabilities")
puts "Endpoint: #{capabilities.fetch('endpoint')}"
page = tool_data(
application, "tmux_snapshot", {entity: "session", limit: 1}
)
loop do
page.fetch("items").each do |item|
puts item.fetch("fields").fetch("name")
end
break unless page.fetch("truncated")
page = tool_data(
application, "tmux_snapshot", {cursor: page.fetch("next_cursor")}
)
end
ensure
begin
application&.close
rescue StandardError => error
failures << "MCP cleanup failed: #{describe_failure(error)}"
end
end
end
end.wait
rescue StandardError => error
failures << describe_failure(error)
ensure
begin
server&.close
rescue StandardError => error
failures << "Server cleanup failed: #{describe_failure(error)}"
end
end
abort failures.join("\n") unless failures.empty?

Run it with tmux available on PATH:

Terminal window
$ ruby -W:no-experimental snapshot-sessions.rb

Expected output:

Endpoint: example
build
editor

The Ruby option suppresses the experimental IO::Buffer warning. It does not suppress operation or cleanup errors. A failure produces a nonzero exit and diagnostics on stderr; earlier stdout may contain partial results.

Pagination

Application requires a LibTmux::Async::Server facade inside its owning Async scheduler. LibTmux::Async.open supplies that facade and closes the scope after the block. The application and its calls stay in the same scheduler throughout the example.

The first tmux_snapshot call selects session metadata with a page limit of one. A continuation sends only the returned next_cursor. It keeps the same capture and selection; it does not relist sessions. The private daemon has two sessions, so this example receives two pages. truncated: false ends the loop.

Every call checks the application’s structured_content envelope before using "data". Tool failures include a code, delivery state, and message. A missing or expired cursor is an error to handle, not an instruction to silently restart the listing. The snapshot topic explains cursor lifetime, capacity, and reference identity.

Resource ownership

Server.start creates a private socket directory and foreground tmux daemon with an empty configuration. The program creates its own build and editor sessions running cat; no existing socket, session, or pane is required.

The MCP application uses its default capability and snapshot tools. The two sessions are ordinary Ruby API setup, not mutations admitted through the MCP tool policy. Embedding the application does not constrain what other Ruby code in the host can do.

Startup and each session creation have separate five-second deadlines. The application uses request_timeout: 5.0 for metadata acquisition during capability discovery and fresh snapshot calls. Continuations read retained data. Shutdown first closes the application, then its Async scope, then the owned daemon. Each cleanup attempt still runs if an earlier operation or cleanup fails, and the program reports the collected errors.

The pinned application implementation and Async scope define these ownership and request contracts.

Esc

Type to search.

Reference preview Open reference