Standards:MUD Client Vocabulary Protocol
DRAFT: This specification is under discussion and subject to change. Do not implement against this text.
MUD Client Vocabulary Protocol (MCVP)
A Standard for Publishing a Game's Command Vocabulary to MUD Clients over GMCP
Status of this Memo
This memo describes the addition of a standardized namespace of the Generic Mud Communication Protocol (GMCP) telnet sub-negotiation protocol (201) that provides syntax and usage guidance for sending JSON formatted messages from game servers to text-based game clients describing the words, shortcuts and command patterns available to a character.
Overview and Rationale
Every game defines its own language (commands, socials, channels, directions, player-defined shortcuts), and today that language is undocumented folklore that each player's client rediscovers by screen-scraping, if at all. The server is the sole authority on its own vocabulary; this protocol makes that authority available to clients as structured data.
The motivating consumer is speech recognition: on-device speech-to-text engines can be told which words to expect (biasing, explained under Priorities and Budgets), and can repair a near-miss against a known word list, both of which substantially improve recognition of game commands, but only when the client knows which words the player is likely to say. The same data serves tab-completion, command discovery for new players, syntax hinting, and text-to-speech pronunciation of game-specific terms. A client implements the protocol once and gains these capabilities on every conforming game; a game implements it once and improves every conforming client. Manual:Speech to Text describes what that looks like to a player on one such client.
The design is shaped by a practical constraint of speech engines: biasing lists degrade recognition when they grow past a few hundred entries. The protocol therefore carries priorities and budgets, assigned by the server, so that scarce biasing capacity is spent on the vocabulary a character actually uses.
That constraint is why the budgets exist: engine documentation is plain that oversized biasing lists degrade recognition rather than merely plateau. Measurement across a conforming server and client bears the budgets out: a budget-sized list, dynamic context first with the catalog filling the remainder, improves exact recognition, and the words rescued most often are the game's short command verbs. What the budget is spent on matters as much as how it is filled.
Overview of the GMCP (201) Sub-negotiation Protocol
To recap, the GMCP protocol is a bidirectional telnet sub-negotiation protocol (See RFC 854) which fulfills the following conditions:
- GMCP is separated into several ‘namespaces’, which may be enabled or disabled by the client at any time. The ‘Core’ namespace is always enabled and may not be disabled. Namespaces should consist of case-insensitive alphabetical characters and stop characters (. - ASCII 46/0x2E), representable as [A-Za-z.].
- GMCP messages consist of the namespace and a command (case insensitive alpha characters) delineated by a stop character, and optionally a space character and JSON-encoded payload.
- GMCP messages may be sent by both the client or server at any time with no warning.
Update to namespace: Client.* for Client.Vocabulary
We are proposing an addition to the Client.* meta-namespace for Client.Vocabulary. This serves the purpose of reserving this namespace for publishing game vocabulary to clients and supporting future expansion and integration.
Design Principles
- One-directional forever. The server publishes vocabulary and nothing else. Nothing in this protocol requests, receives, or stores anything about what a client recognized, spoke, completed or corrected. Extensions MUST preserve this property.
- Data, not code. The catalog is descriptive. It carries no executable content, no trigger patterns, and no instructions; a client interprets it entirely on its own terms.
- The catalog is personal. It describes what one character can say, not what the game contains: that character's available commands, readable channels, and own shortcuts. A player's shortcuts are sent only to that player's own client.
- The catalog is durable. It carries the vocabulary that outlives a moment: commands, socials, channels, directions, shortcuts. It does not carry the things standing in front of a character, which change with every step and would defeat the caching and version identity the rest of this protocol is built on. Those are bound by the client from data the game already publishes; see Syntax Patterns.
- Fail safe. Wherever a client meets something it cannot interpret (an unknown category, field, slot class or flag value), the required reading is the conservative one. In particular, this data nudges a speech recognizer toward saying things, so anything ambiguous is excluded from that nudge rather than included.
- Lineage is protocol-verified; content is a server obligation. Incremental updates carry the version they apply on top of, so a client can detect a broken chain. It cannot verify content: correctness of each link is the server's responsibility alone.
Negotiation and Lifecycle
The package is never sent unnegotiated. A client opts in through the standard Core.Supports mechanism:
Core.Supports.Set ["Client.Vocabulary 1", ...]
A server SHOULD accept any advertised version greater than or equal to 1 and speak the highest version it supports that does not exceed the client's, rather than comparing strictly against one value.
After negotiation the server sends one full Client.Vocabulary.Catalog. After that it sends again only when the catalog content changes, either a fresh Catalog or an incremental Client.Vocabulary.Update.
- Re-request: a client may request the full Catalog at any time by sending the bare package name
Client.Vocabularyas a GMCP message; the server answers with a full Catalog even when the version is unchanged. Servers MAY throttle catalog rebuilds and answer from a held copy inside the throttle window; clients therefore treat an identical-version response as success and MUST NOT build retry loops around re-requests. - Reconnect: the server always starts a session with a full Catalog. An Update is never the first vocabulary message of a session; a client receiving one MUST discard it and issue a single re-request.
- Withdrawal:
Core.Supports.Removestops all vocabulary traffic. A later re-add starts over with an unsolicited full Catalog; the client does not pair the re-add with a re-request. A client's locally cached catalog survives withdrawal and may satisfy the new Catalog by version match.
Field Encoding
Boolean-valued fields in this protocol are declared as true or false, and that is their canonical encoding. However, several MUD drivers have no JSON boolean in their serializer at all (LDMud's json_serialize(), for example, maps integers to JSON numbers and offers no true/false), so a server obliged to report one may have nothing canonical to send. A server MAY therefore send any boolean field as a JSON boolean, as the string "true"/"false", or as the integer 1/0; clients MUST accept all three forms and understand them as the boolean they represent after parsing. Servers SHOULD send a JSON boolean where the serializer can produce one, SHOULD NOT vary the encoding of a given field within one connection, and SHOULD omit an optional boolean field entirely rather than sending its false value.
For the safety flag protected, lenient parsing is deliberately asymmetric: a client MUST treat any present value as true unless it is explicitly false, 0, "0" or "false" (matched case-insensitively). The field exists only to mark words that are dangerous to steer input toward, so a server that encodes it sloppily may only fail in the safe direction.
Integer fields (priority) may arrive as strings; clients accept numeric strings, and values that parse to nothing valid fall back to the documented default.
Empty collections have the same difficulty as booleans, and for the same reason. A driver with no distinct empty-array form may render an empty categories, entries, aliases, add or remove as the empty string "" rather than as []; this is observable today in packages neighbouring this one on a reference implementation. A client MUST therefore treat an empty string where an array or object is expected as an empty collection, not as a malformed message; the distinction matters, because a client that rejects the message instead discards a valid Update and breaks its own chain. Servers SHOULD omit an empty optional collection entirely, and SHOULD omit a category with no entries rather than sending an empty one.
Messages
All vocabulary messages flow server to client. There are exactly two.
Client.Vocabulary.Catalog
- Package: Client.Vocabulary.Catalog
- Purpose: Deliver the complete vocabulary catalog for the connected character.
- Guidance: Sent once after negotiation, on reconnect, in answer to a re-request, and whenever a change cannot be expressed as an Update.
Syntax:
Client.Vocabulary.Catalog {
"version": "opaque-content-hash",
"categories": {
"commands": { "priority": 1, "entries": [
{ "word": "kill", "syntax": "kill %living", "aliases": ["attack"] },
{ "word": "tell", "syntax": "tell %player %text" },
{ "word": "get", "syntax": "get %item" },
{ "word": "mudlist", "priority": 3 },
{ "word": "quit", "priority": 3, "protected": true }
]},
"socials": { "priority": 2, "entries": [ { "word": "wave" }, { "word": "grin" } ] },
"directions": { "priority": 1, "entries": [ { "word": "north", "aliases": ["n"] } ] },
"channels": { "priority": 2, "entries": [ { "word": "gossip", "syntax": "gossip %text" } ] },
"nicknames": { "priority": 1, "entries": [
{ "word": "gc", "expansion": "guild chat", "position": "leading" } ] },
"helptopics": { "priority": 3, "entries": [ { "word": "combat" }, { "word": "tips and tricks" } ] }
}
}
Usage:
| Required | Key | Value | Purpose |
|---|---|---|---|
| Yes | "version" | <opaque string>
|
|
| Yes | "categories" | <object>
|
|
Each category object carries a priority (the default tier for every entry in it) and an entries array. Entry fields:
| Required | Key | Value | Purpose |
|---|---|---|---|
| Yes | "word" | <string>
|
|
| No | "priority" | 1 to 3 |
|
| No | "syntax" | <pattern>
|
|
| No | "aliases" | <array of strings>
|
|
| No | "expansion" | <string>
|
|
| No | "position" | "leading" or "argument" |
|
| No | "protected" | true or false |
|
Client.Vocabulary.Update
- Package: Client.Vocabulary.Update
- Purpose: Deliver an incremental change to a catalog already held by the client.
- Guidance: Sent only after a Catalog has been delivered in the same session, for changes the delta format can express.
Syntax:
Client.Vocabulary.Update {
"version": "opaque-hash-of-new-content",
"from": "opaque-hash-of-previous-content",
"categories": {
"commands": { "add": [ { "word": "shapeshift" } ], "remove": [ "meditate" ] },
"nicknames": { "add": [ { "word": "gg", "expansion": "guild greet", "position": "leading" } ] }
}
}
Processing rules, all of which are requirements:
- Chain check first.
fromnames the catalog version this Update applies on top of. A client MUST apply an Update only whenfromequals its current merged version; on mismatch it discards the Update and issues a single re-request. This converts a lost or misordered frame (otherwise a silent, permanent divergence) into detected, bounded recovery. - Removes before adds. All
removelists are applied before anyaddlist is consulted.removecarries bare words and removes every position of that word; surviving positions arrive inaddwithin the same Update. Example: a category holdingzz/argument andzz/leading, dropping the argument form, wires as"remove": ["zz"], "add": [{ "word": "zz", "position": "leading" }]; a client applying adds first would delete the survivor it was just given. - Add is replace. An
addentry carries the complete entry object, both for new words and for words whose content changed; the client replaces its entry wholesale. An Update whose only change to an entry is itspriorityis legitimate: tier budgets rebalance globally, so a small change elsewhere can demote an unrelated entry. - Structure never travels by delta. A category appearing, disappearing, or changing its default
priorityMUST NOT be expressed as an Update; the server sends a full Catalog instead. Clients may therefore cache category defaults from the last Catalog and treat them as immutable while applying Updates. - No baseline, no Update. An Update received before any Catalog in the current session has no baseline; the client discards it and issues a single re-request. (This is the chain check's degenerate case, stated explicitly.)
- After applying an Update, the client's merged state equals the Catalog the server would send at that moment, and
versionidentifies that state.
Messages at a glance
| Direction | Message | Purpose |
|---|---|---|
| Client → Server | Core.Supports.Set ["Client.Vocabulary 1"] |
opt in |
| Client → Server | Client.Vocabulary (bare, no payload) |
request a full Catalog (one-shot; no retry loops) |
| Server → Client | Client.Vocabulary.Catalog |
full snapshot of the character's vocabulary |
| Server → Client | Client.Vocabulary.Update |
incremental delta, chained by from
|
Priorities and Budgets
Speech recognition can be steered: a client may hand the engine a short list of words to expect, and a word on that list is much more likely to be heard correctly when the player says it. The list is small (most engines get worse if it runs much past a few hundred words), so it is a budget being spent, and the priority on each entry is how a server says where to spend it. This is the sense in which this document uses biasing throughout.
Priorities are a budget signal assigned by the server; the client trusts them.
| Tier | What a client may do with these words | What belongs here |
|---|---|---|
| 1 | Steer the recognizer toward them, so a player saying one is much more likely to be heard correctly. This is the scarce list; keep it small. | The words this character says constantly: movement, combat, the verbs of their guild. |
| 2 | The same, but only if the client has room left after tier 1. A client with no room treats these as tier 3. | Said regularly but not constantly: socials, channels. |
| 3 | Never steered toward, but still used to repair a near-miss (shapeshit → shapeshift) and to offer completions as the player types. | Everything else the character can say: rarely used commands, help topics, administrative verbs. |
Tier 3 is not a discard pile. Most of a game's vocabulary belongs there, and it is doing real work: repairing near-misses and completing what a player has started to type both need the full vocabulary, and neither costs the recognizer anything.
Servers SHOULD keep tier 1 at or under 300 entries across all categories combined and tier 2 at or under 500; tier 3 is uncapped. When a tier would overflow, the server demotes entries by per-entry override using a rule that is deterministic for identical content, never one driven by volatile state such as in-memory usage counters, which would destabilize the version hash between otherwise identical logins.
Priority is a budget signal and protected is a safety signal; neither may be inferred from the other. On the wire, a word demoted for danger and a word demoted by budget overflow look identical, which is exactly why the flag exists.
Tier 1 is a ceiling, not a target. It is shared with whatever a client binds from the game's live data (the creatures and objects in front of the character), and a client is expected to prefer those words when the two compete, so a server that fills tier 1 to the cap should expect the tail of it to go unused at the moment of speaking. A server that can distinguish the vocabulary a character uses constantly from the vocabulary they merely possess serves its clients better by leaving room than by claiming it.
Syntax Patterns
Slot classes are a small closed set:
| Class | Fills with |
|---|---|
%living |
a creature present, bindable from room-content data the client already has |
%item |
an object in inventory or the room |
%player |
a player name, bindable from who-list data |
%direction |
an exit or direction |
%word |
one argument drawn from the catalog itself, possibly a multi-word entry (help %word filled from helptopics, whose entries include names like tips and tricks); correctable and completable toward the categories that plausibly fill it
|
%text |
free prose; always the final slot in a pattern; a client MUST never correct, complete or bias any span it covers |
The %text distinction is not cosmetic. Message bodies (says, tells, whispers, every channel) are the player's own words, and a client that fuzzy-corrects them rewrites what the player actually said, in front of everyone reading. Any pattern whose tail is a player's own words must end in %text. Patterns with two slots put the correctable one first: tell %player %text keeps the name correctable and walls off the body, and slot boundaries come from the pattern, never from whitespace guessing.
Four of these classes (%living, %item, %player, %direction) are filled from the game's own live data rather than from this catalog, and that is deliberate: the things a character can reach change with every step, and carrying them here would churn the version hash continuously and defeat the caching the rest of this protocol depends on. This protocol therefore describes no schema, path or package name for that data. Games encode it too differently for a description to survive contact (one reference implementation distinguishes a creature from a wearable object by a single-character attribute whose legend lives in the server's own source), so a client binds these slots through a small adapter per GMCP dialect, and the vocabulary consumer behind that adapter stays game-neutral. See Guidance for Client Implementors.
Those four classes therefore depend on the game publishing that data at all, which this protocol does not require and cannot check. A game may conform completely and publish nothing a client can bind them from, in which case they are unfillable, and a client applies no slot correction to the patterns that use them, exactly as it would for a %class it does not recognize. The entry's word itself is unaffected: it is still biasable, correctable and completable on its own terms. A server writing kill %living should know that the slot does nothing for a client it has given nothing to fill it with.
There is deliberately no grammar language beyond these six classes. Implementors are urged to resist per-category slots (%topic, %channel, %social): that road ends in a grammar wearing a disguise, and %word already carries "one argument from the catalog" for all of them. A client that meets a %class it does not recognize MUST treat the whole pattern as unparseable and apply no slot correction to that entry, which makes any future addition a non-event for older clients.
The protected Flag
protected marks words the server judges dangerous to steer input toward: leaving play, and choices a character cannot take back. A protected word:
- is never one of the words a client steers the recognizer toward, at any tier;
- is never a fuzzy-correction target;
- may still be offered by completion, and an exact match (spoken or typed) always passes through untouched. The flag governs what the client injects, never what the player said. A player who says "quit" quits.
Servers SHOULD also hold protected words out of tier 1, since a word that can never be biased has no use for a scarce biasing slot, and SHOULD apply the flag uniformly across every command source. The protected set is server-defined and expected to grow; clients MUST read the flag rather than mirror or hardcode any word list, so the two can never drift.
Security and Privacy
- The one-directional principle is absolute: no conforming implementation sends recognition, completion or correction data to any server, and no extension may add such a channel. Client-side caches of the catalog are local to the client. A catalog describes one character and carries that character's own shortcuts, so a client MUST NOT serve a cached catalog to any character other than the one it was built for. This protocol carries no character identifier; a client obtains one out of band, the way it binds the dynamic slot classes, and a client that cannot identify the character MUST NOT persist the catalog at all. Keying a cache to the connection profile alone does not satisfy this, since one profile may be used by several characters.
- A player's own shortcuts (
nicknames) and their expansions are sent only to that player's client. Catalogs are built per character; command sets gated by privilege are omitted for characters without the privilege. - The catalog is data, not code. It contains no executable content and no patterns intended for client-side matching against game output; clients MUST NOT execute or evaluate any part of it.
- A client connects to arbitrary servers of the player's choosing, so catalogs must be safe to receive from a hostile peer: clients SHOULD enforce their own size bounds when merging. Those bounds SHOULD sit well above what a conforming server sends, so that they turn away a hostile catalog without truncating an honest one, and a client that does discard entries to stay inside them SHOULD say so rather than silently serving a partial vocabulary. A client MAY also apply its own judgment before biasing toward words they consider destructive regardless of flags;
protectedis a cooperative signal, not a trusted one. - Words a client binds from dynamic context are words the server has already sent it for other purposes; binding them confers no new access and creates no new channel. They are held for the session only and are never written to a cache keyed by catalog version, which describes published vocabulary alone.
Size and Pagination
A full catalog for a typical character is on the order of a thousand entries and a few tens of kilobytes of JSON, which fits comfortably in a single GMCP frame. A server with a much larger vocabulary may split a Catalog by category across several frames sharing the same version; clients should merge categories from same-version Catalog frames additively.
Guidance for Server Implementors
- Build from the live sources of truth: command tables, social daemons, channel permission checks, the player's own shortcut storage, never documentation files, which drift from code.
- Keep the version hash stable. Compute it over a canonical rendering (categories sorted, entries sorted, fields in fixed order), never over serialized JSON text, and never from volatile state.
- Throttle rebuilds. Catalog construction is typically the most expensive request a client can trigger; a minimum interval between rebuilds, answering interim requests from the held copy, protects the game loop. Ensure the held copy reflects every Update already emitted, so a served Catalog is never older than the latest delta.
- Push what you can; document what you don't. Hook the changes you can observe (shortcut edits, ability grants) to emit Updates, and say plainly which changes are only reflected on the next Catalog. Clients are required to tolerate staleness for unpushed changes; they are not required to guess which changes those are.
- Say where a word is valid when it is not valid everywhere.
positionomitted means valid anywhere, which is the permissive reading rather than the cautious one: a social or a command verb published without it stays a correction candidate in the middle of a line, where a client may steer an ordinary word toward it. Mark words that are only ever a line's first word"position": "leading". This matters most for large categories of short, ordinary-looking words - socials especially, which are frequently near-homophones of English ones. - Spend tier 1 on the verbs a character actually says. A two-letter administrative command occupies the same scarce slot as a spell the character casts constantly, and costs more than it returns: a client that biases toward a short word will hear it in place of the longer word it abbreviates. Demote what is rarely spoken and what is barely a word.
- Provide an offline inspection command that prints the catalog any character would receive, tier counts against the budgets, and a version-stability check, and a regression assertion that a category-default change yields no delta. That last property earns a permanent test because its failure has no symptom.
- Publish what fills the slots you write.
%living,%item,%playerand%directionare bound from the game's live data, not from the catalog, so a pattern using one is inert unless the game already sends room contents, inventory or a who-list in some package the client can read. Whichever packages those are is outside this protocol; that they exist is what makes those slots worth writing.
Guidance for Client Implementors
- Cache the merged catalog keyed by
version, and treat the cache as a head start rather than a substitute for parsing. The first Catalog of a session is complete and authoritative, so it replaces cached state outright even when the two versions match; additive merging applies only to the same-version frames that follow it within that session, which are pagination. A client that instead merges an arriving Catalog into a restored cache keeps any category the server has since dropped, and nothing can ever retract it. - A full Catalog is authoritative by construction: apply it. A client cannot order opaque versions, so a Catalog bearing a version the client held earlier is indistinguishable from one whose content has returned to an earlier state — which a content hash makes not merely possible but expected, as a player creating and then deleting a shortcut demonstrates. A client therefore MUST NOT discard a Catalog on the grounds that it has seen its version before. It MAY note the recurrence, but MUST NOT answer it with a re-request that the answer would arm again, which is the retry loop forbidden under Negotiation and Lifecycle. A served Catalog being no older than the latest delta is a server obligation; see the held-copy requirement under Guidance for Server Implementors.
- Bind slot classes to whatever room, inventory and player data the connected game already publishes; those packages vary by game, so an adapter layer per GMCP dialect keeps the vocabulary consumer itself game-neutral. Sample dynamic context when input capture begins (a push-to-talk press, a completion request) rather than chasing every state update.
- Spend the biasing budget on what is about to be said. Dynamic context and the catalog compete for one small allowance, and they are not equally valuable: the thing standing in the room is a word the player is about to speak, while much of the catalog is vocabulary the recognizer already handles. Fill the list from context first and let the catalog take what remains. Measured with both sources in one list, the words rescued most often were the game's short command verbs (catalog words), so the two sources are complements rather than competitors: context supplies the nouns that change with every room, the catalog the verbs said in every one. Biasing also reaches errors that client-side correction cannot: a word heard as a non-word two edits away is outside any conservative correction budget, and only the recognizer itself can prevent it.
- Reduce a game's display names to the words a player speaks. Live data names things for reading, not for saying: an object presented as "A bottle of beer" is spoken as "get beer" or "get bottle", so bind the content words and drop the articles. Deduplicate what remains: a single room may hold thirty copies of one object, and thirty copies of one word exhaust an allowance while adding nothing.
- Leave the shortest words out of biasing. One- and two-letter forms are abbreviations, and steering a recognizer toward them risks it preferring the abbreviation to the word it abbreviates. Correction and completion may still use them.
- Where a game's live data does not let a client tell a creature from an object, bias toward the union of both (biasing needs no classification), and withhold only the slot-specific correction that does, per the fail-safe principle.
- Read the pattern before correcting anything after the verb. The
%textrule under Syntax Patterns is a MUST, and it is the one a client is most likely to miss, because a client that ignoressyntaxaltogether looks conformant until someone speaks on a channel: correction by token position alone will happily rewrite a word inside a message body toward a social or a command that resembles it. Once the leading word is known, its pattern says where the player's own words begin. A verb carrying no pattern yields no boundary - correction there is a matter forposition, below. - Honor
positionin correction and completion: leading-only words are candidates at the start of a line, argument-only words after it. A game's parser may enforce this split; correcting a leading word toward an argument-only shortcut manufactures a line the game cannot expand. - Merged state may lag changes the server does not push (privilege changes, new help topics) until the next Catalog; treat this as an accepted property, not a defect.
Versions
- Version 1.0: Initial specification by Mike Conley (mike.conley[at]stickmud.com) on 15-AUG-2026.
- Version 1.0.1: Clarifications drawn from the first server and client implementations, on 19-AUG-2026. Empty collections are documented alongside the other driver encoding quirks; the durability of the catalog is stated as a design principle, and the dynamic slot classes say plainly why this protocol describes no schema for the data that fills them; guidance is added on how a shared biasing budget should be spent. No change to the wire format: a version 1 client and a version 1 server interoperate exactly as before.
- Version 1.0.2: Editorial and corrective, on 22-AUG-2026. Priorities and Budgets is rewritten after review feedback that its table assumed the reader already knew what speech-recognition biasing is: the term is now explained in plain language where it is first needed, the tier table says what a client does with each tier and what belongs there, and tier 3 is stated to be where most of a game's vocabulary belongs rather than a discard pile. The biasing measurements cited in 1.0.1 are withdrawn. They were taken through a client whose engine integration compiled the biasing words in but applied them with a weight of zero, so both conditions ran effectively unbiased and the reported differences were noise. Valid measurement: StickMUD against the Mudlet consumer package, two sessions, each speaking a fixed phrase list under both conditions at matched input levels with the order of the two drawn at random. The first, 24 paired trials, improved exact recognition from 63% to 79% and cut losses of the leading command verb from 5 to 2, with 7 phrase-instances improved and 3 made worse. The second, 12 paired trials against game-specific targets, improved it from 50% to 92% with 5 improved and none made worse. Neither aggregate is large enough to be decisive by itself; what the two agree on is the direction, and the mechanism is visible in both. Biasing repairs the errors a general recognizer makes on a game's own words: a compound name was split into two ordinary words on every unbiased attempt, and a proper noun came back as a non-word, neither of which client-side correction can repair, both being more than one edit from the target. The effect's size is not pinned, and it is not uniformly free: one session saw two phrases get worse under biasing, the other saw none. The larger gain came where the target words were least like ordinary English, which is a further argument for spending the budget on what is in reach before what is in the catalog. Rationale and guidance text are corrected accordingly. The budgets, tiers and context-first guidance are unchanged, and there is no change to the wire format.
- Version 1.0.3: Illustrative diagrams added, on 03-SEP-2026. Three figures: the negotiation lifecycle, the Update chain with a lost frame and its recovery, and the order in which the biasing budget is filled. Each is marked illustrative in its caption and names the section that governs it; every rule they depict is stated in the text, so a reader who cannot see them loses nothing. No change to the wire format.
- Version 1.0.4: Corrective, drawn from an adversarial review of the reference client, on 05-SEP-2026. Two client rules are replaced because they could not be implemented as written. The defensive version-regression rule asked a client to detect a Catalog "older than the current merged state", which an opaque version forbids: the only available proxy is "seen before and not current", and because the version is stable for identical content, a catalog whose content has reverted reproduces it legitimately. A client following the rule rejected the server's current catalog and re-requested it on every recurrence, which is the retry loop this document forbids elsewhere. A full Catalog is now stated to be authoritative. The caching guidance that an arriving Catalog matching a cached version "needs no re-parse" is likewise replaced: it contradicts pagination, where same-version frames must be merged additively, and merging an arriving Catalog into a restored cache leaves categories the server has dropped alive with nothing able to retract them. Four clarifications:
categoriesjoins the fields an empty collection may arrive as the empty string; entry identity is stated to survive an unrecognizedposition; the client size-bound SHOULD gains a floor, so conformance cannot mean silently truncating an honest catalog; and a cached catalog is forbidden from reaching a character other than the one it was built for, since a catalog carries that character's own shortcuts. Because this protocol carries no character identifier, that last rule is written as this document's other client rules now are - satisfiable with what the protocol provides: a client that cannot identify the character does not persist the catalog, rather than being asked to key a cache by something it has no way to learn. Thewordfield's "lowercase ASCII" is marked as a description of what servers send rather than an assumption clients may make of a hostile peer. No change to the wire format: a version 1 client and a version 1 server interoperate exactly as before. - Version 1.0.5: Editorial, drawn from a defect found in live play against the reference server, on 07-SEP-2026. No change to the wire format, to any field, or to any normative rule. A speech client corrected "wiz say hello" to "wiz say hallo" - hallo being a social on that game and hello being absent from its catalog - and so rewrote what the player had said, on a channel, in front of everyone reading it. Both halves of the answer were already here and already in use: the server was publishing
wiz %text, and%texthas always carried a MUST against correcting the span it covers. The client had never readsyntaxat all, correcting by token position alone, and nothing in Guidance for Client Implementors pointed at the obligation - it was reachable only from Syntax Patterns, which a client author who has decided not to implement patterns has no reason to read closely. That guidance now carries the rule, stated as the one most easily missed and with the symptom named, since a client ignoringsyntaxlooks conformant until somebody speaks on a channel. Guidance for Server Implementors gains the matching note:positionomitted means valid anywhere, which is the permissive reading, so a category of short ordinary-looking words - socials above all - should say"position": "leading"rather than rely on it. The reference server was already conformant on both counts for its channels and message commands; the defect was entirely in the client.
Advertise Support for MCVP
Consider advertising your game's support of MCVP (and GMCP) to mud lists via the Mud Server Status Protocol, MSSP, by providing a MSSP variable of "MCVP" and value of "1".
Authors
- Mike Conley (Tamarindo, Administrator at StickMUD and Mudlet contributor) mike.conley[at]stickmud.com
Implementations
- Servers implementing this specification:
- StickMUD: reference server implementation.
- Clients implementing this specification:
- Mudlet: MudletMCVP consumer package, feeding speech-to-text biasing and correction (with MudletSTT). The player-facing result is documented at Manual:Speech to Text.