Orz Discord Relay Ws
FreeNot checkedMinimal Discord Gateway relay for Claude Code and any MCP client using raw WebSocket and MCP-native notifications.
About
Minimal Discord Gateway relay for Claude Code and any MCP client using raw WebSocket and MCP-native notifications.
README
Minimal Discord Gateway relay for Claude Code (and any MCP client). Raw WebSocket, no discord.js, MCP-native. Zod-typed access.json schema (v0.2.0).
Answers nazt's Oracle School directives:
- 2026-07-09 07:54 UTC (v0.1.0): "everyone write your own version, prove it, open source it, blog it"
- 2026-07-09 11:50 UTC (v0.2.0): "write your own?" — after nh-oracle's Host-vs-Hermes side-by-side of
auto_thread. Answer: fully-typed access.json (Zod schema + inferred TS type), fail-loud on malformed config (matches Hermes hard-fail discipline nh flagged as the deeper lesson).
Design lineage
- Bo's
discord-relay-ws.ts(private, ~1044–1348 LOC) — raw WS Gateway +spawnSync("maw hey", ...)shell-out to any CLI agent (grok, gemini, claude, ...) - Full Discord plugin (
anthropics/claude-plugins-official/external_plugins/discord/, 900 LOC) —discord.js+ MCP stdio +access.json+ pairing + skills - Orz's
minimal-channel-mqtt(~230 LOC, 2026-07-09) — MQTT broker → MCP notification - This — raw WS (like Bo) + MCP notification (like the full plugin + like
mqtt-channel-min)
Same notifications/claude/channel contract as all three peers above. A Claude Code session subscribed to this bridge can't tell from the notification whether the transport was Discord Gateway, MQTT, or a Bo-style shell-out layer — that's the point.
What's in / out
In:
- Raw
wss://gateway.discord.gg/?v=10&encoding=json(no dependency ondiscord.jsorws) - Op 10 Hello → heartbeat + Op 2 Identify (
intents=37377, peer-cited from Bo's file via No.1) - Op 0
MESSAGE_CREATE→shouldRelay()gate → MCP notification access.jsoncompatible with Discord plugin's file (hot-reload per message)- REST tools:
reply/react/edit_message assertSendable()outbound file gate (matches full Discord +mqtt-channel-min)- Optional shell-out via
MAW_HEY_AGENTenv →spawnSync("maw hey", <target>, formatted)alongside the MCP notification (federation mode, matches Bo's pattern) READ_ONLY_REST=1mode: skip Gateway entirely, do a REST catchup. Use this when another process already owns the bot's Gateway slot (Discord allows only one Identify per token).
Deliberately out:
- Pairing lifecycle (drop
pending/checkApprovals/randomBytes(3)— matchesmqtt-channel-min— layer on top if needed) - Permission-relay
.../permissionintercept (Discord plugin-specific UX) ackReaction,replyToMode,textChunkLimit,chunkModefetch_messagestool (REST catchup covers the common case)download_attachmenttool (attachments are listed in meta; if needed, port from mqtt-channel-min or full plugin — kept out for line-count parity withmqtt-channel-min)
Setup
bun install
export DISCORD_BOT_TOKEN='<your bot token>'
export AGENT_NAME='orz' # state dir suffix
export DISCORD_STATE_DIR="$HOME/.claude/channels/discord-orz"
bun relay.ts
Optional:
export MAW_HEY_AGENT='06-gemini' # also spawnSync maw hey to CLI agent
export READ_ONLY_REST=1 # skip Gateway; REST catchup only
export REST_CATCHUP_CHANNEL='<channel_id>' # channel to catchup on (with WS or REST-only)
export REST_CATCHUP_LIMIT=10
access.json
Shares the format used by anthropics/claude-plugins-official/external_plugins/discord/:
{
"version": 1,
"dmPolicy": "allowlist",
"allowFrom": ["<discord user id>"],
"groups": {
"<channel_id>": {
"requireMention": true,
"allowFrom": ["<user_id>", "..."]
}
}
}
Every inbound message re-reads the file (hot-reload). This mirrors nh-oracle's book §Chapter 4 point: "gate() reads access.json fresh every message — that's the difference between getting permission wrong and being safe."
Notification shape (what Claude Code / any MCP client receives)
{
"method": "notifications/claude/channel",
"params": {
"content": "hello world",
"meta": {
"chat_id": "1512079809021214730",
"message_id": "1524685099780804720",
"user": "nazt_",
"user_id": "691531480689541170",
"ts": "2026-07-09T07:54:15.067Z",
"attachment_count": "1",
"attachments": "diagram.png (image/png, 42KB)"
}
}
}
Same shape as mqtt-channel-min and the full Discord plugin. Deliberate.
Tests
bun test
Covers the pure-logic layer:
shouldRelay()— 9 access-policy cases (bot loop guard, DM allowlist / disabled / default, guild room opt-in / requireMention / per-group allowFrom)INTENTSvalue equals37377(peer-verified vs Bo's file via No.1)assertSendable()— inbox path constraint (accept / reject / missing)
Honest failure section (per ArraMQ workshop-07 §7 convention)
Cannot test the Gateway path live in this session. The Orz Discord bot token is already in use by the running claude-plugins-official/discord plugin process (that's how the conversation prompting this build reaches me). Discord Gateway allows only one Identify per token — a second connection with the same token boots the first. Doing so mid-conversation would break the reply channel.
What this means concretely:
- Unit tests pass (13/13 for pure logic).
- REST catchup mode (
READ_ONLY_REST=1) is safe to run alongside a live Gateway session and IS the verification path the author of this repo can use in this session. - The Gateway path (
connectGateway(), op10 heartbeat + op2 Identify + op0 MESSAGE_CREATE) is copy-of-the-spec + peer-cited (Bo's file via No.1 disclosure). Not run live here. - Line-for-line correctness of the Gateway payload shapes is verified against the Discord Gateway v10 spec (docs.discord.com). Semantic correctness against a real broker was NOT run in this session — flagged honestly rather than claimed.
To exercise the Gateway path safely, use a second bot token (create a new bot at discord.com/developers/applications) — or run this bridge in place of the plugin on a fresh state dir. The path exists; the author just didn't have a safe context to run it live.
Peer credits (cite-then-claim)
Direct disclosure of key mechanism:
- No.1 (Lord Knight, msg
1524684061262745710) posted the raw-WS + op10/op2 pattern - No.6 (Gemini, msg
1524684029515796671) posted thespawnSync("maw hey", ...)pattern - Tinky (via nazt relay
1524683344762241075) surfaced the file exists as 771 LOC (later revised to 1044/1348 by other peers — line-count discrepancy still open) - Leica (msg
1524683566368424008) synthesized the architecture - ChaiKlang (msg
1524683566368424008) cross-checked via his own dindex.db
Auxiliary evidence from public repos:
MEYD-605/no10-oracle/README.md—ExecStart=…discord-relay-ws.ts --agent no10 --state-dir …sila-build-with-oracle/grok-cli-oracle/.grok/infra.md— Discord stack layer tablesila-build-with-oracle/grok-cli-oracle/scripts/setup.sh— state dir prep pattern
Book cite:
- nh-oracle, ผ่าไส้ Discord Channel ของ Claude Code (2026-07-09, 104 pages) — access.json hot-reload principle
License
MIT.
Installing Orz Discord Relay Ws
This server has no published package — it is built from source. Open the repository and follow its README.
▸ github.com/xaxixak/orz-discord-relay-wsFAQ
Is Orz Discord Relay Ws MCP free?
Yes, Orz Discord Relay Ws MCP is free — one-click install via Unyly at no cost.
Does Orz Discord Relay Ws need an API key?
No, Orz Discord Relay Ws runs without API keys or environment variables.
Is Orz Discord Relay Ws hosted or self-hosted?
Self-hosted: the server runs locally on your machine via the install command above.
How do I install Orz Discord Relay Ws in Claude Desktop, Claude Code or Cursor?
Open Orz Discord Relay Ws on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.
Related MCPs
Gmail
Read, send and search emails from Claude
by GoogleSlack
Send, search and summarize Slack messages
by SlackRunbear
No-code MCP client for team chat platforms, such as Slack, Microsoft Teams, and Discord.
Discord Server
A community discord server dedicated to MCP by [Frank Fiegel](https://github.com/punkpeye)
Compare Orz Discord Relay Ws with
Not sure what to pick?
Find your stack in 60 seconds
Author?
Embed badge for your README
Browse similar
All communication MCPs
