discord-broker
Provides an MCP client with a Discord bot interface: inspect the bot identity and privileged intents, list allowed guilds and channels, read and fetch messages, post/edit/delete the bot's own messages, add or remove reactions, create threads, and make scoped Discord REST API requests under configurable guild/channel allowlists.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
discord-broker
A remote MCP server on Cloudflare Workers that lends an MCP client (Claude) a Discord bot. The bot token lives only in the Worker's secret store. The client never sees it, only tool results.
Claude ──OAuth bearer──▶ /mcp ──▶ scope check ──▶ tools ──"Bot <token>"──▶ discord.com/api/v10
│
└─ rate-limit buckets, 429 retry, 401 breakerIt's the twin of github-broker: same OAuth front door, same hand-written MCP transport,
same audit logging. Sign-in goes through Discord instead of GitHub.
What you get
Tool | What it does |
| The bot's identity, which privileged intents its application has (Message Content above all), the active allowlists, and who is signed in. Never returns the token. |
| Servers the bot is in (filtered by the allowlist), with its permission bits. |
| A server's channels, categories first, plus active threads. |
| Up to 100 messages, oldest first, paged with |
| One message, the message it replies to, and a jump link. |
| Send as the bot. |
| Replace the text of one of the bot's messages. |
| Delete one of the bot's own messages. Refuses anyone else's, even if the bot could. |
| Unicode emoji as the character, custom emoji as |
| From a message, standalone (public or private) in a text channel, or as a post in a forum/media channel. |
| Any REST endpoint: method, path under |
IDs are numeric strings. Every tool call writes one audit line (tool, signed-in Discord user, guild or channel, ok, ms; never message text) to Workers Logs. Discord shows everything as the bot.
Related MCP server: Discord Cloud MCP
Auth
The connector URL alone grants nothing. /mcp is an OAuth 2.1 protected resource, and
the Worker is its own authorization server
(@cloudflare/workers-oauth-provider
1.2.4, the one runtime dependency):
Claude finds the server from the
401challenge and registers itself (Client ID Metadata Document, or Dynamic Client Registration as a fallback). PKCE S256 throughout./authorizeshows a consent page naming the client and the host the grant goes to. Approve only when it saysclaude.ai(orlocalhostfor Claude Code).You sign in with Discord, through the same Discord application's OAuth2 with scope
identifyonly. The Worker reads/users/@meonce, revokes that user token, and checks your numeric Discord user ID againstALLOWED_DISCORD_USERS.Claude gets a 1-hour access token plus a rotating refresh token, both bound to
PUBLIC_URL/mcp. The allowlist is checked again on every MCP request, so removing an ID cuts off existing tokens immediately.
Before redirecting to Discord the Worker checks its own config and shows a plain error
page instead of Discord's: a non-numeric or placeholder DISCORD_CLIENT_ID, a bot token
pasted into DISCORD_CLIENT_ID (it tells you to reset the token), a username in
ALLOWED_DISCORD_USERS, a missing client secret. A failed code exchange says whether the
client secret or the redirect URI is the likely cause. All of these are logged for
wrangler tail.
PKCE towards Discord is off by default. Discord's docs say to use the authorization code
grant "with PKCE" but document no PKCE parameters, so the default flow relies on the
client secret plus the cookie-bound state. Set DISCORD_OAUTH_PKCE="true" to send
code_challenge/code_verifier as well; if sign-in then fails at the code exchange, turn
it back off.
Where the bot may act
Two optional vars, both numeric IDs, comma- or space-separated:
DISCORD_ALLOWED_GUILDS: only channels in these servers. DMs are refused.DISCORD_ALLOWED_CHANNELS: only these channels, plus threads under them and channels under a listed category.
Both empty means anywhere the bot can reach, DMs included. A malformed entry makes every
tool call fail with a config error rather than opening up. discord_request follows the
same lists: /channels/ID/... must name an allowed channel (and a forward's source
channel must be allowed too), /guilds/ID/... an allowed server (read-only while a
channel list is set), and any other path is GET only. Discord's own permissions for the
bot still apply on top, and are the real limit when both lists are empty.
Message Content intent (checked against Discord's docs)
A common assumption is that the intent only matters for the gateway.
It doesn't: Discord's gateway docs say MESSAGE_CONTENT "permits your app to receive
message content data across the APIs", and apps without it "receive empty values" in
content, embeds, attachments, components and poll, REST reads included. The
exceptions are the bot's own messages, DMs with the bot, and messages that mention it.
So for read_messages to show what Nyx, Muse and you write, turn on Message Content
Intent on the application's Bot page. Under 100 servers that's a toggle, no review.
bot_info shows whether it's on, and read_messages adds a note when it sees the
telltale empty messages.
Permissions
Invite the bot with these permissions (integer 377957207104):
Permission | For |
View Channels | everything |
Read Message History |
|
Send Messages |
|
Send Messages in Threads | posting inside threads (Send Messages doesn't cover them) |
Add Reactions | a new emoji on a message |
Create Public Threads / Create Private Threads |
|
Embed Links | link previews in its own messages |
Channel overrides can still hide channels from it; list_channels shows what this server
allows, not what Discord lets the bot read. A 403 from Discord comes back as "the bot lacks
a permission here".
Rate limits
Per-route buckets from
X-RateLimit-Bucket/Remaining/Reset-After, keyed by the major parameter (channel, guild, webhook), shared across requests in an isolate. An exhausted bucket is waited out before sending.A
429withretry_afterup to 10 s is waited out and retried (twice at most); longer ones come back as an error with the wait. A global 429 pauses every route.After a
401the Worker stops calling Discord for 5 minutes, and it caps refused answers (401/403/429) at 1,000 per 10 minutes. Discord bans an IP for a while after 10,000 invalid requests in 10 minutes, and Workers share egress IPs, so a bad token shouldn't be allowed to hammer it.Known risk: Discord's Cloudflare edge sometimes answers Workers with an HTML 429 (error 1015) because of someone else's traffic on the shared IP. The Worker reports that plainly. It usually clears on its own; if it becomes chronic, the fix is a proxy with its own IP, not anything in this code.
Setup
You need Node 20+ and a Cloudflare account. The examples assume a new Discord application for Claude; see "Which bot" below.
1. Discord application
Developer Portal → New Application (e.g. "Claude"), then:
General Information: copy the Application ID (numeric). That's
DISCORD_CLIENT_ID.Bot: Reset Token, and keep it for step 4 without pasting it anywhere else. Turn on Message Content Intent. Turning off "Public Bot" stops other people adding it to their servers.
OAuth2: Reset Secret, keep it for step 4 (
DISCORD_CLIENT_SECRET; this is not the bot token). Add a Redirect:https://<your-worker-host>/callback. You learn the host in step 3; come back then. It must match exactly, no trailing slash.Invite the bot: open
https://discord.com/oauth2/authorize?client_id=<APPLICATION_ID>&scope=bot&permissions=377957207104and pick your agents' server.
Your Discord user ID: Settings → Advanced → Developer Mode on, then right-click your name → Copy User ID. Server and channel IDs come the same way, for the optional allowlists.
2. Configure
npm install
cp wrangler.example.jsonc wrangler.jsonc
npx wrangler login
npx wrangler kv namespace create OAUTH_KVwrangler.jsonc is git-ignored: it holds your own IDs and URL, no secrets. Paste the KV
id into it, then fill in vars: DISCORD_CLIENT_ID,
ALLOWED_DISCORD_USERS (your numeric user ID, not a username), and optionally
DISCORD_ALLOWED_GUILDS / DISCORD_ALLOWED_CHANNELS.
3. First deploy, to learn the URL
npx wrangler deployIt prints https://discord-broker.<subdomain>.workers.dev. Put that in PUBLIC_URL (no
trailing slash), and add <PUBLIC_URL>/callback as the OAuth2 Redirect on the Discord
application.
4. Secrets
Paste each at the prompt so it never lands in shell history or a file:
npx wrangler secret put DISCORD_BOT_TOKEN
npx wrangler secret put DISCORD_CLIENT_SECRET5. Deploy and smoke test
npx wrangler deploy
curl -si -X POST "$PUBLIC_URL/mcp" -d '{}' | grep -i www-authenticate # 401 + resource_metadata
curl -s "$PUBLIC_URL/.well-known/oauth-authorization-server" | jq .issuer6. Connect
claude.ai: Settings → Connectors → Add custom connector. Name it, and give the URL
<PUBLIC_URL>/mcpexactly (the resource metadata must match it). Leave Advanced settings blank: no client ID or secret there. You'll see the consent page (check it saysclaude.ai), then Discord's "authorize" screen asking only for your username.Claude Code:
claude mcp add --transport http discord-broker <PUBLIC_URL>/mcp.
Then ask for bot_info: it should show the bot's name and message_content: enabled.
If sign-in fails
You see | Usually |
The Worker's "Sign-in failed" page about | Exactly what it says; fix the var and deploy. |
Discord says "Invalid OAuth2 redirect_uri" | The Redirect on the application isn't exactly |
"Discord did not return a token (invalid_client)" | Wrong client secret (the OAuth2 secret, not the bot token) or wrong Application ID. |
"not in ALLOWED_DISCORD_USERS" | The page names the account and its ID; put that ID in the var. |
Tools answer "bot token was rejected" |
|
npx wrangler tail shows every sign-in step as a JSON line (config_error,
token_exchange_failed, sign_in_refused, sign_in, oauth_endpoint, tool_call).
Which bot
Claude can use a new application of its own or one of the existing ones. A separate "Claude" application keeps its messages, permissions, intents and token separate from Nyx and Muse, and you can kick or reset it without touching them. Reusing one would make Claude post under that agent's name.
Operating it
Logs:
npx wrangler tail, or Workers Logs in the dashboard (observabilityis on).Cut someone off: remove them from
ALLOWED_DISCORD_USERSand deploy. Takes effect on the next request.Rotate the token: Bot → Reset Token, then
wrangler secret put DISCORD_BOT_TOKEN. The old token stops working the moment you reset it.Kill switch: kick the bot from the server, or reset its token.
Narrow it: set
DISCORD_ALLOWED_GUILDS/DISCORD_ALLOWED_CHANNELSand deploy, or take permissions away from its role in Discord.
Development
npm test # vitest: client (rate limits, breakers), tools, scope, MCP conformance, full OAuth flow
npm run typecheck
cp .dev.vars.example .dev.vars && npm run dev # local workerd on :8787The tests run the server against the official MCP clients (@modelcontextprotocol/client
2.4 in 2026-07-28 and legacy modes, @modelcontextprotocol/sdk 1.32), and walk the whole
sign-in: registration → consent → Discord (faked) → token → MCP calls. Discord's API is
faked at the fetch layer; no test touches a real token.
Layout: src/index.ts (routing, OAuth roles), src/auth.ts (consent, Discord sign-in,
allowlist, config checks), src/mcp.ts (transport, identical to github-broker's),
src/tools.ts, src/discord/client.ts (REST client, rate limits),
src/discord/scope.ts (guild/channel allowlists).
Not covered yet: uploading files (multipart), slash commands and interactions, the gateway (the Worker only speaks REST, so it can't be woken by a mention; Claude reads when asked).
License
MIT — see LICENSE.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
This server cannot be deployed
Maintenance
Related MCP Connectors
Zero-dependency Cloudflare Worker gateway turning websites and REST APIs into MCP tools.
Discord MCP server for ChatGPT, Claude and any AI agent: list servers and channels, read channel messages, search messages, send message, reply to message, send DM, read mentions and assign role in your own Discord account. Cotlet works in the account through your Cloud Browser, a real Chrome where you sign in to Discord yourself; Cotlet does not store your password and you need no Discord API key. About the official Discord API: Yes, Discord has an official API for bots and OAuth2 apps, but it acts as a bot that a server manager adds, not as you, and Discord has no official MCP server for it (checked 5 Nov 2026). The MCP server on Discord's docs site only searches documentation. Setup and FAQ: https://cotlet.dev/integrations/discord-api-mcp-server
Remote streamable-HTTP MCP server running on a single Cloudflare Worker. Your assistant gets live Airbnb, Amazon, Booking.com, Google Flights, Maps and Reddit data, social search on X, Instagram and TikTok, the Meta Ad Library, and image/video generation without any keys. Connect your own accounts to let it send WhatsApp or Telegram messages, work an IMAP inbox, manage Meta Ads campaigns and publish to X and LinkedIn. OAuth 2.1 with PKCE; stored credentials are AES-256-GCM encrypted.
One OAuth connector for your team's remote MCP servers, with Tool Packs, guardrails and logs.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceA remote MCP server deployed on Cloudflare Workers that provides AI agents with authenticated, permission-aware access to Discord servers. It enables agents to read, search, and post messages while maintaining secure Role-Based Access Control via OAuth2.16MIT
- AlicenseNot gradedqualityDmaintenanceProvides full Discord API access via the Model Context Protocol, allowing users to manage servers, messages, channels, and webhooks through MCP clients. It is specifically designed for deployment on Cloudflare Workers to facilitate remote bot control.6Apache 2.0
- FlicenseNot gradedqualityCmaintenanceEnables MCP clients to read, post to, and moderate a Discord server through the Discord REST API, with opt-in guardrails and dry-run safeguards.-
- AlicenseNot gradedqualityBmaintenanceEnables AI clients to read Discord channels, recent messages, and searchable history, then turn that evidence into freshness-aware team context, briefs, decision baselines, and signed checkpoint diffs through remote MCP and GPT Actions. It keeps Discord access read-only while supporting OAuth/Bearer authentication for multiple clients.Apache 2.0