Developer reference · MCP

Optimly MCP connector

One endpoint, 13 tools, and the brand named per call — by a credential that reaches every brand in your organization, or one pinned to a single brand. The connector speaks Streamable HTTP and keeps no session, so any request can land on any instance.

Endpoint

Server URLhttps://app.optimly.ai/api/mcp
TransportStreamable HTTP, JSON responses, stateless
AuthenticationAuthorization: Bearer — an organization sk_live_… key, a brand-scoped one, or an oat_ OAuth access token
Rate limit60 requests per minute per token, shared with the keyed REST API
BrandNamed per tool call with the brand argument — an id, a slug or the unique name — and defaulted when the organization has one brand. A brand-scoped key or connection pins it instead, and refuses a sibling.

Connect a client

Create a key in the application under Connections — it is shown once, and leaving its scope on every brand gives you one credential for all of them. The three shapes below cover the clients most people use; the Connections page has the paste-ready setup, extraction, and interview prompts that go with them.

Claude Code

claude mcp add optimly --url https://app.optimly.ai/api/mcp --bearer-token-env-var OPTIMLY_KEY

Keep the key in the named environment variable rather than a committed file.

Cursor, and other JSON clients

{
  "mcpServers": {
    "optimly": {
      "url": "https://app.optimly.ai/api/mcp",
      "headers": { "Authorization": "Bearer <your-key>" }
    }
  }
}

Hosted connectors

Add a custom connector pointing at https://app.optimly.ai/api/mcp and complete the OAuth consent screen in the browser. The connector discovers the flow on its own: an unauthenticated request answers 401 with a WWW-Authenticate header naming the resource metadata at https://app.optimly.ai/.well-known/oauth-protected-resource, which in turn advertises the authorization server at https://app.optimly.ai/.well-known/oauth-authorization-server. PKCE with S256 is required.

Verify the connection

Call list_brands to see what the credential reaches, then get_brand and check the name and domain it returns. Do not treat the connection as working until that call succeeds — a server that answers is not the same as a server that resolved your brand.

Tool reference

All 13 tools, as the connector advertises them. Reading is unconstrained; writing is limited to publish_vault and act_on_recommendation. No tool starts a measurement run.

list_brands

The brands this credential can reach — the list every other tool's `brand` argument is drawn from, with each brand's id, name, domain and slug. A brand-bound credential sees only its own; an org-wide one sees the whole organization. Listed first on purpose: a caller that does not know what it can reach should be able to find out before it tries to read or write anything.

No parameters beyond the authenticated brand.

get_brand

Identity of the authenticated brand: name, canonical domain, slugs, claim state.

ParameterTypeRequiredDescription
brand any no

get_vault

The brand's active vault: all sections (with publish_state), competitors, and disambiguation. Falls back to the draft when nothing is published.

ParameterTypeRequiredDescription
brand any no

publish_vault

Publish a new vault version. Payload shape: `{"sections": {<registry key>: {"content", "publish_state": "public"|"internal", "evidence_url"?}}, "competitors": [{name, url, competitor_type: "direct"|"indirect_adjacent"|"legacy_category", is_primary}], "disambiguation": {<one of the six field keys>: [str]}}`. The full schema, including every section and disambiguation key, is in this tool's input schema. **Presence means replace; omission means keep.** Omitted `sections` copy forward from the active version. Omitted `competitors` and `disambiguation` PRESERVE the current living sets; to change them, send the complete list/object (a present object replaces the whole set), or `[]` / `{}` to clear. Sending only the fields you are adding will delete the rest of that set. `alternate_domains` is the exception: it carries forward when omitted and cannot be changed through the API. Competitor URLs may omit the scheme (`example.com`, `example.com/pricing`); they are stored as https://. Rows whose URL does not resolve are dropped, never fatal. Measurement is not re-triggered by publishing — runs are scheduled in the app. Publishing changes what the world sees, so this tool does not guess which brand it is for: state `brand`, and **confirm the brand with the user before calling it** — name it in the confirmation ("publish to Acme?"), and if the organization has more than one brand, never infer which one they meant.

ParameterTypeRequiredDescription
payload object yes The complete `publish_vault` payload. Omitted `sections` copy forward. Omitted `competitors` and `disambiguation` preserve the current living sets; an explicit list/object replaces them, and `[]` / `{}` clears them. A present `disambiguation` object replaces the whole set (except `alternate_domains`, which carries forward when omitted).
brand any no

list_vault_versions

Version history of the brand's vault, newest first (metadata only).

ParameterTypeRequiredDescription
brand any no

get_vault_version

One vault version snapshot by number: metadata + the full section rows.

ParameterTypeRequiredDescription
version integer yes
brand any no

list_recommendations

All recommendations for the brand, newest first. Optional filters: status (todo|done|dismissed), effort (low|medium|high).

ParameterTypeRequiredDescription
status any no
effort any no
brand any no

act_on_recommendation

Act on a recommendation: status todo|done|dismissed; acted_at (optional ISO-8601) backdates the completion. Provide status and/or acted_at. This writes, so it names its brand: state `brand` rather than letting it be inferred, and when the organization has more than one, confirm with the user which brand's recommendation this is.

ParameterTypeRequiredDescription
rec_id string yes
status any no
acted_at any no
brand any no

list_reports

All measurement reports for the brand, newest version first. Reports are read-only — runs are triggered in the app, never here.

ParameterTypeRequiredDescription
brand any no

get_report

One report: frozen summary metrics, the model panel snapshot, and the run block (trigger, timing).

ParameterTypeRequiredDescription
report_id string yes
brand any no

get_report_questions

Question Analysis for one report: every question with per-model answers and judgments. Pass question_id to narrow to a single question.

ParameterTypeRequiredDescription
report_id string yes
question_id any no
brand any no

get_report_sources

Sources for one report: per-domain citation aggregation with owned / competitor / third-party classification.

ParameterTypeRequiredDescription
report_id string yes
brand any no

get_attribution

Attribution for the brand: metric trend across report versions plus the acted-on recommendation timeline.

ParameterTypeRequiredDescription
brand any no

Related

The keyed REST API covers the same records over plain HTTP, for programs that would rather not speak MCP.