This release is a pre-release and may not be stable for production use.
MegaMOO
A modern, from-scratch text world engine in Python 3, built on the LambdaMOO model — a persistent, multiplayer, fully programmable text world. Around 38,000 lines of Python built on asyncio, with zero third-party dependencies: the engine runs on the standard library alone.
Where classic MOO servers make you program in a purpose-built MOO language, MegaMOO's in-world programming language is Python itself. Every verb — from look to your own game systems — is a plain Python file with the full standard library available, executed live against a persistent object database.
This reimplements MOO's model, not its language. You get objects carrying their own properties and verbs, single-parent inheritance, live in-world programming, and a wizard/programmer/owner permission model. You do not get a MOO-language interpreter — existing MOO code does not run unmodified and has to be ported to Python. That port is largely mechanical; see Porting from LambdaMOO.
Quick start
Requires Python 3.10+.
pip install megamoo
megamoo init mygame
cd mygame && megamoo --dev
Then open the URL it prints, or telnet localhost 6770, and you are in a
world you can build.
Also on the releases
page as a wheel, if you
would rather pin a specific build than take whatever pip resolves.
megamoo --dev picks the database in the current directory, reloads verbs
as you edit them, and publishes a discovery file so external tooling can
find the running world. What init created is yours.
Optional extras: bcrypt for stronger password hashing (a salted SHA-256
fallback is built in), websockets for browser clients.
What a verb looks like
verbs/17/jump.py — the jump command available in every IC room:
"""
Jump across a gap or obstacle.
Usage: jump <exit>
"""
if not args:
pobj.msg("Jump what?")
return
# Can the character act? do_wait covers roundtime as well as the
# immobilising conditions, and emits its own message.
if pobj.do_wait():
return
pos = pobj.position or 0
if pos:
pobj.msg("You can't do that in your current position.")
return
# Match exit in room contents
exit = pmatch(dobj, pobj, list(pobj.location.contents))
if not exit or not exit.is_exit:
pobj.msg("Jump what?")
return
if exit.jumpable:
call_verb(exit, 'invoke')
elif exit.climbable:
pobj.msg("You have to climb that!")
else:
pobj.msg("You can't jump that!")
No imports, no boilerplate: the verb namespace arrives pre-loaded with the acting player (pobj), parsed arguments (args, dobj, iobj), object matching (pmatch), cross-object calls (call_verb), and the rest of the builtin library. Objects are matched the way a player thinks — jump gap, jump 2nd rope — and the verb reads top to bottom like the action it performs.
Your game is yours
mygame/
world.db the live world -- objects, properties, players
verbs/ the world's code, one file per verb
game/ your own Python, imported by verbs
megamoo.toml what to serve, and where
verbs/ and game/ are the source, and belong in your version control.
world.db is the running state — it holds player data and password
hashes, and megamoo init gitignores it for you.
Game-specific Python goes in game/ and a verb imports it the ordinary
way:
from game.combat import swing
which is the same spelling verbs already use for moo.objects. That is
the whole extension mechanism — no plugin registry, no hooks, nothing
new to learn.
The starter world comes with 191 verbs: player commands, staff and
building tools (75 @-commands), rooms and exits, containers and
furniture, and character generation. They are copied into your game, so
they are yours to edit — the engine keeps no copy you have to merge
against.
What makes it different
You change the world while people are standing in it. Edit a verb in
your editor and the running server picks it up; edit one from inside the
game with @program and the file is written for you. No build, no
restart, no deploy step, no players disconnected. The file on disk is the
source of truth and the running world follows it — in both directions.
The in-world language is Python itself. Not a MOO-like language, not a
scripting subset: verb bodies are Python source with the whole standard
library available. #42 resolves to object 42 and $utils to a system
object; that is the entire preprocessor.
Your game is a directory the engine never touches. megamoo init
gives you verbs/, game/ and a world file that are yours. Upgrading is
pip install --upgrade megamoo — there is no fork to merge, because you
never had a copy of the engine to begin with.
Nothing to install but Python. Zero third-party dependencies. The engine runs on the standard library, so a world deploys anywhere Python runs, with no build toolchain and nothing to compile.
Underneath: an asyncio core serving Telnet and WebSocket at once with
classic MUD protocol negotiation (MXP, GMCP, MSDP, MSSP); SQLite
persistence in WAL mode with a lazy object cache and periodic
checkpointing; LambdaMOO-style single-parent inheritance with a flattened
ancestor cache; a parser that handles articles, ordinals (get 2nd sword), adjective–noun matching and prepositions; tick-driven timed
effects that survive restarts; managed tasks with time limits,
suspend(5), and fork-bomb protection; and a gender-aware emit
substitution engine so actor, target and room each read correct prose.
Accessibility is part of the engine's job, not a plugin — per-player screenreader mode, ANSI-aware wrapping that measures visible width, and the option to turn server-side wrapping off entirely. See Accessibility.
Documentation
| What MegaMOO Is | Start here |
| Getting Started | Your first room, object and verb |
| Building Worlds | Rooms, exits, objects, shops |
| Writing Verbs | The programming model |
| Command Reference | All 75 staff commands |
| Coming from LambdaMOO | Importing an old database, and porting its verbs |
| The Web Client | Playing in a browser |
The engine's internals — object model, parser, verb types, the systems
underneath — are in the developer manual under docs/manual/.
Inside the engine
| Subsystem | Module | What it does |
|---|---|---|
| Server core | moo/server.py |
Event loop, serialised verb execution (one verb at a time, via the baton in moo/verb_baton.py), context propagation, graceful shutdown/restart |
| Networking | moo/network.py |
Telnet/WebSocket connections, protocol negotiation, color and wrapping |
| Database | moo/database.py |
SQLite persistence, object cache, checkpointing |
| Object model | moo/objects.py |
Inheritance, properties as native Python attributes, flags, tags |
| Parser | moo/parser.py |
Command → verb/direct-object/preposition/indirect-object resolution |
| Verbs | moo/verbs.py |
Verb compilation, preprocessing, caching, dispatch |
| Matching | moo/match_utils.py |
pmatch/bmatch natural-language object matching |
| Tasks | moo/tasks.py |
Time-limited execution, suspend/fork, scheduling |
| Effects | moo/effects.py |
Tick-driven buffs/debuffs with persistence |
| Permissions | moo/permissions.py |
Wizard/programmer/owner hierarchy, quotas |
| Builtins | moo/builtins.py |
The function library injected into every verb's namespace |
Porting from LambdaMOO
There is no MOO-language interpreter here, so existing MOO verbs have to be
rewritten in Python. The good news is that the hard part of a MOO — the object
model — carries over directly, because MegaMOO uses the same one: single-parent
inheritance, properties and verbs living on objects, ownership and permission
bits, $name system references.
What changes is the verb body. The usual substitutions:
| LambdaMOO | MegaMOO |
|---|---|
player:tell("...") |
pobj.msg("...") |
this.location |
this.location (unchanged) |
$string_utils:... |
su.... — the LambdaMOO string utilities are provided, alongside Python's own str methods |
$object_utils:... |
ou.... |
pass(@args) |
pass_(*args) |
suspend(n) |
suspend(n) — same meaning: other verbs run, yours resumes on the next line |
E_PERM and friends |
E_PERM — first-class values here too, so they can be returned, stored and compared as well as raised |
player:my_huh(...) |
verb dispatch through the parser |
length(x), tostr(a, b) |
len(x), f"{a}{b}" |
{1, 2, 3}, x[1] |
[1, 2, 3], x[0] — lists are 0-based |
A world's content is the part worth preserving — the rooms, the objects, the descriptions, the shape of the hierarchy. Verb code is usually the smaller and more replaceable half, and a lot of what old MOO utility packages exist to do (string formatting, sorting, list manipulation) is already in Python's standard library.
@import reads a LambdaMOO database — format versions 1 through 4, so
LambdaMOO up to 1.8 — and carries the objects, hierarchy and properties across,
remapping object references as it goes. Verb code comes too, but inert: it is
kept verbatim under a docstring recording where it came from and how to port
it, stored hidden and without the execute permission, so your old code sits on
the right objects under the right names instead of in a tarball. @grep 'UNPORTED MOO SOURCE' lists what is left. See
Coming from LambdaMOO for the whole process.
Who may write verbs
Verb code is ordinary Python, executed with ordinary Python privileges. A
verb can import any module, open files, and reach __builtins__. That is
deliberate — it is what "the in-world language is Python itself" means, and
it is why the standard library is genuinely available rather than a curated
subset.
The consequence is that the security boundary is who can create a verb,
not what a verb can do once created. @program, @adverb and eval are
gated at gm3. Anyone holding gm3 can run arbitrary code as the server
process and should be considered as trusted as the person running it.
Two things follow:
- The permission-checking
getattr/setattrin the verb namespace are a guard rail for well-behaved code, not an enforcement boundary. Verb code that wants the unchecked versions can reach them. - Think carefully before granting gm3 broadly. Classic MOO let ordinary players program, which worked because the MOO language was itself the sandbox. MegaMOO has no equivalent boundary, so opening programming to untrusted players would hand them the host account.
If you want a world where players write code, that needs a real sandbox — a separate piece of work, and not something the current permission levels provide.
Accessibility
MegaMOO is developed by a quadriplegic (C1–C2) programmer using a head-pointer input device at about 30 words per minute, in collaboration with AI pair-programming tools. That vantage point shapes the engine:
- Screenreader mode (
screenreadercommand) strips all ANSI color and visual decoration from output, per player, persistently. - ANSI-aware wrapping computes visible text width so escape sequences never break line layout; server-side wrapping can also be disabled entirely (
WRAP_WIDTH = 0) for screen readers and clients that reflow. - Text-native gameplay: everything in the world — movement, building, programming — is fully playable through assistive input at any typing speed, because a MOO rewards thought, not reflexes.
Text worlds were the original accessible online games. MegaMOO treats keeping them that way as part of the engine's job.
MCP Server (AI Integration)
Claude Code (or any MCP client) can drive the running game directly: execute commands as a TestBot character, inspect and edit live objects and verbs, and read server logs — all without touching the database by hand.
Architecture
Claude Code ──stdio──> tools/megamoo_mcp.py ──TCP/JSON──> ApiServer (port 7778)
(MCP bridge) inside running MegaMOO
│
└──reads──> megamoo.log (works even when server is down)
tools/megamoo_mcp.py is a standalone FastMCP bridge. The game server gains no new dependencies.
One-time setup
pip install mcp
megamoo --dev # API on, token reused
claude mcp add megamoo -e MEGAMOO_API_TOKEN=<token> -- python3 tools/megamoo_mcp.py
Tools (17 total)
Game tools — require the server to be running:
| Tool | Description |
|---|---|
run_command |
Execute a game command as TestBot; returns all output (color stripped) |
disconnect_testbot |
Cleanly unpuppet TestBot via the normal on_unpuppet path |
get_object |
Full object info: name, parent, flags, properties, verbs |
get_location |
Object's current location (objnum + name) |
list_contents |
Contents of a room or container (objnum + name pairs) |
list_verbs |
Verb names defined on an object, optionally including inherited |
get_verb |
Source code of a verb |
set_verb |
Write or replace a verb's source code |
delete_verb |
Remove a verb from an object |
list_properties |
Property names defined on an object, optionally including inherited |
get_property |
Value of a single property |
set_property |
Write a property value (any JSON-representable type) |
eval_code |
Evaluate arbitrary Python in the game's verb context |
search_verbs |
Full-text search across all verb source files |
search_objects |
Search objects by name or description |
Disk tools — work even when the server is down:
| Tool | Description |
|---|---|
tail_log |
Last N lines of megamoo.log, with optional regex filter |
server_status |
API reachability; if up, includes uptime and player count |
Notes
- TestBot is auto-created on first
run_commandif no character named TestBot exists under#5 ICharacter. To use a specific character instead, settestbot_objnumin the API config section before starting the server. - The integration test suite (
python3 -m pytest tests/) boots its own server on ports 7901/7902 against a scratch copy of the starter world, so it can run while a dev server is up. Two caveats: the test subprocess appends to the shared repo-rootmegamoo.log, and the snapshot is a plain file copy — if the live server is writing heavily at that moment, the copy could be torn. When in doubt, stop the server or run only the unit tests:python3 -m pytest tests/ --ignore=tests/test_integration_api.py. - Design spec: docs/superpowers/specs/2026-06-12-mcp-server-design.md
Status
Active development. The engine runs a private in-progress game world (character generation, building tools, and the accessibility layer are all exercised daily); it has not yet had a public multiplayer deployment. Near-term roadmap:
- Web client polish on the WebSocket path
- Documentation for the builtin library and verb-authoring conventions
Credits
- LambdaMOO by Pavel Curtis et al. — the object model, permission system, and the idea that a world should be programmable from the inside.
- Evennia — inspiration for network protocol handling and the tag system.
License
MIT — see LICENSE.
Metadata
Release files for megamoo 0.10.0b12
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| megamoo-0.10.0b12.tar.gz | 993.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| megamoo-0.10.0b12-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 2.0 MB
Release files / megamoo-0.10.0b12.tar.gz
| Download URL | megamoo-0.10.0b12.tar.gz |
|---|---|
| Size | 993.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2f964476655107e8553bac441fb62c6bb0bb525c154c5702500e42835d02a7ca
|
|
BLAKE2b-256 checksum How to use checksums |
8adafe6b3bb1fd2d9b80b6d2277575b90d2552f581efa088e13cec169165718b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Release files / megamoo-0.10.0b12-py3-none-any.whl
| Download URL | megamoo-0.10.0b12-py3-none-any.whl |
|---|---|
| Size | 1.0 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c234cf1f172b9a3e302f49e246141cf637865cfec6d9592fa276af87e194512a
|
|
BLAKE2b-256 checksum How to use checksums |
6be3a8c649ee9a132ec6e0ba9b94bf6ffc2497c4f4a257c16d4e96133ab4bfd7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|