@buygolinks/mcp 0.17.1 → 0.21.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 +3 -2
- package/package.json +1 -1
- package/src/index.js +50 -2
package/README.md
CHANGED
|
@@ -64,6 +64,7 @@ Most MCP clients use the same shape. Point them at the `npx -y @buygolinks/mcp`
|
|
|
64
64
|
|---|---|
|
|
65
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 destination URL, title, source label, custom domain, password, archive state, or custom alias (e.g. `summer-sale` → `https://click.example.com/summer-sale`). Repointing the destination preserves the short code, alias, and click history. |
|
|
67
|
+
| `refresh_link_image` | Re-attempt fetching a link's product/preview image (Amazon PDP scrape for `/dp/` links, Open Graph otherwise). Use when a link has no thumbnail or a stale one. Returns `{ found: false }` if nothing could be fetched this time. |
|
|
67
68
|
| `list_links` | List your links with click counts. |
|
|
68
69
|
| `get_link_stats` | Per-link analytics (by day, country, device, browser, hour, weekday). |
|
|
69
70
|
| `get_overview_stats` | Account-wide analytics. |
|
|
@@ -71,12 +72,12 @@ Most MCP clients use the same shape. Point them at the `npx -y @buygolinks/mcp`
|
|
|
71
72
|
| `create_domain` | Register a new branded domain (e.g. `go.acme.com`). Returns the CNAME target. |
|
|
72
73
|
| `verify_domain` | Force a fresh Cloudflare status check after DNS is added. |
|
|
73
74
|
| `delete_domain` | Remove a branded domain. |
|
|
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
|
+
| `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. Set `amazon_tag` (e.g. `systemway-20`) on an Amazon network to auto-tag every Amazon link with `?tag=…` at click time. |
|
|
75
76
|
| `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
77
|
| `list_landers` / `create_lander` / `update_lander` / `delete_lander` | Manage landing pages. Lander URLs may contain `{clickid}` tokens which the resolver expands. |
|
|
77
78
|
| `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
79
|
| `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` / `duplicate_campaign` | Manage campaigns. `duplicate_campaign` deep-copies a campaign (settings + full routing flow + offer/lander pools) into a fresh short link named `COPY - {name}` (`COPY (1) - …` when copying a copy); stats and the unique direct-link slug are not carried over. 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
|
|
80
|
+
| `list_campaigns` / `create_campaign` / `get_campaign` / `update_campaign` / `delete_campaign` / `duplicate_campaign` | Manage campaigns. `duplicate_campaign` deep-copies a campaign (settings + full routing flow + offer/lander pools) into a fresh short link named `COPY - {name}` (`COPY (1) - …` when copying a copy); stats and the unique direct-link slug are not carried over. 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 the pixel's CAPI token** when the campaign converts (a pixel with no CAPI token can't fire back). `report_value_mode` controls the value reported back — `payout` (real), `fixed` (a set number), or `adjusted` (payout − `report_value_adjust_pct`% − `report_value_adjust_flat`) so you can withhold your margin. Each campaign also returns a `click_url` (`…/click`) — the single, campaign-agnostic lander CTA link (same on every campaign, so one lander serves many) that always resolves to an offer (never a dead page); the campaign is read from the visitor's cookie or the ad's `?utm_campaign={direct_slug}` forwarded onto the CTA. Set `direct_slug` to run traffic straight to a lander with no tracker hop (`?utm_campaign={direct_slug}` resolves the campaign at `/click` and stitches via the property tag); `fallback_url` is the never-dead-end guarantee. |
|
|
80
81
|
| `set_campaign_offers` / `set_campaign_landers` | Replace the campaign's offer/lander split. Items are `{ offer_id, weight }` / `{ lander_id, weight }`. |
|
|
81
82
|
| `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. |
|
|
82
83
|
| `get_campaign_stats` | Full campaign report: visits, lander views, offer clicks, conversions, revenue, cost, profit, CTR/CR/EPC/ROI, breakdowns, recent events. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buygolinks/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.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
|
@@ -91,6 +91,18 @@ const TOOLS = [
|
|
|
91
91
|
required: ['link_id'],
|
|
92
92
|
},
|
|
93
93
|
},
|
|
94
|
+
{
|
|
95
|
+
name: 'refresh_link_image',
|
|
96
|
+
description:
|
|
97
|
+
'Re-attempt fetching the product/preview image for an existing short link. Re-runs the same best-effort scrape used at link creation — the Amazon product-image scraper for Amazon /dp/ links, or a generic Open Graph (og:image / twitter:image) scrape for any other URL — and rehosts the result on buygolinks storage. Use this when a link was created without an image (the thumbnail shows a letter/placeholder) or to update a stale image. Returns { found: true, link } with the updated `product_image_url` on success, or { found: false } if no image could be fetched this time (the existing image, if any, is left untouched — try again later).',
|
|
98
|
+
inputSchema: {
|
|
99
|
+
type: 'object',
|
|
100
|
+
properties: {
|
|
101
|
+
link_id: { type: 'number', description: 'The numeric id of the link (from list_links / create_link).' },
|
|
102
|
+
},
|
|
103
|
+
required: ['link_id'],
|
|
104
|
+
},
|
|
105
|
+
},
|
|
94
106
|
{
|
|
95
107
|
name: 'list_links',
|
|
96
108
|
description:
|
|
@@ -194,7 +206,7 @@ const TOOLS = [
|
|
|
194
206
|
{
|
|
195
207
|
name: 'create_affiliate_network',
|
|
196
208
|
description:
|
|
197
|
-
'Add a new affiliate network. The server mints a per-network postback URL (https://buygolinks.com/postback/n/{token}) the user pastes into the network. The postback accepts ?{conversion_param}={clickid}&{payout_param}={payout}&{currency_param}={currency}&{txid_param}={txid} — payout, currency, and txid are optional. Returns the network row including postback_url.',
|
|
209
|
+
'Add a new affiliate network. The server mints a per-network postback URL (https://buygolinks.com/postback/n/{token}) the user pastes into the network. The postback accepts ?{conversion_param}={clickid}&{payout_param}={payout}&{currency_param}={currency}&{txid_param}={txid} — payout, currency, and txid are optional. Returns the network row including postback_url. For Amazon Associates, set amazon_tag (e.g. "systemway-20"): every Amazon destination link the user adds is then auto-tagged with ?tag={amazon_tag} at click time (a tag already on the URL always wins).',
|
|
198
210
|
inputSchema: {
|
|
199
211
|
type: 'object',
|
|
200
212
|
properties: {
|
|
@@ -203,13 +215,14 @@ const TOOLS = [
|
|
|
203
215
|
payout_param: { type: 'string', description: 'What the network calls the payout amount (default "payout").' },
|
|
204
216
|
currency_param: { type: 'string', description: 'What the network calls the currency code (default "currency").' },
|
|
205
217
|
txid_param: { type: 'string', description: 'What the network calls the transaction/order id in its postback (default "txid"). Stored on the conversion for reconciliation/dedupe.' },
|
|
218
|
+
amazon_tag: { type: 'string', description: 'Amazon Associates tracking tag (e.g. "systemway-20"). When set, Amazon links are auto-converted to affiliate links by appending ?tag={amazon_tag} on each click.' },
|
|
206
219
|
},
|
|
207
220
|
required: ['name'],
|
|
208
221
|
},
|
|
209
222
|
},
|
|
210
223
|
{
|
|
211
224
|
name: 'update_affiliate_network',
|
|
212
|
-
description: 'Update an affiliate network (rename
|
|
225
|
+
description: 'Update an affiliate network (rename, change postback param names, or set the Amazon Associates amazon_tag — pass an empty string to clear it). Pass archived=true to soft-archive.',
|
|
213
226
|
inputSchema: {
|
|
214
227
|
type: 'object',
|
|
215
228
|
properties: {
|
|
@@ -219,6 +232,7 @@ const TOOLS = [
|
|
|
219
232
|
payout_param: { type: 'string' },
|
|
220
233
|
currency_param: { type: 'string' },
|
|
221
234
|
txid_param: { type: 'string' },
|
|
235
|
+
amazon_tag: { type: 'string', description: 'Amazon Associates tracking tag (e.g. "systemway-20"); empty string clears it.' },
|
|
222
236
|
archived: { type: 'boolean' },
|
|
223
237
|
},
|
|
224
238
|
required: ['network_id'],
|
|
@@ -446,6 +460,10 @@ const TOOLS = [
|
|
|
446
460
|
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.' },
|
|
447
461
|
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.' },
|
|
448
462
|
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.' },
|
|
463
|
+
report_value_mode: { type: 'string', enum: ['payout', 'fixed', 'adjusted'], description: 'What revenue VALUE to report back to the ad platform on a CAPI conversion. "payout" (default) = the real conversion payout; "fixed" = always report report_value_fixed (e.g. 1); "adjusted" = real payout minus report_value_adjust_pct % then minus report_value_adjust_flat (so the user\'s margin/cost is withheld from what the platform optimizes against). Floored at 0.' },
|
|
464
|
+
report_value_fixed: { type: ['number', 'null'], description: 'Used when report_value_mode = "fixed": the exact value (USD) reported on every conversion regardless of real payout.' },
|
|
465
|
+
report_value_adjust_pct: { type: ['number', 'null'], description: 'Used when report_value_mode = "adjusted": percentage (0–100) subtracted from the real payout first (e.g. 10 reports payout − 10%).' },
|
|
466
|
+
report_value_adjust_flat: { type: ['number', 'null'], description: 'Used when report_value_mode = "adjusted": flat amount (USD) subtracted after the percentage.' },
|
|
449
467
|
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.' },
|
|
450
468
|
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.' },
|
|
451
469
|
offer_ids: { type: 'array', items: { type: 'number' } },
|
|
@@ -479,6 +497,10 @@ const TOOLS = [
|
|
|
479
497
|
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.' },
|
|
480
498
|
conversion_pixel_id: { type: ['number', 'null'], description: 'Id of a tracking pixel to fire on conversion, or null to clear.' },
|
|
481
499
|
conversion_event: { type: 'string', description: 'Event the conversion pixel fires (must be valid for that pixel\'s platform).' },
|
|
500
|
+
report_value_mode: { type: 'string', enum: ['payout', 'fixed', 'adjusted'], description: 'What revenue value to report back to the ad platform on conversion: "payout" (real), "fixed" (report_value_fixed), or "adjusted" (real payout − report_value_adjust_pct% − report_value_adjust_flat, floored at 0).' },
|
|
501
|
+
report_value_fixed: { type: ['number', 'null'], description: 'Fixed value (USD) reported on every conversion when report_value_mode = "fixed". Null to clear.' },
|
|
502
|
+
report_value_adjust_pct: { type: ['number', 'null'], description: 'Percentage (0–100) subtracted from real payout when report_value_mode = "adjusted". Null to clear.' },
|
|
503
|
+
report_value_adjust_flat: { type: ['number', 'null'], description: 'Flat USD amount subtracted (after the %) when report_value_mode = "adjusted". Null to clear.' },
|
|
482
504
|
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.' },
|
|
483
505
|
fallback_url: { type: ['string', 'null'], description: 'Never-dead-end fallback URL when no live offer exists, or null to use the platform default.' },
|
|
484
506
|
archived: { type: 'boolean' },
|
|
@@ -680,6 +702,12 @@ const TOOLS = [
|
|
|
680
702
|
'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.',
|
|
681
703
|
inputSchema: { type: 'object', properties: {} },
|
|
682
704
|
},
|
|
705
|
+
{
|
|
706
|
+
name: 'list_observed_campaign_tags',
|
|
707
|
+
description:
|
|
708
|
+
'List the distinct `utm_campaign` values that have actually been SEEN arriving on the user\'s properties/visits over the last 90 days, most-active first. Each entry is `{ utm_campaign, events, utm_source, utm_medium, lander_url, claimed_by }` where `claimed_by` is `{ id, name }` of the BGL campaign whose `direct_slug` already owns that tag, or null if unclaimed. Use this to WIRE UP AN ALREADY-RUNNING DIRECT-LINK CAMPAIGN: when an ad on FB/Google points straight at a lander (no tracker hop) with `?utm_campaign={tag}`, that tag shows up here once the universal property tag reports a page view. Pick an unclaimed tag and set it as a campaign\'s `direct_slug` (via `update_campaign`) so BGL attributes that traffic — and its `/click` CTA — to the right campaign + offer. Skip tags whose `claimed_by` is non-null (another campaign already owns them).',
|
|
709
|
+
inputSchema: { type: 'object', properties: {} },
|
|
710
|
+
},
|
|
683
711
|
{
|
|
684
712
|
name: 'get_tracker_overview',
|
|
685
713
|
description:
|
|
@@ -760,6 +788,12 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
760
788
|
});
|
|
761
789
|
break;
|
|
762
790
|
}
|
|
791
|
+
case 'refresh_link_image': {
|
|
792
|
+
result = await api(`/api/links/${Number(args.link_id)}/refresh-image`, {
|
|
793
|
+
method: 'POST',
|
|
794
|
+
});
|
|
795
|
+
break;
|
|
796
|
+
}
|
|
763
797
|
case 'list_links': {
|
|
764
798
|
const qs = args?.include_archived ? '?include_archived=1' : '';
|
|
765
799
|
result = await api(`/api/links${qs}`);
|
|
@@ -806,6 +840,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
806
840
|
if (typeof args.payout_param === 'string') body.payout_param = args.payout_param;
|
|
807
841
|
if (typeof args.currency_param === 'string') body.currency_param = args.currency_param;
|
|
808
842
|
if (typeof args.txid_param === 'string') body.txid_param = args.txid_param;
|
|
843
|
+
if (typeof args.amazon_tag === 'string') body.amazon_tag = args.amazon_tag;
|
|
809
844
|
result = await api('/api/affiliate-networks', { method: 'POST', body: JSON.stringify(body) });
|
|
810
845
|
break;
|
|
811
846
|
}
|
|
@@ -816,6 +851,7 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
816
851
|
if (typeof args.payout_param === 'string') body.payout_param = args.payout_param;
|
|
817
852
|
if (typeof args.currency_param === 'string') body.currency_param = args.currency_param;
|
|
818
853
|
if (typeof args.txid_param === 'string') body.txid_param = args.txid_param;
|
|
854
|
+
if (typeof args.amazon_tag === 'string') body.amazon_tag = args.amazon_tag;
|
|
819
855
|
if (typeof args.archived === 'boolean') body.archived = args.archived;
|
|
820
856
|
result = await api(`/api/affiliate-networks/${Number(args.network_id)}`, { method: 'PATCH', body: JSON.stringify(body) });
|
|
821
857
|
break;
|
|
@@ -950,6 +986,10 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
950
986
|
if ('domain_id' in args) body.domain_id = args.domain_id;
|
|
951
987
|
if ('traffic_source_id' in args) body.traffic_source_id = args.traffic_source_id;
|
|
952
988
|
if ('conversion_pixel_id' in args) body.conversion_pixel_id = args.conversion_pixel_id;
|
|
989
|
+
if (typeof args.report_value_mode === 'string') body.report_value_mode = args.report_value_mode;
|
|
990
|
+
if ('report_value_fixed' in args) body.report_value_fixed = args.report_value_fixed;
|
|
991
|
+
if ('report_value_adjust_pct' in args) body.report_value_adjust_pct = args.report_value_adjust_pct;
|
|
992
|
+
if ('report_value_adjust_flat' in args) body.report_value_adjust_flat = args.report_value_adjust_flat;
|
|
953
993
|
if ('direct_slug' in args) body.direct_slug = args.direct_slug;
|
|
954
994
|
if ('fallback_url' in args) body.fallback_url = args.fallback_url;
|
|
955
995
|
if (Array.isArray(args.offer_ids)) body.offer_ids = args.offer_ids;
|
|
@@ -973,6 +1013,10 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
973
1013
|
if (typeof args.cost_value === 'number') body.cost_value = args.cost_value;
|
|
974
1014
|
if ('domain_id' in args) body.domain_id = args.domain_id;
|
|
975
1015
|
if ('conversion_pixel_id' in args) body.conversion_pixel_id = args.conversion_pixel_id;
|
|
1016
|
+
if (typeof args.report_value_mode === 'string') body.report_value_mode = args.report_value_mode;
|
|
1017
|
+
if ('report_value_fixed' in args) body.report_value_fixed = args.report_value_fixed;
|
|
1018
|
+
if ('report_value_adjust_pct' in args) body.report_value_adjust_pct = args.report_value_adjust_pct;
|
|
1019
|
+
if ('report_value_adjust_flat' in args) body.report_value_adjust_flat = args.report_value_adjust_flat;
|
|
976
1020
|
if ('direct_slug' in args) body.direct_slug = args.direct_slug;
|
|
977
1021
|
if ('fallback_url' in args) body.fallback_url = args.fallback_url;
|
|
978
1022
|
if (typeof args.archived === 'boolean') body.archived = args.archived;
|
|
@@ -1058,6 +1102,10 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
1058
1102
|
result = await api('/api/property-pages');
|
|
1059
1103
|
break;
|
|
1060
1104
|
}
|
|
1105
|
+
case 'list_observed_campaign_tags': {
|
|
1106
|
+
result = await api('/api/observed-campaign-tags');
|
|
1107
|
+
break;
|
|
1108
|
+
}
|
|
1061
1109
|
|
|
1062
1110
|
case 'get_tracker_overview': {
|
|
1063
1111
|
const days = args?.days ? `?days=${Number(args.days)}` : '';
|