Skip to main content
Glama
fboiero

Discord MCP

by fboiero
README.md
# Discord MCP

Servidor MCP remoto (Streamable HTTP) que expone Discord como herramientas,
pensado para conectarse desde **Claude → Configuración → Conectores → Add
custom connector** (`https://claude.ai/ask-your-org/setup`, opción *Custom*).

## Qué expone

Herramientas (tools) sobre la API de Discord, usando `discord.py`:

- `list_guilds` — servidores a los que tiene acceso el bot.
- `list_channels(guild_id)` — canales de un servidor.
- `list_members(guild_id, limit)` — miembros de un servidor.
- `list_roles(guild_id)` — roles de un servidor.
- `send_message(channel_id, content)` — enviar un mensaje.
- `read_messages(channel_id, limit, before_message_id)` — leer historial.
- `create_thread(channel_id, name, message_id?, auto_archive_minutes)` — crear un hilo.
- `add_reaction(channel_id, message_id, emoji)` / `remove_reaction(...)` — reacciones.
- `add_role_to_member(guild_id, user_id, role_id)` / `remove_role_from_member(...)` — roles.

Todos los ids (servidor, canal, mensaje, usuario, rol) son los snowflakes de
Discord, como string.

## 1. Crear el bot de Discord

1. Entrá al [Discord Developer Portal](https://discord.com/developers/applications) y creá una **New Application**.
2. En **Bot**, creá el bot y copiá el **Token** (botón *Reset Token*). Es el valor de `DISCORD_BOT_TOKEN`.
3. En la misma sección **Bot → Privileged Gateway Intents**, activá:
   - **Server Members Intent** (necesario para `list_members` y gestión de roles).
   - **Message Content Intent** (necesario para leer el contenido de los mensajes en `read_messages`).
4. En **OAuth2 → URL Generator**:
   - Scopes: `bot`.
   - Bot Permissions (ajustá según lo que vayas a usar): `View Channels`, `Send Messages`,
     `Read Message History`, `Create Public Threads`, `Add Reactions`, `Manage Roles`.
   - Abrí la URL generada e invitá el bot al servidor donde lo vas a usar.
5. Si vas a usar `add_role_to_member` / `remove_role_from_member`, asegurate de que el
   **rol del bot esté por encima** (mayor jerarquía) de los roles que quiera asignar.

## 2. Configurar variables de entorno

Copiá `.env.example` a `.env` y completá:

```bash
cp .env.example .env
```

- `DISCORD_BOT_TOKEN`: el token del paso anterior.
- `MCP_AUTH_TOKEN`: un secreto propio para proteger el servidor (generalo con
  `python -c "import secrets; print(secrets.token_urlsafe(32))"`). Claude.ai va a
  enviarlo como header `Authorization: Bearer <token>` en cada request (ver paso 4).
- El resto de las variables tienen defaults razonables — ver comentarios en `.env.example`.

## 3. Correr el servidor

### Local (sin Docker)

```bash
python -m venv .venv && source .venv/bin/activate
pip install -e .
python -m discord_mcp   # o: discord-mcp
```

Por defecto escucha en `http://0.0.0.0:8000/mcp`.

### Con Docker

```bash
docker compose up --build
```

o manualmente:

```bash
docker build -t discord-mcp .
docker run --env-file .env -p 8000:8000 discord-mcp
```

### Desplegar (Render / Railway / Fly.io / cualquier VPS)

La imagen es un contenedor genérico HTTP sin estado persistente propio (el
estado del bot vive en memoria y en Discord), así que corre en cualquier
proveedor que soporte Docker:

- Configurá las variables de entorno del paso 2 en el panel del proveedor.
- Exponé el puerto `8000` (o el que definas en `PORT`) por HTTPS — Claude.ai
  requiere que el servidor MCP sea accesible por `https://`.
- Una vez desplegado, la URL del conector va a ser
  `https://<tu-dominio>/mcp`.

Para reforzar la protección contra DNS rebinding en producción, seteá
`ALLOWED_HOSTS` (el hostname público del servidor) y `ALLOWED_ORIGINS`
(por ejemplo `https://claude.ai`).

## 4. Conectarlo en claude.ai como conector custom

1. Entrá a `https://claude.ai/ask-your-org/setup` (o Configuración → Conectores)
   y elegí **Add custom connector** / **Custom**.
2. **Name**: `Discord` (o el que prefieras).
3. **URL**: `https://<tu-dominio>/mcp`.
4. En **Request headers / Advanced settings**, agregá:
   - Header: `Authorization`
   - Valor: `Bearer <el mismo valor que pusiste en MCP_AUTH_TOKEN>`
5. Guardá y probá pidiéndole a Claude algo como *"Listá los servidores de
   Discord disponibles"* — debería llamar a `list_guilds`.

Si preferís no usar autenticación (sólo para pruebas rápidas, nunca en
producción), dejá `MCP_AUTH_TOKEN` vacío y omití el header.

## Desarrollo

```bash
pip install -e ".[dev]"
pytest
```

Los tests cubren configuración, el middleware de autenticación y la lógica
de `DiscordService` que no requiere una conexión real a Discord (no hay
tests de integración contra la API real de Discord).

## Notas de arquitectura

- El bot de `discord.py` corre como una tarea de background dentro del mismo
  event loop que el servidor ASGI (Starlette/uvicorn); arranca y se apaga
  junto con el lifespan de la app (`src/discord_mcp/server.py`).
- El servidor MCP usa transporte **Streamable HTTP** (`mcp.server.MCPServer`
  del SDK oficial `mcp`), el transporte remoto soportado por los conectores
  custom de Claude.
- La autenticación es un bearer token estático validado por un middleware
  ASGI propio (`auth_middleware.py`), pensado para el mecanismo de "Request
  headers" de los conectores custom — no implementa el flujo OAuth completo.