GMCP with open-source tools
This guide outlines the technical implementation of the Generic MUD Communication Protocol (GMCP), also known as Telnet Option 201. GMCP allows servers to send structured JSON data to modern clients like Mudlet or BeipMU, enabling features like graphical health bars, auto-mapping, and inventory windows without interfering with the human-readable text stream.

Initiate Telnet Option 201 Negotiation
The server must signal its intent to use GMCP during the initial Telnet handshake. Send the 'IAC WILL GMCP' sequence (255 251 201). The server should only send GMCP data if it receives 'IAC DO GMCP' (255 253 201) from the client. Store a boolean flag on the user's connection object to track this state.
#define TELOPT_GMCP 201
const unsigned char gmcp_will[] = { 255, 251, 201 };
write_to_descriptor(d, (const char*)gmcp_will, 3);⚠ Common Pitfalls
- •Sending GMCP payloads to clients that have not sent DO GMCP can cause 'garbage' characters to appear in their terminal.
- •Failure to handle WONT GMCP may lead to infinite negotiation loops.
Implement the Subnegotiation Parser
Incoming GMCP data is wrapped in a subnegotiation block: IAC SB 201 [Payload] IAC SE. The payload follows the format 'Package.Message [JSON]'. Your parser must identify the package name, the message, and then hand the remaining string to a JSON decoder.
def handle_gmcp_input(data):
# data is the content between IAC SB 201 and IAC SE
parts = data.split(' ', 1)
cmd = parts[0]
payload = json.loads(parts[1]) if len(parts) > 1 else {}
return cmd, payload⚠ Common Pitfalls
- •Some clients send the package name and message without a space before the JSON object.
- •Buffer overflows if the incoming GMCP string exceeds expected length limits.
Establish Core Handshake
Once negotiated, the client usually sends 'Core.Hello'. The server should respond with its own 'Core.Hello' and use 'Core.Supports.Set' to define which packages it intends to send (e.g., Char 1, Room 1, Comm 1). This prevents the client from being overwhelmed by unused data.
Core.Supports.Set [ "Char 1", "Room 1", "Comm 1" ]Send Character Vitals (Char.Vitals)
Implement a function to broadcast character stats whenever they change. To optimize bandwidth, only send updates when values actually change or at a fixed heartbeat interval. Common keys include 'hp', 'maxhp', 'mp', 'maxmp', and 'stamina'.
/* Send IAC SB 201 Char.Vitals { "hp": 100, "maxhp": 120 } IAC SE */
void send_gmcp_vitals(CHAR_DATA *ch) {
char buf[MAX_STRING_LENGTH];
sprintf(buf, "Char.Vitals { \"hp\": %d, \"maxhp\": %d }", ch->hit, ch->max_hit);
write_to_gmcp(ch->desc, buf);
}⚠ Common Pitfalls
- •Sending vitals every single pulse can cause lag on high-latency connections.
- •Forgetting to escape quotes in the JSON string if not using a library.
Implement Room.Info for Mapping
To support Mudlet's auto-mapper, send 'Room.Info' whenever a player moves. This should include a unique numeric 'num', the room 'name', 'area', and 'exits'. This allows the client to build a visual representation of the world.
Room.Info {
"num": 1205,
"name": "The Town Square",
"area": "Midgaard",
"exits": { "n": 1206, "s": 1204, "w": 1200 }
}⚠ Common Pitfalls
- •Using non-unique IDs for rooms will break client-side mapping.
- •Inconsistent exit naming (e.g., 'north' vs 'n') can confuse client scripts.
Validate and Debug with Client Consoles
Use the Mudlet 'errors' and 'debug' views to verify incoming payloads. In Mudlet, you can run 'lua display(gmcp)' in the command line to see the current state of all received data. Ensure that the JSON is well-formed and that no telnet control codes are leaking into the visible text buffer.
⚠ Common Pitfalls
- •JSON syntax errors (like trailing commas) will cause modern clients to silently discard the entire packet.
- •Hidden characters in the byte stream can cause the Telnet state machine to hang.
What you built
Successful GMCP implementation moves your MUD from a pure text interface to a rich, data-driven experience. Start with Char.Vitals and Room.Info, as these provide the most immediate value to players using modern clients. Always prioritize using a robust JSON library over manual string concatenation to ensure protocol stability.