API & MCP

The Sonar MCP server

How the Sonar MCP server works, how to authenticate with an API key, and what requests and responses look like.

Sonar's data is available through an MCP server. MCP (the Model Context Protocol) is the standard way AI assistants such as Claude, Cursor and ChatGPT connect to other tools, and it's also a simple JSON API you can call from your own code.

Use it to:

  • Ask an AI assistant about your analytics. See Connecting AI assistants.
  • Pull Sonar data into your own scripts and reports, such as a weekly Slack message or a client dashboard. See Code examples.

Everything is read-only: the server can't change your sites, settings or data.

Before you start

  • You need a Pro or Agency plan.
  • Create an API key in Sonar: open the avatar menu, then Settings → API keys. The key starts with sonar_ and is shown only once, so copy it straight away.
  • A key can read every site you can see in Sonar, and nothing else. Revoke it from the same page if it's ever shared by mistake; it stops working straight away.

The endpoint

POST https://sonar-app.co.uk/api/mcp

Every request needs these headers:

Header Value
Authorization Bearer sonar_YOUR_KEY
Content-Type application/json
Accept application/json, text/event-stream

The Accept header must list both types, as the MCP standard requires; without it the server answers 406.

Requests

Requests use JSON-RPC 2.0. There are two methods you'll use:

  • tools/list returns every tool, with a description and a JSON Schema for its arguments. See the Tool reference.
  • tools/call runs one tool.
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_overview",
    "arguments": { "site_id": 50, "period": "30d" }
  }
}

The server keeps no state between requests, so you don't need to send initialize first or keep a session open. MCP client libraries still send it, and that's fine.

Responses

A tool's result is JSON, returned as text inside result.content, as MCP requires:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"period\": \"last 30 days\", \"current\": {\"visitors\": 91, \"pageviews\": 175, \"conversions\": 0, \"avg_session_seconds\": 33}, ...}"
      }
    ]
  }
}

Parse result.content[0].text as JSON to get the data. With jq:

jq -r '.result.content[0].text | fromjson'

Errors

What went wrong HTTP status What you get
No key, or not a Sonar key 401 error.message: "Missing or malformed API key…"
Key revoked or unknown 401 error.message: "This API key is not valid…"
Plan doesn't include the API 403 error.message: "API access is part of the Pro and Agency plans…"
A tool couldn't run, such as a site you can't see or an invalid argument 200 result.isError is true, and result.content[0].text explains why

Good to know

  • Periods are 24h, 7d, 30d, 90d or all (all time), and always end now. campaigns also takes exact from and to dates.
  • Bots are left out, as they are in the app, so the numbers match your dashboard.
  • Visitors' IP addresses are never returned. Visit IDs (id and session_id) are encrypted: use them with get_session as they are, on the same site.
  • Ask Sonar answers take a few seconds, because Sonar writes and runs a query for each question. Other tools usually answer in under a second.
  • Results are capped: 50 rows for top lists, 100 campaign rows, 20 visits per page, and 200 rows for Ask Sonar.