Overview
Architecture overview
Section titled “Architecture overview”discord-mcp is a layered server: a Sapphire-style piece registry owns the 192 tools, a Koa-style middleware chain wraps every call with cross-cutting concerns, a resilient REST adapter sits between the tools and Discord’s HTTP API, and an audit + telemetry layer observes the whole thing. This page is the map; the rest of the Architecture section is the detail.
The call path
Section titled “The call path”graph TD Client[MCP client] --> Transport[InMemoryTransport / stdio] Transport --> Server[McpServer] Server --> MW[Middleware chain] MW --> Tool[Tool piece] Tool --> REST[Resilient REST adapter] REST --> Discord[Discord REST API] Tool -. optional .-> Sampling[Client LLM via sampling] Server -. optional .-> Gateway[Gateway client] Gateway --> DiscordWS[Discord Gateway WebSocket]What each box does:
- MCP client — Claude Desktop, Claude Code, Cursor, or any spec-compliant
client. Sends JSON-RPC
tools/callover stdio. - Transport —
stdioin production;InMemoryTransportin tests. Owns the JSON-RPC framing; never sees tool arguments. - McpServer — the bridge between MCP protocol and the piece registry. Hosts middleware, tool, precondition, and resource stores.
- Middleware chain — four layers (telemetry, validate, precondition, audit) that wrap every call. See Middleware chain.
- Tool piece — one of the 192 tool implementations. Calls Discord via the REST adapter or (rarely) calls the client back via sampling.
- REST adapter — wraps
@discordjs/restin Cockatiel resilience policies. See Operations → Resilience and Architecture → Rate limits. - Gateway client (optional) —
discord.jslazy-loaded when the--gatewayflag is set. Enables 5 subscribable resource URIs. See Architecture → Gateway.
The piece model (Sapphire-inspired)
Section titled “The piece model (Sapphire-inspired)”discord-mcp borrows the Sapphire framework’s “piece + store” model:
- Pieces are the unit of registration: a
Tool, aPrecondition, aResource. Each piece extends a base class and is auto-discovered from a directory tree at boot. - Stores hold pieces of one type and expose lookup by name. The server
has
ToolStore,PreconditionStore,ResourceStore,MiddlewareStore,GatewayHandlerStore, andSamplingStore. - Container is the global registry that ties stores together. Pieces can reach sibling pieces via the container without import cycles.
This is conventional in Sapphire bots; we adopted it because it gives us
auto-discovery (drop a file in tools/ and it’s registered) without
hand-maintained import lists.
Source:
packages/mcp-core/src/server.ts,
pieces/Tool.ts,
stores/ToolStore.ts.
What’s where in the repo
Section titled “What’s where in the repo”packages/ mcp-core/ # All tool definitions, middleware, resilience policy src/ tools/ # 192 tools across ~20 categories middleware/ # 4 middleware layers + compose preconditions/ # ConfirmRequired, CategoryEnabled pieces/ # Base classes for Tool, Precondition, Resource stores/ # Auto-discovery containers rest/ # Cockatiel-wrapped @discordjs/rest gateway/ # discord.js gateway client (optional) pipeline/ # mcp_pipeline executor + interpolation errors/ # Error hierarchy + formatErrorForUser audit/ # Audit middleware + sinks + redaction telemetry/ # OTel SDK boot + custom metrics mcp-server/ # Thin CLI + stdio transport wiring src/ transports/ # stdio, future http otel.ts # OTel SDK lifecyclemcp-core has no transport assumptions — it’s a library that anything could
host. mcp-server is the thin shell that wires it to stdio + OTel + signal
handling.
Why this shape
Section titled “Why this shape”The pieces below split the inheritance into explainable units:
- Middleware chain — the four layers and why their order matters.
- Components V2 — the rich layout primitives and their schema validator.
- Pipeline — atomic multi-tool calls.
- Sampling — the zero-API-key intelligence path.
- Rate limits — Discord’s quota model, our retry/bulkhead response.
- Gateway — optional WebSocket subscriptions.
- Error handling — error hierarchy, recovery hints, Cockatiel mapping.
- Confirmation — the
__confirm:true+MCP_DRY_RUN=falsecontract.