@buygolinks/mcp 0.8.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -5
- package/package.json +1 -1
- package/src/index.js +311 -15
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @buygolinks/mcp
|
|
2
2
|
|
|
3
|
-
Model Context Protocol (MCP) server for [buygolinks.com](https://buygolinks.com) — create and manage branded short links
|
|
3
|
+
Model Context Protocol (MCP) server for [buygolinks.com](https://buygolinks.com) — create and manage branded short links, campaigns, offers, and analytics from any MCP-compatible AI agent: Claude Desktop, Cursor, Cline, Continue, Hermes, and others.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -62,7 +62,7 @@ Most MCP clients use the same shape. Point them at the `npx -y @buygolinks/mcp`
|
|
|
62
62
|
|
|
63
63
|
| Tool | Description |
|
|
64
64
|
|---|---|
|
|
65
|
-
| `create_link` | Create a short link.
|
|
65
|
+
| `create_link` | Create a short link. The destination URL is passed through untouched (include your own affiliate tag). |
|
|
66
66
|
| `update_link` | Edit a link's title, source label, custom domain, password, archive state, or custom alias (e.g. `summer-sale` → `https://click.example.com/summer-sale`). |
|
|
67
67
|
| `list_links` | List your links with click counts. |
|
|
68
68
|
| `get_link_stats` | Per-link analytics (by day, country, device, browser, hour, weekday). |
|
|
@@ -74,16 +74,22 @@ Most MCP clients use the same shape. Point them at the `npx -y @buygolinks/mcp`
|
|
|
74
74
|
| `list_affiliate_networks` / `create_affiliate_network` / `update_affiliate_network` / `delete_affiliate_network` | Manage affiliate networks. Each network exposes a per-network postback URL (`/postback/n/{token}`) the user pastes into the network's S2S settings. |
|
|
75
75
|
| `list_offers` / `create_offer` / `update_offer` / `delete_offer` | Manage destination URLs ("offers"). Optionally tied to an affiliate network so the right conversion-param name is propagated on outbound clicks. |
|
|
76
76
|
| `list_landers` / `create_lander` / `update_lander` / `delete_lander` | Manage landing pages. Lander URLs may contain `{clickid}` tokens which the resolver expands. |
|
|
77
|
-
| `
|
|
77
|
+
| `list_traffic_sources` / `create_traffic_source` / `update_traffic_source` / `delete_traffic_source` | Manage **traffic sources** — the platforms you buy traffic from (Facebook, TikTok, Google, …). Each has a `name`, optional `platform`, and `token_params` mapping the source's click macros onto sub1..sub5 (e.g. Facebook sub1 = Ad ID `{{ad.id}}`). `list_traffic_sources` returns a `presets` catalog. Campaigns reference one via `traffic_source_id`. |
|
|
78
|
+
| `list_tracking_pixels` / `create_tracking_pixel` / `update_tracking_pixel` / `delete_tracking_pixel` | Manage ad-platform pixels (Facebook, Google, TikTok, …). Each pixel has a `default_event` (e.g. `Purchase`, `Lead`, `CompletePayment`) and, for Facebook/TikTok, an optional server-side Conversions API `capi_token`. `list_tracking_pixels` also returns a `platforms` catalog with each platform's valid `events`, `defaultEvent`, and `capi` support. Pixels fire on short-link clicks and can be chosen as a campaign's conversion pixel. |
|
|
79
|
+
| `list_campaigns` / `create_campaign` / `get_campaign` / `update_campaign` / `delete_campaign` | Manage campaigns. One campaign = one tracked short link + N offers (weighted) + N optional landers + a per-campaign postback URL + a pixel snippet. A campaign references one saved traffic source via `traffic_source_id` and an optional `conversion_pixel_id` + `conversion_event` that fires (server-side via CAPI when configured) when the campaign converts. Each campaign also returns a `click_url` (`…/click?cmp={code}`) — the defined lander CTA link that always resolves to an offer (never a dead page). Set `direct_slug` to run traffic straight to a lander with no tracker hop (`?utm_campaign={direct_slug}` stitches via the property tag); `fallback_url` is the never-dead-end guarantee. |
|
|
78
80
|
| `set_campaign_offers` / `set_campaign_landers` | Replace the campaign's offer/lander split. Items are `{ offer_id, weight }` / `{ lander_id, weight }`. |
|
|
81
|
+
| `get_campaign_flow` / `set_campaign_flow` | Read/replace the campaign's **routing flow graph** (n8n-style). Nodes are `source` (one entry) / `lander` / `offer`; weighted edges go source→lander, source→offer, or lander→offer. The resolver walks this graph per click so a specific lander routes to its own specific offers — use it to split-test offers, divert a lander to a different offer, or insert a landing page before an offer. Saving also re-derives the flat offer/lander pools. |
|
|
79
82
|
| `get_campaign_stats` | Full campaign report: visits, lander views, offer clicks, conversions, revenue, cost, profit, CTR/CR/EPC/ROI, breakdowns, recent events. |
|
|
80
|
-
| `list_properties` / `create_property` / `get_property` / `update_property` / `delete_property` | Manage **properties** — GTM/GA-style site containers. One property = one universal tag (`/t.js?p={key}`) the user installs on every page of their site. Every page auto-reports pageviews, time-on-page, scroll, outbound clicks, and conversions; campaign traffic auto-attributes by click. `create_property` returns the install `snippet`. |
|
|
83
|
+
| `list_properties` / `create_property` / `get_property` / `update_property` / `delete_property` | Manage **properties** — GTM/GA-style site containers. One property = one universal tag (`/t.js?p={key}`) the user installs on every page of their site. Every page auto-reports pageviews, time-on-page, scroll, outbound clicks, and conversions; campaign traffic auto-attributes by click. `create_property` requires an active custom domain (the tag is served first-party from the user's own domain so it tracks on iOS/Safari) and returns the bound `tracking_domain` + install `snippet`. |
|
|
81
84
|
| `set_property_campaigns` | Link/unlink campaigns to a property (many-to-many, for the source split in reports). Items are `{ campaign_id }`. |
|
|
82
85
|
| `get_property_stats` | Full property report: page views, unique visitors, outbound, conversions, revenue, avg time/scroll, top pages, source split (organic vs campaign), country/device/browser/referrer breakdowns, recent events. |
|
|
86
|
+
| `list_property_pages` | Distinct pages seen across all the user's properties (most-viewed first): `{ url, path, title, property_name, views }`. Any page here can be used as a campaign **lander** — pass its `url` to `create_lander`, then wire that lander into a campaign flow with `set_campaign_flow`. |
|
|
87
|
+
| `get_tracker_overview` | Unified Tracker dashboard across **both** streams (sites + campaigns) in one call: totals (page views / visitors / visits / offer clicks / conversions / revenue), daily activity, country/device/browser breakdowns, per-property + per-campaign rollups, a **per-UTM-campaign** rollup (auto-detected Google `utm_*` tags), and the 50 most recent events. The "is everything running and getting traffic?" view. |
|
|
88
|
+
| `get_tracker_events` | Unified event log across both streams with full **search / filter / sort** over auto-detected UTM tags. `q` free-text, `stream`, `event`, exact `utm_*` filters, `sort`/`dir` (ts\|revenue\|utm_campaign\|utm_source\|country), pagination. Returns events + `facets` (distinct UTM values) + `total`. |
|
|
83
89
|
|
|
84
90
|
## Example prompts
|
|
85
91
|
|
|
86
|
-
> "Create a short link for `https://www.amazon.com/dp/B0CFG6RB6T` with the title 'Anker GaN charger'."
|
|
92
|
+
> "Create a short link for `https://www.amazon.com/dp/B0CFG6RB6T?tag=mytag-20` with the title 'Anker GaN charger'."
|
|
87
93
|
|
|
88
94
|
> "How many clicks did I get last week? Which country drove the most?"
|
|
89
95
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buygolinks/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "MCP server for buygolinks.com — create and manage branded short links with Amazon affiliate geo-routing from any MCP-compatible AI agent (Claude Desktop, Cursor, Cline, Continue, Hermes).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/index.js
CHANGED
|
@@ -48,7 +48,7 @@ const TOOLS = [
|
|
|
48
48
|
{
|
|
49
49
|
name: 'create_link',
|
|
50
50
|
description:
|
|
51
|
-
'Create a new branded short link on buygolinks.com.
|
|
51
|
+
'Create a new branded short link on buygolinks.com. The link redirects to exactly the destination URL you provide — for Amazon links, include your own Associates tag in the URL (e.g. ?tag=yourtag-20); buygolinks passes it through untouched and does not rewrite or geo-route Amazon URLs. By default, the link is created under the user\'s first active custom domain (if any); pass `domain_id` to pin it to a specific one. Returns the short URL and link metadata (including `short_domain`, the hostname the link will resolve at).',
|
|
52
52
|
inputSchema: {
|
|
53
53
|
type: 'object',
|
|
54
54
|
properties: {
|
|
@@ -58,11 +58,11 @@ const TOOLS = [
|
|
|
58
58
|
},
|
|
59
59
|
title: {
|
|
60
60
|
type: 'string',
|
|
61
|
-
description: 'Optional human-readable title for the link.
|
|
61
|
+
description: 'Optional human-readable title for the link.',
|
|
62
62
|
},
|
|
63
63
|
source_label: {
|
|
64
64
|
type: 'string',
|
|
65
|
-
description: 'Optional source label (campaign tag)
|
|
65
|
+
description: 'Optional source label (campaign tag) for your own attribution.',
|
|
66
66
|
},
|
|
67
67
|
domain_id: {
|
|
68
68
|
type: 'number',
|
|
@@ -126,7 +126,7 @@ const TOOLS = [
|
|
|
126
126
|
{
|
|
127
127
|
name: 'get_overview_stats',
|
|
128
128
|
description:
|
|
129
|
-
'Get account-wide
|
|
129
|
+
'Get account-wide analytics for the user\'s branded short LINKS only: total link CLICKS, clicks by day, top countries, devices, browsers, referrers, peak hour and weekday. This covers the Links section ONLY — it does NOT include Tracker page views, site-property traffic, or campaign visits/conversions. For PAGE VIEWS (site properties) or campaign traffic use the Tracker tools instead: get_tracker_overview (clicks-vs-pageviews unified rollup), list_properties, or get_property_stats. If the user asks for "clicks AND page views" together, call BOTH this tool and get_tracker_overview and combine them — they read from separate event streams. Use this for "how are my links doing" type questions.',
|
|
130
130
|
inputSchema: {
|
|
131
131
|
type: 'object',
|
|
132
132
|
properties: {
|
|
@@ -240,7 +240,7 @@ const TOOLS = [
|
|
|
240
240
|
{
|
|
241
241
|
name: 'create_offer',
|
|
242
242
|
description:
|
|
243
|
-
'Create an offer (a destination URL: affiliate landing page, product page, etc).
|
|
243
|
+
'Create an offer (a destination URL: affiliate landing page, product page, etc). The URL is used exactly as given when redirecting — include any affiliate tag yourself. Link the offer to an affiliate_network_id so the resolver knows which postback param name to use when redirecting (e.g. some networks expect ?aff_sub=… instead of ?clickid=…).',
|
|
244
244
|
inputSchema: {
|
|
245
245
|
type: 'object',
|
|
246
246
|
properties: {
|
|
@@ -319,6 +319,105 @@ const TOOLS = [
|
|
|
319
319
|
inputSchema: { type: 'object', properties: { lander_id: { type: 'number' } }, required: ['lander_id'] },
|
|
320
320
|
},
|
|
321
321
|
|
|
322
|
+
// ─── Traffic sources ────────────────────────────────────────────────────
|
|
323
|
+
{
|
|
324
|
+
name: 'list_traffic_sources',
|
|
325
|
+
description:
|
|
326
|
+
'List the user\'s traffic sources (the platforms they buy traffic from — Facebook, TikTok, Google, …). Each source has a `name`, an optional `platform` (pixel-platform key), and `token_params`: how the source\'s click macros map onto the Google UTM params the resolver captures (utm_source/medium/campaign/term/content), e.g. Facebook utm_campaign = `{{campaign.id}}`, utm_content = Ad ID `{{ad.id}}`. The response also includes a `presets` catalog of ready-made sources with standard macros. Campaigns reference a source via `traffic_source_id` (see create_campaign / update_campaign).',
|
|
327
|
+
inputSchema: { type: 'object', properties: {} },
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
name: 'create_traffic_source',
|
|
331
|
+
description:
|
|
332
|
+
'Create a traffic source. `token_params` is an array of `{ slot, label, token }` where slot is one of utm_source/utm_medium/utm_campaign/utm_term/utm_content, label is a human name (e.g. "Campaign ID"), and token is the platform macro (e.g. "{{campaign.id}}"). Use `list_traffic_sources` first to copy a preset\'s token_params for the big platforms.',
|
|
333
|
+
inputSchema: {
|
|
334
|
+
type: 'object',
|
|
335
|
+
properties: {
|
|
336
|
+
name: { type: 'string', description: 'e.g. "Facebook".' },
|
|
337
|
+
platform: { type: ['string', 'null'], description: 'Optional pixel-platform key (facebook, google_ads, tiktok, bing, …) for the icon + conversion-pixel suggestion.' },
|
|
338
|
+
token_params: {
|
|
339
|
+
type: 'array',
|
|
340
|
+
items: {
|
|
341
|
+
type: 'object',
|
|
342
|
+
properties: {
|
|
343
|
+
slot: { type: 'string', enum: ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'] },
|
|
344
|
+
label: { type: 'string' },
|
|
345
|
+
token: { type: 'string' },
|
|
346
|
+
},
|
|
347
|
+
required: ['slot'],
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
required: ['name'],
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
name: 'update_traffic_source',
|
|
356
|
+
description: 'Update a traffic source\'s name, platform, token_params, or archive state. token_params replaces the whole set.',
|
|
357
|
+
inputSchema: {
|
|
358
|
+
type: 'object',
|
|
359
|
+
properties: {
|
|
360
|
+
traffic_source_id: { type: 'number' },
|
|
361
|
+
name: { type: 'string' },
|
|
362
|
+
platform: { type: ['string', 'null'] },
|
|
363
|
+
token_params: { type: 'array', items: { type: 'object', properties: { slot: { type: 'string' }, label: { type: 'string' }, token: { type: 'string' } }, required: ['slot'] } },
|
|
364
|
+
archived: { type: 'boolean' },
|
|
365
|
+
},
|
|
366
|
+
required: ['traffic_source_id'],
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
name: 'delete_traffic_source',
|
|
371
|
+
description: 'Delete a traffic source. Campaigns using it keep their data but their `traffic_source_id` is cleared.',
|
|
372
|
+
inputSchema: { type: 'object', properties: { traffic_source_id: { type: 'number' } }, required: ['traffic_source_id'] },
|
|
373
|
+
},
|
|
374
|
+
|
|
375
|
+
// ─── Tracking pixels ────────────────────────────────────────────────────
|
|
376
|
+
{
|
|
377
|
+
name: 'list_tracking_pixels',
|
|
378
|
+
description:
|
|
379
|
+
'List the user\'s tracking pixels (Facebook, Google Ads, TikTok, etc). Each pixel has a platform, pixel_id, a default_event it fires, an active flag, and has_capi_token (whether a server-side Conversions API token is set — the token itself is never returned). The response also includes a `platforms` catalog with each platform\'s valid `events`, its `defaultEvent`, and `capi` (whether server-side conversions are supported). Pixels fire on short-link clicks (account-wide by default, or per-link selection) and can be chosen as a campaign\'s conversion pixel.',
|
|
380
|
+
inputSchema: { type: 'object', properties: {} },
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
name: 'create_tracking_pixel',
|
|
384
|
+
description:
|
|
385
|
+
'Create a tracking pixel. `platform` is one of: facebook, google_ads, google_analytics, google_tag_manager, linkedin, twitter, tiktok, pinterest, snapchat, reddit, quora, bing, adroll. `default_event` must be a valid event for that platform (see the `platforms` catalog from list_tracking_pixels) — defaults to the platform default (e.g. Facebook "PageView"). For Facebook and TikTok you can pass a `capi_token` (server-side Conversions API access token) so campaign conversions fire server-to-server.',
|
|
386
|
+
inputSchema: {
|
|
387
|
+
type: 'object',
|
|
388
|
+
properties: {
|
|
389
|
+
platform: { type: 'string', description: 'Pixel platform key (facebook, google_ads, tiktok, …).' },
|
|
390
|
+
pixel_id: { type: 'string', description: 'The platform\'s pixel/dataset id.' },
|
|
391
|
+
label: { type: 'string', description: 'Optional friendly name.' },
|
|
392
|
+
default_event: { type: 'string', description: 'Event this pixel fires by default (must be valid for the platform).' },
|
|
393
|
+
capi_token: { type: 'string', description: 'Optional server-side Conversions API access token (Facebook/TikTok only).' },
|
|
394
|
+
active: { type: 'boolean', description: 'Whether the pixel is active. Defaults to true.' },
|
|
395
|
+
},
|
|
396
|
+
required: ['platform', 'pixel_id'],
|
|
397
|
+
},
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
name: 'update_tracking_pixel',
|
|
401
|
+
description: 'Update a tracking pixel\'s pixel_id, label, default_event, active flag, or CAPI token. Pass capi_token as an empty string or null to clear the stored token.',
|
|
402
|
+
inputSchema: {
|
|
403
|
+
type: 'object',
|
|
404
|
+
properties: {
|
|
405
|
+
pixel_id_row: { type: 'number', description: 'The id of the tracking_pixels row to update (from list_tracking_pixels).' },
|
|
406
|
+
pixel_id: { type: 'string', description: 'New platform pixel id.' },
|
|
407
|
+
label: { type: 'string' },
|
|
408
|
+
default_event: { type: 'string', description: 'New default event (must be valid for the pixel\'s platform).' },
|
|
409
|
+
capi_token: { type: ['string', 'null'], description: 'New Conversions API token, or empty/null to clear.' },
|
|
410
|
+
active: { type: 'boolean' },
|
|
411
|
+
},
|
|
412
|
+
required: ['pixel_id_row'],
|
|
413
|
+
},
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
name: 'delete_tracking_pixel',
|
|
417
|
+
description: 'Delete a tracking pixel. It is removed from any per-link selections and any campaign that used it as its conversion pixel (the campaign\'s conversion_pixel_id is cleared).',
|
|
418
|
+
inputSchema: { type: 'object', properties: { pixel_id_row: { type: 'number' } }, required: ['pixel_id_row'] },
|
|
419
|
+
},
|
|
420
|
+
|
|
322
421
|
// ─── Tracker: campaigns ─────────────────────────────────────────────────
|
|
323
422
|
{
|
|
324
423
|
name: 'list_campaigns',
|
|
@@ -329,17 +428,23 @@ const TOOLS = [
|
|
|
329
428
|
{
|
|
330
429
|
name: 'create_campaign',
|
|
331
430
|
description:
|
|
332
|
-
'Create a campaign. A campaign mints one short link (campaign.code) that the user pastes into Facebook / TikTok / etc. The resolver mints a click_id per visit, optionally routes through a lander (weighted), then redirects to an offer (weighted). Conversions come back via the campaign\'s postback URL or the affiliate network\'s postback URL. You can optionally attach offer_ids / lander_ids inline with matching offer_weights / lander_weights arrays for a one-call setup.',
|
|
431
|
+
'Create a campaign. A campaign mints one short link (campaign.code) that the user pastes into Facebook / TikTok / etc. The resolver mints a click_id per visit, optionally routes through a lander (weighted), then redirects to an offer (weighted). The response also includes a `click_url` (`…/click?cmp={code}`) — the DEFINED lander click URL the user pastes into their lander\'s CTA button; it always resolves to an offer (chosen by the flow graph), never a lander, never a dead page. For DIRECT-LINK campaigns (ad points straight at the lander, no tracker hop), set `direct_slug` and put `?utm_campaign={direct_slug}` on the ad URL — the property tag then stitches the page view to this campaign. `fallback_url` guarantees the click never dead-ends. Conversions come back via the campaign\'s postback URL or the affiliate network\'s postback URL. You can optionally attach offer_ids / lander_ids inline with matching offer_weights / lander_weights arrays for a one-call setup.',
|
|
333
432
|
inputSchema: {
|
|
334
433
|
type: 'object',
|
|
335
434
|
properties: {
|
|
336
435
|
name: { type: 'string' },
|
|
337
|
-
|
|
436
|
+
traffic_source_id: { type: ['number', 'null'], description: 'Id of one of the user\'s saved traffic sources (from list_traffic_sources) to attribute this campaign to. Preferred over the legacy `traffic_source` string. Mirrors the source name into the display label.' },
|
|
437
|
+
traffic_source: { type: 'string', description: 'Legacy structured traffic source key — one of: facebook, instagram, google, youtube, tiktok, microsoft, linkedin, twitter, pinterest, snapchat, reddit, native, email, organic, other. Prefer `traffic_source_id`.' },
|
|
438
|
+
traffic_source_label: { type: 'string', description: 'Free text display label.' },
|
|
338
439
|
country: { type: 'string', description: 'ISO2 default geo.' },
|
|
339
440
|
cost_model: { type: 'string', enum: ['not_tracked', 'cpc', 'cpm'] },
|
|
340
441
|
cost_value: { type: 'number' },
|
|
341
442
|
transition_mode: { type: 'string', enum: ['302', 'meta_refresh'] },
|
|
342
443
|
domain_id: { type: ['number', 'null'], description: 'Optional id of one of the user\'s active custom domains (from list_domains). The campaign\'s short link will resolve at that hostname. If omitted, defaults to the user\'s first active custom domain, or buygolinks.com if they have none. Pass null to force the platform default.' },
|
|
444
|
+
conversion_pixel_id: { type: ['number', 'null'], description: 'Optional id of one of the user\'s tracking pixels (from list_tracking_pixels) to fire when this campaign converts (server-side via the pixel\'s CAPI token when set). Pass null for no conversion pixel.' },
|
|
445
|
+
conversion_event: { type: 'string', description: 'Event name to fire on conversion (e.g. "Purchase", "Lead", "CompletePayment"). Must be a valid event for the conversion pixel\'s platform; defaults to that pixel\'s default_event.' },
|
|
446
|
+
direct_slug: { type: ['string', 'null'], description: 'Optional direct-link tag, unique per user (lowercased alnum + dashes, e.g. "bsu-fb-cold"). For running traffic STRAIGHT to a lander with no tracker hop: put `?utm_campaign={direct_slug}` on the ad URL and the property tag stitches the page view to this campaign. Returns 409 (direct_slug_taken) if another campaign already uses it. Pass null to clear.' },
|
|
447
|
+
fallback_url: { type: ['string', 'null'], description: 'Optional URL the click falls back to when the flow yields no live offer — the never-dead-end guarantee. Pass null to use the platform default.' },
|
|
343
448
|
offer_ids: { type: 'array', items: { type: 'number' } },
|
|
344
449
|
offer_weights: { type: 'array', items: { type: 'number' } },
|
|
345
450
|
lander_ids: { type: 'array', items: { type: 'number' } },
|
|
@@ -355,18 +460,24 @@ const TOOLS = [
|
|
|
355
460
|
},
|
|
356
461
|
{
|
|
357
462
|
name: 'update_campaign',
|
|
358
|
-
description: 'Update a campaign\'s name, country, cost model/value, transition mode, traffic source label, branded domain, or archive state. Use set_campaign_offers / set_campaign_landers to change the destination wiring. Pass domain_id to move the campaign\'s short link to a different custom domain, or null to send it back to buygolinks.com.',
|
|
463
|
+
description: 'Update a campaign\'s name, structured traffic source, country, cost model/value, transition mode, traffic source label, branded domain, conversion pixel/event, direct-link slug, fallback URL, or archive state. Use set_campaign_offers / set_campaign_landers (or set_campaign_flow) to change the destination wiring. Pass domain_id to move the campaign\'s short link to a different custom domain, or null to send it back to buygolinks.com.',
|
|
359
464
|
inputSchema: {
|
|
360
465
|
type: 'object',
|
|
361
466
|
properties: {
|
|
362
467
|
campaign_id: { type: 'number' },
|
|
363
468
|
name: { type: 'string' },
|
|
469
|
+
traffic_source_id: { type: ['number', 'null'], description: 'Id of a saved traffic source (from list_traffic_sources), or null to clear. Preferred over `traffic_source`.' },
|
|
470
|
+
traffic_source: { type: ['string', 'null'], description: 'Legacy structured traffic source key or null to clear.' },
|
|
364
471
|
traffic_source_label: { type: 'string' },
|
|
365
472
|
country: { type: 'string' },
|
|
366
473
|
cost_model: { type: 'string', enum: ['not_tracked', 'cpc', 'cpm'] },
|
|
367
474
|
cost_value: { type: 'number' },
|
|
368
475
|
transition_mode: { type: 'string', enum: ['302', 'meta_refresh'] },
|
|
369
476
|
domain_id: { type: ['number', 'null'], description: 'Id of one of the user\'s active custom domains, or null to move the campaign back to buygolinks.com.' },
|
|
477
|
+
conversion_pixel_id: { type: ['number', 'null'], description: 'Id of a tracking pixel to fire on conversion, or null to clear.' },
|
|
478
|
+
conversion_event: { type: 'string', description: 'Event the conversion pixel fires (must be valid for that pixel\'s platform).' },
|
|
479
|
+
direct_slug: { type: ['string', 'null'], description: 'Direct-link tag for utm_campaign-based stitching (unique per user), or null to clear. Returns 409 (direct_slug_taken) on collision.' },
|
|
480
|
+
fallback_url: { type: ['string', 'null'], description: 'Never-dead-end fallback URL when no live offer exists, or null to use the platform default.' },
|
|
370
481
|
archived: { type: 'boolean' },
|
|
371
482
|
},
|
|
372
483
|
required: ['campaign_id'],
|
|
@@ -415,6 +526,54 @@ const TOOLS = [
|
|
|
415
526
|
required: ['campaign_id', 'items'],
|
|
416
527
|
},
|
|
417
528
|
},
|
|
529
|
+
{
|
|
530
|
+
name: 'get_campaign_flow',
|
|
531
|
+
description:
|
|
532
|
+
'Get the campaign\'s routing flow graph (n8n-style). Returns { flow: { nodes, edges }, offers, landers }. Nodes are { id, kind: "source"|"lander"|"offer", ref_id, pos_x, pos_y, offer?, lander? } — exactly one "source" node (the traffic entry). Edges are { id, from_node_id, to_node_id, weight } and only go source→lander, source→offer (direct), or lander→offer; weight is the split-test weight. This graph is what the resolver walks per click, letting a specific lander route to its own specific offers.',
|
|
533
|
+
inputSchema: {
|
|
534
|
+
type: 'object',
|
|
535
|
+
properties: { campaign_id: { type: 'number' } },
|
|
536
|
+
required: ['campaign_id'],
|
|
537
|
+
},
|
|
538
|
+
},
|
|
539
|
+
{
|
|
540
|
+
name: 'set_campaign_flow',
|
|
541
|
+
description:
|
|
542
|
+
'Replace the campaign\'s routing flow graph. Body: { nodes: [{ tmp_id, kind, ref_id, pos_x, pos_y }], edges: [{ from, to, weight }] }. tmp_id is any client-stable string key; edges reference nodes by tmp_id. Provide exactly one node with kind="source" (ref_id null) as the traffic entry; "lander" / "offer" nodes set ref_id to a lander/offer id you own. Edges must go source→lander, source→offer, or lander→offer. weight is an integer split-test weight (higher = more traffic). Saving also re-derives the flat offer/lander pools, so legacy reports stay consistent. Use this to add a second offer for a split test, divert a lander to a different offer, or insert a landing page before an offer.',
|
|
543
|
+
inputSchema: {
|
|
544
|
+
type: 'object',
|
|
545
|
+
properties: {
|
|
546
|
+
campaign_id: { type: 'number' },
|
|
547
|
+
nodes: {
|
|
548
|
+
type: 'array',
|
|
549
|
+
items: {
|
|
550
|
+
type: 'object',
|
|
551
|
+
properties: {
|
|
552
|
+
tmp_id: { type: 'string' },
|
|
553
|
+
kind: { type: 'string', enum: ['source', 'lander', 'offer'] },
|
|
554
|
+
ref_id: { type: ['number', 'null'] },
|
|
555
|
+
pos_x: { type: 'number' },
|
|
556
|
+
pos_y: { type: 'number' },
|
|
557
|
+
},
|
|
558
|
+
required: ['tmp_id', 'kind'],
|
|
559
|
+
},
|
|
560
|
+
},
|
|
561
|
+
edges: {
|
|
562
|
+
type: 'array',
|
|
563
|
+
items: {
|
|
564
|
+
type: 'object',
|
|
565
|
+
properties: {
|
|
566
|
+
from: { type: 'string' },
|
|
567
|
+
to: { type: 'string' },
|
|
568
|
+
weight: { type: 'number' },
|
|
569
|
+
},
|
|
570
|
+
required: ['from', 'to'],
|
|
571
|
+
},
|
|
572
|
+
},
|
|
573
|
+
},
|
|
574
|
+
required: ['campaign_id', 'nodes', 'edges'],
|
|
575
|
+
},
|
|
576
|
+
},
|
|
418
577
|
{
|
|
419
578
|
name: 'get_campaign_stats',
|
|
420
579
|
description:
|
|
@@ -433,18 +592,19 @@ const TOOLS = [
|
|
|
433
592
|
{
|
|
434
593
|
name: 'list_properties',
|
|
435
594
|
description:
|
|
436
|
-
'List the user\'s properties with quick-look stats (page_views / unique_visitors / conversions / revenue). A property is a whole-site container: the user installs ONE universal tag (returned as `snippet`) on every page of their site and every page auto-reports pageviews, time-on-page, scroll depth, outbound clicks, and conversions. Page events auto-attribute to a campaign when a visitor arrives carrying that campaign\'s click.',
|
|
595
|
+
'List the user\'s properties with quick-look stats (page_views / unique_visitors / conversions / revenue). Best one-call tool for "page views per property" / "page views for each site". A property is a whole-site container: the user installs ONE universal tag (returned as `snippet`) on every page of their site and every page auto-reports pageviews, time-on-page, scroll depth, outbound clicks, and conversions. Page events auto-attribute to a campaign when a visitor arrives carrying that campaign\'s click.',
|
|
437
596
|
inputSchema: { type: 'object', properties: {} },
|
|
438
597
|
},
|
|
439
598
|
{
|
|
440
599
|
name: 'create_property',
|
|
441
600
|
description:
|
|
442
|
-
'Create a property (a GTM/GA-style site container). Returns the property with its public property_key and an install `snippet` — a single <script> tag the user pastes into the <head> of every page of their site. To record conversions, the site sets window.bglConversion = true (optionally window.bglRevenue) on a thank-you page, or calls window.bglFireConversion(revenue).',
|
|
601
|
+
'Create a property (a GTM/GA-style site container). Returns the property with its public property_key, the `tracking_domain` it\'s bound to, and an install `snippet` — a single GA/Facebook-style inline <script> block (a self-invoking function that sets up a conversion-queue stub and injects the async tag) the user pastes into the <head> of every page of their site. REQUIRES an active custom domain: the tag is served from the user\'s own domain so it stays first-party to their site, which is required for tracking to work on iOS/Safari (a tag loaded cross-site from buygolinks.com gets its visitor cookie partitioned/blocked). Defaults to the user\'s first active custom domain; pass `domain_id` (from list_domains) to pin a specific one. If the user has no active custom domain, returns error `domain_required` — have them add one via create_domain first. To record conversions, the site sets window.bglConversion = true (optionally window.bglRevenue) on a thank-you page, or calls window.bglFireConversion(revenue) — the queue stub makes that call safe even before the async tag finishes loading.',
|
|
443
602
|
inputSchema: {
|
|
444
603
|
type: 'object',
|
|
445
604
|
properties: {
|
|
446
605
|
name: { type: 'string' },
|
|
447
606
|
site_url: { type: 'string', description: 'Optional primary site URL, informational.' },
|
|
607
|
+
domain_id: { type: 'number', description: 'Optional id of one of the user\'s active custom domains (from list_domains) to serve the tag from. Defaults to the first active custom domain.' },
|
|
448
608
|
},
|
|
449
609
|
required: ['name'],
|
|
450
610
|
},
|
|
@@ -456,13 +616,14 @@ const TOOLS = [
|
|
|
456
616
|
},
|
|
457
617
|
{
|
|
458
618
|
name: 'update_property',
|
|
459
|
-
description: 'Update a property\'s name, site_url, or archive state.',
|
|
619
|
+
description: 'Update a property\'s name, site_url, tracking domain (domain_id), or archive state. Pass `domain_id` to re-bind the install snippet to a different active custom domain — it must be one of the user\'s active custom domains (a property can never fall back to buygolinks.com, since that breaks iOS tracking). Returns the updated property with its `tracking_domain` and regenerated `snippet`.',
|
|
460
620
|
inputSchema: {
|
|
461
621
|
type: 'object',
|
|
462
622
|
properties: {
|
|
463
623
|
property_id: { type: 'number' },
|
|
464
624
|
name: { type: 'string' },
|
|
465
625
|
site_url: { type: ['string', 'null'] },
|
|
626
|
+
domain_id: { type: 'number', description: 'Id of one of the user\'s active custom domains (from list_domains) to serve the tag from.' },
|
|
466
627
|
archived: { type: 'boolean' },
|
|
467
628
|
},
|
|
468
629
|
required: ['property_id'],
|
|
@@ -495,7 +656,7 @@ const TOOLS = [
|
|
|
495
656
|
{
|
|
496
657
|
name: 'get_property_stats',
|
|
497
658
|
description:
|
|
498
|
-
'Get a property\'s GA-style report: totals (page_views / unique_visitors / outbound / conversions / revenue / avg_time_on_page_ms / avg_scroll_pct / CR), by_day, by_page (top paths with views+conversions+revenue), by_source (organic vs each stitched campaign), by_country / by_device / by_browser / by_referrer, and the 50 most recent events.',
|
|
659
|
+
'Get a single property\'s GA-style PAGE VIEW report: totals (page_views / unique_visitors / outbound / conversions / revenue / avg_time_on_page_ms / avg_scroll_pct / CR), by_day, by_page (top paths with views+conversions+revenue), by_source (organic vs each stitched campaign), by_country / by_device / by_browser / by_referrer, and the 50 most recent events.',
|
|
499
660
|
inputSchema: {
|
|
500
661
|
type: 'object',
|
|
501
662
|
properties: {
|
|
@@ -505,10 +666,50 @@ const TOOLS = [
|
|
|
505
666
|
required: ['property_id'],
|
|
506
667
|
},
|
|
507
668
|
},
|
|
669
|
+
{
|
|
670
|
+
name: 'list_property_pages',
|
|
671
|
+
description:
|
|
672
|
+
'List the distinct pages observed across ALL the user\'s properties (the pageview stream), most-viewed first. Each entry is `{ url, path, title, property_id, property_name, views }`. Any of these pages can be used as a campaign LANDER — pass its `url` to `create_lander` (the page title makes a good lander name) and then wire the resulting lander into a campaign flow via `set_campaign_flow`. Use this to turn a real page on the user\'s site into a lander without retyping its URL.',
|
|
673
|
+
inputSchema: { type: 'object', properties: {} },
|
|
674
|
+
},
|
|
675
|
+
{
|
|
676
|
+
name: 'get_tracker_overview',
|
|
677
|
+
description:
|
|
678
|
+
'PAGE VIEWS live here. Get the unified Tracker dashboard rollup across BOTH event streams (site properties + campaigns) in one call. Returns totals (page_views / unique_visitors / visits / offer_clicks / conversions / revenue), a merged daily activity series (by_day), dimension breakdowns (by_country / by_device / by_browser), per-property and per-campaign rollups (the per-property rollup gives page_views for each site/property), a per-UTM-campaign rollup (utm_campaigns — auto-detected Google utm_source/medium/campaign/content tags with events/conversions/revenue), and the 50 most recent events across both streams. This is the go-to tool for "page views", "page views per property", "campaign traffic", and "is everything running and getting traffic?". NOTE: page_views here is Tracker site traffic and is SEPARATE from branded short-link clicks (get_overview_stats). If the user asks for "clicks AND page views" together, call BOTH this tool and get_overview_stats and combine them.',
|
|
679
|
+
inputSchema: {
|
|
680
|
+
type: 'object',
|
|
681
|
+
properties: {
|
|
682
|
+
days: { type: 'number', description: 'Window size in days (1–90). Defaults to 14.' },
|
|
683
|
+
},
|
|
684
|
+
},
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
name: 'get_tracker_events',
|
|
688
|
+
description:
|
|
689
|
+
'Query the unified Tracker event log across BOTH streams (site properties + campaigns), with full search / filter / sort over auto-detected Google UTM tags. Every event carries utm_source / utm_medium / utm_campaign / utm_term / utm_content. Use `q` for free-text search (matches any utm value, country, device, browser, referrer, source name, event type); `stream` to scope to all|property|campaign; `event` to filter by event_type; the utm_* params for exact-match filtering; and `sort`+`dir` to order by ts|revenue|utm_campaign|utm_source|country. Returns the matching page of events plus `facets` (distinct utm values present in the window) to drive filter pickers, and `total` for the full match count.',
|
|
690
|
+
inputSchema: {
|
|
691
|
+
type: 'object',
|
|
692
|
+
properties: {
|
|
693
|
+
days: { type: 'number', description: 'Window size in days (1–90). Defaults to 14.' },
|
|
694
|
+
q: { type: 'string', description: 'Free-text search across utm_*, country, device, browser, referrer, source name, event type.' },
|
|
695
|
+
stream: { type: 'string', enum: ['all', 'property', 'campaign'], description: 'Scope to one stream. Defaults to all.' },
|
|
696
|
+
event: { type: 'string', description: 'Exact event_type filter, e.g. page_view, outbound, conversion, visit, lander_view, offer_click.' },
|
|
697
|
+
utm_source: { type: 'string', description: 'Exact utm_source filter.' },
|
|
698
|
+
utm_medium: { type: 'string', description: 'Exact utm_medium filter.' },
|
|
699
|
+
utm_campaign: { type: 'string', description: 'Exact utm_campaign filter.' },
|
|
700
|
+
utm_term: { type: 'string', description: 'Exact utm_term filter.' },
|
|
701
|
+
utm_content: { type: 'string', description: 'Exact utm_content filter.' },
|
|
702
|
+
sort: { type: 'string', enum: ['ts', 'revenue', 'utm_campaign', 'utm_source', 'country'], description: 'Sort field. Defaults to ts.' },
|
|
703
|
+
dir: { type: 'string', enum: ['asc', 'desc'], description: 'Sort direction. Defaults to desc.' },
|
|
704
|
+
limit: { type: 'number', description: 'Page size (1–500). Defaults to 200.' },
|
|
705
|
+
offset: { type: 'number', description: 'Pagination offset. Defaults to 0.' },
|
|
706
|
+
},
|
|
707
|
+
},
|
|
708
|
+
},
|
|
508
709
|
];
|
|
509
710
|
|
|
510
711
|
const server = new Server(
|
|
511
|
-
{ name: 'buygolinks', version: '0.
|
|
712
|
+
{ name: 'buygolinks', version: '0.11.0' },
|
|
512
713
|
{ capabilities: { tools: {} } },
|
|
513
714
|
);
|
|
514
715
|
|
|
@@ -669,6 +870,61 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
669
870
|
break;
|
|
670
871
|
}
|
|
671
872
|
|
|
873
|
+
// ── Traffic sources ──
|
|
874
|
+
case 'list_traffic_sources': {
|
|
875
|
+
result = await api('/api/traffic-sources');
|
|
876
|
+
break;
|
|
877
|
+
}
|
|
878
|
+
case 'create_traffic_source': {
|
|
879
|
+
const body = { name: args.name };
|
|
880
|
+
if ('platform' in args) body.platform = args.platform;
|
|
881
|
+
if (Array.isArray(args.token_params)) body.token_params = args.token_params;
|
|
882
|
+
result = await api('/api/traffic-sources', { method: 'POST', body: JSON.stringify(body) });
|
|
883
|
+
break;
|
|
884
|
+
}
|
|
885
|
+
case 'update_traffic_source': {
|
|
886
|
+
const body = {};
|
|
887
|
+
if (typeof args.name === 'string') body.name = args.name;
|
|
888
|
+
if ('platform' in args) body.platform = args.platform;
|
|
889
|
+
if (Array.isArray(args.token_params)) body.token_params = args.token_params;
|
|
890
|
+
if (typeof args.archived === 'boolean') body.archived = args.archived;
|
|
891
|
+
result = await api(`/api/traffic-sources/${Number(args.traffic_source_id)}`, { method: 'PATCH', body: JSON.stringify(body) });
|
|
892
|
+
break;
|
|
893
|
+
}
|
|
894
|
+
case 'delete_traffic_source': {
|
|
895
|
+
result = await api(`/api/traffic-sources/${Number(args.traffic_source_id)}`, { method: 'DELETE' });
|
|
896
|
+
break;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// ── Tracking pixels ──
|
|
900
|
+
case 'list_tracking_pixels': {
|
|
901
|
+
result = await api('/api/tracking-pixels');
|
|
902
|
+
break;
|
|
903
|
+
}
|
|
904
|
+
case 'create_tracking_pixel': {
|
|
905
|
+
const body = { platform: args.platform, pixel_id: args.pixel_id };
|
|
906
|
+
if (typeof args.label === 'string') body.label = args.label;
|
|
907
|
+
if (typeof args.default_event === 'string') body.default_event = args.default_event;
|
|
908
|
+
if (typeof args.capi_token === 'string') body.capi_token = args.capi_token;
|
|
909
|
+
if (typeof args.active === 'boolean') body.active = args.active;
|
|
910
|
+
result = await api('/api/tracking-pixels', { method: 'POST', body: JSON.stringify(body) });
|
|
911
|
+
break;
|
|
912
|
+
}
|
|
913
|
+
case 'update_tracking_pixel': {
|
|
914
|
+
const body = {};
|
|
915
|
+
if (typeof args.pixel_id === 'string') body.pixel_id = args.pixel_id;
|
|
916
|
+
if (typeof args.label === 'string') body.label = args.label;
|
|
917
|
+
if (typeof args.default_event === 'string') body.default_event = args.default_event;
|
|
918
|
+
if ('capi_token' in args) body.capi_token = args.capi_token;
|
|
919
|
+
if (typeof args.active === 'boolean') body.active = args.active;
|
|
920
|
+
result = await api(`/api/tracking-pixels/${Number(args.pixel_id_row)}`, { method: 'PATCH', body: JSON.stringify(body) });
|
|
921
|
+
break;
|
|
922
|
+
}
|
|
923
|
+
case 'delete_tracking_pixel': {
|
|
924
|
+
result = await api(`/api/tracking-pixels/${Number(args.pixel_id_row)}`, { method: 'DELETE' });
|
|
925
|
+
break;
|
|
926
|
+
}
|
|
927
|
+
|
|
672
928
|
// ── Tracker: campaigns ──
|
|
673
929
|
case 'list_campaigns': {
|
|
674
930
|
result = await api('/api/campaigns');
|
|
@@ -676,11 +932,15 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
676
932
|
}
|
|
677
933
|
case 'create_campaign': {
|
|
678
934
|
const body = { name: args.name };
|
|
679
|
-
for (const k of ['traffic_source_label', 'country', 'cost_model', 'transition_mode']) {
|
|
935
|
+
for (const k of ['traffic_source', 'traffic_source_label', 'country', 'cost_model', 'transition_mode', 'conversion_event']) {
|
|
680
936
|
if (typeof args[k] === 'string') body[k] = args[k];
|
|
681
937
|
}
|
|
682
938
|
if (typeof args.cost_value === 'number') body.cost_value = args.cost_value;
|
|
683
939
|
if ('domain_id' in args) body.domain_id = args.domain_id;
|
|
940
|
+
if ('traffic_source_id' in args) body.traffic_source_id = args.traffic_source_id;
|
|
941
|
+
if ('conversion_pixel_id' in args) body.conversion_pixel_id = args.conversion_pixel_id;
|
|
942
|
+
if ('direct_slug' in args) body.direct_slug = args.direct_slug;
|
|
943
|
+
if ('fallback_url' in args) body.fallback_url = args.fallback_url;
|
|
684
944
|
if (Array.isArray(args.offer_ids)) body.offer_ids = args.offer_ids;
|
|
685
945
|
if (Array.isArray(args.offer_weights)) body.offer_weights = args.offer_weights;
|
|
686
946
|
if (Array.isArray(args.lander_ids)) body.lander_ids = args.lander_ids;
|
|
@@ -694,11 +954,16 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
694
954
|
}
|
|
695
955
|
case 'update_campaign': {
|
|
696
956
|
const body = {};
|
|
697
|
-
for (const k of ['name', 'traffic_source_label', 'country', 'cost_model', 'transition_mode']) {
|
|
957
|
+
for (const k of ['name', 'traffic_source_label', 'country', 'cost_model', 'transition_mode', 'conversion_event']) {
|
|
698
958
|
if (typeof args[k] === 'string') body[k] = args[k];
|
|
699
959
|
}
|
|
960
|
+
if ('traffic_source' in args) body.traffic_source = args.traffic_source;
|
|
961
|
+
if ('traffic_source_id' in args) body.traffic_source_id = args.traffic_source_id;
|
|
700
962
|
if (typeof args.cost_value === 'number') body.cost_value = args.cost_value;
|
|
701
963
|
if ('domain_id' in args) body.domain_id = args.domain_id;
|
|
964
|
+
if ('conversion_pixel_id' in args) body.conversion_pixel_id = args.conversion_pixel_id;
|
|
965
|
+
if ('direct_slug' in args) body.direct_slug = args.direct_slug;
|
|
966
|
+
if ('fallback_url' in args) body.fallback_url = args.fallback_url;
|
|
702
967
|
if (typeof args.archived === 'boolean') body.archived = args.archived;
|
|
703
968
|
result = await api(`/api/campaigns/${Number(args.campaign_id)}`, { method: 'PATCH', body: JSON.stringify(body) });
|
|
704
969
|
break;
|
|
@@ -719,6 +984,16 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
719
984
|
});
|
|
720
985
|
break;
|
|
721
986
|
}
|
|
987
|
+
case 'get_campaign_flow': {
|
|
988
|
+
result = await api(`/api/campaigns/${Number(args.campaign_id)}/flow`);
|
|
989
|
+
break;
|
|
990
|
+
}
|
|
991
|
+
case 'set_campaign_flow': {
|
|
992
|
+
result = await api(`/api/campaigns/${Number(args.campaign_id)}/flow`, {
|
|
993
|
+
method: 'PUT', body: JSON.stringify({ nodes: args.nodes, edges: args.edges }),
|
|
994
|
+
});
|
|
995
|
+
break;
|
|
996
|
+
}
|
|
722
997
|
case 'get_campaign_stats': {
|
|
723
998
|
const days = args?.days ? `?days=${Number(args.days)}` : '';
|
|
724
999
|
result = await api(`/api/campaigns/${Number(args.campaign_id)}/stats${days}`);
|
|
@@ -732,6 +1007,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
732
1007
|
case 'create_property': {
|
|
733
1008
|
const body = { name: args.name };
|
|
734
1009
|
if (typeof args.site_url === 'string') body.site_url = args.site_url;
|
|
1010
|
+
if (typeof args.domain_id === 'number') body.domain_id = args.domain_id;
|
|
735
1011
|
result = await api('/api/properties', { method: 'POST', body: JSON.stringify(body) });
|
|
736
1012
|
break;
|
|
737
1013
|
}
|
|
@@ -743,6 +1019,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
743
1019
|
const body = {};
|
|
744
1020
|
if (typeof args.name === 'string') body.name = args.name;
|
|
745
1021
|
if ('site_url' in args) body.site_url = args.site_url;
|
|
1022
|
+
if (typeof args.domain_id === 'number') body.domain_id = args.domain_id;
|
|
746
1023
|
if (typeof args.archived === 'boolean') body.archived = args.archived;
|
|
747
1024
|
result = await api(`/api/properties/${Number(args.property_id)}`, { method: 'PATCH', body: JSON.stringify(body) });
|
|
748
1025
|
break;
|
|
@@ -762,6 +1039,25 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
762
1039
|
result = await api(`/api/properties/${Number(args.property_id)}/stats${days}`);
|
|
763
1040
|
break;
|
|
764
1041
|
}
|
|
1042
|
+
case 'list_property_pages': {
|
|
1043
|
+
result = await api('/api/property-pages');
|
|
1044
|
+
break;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
case 'get_tracker_overview': {
|
|
1048
|
+
const days = args?.days ? `?days=${Number(args.days)}` : '';
|
|
1049
|
+
result = await api(`/api/tracker/overview${days}`);
|
|
1050
|
+
break;
|
|
1051
|
+
}
|
|
1052
|
+
case 'get_tracker_events': {
|
|
1053
|
+
const qs = new URLSearchParams();
|
|
1054
|
+
for (const k of ['days', 'q', 'stream', 'event', 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'sort', 'dir', 'limit', 'offset']) {
|
|
1055
|
+
if (args?.[k] !== undefined && args[k] !== null && args[k] !== '') qs.set(k, String(args[k]));
|
|
1056
|
+
}
|
|
1057
|
+
const q = qs.toString();
|
|
1058
|
+
result = await api(`/api/tracker/events${q ? `?${q}` : ''}`);
|
|
1059
|
+
break;
|
|
1060
|
+
}
|
|
765
1061
|
|
|
766
1062
|
default:
|
|
767
1063
|
throw new Error(`unknown tool: ${name}`);
|