MMCP Specification
MUD Master Chat Protocol (MMCP)
Sources:
- Original specification mirrored at MUSHclient.com
- More recent extensions at Mudhalla
MMCP is a decentralized chat protocol that allows MUD clients to communicate with each other over a TCP/IP connection.
Terminology
A peer is any remote MUD client with which a direct MMCP TCP/IP connection has been established.
The caller is the side that initiates the TCP/IP connection; the answerer is the side that accepts it. An “incoming connection” means the remote peer is the caller. An “outgoing connection” means the remote peer is the answerer.
The sender is the peer that transmits a particular command or message; the receiver is the peer that receives and processes that command or message.
A Command Byte is a single byte value (0-255).
An End of Command (CHAT_END_OF_COMMAND) is a single byte value of 255.
Establishing a Connection
Caller
When a TCP/IP connection is established, the caller begins a handshake sequence with the answerer by sending an initial string with the following format:
CHAT:<chat name>\n<ip address><port>
This consists of the literal string “CHAT:”, followed by the chat name, a line feed (\n), and the caller’s provided IPv4 address immediately followed by the caller’s provided port number.
The provided IPv4 address and port number are concatenated without a separator and can differ from the caller’s real IPv4 address and port number. The IPv4 address is a variable length, dotted-decimal string or the literal string <Unknown>. The port number is a fixed width, left-aligned value which occupies the final 5 characters of the handshake string, padded with trailing spaces if necessary. When parsing the initial string, the answerer MUST obtain the port number by reading the final 5 characters of the string and trimming any trailing spaces.
The sprintf format is: CHAT:%s\n%s%-5u
After sending this string, the caller waits for a response. The answerer replies with:
NO(without a newline character) to reject the connection, orYES:<chat name>\nto accept it.
The caller then optionally sends its client version using a CHAT_VERSION command (described below). Version exchange is not required during handshake and may occur at any time.
Answerer
Upon detecting an incoming connection, the answerer accepts the socket and waits for the CHAT: string. To reject the connection, it sends NO (without a newline character). To accept, it sends YES:<chat name>\n.
The answerer then optionally sends its own client version using a CHAT_VERSION command.
Chat Data Blocks
A standard chat data block has the format:
<COMMAND BYTE><data><END OF COMMAND>
Command boundaries are determined solely by the CHAT_END_OF_COMMAND byte. All commands follow this structure except the initial handshake and CHAT_FILE_BLOCK data blocks, which omit the CHAT_END_OF_COMMAND byte.
Command Byte Values
In the following table, Portable indicates commands intended to function across multiple client implementations.
| Command Name | Byte Value | Notes |
|---|---|---|
| CHAT_NAME_CHANGE | 1 | Portable; ALL clients SHOULD support. |
| CHAT_REQUEST_CONNECTIONS | 2 | Portable; ALL clients SHOULD support. |
| CHAT_CONNECTION_LIST | 3 | Portable; ALL clients SHOULD support. |
| CHAT_TEXT_EVERYBODY | 4 | Portable; ALL clients SHOULD support. |
| CHAT_TEXT_PERSONAL | 5 | Portable; ALL clients SHOULD support. |
| CHAT_TEXT_GROUP | 6 | Portable; ALL clients SHOULD support. |
| CHAT_MESSAGE | 7 | Portable; ALL clients SHOULD support. |
| CHAT_DO_NOT_DISTURB | 8 | Portable; most clients LACK support. |
| CHAT_SEND_ACTION | 9 | NOT portable; used by Mud Master. |
| CHAT_SEND_ALIAS | 10 | NOT portable; used by Mud Master. |
| CHAT_SEND_MACRO | 11 | NOT portable; used by Mud Master. |
| CHAT_SEND_VARIABLE | 12 | NOT portable; used by Mud Master. |
| CHAT_SEND_EVENT | 13 | NOT portable; used by Mud Master. |
| CHAT_SEND_GAG | 14 | NOT portable; used by Mud Master. |
| CHAT_SEND_HIGHLIGHT | 15 | NOT portable; used by Mud Master. |
| CHAT_SEND_LIST | 16 | NOT portable; used by Mud Master. |
| CHAT_SEND_ARRAY | 17 | NOT portable; used by Mud Master. |
| CHAT_SEND_BARITEM | 18 | NOT portable; used by Mud Master. |
| CHAT_VERSION | 19 | Portable; ALL clients SHOULD support. |
| CHAT_FILE_START | 20 | Portable; ALL clients SHOULD support. |
| CHAT_FILE_DENY | 21 | Portable; ALL clients SHOULD support. |
| CHAT_FILE_BLOCK_REQUEST | 22 | Portable; SOME clients MAY support. |
| CHAT_FILE_BLOCK | 23 | Portable; SOME clients MAY support. |
| CHAT_FILE_END | 24 | Portable; SOME clients MAY support. |
| CHAT_FILE_CANCEL | 25 | Portable; SOME clients MAY support. |
| CHAT_PING_REQUEST | 26 | Portable; ALL clients SHOULD support. |
| CHAT_PING_RESPONSE | 27 | Portable; ALL clients SHOULD support. |
| CHAT_PEEK_CONNECTIONS | 28 | Portable; ALL clients SHOULD support. |
| CHAT_PEEK_LIST | 29 | Portable; ALL clients SHOULD support. |
| CHAT_SNOOP_START | 30 | Portable; SOME clients MAY support. |
| CHAT_SNOOP_DATA | 31 | Portable; SOME clients MAY support. |
| CHAT_SNOOP_COLOR | 32 | NOT portable; used by Mud Master. |
| CHAT_SEND_SUBSTITUTE | 33 | NOT portable; used by Mud Master. |
| CHAT_SIDE_CHANNEL | 40 | Mudlet-specific; REQUIRES Mudlet. |
| CHAT_CHANNEL_DATA | 240 | Mudlet-specific; REQUIRES Mudlet. |
| CHAT_END_OF_COMMAND | 255 | Terminates all MMCP commands. |
Note: Commands 8-18 (CHAT_DO_NOT_DISTURB through CHAT_SEND_BARITEM) are used to share MUD client objects (actions, aliases, macros, variables, events, gags, highlights, lists, arrays, and bar items). Support is optional and varies by client.
MMCP Commands
CHAT_NAME_CHANGE (1)
Sender
Sent when changing the local chat name.
Format: <CHAT_NAME_CHANGE><new name><CHAT_END_OF_COMMAND>
sprintf: %c%s%c
The new name must be broadcast to all existing connections.
Receiver
Replace the stored name for this peer with the new name.
CHAT_REQUEST_CONNECTIONS (2)
Sender
Requests the receiver’s public connections for automatic outgoing connections.
Format: <CHAT_REQUEST_CONNECTIONS><CHAT_END_OF_COMMAND>
sprintf: %c%c
Receiver
Respond with a CHAT_CONNECTION_LIST containing all public connections.
CHAT_CONNECTION_LIST (3)
Sender
Sends a comma-delimited list of public connections (IP address and port pairs).
Format: <CHAT_CONNECTION_LIST><IP/port list><CHAT_END_OF_COMMAND>
sprintf: %c%s%c
List format: <ip1>,<port1>,<ip2>,<port2>,...
- Ports are not padded.
- List must never end with a comma.
- Unknown IPs are represented as
<Unknown>(case-sensitive, including the angle brackets). - If no public connections exist, send an empty list.
Example: 28.25.102.48,4050,100.284.27.65,4000,<Unknown>,4050
Receiver
Store the list and attempt outbound connections to all valid entries.
CHAT_TEXT_EVERYBODY (4)
Sender
Broadcast message to all peers.
Format: <CHAT_TEXT_EVERYBODY><text><CHAT_END_OF_COMMAND>
sprintf: %c%s%c
The sender generates the complete display text, including newlines and prefix.
Example: \n%s chats to everybody, '%s'\n
Receiver
- If the message arrives on an incoming connection (i.e. the remote peer is a caller): relay to ALL other connections (both incoming and outgoing) except the original sender.
- If the message arrives on an outgoing connection (i.e. the remote peer is an answerer): relay ONLY to incoming connections (callers being served).
A relayed message must never be sent back to the connection from which it was received.
The caller/answerer distinction enables “serving” mode so peers behind NAT/firewalls can still receive broadcasts.
CHAT_TEXT_PERSONAL (5)
Sender
Private message to one peer.
Format: <CHAT_TEXT_PERSONAL><text><CHAT_END_OF_COMMAND>
sprintf: %c%s%c
The sender generates the complete display text, including newlines and prefix.
Example: \n%s chats to you, '%s'\n
Receiver
If the sender is not being ignored, display the text.
CHAT_TEXT_GROUP (6)
Sender
Message to a named group.
Format: <CHAT_TEXT_GROUP><group name><text><CHAT_END_OF_COMMAND>
The group name field is exactly 15 bytes, left-aligned, and padded with trailing spaces if necessary. When parsing the group name, the receiver MUST read exactly 15 bytes for the group name and trim any trailing spaces. The remainder of the payload is the message text.
sprintf: %c%-15s%s%c
The sender generates the complete display text, including newlines and prefix.
Example: \n%s chats to the group, '%s'\n
Receiver
- If the message arrives on an incoming connection (i.e. the remote peer is a caller): Do NOT relay.
- If the message arrives on an outgoing connection (i.e. the remote peer is an answerer): relay ONLY to incoming connections (callers being served). This is subject to local group filtering (see Note below).
A relayed message must never be sent back to the connection from which it was received.
Note (Implementation-specific behavior in MUSHclient)
MUSHclient loops through its connections and only sends the message to the connections the user manually tagged with that group name (case-insensitive), regardless of whether the user composed the group chat message or is simply relaying it. Group membership in MUSHclient is therefore asymmetric. Alice may have Bob tagged as “Warriors” while Bob has Alice tagged as “Friends”. If this happens, Alice sending a group chat message to “Friends” or Bob sending a group chat message to “Warriors” will result in the message failing to send.
CHAT_MESSAGE (7)
Sender
System/notification message.
Format: <CHAT_MESSAGE><message><CHAT_END_OF_COMMAND>
sprintf: %c%s%c
The sender generates the complete display text, including newlines and prefix.
Example: \n<CHAT> %s has refused your connection because your name is too long.\n
Receiver
Display the message.
CHAT_VERSION (19)
Sender
Sends client name and version string.
Format: <CHAT_VERSION><version string><CHAT_END_OF_COMMAND>
sprintf: %c%s%c
CHAT_PING_REQUEST (26)
Sender
Format: <CHAT_PING_REQUEST><timing data><CHAT_END_OF_COMMAND>
sprintf: %c%s%c
Receiver
Echo the timing data unchanged in CHAT_PING_RESPONSE.
CHAT_PING_RESPONSE (27)
Sender
Returns timing data from the request.
Format: <CHAT_PING_RESPONSE><timing data><CHAT_END_OF_COMMAND>
sprintf: %c%s%c
Receiver
Calculate round-trip time.
CHAT_PEEK_CONNECTIONS (28)
Sender
Requests public connection list for viewing only.
Format: <CHAT_PEEK_CONNECTIONS><CHAT_END_OF_COMMAND>
sprintf: %c%c
Receiver
Respond with CHAT_PEEK_LIST.
CHAT_PEEK_LIST (29)
Sender
Tilde-delimited list of public connections (IP, port, name triples).
Format: <CHAT_PEEK_LIST><list><CHAT_END_OF_COMMAND>
sprintf: %c%s%c
The list always ends with a single tilde unless it is empty.
If no public connections exist, send an empty list.
Example: 204.285.28.18~4050~Omawarisan~<Unknown>~4050~Baalzebul~
Receiver
Display the list; do not attempt connections.
CHAT_SNOOP_START (30)
Sender
Requests start/stop of snooping (single command; acts as a toggle, receiver decides whether to honor it).
Format: <CHAT_SNOOP_START><CHAT_END_OF_COMMAND>
sprintf: %c%c
Receiver
Decide whether to honor the request.
CHAT_SNOOP_DATA (31)
Sender
Sends MUD output to snoop recipient.
Format: <CHAT_SNOOP_DATA><message><CHAT_END_OF_COMMAND>
sprintf: %c%s%c
Receiver
Display the message. Never forward snoop data to avoid loops.
File Transfer Commands
CHAT_FILE_START (20)
Sender
Initiates file transfer.
Format: <CHAT_FILE_START><filename>,<length><CHAT_END_OF_COMMAND>
sprintf: %c%s,%lu%c
The filename must be the base name only; the length is the file size in bytes.
Receiver
Validate sender, filename, and length. Reject with CHAT_FILE_DENY if unacceptable. Otherwise accept and immediately request the first block with CHAT_FILE_BLOCK_REQUEST.
CHAT_FILE_DENY (21)
Sender
Rejects an incoming file transfer.
Format: <CHAT_FILE_DENY><reason><CHAT_END_OF_COMMAND>
sprintf: %c%s%c
Receiver
Display the reason and clean up any partial files.
CHAT_FILE_BLOCK_REQUEST (22)
Sender
Requests next block (receiver-driven transfer).
Format: <CHAT_FILE_BLOCK_REQUEST><CHAT_END_OF_COMMAND>
sprintf: %c%c
Receiver
Send next 500-byte block via CHAT_FILE_BLOCK. On EOF, send CHAT_FILE_END instead.
CHAT_FILE_BLOCK (23)
Sender
Fixed 500-byte data block (may be shorter on final block).
Format: <CHAT_FILE_BLOCK><raw bytes> (no END byte)
Receiver
Write data, then request next block with CHAT_FILE_BLOCK_REQUEST. Track total length from CHAT_FILE_START to determine final block.
CHAT_FILE_END (24)
Sender
Signals completion of transfer.
Format: <CHAT_FILE_END><CHAT_END_OF_COMMAND>
sprintf: %c%c
Receiver
Close the file.
CHAT_FILE_CANCEL (25)
Sender
Aborts an in-progress transfer (either side).
Format: <CHAT_FILE_CANCEL><CHAT_END_OF_COMMAND>
sprintf: %c%c
Implementation Notes
Character Set
The protocol does not mandate a character encoding. Historically, most implementations used ASCII or system-local 8-bit encodings. The original Mud Master client restricted chat names to printable ASCII characters 32-122 inclusive, excluding { | } ~. Other clients may permit additional characters.
String Sanitization Recommendations
- Remove or replace 0xFF bytes in all user-controlled strings (except CHAT_FILE_BLOCK raw data) to prevent protocol stream corruption due to malformed commands.
- Strip ANSI/VT100 terminal escape sequences from user input to avoid terminal interference.
- Remove line feeds (
\n) from chat names and group names. - Remove or replace tildes (
~) in chat names to avoid delimiter collision and preserve CHAT_PEEK_LIST parsing.
Chat Name Length
Original Mud Master allowed <= 30 characters; TinTin++ enforces <= 20. Use the stricter limit of 20 for maximum compatibility.
Connection List vs. Peek List Formatting
Connection lists use commas as separators and never end with a comma. Peek lists use tildes and always terminate with one tilde.
IPv6 Support
The protocol does not support IPv6. All addresses exchanged in the handshake and connection lists use IPv4 dotted-decimal format or the literal string <Unknown>. Provided addresses may differ from the actual socket addresses due to firewall or NAT handling.
Message Loops
The protocol lacks built-in loop detection. Complex topologies can cause infinite relaying. MUSHclient mitigates this by discarding recently sent messages for a short period (currently 5 seconds). Relay-only implementations should replace real IP addresses with <Unknown> and apply rate limiting.