teamspeak-cli is a terminal-first CLI for controlling a TeamSpeak 3 client through a local plugin bridge. The ts binary talks to a local control socket, and the plugin loaded inside the official TeamSpeak 3 client talks to regular TeamSpeak servers through the TeamSpeak 3 Client Plugin SDK.
This project is not ServerQuery, not WebQuery, and not a standalone TeamSpeak ClientLib client.
Quick links: Highlights • Project Status • Quick Start • Installation • Usage • Configuration • Development • Troubleshooting • Documentation • Contributing • Security • License
Docs: Architecture • Plugin integration • Command reference • Output format • Event catalog • Roadmap • Contributing • Security
ts: the CLI users runts3cli_plugin.so: the optional TeamSpeak 3 client plugin used for live integrationts_mock_bridge_host: a local mock bridge host used by tests and CI- a fully local
mock-localprofile for development without TeamSpeak installed - install and uninstall scripts for a user-level Linux
x86_64setup
- the primary supported install surface is user-level Linux
x86_64 mock-localis the fastest path for normal development and the most deterministic CI surface- GitHub Actions runs
make teston Ubuntu 22.04 for pushes tomainand pull requests make test-e2eandmake env-upare available, but they are still host-sensitive local integration tools rather than the primary always-green workflow
Use this when you want a normal user install from the latest published GitHub release without cloning the repo:
curl -fsSL https://raw.githubusercontent.com/spi3/teamspeak-cli/main/scripts/install-release.sh | bashts version
ts client startUse this when you want to work on the CLI, config, rendering, socket protocol, or session layer without any proprietary TeamSpeak runtime on your machine.
make build-mock
make test-mock
./build-mock/ts config init
./build-mock/ts --profile mock-local status
./build-mock/ts --profile mock-local channel listUse this when you need the real TeamSpeak 3 client plugin and runtime bridge.
make build
make test
ts --profile plugin-local plugin info
ts --profile plugin-local statusmake build does more than raw CMake defaults:
- bootstraps managed TeamSpeak inputs under
third_party/teamspeak/managed - configures the plugin-backed build
- builds
ts,ts3cli_plugin.so, tests, and helper binaries
If you want to prefetch the managed runtime inputs first, run:
make depsFor normal user installs on Linux x86_64, use the published GitHub release installer directly without cloning the repo:
curl -fsSL https://raw.githubusercontent.com/spi3/teamspeak-cli/main/scripts/install-release.sh | bashThat installer:
- resolves the latest published GitHub release from
spi3/teamspeak-cli - downloads the release archive and checksum
- caches release downloads and TeamSpeak runtime assets under
~/.cache/teamspeak-cli/install - installs
tsand the bundled docs/examples under~/.local - installs the TeamSpeak client and
ts3cli_plugin.sounder~/.local/share/teamspeak-cli/teamspeak3-client - installs
~/.local/bin/ts3clientas a wrapper launcher for the installed client - resolves
Xvfbfor headless client launches, bootstrapping it into the managed cache when needed - installs PulseAudio-compatible audio tooling for safe headless/media routing when it is missing
- installs
ffmpegfor MP3 playback injection when it is missing - installs
~/.local/bin/ts-uninstall - initializes
~/.config/ts/config.iniwhen that file does not already exist
To pin a specific release instead of the latest one, run:
curl -fsSL https://raw.githubusercontent.com/spi3/teamspeak-cli/main/scripts/install-release.sh | bash -s -- --release-tag vX.Y.ZIf you already have a checkout and want to inspect or customize the release installer locally first, run:
./scripts/install-release.sh --helpRemove a user-level install later with:
ts-uninstallUse ts-uninstall --keep-config if you want to preserve a config file that the installer created.
After installing from a published release, update the installed CLI, bundled plugin, and managed TeamSpeak client bundle with:
ts updatets update reads the install receipt, then reruns the release installer against the same install paths using the
latest published release from spi3/teamspeak-cli. To pin a specific release, run ts update --release-tag vX.Y.Z.
If you want the installer to build directly from your local checkout instead of using published release artifacts, run:
./scripts/install.shBy default the local-checkout installer:
- caches managed downloads under
~/.cache/teamspeak-cli/install - builds a
Releasetree inbuild-install - installs
tsand the bundled docs/examples under~/.local - installs the TeamSpeak client and
ts3cli_plugin.sounder~/.local/share/teamspeak-cli/teamspeak3-client - installs
~/.local/bin/ts3clientas a wrapper launcher for the installed client - resolves
Xvfbfor headless client launches, bootstrapping it into the managed cache when needed - installs PulseAudio-compatible audio tooling for safe headless/media routing when it is missing
- installs
ffmpegfor MP3 playback injection when it is missing - installs
~/.local/bin/ts-uninstall - initializes
~/.config/ts/config.iniwhen that file does not already exist
Inspect local-build installer overrides with:
./scripts/install.sh --helpThe CLI is organized into small command groups:
versionupdateplugin infoconfig init,config path,config viewprofile create,profile list,profile show,profile set,profile unset,profile delete,profile useconnect,disconnect,mute,unmute,speakers mute,speakers unmute,status,server info,server group applydaemon start,daemon stop,daemon statuschannel list,channel get,channel join,channel rename,channel clientsclient status,client start,client inspect-windows,client stop,client list,client getmessage send,message inboxplayback status,playback sendevents watch,events hook add,events hook list,events hook removecompletion bash|zsh|fish|powershell
Examples against the offline backend:
./build-mock/ts --profile mock-local status
./build-mock/ts --profile mock-local channel list --json
./build-mock/ts --profile mock-local channel clients Engineering
./build-mock/ts --profile mock-local channel rename Engineering --name Platform
./build-mock/ts --profile mock-local server group apply --group Operator --client alice
./build-mock/ts daemon start
./build-mock/ts message inbox
./build-mock/ts --profile mock-local events watch --count 5
./build-mock/ts --profile mock-local events watch --count 5 --output ndjsonExamples against the real plugin backend after the TeamSpeak client is running and the plugin is enabled:
ts --profile plugin-local plugin info
ts --profile plugin-local status
ts --profile plugin-local channel list
ts --profile plugin-local client list
ts --profile plugin-local channel rename Engineering --name Platform
ts --profile plugin-local server group apply --group Operator --client alice
ts --profile plugin-local speakers mute
ts daemon start
ts message inbox
ts --profile plugin-local message send --target channel --id Lobby --text "hello"
ts --profile plugin-local playback status
ts --profile plugin-local playback send --file ./message.mp3To connect through the real client:
ts --profile plugin-local \
--server voice.example.com:9987 \
--nickname terminal \
connectWhen output is table, these commands stream human-readable progress by default:
connectdisconnectclient startclient stop
When output is json or experimental yaml, they print one structured result at the end instead. Use JSON for stable automation.
events watch --output ndjson prints one JSON event object per line for line-oriented consumers. It currently waits for the requested count or timeout, then writes and flushes each returned event line.
For shell scripts that need one JSON scalar, use --field <path> with JSON output:
ts --json status --field phase
ts --json plugin info --field media_diagnostics.transmit_path_readyFor human tables, --no-headers removes header rows and --wide adds extra columns to supported list tables. These options do not change JSON, YAML, NDJSON, or --field output.
To capture messages and trigger local scripts without keeping ts events watch attached, start the local daemon:
ts daemon start
ts events hook add --type message.received --message-kind client --exec 'notify-send "TeamSpeak DM" "$TS_MESSAGE_FROM: $TS_MESSAGE_TEXT"'
ts message inbox --count 20See docs/events.md for the supported event types, backend availability, payload fields, and the separate media bridge event surface.
See docs/output-format.md for the stdout/stderr and machine-output contract.
ts uses an INI config file.
- path precedence:
--config /path/to/config.ini, thenTS_CONFIG_PATH, then$XDG_CONFIG_HOME/ts/config.inior the default~/.config/ts/config.ini - inspect the resolved path with
ts config path - initialize a starter file with
ts config init
Config files can include server_password= and channel_password= values. Treat them as private; ts writes config
files with owner-only permissions on Unix. Profile list/show output redacts or omits secret values.
The starter config ships with two profiles:
mock-local: fully local mock backendplugin-local: local socket backend for a real TeamSpeak client plugin
Useful profile commands:
ts config path
ts config init
ts config view
ts profile list
ts profile show plugin-local
ts profile set plugin-local nickname terminal
ts profile unset plugin-local default_channel
ts profile delete old-profile
ts profile use mock-localThe plugin socket path resolves in this order:
control_socket_path=in the selected profile, if non-emptyTS_CONTROL_SOCKET_PATH, if set- otherwise a runtime-local default such as
$XDG_RUNTIME_DIR/ts3cli.sockor/tmp/ts3cli-<uid>.sock
Leave control_socket_path= blank unless you intentionally want to pin one fixed socket path in config.
make help: list the supported top-level workflowsmake build-mock: build the offline development treemake test-mock: run the offline suitemake build: build the TeamSpeak-backed tree and bootstrap managed dependenciesmake test: run the default automated suite without the Docker/XvfbE2E case; this mocks PulseAudio bootstrap instead of installing host audio packagesmake test-e2e: run the TeamSpeak-backed local integration harnessmake env-up,make env-info,make env-down: keep the TeamSpeak-backed environment running for manual checks
Cut a tagged release from a clean branch with:
./scripts/release.sh patch
./scripts/release.sh minor
./scripts/release.sh 1.2.3That script bumps the version in the CMake version files, runs make test,
commits the release change, pushes an annotated vX.Y.Z tag, and creates the
GitHub release entry. The tag push then triggers the release workflow to build
and upload the packaged release assets.
There are two important build surfaces:
make ...: the repo's high-level workflow for normal use- raw
cmake ...: the low-level workflow when you want precise control
Raw CMake defaults do not build the TeamSpeak plugin target. They build the CLI, the mock bridge host, and the test binaries.
Example plain CMake configure:
cmake -S . -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_MAKE_PROGRAM=ninjaTo build the TeamSpeak plugin with raw CMake, opt in explicitly:
cmake -S . -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_MAKE_PROGRAM=ninja \
-DTS_ENABLE_TS3_PLUGIN=ON \
-DTS_ENABLE_TS3_E2E=ON \
-DTS3_MANAGED_DIR=third_party/teamspeak/managedIf SDK auto-discovery is not enough, pass -DTS3_PLUGIN_SDK_INCLUDE_DIR=/path/to/ts3client-pluginsdk/include.
If cmake or ninja are not on PATH, prefer the top-level make targets instead of hardcoding one machine's fallback toolchain path.
The repo-managed TeamSpeak runtime is intentionally pinned, not floating:
- the Linux client bootstrap defaults live in
tests/e2e/runtime_common.sh - the managed cache lives under
third_party/teamspeak/managed make depswrites resolved paths tothird_party/teamspeak/managed/deps.mkanddeps.env
Useful overrides:
TS3_MANAGED_DIRTS3_CLIENT_DIRTS3_CLIENT_VERSIONTS3_CLIENT_URLTS3_CLIENT_SHA256TS3_XDOTOOLTS3_XDOTOOL_LIBRARY_PATHTS3_XVFBTS3_XVFB_LIBRARY_PATHTS3_XVFB_XKB_DIRTS3_XVFB_BINARY_DIR
The TeamSpeak-backed local harness starts:
- a TeamSpeak 3 server in Docker
- a TeamSpeak 3 client under
Xvfb ts3cli_plugin.soinside that client
Run it directly:
make test-e2eOr keep it running for manual checks:
make env-up
make env-info
make env-ts ARGS='plugin info'
make env-ts ARGS='status'
make env-downThis path is still host-sensitive. TeamSpeak first-run dialogs, display quirks, audio backends, or upstream UI changes can still break automation even when the plugin itself loads correctly.
- format C++ with
clang-format -i - prefer
rgfor searches - keep CLI parsing and command dispatch in
src/teamspeak_cli/cli/ - keep TeamSpeak-specific details behind the backend and socket bridge layers
- prefer extending the backend seam over leaking TeamSpeak callback details into the CLI
If ts plugin info or ts status says the plugin bridge is unavailable:
- run
ts client start - make sure the TeamSpeak client plugin is enabled
- confirm the CLI and client agree on the same socket path
- use
ts plugin infoagain to inspect the resolved socket path and backend note
If ts client start cannot find a launcher:
- install the user-level bundle with
./scripts/install.sh - or set
TS_CLIENT_LAUNCHER - or set
TS3_CLIENT_DIRto a TeamSpeak client tree containingts3client_runscript.sh
If headless launch fails:
- rerun the installer so it can resolve or bootstrap
Xvfb - on Debian/Ubuntu, make sure
x11-xkb-utilsandxkb-dataare installed if/usr/bin/xkbcompor XKB data is missing - on Debian/Ubuntu, make sure
pulseaudio-utilsand eitherpulseaudioorpipewire-pulseare installed if the launcher reports that audio preflight is unavailable - on Debian/Ubuntu, make sure
ffmpegis installed if MP3 playback decoding is unavailable - or set
TS_CLIENT_HEADLESS=0to force a GUI launch on an existing display - or set
TS_CLIENT_XVFBandTS_CLIENT_HEADLESS_DISPLAYexplicitly
If ts playback send --file message.mp3 reports that MP3 decoding is unavailable, install ffmpeg or send a WAV file
encoded as PCM signed 16-bit little-endian, 48000 Hz, mono.
If headless ts connect times out while ts plugin info is responsive:
- run
ts client inspect-windowsto list visible TeamSpeak dialogs on the tracked X11 display - complete any required TeamSpeak license or identity setup before retrying
ts connect - use
ts client start --accept-licenseorTS_CLIENT_ACCEPT_LICENSE=1only if you accept the TeamSpeak license terms and want first-run headless automation to click the license dialog - use
ts client logsalongside the window list when the display cannot be inspected
If ts client start succeeds in a headless non-interactive session but the TeamSpeak client disappears again when that
session ends:
- leave
TS_CLIENT_SYSTEMD_RUNunset or set it to1sots client startcan use a transientsystemd-run --userunit - if your user does not have a working user
systemdmanager, setTS_CLIENT_SYSTEMD_RUN=0to fall back to the legacy detached launcher path - confirm
systemd-run --user trueworks for the current user if you expect the launched client to survive session teardown
If ts client start reports a missing shared library such as libXi.so.6:
- install a system package that provides that library
- or add the library under the TeamSpeak client
runtime-libstree - if you used the bundled installer, rerun it to refresh the managed runtime library bundle
If the installed ts3client launcher prints QCoreApplication::applicationDirPath and then the TeamSpeak client segfaults:
- this is usually an upstream TeamSpeak Linux audio-stack crash, not a
ts3cli_plugin.sostartup failure - check custom PipeWire or PulseAudio sinks and sources for missing
device.descriptionornode.description - the generated
ts3clientwrapper now warns whenpactlreports sinks or sources without aDescription:field - set
TS3CLIENT_SKIP_AUDIO_PREFLIGHT=1only if you need to suppress that warning and have already ruled audio metadata out
If make test-e2e fails after the client starts:
- read the temp directory that the harness prints on failure
- inspect the client log, visible dialogs, and socket path in that temp dir
- expect to debug first-run TeamSpeak dialogs and host-specific runtime issues before treating the harness as stable
Released under the MIT License.