orz-discord-relay-ws
Relays messages from Discord channels to MCP clients, supporting message receipt, replies, reactions, and message editing via REST tools.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@orz-discord-relay-wssend 'Hey' to channel 987654321"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
orz-discord-relay-ws
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 + skillsOrz's
minimal-channel-mqtt(~230 LOC, 2026-07-09) — MQTT broker → MCP notificationThis — 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.
Related MCP server: MCP Server Demo
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 notificationaccess.jsoncompatible with Discord plugin's file (hot-reload per message)REST tools:
reply/react/edit_messageassertSendable()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.tsOptional:
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=10access.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 testCovers 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 patternNo.6 (Gemini, msg
1524684029515796671) posted thespawnSync("maw hey", ...)patternTinky (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 architectureChaiKlang (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.
This server cannot be deployed
Maintenance
Related MCP Connectors
Hosted MCP server connecting claude.ai, ChatGPT and other AI apps to your own computer
- QuallaaOAuthcom.quallaa
Talk to your public-facing AI from any MCP client — Claude, ChatGPT, Cursor, Cline, Windsurf.
Cloudflare Workers MCP server: ai-gateway
Cloudflare Workers MCP server: claude-skill-validator
Related MCP Servers
- AlicenseAqualityFmaintenanceProvides Discord integration capabilities to MCP clients like Claude Desktop.15168MIT
- AlicenseNot gradedqualityDmaintenanceA minimal WebSocket-based MCP server implementation that enables modern tool integrations with VSCode, Claude, and other applications.6 npmISC
- AlicenseNot gradedqualityFmaintenanceThe self-hosted MCP bridge between Claude Chat and Claude Code.46AGPL 3.0
- AlicenseNot gradedqualityFmaintenanceA simple MCP server that enables Claude to communicate with locally running LLM models via LM Studio.9MIT