A simple, functional Matrix chat client built with Python, Typer, Pydantic, Nio, and Rich. Every interaction is a single CLI command for easy automation.
- Fast CLI commands for quick Matrix operations
- Thread support - view and navigate threaded conversations
- Reactions support - add and view emoji reactions on messages
- Message redaction - delete messages with optional reasons
- AI-friendly - every action is a single CLI command
- Functional programming style (minimal classes, maximum functions)
- Environment-based configuration
- Multiple output formats (rich, simple, JSON)
- Type-safe with dataclasses and type hints
- Persistent simple ID mapping for complex Matrix IDs
uv tool install matty
# or
pipx install matty
# or
pip install mattyFor development, clone the repo and install dependencies:
# Clone the repository
git clone https://github.com/basnijholt/matrix-cli
cd matrix-cli
# Install dependencies with uv
uv sync
# Optional: Install pre-commit hooks
uv run pre-commit installMatty uses environment variables for configuration. Create a .env file in your working directory with your Matrix credentials:
MATRIX_HOMESERVER=https://matrix.org
MATRIX_USERNAME=your_username
MATRIX_PASSWORD=your_password
MATRIX_SSL_VERIFY=true # Set to false for test servers| Variable | Description | Default | Example |
|---|---|---|---|
MATRIX_HOMESERVER |
The Matrix homeserver URL to connect to | https://matrix.org |
https://matrix.example.com |
MATRIX_USERNAME |
Your Matrix username (without @ or :server) | None (required) | alice |
MATRIX_PASSWORD |
Your Matrix account password | None (required) | secretpassword |
MATRIX_SSL_VERIFY |
Whether to verify SSL certificates | true |
false (for test servers) |
Notes:
- The username should be provided without the
@prefix or:serversuffix - Set
MATRIX_SSL_VERIFY=falsewhen connecting to test servers with self-signed certificates - Command-line options (
--username,--password) override environment variables
Usage: matty [OPTIONS] COMMAND [ARGS]...
Functional Matrix CLI client
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --install-completion Install completion for the current shell. │
│ --show-completion Show completion for the current shell, to │
│ copy it or customize the installation. │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Commands ───────────────────────────────────────────────────────────────────╮
│ rooms List all joined rooms. (alias: r) │
│ messages Show recent messages from a room. (alias: m) │
│ users Show users in a room. (alias: u) │
│ send Send a message to a room. Supports @mentions. (alias: s) │
│ threads List all threads in a room. (alias: t) │
│ thread Show all messages in a specific thread. (alias: th) │
│ reply Reply to a specific message using its handle. (alias: re) │
│ thread-start Start a new thread from a message using its handle. (alias: │
│ ts) │
│ thread-reply Reply within an existing thread. (alias: tr) │
│ react Add a reaction to a message using its handle. (alias: rx) │
│ edit Edit a message using its handle. (alias: e) │
│ redact Delete/redact a message using its handle. (alias: del) │
│ reactions Show detailed reactions for a specific message. (alias: rxs) │
╰──────────────────────────────────────────────────────────────────────────────╯
List all joined Matrix rooms:
Usage: matty rooms [OPTIONS]
List all joined rooms. (alias: r)
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --username -u TEXT Matrix username (overrides │
│ MATRIX_USERNAME env var) │
│ [default: None] │
│ --password -p TEXT Matrix password (overrides │
│ MATRIX_PASSWORD env var) │
│ [default: None] │
│ --format -f [rich|simple|json] Output format (rich/simple/json) │
│ [default: rich] │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────╯
Get recent messages from a room:
Usage: matty messages [OPTIONS] [ROOM]
Show recent messages from a room. (alias: m)
╭─ Arguments ──────────────────────────────────────────────────────────────────╮
│ room [ROOM] Room ID or name [default: None] │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --limit -l INTEGER [default: 20] │
│ --username -u TEXT Matrix username (overrides │
│ MATRIX_USERNAME env var) │
│ [default: None] │
│ --password -p TEXT Matrix password (overrides │
│ MATRIX_PASSWORD env var) │
│ [default: None] │
│ --format -f [rich|simple|json] Output format (rich/simple/json) │
│ [default: rich] │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────╯
List users in a room:
Usage: matty users [OPTIONS] [ROOM]
Show users in a room. (alias: u)
╭─ Arguments ──────────────────────────────────────────────────────────────────╮
│ room [ROOM] Room ID or name [default: None] │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --username -u TEXT Matrix username (overrides │
│ MATRIX_USERNAME env var) │
│ [default: None] │
│ --password -p TEXT Matrix password (overrides │
│ MATRIX_PASSWORD env var) │
│ [default: None] │
│ --format -f [rich|simple|json] Output format (rich/simple/json) │
│ [default: rich] │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────╯
View and interact with threads:
Usage: matty threads [OPTIONS] [ROOM]
List all threads in a room. (alias: t)
╭─ Arguments ──────────────────────────────────────────────────────────────────╮
│ room [ROOM] Room ID or name [default: None] │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --limit -l INTEGER Number of messages to check │
│ [default: 50] │
│ --username -u TEXT Matrix username (overrides │
│ MATRIX_USERNAME env var) │
│ [default: None] │
│ --password -p TEXT Matrix password (overrides │
│ MATRIX_PASSWORD env var) │
│ [default: None] │
│ --format -f [rich|simple|json] Output format (rich/simple/json) │
│ [default: rich] │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────╯
Usage: matty thread [OPTIONS] [ROOM] [THREAD_ID]
Show all messages in a specific thread. (alias: th)
╭─ Arguments ──────────────────────────────────────────────────────────────────╮
│ room [ROOM] Room ID or name [default: None] │
│ thread_id [THREAD_ID] Thread ID (t1, t2, etc.) or full Matrix ID │
│ [default: None] │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --limit -l INTEGER Number of messages to fetch │
│ [default: 50] │
│ --username -u TEXT Matrix username (overrides │
│ MATRIX_USERNAME env var) │
│ [default: None] │
│ --password -p TEXT Matrix password (overrides │
│ MATRIX_PASSWORD env var) │
│ [default: None] │
│ --format -f [rich|simple|json] Output format (rich/simple/json) │
│ [default: rich] │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────╯
Send messages to rooms:
Usage: matty send [OPTIONS] [ROOM] [MESSAGE]
Send a message to a room. Supports @mentions. (alias: s)
╭─ Arguments ──────────────────────────────────────────────────────────────────╮
│ room [ROOM] Room ID or name [default: None] │
│ message [MESSAGE] Message to send (use @username for mentions) │
│ [default: None] │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --stdin Read message from stdin │
│ --file -f PATH Read message from file [default: None] │
│ --no-mentions Don't parse @mentions in messages │
│ --username -u TEXT Matrix username (overrides MATRIX_USERNAME env │
│ var) │
│ [default: None] │
│ --password -p TEXT Matrix password (overrides MATRIX_PASSWORD env │
│ var) │
│ [default: None] │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────╯
Reply Command
Reply to messages:
Usage: matty reply [OPTIONS] [ROOM] [HANDLE] [MESSAGE]
Reply to a specific message using its handle. (alias: re)
╭─ Arguments ──────────────────────────────────────────────────────────────────╮
│ room [ROOM] Room ID or name [default: None] │
│ handle [HANDLE] Message handle (m1, m2, etc.) to reply to │
│ [default: None] │
│ message [MESSAGE] Reply message [default: None] │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --no-mentions Don't parse @mentions in messages │
│ --username -u TEXT Matrix username (overrides MATRIX_USERNAME env │
│ var) │
│ [default: None] │
│ --password -p TEXT Matrix password (overrides MATRIX_PASSWORD env │
│ var) │
│ [default: None] │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────╯
Start a thread from a message:
Usage: matty thread-start [OPTIONS] [ROOM] [HANDLE] [MESSAGE]
Start a new thread from a message using its handle. (alias: ts)
╭─ Arguments ──────────────────────────────────────────────────────────────────╮
│ room [ROOM] Room ID or name [default: None] │
│ handle [HANDLE] Message handle (m1, m2, etc.) to start thread from │
│ [default: None] │
│ message [MESSAGE] First message in the thread [default: None] │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --no-mentions Don't parse @mentions in messages │
│ --username -u TEXT Matrix username (overrides MATRIX_USERNAME env │
│ var) │
│ [default: None] │
│ --password -p TEXT Matrix password (overrides MATRIX_PASSWORD env │
│ var) │
│ [default: None] │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────╯
Reply in a thread:
Usage: matty thread-reply [OPTIONS] [ROOM] [THREAD_ID] [MESSAGE]
Reply within an existing thread. (alias: tr)
╭─ Arguments ──────────────────────────────────────────────────────────────────╮
│ room [ROOM] Room ID or name [default: None] │
│ thread_id [THREAD_ID] Thread ID (t1, t2, etc.) or full Matrix ID │
│ [default: None] │
│ message [MESSAGE] Reply message [default: None] │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --no-mentions Don't parse @mentions in messages │
│ --username -u TEXT Matrix username (overrides MATRIX_USERNAME env │
│ var) │
│ [default: None] │
│ --password -p TEXT Matrix password (overrides MATRIX_PASSWORD env │
│ var) │
│ [default: None] │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────╯
Add reactions to messages:
Usage: matty react [OPTIONS] [ROOM] [HANDLE] [EMOJI]
Add a reaction to a message using its handle. (alias: rx)
╭─ Arguments ──────────────────────────────────────────────────────────────────╮
│ room [ROOM] Room ID or name [default: None] │
│ handle [HANDLE] Message handle (m1, m2, etc.) to react to │
│ [default: None] │
│ emoji [EMOJI] Emoji reaction (e.g., 👍, ❤️, 😄) [default: None] │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --username -u TEXT Matrix username (overrides MATRIX_USERNAME env │
│ var) │
│ [default: None] │
│ --password -p TEXT Matrix password (overrides MATRIX_PASSWORD env │
│ var) │
│ [default: None] │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────╯
View reactions on a message:
Usage: matty reactions [OPTIONS] [ROOM] [HANDLE]
Show detailed reactions for a specific message. (alias: rxs)
╭─ Arguments ──────────────────────────────────────────────────────────────────╮
│ room [ROOM] Room ID or name [default: None] │
│ handle [HANDLE] Message handle (m1, m2, etc.) to show reactions for │
│ [default: None] │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --username -u TEXT Matrix username (overrides │
│ MATRIX_USERNAME env var) │
│ [default: None] │
│ --password -p TEXT Matrix password (overrides │
│ MATRIX_PASSWORD env var) │
│ [default: None] │
│ --format -f [rich|simple|json] Output format (rich/simple/json) │
│ [default: rich] │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────╯
Delete/redact messages:
Usage: matty redact [OPTIONS] [ROOM] [HANDLE]
Delete/redact a message using its handle. (alias: del)
╭─ Arguments ──────────────────────────────────────────────────────────────────╮
│ room [ROOM] Room ID or name [default: None] │
│ handle [HANDLE] Message handle (m1, m2, etc.) to redact/delete │
│ [default: None] │
╰──────────────────────────────────────────────────────────────────────────────╯
╭─ Options ────────────────────────────────────────────────────────────────────╮
│ --reason -r TEXT Reason for redaction [default: None] │
│ --username -u TEXT Matrix username (overrides MATRIX_USERNAME env │
│ var) │
│ [default: None] │
│ --password -p TEXT Matrix password (overrides MATRIX_PASSWORD env │
│ var) │
│ [default: None] │
│ --help -h Show this message and exit. │
╰──────────────────────────────────────────────────────────────────────────────╯
For faster typing, all commands have short aliases:
matty r→matty rooms- List all roomsmatty m→matty messages- Show messages from a roommatty u→matty users- Show users in a roommatty s→matty send- Send a messagematty t→matty threads- List threadsmatty th→matty thread- Show thread messagesmatty re→matty reply- Reply to a messagematty ts→matty thread-start- Start a threadmatty tr→matty thread-reply- Reply in a threadmatty rx→matty react- Add a reaction to a messagematty del→matty redact- Delete/redact a messagematty rxs→matty reactions- Show reactions on a message
# List all rooms
matty rooms
# or use alias: matty r
# Show users in a room (with mention hints)
matty users lobby
# or: matty u lobby
# Get recent messages from a room
matty messages lobby --limit 10
# or: matty m lobby --limit 10
# Send a message to a room
matty send lobby "Hello from CLI!"
# or: matty s lobby "Hello from CLI!"
# Send a message with mentions
matty send lobby "@alice check this out!"
# or: matty s lobby "@bob @alice meeting at 3pm"
# Use different output formats
matty rooms --format json
matty rooms --format simple
# or: matty r --format json# List threads in a room
matty threads lobby
# View messages in a specific thread (using simple ID)
matty thread lobby t1
# Start a thread from a message
matty thread-start lobby m2 "Starting a thread!"
# Reply in a thread (using simple thread ID)
matty thread-reply lobby t1 "Reply in thread"# Add a reaction to a message
matty react lobby m3 "👍"
# or: matty rx lobby m3 "🚀"
# View reactions on a message
matty reactions lobby m3
# or: matty rxs lobby m3 --format simple
# Delete/redact a message
matty redact lobby m5 --reason "Accidental message"
# or: matty del lobby m5# Reply to a message using handle
matty reply lobby m3 "This is a reply!"
# Reply to the 5th message in a room
matty messages lobby --limit 10
matty reply lobby m5 "Replying to message 5"The CLI supports @mentions in messages:
# Mention a user by username
matty send lobby "@alice can you check this?"
# Multiple mentions
matty send lobby "@bob @alice meeting in 5 minutes"
# List users to see available mentions
matty users lobby # Shows User IDs and simplified @mentions
# Mentions work in replies and threads too
matty reply lobby m3 "@alice I agree with your point"
matty thread-reply lobby t1 "@bob what do you think?"The mention system will:
- Automatically find the full Matrix ID for @username mentions
- Support full Matrix IDs like @user:server.com
- Format mentions properly so users get notified
The CLI uses convenient handles to reference messages and threads:
- Message handles:
m1,m2,m3, etc. - Reference messages by their position - Thread IDs:
t1,t2,t3, etc. - Reference threads with simple persistent IDs
These IDs are stored in ~/.matrix_cli_ids.json and persist across sessions.
Matrix uses complex IDs like:
- Event:
$Uj2XuH2a8EqJBh4g:matrix.org - Room:
!DfQvqvwXYsFjVcfLTp:matrix.org
Our CLI simplifies these to:
- Messages:
m1,m2,m3(temporary handles for current view) - Threads:
t1,t2,t3(persistent IDs across sessions)
The CLI supports three output formats:
- Rich (default) - Beautiful terminal UI with tables and colors
- Simple - Plain text output, perfect for scripts
- JSON - Machine-readable format for automation
Example:
# Pretty tables with colors
matty rooms
# Simple text output
matty rooms --format simple
# JSON for automation
matty rooms --format json | jq '.[] | .name'matrix-cli/
├── matty.py # Main CLI application (functional style)
├── test_client.py # Connection testing utility
├── tests/ # Test suite
│ ├── __init__.py
│ ├── conftest.py # Pytest configuration
│ └── test_matrix_cli.py # Unit tests
├── .github/ # GitHub Actions workflows
│ └── workflows/
│ ├── pytest.yml # Test runner
│ ├── release.yml # PyPI release
│ └── markdown-code-runner.yml # README updater
├── .env # Your credentials (not in git)
├── .env.example # Example environment file
├── CLAUDE.md # Development guidelines
├── pyproject.toml # Project configuration
└── README.md # This file
This project follows functional programming principles:
- Private functions (
_function_name) for internal logic - Dataclasses over dictionaries for data structures
- Type hints everywhere for clarity
- No unnecessary abstractions or class hierarchies
- Functions over classes where possible
See CLAUDE.md for detailed development guidelines.
# Run tests
uv run pytest tests/ -v
# Test with coverage
uv run pytest tests/ -v --cov=matty --cov-report=term-missing
# Test connection to Matrix server
uv run python test_client.py
# Run pre-commit checks
uv run pre-commit run --all-filesMIT