Read Apple, Google, Meta and TikTok ad campaigns and draft changes a person approves in Loop.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Bearer lmk_… API key from Loop: Settings › Team › API keys (read or propose scope).
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Prepare a new campaign as a proposal in the Loop inbox (needs a propose key). Same checks as the Loop form: connected channel, unique name, budget plan, guardrails. Nothing is sent to the ad platform; a person approves it in Loop.
Campaign name. Must not exist in the workspace or wait in the inbox already.
Total budget in major units of the currency the channel's ad account bills in (e.g. 30000 for ¥30,000). Channels whose account currency cannot be read use the workspace currency.
Ad platform key, e.g. meta, google_ads, tiktok, apple_search_ads, microsoft_ads, linkedin_ads, amazon_ads. Must be connected.
End, ISO 8601 with offset, e.g. 2026-11-30T23:59:59+09:00.
Destination URL (store or website).
Id of a pending draft to replace. Omit for a new draft.
Meta app event to optimise for instead of installs (see list_app_events).
ISO country codes, e.g. ["JP"].
Optional start, ISO 8601 with offset.
Meta placements. Omit to let Meta choose.
Google Ads App campaigns only: the 2nd to 5th headlines (Google needs 2–5, each up to 30 wide; full-width counts as 2).
The currency you mean the amount in (ISO 4217, e.g. JPY). If the ad account bills in another currency, nothing is saved.
Library file id from this workspace.
Creative id from this workspace.
This tool isn't marked read-only, so it may change something on the server or in an account you connected.
Pick your client. Values in capitals, like YOUR_TOKEN, are yours to fill in.
Run in a terminal
claude mcp add --transport http loop https://loopmarket.ing/api/mcp \ --header 'Authorization: Bearer YOUR_TOKEN'
Headers
Authorization (required): Bearer lmk_… API key from Loop: Settings › Team › API keys (read or propose scope).Prepare a new campaign as a proposal in the Loop inbox (needs a propose key). Same checks as the Loop form: connected channel, unique name, budget plan, guardrails. Nothing is sent to the ad platform; a person approves it in Loop.
| Argument | Type | Description |
|---|---|---|
| link | string | Destination URL (store or website). |
| name* | string | Campaign name. Must not exist in the workspace or wait in the inbox already. |
| amount* | number | Total budget in major units of the currency the channel's ad account bills in (e.g. 30000 for ¥30,000). Channels whose account currency cannot be read use the workspace currency. |
| channel* | string | Ad platform key, e.g. meta, google_ads, tiktok, apple_search_ads, microsoft_ads, linkedin_ads, amazon_ads. Must be connected. |
| draftId | string | Id of a pending draft to replace. Omit for a new draft. |
| endTime* | string | End, ISO 8601 with offset, e.g. 2026-11-30T23:59:59+09:00. |
| appEvent | string | Meta app event to optimise for instead of installs (see list_app_events). |
| bodyText | string | |
| headline | string | |
| countries | array | ISO country codes, e.g. ["JP"]. |
| startTime | string | Optional start, ISO 8601 with offset. |
| placements | array | Meta placements. Omit to let Meta choose. |
| moreHeadlines | array | Google Ads App campaigns only: the 2nd to 5th headlines (Google needs 2–5, each up to 30 wide; full-width counts as 2). |
| amountCurrency | string | The currency you mean the amount in (ISO 4217, e.g. JPY). If the ad account bills in another currency, nothing is saved. |
| libraryAssetId | string | Library file id from this workspace. |
| imageCreativeId | string | Creative id from this workspace. |
| youtubeVideoIds | array |
{
"type": "object",
"required": [
"name",
"channel",
"amount",
"endTime"
],
"properties": {
"link": {
"type": "string",
"description": "Destination URL (store or website)."
},
"name": {
"type": "string",
"description": "Campaign name. Must not exist in the workspace or wait in the inbox already."
},
"amount": {
"type": "number",
"description": "Total budget in major units of the currency the channel's ad account bills in (e.g. 30000 for ¥30,000). Channels whose account currency cannot be read use the workspace currency."
},
"channel": {
"type": "string",
"description": "Ad platform key, e.g. meta, google_ads, tiktok, apple_search_ads, microsoft_ads, linkedin_ads, amazon_ads. Must be connected."
},
"draftId": {
"type": "string",
"description": "Id of a pending draft to replace. Omit for a new draft."
},
"endTime": {
"type": "string",
"description": "End, ISO 8601 with offset, e.g. 2026-11-30T23:59:59+09:00."
},
"appEvent": {
"type": "string",
"description": "Meta app event to optimise for instead of installs (see list_app_events)."
},
"bodyText": {
"type": "string"
},
"headline": {
"type": "string"
},
"countries": {
"type": "array",
"items": {
"type": "string"
},
"description": "ISO country codes, e.g. [\"JP\"]."
},
"startTime": {
"type": "string",
"description": "Optional start, ISO 8601 with offset."
},
"placements": {
"type": "array",
"items": {
"type": "string"
},
"description": "Meta placements. Omit to let Meta choose."
},
"moreHeadlines": {
"type": "array",
"items": {
"type": "string"
},
"description": "Google Ads App campaigns only: the 2nd to 5th headlines (Google needs 2–5, each up to 30 wide; full-width counts as 2)."
},
"amountCurrency": {
"type": "string",
"description": "The currency you mean the amount in (ISO 4217, e.g. JPY). If the ad account bills in another currency, nothing is saved."
},
"libraryAssetId": {
"type": "string",
"description": "Library file id from this workspace."
},
"imageCreativeId": {
"type": "string",
"description": "Creative id from this workspace."
},
"youtubeVideoIds": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": true
}One campaign by id (from list_campaigns): numbers, daily spend, pending proposals and what was executed on it.
| Argument | Type | Description |
|---|---|---|
| window | integer | |
| campaignId* | string |
{
"type": "object",
"required": [
"campaignId"
],
"properties": {
"window": {
"enum": [
7,
14,
30,
90,
365
],
"type": "integer"
},
"campaignId": {
"type": "string"
}
},
"additionalProperties": false
}Home KPIs (spend, paid installs, cost per install vs the previous period), the honest headline, measurement caveats and how many proposals wait for a person.
| Argument | Type | Description |
|---|---|---|
| window | integer | Days. Default follows the data. |
{
"type": "object",
"properties": {
"window": {
"enum": [
7,
14,
30,
90,
365
],
"type": "integer",
"description": "Days. Default follows the data."
}
},
"additionalProperties": false
}Meta in-app events this workspace's app sends, usable as appEvent in create_launch_draft.
No arguments.
{
"type": "object",
"properties": {},
"additionalProperties": false
}Guardrailed budget tests: campaign, dates, daily range, stop rules and the latest verdict.
No arguments.
{
"type": "object",
"properties": {},
"additionalProperties": false
}Campaigns this workspace manages, with state, budget and platform-reported spend and installs in the window.
| Argument | Type | Description |
|---|---|---|
| window | integer |
{
"type": "object",
"properties": {
"window": {
"enum": [
7,
14,
30,
90,
365
],
"type": "integer"
}
},
"additionalProperties": false
}The inbox: proposals waiting for a person, ones a guardrail is holding, and the recent execution log (newest first) with each failure's message.
| Argument | Type | Description |
|---|---|---|
| limit | integer | Max rows of the execution log. Default 20. |
{
"type": "object",
"properties": {
"limit": {
"type": "integer",
"maximum": 100,
"minimum": 1,
"description": "Max rows of the execution log. Default 20."
}
},
"additionalProperties": false
}The workspace this API key belongs to (one per key), with its currency and the key's scope.
No arguments.
{
"type": "object",
"properties": {},
"additionalProperties": false
}Prepare a change to an existing campaign as a proposal in the Loop inbox (needs a propose key): set a Google Ads search campaign's languages, replace a running search ad's wording and landing page, change a daily budget by more than the per-change limit (the daily cap still applies), give a campaign a total cap that Loop pauses it at, set a max CPC you choose (campaign or ad group; above the workspace max CPC only with overrideWorkspaceCap), move the same daily amount from one campaign to another (total unchanged; both or neither), add an ad group (keywords and one responsive search ad) to a running Google Ads search campaign within its current budget, move a Google Ads or Apple Ads campaign's end date (optionally raising the total cap Loop watches so it does not pause before the new end), or keep only chosen keywords in an Apple Ads campaign and pause the rest. Nothing is sent to the ad platform; a person approves it in Loop.
| Argument | Type | Description |
|---|---|---|
| cap | number | total_cap: the total in the campaign's currency. |
| keep | array | keep_keywords: the keyword texts to keep enabled; the campaign's other enabled keywords are paused. |
| kind* | string | |
| adRef | string | replace_search_ad: the ad to replace (customers/…/adGroupAds/…). |
| since | string | total_cap: YYYY-MM-DD to count spend from (default today). |
| amount | number | budget_transfer: the daily amount to move. |
| family | string | replace_search_ad / add_ad_group: the workspace campaign family for utm_campaign. |
| maxCpc | number | max_cpc: the new max CPC in the account's currency. add_ad_group: the new ad group's max CPC (manual CPC campaigns only). |
| endDate | string | end_date: the new last day (YYYY-MM-DD) in the ad account's time zone. |
| landing | string | replace_search_ad / add_ad_group: landing page without utm_*; UTM is added by Loop. |
| fundedBy | string | budget_move: id of the campaign whose budget this uses (recorded only). |
| keywords | array | add_ad_group: the new ad group's keywords. |
| headlines | array | |
| languages | array | languages: codes such as vi, ja; empty = all languages. |
| campaignId* | string | The campaign id from list_campaigns (budget_transfer: the campaign that receives). |
| adGroupName | string | max_cpc: an ad group of a manual CPC campaign (omit for the campaign max CPC). add_ad_group: the new ad group's name. |
| dailyBudget | number | budget_move: the new daily budget in the campaign's currency. |
| utmCampaign | string | replace_search_ad / add_ad_group: utm_campaign for this campaign ({adgroup} allowed). |
| descriptions | array | |
| raiseTotalCap | boolean | end_date: if Loop watches a total cap on this campaign, raise it by the added days × daily budget. |
| fromCampaignId | string | budget_transfer: the campaign that gives the budget. |
| overrideWorkspaceCap | boolean | max_cpc: allow a value above the workspace max CPC. |
{
"type": "object",
"required": [
"kind",
"campaignId"
],
"properties": {
"cap": {
"type": "number",
"description": "total_cap: the total in the campaign's currency."
},
"keep": {
"type": "array",
"items": {
"type": "string"
},
"description": "keep_keywords: the keyword texts to keep enabled; the campaign's other enabled keywords are paused."
},
"kind": {
"enum": [
"languages",
"replace_search_ad",
"budget_move",
"total_cap",
"max_cpc",
"budget_transfer",
"add_ad_group",
"end_date",
"keep_keywords"
],
"type": "string"
},
"adRef": {
"type": "string",
"description": "replace_search_ad: the ad to replace (customers/…/adGroupAds/…)."
},
"since": {
"type": "string",
"description": "total_cap: YYYY-MM-DD to count spend from (default today)."
},
"amount": {
"type": "number",
"description": "budget_transfer: the daily amount to move."
},
"family": {
"type": "string",
"description": "replace_search_ad / add_ad_group: the workspace campaign family for utm_campaign."
},
"maxCpc": {
"type": "number",
"description": "max_cpc: the new max CPC in the account's currency. add_ad_group: the new ad group's max CPC (manual CPC campaigns only)."
},
"endDate": {
"type": "string",
"description": "end_date: the new last day (YYYY-MM-DD) in the ad account's time zone."
},
"landing": {
"type": "string",
"description": "replace_search_ad / add_ad_group: landing page without utm_*; UTM is added by Loop."
},
"fundedBy": {
"type": "string",
"description": "budget_move: id of the campaign whose budget this uses (recorded only)."
},
"keywords": {
"type": "array",
"items": {
"type": "object",
"required": [
"text",
"match"
],
"properties": {
"text": {
"type": "string"
},
"match": {
"enum": [
"exact",
"phrase",
"broad"
],
"type": "string"
}
},
"additionalProperties": false
},
"description": "add_ad_group: the new ad group's keywords."
},
"headlines": {
"type": "array",
"items": {
"type": "string"
}
},
"languages": {
"type": "array",
"items": {
"type": "string"
},
"description": "languages: codes such as vi, ja; empty = all languages."
},
"campaignId": {
"type": "string",
"description": "The campaign id from list_campaigns (budget_transfer: the campaign that receives)."
},
"adGroupName": {
"type": "string",
"description": "max_cpc: an ad group of a manual CPC campaign (omit for the campaign max CPC). add_ad_group: the new ad group's name."
},
"dailyBudget": {
"type": "number",
"description": "budget_move: the new daily budget in the campaign's currency."
},
"utmCampaign": {
"type": "string",
"description": "replace_search_ad / add_ad_group: utm_campaign for this campaign ({adgroup} allowed)."
},
"descriptions": {
"type": "array",
"items": {
"type": "string"
}
},
"raiseTotalCap": {
"type": "boolean",
"description": "end_date: if Loop watches a total cap on this campaign, raise it by the added days × daily budget."
},
"fromCampaignId": {
"type": "string",
"description": "budget_transfer: the campaign that gives the budget."
},
"overrideWorkspaceCap": {
"type": "boolean",
"description": "max_cpc: allow a value above the workspace max CPC."
}
},
"additionalProperties": false
}We read every tool's name, description and inputs the way the model will, looking for instructions hidden from you. A clean result means no warning signs, not a guarantee. Scanned Nov 12, 2026.