Discord control
Start tasks, send follow-ups, answer approvals and get notified from Discord. The bot controls the same tasks as the web app, and only you can use it.
Set this up after a browser task works. The bot connects out to Discord, so it needs no public webhook, and it needs no extra model API key. Enabling it doesn't make the web app public.
Commands are explicit /ccr slash commands. A conversational assistant that understands plain messages is proposed, not implemented; see the dispatcher design.
#Set up the bot
- Create an application in https://discord.com/developers/applications and create its bot. Use a dedicated application for this server. Copy the bot token into your private server configuration; never put it in a repository or chat.
- Enable Discord Developer Mode and copy your user ID (not a username, guild ID or bot ID). Only that numeric user ID can read or control tasks through the bot. This is the initial pairing mechanism.
- In the application's Installation/OAuth2 settings, add the bot to a private server using the
botandapplications.commandsscopes. Do not grant Administrator. No privileged intents, Message Content intent or public Interactions Endpoint URL are needed. Allow the bot to send you DMs. Add these lines to
server.envin your data directory:SHELLDISCORD_BOT_TOKEN='YOUR_BOT_TOKEN' DISCORD_OWNER_ID='YOUR_NUMERIC_USER_ID' # Optional: register commands in this guild for quicker development testing. DISCORD_GUILD_ID='YOUR_PRIVATE_SERVER_ID' # Optional link button; use a URL reachable from your phone/browser. CCR_CLOUD_PUBLIC_URL='https://your-private-host.example'- Restart the service and look for
[discord] connected; owner-only /ccr commands ready. Global registration (without a guild ID) may take time to appear. Guild-only commands appear in that guild, not in DMs. All guild responses are ephemeral.
Commands are registered at startup using the bot application's identity. The adapter only updates its /ccr command and leaves unrelated application commands alone. When switching from guild-only to global registration, remove the old guild command in Discord to avoid seeing both registrations. Removing the configuration stops the integration but does not unregister its commands on Discord.
#Commands
/ccr new prompt:Fix the failing tests repo:https://github.com/you/project.git
/ccr sessions
/ccr status session:s-...
/ccr say session:s-... text:Add a regression test for the edge case
/ccr interrupt session:s-...
/ccr stop session:s-...
/ccr resume session:s-...
/ccr permission session:s-... request:REQUEST_ID decision:allow
new without repo creates a scratch workspace. Session IDs are displayed in command responses and in the PWA. Follow-ups go directly to that Claude session. Ordinary channel messages and replies are not interpreted as commands in this release. Inspect a permission using /ccr status before answering; DM approval buttons are tied to an exact session and request. A decision already submitted from either Discord or the PWA is rejected until the request resolves.
#Notifications
By default the adapter DMs the owner when a new turn finishes, a session fails, or a tool needs approval. It does not stream every token or send routine status updates. Turn completion means Claude ended a turn, not independent verification that tests passed. Messages may include repository names, tool inputs and model output: enabling this option sends that content to Discord.
Use DISCORD_NOTIFICATIONS=0 to disable proactive DMs while retaining commands. A new integration starts at the end of existing logs; it does not send historical notifications. Use status to inspect any already-pending approval. Later scan progress is persisted in discord-cursors.json. Delivery failures retry; a crash between sending and persisting progress can duplicate a notification.
#Troubleshooting
- No commands: check guild ID, installation scopes and bot membership; restart the control plane. Ensure the host can reach Discord's HTTPS API and Gateway.
- Paired to another user: verify
DISCORD_OWNER_IDis your user ID. Every command and button checks this identity, including in shared servers. - No DMs: enable DMs from the bot/shared server and check server logs. The PWA and slash commands remain usable when notification delivery fails.
- Link opens nowhere: set
CCR_CLOUD_PUBLIC_URLto your real HTTPS/VPN URL; the bot cannot open a laptop SSH tunnel or configure server networking for you. - A stale approval button fails: refresh task status; another surface may already have answered it. The adapter never silently approves a different request.
#Next steps
- Notifications in the browser and installed app
- Configuration