{
  "$schema": "https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/mcp-server-card.schema.json",
  "serverInfo": {
    "name": "mora",
    "version": "1.0.0",
    "title": "Mora AI",
    "description": "Read-only MCP server over a Mora account: its posts and their performance, its brand profile and offerings, its Shopify catalogue and revenue attribution, its content projects, audiences, content angles and brief runs. An account can hold several workspaces (brand kits, e.g. different clients or brands) -- every tool answers for one workspace at a time, defaulting to the account's active one; call list_workspaces first on any account that might hold more than one. Intended to ground an agent's marketing decisions in what a business actually sells and what has actually worked."
  },
  "status": "available",
  "transport": {
    "type": "http",
    "url": "https://app.mora-marketer.com/api/mcp",
    "_note": "Streamable HTTP. POST JSON-RPC 2.0. Every request must carry an OAuth-issued Bearer token; the token is checked on every message, not once per session."
  },
  "authentication": {
    "type": "oauth2",
    "_note": "Mora runs its own OAuth 2.1 authorization server (reviewed 2026-08-11). Discover it via /.well-known/oauth-protected-resource (RFC 9728) and /.well-known/oauth-authorization-server (RFC 8414). Client identification uses CIMD (OAuth Client ID Metadata Documents) rather than Dynamic Client Registration -- a client's client_id is an https URL Mora fetches and validates; there is no /register endpoint. PKCE (S256) is required on every authorization_code grant. Never paste a session token into a client configuration."
  },
  "capabilities": {
    "tools": [
      {
        "name": "list_posts",
        "description": "List this account's social posts, newest first, optionally filtered by status.",
        "tier": "read"
      },
      {
        "name": "get_post_performance",
        "description": "This account's best-performing published posts with engagement metrics.",
        "tier": "read"
      },
      {
        "name": "get_brand_profile",
        "description": "What this business is: name, what it sells, who it sells to, voice, and its offerings, led by the workspace's voice canon (governing documents, rules, banned terms) when it has one.",
        "tier": "read"
      },
      {
        "name": "get_brief_workspace",
        "description": "A bounded, redacted workspace snapshot for one authenticated account Brief: its objective, safe research state, products, personas, concepts, delivery intents, evidence decision, and blockers.",
        "tier": "read"
      },
      {
        "name": "list_products",
        "description": "This account's Shopify catalogue.",
        "tier": "read"
      },
      {
        "name": "get_revenue_attribution",
        "description": "This account's Shopify revenue attributed to marketing channels, first- and last-touch.",
        "tier": "read"
      },
      {
        "name": "list_projects",
        "description": "This account's content projects and their scheduled slots.",
        "tier": "read"
      },
      {
        "name": "list_audiences",
        "description": "This account's defined audiences: who they're for, what they want, and their exact pain language.",
        "tier": "read"
      },
      {
        "name": "list_content_angles",
        "description": "This account's content angle bank, ranked by reach x urgency x durability.",
        "tier": "read"
      },
      {
        "name": "list_brief_runs",
        "description": "This account's brief-order runs and every step's research finding.",
        "tier": "read"
      },
      {
        "name": "get_paid_campaign_performance",
        "description": "This account's Google Ads, Meta Ads and LinkedIn Ads campaign delivery -- provider-reported spend, impressions, clicks, conversions and conversion value, never summed across currencies. Listed for any read connection; calling it without the separately-consented mora.read.ads scope returns an HTTP 403 insufficient_scope step-up challenge instead of data.",
        "tier": "read",
        "requiredScope": "mora.read.ads"
      },
      {
        "name": "estimate_linkedin_audience",
        "description": "Estimate a LinkedIn Ads audience size before a campaign exists, from free-text targeting terms (locations required; job functions, seniorities, industries, job titles and companies optional). Calls LinkedIn live -- every number is real, never invented. Listed for any read connection; calling it without the separately-consented mora.read.ads scope returns an HTTP 403 insufficient_scope step-up challenge instead of data.",
        "tier": "read",
        "requiredScope": "mora.read.ads"
      },
      {
        "name": "get_linkedin_campaign_setup",
        "description": "Read how one named LinkedIn Ads campaign was built: targeting facets (raw URNs), schedule, format, budgets, and creative copy -- setup, not delivery. Listed for any read connection; calling it without the separately-consented mora.read.ads scope returns an HTTP 403 insufficient_scope step-up challenge instead of data.",
        "tier": "read",
        "requiredScope": "mora.read.ads"
      },
      {
        "name": "list_linkedin_matched_audiences",
        "description": "This LinkedIn Ads account's synced matched-audience segments and account/contact lists, with real member-count sizes -- not the same object as list_audiences (hand-authored personas). Listed for any read connection; calling it without the separately-consented mora.read.ads scope returns an HTTP 403 insufficient_scope step-up challenge instead of data.",
        "tier": "read",
        "requiredScope": "mora.read.ads"
      },
      {
        "name": "get_linkedin_conversion_tracking",
        "description": "This LinkedIn Ads account's Insight Tag status and synced conversion rules, so conversion-tracking readiness can be checked before crediting or ruling it out. Listed for any read connection; calling it without the separately-consented mora.read.ads scope returns an HTTP 403 insufficient_scope step-up challenge instead of data.",
        "tier": "read",
        "requiredScope": "mora.read.ads"
      },
      {
        "name": "get_workspace_insights",
        "description": "This workspace's own /insights conclusions in one call: classified business model, business-health pillars, and the headline figures that model actually opens on.",
        "tier": "read"
      },
      {
        "name": "get_paid_delivery_status",
        "description": "For this workspace's connected ad accounts, what Mora actually has stored -- the real synced calendar range and connection health -- before trusting any other paid tool's numbers.",
        "tier": "read",
        "requiredScope": "mora.read.ads"
      },
      {
        "name": "get_paid_trend",
        "description": "A daily, weekly, or monthly time series of paid delivery -- spend, impressions, clicks, leads and purchases -- per provider and per currency, with the prior window's totals and delta.",
        "tier": "read",
        "requiredScope": "mora.read.ads"
      },
      {
        "name": "get_paid_audience_delivery",
        "description": "Who a paid campaign (or the whole connected ad account) actually reached on LinkedIn Ads and Meta Ads -- seniority, industry, job function, company size, age, gender, country/region.",
        "tier": "read",
        "requiredScope": "mora.read.ads"
      },
      {
        "name": "get_conversion_setup",
        "description": "This workspace's conversion-tracking readiness across LinkedIn Ads, Meta Ads and Google Ads -- not how many conversions happened, whether tracking is actually wired to convert.",
        "tier": "read",
        "requiredScope": "mora.read.ads"
      },
      {
        "name": "get_creative_performance",
        "description": "Per-ad/creative delivery for one named campaign on one provider -- which specific creative is carrying or dragging a campaign's numbers.",
        "tier": "read",
        "requiredScope": "mora.read.ads"
      },
      {
        "name": "get_google_search_terms",
        "description": "The search terms that triggered this workspace's Google Ads, as Google stores them -- spend, clicks and conversions per term, the hidden-terms remainder Google withholds, and spend-without-conversion review candidates. Provider-reported figures, read-only.",
        "tier": "read",
        "requiredScope": "mora.read.ads"
      },
      {
        "name": "audit_paid_account",
        "description": "A deterministic health scorecard for this workspace's connected ad account(s): data freshness, delivery, conversion signal, Google auction, and structure, each with a status and evidence.",
        "tier": "read",
        "requiredScope": "mora.read.ads"
      },
      {
        "name": "list_workspaces",
        "description": "This account's workspaces (brand kits) -- one Mora client/brand each. Every other tool answers for ONE workspace at a time (its active/default one, or a named one via workspace_id/workspace_name); call this first on any account that might hold more than one.",
        "tier": "read"
      },
      {
        "name": "search_sources",
        "description": "Search the workspace's saved knowledge library and get cited passages (document, page, heading, exact span) with source-authority labels.",
        "tier": "read"
      },
      {
        "name": "read_source",
        "description": "Read the full extracted text of one saved source, with its note and governance labels.",
        "tier": "read"
      },
      {
        "name": "list_sources",
        "description": "List saved knowledge sources with their note, status and governance labels.",
        "tier": "read"
      },
      {
        "name": "save_source",
        "description": "Add a link, document text or note to the workspace's knowledge library. Additive only; requires the separately consented mora.write.knowledge scope (step-up).",
        "tier": "mutate"
      },
      {
        "name": "request_upload_link",
        "description": "Get a one-time, 30-minute link where the person uploads an original file (PDF, Word, text, email) to the workspace's knowledge library. Additive only; requires the separately consented mora.write.knowledge scope (step-up).",
        "tier": "mutate"
      },
      {
        "name": "update_source",
        "description": "Change one saved source's labels, note or status (superseded, archived), or record who approved it as proof. Never deletes; every change is logged on the source. Only the account owner or a workspace admin can make a source approved proof. Requires the separately consented mora.write.knowledge scope (step-up).",
        "tier": "mutate"
      },
      {
        "name": "verify_draft",
        "description": "Check a draft against the library sources it cites: figures and quotations not found in them, and text copied from internal sources into external copy. Deterministic and read-only.",
        "tier": "read"
      },
      {
        "name": "review_draft",
        "description": "An independent second opinion on a draft from Claude Opus 5.5, run by Mora on its own API key: claims against the cited sources, internal material in external copy, inference stated as fact, dated facts, unapproved results, the ask and the workspace voice. Runs verify_draft and check_copy first. Read-only; never stores the draft. Capped per account per day.",
        "tier": "read"
      },
      {
        "name": "check_copy",
        "description": "Check a draft against the workspace's voice rules: banned terms with replacements, em dashes, stock AI phrasing, and quantified claims that must be confirmed as approved proof. Deterministic; never rewrites or stores the draft.",
        "tier": "read"
      }
    ],
    "resources": [
      {
        "uri": "mora://brand/voice",
        "name": "brand_voice",
        "mimeType": "text/markdown",
        "description": "How this business sounds, what it sells and who it sells to, as prose. Attach it so every message is written on-brand."
      }
    ],
    "prompts": [
      {
        "name": "product_hunt_launch_week",
        "description": "A full week of launch content, compliant with Product Hunt rules on vote solicitation."
      },
      {
        "name": "first_maker_comment",
        "description": "The single highest-leverage Product Hunt asset."
      },
      {
        "name": "write_like_what_worked",
        "description": "Writes from this account's own best-performing posts."
      },
      {
        "name": "marketing_performance_overview",
        "description": "Organic and paid performance together, as two separate never-blended figures -- the single most common opening question, 'how is my marketing doing right now?'."
      },
      {
        "name": "research_from_sources",
        "description": "Answer a question from the workspace's saved sources first, with citations and authority labels, then clearly marked current external research."
      },
      {
        "name": "presentation_from_sources",
        "description": "Build an editable, cited PowerPoint from the workspace's saved sources and brand, without confidential material or unapproved proof."
      },
      {
        "name": "write_in_brand_voice",
        "description": "Draft in the workspace's governing voice: canon documents, banned terms and proof rules, grounded in saved sources and checked with check_copy."
      },
      {
        "name": "competitor_profile",
        "description": "An internal profile of a competitor: who they are, how they charge, recent moves, where they are strong (as their claims), where we differ, and a talk track if they are the incumbent. Library first, then their own pages."
      },
      {
        "name": "industry_trends",
        "description": "A dated, sourced brief on the most important current trends for a market, what each means for clients, and article ideas the firm could own. Library first, then current official sources."
      },
      {
        "name": "meeting_one_pager",
        "description": "A one-page prep sheet for a meeting with a client or prospect: key facts, what is public about their taxes, where we may help, questions to ask, watch-outs and an opener. Library first, then their releases, the appraisal district and local news."
      },
      {
        "name": "respond_to_article",
        "description": "The firm's point of view on a saved or linked article: what happened (dated), our view, what changed, and actions for owners, current as of today and checked against primary sources."
      },
      {
        "name": "valuation_to_property_tax",
        "description": "Write a post or article that takes one appraisal or valuation concept (from saved textbooks, appraisal reports or articles) and shows what it means for a property-tax assessment, in the workspace voice and in its own words."
      },
      {
        "name": "marketing_diagnosis",
        "description": "A deeper, sequenced diagnosis for a specific paid-media question -- workspace insights, sync coverage, campaign delivery, trend, audience, conversion setup and creative performance, in the order that keeps an earlier gap from being misread as a later finding."
      }
    ]
  },
  "_scope": {
    "readOnly": false,
    "_note": "Read-only except for three knowledge-library writes (ITC Brain), all gated by the separately consented mora.write.knowledge scope by step-up: save_source adds a source to the account's own knowledge library (2026-09-25), request_upload_link mints a single-use, 30-minute link where the person uploads an original file into that same library (2026-09-27), and update_source changes one existing source's labels, note or status or records an approval (2026-10-02). None can delete anything: update_source can mark a source superseded or archived (hidden, row kept), every change is logged on the source, a Viewer cannot call it, and only the account owner or a workspace admin can make a source approved proof. No tool publishes, schedules, connects an account, changes billing or writes a brand kit. Ad-account data (get_paid_campaign_performance) is gated behind the separately-consented mora.read.ads scope: the tool is listed for a base mora.read grant, but a call without mora.read.ads returns HTTP 403 insufficient_scope, never ad data, until the account owner separately approves it.",
    "excludes": [
      "Discover article corpus — licensed third-party news content, deliberately not redistributed"
    ]
  },
  "links": [
    {
      "rel": "service-desc",
      "href": "https://app.mora-marketer.com/openapi.json",
      "type": "application/vnd.oai.openapi+json;version=3.1",
      "title": "Underlying HTTP API (OpenAPI 3.1)"
    },
    {
      "rel": "describedby",
      "href": "https://app.mora-marketer.com/llms.txt",
      "type": "text/markdown",
      "title": "LLM-friendly description of the public surface"
    },
    {
      "rel": "help",
      "href": "https://www.mora-marketer.com/developers/mcp",
      "type": "text/html",
      "title": "Human-readable MCP connection, tool, OAuth, and troubleshooting guide"
    },
    {
      "rel": "repository",
      "href": "https://github.com/Mora-AI-Content-Studio/mora-claude-plugin",
      "type": "text/html",
      "title": "Public Mora AI connector and workflow package for the remote MCP server"
    },
    {
      "rel": "https://www.iana.org/assignments/relation/contact",
      "href": "mailto:hello@mora-marketer.com"
    }
  ],
  "_meta": {
    "protocolVersion": "2026-07-28",
    "last_reviewed": "2026-09-24"
  }
}
