Skip to content

Safe guild blueprints

Use the blueprint workflow when the request describes a complete server rather than one isolated Discord operation. Request-only target resolution requires the selected caller profile to lock one DISCORD_EXPECTED_BOT_ID and an ALLOWED_GUILDS boundary. A small model needs only the natural-language request when DISCORD_DEFAULT_GUILD_ID is itself allowlisted, or when exactly one allowlisted guild exists; discord-mcp resolves that target, owns template selection, safe compilation, live reconciliation, and operation ordering. If a profile allows several guilds without an allowlisted default, plan fails closed until the caller supplies one explicit guild_id.

  1. On the recommended progressive surface, call build_discord_server with request; it is the directly advertised alias of canonical guild_blueprint_plan, which remains the name on the full surface and in search results. It resolves expected_bot_id from the locked profile and resolves guild_id only from an allowlisted default or exactly one allowlisted guild. Explicit target IDs remain available for multi-guild profiles. This is a target-bound dry-run that does not mutate Discord.
  2. Present its source evidence, blockers, operation summary, risks, and approval_id for review.
  3. After approval, call guild_blueprint_apply with the unchanged caller-local plan_ref, the same IDs and approval_id, __confirm:true, and an optional operation_budget from 1 through 50. Portable and older clients may instead pass the legacy self-contained plan_token, but never pass both.
  4. If apply returns partial or busy, retry the same apply call. If it returns stale or blocked with next_action: replan, create and review a new plan.
  5. A terminal complete or already_current result contains evidence.activity.evidence_id. At any later time, call guild_blueprint_evidence with the same guild_id, expected_bot_id, and plan_id for read-only revalidation. It does not accept plan_token, __confirm, or a Discord token argument.

The preferred reference is a short deterministic HMAC bound to the active caller deployment. It is only a local locator, not standalone authorization: apply still requires the exact guild, bot, approval, caller boundary, and confirmation. The referenced payload is stored in an authenticated private local envelope; the raw legacy token is never written there. The legacy token contains the compressed blueprint and is authenticated, not encrypted, so keep it private. discord-mcp redacts credential-shaped *_token arguments from audit events and never writes a raw plan token into local state.

The planner selects one live-verified primary template and zero through three bounded inspirations, then discards all source IDs, names, descriptions, permissions, and overwrites. The trusted compiler regenerates the desired guild settings, roles, channel overwrites, ordering, Welcome Screen, Onboarding, AutoMod rules, and Components V2 publications.

Every inspected live template is audit-bound to a canonical SHA-256 digest, fetch timestamp, and safe source-guild metadata. Selected candidates carry that provenance; rejected candidates retain explicit reasons and provenance when a valid live response existed. The catalog version binds the local candidate set. These records prove which Discord response informed selection without promoting third-party text or permission bitfields into trusted blueprint state.

Before returning an apply reference or token, plan verifies:

  • the active Discord credential matches DISCORD_EXPECTED_BOT_ID;
  • the resolved or explicit guild is in ALLOWED_GUILDS, with ambiguity rejected;
  • the live bot permissions and role hierarchy can perform every planned step;
  • existing same-name resources are exact, unique safe matches;
  • no bound resource has drifted; and
  • the preview is bound to a digest of the live Discord target.

Plan never mutates Discord and never writes local checkpoint state. It may idempotently cache one authenticated plan envelope so the short plan_ref survives an MCP process restart. If that private write is unavailable, plan returns plan_ref: null, keeps the compatible plan_token, and reports a warning instead of weakening verification.

Apply acquires both a guild-wide lock and a plan lock, then re-reads and reconciles Discord at the start of every call or resume. It saves an authenticated append-only local checkpoint after every successful operation and performs an independent final readback. If the process or Discord fails, the next call adopts exact Discord matches and continues from verified state; it does not blindly replay the previous request. The only bounded exception is a Discord-protected singleton AutoMod slot: the reconciler may bind the sole same-trigger rule when its creator_id is the exact caller-owned bot, then update it to the approved blueprint. Foreign-owned or ambiguous singleton rules block before mutation.

The first apply must still match the snapshot shown by plan. Once a checkpoint is complete, that approval is terminal. Repeating it while Discord still matches returns already_current; repeating it after external drift returns PLAN_ALREADY_CONSUMED and performs no mutation. Repairing later drift always requires a new preview and approval.

The executor never deletes resources, grants new permissions to its own bot, or guesses through duplicate or mismatched resources. Community, Welcome Screen, and Onboarding are full-replacement Discord surfaces, so they remain explicitly confirmed high-risk operations.

Components V2 publications use a deterministic marker plus Discord nonce. The readback paginates through a bounded message history and fails closed if it cannot prove that an older managed copy is absent. Dedicated announcement and rules channels keep this verification both fast and complete.

Plan envelopes, checkpoints, and Activity Evidence default to the platform user-state directory. Set MCP_BLUEPRINT_STATE_DIR to a persistent private directory for containers or tests. A plan envelope contains the trusted payload needed to resolve plan_ref; checkpoints contain target IDs and bindings; the completion-only evidence sidecar retains the trusted blueprint for later read-only verification. These files never contain the bot token or raw plan_token, but blueprint names and Components V2 content are local application data, so keep the directory private. File integrity is authenticated with the active caller secret.

The local reference cache is bounded to 256 records and 64 MiB. Once either limit is reached, planning still returns a usable legacy plan_token with plan_ref: null; it does not evict a possibly resumable plan automatically. Operators may archive or remove reference records only for plans they know are completed or intentionally abandoned. This limit is per configured state directory, so separate caller deployments should use separate directories.

Rotating DISCORD_TOKEN invalidates pending plan references, legacy plan tokens, checkpoints, and saved Activity Evidence by design. Finish or intentionally abandon pending work, rotate the credential, restart the MCP process once, and create a fresh plan. For HTTP deployments, rotating DISCORD_MCP_ACCESS_TOKEN also invalidates HTTP-minted state because the bearer credential defines that deployment’s caller principal. Old sidecars are not migrated across caller boundaries; a fresh plan/apply creates fresh evidence.

One stdio process/profile is one trusted caller boundary. One HTTP bearer token is likewise one caller principal: do not give the same endpoint credential and bot to mutually untrusted users. Use separate deployments, access tokens, bot profiles, and state directories instead.

A caller-local plan_ref also requires every process that may receive the resume call to share the same protected state directory. For a stateless multi-replica deployment without correctly shared and locked storage, use the legacy self-contained plan_token; a missing local reference fails closed and never falls back to another plan.

A successful final readback persists one immutable, authenticated activity-evidence.json sidecar for the plan. Apply returns only its ID, timestamp, trusted blueprint policy invariants, and execution/readback record; it never returns the stored full blueprint or a credential. Policy invariants are labeled separately from live observations: the latter bind the initial and final snapshot IDs, completed operations, exact resource bindings, and successful zero-drift blueprint readback. Partial, stale, busy, blocked, and drifted apply results always return activity: null.

guild_blueprint_evidence validates the caller boundary and local HMAC before any Discord request, verifies the exact bot, reads the current guild, and runs the same pure reconciler without acquiring an apply lock or writing state. Its statuses are:

  • verified: the current Discord state still conforms to the evidenced blueprint;
  • drifted: safe operations or blockers remain against the blueprint;
  • not_found: no completion evidence exists for that plan in this caller profile; and
  • blocked: the caller, target, local proof, identity, or Discord readback cannot be verified safely.

verification.snapshot_unchanged separately reports whether the whole-guild snapshot digest is identical to the completion snapshot. It can be false while status remains verified when unrelated additive state changed but every blueprint requirement still matches. The tool never repairs drift; create a fresh preview and approval before any later mutation.

See Access and scope for the required identity and guild locks, and Confirmation model for the server-side __confirm boundary.