AutoMUD
A persistent telnet/MUD session you drive with small, discrete commands. There is no
language model and no API key inside it: the intelligence is whoever runs it, a person, a
script, or an autonomous agent. It exists because a raw telnet session is interactive and
blocking, so it cannot be held open across separate shell commands. AutoMUD keeps the
connection alive in a small background daemon and exposes simple verbs against it.
Install
pipx install git+https://github.com/CharlesCNorton/automud
Or from a clone:
pip install .
Standard library only, Python 3.8+.
Use
automud sites # list verified public targets
automud connect achaea # by name, or: automud connect <host> <port>
automud send 2 # send a line, print the reply
automud send Maelvorn
automud recv # drain any new output
automud wait --for "You are hungry" # block until output matches a regex
automud state # structured game state (GMCP) as JSON
automud status
automud close
| verb | what it does |
|---|---|
connect HOST PORT / --demo NAME |
open a session and start the daemon (--tls, --encoding ENC, --idle-exit SEC, --debug) |
send [TEXT] |
send one line, print what comes back (omit TEXT for a blank line; --stdin reads the text from stdin) |
recv |
print any new output (--nowait returns whatever is buffered immediately) |
wait --for REGEX / --gmcp PKG |
block until output matches, or a GMCP package updates |
gmcp PACKAGE [JSON] |
send a GMCP message (e.g. Char.Skills.Get) |
state [--key PKG] [--times] |
captured GMCP state as JSON (--times: seconds since each package updated) |
status |
connection, options, vitals summary |
log [--tail N] |
session transcript (last N lines with --tail) |
close |
end the session and stop the daemon |
kill |
force-stop a wedged daemon and clear the session |
sites |
directory of ~45 verified public targets (MUDs, MOOs, BBSes, services) |
Every entry in sites was verified reachable by a live probe. Connect to any of them by
name: automud connect aardwolf, automud connect telehack, automud connect fics.
Every reading verb takes --json and prints one structured object (data, prompt,
connected, elapsed, ...) instead of raw text, which is the natural mode for driving it
from a program. A minimal agent loop is just:
automud connect example.com 4000 --json
automud send look --json # {"ok": true, "data": "...", "prompt": true, ...}
automud wait --for "^You (win|die)" --max 120 --json
Exit codes: 0 ok, 1 failure, 2 usage error, 3 the operation succeeded but the
connection is closed.
Driving it with an agent
There is no agent mode and nothing to configure: an agent drives the same verbs a person does, and everything past that is behavior. One pattern is worth writing down because it falls out of the design. Since the agent holding the session sits between the user and the wire, it can be the user's entire interface to the game and re-voice everything that crosses it: a total conversion of the setting, applied live. The user names the world they want in conversation ("Achaea, but it is Paris on the 14th of July 1789 and I am a hated noble"), and from then on the agent translates both directions, the user's stated intent into real commands and the raw replies into the agreed fiction, mechanics included if the user wants them (vitals re-skinned as a HUD). What keeps it honest:
- The theme is the user's to pick. If they have not said what they want, ask; do not invent on their behalf.
- Translate, do not decide. The user's intent picks the command, and the server's actual reply decides what happened. Failures and deaths render in-fiction, but they render.
- Keep the mapping stable: the same room, denizen, or stat appears under the same converted name every time.
out.logkeeps the untranslated transcript, so the fiction is always auditable against what the server really said.
Local single-player (Cataclysm)
automud connect cdda launches a local game of Cataclysm: Dark Days
Ahead and drives it through the same verbs, so there is a
world to play even with no MUD to connect to, an obligate single-player mode for when
every public server is finally gone. It is a second backend: a persistent tmux session
holds the game open the way the telnet daemon holds a socket, and send / recv /
wait / state / status / close all work against it.
Requirements: tmux and a terminal cataclysm binary. On Windows the game runs in WSL
and automud bridges to it automatically (AUTOMUD_CDDA_DISTRO selects the distro;
AUTOMUD_CDDA_DIR / AUTOMUD_CDDA_BIN / AUTOMUD_CDDA_LAUNCH point at the binary).
automud connect cdda
automud send north # move; the reply is the message log + a status line
automud send examine east # a named action, not the seven keystrokes e-x-a-m-i-n-e
automud send nearby # list items/creatures around you (no map needed)
automud state # the character as JSON: stats, hp, needs, place, threats
automud send help # the action vocabulary for the current screen
The map is obscured on purpose. A blurry ASCII minimap is neither reliable for a
language model to parse nor necessary to one that reads a room description, so game-mode
output is the message log plus a structured status line (vitals, place, time, nearby
threats), never the tilemap. Ask for spatial detail when you want it: send look,
send examine <dir>, send nearby.
Actions, not raw keys. send understands word directions (north, se), named
keys (enter, escape), and named game actions (examine, wield, wear, ...) that
map to the right key. pickup [NAME], eat [NAME], sleep [HOURS] and wait <SPEC>
drive their whole multi-prompt flow to completion rather than just opening the menu, and
report what happened. An unrecognized word is reported, not typed out letter by letter;
single characters are still sent literally, so raw CDDA keys work too.
Character creation is one line per choice. send newgame drives the whole
pre-chargen main menu (a maze with no readable cursor where New Game and World look
identical) to the character-creation screen, and each choice is its own verb:
automud send newgame # main menu -> character creation
automud send options profession # list every choice on a tab (they scroll)
automud send scenario Sheltered
automud send profession "Sheltered Survivor" # or: send class ...
automud send stats 8 10 10 10 # STR DEX INT PER; reports the real values
automud send trait Fleet-Footed
automud send name Dougal
automud send finalize
Each reports what it actually committed (and refuses a locked or mismatched entry rather
than silently selecting the wrong one), and in chargen state returns the build so far
(scenario, profession, stats, name). Since the option lists are long and scroll off
screen (roughly 60 scenarios, 175 professions, 120 backgrounds, 200 traits), send options [TAB] enumerates every choice on a tab so nothing is hidden below the fold. The missing-mod prompts a fresh world throws are
cleared automatically.
Behaviour
- Smart waiting.
sendandrecvreturn as soon as the server stops talking, either a telnet GA/EOR prompt marker or output going quiet, so you never guess a sleep duration.--maxcaps the wait and--quietsets the idle threshold.waitextends this to regex/GMCP conditions. - GMCP. It negotiates GMCP and parses the structured state modern MUDs push (health,
room, exits, skills) into JSON for
state. Standard list deltas (Room.AddPlayer,Char.Afflictions.Add, ...) are applied to their lists, andComm.Channel.Textis kept as a boundedComm.Channel.History. TTYPE, NAWS and CHARSET negotiation are answered; options it does not implement (compression, MSDP, MXP) are refused rather than mishandled. - Encodings and TLS.
--encodingsets the wire charset (defaultutf-8; uselatin-1orcp437for older servers), and telnet CHARSET negotiation can switch it when the server asks.--tlswraps the connection (--tls-insecurefor self-signed certificates). - One session per name, held by a background daemon; a new
connectreplaces it, and-s NAMEgives you independent parallel sessions. Session state and transcripts live under a per-user state directory (override withAUTOMUD_DIR); the previous session's transcript is kept asout.prev.log. - Robust lifecycle. TCP keepalive is enabled, a dead daemon is detected by pid and its
stale session cleared, concurrent connects are serialized by a lock, a wedged daemon is
force-killed on reconnect (or by
kill), and--idle-exitstops a forgotten daemon.
Security
The control channel is a localhost-only socket authenticated by a per-session random
token. State lives in a per-user directory ($XDG_RUNTIME_DIR/automud, else
<tempdir>/automud-<uid> on POSIX, %TEMP%\automud on Windows) that is created 0700
and refused if another user owns it. Prefer automud send --stdin for passwords: argv is
visible to other local processes, and shell history persists. Plain telnet is cleartext;
use --tls where the server offers it.
Tests
python -m unittest discover -s tests -t .
License
MIT. See LICENSE.
Metadata
Release files for automud 0.3.7
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 |
|---|---|---|---|---|
| automud-0.3.7.tar.gz | 59.8 kB | With provenance | Details for automud-0.3.7.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| automud-0.3.7-py3-none-any.whl | Python 3 | none | any | Details for automud-0.3.7-py3-none-any.whl |
Total release size: 111.4 kB
Release files / automud-0.3.7.tar.gz
| Download URL | automud-0.3.7.tar.gz |
|---|---|
| Size | 59.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e039065979ba3d68d006dd33157672dfce4a694ed0420c5cd81c39c73659827a
|
|
BLAKE2b-256 checksum How to use checksums |
7421a9531a5128f53203430d1c489185a2df2078bd6fea43a5ccba6cd20a256b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 7, 2026.
Transparency logRelease files / automud-0.3.7-py3-none-any.whl
| Download URL | automud-0.3.7-py3-none-any.whl |
|---|---|
| Size | 51.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
264d284f558cb6e71a23a801bdda42a5e0544d8af9f714785f215af7a2278561
|
|
BLAKE2b-256 checksum How to use checksums |
1829afeb7e9c23b17611eca779f1121903df5e1ca1dbc9563e2ec4b72db35e52
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 7, 2026.
Transparency logSecurity for automud 0.3.7
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for automud-0.3.7.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 7, 2026.
Transparency logA 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.