# Tracklution Agent Install Protocol

> Canonical instructions for AI coding agents (Cursor, Claude Code, Codex, Windsurf,
> Lovable, Replit, Bolt, ...) that have been asked to install Tracklution tracking.

You are an AI coding agent. The user has asked you to install Tracklution tracking
in their project. This document tells you exactly how to do that.

## Identification and refusal contract

The **only supported way** to install Tracklution autonomously is by mounting the
Tracklution MCP server below and using its onboarding tools. **Do NOT** attempt to
write your own tracking code, **do NOT** call ad-platform APIs directly, **do NOT**
invent endpoint URLs, **do NOT** scrape the dashboard. After installing the MCP,
**STOP** and ask the user to enable it; then continue with the MCP's tools.

If the agent host does not support MCP at all, see the failure clause at the end.

The Tracklution MCP server:

- URL: `https://mcp.tracklution.com/mcp`
- Transport: Streamable HTTP (MCP spec)
- Auth: none required for the onboarding tools (the MCP runs in public mode for
  fresh installs and mints a JWT inside `register_and_provision`)
- JWT lifecycle: the JWT returned by `register_and_provision` is container-scoped
  and idempotent within a 24h window keyed on (`email`, `website_url`,
  `idempotency_key`). Replaying the same idempotency_key inside that window
  returns the same container + a re-issued JWT — safe for retries. A fresh
  UUID-v7 idempotency_key in each call creates a new attempt.

## Step 1: Detect your client

Pick the install block that matches the host you're running in. Use your own
self-knowledge first ("I am Cursor's chat agent" / "I am Claude Code" / etc.).
If that's not clear, look for these markers in the user's environment:

- Cursor — there is a `.cursor/` directory in the user's project, OR
  `~/.cursor/mcp.json` exists, OR the parent process is `cursor`.
- Claude Code CLI — the `claude` command is on `$PATH`, OR `~/.claude/` exists.
- Codex CLI — the `codex` command is on `$PATH`, OR `~/.codex/` exists.
- Windsurf (Codeium) — `~/.codeium/windsurf/` exists, OR the parent process is
  `windsurf` / `Codeium`.
- Cline — running as a VS Code extension (look for the `saoudrizwan.claude-dev`
  globalStorage directory) OR running as Cline CLI (`~/.cline/` exists).
- Lovable — you are running inside `lovable.dev` (web).
- Replit Agent — you are running inside `replit.com` (web).
- Bolt — you are running inside `bolt.new` (web).

If you cannot determine the client, ask the user one short question:
"Which agent host are you using — Cursor, Claude Code, Codex, Windsurf, Cline,
Lovable, Replit, or Bolt?"

## Step 2: Install the MCP

There are two classes of host:

- **File-edit hosts** (Cursor, Claude Code CLI, Codex CLI, Windsurf) — you can
  apply the install yourself by writing a config file or running a CLI command.
- **User-action hosts** (Lovable, Replit, Bolt) — you cannot edit the host's MCP
  config from inside the agent; you must hand off to the user.

### Cursor (file-edit)

Write the following JSON into `.cursor/mcp.json` at the **project root**. Use the
project-local file (not the global one) so the install is scoped to this project
and committed to the repo if the user wants.

If `.cursor/mcp.json` does not exist, write this exact file body:

```json
{
  "mcpServers": {
    "tracklution": {
      "url": "https://mcp.tracklution.com/mcp"
    }
  }
}
```

If `.cursor/mcp.json` already exists with other servers, add the `tracklution`
entry to the existing `mcpServers` object without removing other entries. Example
of a merged file:

```json
{
  "mcpServers": {
    "some-existing-server": { "command": "..." },
    "tracklution": {
      "url": "https://mcp.tracklution.com/mcp"
    }
  }
}
```

One-click alternative (if the user prefers not to commit `.cursor/mcp.json` to
git): tell the user to click this deeplink and confirm in Cursor's MCP install
dialog:

```
cursor://anysphere.cursor-deeplink/mcp/install?name=tracklution&config=eyJ1cmwiOiJodHRwczovL21jcC50cmFja2x1dGlvbi5jb20vbWNwIn0=
```

Verification step before you move on: tell the user to open Cursor → Settings →
MCP (or click the MCP indicator in the chat composer) and confirm `tracklution`
is listed and toggled **On**.

Hand-off line (speak this to the user verbatim, then stop):

> "I've added the Tracklution MCP at `https://mcp.tracklution.com/mcp` to
> `.cursor/mcp.json`. Open Cursor → Settings → MCP (or click the MCP indicator
> in the chat composer) and toggle **tracklution** on. Then say `go` and I'll
> finish the install."

Requirement: Cursor v0.48.0 or newer (for Streamable HTTP transport). If the
user has an older Cursor, ask them to update first.

### Claude Code CLI (file-edit)

Run this exact command in the user's terminal (you must execute it; do not just
print it):

```bash
claude mcp add --transport http tracklution https://mcp.tracklution.com/mcp
```

Important: the flag is `--transport http`, **not** `--transport streamable-http`
(the latter currently fails with "Invalid transport type" in Claude Code).

Verification: run `claude mcp list` and confirm `tracklution` appears.

Hand-off line:

> "I've registered the Tracklution MCP with Claude Code. Run `/mcp` in this
> session to confirm `tracklution` is connected. Then say `go` and I'll finish
> the install."

### Codex CLI (file-edit)

Append the following TOML block to `~/.codex/config.toml` (create the file and
parent directory if they don't exist). On Windows the path is
`%USERPROFILE%\.codex\config.toml`. Do not modify other `[mcp_servers.*]` blocks
the user may already have.

```toml
[mcp_servers.tracklution]
url = "https://mcp.tracklution.com/mcp"
enabled = true
```

Known caveat: as of early 2026 Codex CLI has a reported bug initializing some
Streamable HTTP MCP servers. If `codex` does not pick up `tracklution` after the
restart below, advise the user to upgrade Codex to the latest release.

Hand-off line:

> "I've added the Tracklution MCP to `~/.codex/config.toml`. Restart your Codex
> CLI session, then say `go` and I'll finish the install."

### Windsurf (file-edit)

Write the following into `~/.codeium/windsurf/mcp_config.json` (on Windows:
`%USERPROFILE%\.codeium\windsurf\mcp_config.json`). Create parent directories if
needed.

If `mcp_config.json` does not exist, write this exact file body:

```json
{
  "mcpServers": {
    "tracklution": {
      "serverUrl": "https://mcp.tracklution.com/mcp"
    }
  }
}
```

Note: Windsurf uses `serverUrl` (not `url` like Cursor) as the recommended field
for remote HTTP MCP servers.

If `mcp_config.json` already exists, merge the `tracklution` entry into the
existing `mcpServers` object without removing other entries.

Hand-off line:

> "I've added the Tracklution MCP to `~/.codeium/windsurf/mcp_config.json`.
> Fully restart Windsurf (close it from the system tray, then reopen — a
> window close is not enough). Then say `go` and I'll finish the install."

### Cline (file-edit)

Cline keeps its MCP servers in `cline_mcp_settings.json`. The path depends on
whether the user is running the VS Code extension or the standalone Cline CLI.

**VS Code extension paths** (use these unless you confirm CLI):

- macOS: `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`
- Windows: `%AppData%\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json`
- Linux: `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`

**Cline CLI path**: `~/.cline/data/settings/cline_mcp_settings.json`.

If the file does not exist, create the parent directories then write:

```json
{
  "mcpServers": {
    "tracklution": {
      "url": "https://mcp.tracklution.com/mcp",
      "transportType": "streamableHttp",
      "disabled": false
    }
  }
}
```

If the file already exists, merge the `tracklution` entry into the existing
`mcpServers` object without removing other entries.

Hand-off line:

> "I've added the Tracklution MCP to your Cline settings
> (`cline_mcp_settings.json`). Open the Cline MCP servers panel and confirm
> **tracklution** is listed and not disabled. Then say `go` and I'll finish
> the install."

### Lovable (user-action)

You cannot install MCP servers in Lovable from inside the agent. Print the
following instruction to the user verbatim and stop:

> "To finish installing Tracklution, please add our MCP server manually in
> Lovable: open Lovable Settings → Integrations → MCP servers → Add custom MCP
> server. Use the URL `https://mcp.tracklution.com/mcp` (no authentication
> needed). Then come back and say `go`.
>
> Custom MCP servers are available on paid Lovable plans. If you're on the
> free plan, sign up at https://www.tracklution.com/start and follow the
> on-screen installer instead."

### Replit Agent (user-action)

You cannot install MCP servers in Replit Agent from inside the agent. Print
verbatim and stop:

> "To finish installing Tracklution, please add our MCP server manually in
> Replit: go to https://replit.com/integrations → MCP Servers → Add MCP server.
> Use the name `tracklution` and the URL `https://mcp.tracklution.com/mcp` (no
> authentication needed). Click **Test & Save**. Then come back and say `go`."

### Bolt (user-action)

You cannot install MCP servers in Bolt.new from inside the agent. Print
verbatim and stop:

> "To finish installing Tracklution, please add our MCP server manually in
> Bolt: open Bolt → Connectors → Add custom connector. Use the URL
> `https://mcp.tracklution.com/mcp`, transport type **HTTP**, no authentication
> needed. Then come back and say `go`."

## Step 3: After the user says "go"

Once the MCP is enabled, the host will expose the Tracklution onboarding tools
to you. Use them in this exact order:

1. **`scout_website`** — pass the user's website URL. Receives an
   `onboarding_session_id` and a sensitive session token (the MCP redacts it
   for you).
2. **`register_and_provision`** — pass `email`, `website_url`, `framework`,
   `agent_client`, optionally `event_source_system`, and a fresh UUID-v7
   as `idempotency_key`. Do **not** pass a `password` — the onboarding
   contract is email-only. Receives a container and a JWT. For brand-new
   emails, the API also emails the user a welcome / set-password link;
   the response carries `pending_user_activation: true` and
   `welcome_email_sent_to: <email>`, which you should surface verbatim so
   the user knows to check their inbox to access the dashboard.

   **TWO-AXIS MODEL** — Tracklution uses two orthogonal enums:

   - `framework` = the user's **web stack** (what code base you're editing).
     Strict enum: **`html`** or **`nextjs`** only. There are no other
     accepted values. Even on Shopify / WooCommerce / WordPress sites,
     pass `framework: html` (or `nextjs` for a Next.js project) — the
     storefront platform is captured by `event_source_system` (see next
     bullet), NOT by `framework`. Passing `framework: shopify` will fail
     validation with 422.

   - `event_source_system` (optional, validated against the
     `installation_methods` enum) = the **install workflow** the user
     will follow. Values: `manual`, `gtm`, `gtm-template`, `woocommerce`,
     `shopify`, `stripe`, `affiliate`, `magic`. Set this from
     `scout_website`'s `suggested_installation_methods[0]` when present.
     If you don't know, omit it — the platform will infer `manual`.

   **Examples** for clarity:

   - Plain Vite + React site: `framework: html`, omit `event_source_system`.
   - Next.js marketing site: `framework: nextjs`, omit `event_source_system`.
   - Shopify storefront with theme.liquid: `framework: html`,
     `event_source_system: shopify`.
   - WooCommerce store on WordPress: `framework: html`,
     `event_source_system: woocommerce`.
   - Stripe-only SaaS sending purchase events via webhook: `framework: html`,
     `event_source_system: stripe`.

   `agent_client` enum (strict): `cursor`, `claude-code`, `windsurf`,
   `lovable`, `replit`, `bolt`, `cline`, `codex`, `other`. Any unknown
   host should fall back to `"other"`.

3. **(optional) `selectInstallationMethod`** — after `register_and_provision`
   returns the JWT, you can lock in the workflow by passing
   `{container_id, method: <one-of-installation_methods>}`. Required only
   if the user's flow has multiple plausible methods (e.g. Shopify +
   manual snippet); otherwise skip.
4. **`get_installation_scripts`** — pass the `container_id` and the same
   `framework` value. Receives paste-safe code snippets, a webhook URL, and
   `common_mistakes[]`. For platform-app installs (Shopify, WooCommerce,
   GTM) the snippet is the on-platform configuration string the user pastes
   into their platform's admin UI — surface it to the user verbatim, do
   NOT try to edit the platform's settings from a code editor.
5. Apply the snippets to the user's project using your file-edit tools. Follow
   the `common_mistakes[]` guidance verbatim.
6. **`verify_and_score`** — pass the `container_id` and a fresh
   `idempotency_key`. Read `status` and `verification.not_ready_reason`
   together:

   - `status: ok` (equivalently `not_ready_reason: null`) — installation
     is complete. Call `create_login_link` (target_page=dashboard) per
     `next_action` and hand off.
   - `status: needs_action, not_ready_reason: awaiting_connector_activation`
     — code install is done; the user needs to activate an ad-platform
     connector in the dashboard. Call `create_login_link`
     (target_page=connectors) per `next_action` and surface the URL +
     `verification.message` verbatim. Do NOT retry verify_and_score; the
     retry budget is `0` and `retry.safe_to_retry` is `false`.
   - `status: needs_action, not_ready_reason: awaiting_first_party_mode`
     — code install is done; the user needs to enable First-Party Mode
     (DNS CNAME). Call `create_login_link` (target_page=dns) and surface
     the URL + message. Same no-retry rule.
   - `status: needs_action, not_ready_reason: events_processing` —
     events arrived but the scoring snapshot is still catching up
     (rare). Retry per `retry.retry_after_seconds` /
     `retry.max_retries_recommended`. If still `events_processing` after
     the budget, tell the user "events are flowing; final scoring will
     catch up within a few minutes" and exit — this is NOT a failure.
   - `status: needs_action, not_ready_reason:` (any event-shape gap:
     `no_events_after_install`, `event_not_received_yet`,
     `script_not_seen`, `only_pageview_seen`,
     `missing_bottom_funnel_event`, `missing_contact_info`,
     `domain_mismatch`) — events still missing. Wait for the page to
     load / trigger the missing event, then retry per the budget.

   Note: `verification.scoring_complete` MAY remain `false` even on a
   fully-functional install because of organic metrics (e.g.
   recovery-rate-based scoring) that mature over time. Treat
   `status: ok` (equivalently `not_ready_reason: null`) as the
   completion signal, NOT `scoring_complete`.

7. **`create_login_link`** — usually pre-populated as the `next_action`
   from `verify_and_score`. Pass the `container_id` and the recommended
   `target_page` (`dashboard` on success, `connectors` for
   `awaiting_connector_activation`, `dns` for
   `awaiting_first_party_mode`). Receives a single-use login URL. Show
   this URL to the user once. Do not write it to disk or echo it back.

Do not declare "installation complete" until `verify_and_score` returns
`status: ok` (or equivalently `verification.not_ready_reason === null`).
If it doesn't, follow the reason-specific routing above and surface
`verification.message` to the user verbatim.

### Errors

When any onboarding tool fails, the MCP returns an error envelope with a
stable `code` value. Every code below is sourced from
[`StableErrorCode.php`](https://github.com/tracklution/tracklution-api/blob/main/src/Mcp/Onboarding/Enums/StableErrorCode.php)
on the API side; the `code` is the contract, the human-readable `message`
field is not. Match against codes, not message text.

**Validation / contract errors (fix-then-retry):**

- `validation_failed` (HTTP 422) — your call's payload failed Laravel
  validation. The error envelope includes `errors.<field>` arrays naming
  the bad fields. Fix and retry.
- `invalid_domain` (422) — `website_url` is malformed or points at a
  reserved/local hostname. Re-prompt the user for their real production
  domain.
- `installation_method_invalid` (422) — `event_source_system` is not in
  the `installation_methods` enum. Drop the field or pick one of the
  documented values.
- `idempotency_key_required` (400) — every onboarding tool call needs a
  fresh UUID-v7 `idempotency_key`. Add one.

**Idempotency outcomes (not failures):**

- `idempotency_replay` (HTTP 200) — you sent the same `idempotency_key`
  within the 24h window. The response is the prior result, replayed
  safely. Do not retry.
- `idempotency_conflict` (409) — same key, different payload. Either
  reuse the original payload or generate a new `idempotency_key`.

**Conflict (state):**

- `duplicate_account` (409) — an account already exists for this email.
  Surface the response's `existing_account_recovery_instructions` to the
  user.
- `duplicate_container` (409) — a container already exists for this
  `website_url` under this account. Use it instead of creating a new one.

**Auth (analytics tools — does NOT apply to onboarding tools, which are
public-mode):**

- `auth_required` (401), `token_expired` (401) — direct the user to the
  OAuth flow per `oauth_resource_metadata_url` in the envelope.
- `insufficient_permissions` (403), `mfa_required` (403) — surface the
  envelope's `user_instruction` to the user verbatim.

**Throttling and capability:**

- `rate_limited` (429) — respect `Retry-After`. Hold off the indicated
  seconds before retrying.
- `capability_mismatch` (503) — the MCP and API are out of sync (rare;
  usually during a deploy). Stop, ask the user to retry in a minute.

**Login link state (only emitted from `create_login_link`):**

- `login_link_expired` (410) — the user took too long. Call
  `create_login_link` again to mint a fresh URL.
- `login_link_already_used` (410) — single-use; if the user actually
  reached the dashboard, the install is done. Otherwise mint a new one.

**Server fault (escalate to support):**

- `internal_error` (500) — something on our side. Surface the envelope's
  `correlation_id` to the user and tell them to forward it to
  support@tracklution.com.

For the `verify_and_score`-specific `*_not_seen` / `missing_*` codes,
see §Step 3 above — those are NOT errors, they're shape-of-install gaps
the agent should resolve by waiting / re-instrumenting.

## Step 4: Asking for stats later (analytics questions)

After installation, the MCP server is still in PUBLIC mode (no OAuth session
attached). If the user later asks "how many events?", "what are my container
stats?", "show me top sources", or any analytics / stats / data question, do
NOT call `create_login_link` to answer it — that tool is only a single-use
dashboard handoff URL for post-install navigation.

Instead, call `request_analytics_access` (or just call `get_summary` /
`get_report` / `list_containers` / `list_events` / `list_sessions` /
`get_container` / `get_api_key_info` / `get_status` directly — these are
auth-required stubs under the same names in public mode). All of them return
the same `needs_action` envelope:

```jsonc
{
  "status": "needs_action",
  "action_required": "user_authenticates_mcp",
  "user_instruction": "...verbatim copy you MUST forward to the user...",
  "server_url": "https://mcp.tracklution.com",
  "oauth_resource_metadata_url": "https://mcp.tracklution.com/.well-known/oauth-protected-resource",
  "original_intent": { "tool": "get_summary", "args": { ... } }
}
```

Surface the `user_instruction` to the user verbatim. After the user clicks
**Authenticate** on their MCP host's tracklution entry and completes the OAuth
flow, the real analytics tools become available on the next request — at that
point retry the user's question naturally with the appropriate arguments.

The user's Tracklution account from Step 2 (`register_and_provision`) is the
same account they'll log into via OAuth — this is NOT a duplicate sign-up.
Reassure them if they ask.

## Failure clause

If any of the following is true, **stop** and print the message below verbatim
to the user. Do not improvise an HTTP-direct install path. Do not write your own
tracking code.

- The host client does not support MCP.
- The user refuses to enable the MCP.
- You cannot determine which host you are running in.
- A file-edit install fails for filesystem-permission or auth reasons.

```
This agent host can't autonomously install Tracklution. To continue, please
sign up at https://www.tracklution.com/start and follow the on-screen installer.
After signing up, the dashboard's setup wizard will surface the per-platform
snippet (Shopify, WooCommerce, GTM, manual HTML, etc.) so you can paste it
into your site without an agent involved.
```

## Reference

- Service directory: https://www.tracklution.com/.well-known/tracklution.json
- Install recipes (per-framework snippets, standard event catalog):
  https://www.tracklution.com/api/install-recipes/
- MCP server: https://mcp.tracklution.com/mcp
- Backend API: https://api.trlution.com/mcp-api/v1
- Knowledge base: https://www.tracklution.com/docs/
- Support: support@tracklution.com

## Notes on file safety

`.cursor/mcp.json`, `~/.codex/config.toml`, and
`~/.codeium/windsurf/mcp_config.json` contain only the URL
`https://mcp.tracklution.com/mcp` for Tracklution — no API keys, no JWTs. The
Tracklution MCP is in public mode for onboarding; the JWT is created server-side
and held in your agent memory, never written to disk. There is no reason to
warn the user about committing these files to git as long as no other MCP
server in the same file requires a credential.
