Your First MOO Verb
This tutorial walks you through creating, editing, and testing a MOO verb from scratch. By the end you’ll have written working Python code that runs inside the MOO, know how to send output to players, and know how to accept command arguments.
Prerequisites
Before you start:
A running DjangoMOO development environment (see Development)
An SSH connection to the server as the Wizard user
To connect, run:
ssh -p 8022 Wizard@localhost
Step 1: Orient yourself
Type look and press Enter. You’ll see whatever room your wizard starts in — for the default bootstrap, it’s The Laboratory:
↖ ↑ ↗ The Laboratory
← ↕ → A cavernous laboratory filled with gadgetry of every kind, this seems
↙ ↓ ↘ like a dumping ground for every piece of dusty forgotten equipment a mad
scientist might require.
You see a heavy wooden workbench here.
Newman, Cliff, and Player are here.
Type @who to confirm you’re connected as Wizard:
Connected players:
Wizard [The Laboratory]
As Wizard you can create and edit verbs anywhere.
Step 2: Create and edit a verb
MOO verbs live on objects. The simplest place to put your first verb is on yourself — the Wizard object. Run:
@edit verb greet on me
This creates a new verb named greet on the Wizard object and opens the built-in editor. The status bar at the bottom shows the available keybindings:
[Ctrl+S] Save [Ctrl+C/Q] Cancel
Type this as the verb code:
#!moo verb greet --on Wizard
print("Hello, world!")
The first line is a shebang — it tells DjangoMOO this is a verb named greet attached to the Wizard object. Every verb starts with one. We’ll add more flags to it in Step 4.
Press Ctrl+S, then Y to confirm the save. The editor closes and you’re back at the prompt.
(For scripted use, @edit verb also accepts a with "<code>" argument that bypasses the editor — useful for automation but harder to read for multi-line code.)
Step 3: Test it
Type greet:
$ greet
Hello, world!
You just ran your first MOO verb. print() sends output back to you, the caller, buffered until the verb finishes.
Step 4: Accept an argument
Let’s update greet to accept a name. Open the editor again:
@edit verb greet on me
Replace the code with:
#!moo verb greet --on Wizard --dspec any
from moo.sdk import context
name = context.parser.get_dobj_str()
print(f"Hello, {name}!")
Save with Ctrl+S → Y.
We’ve added --dspec any to the shebang. That tells the parser this verb requires a direct object. When the verb fires, context.parser.get_dobj_str() returns whatever the player typed after the verb name. If the player types greet with nothing after it, the parser refuses to dispatch the verb at all.
--on accepts a few forms: a player name (Wizard), an object ID (#5), or a system property ($thing, $room). When editing interactively, the verb stays on whatever object the @edit verb ... on <object> command attached it to.
Test it:
$ greet world
Hello, world!
$ greet
The verb "greet" requires a direct object.
The second response comes from the parser itself, not your code, because --dspec any prevented dispatch.
What just happened
context.parser is the parser instance for the current command. get_dobj_str() returns the direct object as a plain string — whatever the player typed after the verb name. It raises NoSuchObjectError if nothing was typed, so when --dspec any is in place you can call it unconditionally. Only catch the exception if you want to provide a custom message or fallback behavior.
from moo.sdk import context brings the per-task context object into your verb scope. It’s the standard way verbs access the parser, the player, the caller, and other ambient state. Most non-trivial verbs need it.
print() sends a line of text to the player who ran the command. For output to other players in the room, use context.player.location.announce_all_but(context.player, msg) instead.
Return values from player-typed commands are discarded. Whatever this verb returns when invoked via the parser goes nowhere — use print() for player-visible output. Verbs called from other verb code can and do return values; that’s how a lot of the standard library works.
The shebang line is how verb dispatch properties are set. --on names the object the verb lives on. When editing interactively, the verb stays wherever @edit verb ... on <object> put it; --on in the shebang doesn’t move it. --dspec any means a direct object must be present. --dspec either makes it optional. Omitting --dspec (or --dspec none) means the verb only matches when no direct object is given. See Creating MOO Verbs for the full shebang syntax including --ispec for indirect objects.
Step 5: Make it permanent (optional)
Verbs created with @edit verb live in the database and survive server restarts, but it won’t be part of other deployments using the default dataset. If you want this verb to be part of your bootstrap dataset, add a verb file in default/verbs/. See Bootstrapping Reference for how that works.
Where to go next
Creating MOO Verbs — Complete guide to verb code format, the full shebang syntax, parser API, error handling, and output mechanisms
Advanced Verb Patterns — Calling other verbs, async patterns, time-limited tasks, SDK functions
The DjangoMOO Runtime — The
contextvariable and all its attributesCommand Parser Reference — Full reference for parser methods:
get_pobj_str(),has_dobj_str(), and more