Brand Guide MCP — PRD
Status: Living doc — last refresh 2026-04-27 (originally drafted 2026-04-21) Author: Eric Downs (Technical Director, Grain & Mortar) First tenant: California Forever (live since 2026-04-21) Second tenant (dogfood): Grain & Mortar (live since 2026-04-26)
1. Problem Statement
Brand guides ship as PDFs, Notion pages, or static websites. Once delivered, they become reference material that clients rarely open. The knowledge inside — colors, type scale, voice rules, logo usage, messaging pillars — is locked away from the tools teams actually work in.
The result: internal teams guess at brand values, copy drifts off-voice, designers ask "what's our navy hex again?" in Slack, developers hardcode hex values they think are right, and the brand guide G&M built erodes the moment it's delivered.
2. Product Vision
The brand guide as a service. Grain & Mortar operates a single multi-tenant MCP server that holds every client's brand guide in a structured, queryable form. Clients pay a monthly retainer for access. Different surfaces — Slack, Claude Code, Codex, design-tool skills — sit on top of the same MCP, and each client toggles on the surfaces their team actually uses.
The MCP is the product. Surfaces are how the product reaches a client's team:
- Designers want the brand guide inside the design tools they live in (Figma-equivalent, Claude Design, Claude Code) so they can see the current palette, type scale, and assets without context-switching.
- Developers want it in Claude Code and Codex so the LLM tools they use to write code stay on-brand.
- Marketing / content / executive teams want it conversationally in Slack via
@brandbot, where they already work.
Every surface pulls from the same source — each client's own brand guide site (/api/brand/*). G&M operates the MCP, owns the per-tenant subscription state, gates access, attributes cost, and bills.
For Grain & Mortar, this turns a one-time deliverable into a recurring product relationship — a retainer-worthy surface we own, with a clear upgrade path as more surfaces come online.
Design principle: premium brand agent, not chat log
Visual fidelity of the bot's responses is a product differentiator, not decoration. Two otherwise-identical MCP servers will feel like entirely different products if one returns a wall of plain text and the other returns a polished, native-feeling brand card. Every surface the bot exposes (slash responses, @mention replies, Home tab, design-tool skills, MCP tool output rendered in Claude Code) should feel like an agent that represents the client's brand — not a terminal running queries.
For Slack specifically, that means:
- Native inline color swatches via rich_text.color elements wherever colors appear (no images, no sidebar stripes)
- Copy buttons on every quotable piece of text (taglines, pitches, boilerplate, hex codes) via rich_text_preformatted / inline code
- Per-tenant bot name + icon overrides on @mention replies via chat:write.customize
- App Home tab as the persistent "brand surface" for each workspace — always-on palette, quick actions, personalized stats
- Typography, spacing, and visual rhythm via headers, dividers, and the 2-column fields layout — not bullet lists
The Slack formatting reference lives at ~/.claude/skills/slack-formatting/SKILL.md. The Slack-UX-as-product-surface mandate is enforced via docs/SLACK-UX.md (critique → arrange → typeset → spec → approval → code). Invoke before any Slack UX work.
For non-Slack surfaces (design skills, Claude Code skill output, Codex), the same premium-brand mandate applies — the rendering format is just whatever's native to that surface.
3. Users
End users (in client teams): - Marketing / content teams — query voice rules, draft copy, compliance-check before publishing — primarily via Slack - Designers — pull hex codes, logo files, type scale, photography rules — primarily via design-tool skills (Figma equivalent, Claude Design) and Claude Code - Developers — pull tokens, asset URLs, usage rules into the code they're writing — primarily via Claude Code and Codex MCP access - Executives / internal comms — quick reference questions ("what's our tone when talking about X?") — Slack
G&M (platform operator): - Onboard new clients - Provision per-surface access (issue API keys, install Slack app) - Monitor cost and usage per tenant - Pause/cancel access on non-payment - Refresh brand guide content when the client revises (already self-serve — clients edit their own brand guide site, the MCP picks up changes within a 60s cache window)
Client admins (future): - View their own usage and credit consumption (deferred — card #11) - Edit their guide content without a G&M PR (deferred — Phase 3)
4. Core Use Cases
4.1 Slack surface (live)
Slash (deterministic, no LLM):
1. /brand colors → palette with hex + usage, rendered with native rich_text color swatches
2. /brand typography → typefaces + type scale as a spec sheet
3. /brand messaging → tagline, elevator pitch, boilerplate — with native copy buttons
4. /brand logo → variant list with inline PNG previews + downloadable files
5. /brand search <query> → full-text search across the guide
6. /brand foundation, /brand voice, /brand applications, /brand icons, /brand photography, /brand help
Agent (conversational, reasoned via @mention or DM):
7. "We're doing a banner. What color should we put on it?" → agent reads each color's usage field, recommends based on context
8. "Draft a tagline for our spring campaign in our voice" → agent uses voice rules + messaging pillars to generate on-brand copy [Phase 1, generative tools deferred]
9. "Is this copy on-brand?" → rule violations + tone assessment with suggestions [Phase 1]
10. Thread follow-ups within a Slack thread, plus per-user persistent memory across threads (memory_20250818 tool, scoped per tenant + Slack user)
Message shortcuts (right-click on a message): 11. "Check against brand guide" → runs the rule engine on the selected message [live; discoverability pivot pending]
App Home tab: persistent branded surface — palette, quick links, recent queries, per-workspace customization.
4.2 Claude Code skill surface (live)
A G&M-published Claude Code skill (gm-brand-guide) that proxies to the MCP. Designers and developers using Claude Code on a client's project install the skill, authenticate with their per-tenant API key, and ask Claude Code questions like:
- "What's the navy hex?" → MCP get_colors → exact spec returned
- "Generate a button component in our brand colors" → Claude Code reads the brand-guide JSON via the MCP and produces on-brand code
- "Pull the photography mood rules" → MCP get_photography
The skill ships as a discoverable artifact in Claude Code's skill registry once distribution lands. Pre-distribution, G&M provisions it manually for client teams.
4.3 Design-tool skill surface (planned)
Same idea as 4.2 but exposed in a design context (Figma-equivalent / Claude Design). The audience is designers, not developers, and the affordances differ:
- Live palette swatches in the design canvas pulled from get_colors
- Type scale tokens injected as a Figma variable set / equivalent
- Photography mood references shown alongside the design view
- Logo variants pasted into the canvas with the right padding and clear-space rules already applied
The MCP doesn't have to be visual-only — voice rules, messaging pillars, and copy guidance are equally valuable in a design tool when designers are mocking up content.
4.4 Codex surface (live as a transport, no skill yet)
Per-tenant API keys for OpenAI Codex consumers. Same MCP, same tools, different LLM driver. Used today by anyone on a client team who prefers Codex over Claude Code. No published Codex-specific skill yet.
4.5 Direct MCP surface (live)
Per-tenant API keys for clients who want to plug the MCP into something else (custom internal tools, agentic workflows, automation). G&M issues a direct surface key and the consumer brings their own LLM.
4.5 Surfaces — Status & Pricing
The MCP itself is the product. Each surface is a separately-togglable access path, gated per tenant via the subscription kill switch (live, card #3) and feature flags in tenants.json. Any surface can be turned on or off for any tenant without a code deploy beyond the JSON change.
| Surface | Status | LLM cost paid by | Pricing model |
|---|---|---|---|
| Slack (base retainer) | Live | G&M (within included token credit) | $100/mo retainer + $20 token credit included; overage auto-pauses (card #4) |
Claude Code skill (claude_code / claude_skill) |
Live | Client (own Anthropic key) | Upcharge — amount TBD |
| Codex | Live (no published skill) | Client (own OpenAI key) | Upcharge — amount TBD |
Design Check (design_check) |
Live | G&M (passthrough on tenant cap) | Bundled with the brand-bot retainer for now; price out separately if a tenant uses it heavily |
Web Chat / Q&A (chat) |
Live | G&M (passthrough on tenant cap) | Bundled with the brand-bot retainer; per-tenant frontend ships separately (e.g. guardify-brand-bot) |
| Design-tool skill (Claude Design / Figma equivalent) | Planned | Client (own LLM key) | Upcharge — amount TBD |
| Direct MCP | Live | Client (own LLM key) | Custom / negotiated |
Open question — bundling vs à la carte. Whether the upcharges are flat-bundle ("$X/mo gets the tenant every surface") or per-surface ("Slack base + Claude Code +$X + design +$Y") is undecided. Both are technically supported by the kill-switch architecture; this is a packaging decision, not an engineering one. Revisit when the second paying client is in flight.
Token reimbursement caveat. Slack, Design Check, and Web Chat are the surfaces where G&M pays Anthropic — all three call our ANTHROPIC_API_KEY server-side via recordedMessagesCreate. Every external MCP surface (Claude Code, Codex, design tool, direct) authenticates the consumer with the consumer's own LLM key, so tokens cost the client directly. Backend token tracking is fully built (usage_events table, frozen cost_usd per row, alerts at thresholds). Customer-facing visibility — letting a tenant admin see "you've used $X of your $Y credit this month" — is card #11, currently blocked on auth-model decision (Slack OAuth vs admin API key vs magic link).
Tenant onboarding default — Figma-first. When G&M built the brand (the standard play), the Figma file is canonical and the brand-guide content site can be seeded from it (manual today; a pull-from-figma tool is on the PRODUCT-EXTENSIONS roadmap). When an external designer built the brand (Guardify is the precedent — Nicholas Petersen / less.is), the same play applies: ask the designer for the Figma file as the primary path, fall back to a per-asset request list only if Figma isn't their source. The Brand Bot itself is the secondary deliverable; the Figma file is the primary source-of-truth that everything downstream (MCP, Brand Bot, future engineering surfaces) reads from.
5. System Architecture
┌─────────────────────────────────────────────────────────────────┐
│ CLIENT'S TEAM (per tenant) │
│ │
│ Slack workspace ←→ Claude Code (with skill) ←→ Codex │
│ ←→ Design tool (with skill, planned) ←→ Direct MCP consumer │
└──────────────┬──────────────────────────┬────────────────────────┘
│ │
│ Slack Events API │ HTTPS + per-tenant Bearer key
▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ BRAND GUIDE MCP APP (Next.js on Vercel) │
│ Repo: ericdowns/brand-guide-mcp │
│ │
│ ┌────────────────┐ ┌─────────────────────────────────┐ │
│ │ Slack endpoint │───▶│ Tenant resolver │ │
│ │ /api/slack/* │ │ workspace_id → tenant_id │ │
│ └────────────────┘ │ OR API key → tenant_id + surface │ │
│ └────────────┬────────────────────┘ │
│ │ │
│ ┌──────────────────────────────────▼──────────────────────┐ │
│ │ Subscription gate (card #3) │ │
│ │ surface !== 'slack' AND status !== 'active' → 402 │ │
│ └──────────────────────────────────┬──────────────────────┘ │
│ │ │
│ ┌──────────────────────────────────▼──────────────────────┐ │
│ │ Slack path: │ External path: │ │
│ │ agent loop in lib/agent.ts │ MCP JSON-RPC route │ │
│ │ Anthropic Messages API direct │ /api/mcp/[tenantId] │ │
│ │ + MCP tools attached │ Bearer per-tenant │ │
│ │ + per-user memory tool │ pk_live_* │ │
│ │ recordedMessagesCreate │ │ │
│ └──────────────────────────────────┬──────────────────────┘ │
│ │ │
│ ┌──────────────────────────────────▼──────────────────────┐ │
│ │ MCP SERVER (multi-tenant) │ │
│ │ get_colors, get_typography, get_voice, get_messaging, │ │
│ │ get_logos, get_icons, get_brand_foundation, │ │
│ │ get_photography, get_applications, search │ │
│ │ write_in_voice [Phase 1], check_compliance [Phase 1] │ │
│ └──────────────────────────────────┬──────────────────────┘ │
│ │ │
│ ┌──────────────────────────────────▼──────────────────────┐ │
│ │ Postgres (Neon) — Drizzle ORM │ │
│ │ usage_events (frozen cost per row) │ │
│ │ tenant_subscription (status: active/paused/canceled) │ │
│ │ tenant_overrides (logInteractions feature flag) │ │
│ │ api_keys (per-tenant, per-surface, hashed) │ │
│ │ agent_memory (per-user, scoped to tenant) │ │
│ │ interactions, alert_events │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ G&M ADMIN DASHBOARD (/admin) │ │
│ │ Tenants · Usage · Interactions · Memory · API Keys │ │
│ │ Subscription status toggle (live, card #3) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ Future: CUSTOMER PORTAL /portal/[tenantId] (card #11) │
│ Tenant admin self-serve view of their own usage + credit. │
└──────────────────────────────┬──────────────────────────────────┘
│ fetch (60s cache)
▼
┌──────────────────────────────────────────────────────────────┐
│ CLIENT BRAND GUIDE SITE (one per tenant) │
│ Next.js app exposing /api/brand/* endpoints │
│ Source of truth: content/*.json in the client's repo │
└──────────────────────────────────────────────────────────────┘
Three-layer model
| Layer | What lives there | Cardinality | Owned by |
|---|---|---|---|
| Anthropic | One workspace, one API key, used only for the Slack agent path | 1 | G&M |
| Slack | One shared app (Brand Guide / @brandbot), N per-workspace installs via OAuth |
1 app · N installs | G&M owns app; client authorizes install |
| Our app (Next.js + Postgres) | Tenant registry, per-tenant secrets, subscription state, usage tracking, alerts | N tenants | G&M |
Per-client separation exists only in our database, via tenant_id on every usage_events row. Anthropic sees one customer (us). Each external surface's LLM provider sees one customer (the client themselves, via their own key). G&M owns the attribution layer that ties everything to a single tenant.
What we rejected and why
- Anthropic Managed Agents (one agent per client). Initially in the PRD; abandoned 2026-04-23. Would have given native per-workspace billing for free, but provisioning ceremony scales poorly and the API was less mature than Messages-direct + MCP-attached at the time. We attribute per-tenant in our DB instead.
- Per-client Slack app. Same provisioning problem. One shared app + OAuth distribution lets any workspace install in ~30 seconds.
- No database, just events → LLM. Fast to ship but no per-client cost visibility, no kill switch, no usage cap. Hard no.
6. Content Schema (Canonical Brand Guide)
All brand guide content — across every client — conforms to one schema. Each client's content/ folder produces the same JSON shape, served via /api/brand/[category] on the client's brand guide site.
content/
├── brand-foundation.json # mission, vision, values, personality
├── colors.json # palettes (primary/accent/neutral) with hex/rgb/cmyk/usage
├── typography.json # typefaces + type scale (H1–H6, body, caption)
├── logos.json # variants (primary, stacked, mono) with asset refs
├── icons.json # icon set metadata + asset refs
├── voice.json # tone attributes, do's/don'ts, example sentences, banned words
├── messaging.json # pillars, value props, approved taglines, elevator pitch
├── photography.json # art direction, subject matter, composition, don'ts
├── applications.json # usage examples by context (social, print, web)
└── assets/ # source files (SVG, PNG, JPG) referenced by the above
The MCP server is a thin passthrough + tenant-scoping layer over these endpoints. Clients edit their own content/*.json, push to their brand guide repo, and within ~60 seconds (cache TTL) the change is live across every surface.
7. Slack Surface Detail
All four Slack subsurfaces supported. Each is feature-flagged per-tenant in tenants.json so individual clients can be restricted to a subset.
Slash commands (deterministic, no LLM cost) — all 9 categories + search + help. Live.
@mention / DM → agent loop — Anthropic Messages API direct with MCP tools attached + per-user memory tool. Thread follow-ups remember context within the thread; per-user persistent memory remembers preferences across threads. Live.
Message shortcuts — "Check against brand guide" runs the rule engine on the selected message. Live; discoverability pivot pending (the shortcut is buried under Slack's "Connect to apps" submenu — open question whether to also surface it as /brand check modal, Home tab button, both, or document the path in /brand help).
Home tab — persistent dashboard with palette swatches, logo downloads, category quick links. Live.
8. MCP Tool Surface
Every tool accepts an implicit tenant_id injected by the MCP route from the URL (Slack path) or resolved from the bearer key (external path). Tools cannot be invoked across tenants.
Phase 0 (read-only) — live:
- get_brand_foundation(), get_colors(), get_typography(), get_logos(), get_icons(), get_voice(), get_messaging(), get_photography(), get_applications(), search(query)
Phase 1 (generative) — deferred:
- write_in_voice(brief, constraints) — generate on-brand copy
- check_compliance(copy) — rules violations (deterministic) + LLM tone assessment
9. Agent Loop (Slack path) and Per-Tenant Cost Attribution
The Slack @mention / DM path runs through lib/agent.ts, which:
- Resolves the tenant from
team_id. - Hits the per-tenant subscription gate (Slack path bypasses; only external surfaces are blocked when paused).
- Calls Anthropic Messages API directly via
recordedMessagesCreate()inlib/anthropic.ts— the single point where every Anthropic call gets logged. - Brand-guide MCP tools are attached server-side (Anthropic's hosted-MCP feature). Tool calls happen at Anthropic's edge.
- Per-user memory tool (
memory_20250818) is intercepted client-side —tool_useblocks for memory ops are run againstagent_memorytable and the result fed back. Hard cap at 6 round-trips peraskAgentcall. - Every round-trip writes a
usage_eventsrow withtenant_id,surface='slack', frozencost_usdper the pricing table inlib/pricing.ts.
Per-tenant spend at any point is one query:
SELECT tenant_id, SUM(cost_usd) FROM usage_events
WHERE timestamp >= '<period_start>'
GROUP BY tenant_id;
That's the number that gates the auto-pause logic in card #4 and the customer-facing portal in card #11.
10. Multi-Tenant Onboarding Flow
For Slack:
1. Client signs onto retainer.
2. G&M sends OAuth install link (shareable URL with the right scopes baked in).
3. Client admin clicks Allow → Slack callback captures workspace_id + bot_token.
4. G&M sets Vercel env vars TENANT_<ID>_WORKSPACE_ID and TENANT_<ID>_BOT_TOKEN.
5. G&M adds tenant row to tenants.json (id, name, brandGuideUrl, features, optional logoUrl).
6. Redeploy.
7. G&M (admin UI) sets tenant_subscription.subscription_status = 'active' (default).
8. Smoke-test /brand colors in the client's Slack.
For external surfaces (Claude Code / Codex / direct):
1. G&M issues a per-tenant API key via /admin/api-keys (pk_live_<tenant>_<surface>_<32hex>).
2. Plaintext shown ONCE; only the SHA-256 hash is persisted.
3. G&M shares the key with the client securely (1Password share link or equivalent).
4. Client configures their consumer (Claude Code skill, Codex MCP config, etc.) with the key + the MCP endpoint URL.
5. Subscription gate (card #3) blocks the key if subscription is not active.
For the design-tool surface (planned): - Same as external surfaces — per-tenant key — plus eventual published skill/plugin distribution. - TBD whether the design tool's plugin distribution model requires anything beyond an API key (e.g. workspace allowlists, signed plugin manifests).
When N=2+ paying clients are billing through Stripe, move tenant registry from tenants.json to the database. Today (N=2 dogfood + 1 paying anchor) the JSON is fine.
11. Security & Privacy
- Slack signature verification on every incoming Slack event (HMAC-SHA256 + 5-minute timestamp check,
lib/slack/verify.ts). - Per-tenant API keys stored as SHA-256 hashes (
api_keys.key_hash) — plaintext is never persisted, shown to the operator once at issuance. - Subscription kill switch (card #3) — non-Slack surfaces return HTTP 402 (Payment Required, distinct from 401 Unauthorized) when status ≠ 'active'. Lets support tell "wrong key" from "billing" at a glance in access logs.
- Workspace → tenant binding enforced at the auth resolver. No cross-tenant data leakage possible — every DB read scopes by
tenant_idand every tool call hastenant_idinjected by the route, not the caller. - Per-user memory scoped (tenant, slack_user_id). What one teammate's chats produce is invisible to others, and not shared across workspaces. 365-day inactivity TTL via daily cron.
- Interaction logging opt-in per tenant via
features.logInteractions+tenant_overrides. Disclosed in/privacy. 30-day retention via daily cron. - PII — Slack message content passed to LLM only for @mention / shortcut / DM flows. Stored in
interactionstable only when the tenant flag is on. - Admin dashboard behind shared basic auth (
ADMIN_PASSWORDin Vercel env). Move to Google SSO when team grows beyond 3-4.
Secrets: every ANTHROPIC_API_KEY, SLACK_*, MCP_SHARED_SECRET, TENANT_*, DATABASE_URL, ADMIN_PASSWORD, CRON_SECRET is mirrored to 1Password → G&M vault → "Brand Guide MCP — Production".
12. Analytics & Metering
- Per-tenant usage events — one row per Anthropic call (Slack path) and per MCP tool invocation (external paths). Includes
tenant_id,surface,api_key_id, frozencost_usd, model, tokens, duration. Source of truth for billing attribution and the auto-pause gate. - Spend alerts —
alert_eventstable debounces soft alerts (per tenant per 24h) and hard-cap blocks (refuses new calls until the cap window ages out). Today this is a global per-tenant cap; card #4 extends it to a credit-aware cap that auto-pauses the tenant when monthly usage exceeds the included credit. - Admin dashboard at
/admin/usage— per-tenant summary, per-surface breakdown, daily spend charts, top threads, top users. - Customer portal (card #11, planned) — same data, scoped to one tenant, exposed to the tenant's admin via Slack OAuth (likely auth model). Open question: which fields are appropriate to expose to a tenant admin (usage volume + spend yes; raw user-typed queries probably not).
13. Deployment & Hosting
| Component | Platform | Notes |
|---|---|---|
| Brand Guide MCP app | Vercel | Next.js 16, App Router, Edge-compatible API routes |
| Database | Neon Postgres | Drizzle ORM; migrations applied via direct-SQL pattern (drizzle-kit's migrate path hangs on the Neon driver) |
| Asset storage | Each client's brand guide site (their public/) |
G&M doesn't host assets centrally |
| Slack app | Slack (managed) | One shared G&M app, OAuth distribution per workspace |
| LLM (Slack path) | Anthropic Messages API direct (claude-sonnet-4-6) with MCP attached + memory tool |
Single G&M API key; per-tenant attribution in our DB |
| LLM (external surfaces) | Each consumer's own provider key | G&M doesn't see or pay for these tokens |
| DNS | brand-guide-mcp.vercel.app (today); custom domain like mcp.grainandmortar.com deferred |
Not user-facing — just MCP endpoints, Slack webhooks, and admin |
14. Phasing
Phase 0 — Slack MVP for California Forever and Grain & Mortar — SHIPPED
- Canonical brand guide JSON schema defined and deployed to CF + G&M brand guide sites
- MCP server with 10 read-only tools (9 categories + search)
- Slack bot live in G&M and CF workspaces; both tenants serving their own real content
- Per-thread thread memory (Slack-native via
conversations.replies) + per-user persistent memory (agent_memorytable) - Admin dashboard with Tenants, Usage, Interactions, Memory, API Keys
- Per-tenant API keys for external consumers (claude_code, claude_skill, codex, direct)
- G&M's own Claude Code skill (
gm-brand-guide) as the first published external-surface artifact
Phase 0.5 — Slack UX polish — SHIPPED
- 0.5a Block Kit v1 + 0.5b
rich_textupgrade with native color swatches - 0.5c logo PNG previews via server-side SVG→PNG rasterizer
- App Home tab as persistent branded surface
- 0.5d per-tenant bot identity (
chat:write.customize) — DEFERRED, on the Roadmap with trigger "before CF goes live OR a tenant complains about brandbot showing G&M's icon"
Phase 1 — SaaS productization — IN PROGRESS
- ✅ Per-tenant subscription kill switch (card #3) — SHIPPED 2026-04-27.
tenant_subscriptiontable; 402 gate on non-Slack surfaces when paused/canceled; admin UI toggle. - 🔄 Per-tenant monthly usage cap with auto-pause (card #4) — NEXT. Extends
tenant_subscriptionwithmonthly_credit_usd,current_period_usage_usd,period_starts_at. Daily cron resets the period. Auto-flips status topausedwhen usage > credit. Depends on #3. - 🔄 Customer-facing usage portal (card #11) — BLOCKED on auth-model decision. Self-serve
/portal/[tenantId]view of usage + credit. Auth options: Slack OAuth (lean) vs admin API key vs magic link. - Real-time activity strip on /admin/usage (card #10) — READY.
- Per-tool breakdown on /admin/usage (card #9) — READY.
- Generative tools (
write_in_voice,check_compliance) — deferred behind the SaaS productization track.
Phase 2 — Multi-surface productization
- Design-tool skill — Claude Design / Figma equivalent. Concept locked; build deferred until productization track stabilizes.
- Codex skill (published) — currently consumers configure raw MCP; a published skill comparable to
gm-brand-guidewould close the loop. - OAuth install flow completion —
/api/slack/install/route.tsis a stub; today new tenants are onboarded via env vars + redeploy. Self-serve install for N≥2 paying clients. - Tenant registry to DB — once N≥3, move from
tenants.jsonto Neon. The admin UI becomes the authoritative write path. - Stripe integration — replace the manual
subscription_statusflag with Stripe webhooks. Replace the naïve "30 days fromperiod_starts_at" period with proper anchor dates from Stripe. - Surface bundling vs à la carte — pricing decision; both architecturally supported.
Phase 3+ — Future considerations
- Client-facing CMS (edit brand guide without PRs)
- Per-workspace custom branding (client-branded bot display name in Slack)
- Integrations beyond Slack/Claude/Codex (Teams, Discord, Notion, Linear)
- Localization / multi-language brand guides
- White-label MCP for agencies that want to resell the platform
14.5 Bot Identity & Personality (cross-cutting, Slack-specific)
The shared @brandbot app is G&M-branded globally, but every interaction should feel client-owned. Layered approach:
| Layer | Lever | Notes |
|---|---|---|
| App-level (shared across tenants) | App name, icon, background_color, descriptions at api.slack.com/apps |
Signals "this is a product." Set once. |
| Per-message (chat.postMessage only) | username, icon_url, icon_emoji with chat:write.customize scope |
@mention replies render as the tenant's brand. Slash responses can't override identity. |
| Per-workspace (App Home) | Fully customized views.publish payload per user_id |
The strongest "this bot is ours" surface. 100 block budget. Live. |
| Content voice | Message copy + tone | The bot's "personality" is how it writes. Inherits from the client's voice guide. |
For non-Slack surfaces, identity is handled by whatever surface we're in — the Claude Code skill descriptor, the design-tool plugin label, etc.
14.6 Commercial Model
Working pricing (subject to revision before formal launch): - Slack base retainer: $100/mo per tenant. - Included token credit: $20/mo of Anthropic spend bundled in. Beyond $20, the tenant auto-pauses (card #4 gate). Reactivation requires a deliberate conversation — by default the cron resets the period counter but does NOT auto-unpause an overage-driven pause. - External surfaces (Claude Code, Codex, design tool, direct): Upcharge per surface, amount TBD. Surface-by-surface or bundle is an open packaging decision.
Token reimbursement model. Clients reimburse G&M for tokens consumed on the Slack path (the only surface where G&M holds the LLM key). External surfaces use the consumer's own LLM key, so token cost is direct between the client and their LLM provider — no G&M markup, no G&M billing involvement. The included $20/mo credit covers normal Slack usage; auto-pause prevents runaway spend on the G&M-paid path.
Subscription state lifecycle (live as of 2026-04-27):
- active — normal traffic.
- paused — temporary stop. Triggered by manual admin action OR auto-pause from credit overage (card #4). Reversible by an admin via the dashboard toggle.
- canceled — ended subscription. Same gate as paused for now; kept distinct so admin UI shows the right badge and so #4's auto-reactivation logic can tell "billing lapsed" from "client quit."
Hard prerequisites for monetizing past N=2: - Customer portal so clients can see what they're paying for (card #11). - Stripe integration so subscription state is driven by payment, not by a manual admin toggle (Phase 2).
15. Open Questions / Deferred Decisions
- Surface bundling vs à la carte — single-bundle pricing or per-surface upcharges. Both architecturally supported. Decide once the second paying client is in flight.
- Auto-reactivation policy after overage pause — does the daily period-reset cron auto-unpause overage-paused tenants, or require manual unpause? Lean manual until Stripe lands.
- Customer portal auth model — Slack OAuth (lean) vs per-tenant admin API key vs magic link. Drives card #11.
- Custom domain —
mcp.grainandmortar.comor similar. Not user-facing pressure today. - Compliance rule format — Phase 1 generative tooling. Proposal: separate
compliance-rules.jsonper tenant with banned words, required terminology, disclaimers. - Asset storage location — clients host their own (today). Migrate to G&M-hosted (signed URLs) only if a client's guide is private.
- Rate limiting — not enforced today. Add token-bucket per tenant when N≥3 or any tenant trips the global cap repeatedly.
- Design-tool surface specifics — Claude Design's MCP/plugin model; whether the same skill works in Figma; whether to build one cross-tool skill or per-tool variants. Out of scope until Phase 2.
16. Risks
- Multi-surface support burden as N grows. Each surface is a different rendering target with different idioms. Mitigation: the MCP layer stays uniform; surface-specific rendering is isolated to the consumer (skill, Slack formatter, design plugin). Bundle upcharges with explicit support hours.
- Token-cost volatility on G&M-paid surfaces. If Anthropic raises prices mid-month, the $20 credit goes further or shorter without us renegotiating. Mitigation: freeze
cost_usdper row at write time; review the cap quarterly; alert when spend per tenant trends up across two billing periods. - Per-thread / per-user memory quirks. Long threads can blow past context window. Mitigation: thread memory caps (last N messages); compression for older messages if needed; the per-user memory store is the "long-term" layer above thread memory.
- Client doesn't use Slack. Blocks the Slack surface specifically, not the product. Designers/developers can still use the Claude Code / Codex / design-tool skills. The Slack bot is one surface, not the only product.
- A surface's distribution channel changes underneath us. E.g. Slack changes their app review process; Anthropic/OpenAI changes Claude Code/Codex skill distribution. Mitigation: the MCP itself is portable — surface-specific code is the only thing that has to adapt.
17. Success Criteria
Phase 0 (Slack MVP) — MET 2026-04-26
- ✅ G&M and CF teams have
@brandbotinstalled and answering questions correctly - ✅ Both tenants serving their own real brand-guide content (no hardcoded dogfood)
- ✅ Per-query log live in admin dashboard
- ✅ Per-tenant cost attribution validated end-to-end
- ✅ Zero cross-tenant data leakage (validated before adding G&M as second tenant)
Phase 1 (SaaS productization) — in progress
- ✅ Subscription kill switch shipped (card #3, PR #14)
- 🔄 Auto-pause at credit threshold (card #4)
- 🔄 Customer-facing usage portal (card #11, blocked on auth decision)
- 🎯 At least one surface beyond Slack actively used by a paying client (Claude Code skill installed and used by a client team weekly)
- 🎯 First paid invoice delivered to a tenant for either base retainer + token overage or surface upcharge
Phase 2 (Multi-surface productization) — gating criteria
- 🎯 Design-tool skill shipped to at least one client design team
- 🎯 Stripe-driven subscription state replaces the manual admin toggle
- 🎯 Tenant registry moved from JSON to DB
- 🎯 N≥3 paying tenants on at least the Slack surface
Sibling project notes:
| Location | Role |
|---|---|
../brand-guide-research/ |
Original 2026-03-10 PRD that conceived the brand guide as a standalone web product. Superseded by this PRD once the MCP-as-a-service strategy emerged. Preserved as historical record. |
../california-forever-brand-guide/ |
First tenant content site. Live since 2026-04-21. |
../grainandmortar-brand-guide/ |
Second tenant content site (G&M dogfood). Live since 2026-04-26. |
Project status timeline lives in README.md. Roadmap of explicitly-deferred items lives in docs/ROADMAP.md in the repo. Architecture deep-dive: docs/ARCHITECTURE.md.