Buttercup MCP Server

Buttercup MCP Server

$45,000.00
Sale price  $45,000.00 Regular price 
Skip to product information
Buttercup MCP Server

Buttercup MCP Server

$45,000.00
Sale price  $45,000.00 Regular price 

Domain: connects marketing/campaign tooling (Claude, internal agent orchestrators) to a multi-brand tool manufacturer's product catalog, dealer network, DAM, and campaign platform.

Transport

Streamable HTTP (the current MCP spec, not the deprecated HTTP+SSE variant), deployed behind the company's API gateway. Reasoning: this server is a shared, always-on resource hit by multiple clients (Claude Desktop for brand managers, an internal campaign-ops agent, a nightly reporting job) — not a local dev tool — so stdio was never in the running. I kept SSE as a fallback transport for one legacy internal client that hadn't upgraded its MCP SDK yet, but Streamable HTTP is primary because it supports plain request/response for simple tool calls and only upgrades to a stream when a tool (like a bulk asset export) needs to push incremental progress.

Auth

OAuth 2.1 with PKCE, using the MCP Authorization spec's pattern of treating the MCP server as a resource server, not an identity provider:

  • Human sessions (brand manager using Claude Desktop) authenticate against the company's Azure AD tenant; Buttercup validates the JWT audience/issuer and maps AD groups to internal roles (brand-manager:dewalt-equivalent, campaign-ops, analyst-readonly).
  • Machine sessions (the nightly agent) use client_credentials against the same AD tenant, issued a service principal scoped to catalog:read and campaign:read only — deliberately no write scopes, so a misbehaving automated job can't launch a campaign.
  • Every tool declares required scopes in its manifest; the server does its own scope check on top of what the token carries, so a compromised or overly broad token still gets rejected at the tool boundary, not just the gateway.
  • All tool calls are logged with token subject + scope for audit, since campaign spend and warranty data both have compliance requirements.

Tool design

I split tools by write-risk rather than by resource type, because that's what actually needs different guardrails:

  • search_product_catalog(brand_id, query, filters, cursor) — read-only, paginated, cheap to expose broadly.
  • get_dealer_locations(zip, radius_miles, brand_id) — read-only, backed by the dealer locator service.
  • get_campaign_performance(campaign_id, date_range) — read-only, pulls from the analytics warehouse, not the live ad platform (avoids hammering rate-limited downstream APIs).
  • create_campaign_segment(name, criteria, brand_id, dry_run) — write, requires campaign-ops scope, requires an idempotency key parameter so retries from a flaky agent don't create duplicate segments, and defaults dry_run=true unless explicitly set false — an agent has to opt into a real write.
  • submit_creative_for_approval(asset_id, campaign_id, reviewer_group) — write, kicks off a human approval workflow rather than doing anything irreversible itself.
  • lookup_warranty_status(serial_number) — read-only but scoped tightly (warranty:read) since serial numbers can be tied to customer PII in the CRM; this tool intentionally does not return the linked customer record, only status/coverage dates, to keep the blast radius small if a token leaks.

Resources

Static-ish reference material is exposed as MCP resources rather than tools, since it doesn't need parameters: brand style guides, current campaign briefs, and product spec-sheet PDFs, each with a stable buttercup://brand/{brand_id}/style-guide style URI. This lets Claude pull brand voice guidelines into context without a round-trip tool call every time.

A couple of specific decisions I'd defend

  • No single mega-tool. I could've built one query_martech_data(type, params) tool, but that pushes all the validation into the LLM's judgment about what params are valid for what type — separate tools give me real JSON-schema validation per action and let scopes map cleanly to individual tools.
  • dry_run default true on the segment-creation tool specifically because campaign segments feed into paid ad spend downstream — an agent hallucinating a bad audience definition should cost nothing until a human (or an explicit dry_run=false) confirms it.

You may also like