All posts

Connecting Your Analytics to Claude Desktop with MCP

September 2, 20268 min read

What MCP actually is

The Model Context Protocol is an open standard for exposing tools — callable actions — to AI clients. Claude Desktop, Cursor and others speak it. Publish an MCP server and any of them can use your data without a custom integration each time.

For analytics that matters, because the natural interface to a dataset is a question, not a dashboard. "What were my top pages last week and how did conversions trend?" is faster to ask than to click.

One registry, two surfaces

The mistake is building the AI integration twice: once for an in-app assistant, once for MCP. InsightsTrack defines tools once, in a shared registry:

{
  name: 'get_top_pages',
  description: 'Most-visited pages for a time range…',
  inputSchema: { /* JSON Schema */ },
  async run(args, ctx) { /* returns a result envelope */ }
}

The in-dashboard assistant and the external MCP server both read that registry. A tool written once appears in both. The description field is not documentation — it is the instruction manual the model reads to decide whether to call the tool, so it is worth writing carefully.

The MCP server is deliberately thin

It would be tempting to have the MCP server open the analytics database directly. That is a mistake for two reasons.

First, DuckDB is single-writer. A second process holding the file conflicts with the API that owns it.

Second, authorization lives in the API. Site scoping, per-user roles and the site-members check are all enforced there. A direct database client would have to reimplement every one of them, and would drift.

So the MCP server proxies over HTTP with the user's connect token. It holds no credentials of its own and can only reach sites that user can already see.

Result envelopes, not raw rows

Each tool returns a structured envelope rather than a blob:

{
  summary: 'Top page was /pricing with 1,240 views (30d).',
  data: [ /* rows */ ],
  render: { type: 'table', columns: ['page','views'] },
  download: { csv: true, filename: 'top-pages-30d.csv' },
  deepLink: { label: 'Open Pages', to: '/pages?dateRange=30d' }
}

The summary is what the model speaks aloud. The data is what it reasons over. The render and deepLink let the in-app panel draw a chart and offer a one-click jump to the matching dashboard page. One shape serves a terminal, a chat panel and an API.

Bounding what the model sees

A wide date range can return tens of thousands of rows. Sent to a model, that is an expensive context window and a slow reply.

Every envelope passes through a cap before it reaches the model: a row limit and a hard character budget, with a note appended so the model knows it is looking at a sample rather than the whole set. The dashboard still renders the full result and the CSV still downloads everything — only the copy the model reads is bounded.

Connecting it

Generate a connect token in the dashboard, then point your client at the server:

{
  "mcpServers": {
    "insightstrack": {
      "command": "npx",
      "args": ["-y", "@insightstrack/mcp-server"],
      "env": {
        "INSIGHTTRACK_API_URL": "https://analytics.example.com",
        "INSIGHTTRACK_TOKEN": "your-connect-token"
      }
    }
  }
}

The token is scoped to one user and can be revoked without touching anything else.

What this is good for, and what it is not

It is good at exploratory questions, follow-ups that would each be a separate dashboard visit, and pulling numbers into something you are already writing.

It is not a replacement for a dashboard you check daily. A chart you read at a glance beats a sentence you have to request. The tools are read-only and analytics-scoped by design — no writes, no cross-site access.

Try InsightsTrack free

Open-source, self-hosted, privacy-first analytics — explore the live demo, no install required.

Open live dashboard