jms-cli
A personal CLI tool and Python library for accessing assets behind a JumpServer v4 bastion host: remote command execution, interactive terminal, parallel SFTP file transfer, and rsync/scp incremental sync via an SSH bridge.
Features
jms exec— run commands on assets (SSH or WebSocket backend, auto fallback)jms login— interactive PTY with keepalive (Ctrl+] to exit)jms sftp— parallel SFTP upload/download, chunked large files, cross-server relayjms ssh-pipe—-ebridge forrsync/scp- Multi-server config, MFA (TOTP) support, AES-256-GCM encrypted credentials
- Local MCP stdio server (
jms mcp) exposing config/asset/exec/SFTP as tools for AI assistants - Usable as a Python library (
from jms import JMSSession, connect, ...)
Install
pip install jms-cli # or: uv tool install jms-cli
Requires Python >= 3.10. For development from source:
git clone git@github.com:GCS-ZHN/jms-cli.git
cd jms-cli && uv sync
Quick start
jms config add prod # add a server (interactive, verifies credentials)
jms ls [-q keyword] # list/search assets
jms exec web@prod uname -a # run a command (asset[@server])
jms login web@prod # interactive terminal
jms sftp ./file.tar.gz web@prod:/tmp/ # direction auto-detected, -j N parallel
rsync -avz -e "jms ssh-pipe" ./dir/ web@prod:/data/
Config lives in the platform config dir (platformdirs, e.g.
~/Library/Application Support/jms/config.yaml on macOS, ~/.config/jms/ on
Linux), written with 0600 permissions. Credentials are stored encrypted.
Each command also accepts a hidden --config <path> option to point at a
different config file.
CLI operations
Target syntax is SSH-style: <asset>[@<server>]. Omit @<server> to use the
default server (the first one you added).
Server config
jms config add prod # interactive; verifies credentials before saving
jms config list # show servers; default marked with '*'
jms config set-default prod # change the default server
jms config remove prod # delete a server (-y to skip confirmation)
List / search assets
jms ls # all assets on the default server
jms ls @prod # all assets on server 'prod'
jms ls -q mysql # filter by keyword
Remote command execution
-b ssh|ws|auto selects the backend (auto = SSH first, WebSocket fallback).
The remote exit code is propagated to the local shell.
jms exec web@prod uname -a
jms exec web ls -la /var/log
jms exec web@prod 'echo hello world'
jms exec -b ws web whoami # force WebSocket backend
jms exec -t 60 web 'sleep 30' # longer timeout (default 30s)
Interactive terminal
jms login web@prod # Ctrl+] to exit
jms login web # default server
jms login -b ssh web # force SSH backend (native PTY)
File transfer (SFTP)
Direction is auto-detected from the arguments; -j N sets parallelism.
jms sftp ./data.tar.gz web@prod:/tmp/ # upload
jms sftp web@prod:/tmp/data.tar.gz ./ # download
jms sftp ./src/ web@prod:~/dst/ # directory tree
jms sftp -j 8 ./big/ web@prod:/data/ # 8 parallel workers
jms sftp --no-verify web@prod:/f ./ # skip md5 verification
jms sftp web@prod:/f other@prod:/g # cross-server relay (memory-streamed)
rsync / scp bridge
rsync -avz -e "jms ssh-pipe" ./dir/ web@prod:/data/
scp -o ProxyCommand="jms ssh-pipe %h" ./f web@prod:/data/
Known limitation: rsync downloads over the bridge may hang on files larger
than ~4KB (see DEVELOP.md §5). Use jms sftp for downloads; rsync uploads
work fine.
Library usage
from jms import JMSSession, ServerConfig, resolve_asset, connect, BackendType
server = ServerConfig(name="prod", host="jump.example.com",
username="alice", password="...", otp_secret="...")
sess = JMSSession(server)
sess.login()
asset = resolve_asset(sess, "web")
with connect(sess, asset, backend=BackendType.AUTO) as term:
print(term.execute("uname -a"))
MCP server (AI assistants)
jms mcp starts a local Model Context Protocol
stdio server exposing jms-cli as MCP tools for AI assistants / MCP clients.
Every tool opens a fresh authenticated session per call (stateless, no shared
state). No interactive PTY is used — all tools are headless operations.
Register it in your MCP client config, e.g. opencode (.opencode/opencode.json):
{
"mcp": {
"jumpserver": {
"type": "local",
"command": ["uv", "run", "jms", "mcp"]
}
}
}
Tools
| Tool | Description |
|---|---|
jms_config_list |
List configured servers (default marked with *) |
jms_ls |
List / keyword-search authorized assets |
jms_resolve_asset |
Resolve an asset to connection info (address/account/protocol) |
jms_exec |
Run a command on an asset, return output (timeout param) |
jms_sftp_upload |
Upload a local file to an asset via SFTP |
jms_sftp_download |
Download a file from an asset to the local machine |
jms_sftp_relay |
Relay a file between two assets (memory-streamed, no local disk) |
Each tool accepts an optional server alias (defaults to the configured
default) and an optional config_path. The server-wide default config path
falls back to the JMS_CONFIG env var, then the platform config dir.
Roadmap / TODO
- Admin operations (JumpServer management API): asset CRUD, user
management, permission/grant management — the REST layer (
jms/core/) is designed for these resource modules - GitHub Actions CI (pure unit tests always run; real-server tests
auto-skip without
JMS_TEST_*env vars) - PyPI + GitHub release on tag push
- Dependency audit (
pip-audit) in CI
Development
See DEVELOP.md (Chinese) for architecture, dev principles and testing rules.
Metadata
Release files for jms-cli 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| jms_cli-0.2.0.tar.gz | 193.5 kB | Without provenance | Details for jms_cli-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| jms_cli-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for jms_cli-0.2.0-py3-none-any.whl |
Total release size: 268.4 kB
Release files / jms_cli-0.2.0.tar.gz
| Download URL | jms_cli-0.2.0.tar.gz |
|---|---|
| Size | 193.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3a5264e8a54368b82f489164aa95e9e74feeac38a1dbe26a7e8413f22364e053
|
|
BLAKE2b-256 checksum How to use checksums |
0828ec6f98954cbcf3b32c652009d074ef1e2fe4da2f64dfc4d6d4338de0a148
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.8
|
Release files / jms_cli-0.2.0-py3-none-any.whl
| Download URL | jms_cli-0.2.0-py3-none-any.whl |
|---|---|
| Size | 74.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2ca899f1c00a8b2f43216ab1a0170617c45bd017886062bacb498d8fa365405b
|
|
BLAKE2b-256 checksum How to use checksums |
2b39e7e529c97b47e1f35b1395172559d620f89985614fd424b1ab485fc00ec5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.8
|
Security for jms-cli 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.