Agency-grade Discord MCP server with multi-guild project routing.
- 48 MCP tools â messaging, channels, moderation, roles, webhooks, audit log, threads, guilds, invites, permissions, search, 23 templates, OG embed unfurling, project introspection
- Multi-guild project routing â
send_message({ project: "my-app", channel: "builds" })instead of raw channel IDs - Notification routing â map notification types (
ci_build,deploy,error) to channels per project - Owner pings â configure project owners so releases, errors, and alerts auto-mention the right people
- Multi-bot support â manage multiple Discord bots from a single MCP server with per-project tokens
- Tool profiles â load only the tools an agent needs; cut schema overhead by 85% with slim profiles
- Smart channel resolution â channel params accept channel name or snowflake ID, with 4-layer fuzzy fallback
- Auto-embed for send_message â every message gets a branded embed by default;
raw: truefor plain text - OG metadata unfurling â
send_embedfetches Open Graph metadata server-side and renders rich link previews - Flexible token configuration â configurable default token env var, optional default when all projects use per-project tokens
- Config validation â
discord-ops validatedetects duplicate guilds, missing tokens, invalid channel refs without connecting to Discord - HTTP/SSE + stdio transports â stdio for Claude Code, HTTP/SSE for remote MCP clients
- HTTP transport auth â bearer token auth via
DISCORD_OPS_HTTP_TOKENwith constant-time comparison - Dry-run mode â simulate destructive operations without calling Discord API
- Interactive setup wizard â
discord-ops setupsupports single-bot and multi-bot configuration - Security hardening â rate limiting, permission pre-flight checks, snowflake ID validation, self-protection guards
- Lazy login â tools enumerate before Discord connects; first tool call triggers login
- Zod validation â all inputs validated before execution
- Error sanitization â tokens, webhook URLs, and snowflake IDs stripped from error output
- Audit logging â every tool call logged to stderr
# Install
npm install -g discord-ops
# Interactive setup (creates ~/.discord-ops.json)
discord-ops setup
# Or manual setup
export DISCORD_TOKEN="your-bot-token"
discord-ops health
# Start MCP server (stdio)
discord-ops
# Start MCP server (HTTP/SSE)
discord-ops serve --port 3000Add to your project's .mcp.json. Use npx with @latest so every session automatically uses the latest published release â without it, npx may serve a stale cached version indefinitely:
{
"mcpServers": {
"discord-ops": {
"command": "npx",
"args": ["-y", "discord-ops@latest"],
"env": {
"DISCORD_TOKEN": "${DISCORD_TOKEN}"
}
}
}
}The ${VAR} syntax is Claude Code's native env var interpolation â it reads the value from your shell environment at startup. Export your bot token in ~/.zshrc (or .bashrc) and it will be available to all projects without hardcoding it in any file.
When each project has its own bot, pass all token env vars and let ~/.discord-ops.json handle which project uses which:
{
"mcpServers": {
"discord-ops": {
"command": "npx",
"args": ["-y", "discord-ops@latest"],
"env": {
"ORG_A_DISCORD_TOKEN": "${ORG_A_DISCORD_TOKEN}",
"ORG_B_DISCORD_TOKEN": "${ORG_B_DISCORD_TOKEN}"
}
}
}
}Each project in ~/.discord-ops.json declares "token_env": "ORG_A_DISCORD_TOKEN" and discord-ops routes automatically. No default DISCORD_TOKEN needed when all projects have token_env set.
If all your projects share one bot, just pass that token:
{
"mcpServers": {
"discord-ops": {
"command": "npx",
"args": ["-y", "discord-ops@latest"],
"env": {
"DISCORD_TOKEN": "${DISCORD_TOKEN}"
}
}
}
}If another tool already claims DISCORD_TOKEN, use DISCORD_OPS_TOKEN_ENV to point at a different name:
{
"mcpServers": {
"discord-ops": {
"command": "npx",
"args": ["-y", "discord-ops@latest"],
"env": {
"DISCORD_OPS_TOKEN_ENV": "MY_BOT_TOKEN",
"MY_BOT_TOKEN": "${MY_BOT_TOKEN}"
}
}
}
}The killer feature: route messages by project name and channel alias instead of raw IDs.
{
"projects": {
"my-app": {
"guild_id": "123456789012345678",
"channels": {
"dev": "CHANNEL_ID",
"builds": "CHANNEL_ID",
"alerts": "CHANNEL_ID",
"releases": "CHANNEL_ID"
},
"default_channel": "dev"
}
},
"default_project": "my-app",
"notification_routing": {
"ci_build": "builds",
"deploy": "builds",
"release": "releases",
"error": "alerts",
"dev": "dev"
}
}Projects can specify their own bot token via token_env:
{
"projects": {
"org-a": {
"guild_id": "111111111111111111",
"channels": { "dev": "CHANNEL_ID" },
"default_channel": "dev",
"token_env": "ORG_A_DISCORD_TOKEN"
},
"org-b": {
"guild_id": "222222222222222222",
"channels": { "dev": "CHANNEL_ID" },
"default_channel": "dev",
"token_env": "ORG_B_DISCORD_TOKEN"
}
}
}When all projects have token_env, the default DISCORD_TOKEN is optional. Each project connects with its own bot.
Configure project owners so that releases, errors, and alerts automatically prepend @mentions. This ensures the right people are always paged for critical events without hardcoding mentions in every message.
{
"projects": {
"my-app": {
"guild_id": "123456789012345678",
"channels": { "releases": "CHANNEL_ID", "alerts": "CHANNEL_ID" },
"owners": ["820027414902079548"],
"notify_owners_on": ["release", "error", "alert"]
}
}
}notify_owners_on values: any notification type (release, error, alert, ci_build, deploy, etc.)
Safety: "dev" is hardcoded to never trigger owner pings regardless of config â dev noise stays quiet.
When a send_message or send_embed call uses a matching notification_type, the owner mentions are automatically prepended to the message. No other changes needed.
The channel param accepts a channel name or snowflake ID anywhere a channel is needed. Resolution happens in four layers:
- Exact alias match â
"builds"hits thebuildsalias in your project config - Fuzzy alias match â
"build"or"blds"resolves to the closest alias - Live Discord API lookup â
"general"resolves even with no configured alias - Error â if none of the above find a match
This means you can pass channel: "general" and it will work even for channels that aren't in your config. You can also pass a raw snowflake ID directly â channel: "1234567890" bypasses alias resolution entirely.
{
"project": "my-app",
"notification_routing": {
"ci_build": "builds",
"deploy": "builds"
}
}# By project + channel alias
send_message({ project: "my-app", channel: "builds", content: "Build passed!" })
# By notification type (auto-routed to channel, owner pinged if configured)
send_message({ project: "my-app", notification_type: "release", content: "v1.0.0 shipped" })
# Direct channel ID (always works)
send_message({ channel_id: "123456789", content: "Hello" })
# Channel by name (live lookup â no alias needed)
send_message({ project: "my-app", channel: "general", content: "Hello" })
send_message automatically wraps messages in a polished embed with a color bar, description, and timestamp. Pass raw: true to send plain text instead.
# Branded embed (default)
send_message({ project: "my-app", channel: "dev", content: "Deploy complete" })
# Plain text
send_message({ project: "my-app", channel: "dev", content: "pong", raw: true })
send_embed fetches Open Graph metadata server-side from any URL and renders a rich preview embed. All OG fields can be overridden.
send_embed({
url: "https://www.npmjs.com/package/discord-ops/v/0.14.0",
project: "my-app",
channel: "releases",
title: "discord-ops v0.14.0",
description: "Owner pings, smart channel resolution, category editing",
color: 5763719,
footer: "Released April 3, 2026"
})
Useful for sharing GitHub PRs, npm releases, blog posts, or any URL with rich previews â the bot fetches the metadata so Discord's CDN doesn't cache-bust client-side unfurls.
| Tool | Description |
|---|---|
send_message |
Send a message with project routing (auto-embed by default) |
send_embed |
Fetch OG metadata from a URL and post a rich embed |
get_messages |
Fetch recent messages (supports ISO 8601 timestamps) |
edit_message |
Edit a bot message |
delete_message |
Delete a message |
add_reaction |
React to a message |
pin_message |
Pin a message in a channel |
unpin_message |
Unpin a message |
search_messages |
Search messages by content, author, or date range |
send_template |
Send a styled embed using a built-in template |
list_templates |
List available templates with required variables |
notify_owners |
Ping project owners based on notification type |
| Tool | Description |
|---|---|
list_channels |
List guild channels |
get_channel |
Get channel details |
create_channel |
Create a channel |
edit_channel |
Edit channel name, topic, category, or position (text, voice, and categories) |
delete_channel |
Delete a channel |
purge_messages |
Bulk-delete messages (max 100, < 14 days old) |
set_slowmode |
Set or disable slowmode |
move_channel |
Move a channel to a different category or position |
set_permissions |
Set channel permission overrides for a role or member |
| Tool | Description |
|---|---|
kick_member |
Kick a member from a guild |
ban_member |
Ban a user from a guild |
unban_member |
Unban a user |
timeout_member |
Timeout (mute) a member |
| Tool | Description |
|---|---|
list_roles |
List guild roles |
create_role |
Create a new role |
edit_role |
Edit role properties |
delete_role |
Delete a role |
assign_role |
Add or remove a role from a member |
| Tool | Description |
|---|---|
create_webhook |
Create a webhook on a channel |
get_webhook |
Get webhook details |
list_webhooks |
List webhooks for a guild or channel |
edit_webhook |
Edit webhook properties |
delete_webhook |
Delete a webhook |
execute_webhook |
Send a message via webhook |
| Tool | Description |
|---|---|
query_audit_log |
Query guild audit log with filters |
| Tool | Description |
|---|---|
list_guilds |
List bot's guilds |
get_guild |
Get guild details |
get_invites |
Get all active invites for a guild |
create_invite |
Create an invite link for a channel |
list_members |
List guild members |
get_member |
Get member details |
| Tool | Description |
|---|---|
create_thread |
Create a thread |
list_threads |
List active threads |
archive_thread |
Archive (and optionally lock) a thread |
| Tool | Description |
|---|---|
health_check |
Bot status, version, connected guilds, and permission audit |
list_projects |
List all projects with guild mappings, token status, and validation |
Load only the tools an agent needs. Reduces schema token overhead by up to 85% for narrow use cases.
| Profile | Tools | Description |
|---|---|---|
full |
48 | All tools (default) |
monitoring |
6 | get_messages, send_message, add_reaction, create_thread, health_check, list_projects |
readonly |
6 | get_messages, list_channels, list_members, get_guild, health_check, list_projects |
moderation |
7 | get_messages, kick_member, ban_member, timeout_member, delete_message, purge_messages, query_audit_log |
messaging |
5 | add_reaction, delete_message, edit_message, get_messages, send_message |
channels |
7 | create_channel, delete_channel, edit_channel, get_channel, list_channels, purge_messages, set_slowmode |
webhooks |
6 | create_webhook, delete_webhook, edit_webhook, execute_webhook, get_webhook, list_webhooks |
# Via CLI flag
discord-ops --profile monitoring
# Load specific tools
discord-ops --tools "send_message,send_template,health_check"
# Combined (profile as base + add tools)
discord-ops --profile readonly --tools "send_message"Profiles can be set per project in ~/.discord-ops.json so each agent gets only what it needs:
{
"projects": {
"my-app": {
"guild_id": "123456789012345678",
"channels": { "dev": "CHANNEL_ID", "alerts": "CHANNEL_ID" },
"tool_profile": "monitoring",
"tool_profile_add": ["send_message"],
"tool_profile_remove": ["list_members"]
}
}
}tool_profileâ base profile to use for this projecttool_profile_addâ add tools not included in the base profiletool_profile_removeâ remove tools from the base profile
discord-ops Start MCP server (stdio transport)
discord-ops serve Start MCP server (HTTP/SSE transport)
discord-ops run <tool> --args '{âĻ}' Run any tool directly (no AI/MCP required)
discord-ops setup Interactive setup wizard (single + multi-bot)
discord-ops health Run health check + permission audit
discord-ops validate Validate config without connecting to Discord
discord-ops --profile Load a built-in tool profile (monitoring/readonly/moderation/full)
discord-ops --tools Load specific tools by name (comma-separated)
discord-ops --dry-run Simulate destructive operations
discord-ops --help Show help
discord-ops --version Show version
The run subcommand executes any discord-ops tool directly from the shell â no MCP client, no AI. Pass all tool input as a single JSON string via --args.
# Send a plain message
npx discord-ops@latest run send_message \
--args '{"project":"my-app","channel":"general","content":"Deployment complete."}'
# Send a rich release announcement
npx discord-ops@latest run send_template \
--args '{
"project": "my-app",
"channel": "releases",
"template": "release",
"vars": {
"name": "my-app",
"version": "v1.2.0",
"author_name": "My Org",
"highlights": "âĸ New feature A\nâĸ Bug fix B",
"npm": "my-app@latest",
"npm_url": "https://www.npmjs.com/package/my-app",
"link": "https://github.com/my-org/my-app/releases/tag/v1.2.0",
"footer": "Published 2026-04-03"
}
}'Any tool name accepted by the MCP server works here â send_message, send_template, send_embed, list_channels, etc. The same input schema applies; validation errors are printed with field paths and exit code 1.
| Variable | Required | Description |
|---|---|---|
DISCORD_TOKEN |
No* | Default Discord bot token (*required unless all projects have token_env) |
DISCORD_OPS_TOKEN_ENV |
No | Override which env var holds the default token (default: DISCORD_TOKEN) |
<PROJECT>_TOKEN |
No | Per-project bot tokens (configured via token_env in project config) |
DISCORD_OPS_CONFIG |
No | Path to global config file, or inline JSON string (default: ~/.discord-ops.json) |
DISCORD_OPS_LOG_LEVEL |
No | debug, info, warn, error (default: info) |
DISCORD_OPS_DRY_RUN |
No | Enable dry-run mode (any truthy value) |
DRY_RUN |
No | Enable dry-run mode (any truthy value, alias) |
DISCORD_OPS_HTTP_TOKEN |
No | Bearer token for HTTP transport authentication (strongly recommended) |
- If
DISCORD_OPS_TOKEN_ENVis set, its value names the env var holding the default token (e.g.,DISCORD_OPS_TOKEN_ENV=MY_BOT_TOKENreadsMY_BOT_TOKEN). - Otherwise, the default token comes from
DISCORD_TOKEN. - Per-project tokens override the default: if a project config has
"token_env": "ORG_A_TOKEN", that project's bot usesORG_A_TOKEN. - If all projects have
token_envset with valid values, no default token is needed at all.
Use discord-ops run in GitHub Actions (or any CI) to post rich Discord notifications after a publish, deploy, or build â no AI agent required.
In CI you typically have one bot token and one project. Pass a minimal config as an inline JSON string via DISCORD_OPS_CONFIG. No file writing needed.
When storing as a GitHub secret, minify to a single line â multiline strings break secret injection. The shape (pretty-printed for readability):
{
"projects": {
"my-app": {
"guild_id": "123456789012345678",
"channels": {
"releases": "987654321098765432",
"builds": "111222333444555666"
},
"default_channel": "releases"
}
},
"default_project": "my-app"
}- No
token_envneeded â omitting it means the project usesDISCORD_TOKEN(the default). ownersandnotify_owners_onare optional â include them if you want owner pings on errors.- Channel values are Discord snowflake IDs. Channel names (aliases) resolve to these IDs.
Store two secrets in your repo:
BOOKED_DISCORD_BOT_TOKENâ your bot tokenDISCORD_OPS_CONFIGâ the config JSON minified to a single line (multiline strings break GitHub secrets)
{"projects":{"my-app":{"guild_id":"123456789012345678","channels":{"releases":"987654321098765432"},"default_channel":"releases"}},"default_project":"my-app"}
- name: Notify Discord
run: |
npx discord-ops@latest run send_template --args '{
"project": "my-app",
"channel": "releases",
"template": "release",
"vars": {
"name": "my-app",
"version": "${{ steps.version.outputs.version }}",
"author_name": "My Org",
"highlights": "${{ steps.changelog.outputs.highlights }}",
"npm": "my-app@latest",
"npm_url": "https://www.npmjs.com/package/my-app",
"link": "${{ steps.release.outputs.url }}",
"footer": "Published ${{ steps.date.outputs.date }}"
}
}'
env:
DISCORD_TOKEN: ${{ secrets.BOOKED_DISCORD_BOT_TOKEN }}
DISCORD_OPS_CONFIG: ${{ secrets.DISCORD_OPS_CONFIG }}DISCORD_TOKEN is the default token variable â no additional configuration needed. The bot token from your secret is used directly.
When running discord-ops serve, the HTTP endpoint is unauthenticated by default with a loud startup warning. Set DISCORD_OPS_HTTP_TOKEN to require bearer auth:
DISCORD_OPS_HTTP_TOKEN=your-secret-token discord-ops serve --port 3000All requests must include:
Authorization: Bearer your-secret-token
The health endpoint (GET /health) is always exempt from auth â load balancers and Docker healthchecks can reach it without a token.
Token comparison uses constant-time comparison to prevent timing attacks.
Enable dry-run to simulate destructive operations (delete, ban, kick, etc.) without actually calling the Discord API:
# Via CLI flag
discord-ops --dry-run
# Via environment variable
DISCORD_OPS_DRY_RUN=1 discord-ops
# Via env alias
DRY_RUN=true discord-opsIn dry-run mode, destructive tools return a simulated success response showing what would have happened.
23 built-in templates with cutting-edge Discord features. Use send_template with project routing.
Features across all templates:
- Author branding â every template has a configurable
author_name+author_iconat the top - Link buttons â clickable buttons below embeds (View Logs, Open PR, Runbook, etc.)
- Discord timestamps â dates auto-convert to each user's timezone with live countdowns
- Native polls â real Discord polls with progress bars and vote tracking
- Multi-embed dashboards â up to 10 embeds per message for service status boards
- Footer icons â status indicator icons (green/red) next to footer text
- Clickable titles â embed titles link directly to URLs
- Syntax-highlighted code â code examples with language-specific highlighting
- Progress bars â visual Unicode block progress indicators
| Template | Description | Key Features |
|---|---|---|
release |
Version release with install + link buttons | Author, link buttons, clickable title |
deploy |
Deploy success/failure with logs button | Footer icon, view/logs buttons |
ci_build |
CI result with build link button | Footer icon, clickable title |
incident |
Incident alert with severity colors | Discord timestamps, status page button |
incident_resolved |
Resolution with postmortem button | Discord timestamps, postmortem link |
maintenance |
Maintenance with live timezone countdowns | Discord timestamps, countdown, status button |
status_update |
Service status (operational/degraded/outage) | Footer icon, dashboard button |
review |
PR review with diff stats + PR button | Clickable title, additions/deletions |
dashboard |
Multi-embed service status board (up to 9 svc) | Multi-embed, per-service color cards |
oncall |
On-call handoff with shift timestamps | Discord timestamps, runbook button |
alert |
Configurable alert (info/warn/error/critical) | Level-based colors, metric thresholds |
| Template | Description | Key Features |
|---|---|---|
celebration |
Celebrate wins with images | Author, thumbnail, image |
welcome |
Welcome members with onboarding buttons | Discord timestamps, handbook/onboarding links |
shoutout |
Recognize work with avatar thumbnail | Thumbnail, nomination attribution |
quote |
Block-quoted inspirational text | Block quote formatting, author avatar |
announcement |
Announcement with deadline countdown | Discord timestamps, countdown, link button |
changelog |
Changelog with 7 section types | Deprecated, performance, security sections |
milestone |
Milestone with target date countdown | Discord timestamps, progress tracking |
tip |
Pro tip with syntax-highlighted code | Language-specific code blocks, doc button |
poll |
Native Discord poll with vote tracking | Native poll API, multiselect, duration |
progress |
Visual progress bar with deadline | Unicode progress bar, countdown |
standup |
Daily standup summary | Yesterday/today/blockers sections |
retro |
Sprint retrospective | Went-well/improve/actions, velocity |
Release announcement:
send_template({
template: "release",
vars: {
version: "v0.14.0",
name: "discord-ops",
highlights: "âĸ Owner pings\nâĸ Smart channel resolution\nâĸ Category channel editing",
npm: "npm install discord-ops@0.14.0",
npm_url: "https://www.npmjs.com/package/discord-ops/v/0.14.0",
link: "https://github.com/bookedsolidtech/discord-ops/pull/20",
footer: "Released April 3, 2026",
author_name: "Booked Solid Technology"
},
project: "my-app",
channel: "releases"
})
Native Discord Poll:
send_template({
template: "poll",
vars: {
question: "Best language for MCP servers?",
options: "TypeScript|Rust|Go|Python",
duration: "48",
multiselect: "true"
},
project: "my-app",
channel: "dev"
})
Multi-Embed Status Dashboard:
send_template({
template: "dashboard",
vars: {
services: "API|Database|CDN|Auth|Queue",
statuses: "operational|operational|degraded|operational|outage",
title: "Production Status",
url: "https://status.example.com"
},
project: "my-app",
channel: "alerts"
})
On-call handoff:
send_template({
template: "oncall",
vars: {
outgoing: "alice",
incoming: "bob",
shift_start: "2026-04-04T09:00:00Z",
notes: "Payment service latency elevated â watch grafana/d/payments",
active_incidents: "INC-342: elevated error rate on /checkout",
runbook_url: "https://wiki.example.com/oncall",
mention: "<@BOB_USER_ID>"
},
project: "my-app",
channel: "team-chat"
})
All templates support project routing (project, channel, notification_type, channel_id) and author branding (author_name, author_icon).
Full ~/.discord-ops.json schema with all options:
{
"projects": {
"my-app": {
"guild_id": "123456789012345678",
"token_env": "MY_APP_DISCORD_TOKEN",
"channels": {
"dev": "CHANNEL_ID",
"builds": "CHANNEL_ID",
"releases": "CHANNEL_ID",
"alerts": "CHANNEL_ID"
},
"default_channel": "dev"Release History
| Version | Changes | Urgency | Date |
|---|---|---|---|
| v0.23.3 | ### Patch Changes - 4faf83d: fix(release): pin npm to 11.5.1 for OIDC trusted-publishing auth The previous corepack-based install activated npm 10.9.7 (corepack's `npm@latest` alias is stale), which supports `--provenance` for sigstore attestation but lacks native trusted-publishing OIDC auth. The publish PUT to the registry therefore went out unauthenticated and was rejected with a misleading E404. Pinning to npm 11.5.1 ensures both provenance signing and TP auth fun | High | 5/20/2026 |
| v0.23.0 | ### Minor Changes - bbc870b: Multi-bot architecture: bot personas, per-channel bot assignment, and per-project tool profile enforcement. **Bot personas:** Named bots with identity metadata (`name`, `role`, `description`) configured in a top-level `bots` section. Each bot references a `token_env` and can have a `default_profile` restricting which tools it can use. **Channel-level bot assignment:** Channels accept `{ "id": "...", "bot": "bot-name" }` to override which bot operates in | High | 4/11/2026 |
| v0.22.0 | ### Minor Changes - a7f7218: Engineering audit remediation: 32 findings fixed across 5 epics. **Breaking:** `get_messages` now returns full embed and attachment objects instead of counts. `embeds` changes from `number` to `array`, `attachments` from `number` to `array`. Update consumers checking `embeds > 0` to `embeds.length > 0`. **New tools:** `send_template`, `list_templates`, `pin_message`, `unpin_message`, `notify_owners`, `get_invites` (42 â 48 tools). **CLI:** `setup`, | High | 4/11/2026 |
| v0.21.2 | ### Patch Changes - 58be846: Enforce `snowflakeId` schema on all Snowflake ID parameters (`channel_id`, `guild_id`, `author_id`) in `search_messages` and `send_embed` tools. Previously these used bare `z.string()` which lacked the `^\d{17,20}$` pattern validation, causing callers that pass 19-digit integer IDs to hit type coercion errors. | Medium | 4/11/2026 |
| v0.21.1 | ### Patch Changes - e34f143: Add messaging, channels, and webhooks tool profiles; consolidate duplicate filterTools implementation; improve HTTP transport branch coverage (70.45% â 88.88%) | Medium | 4/6/2026 |
| v0.21.0 | ### Minor Changes - b1d687e: Add `discord-ops init` CLI subcommand for non-interactive scaffolding of per-project `.discord-ops.json` config files. Accepts `--project`, `--guild-id`, `--token-env`, `--channel`, `--force`, and `--default` flags. Runs without a Discord connection. - b1d687e: Add `max_pages` parameter to `search_messages` (1â5, default 1). When set above 1, the tool paginates through results using the `before` cursor and returns up to `max_pages à 100` messages. Response now i | Medium | 4/6/2026 |
| v0.20.2 | ### Patch Changes - 09596d6: Fix port range validation, rate-limiter stats aggregation, and invite channel consistency - **CLI**: `--port` now rejects out-of-range values (negative, zero, >65535) with a clear message; previously only `NaN` and falsy values were caught - **RateLimiter.stats()**: returns the max-used bucket instead of the sum across all buckets, making `used/limit` a meaningful per-bucket pressure ratio rather than a misleading aggregate - **create_invite**: s | Medium | 4/3/2026 |
| v0.20.1 | ### Patch Changes - 5f65f23: Security hardening and type safety improvements (v0.20.0 audit follow-up) **SSRF protection (og-fetch)** - Block HTTP redirect following â `redirect: "manual"` prevents redirects to private IPs - Full `127.0.0.0/8` loopback range blocked (was only `127.0.0.1`) - IPv4-mapped IPv6 addresses (`::ffff:x.x.x.x`) now blocked, handling both dotted-decimal and hex-normalized URL parser forms - OG regex patterns pre-compiled at module load ins | Medium | 4/3/2026 |
| v0.20.0 | ### Minor Changes - 02f55c7: Security hardening and structural cleanup (v0.20.0) **Security fixes:** - H-1: Add `requiresGuild: true` to `delete_channel`, `edit_channel`, `set_slowmode`, `set_permissions`, `purge_messages`, and `create_invite` â enables permission pre-flight checks for channel-targeting tools - H-1: Extend server permission pre-check to resolve guild from `channel_id` (not just `guild_id`) so channel tools also get bot-permission enforcement - H-2: Rem | Medium | 4/3/2026 |
| v0.19.0 | ### Minor Changes - f9c9bd0: Comprehensive security, type safety, and correctness audit fixes (5-specialist review) **Security** - Fix shell injection in release workflow: use `execFileSync` argv array instead of shell string interpolation for Discord notification step - Remove unused `id-token: write` permission from release workflow - Guard `PUBLISHED_PACKAGES` JSON parse in CI notify script to prevent spurious workflow failures - Inline `DISCORD_OPS_CONFIG` JS | Medium | 4/3/2026 |
| v0.18.0 | ### Minor Changes - 637362e: feat: add move_channel and notify_owners tools - `move_channel` â reposition a channel or category relative to another channel using `before_id`/`after_id` instead of fragile raw position integers. Resolves sibling positions automatically. - `notify_owners` â standalone owner ping tool. Sends `<@mention>`s to a channel based on `notification_type` without a full message or embed. No-ops silently if the type isn't in the project's `notify_owners_on` lis | Medium | 4/3/2026 |
| v0.17.0 | ### Minor Changes - 350cb59: feat: add `run` CLI subcommand and inline JSON support for `DISCORD_OPS_CONFIG` - `discord-ops run <tool> --args '<json>'` â execute any tool directly from shell, no AI/MCP required. Supports the full tool suite including `send_template`, `send_message`, `send_embed`, and all 46 tools. - `DISCORD_OPS_CONFIG` now accepts an inline JSON string in addition to a file path â if the value starts with `{` it is parsed directly. Eliminates the need to write co | Medium | 4/3/2026 |
| v0.16.0 | ### Minor Changes - b42d0fe: feat: add `run` CLI subcommand and inline JSON support for `DISCORD_OPS_CONFIG` - `discord-ops run <tool> --args '<json>'` â execute any tool directly from shell, no AI/MCP required. Supports the full tool suite including `send_template`, `send_message`, `send_embed`, and all 46 tools. - `DISCORD_OPS_CONFIG` now accepts an inline JSON string in addition to a file path â if the value starts with `{` it is parsed directly. Eliminates the need to write co | Medium | 4/3/2026 |
| v0.15.0 | ### Minor Changes - 01c3e44: feat: add `run` CLI subcommand and inline JSON support for `DISCORD_OPS_CONFIG` - `discord-ops run <tool> --args '<json>'` â execute any tool directly from shell, no AI/MCP required. Supports the full tool suite including `send_template`, `send_message`, `send_embed`, and all 46 tools. - `DISCORD_OPS_CONFIG` now accepts an inline JSON string in addition to a file path â if the value starts with `{` it is parsed directly. Eliminates the need to write co | Medium | 4/3/2026 |
| v0.14.3 | ### Patch Changes - 6bd5a54: fix: add "alert" to NotificationType enum â global config with alert in notify_owners_on caused config parse failure and MCP startup crash - 6bd5a54: Fix stale npx cache â use `discord-ops@latest` in MCP config Without `@latest`, npx may serve a cached older version indefinitely, causing MCP clients to run stale code even after new releases are published. All MCP config examples in the README updated to use `discord-ops@latest`. | Medium | 4/3/2026 |
| v0.14.2 | ### Patch Changes - 9fb0d4b: Fix stale npx cache â use `discord-ops@latest` in MCP config Without `@latest`, npx may serve a cached older version indefinitely, causing MCP clients to run stale code even after new releases are published. All MCP config examples in the README updated to use `discord-ops@latest`. | Medium | 4/3/2026 |
| v0.14.1 | ### Patch Changes - a17e500: Comprehensive README documentation update - Document `send_embed` tool (OG metadata unfurling with field overrides) - Document auto-embed behavior for `send_message` and `raw: true` opt-out - Document tool profiles (`--profile`, `--tools`, built-in profiles, per-project config) - Document owner pings (`owners`, `notify_owners_on`, safety behavior for "dev") - Document smart channel resolution (4-layer: alias â fuzzy â Discord API â er | Medium | 4/3/2026 |
| v0.14.0 | ### Minor Changes - b4dafe4: Auto-embed for send_message and health endpoint version field - **Auto-embed for `send_message`**: Messages are now automatically wrapped in a polished embed (color bar, description, timestamp) via the new `simple` template. Set `raw: true` to send plain text. Every message looks professional by default. - **`simple` template**: New minimal utility template â just a branded embed with optional title, color, author, and footer. Used automatically by `se | Medium | 4/3/2026 |
| v0.13.0 | ### Minor Changes - 4928e5e: Auto-embed for send_message and health endpoint version field - **Auto-embed for `send_message`**: Messages are now automatically wrapped in a polished embed (color bar, description, timestamp) via the new `simple` template. Set `raw: true` to send plain text. Every message looks professional by default. - **`simple` template**: New minimal utility template â just a branded embed with optional title, color, author, and footer. Used automatically by `se | Medium | 4/3/2026 |
| v0.12.0 | ### Minor Changes - e70b6ac: Auto-embed for send_message and health endpoint version field - **Auto-embed for `send_message`**: Messages are now automatically wrapped in a polished embed (color bar, description, timestamp) via the new `simple` template. Set `raw: true` to send plain text. Every message looks professional by default. - **`simple` template**: New minimal utility template â just a branded embed with optional title, color, author, and footer. Used automatically by `se | Medium | 4/3/2026 |
| v0.11.0 | ### Minor Changes - c53ebcd: Auto-embed for send_message and health endpoint version field - **Auto-embed for `send_message`**: Messages are now automatically wrapped in a polished embed (color bar, description, timestamp) via the new `simple` template. Set `raw: true` to send plain text. Every message looks professional by default. - **`simple` template**: New minimal utility template â just a branded embed with optional title, color, author, and footer. Used automatically by `se | Medium | 4/2/2026 |
| v0.10.0 | ### Minor Changes - 6c4ef2a: Auto-embed for send_message and health endpoint version field - **Auto-embed for `send_message`**: Messages are now automatically wrapped in a polished embed (color bar, description, timestamp) via the new `simple` template. Set `raw: true` to send plain text. Every message looks professional by default. - **`simple` template**: New minimal utility template â just a branded embed with optional title, color, author, and footer. Used automatically by `se | Medium | 4/2/2026 |
| v0.9.0 | ### Minor Changes - dc744af: Slim mode, enhanced health endpoint, and ISO 8601 timestamp support - **Tool profiles (`--profile`, `--tools`)**: Load only the tools an agent needs. Four built-in profiles â `monitoring` (6 tools), `readonly` (6), `moderation` (7), `full` (all 45) â slash schema overhead by 85%. Also supports `--tools "t1,t2"` for explicit tool selection. Profile can be set via CLI flags, per-project config, or global config. - **Customizable profiles (`tool_profile_a | Medium | 4/2/2026 |
| v0.8.0 | ### Minor Changes - 9979177: Security hardening across the entire codebase - **HTTP transport authentication**: Bearer token auth via `DISCORD_OPS_HTTP_TOKEN` env var with constant-time comparison. Warns loudly when running unauthenticated. Health endpoint exempt for load balancer probes. - **Snowflake ID validation**: All 17 tool files now use the shared `snowflakeId` regex validator (`/^\d{17,20}$/`) for channel_id, guild_id, user_id, message_id, reply_to, before, after, and par | Medium | 3/31/2026 |
| v0.7.1 | ### Patch Changes - 2a752af: Type safety and setup validation improvements - **Tightened TypeScript types**: replaced `any` with proper discord.js types (`MessageReaction`, `Webhook`, `WebhookCollection`) in get-messages and list-webhooks tools - **Token format validation**: setup wizard now validates Discord bot token format (three dot-separated segments) in addition to length check, catching bad tokens before the 10s connection timeout | Medium | 3/31/2026 |
| v0.7.0 | ### Minor Changes - c24d139: Cutting-edge template system upgrade â 23 templates with native Discord features - **6 new templates**: dashboard (multi-embed status board), progress (visual bar), oncall (shift handoff), standup (daily summary), retro (sprint retrospective), alert (configurable levels) - **Native Discord polls**: `poll` template now uses Discord's poll API with progress bars, vote tracking, multiselect, and duration (replaces emoji-based fallback) - **Link butt | Medium | 3/31/2026 |
| v0.6.0 | ### Minor Changes - 5a05546: Message template system with 17 built-in Discord embed templates - `send_template` tool â send styled embeds using project routing (45 tools total) - `list_templates` tool â discover templates with required/optional variables - DevOps templates: release, deploy, ci_build, incident, incident_resolved, maintenance, status_update, review - Team templates: celebration, welcome, shoutout, quote, announcement, changelog, milestone, tip, poll | Medium | 3/31/2026 |
| v0.5.0 | ### Minor Changes - cd129a4: Multi-organization hardening release - Dynamic version from package.json (server + CLI no longer hardcode version strings) - `discord-ops validate` command â checks config without connecting to Discord (duplicate guilds, missing tokens, invalid refs) - `list_projects` tool (43 total) â exposes project routing, token status, and validation to AI agents - Health check hardened â per-guild try-catch, per-project token status reporting, config | Medium | 3/31/2026 |
| v0.4.0 | ### Minor Changes - e335fdf: Flexible token configuration for multi-org MCP setups - `DISCORD_OPS_TOKEN_ENV` meta-variable lets you point the default token at any env var (not just `DISCORD_TOKEN`) - Default token is now optional when all projects specify `token_env` â enables multi-org setups where each project uses its own bot - Improved error messages when token resolution fails - Updated README with multi-org setup examples and token resolution documentation | Medium | 3/31/2026 |
| v0.3.0 | ### Minor Changes - 6e421f2: Add 8 new tools (pin/unpin message, search messages, archive thread, set channel permissions, get/create invites), HTTP/SSE transport, dry-run mode for destructive operations, and interactive CLI setup wizard. | Medium | 3/31/2026 |
