diff --git a/features/mcp.mdx b/features/mcp.mdx index 2fbbcb9..b602c53 100644 --- a/features/mcp.mdx +++ b/features/mcp.mdx @@ -1,15 +1,15 @@ --- -title: "Linkrunner MCP for Claude" +title: "Connect Linkrunner to AI assistants" sidebarTitle: "MCP" -description: "Ask Claude questions about your campaigns, attribution, and revenue from chat." +description: "Connect Linkrunner to Claude, ChatGPT, Claude Code, or Codex with MCP." icon: "sparkles" --- ## Overview -Linkrunner ships a Model Context Protocol (MCP) server that lets Claude answer -questions about your project's data directly from chat. Generate a token in the -dashboard, add it as a Custom Connector in Claude.ai, and ask things like: +Linkrunner ships a remote Model Context Protocol (MCP) server. Connect it to a +supported AI assistant, then ask questions about your project's data directly +from chat: - "How did my campaigns perform last week?" - "Compare my Meta vs Google performance for the last 7 days." @@ -18,18 +18,26 @@ dashboard, add it as a Custom Connector in Claude.ai, and ask things like: - "How much of my growth is organic?" - "Where do users drop off between install and checkout?" -Claude can also make a defined set of changes for you: +With the ad-accounts capability, the assistant can also read from the Meta and +Google accounts you have connected: + +- "How much did we spend on Meta last week, by campaign?" +- "Which Google ad groups are paused right now?" +- "What targeting does our retargeting ad set use?" +- "Compare Meta's reported spend with Linkrunner's installs for yesterday." + +The assistant can also make a defined set of changes for you: - "Create a Meta campaign for the Diwali sale pointing at our sneakers page." - "Pause every campaign with no installs in the last 30 days." - "Save a funnel for install, sign up, then purchase." -Editing tools act on your live project, not a sandbox. Claude can create and -edit campaigns, funnels, domains, store listings, webhooks, and postback event -mappings. A few of these send real traffic to third parties or cannot be undone. -Read [What Claude can change](#what-claude-can-change) before you share the -connector with a team. +Editing tools act on your live project, not a sandbox. The connector can create +and edit campaigns, funnels, domains, store listings, webhooks, and postback +event mappings. A few of these send real traffic to third parties or cannot be +undone. Read [What the connector can change](#what-the-connector-can-change) +before you share the connector with a team. ## Prerequisites @@ -37,11 +45,11 @@ connector with a team. - A project on Linkrunner that you have access to. - The **Admin** or **Member** role on that project. **Influencers** cannot mint MCP tokens. +- A supported client: Claude Code, Claude on Desktop or web, Codex CLI, or + ChatGPT web. - Editing tools run as the person who created the token, using that person's project permissions. A role that cannot edit in the dashboard cannot edit - through Claude either. -- A Claude.ai account on a plan that supports Custom Connectors (Pro, Team, or - Enterprise). + through the connector either. ## Setup @@ -50,46 +58,126 @@ connector with a team. 1. Open the [Linkrunner dashboard](https://dashboard.linkrunner.io) and switch to the project you want to connect. 2. Click **MCP** in the sidebar. -3. Click **Generate Token**. -4. The dashboard shows a one-time **Server URL** with the token already +3. Choose what the token may do: + - **Read Linkrunner data** is always on. + - **Make changes in Linkrunner** lets the assistant use the editing tools + listed under + [What the connector can change](#what-the-connector-can-change). Turn it + off for a read-only token. + - **Read connected ad accounts** lets the assistant read your connected + Meta and Google accounts. See + [What the connector can read from your ad accounts](#what-the-connector-can-read-from-your-ad-accounts). +4. Click **Generate Token**. +5. The dashboard shows a one-time **Server URL** with the token already embedded as a query parameter. Copy it now. The full URL will not be shown again. +Capabilities are fixed once a token exists. To change them, generate a new +token and revoke the old one. Tokens created before capabilities existed keep +read and change access, and no ad-account access. + The URL contains the token as a query parameter. Treat it like a password: don't paste it into shared documents, screenshots, or chat threads. Revoke it from the dashboard if it leaks, and generate a new one. -### 2. Add the connector in Claude - -Under **Register the Linkrunner MCP server**, the dashboard shows a ready-made -command with your server URL already filled in. Copy it and run it in a -terminal: - -```bash -claude mcp add linkrunner --scope user --transport http "" -``` - -The token is part of the URL, so no separate authentication step is needed. +### 2. Connect your client + +Choose your client in the dashboard to see its setup instructions. The +token is part of the Server URL, so no separate authentication step is needed. + +![Choose a client for the Linkrunner MCP connection](/images/mcp/connect-linkrunner.png) + + + + Copy the command under **Register the Linkrunner MCP server** and run it in + a terminal: + + ```bash + claude mcp add linkrunner --scope user --transport http "" + ``` + + Start a new Claude Code session and run `/mcp` to verify the connection. + + + + These steps work in Claude Desktop and at [claude.ai](https://claude.ai). + + On Free, Pro, or Max: + + 1. Open **Customize → Connectors**. + 2. Select **+ → Add custom connector** and name it `Linkrunner`. + 3. Paste the Server URL from the dashboard. + + On Team or Enterprise, an Owner or Primary Owner first adds the Server URL + under **Organization settings → Connectors**. Members then open + **Customize → Connectors** and connect Linkrunner. + + In a chat, select **+ → Connectors** and enable Linkrunner for that + conversation. + + See [Anthropic's custom connector guide](https://support.claude.com/en/articles/11175166-get-started-with-custom-connectors-using-remote-mcp) + for current plan and organization details. + + + + Copy the command under **Register the Linkrunner MCP server** and run it in + a terminal: + + ```bash + codex mcp add linkrunner --url "" + ``` + + Start a new Codex session and run `/mcp` to verify the connection. + + + + + Custom MCP apps currently work in ChatGPT on the web. ChatGPT Desktop is + not supported. Pro apps can use read and fetch tools only. Turn off + **Make changes in Linkrunner** before generating a token on Pro. Full MCP + support, including write tools, is rolling out in beta to Business and + Enterprise/Edu workspaces. + + + 1. Enable Developer mode: + - On Pro, open **Settings → Apps → Advanced settings**. + - On Business, an admin or owner enables Developer mode from + **Workspace settings → Apps → Create**. + - On Enterprise/Edu, an admin grants access under **Permissions & Roles + → Connected Data**. Enabled users then turn on Developer mode under + **Settings → Apps → Advanced settings**. + 2. Open **Settings → Apps → Create**. On Business, an admin or owner uses + **Workspace settings → Apps → Create** instead. + 3. Name the app `Linkrunner` and paste the Server URL as the MCP endpoint. + 4. Choose no authentication, then select **Scan tools** and **Create**. + 5. Test the draft in a new chat. To make it available to workspace members, + an admin or owner opens **Workspace settings → Apps → Drafts** and + selects **Publish**. + 6. Start a chat, select **Apps**, and choose Linkrunner. + + See [OpenAI's MCP apps guide](https://help.openai.com/en/articles/12584461) + for current plan and role requirements. + + -You only need one connector per project. If you manage several Linkrunner -projects, generate a token for each project and add a separate Claude -connector for each one with a clear name. Tokens are scoped to a single -project. +You only need one connection per client and project. If you manage several +Linkrunner projects, generate a token for each project and add a separate +connection with a clear name. Tokens are scoped to a single project. ### 3. Try it -Open a new chat in Claude, make sure your Linkrunner connector is enabled in -the chat's tools, and ask one of the starter prompts above. Claude will fetch -data through the connector and answer in plain language. +Open a new chat or CLI session, make sure Linkrunner is enabled, and ask one of +the starter prompts above. The assistant fetches data through the connector +and answers in plain language. -## What Claude can answer +## What you can ask -The connector exposes the following tools. Claude picks them automatically -based on your question. +The connector exposes the following tools. Your assistant picks them +automatically based on your question. | Tool | Use it for | |------|------------| @@ -113,27 +201,107 @@ based on your question. | `list_postback_event_maps` | Event mappings sent to each ad network, and which are network defaults. | | `get_webhook` | Whether a webhook is configured, its host, and its organic setting. | -Claude reads these tools' descriptions and decides which to call. You don't -need to invoke them manually. +Your assistant reads these tools' descriptions and decides which to call. You +don't need to invoke them manually. `list_campaigns`, `get_channel_performance`, and `get_organic_performance` -return only the metric groups Claude asks for (installs, spend, retention, -custom events, and so on). A group that wasn't requested is absent from the -response rather than reported as zero, so Claude never mistakes "not fetched" -for "measured zero". Ask for a metric by name and Claude will fetch it. +return only the metric groups your assistant asks for (installs, spend, +retention, custom events, and so on). A group that wasn't requested is absent +from the response rather than reported as zero, so the assistant never +mistakes "not fetched" for "measured zero". Ask for a metric by name and it +will fetch it. -## What Claude can change +## What the connector can read from your ad accounts -Ask in plain language ("create a campaign for the Diwali sale") and Claude picks -the right tool. Describe what you want changed, and confirm the summary it gives -you before it writes. +Tokens created with **Read connected ad accounts** get six more tools. They +read from the Meta Marketing API and the Google Ads API using the ad accounts +already connected to the project under Settings, and nothing else. They cannot +change anything on Meta or Google. + +| Tool | Use it for | +|------|------------| +| `enrich_campaigns_from_ad_network` | Adds the network's delivery figures (spend, impressions, clicks, reach, frequency, CPM) and its own reported outcomes beside your Linkrunner campaigns, with a note explaining any gap between the two. | +| `get_ad_insights` | Spend, impressions, clicks, CTR, CPC, CPM, reach and frequency as the ad network reports them, at account, campaign, ad set or ad group, or ad level, with the network's own conversions under `network_reported`. Optional breakdown by date, device, platform, country, age or gender. | +| `list_ad_entities` | Campaigns, ad sets or ad groups, ads, creatives, audiences, conversion actions and keywords, with status, budget and objective. | +| `get_ad_entity` | Every readable field of one object, such as an ad set's targeting or a campaign's bidding strategy. | +| `query_ad_network` | Anything the three tools above cannot express, using the network's own field names: video metrics, impression share, quality rankings, placements, hourly and search-term reports, country, age and gender on Google, attribution-window comparisons on Meta, filters on any field, sorting, and object types such as pixels, labels, saved audiences and asset groups. | +| `ad_network_reference` | Searches the field catalogs of both networks. Your assistant uses it to find exact names before a `query_ad_network` call, and to explain what a metric means. | + +The catalogs behind these tools come from the networks themselves: Google's +own field schema and Meta's official Marketing API field lists. Every name the +assistant passes is checked against them before a request is made, so it cannot +ask for a field that does not exist. A wrong name comes back with suggestions +instead of a failed call. Almost everything the two reporting APIs expose for +reading is reachable this way. + +### Which source answers which question + +Linkrunner stays the source of truth, and connecting an ad account does not +change any answer you already get. It adds to them. + +| Question | Source | Why | +|----------|--------|-----| +| Installs, reinstalls, sign-ups, events, purchases, revenue | Linkrunner | Attributed per device and per user, deduplicated across networks, and the same numbers as the dashboard. | +| Retention, active users, funnels, attribution detail | Linkrunner only | Only Linkrunner has the user-level history. | +| Cost per install, cost per sign-up, ROAS | Linkrunner | Synced ad spend divided by Linkrunner outcomes, the same as the dashboard. | +| Spend, impressions, clicks, reach, frequency, CTR, CPC, CPM | Ad network | The network bills it, and its live figure is the latest. | +| Status, budgets, bids, targeting, placements, creatives, audiences, keywords | Ad network only | Only the network knows. | + +The network's own conversion counts are still available, but always under a +`network_reported` label. Your assistant quotes them as the network's claim +("Meta reports 12 purchases") rather than as the count. When the two sides +disagree, it explains why (attribution window, view-through, SKAN delay, +cross-network deduplication, or a campaign that is not linked) instead of +picking one. + +A few things to know when you read network numbers: + +- **They are the ad network's numbers.** Meta and Google count conversions + with their own attribution windows and view-through rules. Linkrunner's + installs and sign-ups are measured differently. Your assistant reports the + two side by side and will not merge them. +- **Currency and timezone come from the ad account**, which may differ from + your project's display currency. +- **The last three days are provisional.** Networks keep adjusting spend and + conversions for a few days, so recent numbers can move. +- **Meta has no single "conversions" number.** Your assistant reads the actions + Meta reports (app installs, purchases, leads and so on) and names the one it + is quoting. +- **Google's "Conversions" column is not installs.** It adds up every + conversion action the account bids on (installs, trials, purchases). When + comparing installs, the assistant uses only Google's install conversion + actions (the DOWNLOAD category, such as Android first open) and names the + action it used. +- **Answers may come from a short cache.** Closed date ranges are cached for a + day, open ranges for ten minutes, and object lists for five minutes. The + assistant mentions the age when it matters. +- **Reads are budgeted.** Each token gets a daily allowance of live reads, and + Linkrunner reserves part of its Google Ads quota for attribution. When a + limit is hit, the assistant gets a wait time and a cached copy if one exists, + instead of an error loop. +- **Meta connections expire** roughly every 60 days. If your assistant reports that + the connection has expired, reconnect Meta under Settings. + +Only Meta and Google are covered. Accounts on other networks still appear in +`list_ad_accounts` for campaign setup but cannot be queried. + +A few Google reports need a specific resource rather than a breakdown. Country +uses `geographic_view`, age and gender use `age_range_view` and `gender_view`, +and search queries use `search_term_view`. The assistant picks these through +`query_ad_network` when you ask. + +## What the connector can change + +Ask in plain language ("create a campaign for the Diwali sale") and your +assistant picks the right tool. Describe what you want changed, and confirm the +summary it gives you before it writes. -Every tool below writes to your live project. Claude asks before making an -ambiguous change, but it is worth being specific about what you want, especially -for the tools marked as sending live traffic or being irreversible. +Every tool below writes to your live project. Your assistant asks before making +an ambiguous change, but it is worth being specific about what you want, +especially for the tools marked as sending live traffic or being irreversible. ### Campaigns @@ -146,7 +314,8 @@ for the tools marked as sending live traffic or being irreversible. A deep link has to be a full URL on one of your own tracking domains, for example `https://links.yourbrand.com/product/sneakers`. An external URL is -rejected. Ask Claude to list your domains if you are not sure which to use. +rejected. Ask your assistant to list your domains if you are not sure which to +use. Campaigns cannot be deleted through the connector. Pause them with @@ -193,12 +362,12 @@ first. Both tools deliver a real payload to your endpoint. Saving a webhook sends one by itself, because Linkrunner only stores an endpoint that answers `200` or `201`, so configuring a Slack webhook posts a real message to that channel. Ask -Claude to test afterwards only if you want a second message. +your assistant to test afterwards only if you want a second message. Replacing the URL cannot be undone. A project has one webhook, the new URL -overwrites the old one, and Claude cannot read the old value back to restore it -(`get_webhook` hides the path, because a Slack webhook path is itself a -credential). Keep a copy of the current URL before you replace it. +overwrites the old one, and the assistant cannot read the old value back to +restore it (`get_webhook` hides the path, because a Slack webhook path is +itself a credential). Keep a copy of the current URL before you replace it. ### Postback event mappings @@ -222,12 +391,13 @@ your own mapping for the same event. ## Built-in prompts -The connector also surfaces three starter prompts in Claude's prompt picker: +The connector includes three starter prompts. Clients that support MCP prompts +show them in their prompt picker: -- **Daily briefing** — yesterday's performance summary in 1-2 paragraphs. -- **Weekly review** — this week vs last week, with anomalies and a +- **Daily briefing:** yesterday's performance summary in 1-2 paragraphs. +- **Weekly review:** this week vs last week, with anomalies and a keep/cut/scale recommendation list. -- **Cut list** — the worst-performing active campaigns, with reasoning. +- **Cut list:** the worst-performing active campaigns, with reasoning. These are shortcuts. You can always ask the same questions in your own words. @@ -241,9 +411,12 @@ project. For each token you can see: - The user who created it. - The last time it was used (helps spot stale tokens). -Click the trash icon next to a token to revoke it. Anyone using that token in -Claude will be disconnected immediately. Revocation is not reversible. Mint a -new token if you need to reconnect. +Each token also shows its capabilities: **Read**, **Changes**, and **Ad +accounts**. + +Click the trash icon next to a token to revoke it. Anyone using that token will +be disconnected immediately. Revocation is not reversible. Mint a new token if +you need to reconnect. A project can have up to 10 active tokens at any time. If you hit the cap, revoke an unused token before creating a new one. @@ -256,8 +429,15 @@ revoke an unused token before creating a new one. project role, so the connector cannot do anything that person could not do in the dashboard. - Every change made through the connector is recorded with the action, the - token, the person who created it, and what was changed. -- Claude cannot delete campaigns, funnels, domains, or projects, cannot manage + token, the person who created it, and what was changed. Every ad-account + read is recorded the same way, with the account, the query type and the + date range, but never the returned data. +- Ad-account access is read-only and opt-in per token. The Meta and Google + credentials stay on Linkrunner's servers; the connector never receives them, + and they are removed from every response and log. +- An ad-account token can only reach accounts connected to its own project. + Passing another project's account is refused. +- The connector cannot delete campaigns, funnels, domains, or projects, cannot manage members, and cannot disconnect an ad-network account. Those stay in the dashboard. - The webhook URL is never fully readable. `get_webhook` returns the host but @@ -267,61 +447,85 @@ revoke an unused token before creating a new one. - Per-project token cap of 10 limits the blast radius if one leaks. -A token that can read your data can also change it. If you only want someone to -read, give them a token created by a project member whose role cannot edit, or -revoke the token when the work is done. +If you only want someone to read, turn off **Make changes in Linkrunner** when +you generate their token. A read-only token does not even list the editing +tools, so the assistant cannot attempt a change with it. ## Troubleshooting -**Claude says "Couldn't reach the MCP server."** +**The client says "Couldn't reach the MCP server."** The token may have been revoked, or the URL may be malformed. Generate a new -token, copy the full URL exactly, and re-add the connector. +token, copy the full URL exactly, and reconnect Linkrunner. -**Claude says it doesn't have access to the data.** -Make sure your Linkrunner connector is enabled in the current chat. Open the -chat's tool/connector menu and toggle it on. New chats don't always inherit -connector state from previous chats. +**The client does not show Linkrunner tools.** +Make sure Linkrunner is enabled in the current chat or session. Open the +client's app, tool, or connector menu and turn it on. New chats do not always +inherit connector state from previous chats. **Numbers don't match the dashboard.** The connector reads the same data the dashboard does. If you see a mismatch, -check that you and Claude are using the same date range and timezone, and -that any filters (network, platform) match. Linkrunner's ingestion is +check that you and the assistant are using the same date range and timezone, +and that any filters (network, platform) match. Linkrunner's ingestion is real-time, so there's no lag to account for. **Generating a token returns "Maximum of 10 active tokens reached."** You're at the per-project cap. Revoke an unused token from the same settings page before creating a new one. -**Claude says your deep link was rejected.** +**The assistant says your deep link was rejected.** A deep link has to be a full URL on one of your own tracking domains. An external address such as `https://example.com/promo` is refused, and so is a -bare path such as `/promo`. Ask Claude to list your domains and rebuild the URL -on one of them. +bare path such as `/promo`. Ask the assistant to list your domains and rebuild +the URL on one of them. **Saving a webhook fails with "Endpoint needs to return a status code of 200".** Linkrunner only stores an endpoint that answers `200` or `201` to the sample payload, and it must answer directly rather than redirecting. An endpoint that returns `202` or `204` cannot be saved. -**Claude cannot rename a custom channel.** -Renaming needs the channel's internal id, which Claude only sees right after it -creates or edits a campaign. In a later chat it has no way to look the id up. -Rename the channel in the dashboard instead. +**The assistant says the token does not have the ad-accounts capability.** +The token was generated without **Read connected ad accounts**. Generate a new +token with it turned on and re-add the connector. Capabilities cannot be added +to an existing token. + +**The assistant says the Meta or Google connection has expired.** +The stored ad-network credential is no longer valid. Open Settings, reconnect +the account, and ask again. Meta connections expire on their own roughly every +60 days. + +**The assistant says it has to wait before reading the ad account again.** +A rate limit or daily allowance was reached, either on the ad network or on the +token. The assistant is given a wait time and uses a cached answer when one +exists. Ask again after the wait, or narrow the question so fewer live reads +are needed. + +**Ad-network spend does not match the spend Linkrunner shows.** +The two are expected to differ. Ad-account tools return what Meta or Google +report for the ad account, in the account's currency and timezone. Linkrunner's +campaign spend is synced periodically and shown in your display currency. Ask +your assistant for both and it will label which is which. + +**The assistant cannot rename a custom channel.** +Renaming needs the channel's internal id, which the assistant only sees right +after it creates or edits a campaign. In a later chat it has no way to look the +id up. Rename the channel in the dashboard instead. -Asking Claude to rename a channel by editing the campaign does something +Asking the assistant to rename a channel by editing the campaign does something different. It points the campaign at a new channel and leaves the old one behind, which changes the channel's reporting name and splits its history. ## What this is not -- Not a full write API. Claude can make the changes listed above, and nothing - else. Use the [Linkrunner data API](/api-reference/data-apis) for +- Not a full write API. The connector can make the changes listed above, and + nothing else. Use the [Linkrunner data API](/api-reference/data-apis) for programmatic writes. - Not a way to delete things. Campaigns, funnels, domains, and projects can only be deleted from the dashboard. +- Not a way to edit ads. The ad-account tools read from Meta and Google; they + cannot pause a campaign, change a budget, or edit targeting there. - Not a generic SQL/Postgres bridge. It calls the same endpoints the dashboard uses, with the same access controls. - Not a substitute for webhooks. Use [webhooks](/features/webhooks) for diff --git a/images/mcp/connect-linkrunner.png b/images/mcp/connect-linkrunner.png new file mode 100644 index 0000000..0359dc9 Binary files /dev/null and b/images/mcp/connect-linkrunner.png differ