Releases: bookedsolidtech/discord-ops
Release list
v0.25.0
Minor Changes
- 5e85094:
discord-ops setupnow writes.mcp.jsonand configures the coordination board.- The wizard offers to write (or merge into) a
.mcp.jsonin the current
directory, so Claude Code loads discord-ops for the project with one command
— no hand-editing. It references the project's token env var by name (never a
raw token) and preserves any other MCP servers already configured. - Each project can pick a coordination note-board channel during setup; the
wizard shows the channel the smart fallback resolves to and only writes an
explicitboard_channelwhen you override it.
- The wizard offers to write (or merge into) a
v0.24.0
Minor Changes
-
8a589a6: Add read-side primitives for bi-directional agent-to-agent communication over Discord.
get_message— fetch a single message by ID with reply linkage (reply_to), pin state, edit timestamp, thread info (has_thread/thread_id), and the author's bot flag, so an agent can re-check a message it posted earlier.get_reactions— read who reacted to a message and with what emoji (with per-user bot flags), so an agent can detect acks, claims, and blocks left by humans or peer agents. Supports an emoji filter and a per-emoji user limit.get_replies— collect direct replies to a message by scanning messages posted after it, withscanned/last_scanned_idpagination fields and anafterresume cursor.get_messagesnow includesreply_toon every returned message so reply chains are visible in bulk reads.send_messagenow returnsmessage_id(alongside the existingid) so agents can capture it and poll for responses later.- The new read tools are included in the
monitoring,readonly, andmessagingtool profiles.
-
ad0831a: Coordination platform expansion: four new tool suites (15 tools, 67 total).
- Personas — one bot, unlimited posting identities via per-message webhook
overrides:create_persona,send_as,list_personas. No new bot
applications, no developer-portal work; webhook tokens never appear in
results. - Polls — native Discord polls as structured consensus:
send_poll,
get_poll_results(per-answer voter lists with bot flags and pagination),
end_poll(idempotent, destructive-flagged). - Workflow —
forward_message(immutable cross-channel snapshots for
escalation) and forum channels as agent work queues:create_forum_post,
list_forum_posts,update_forum_post(tags by name as status labels,
archive to close). - Botops — bot self-management without the developer portal:
set_bot_nick,update_application(description, SSRF-guarded icon
upload, tags, install params), and application emoji CRUD returning
add_reaction-ready identifiers. - Profiles:
monitoringgainssend_as/send_poll/get_poll_results;
readonlygains the new read tools;messaginggains
send_as/forward_message.
- Personas — one bot, unlimited posting identities via per-message webhook
-
e943136: Real-time event sidecar and security hardening for the coordination suites.
- Gateway sidecar —
discord-ops listenopens a gateway connection per bot
token and streamsmessage_create/reaction_addevents to a local JSONL
sink; the newget_eventstool reads that sink with alast_seqcursor
(mirroringget_replies) and zero Discord API calls per poll, reporting
sink_activeso agents fall back toget_messageswhen the sidecar is down.
The sink is created0600(dir0700), refuses symlinked or traversal
paths, and writes oneJSON.stringifyobject per line. - Botops —
interactions_endpoint_urlis removed fromupdate_application
(a control-plane field an agent must never be able to redirect); the tool now
documents that its edits are application-global. - Personas — reserved authority names (
system,admin,moderator,
owner, …) are blocked, with zero-width/obfuscation normalization, to blunt
impersonation. - Sanitizer — the webhook-URL redaction now covers
discordapp.com,
canary/ptb subdomains, and version-prefixed API paths, and returned (not just
thrown) error results are sanitized before they reach the audit log.
- Gateway sidecar —
-
a74fd13: Project note board — directed, durable note-passing across concurrent sessions.
leave_noteposts a directed note to a project's shared board channel,
addressed to a session id, a role/name, orall, with tags for filtering.get_notesreads the board with recipient/sender/tag/unresolved filters
and anaftercursor — the first call an agent makes on startup to pick up
hand-offs and see what other sessions have done.resolve_notemarks a note handled (✅) with an optional reply, so open
hand-offs drop off the unresolved list.list_sessionsreports which sessions are active on a project (derived
from note activity), so concurrent sessions on the same codebase can
coordinate instead of colliding.- New
board_channelproject config field selects the board, with a fallback
chain (board/agent-boardalias →agent-logs→backchannel→
default_channel). Session identity comes fromDISCORD_OPS_SESSIONor an
auto-derived id. The note tools are included in themonitoringprofile. - New
docs/project-onboarding.mdwalks a project through adopting the full
system.
Patch Changes
-
5f4cdcc: Fix validateConfig() treating missing token_env as fatal error in multi-org setups
When a global ~/.discord-ops.json contains projects from multiple organizations, the server
previously failed to start if any project had a missing token_env — even if that project
belonged to a completely different org irrelevant to the current context.loadConfig()now warns about unavailable projects and starts successfully as long as at
least one project has a valid token. Only if all projects lack tokens does startup fail.validateConfig()likewise downgrades per-project missing token_env from errors to warnings,
since the server can still serve other projects.
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@latestalias is stale), which supports--provenancefor 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 function correctly.
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-levelbotssection. Each bot references atoken_envand can have adefault_profilerestricting which tools it can use.Channel-level bot assignment: Channels accept
{ "id": "...", "bot": "bot-name" }to override which bot operates in that channel. Token resolution follows: channel bot → project bot → projecttoken_env→ default token.Per-project tool profile enforcement: Runtime gate in the MCP server checks per-project
tool_profileand per-botdefault_profileon every tool call. Tools not in the effective profile return an error. Supportsprofile_add/profile_removeoverrides.New tool:
list_bots— returns all configured bot personas with project assignments, channel overrides, and connection status. Does not expose token values.Validation:
discord-ops validatenow checks bot references, profile names, and bot token availability.Backwards compatible — existing configs without
botswork unchanged. Channels accept both plain snowflake strings and{ id, bot }objects.
v0.22.0
Minor Changes
-
a7f7218: Engineering audit remediation: 32 findings fixed across 5 epics.
Breaking:
get_messagesnow returns full embed and attachment objects instead of counts.embedschanges fromnumbertoarray,attachmentsfromnumbertoarray. Update consumers checkingembeds > 0toembeds.length > 0.New tools:
send_template,list_templates,pin_message,unpin_message,notify_owners,get_invites(42 → 48 tools).CLI:
setup,run,validatesubcommands now wired. Flags--allow-unauthenticated,--profile,--tools,--dry-run,--argsaccepted. Version reads from package.json dynamically.Bug fixes:
list_threadsrespectsarchivedparam.timeout_membermarked destructive.resolveTargetreturnsundefinedinstead of""for missing guild. Permission pre-flight logs errors.Security: SSRF DNS pinning prevents rebinding. Template URL vars validated. X-Forwarded-For parsed right-to-left. Per-project rate limit buckets. Extended reserved IP blocking. Token hashing for cache keys. IP counter hard cap.
Code quality: Dead code removed. TTLCache max-size eviction. Profile tool name validation at startup. Rate limiter bucket pruning.
v0.21.2
Patch Changes
- 58be846: Enforce
snowflakeIdschema on all Snowflake ID parameters (channel_id,guild_id,author_id) insearch_messagesandsend_embedtools. Previously these used barez.string()which lacked the^\d{17,20}$pattern validation, causing callers that pass 19-digit integer IDs to hit type coercion errors.
v0.21.1
v0.21.0
Minor Changes
- b1d687e: Add
discord-ops initCLI subcommand for non-interactive scaffolding of per-project.discord-ops.jsonconfig files. Accepts--project,--guild-id,--token-env,--channel,--force, and--defaultflags. Runs without a Discord connection. - b1d687e: Add
max_pagesparameter tosearch_messages(1–5, default 1). When set above 1, the tool paginates through results using thebeforecursor and returns up tomax_pages × 100messages. Response now includeshas_more: booleanto indicate further pages exist. - b1d687e: Add
initial_messageparameter tocreate_thread. When provided, the message is posted into the thread immediately after creation, eliminating the need for a follow-upsend_messagecall.
Patch Changes
- f5441f0: Fix audit log redaction to recurse into arrays of objects. Previously, sensitive keys inside array elements (e.g. embed fields with a
tokenorwebhook_urlkey) passed through unredacted. - 774d97b: Restrict CORS
Access-Control-Allow-Originfrom wildcard*tohttp://localhostby default. The HTTP transport now accepts anallowedOriginoption (and--allowed-originCLI flag) to configure the allowed origin explicitly, preventing arbitrary web pages from fingerprinting the/healthendpoint or establishing unauthenticated SSE connections. - 3e7faab: Make
guild_idoptional inexecute_webhook. The parameter was declared required but never used in the handler body. Callers that omit it no longer receive a validation error, while existing callers that provide it continue to work unchanged. - b1d687e: Enforce auth token requirement on HTTP serve mode startup. The server now refuses to start without
DISCORD_OPS_HTTP_TOKENunless--allow-unauthenticatedis explicitly passed, preventing accidental unauthenticated exposure of the bot API. - bcc2627: Add
trustProxyoption to HTTP transport for proxy-aware per-IP rate limiting. When enabled, extracts the real client IP from the leftmost non-private address inX-Forwarded-Forinstead of usingreq.socket.remoteAddress. Defaults tofalseso existing behavior is unchanged. - eca9978: Add standard security response headers to HTTP transport. All non-OPTIONS responses now include
X-Content-Type-Options: nosniff,X-Frame-Options: DENY,Content-Security-Policy: default-src 'none', andReferrer-Policy: no-referrer. OPTIONS preflight responses are intentionally excluded. - 3b82f09: Fix SSRF vulnerability in og-fetch: resolve hostname via DNS before fetching and re-validate the resolved IP against the blocklist to prevent DNS rebinding attacks. Also adds 100.64.0.0/10 (shared address space) and 198.18.0.0/15 (benchmarking) to the blocked ranges.
- c27e2f4: Validate embed image and URL fields against private/reserved IP ranges before passing to Discord API. Caller-supplied
image_urlinsend_embedand all URL fields inexecute_webhookembeds (url, image.url, thumbnail.url, author.url, author.icon_url, avatar_url) are now checked withisPublicHttpUrl()to prevent SSRF via Discord's CDN proxy.
v0.20.2
Patch Changes
- 09596d6: Fix port range validation, rate-limiter stats aggregation, and invite channel consistency
- CLI:
--portnow rejects out-of-range values (negative, zero, >65535) with a clear message; previously onlyNaNand falsy values were caught - RateLimiter.stats(): returns the max-used bucket instead of the sum across all buckets, making
used/limita meaningful per-bucket pressure ratio rather than a misleading aggregate - create_invite: switched from
getChannel(text-only) togetAnyChannelso voice and stage channels are now supported; aligns with the server-layer permission pre-check which already usedgetAnyChannel - SSRF: block
0.0.0.0(unspecified IPv4, routes to localhost on many systems) and::(IPv6 unspecified address) in OG metadata fetcher
- CLI:
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/8loopback range blocked (was only127.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 instead of per-call
Type safety
- All 48 tool definitions migrated to
defineTool<T>()factory —handleinput parameters are now strongly typed viaz.infer<TSchema> - Fixed 4 latent type bugs exposed by stricter typing:
notification_typefields now useNotificationTypeenum; rolecolorcorrectly cast toColorResolvable
Config resilience
loadGlobalConfig()now throws on corrupt/invalid config files instead of silently returning empty config
HTTP transport
ipCountersmap pruned every 60s to prevent unbounded memory growth from unique IPs
Test coverage
- New
server-perm-check.test.ts: 6 tests covering the perm pre-flight block inserver.ts TOKEN_ENVformat validation tests (8 cases)- Audit redaction assertion fixed — now verifies
[REDACTED]appears in log output - 10 new og-fetch tests: redirect blocking,
127.x.x.xrange, IPv4-mapped IPv6,::1
- Block HTTP redirect following —