claude mcp add discord-mcp -- python -m -e{
"mcpServers": {
"discord-mcp": {
"command": "python",
"args": ["-m", "venv"]
}
}
}Resumen de MCP Servers
# discord-mcp
[](LICENSE)
[](#testing)
An MCP server over the real [Discord REST API](https://discord.com/developers/docs/reference)
-- so a Claude agent calls `list_channels(guild_id="...")` instead of
hand-rolling an authenticated `httpx` request. Built to the
[github-mcp](https://github.com/jaimenbell/github-mcp)/[bus-mcp](https://github.com/jaimenbell/bus-mcp)
standard in this portfolio (own pyproject, fastmcp server, typed errors, real
test suite, honest README) -- fifth flagship, first over Discord.
**5 read-only tools, always on** + **7 write tools, gated OFF by default**
behind `DISCORD_MCP_ENABLE_WRITE=1` -- see "Write tools" below.
## Quickstart (60 seconds)
```bash
python -m venv .venv
.venv/Scripts/python.exe -m pip install -e ".[test]"
```
Add to your Claude Desktop/Code MCP config:
```json
{
"mcpServers": {
"discord-mcp": {
"command": "python",
"args": ["C:/path/to/discord-mcp/run_server.py"],
"env": { "DISCORD_BOT_TOKEN": "your-bot-token-here" }
}
}
}
```
Without `DISCORD_BOT_TOKEN` set, every tool call still returns a clean
structured error (Discord's own 401) instead of crashing -- see "Typed
errors" below. Write tools also need `DISCORD_MCP_ENABLE_WRITE=1` in the same
`env` block, or they refuse locally with `policy_refusal` -- see "Write
tools" below.
## What this is / is not
This is a **reference portfolio implementation** demonstrating an MCP server
over a real external SaaS API (Discord) -- it is **NOT** an official Discord
MCP server, and it is not affiliated with Discord Inc. It started from an
earlier, separate sibling project's `HttpDiscordClient`, a stdlib-`urllib`
Discord transport built and verified against a real live guild for that
project's own server-provisioning tooling. This repo hand-adapts that
client's request-building, header construction (including its deliberately
descriptive `User-Agent` -- see below), and error handling onto `httpx`
(matching this fleet's other MCP servers) as its own standalone client with
no dependency on that sibling project. discord-mcp does not import from or
depend on that other package at all.
Started as 5 read-only tools with no write capability at all; now ships a
7-tool write group, off by default, mirroring the `*_MCP_ENABLE_WRITE`-style
gate already shipped in github-mcp/bus-mcp/desktop-mcp in this same
portfolio -- see "Write tools" below.
## Tools
### Read (always on, no gate)
| Tool | Discord endpoint | Purpose |
|---|---|---|
| `list_channels` | `GET /guilds/{guild_id}/channels` | All channels (every type) in a guild |
| `list_roles` | `GET /guilds/{guild_id}/roles` | All roles in a guild |
| `list_categories` | `GET /guilds/{guild_id}/channels` (filtered) | Category channels only (Discord type 4) -- Discord has no dedicated categories-only endpoint, so this filters the same channels payload client-side |
| `get_channel_permission_overwrites` | `GET /channels/{channel_id}` (`permission_overwrites` field) | Role/member allow+deny bitfields set on one channel |
| `get_member_roles` | `GET /guilds/{guild_id}/members/{member_id}` (`roles` field) | Role ids currently assigned to one guild member |
### Write (gated behind `DISCORD_MCP_ENABLE_WRITE=1`, default OFF)
| Tool | Discord endpoint | Purpose |
|---|---|---|
| `create_channel` | `POST /guilds/{guild_id}/channels` | Create a text/voice/category channel |
| `edit_channel` | `PATCH /channels/{channel_id}` | Rename/re-topic/re-parent/reorder an existing channel |
| `create_role` | `POST /guilds/{guild_id}/roles` | Create a role |
| `edit_role` | `PATCH /guilds/{guild_id}/roles/{role_id}` | Edit an existing role |
| `edit_guild` | `PATCH /guilds/{guild_id}` | Update guild-level identity (name/icon/banner/description) |
| `delete_channel` | `DELETE /channels/{channel_id}` | **Destructive.** Delete a channel |
| `create_message` | `POST /channels/{channel_id}/messages` | Post a message to a text channel (`content`: non-empty, <= 2000 chars) |
## Write tools
Seven write tools were added on top of the original 5 read-only tools,
mirroring the exact write-gate pattern already shipped in this portfolio
(github-mcp's `GITHUB_MCP_ENABLE_WRITE`, desktop-mcp's
`DESKTOP_MCP_ENABLE_*`, and most closely bus-mcp's `BUS_MCP_ENABLE_WRITE` +
`gated_write` decorator, copied as the reference template). `create_message`
was added a night later than the other 6, once it became clear that none of
create_channel/edit_channel/create_role/edit_role/edit_guild/delete_channel
can actually post content to a channel -- it reuses the exact same gate and
the `post()` helper `create_channel`/`create_role` already added to
`client.py`, no new HTTP plumbing.
- **Off by default.** Set `DISCORD_MCP_ENABLE_WRITE=1` (or `true`/`yes`/`on`)
in the server's environment to enable the write group. Unset (or any other
value) means every write tool call refuses **locally**, with zero Discord
API calls attempted, returning a structured `policy_refusal` error:
```json
{"ok": false, "error": {"type": "policy_refusal", "message": "Tool group 'write' is disabled. Set DISCORD_MCP_ENABLE_WRITE=1 in the server's environment to enable it.", "group": "write", "tool": "create_channel", "required_env": "DISCORD_MCP_ENABLE_WRITE"}}
```
- **Enforced at the route layer, not just the MCP-tool layer.** The
`@config.gated_write` decorator wraps each write function directly in
`discord_mcp/routes.py` (not merely the `@mcp.tool` wrapper in
`server.py`), so the gate is unit-testable without spinning up fastmcp or
a real transport, and can't be bypassed by any alternate calling path into
`routes.py`.
- **Read fresh from the environment on every call**, never cached at
import time -- an operator can arm/disarm the write group without
restarting the server process, and tests can monkeypatch it per-test.
- **`delete_channel` gets no separate or lower bar.** Despite being
genuinely destructive/irreversible against a real guild, it is gated
behind the exact same `DISCORD_MCP_ENABLE_WRITE` env var as the other
write tools -- confirmed by dedicated tests in `test_routes.py` (setting
plausible-but-wrong var names like `DISCORD_MCP_ENABLE_DELETE` does
**not** arm it).
- **Same auth path as the read tools.** Write calls reuse the exact same
`_headers()`/pooled `httpx.AsyncClient` construction in `client.py` as
every read tool -- there is only one auth code path in this repo.
- **`create_message` validates content locally, not just via Discord's own
400.** `content` must be a non-empty string of at most 2000 characters
(Discord's real message-length limit) -- checked by
`client.validate_message_content` before any request is built, same
"never even attempt the call" discipline as the snowflake-id checks. A
violation returns `validation_error`, not a raw Discord 400.
### `icon_base64`/`banner_base64` on `edit_guild` -- a flagged assumption
Discord's docs describe `PATCH /guilds/{id}`'s `icon`/`banner` fields as an
"image data" string -- a full data URI (`data:image/png;base64,<base64>`),
not a bare base64 payload. `edit_guild` accepts either:
- a full `data:...;base64,...` URI, passed through unchanged, or
- raw base64 bytes, which are **assumed to be PNG** and wrapped as
`data:image/png;base64,<value>`.
**This PNG assumption is not verified against a real Discord response** --
this repo has not confirmed whether Discord accepts, rejects, or silently
mis-renders a non-PNG image (JPEG, animated GIF for boosted-server icons,
etc.) sent under an `image/png` label. If you have a non-PNG image, pass a
full `data:image/...;base64,...` URI yourself rather than relying on the
default. Flagged here rather than guessed silently.
## Typed errors, never a raw crash
Every tool returns `{"ok": true, ...}` on success or `{"ok": false, "error":
{...}}` on failure -- never an unhandled exception or stack trace.
- **`network_error`** -- connection refused, timeout, DNS failure, or a
malformed request URL. Discord's API wasn't reachable at all.
- **`auth_error`** (401) -- missing or invalid bot token.
- **`permission_error`** (403, with Discord's own JSON error body, e.g.
`{"message": "Missing Access", "code": 50001}`) -- the bot lacks the
permission/scope for this call, or isn't in the guild.
- **`cloudflare_blocked`** (403, with **no** JSON error body) -- Discord's
API docs ask for a descriptive `User-Agent`; without one, a client's
default UA is a well-known bot fingerprint that Discord's Cloudflare edge
can reject with a bare 403 *before* the request ever reaches route-level
permission checks. This is otherwise indistinguishable from a real
`permission_error` 403 -- Discord's real permission-denied responses
always carry a JSON body, so **absence of a JSON body on a 403 is the
signal this type is built on.** This is a best-effort heuristic, not a
certainty: a proxy, load balancer, or future Discord change that strips
the body on a *different* kind of 403 would also land here. discord-mcp
sends the same descriptive `User-Agent` this heuristic exists to explain
(see `client.py`'s `_headers()`), so in practice this type should rarely
fire from this server's own calls -- see "Honest limitations" below.
- **`not_found`** (404) -- bad/unknown guild, channel, role, or member id.
- **`rate_limited`** (429) -- Discord's rate limit. Carries `retry_after_s`,
parsed straight from Discord's own JSON body (`retry_after`, in seconds).
This server does **not** auto-retry -- it surfaces the limit as a
structured error immediately and leaves any backoff/retry decision to the
caller.
- **`decode_error`** -- a 2xx response whose body isn't valid JSON (should
not happen against the real API; guards against a malformed proxy/mock).
- **`discord_api_error`** -- any other 4xx/5xx not covered above.
-Lo que la gente pregunta sobre discord-mcp
¿Qué es jaimenbell/discord-mcp?
+
jaimenbell/discord-mcp es mcp servers para el ecosistema de Claude AI con 0 estrellas en GitHub.
¿Cómo se instala discord-mcp?
+
Puedes instalar discord-mcp clonando el repositorio (https://github.com/jaimenbell/discord-mcp) o siguiendo las instrucciones del README en GitHub. ClaudeWave también te ofrece bloques de instalación rápida en esta misma página.
¿Es seguro usar jaimenbell/discord-mcp?
+
jaimenbell/discord-mcp aún no ha sido auditado por nuestro agente de seguridad. Revisa el repositorio original en GitHub antes de usarlo en producción.
¿Quién mantiene jaimenbell/discord-mcp?
+
jaimenbell/discord-mcp es mantenido por jaimenbell. La última actividad registrada en GitHub es de today, con 0 issues abiertos.
¿Hay alternativas a discord-mcp?
+
Sí. En ClaudeWave puedes explorar mcp servers similares en /categories/mcp, ordenados por popularidad o actividad reciente.
Despliega discord-mcp en tu cloud
Lleva este repo a producción en minutos. Cada plataforma genera su propio entorno con variables de entorno editables.
¿Mantienes este repo? Añade un badge a tu README
Pega el badge en tu README de GitHub para mostrar que está auditado por ClaudeWave. Cada badge enlaza de vuelta a esta página y muestra el Trust Score actual.
[](https://claudewave.com/repo/jaimenbell-discord-mcp)<a href="https://claudewave.com/repo/jaimenbell-discord-mcp"><img src="https://claudewave.com/api/badge/jaimenbell-discord-mcp" alt="Featured on ClaudeWave: jaimenbell/discord-mcp" width="320" height="64" /></a>Más MCP Servers
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.
User-friendly AI Interface (Supports Ollama, OpenAI API, ...)
An open-source AI agent that brings the power of Gemini directly into your terminal.
The fastest path to AI-powered full stack observability, even for lean teams.
Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface
🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl!