@apex-inc/mcp-server 0.40.0 → 0.41.1
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 +1 -1
- package/dist/api-client.d.ts +21 -1
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +55 -2
- package/dist/api-client.js.map +1 -1
- package/dist/auth.js +2 -1
- package/dist/auth.js.map +1 -1
- package/dist/base-url.d.ts +8 -0
- package/dist/base-url.d.ts.map +1 -0
- package/dist/base-url.js +10 -0
- package/dist/base-url.js.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/prompts.d.ts +14 -0
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +31 -6
- package/dist/prompts.js.map +1 -1
- package/dist/repo-binding.d.ts +8 -0
- package/dist/repo-binding.d.ts.map +1 -0
- package/dist/repo-binding.js +13 -0
- package/dist/repo-binding.js.map +1 -0
- package/dist/resources.d.ts +13 -0
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +16 -0
- package/dist/resources.js.map +1 -1
- package/dist/setup-catchup-tools.d.ts +279 -1
- package/dist/setup-catchup-tools.d.ts.map +1 -1
- package/dist/setup-catchup-tools.js +350 -24
- package/dist/setup-catchup-tools.js.map +1 -1
- package/dist/setup-checklist.d.ts +68 -0
- package/dist/setup-checklist.d.ts.map +1 -0
- package/dist/setup-checklist.js +128 -0
- package/dist/setup-checklist.js.map +1 -0
- package/dist/setup-flow.d.ts +14 -0
- package/dist/setup-flow.d.ts.map +1 -0
- package/dist/setup-flow.js +52 -0
- package/dist/setup-flow.js.map +1 -0
- package/dist/tool-groups.d.ts.map +1 -1
- package/dist/tool-groups.js +10 -0
- package/dist/tool-groups.js.map +1 -1
- package/dist/tool-shared.d.ts.map +1 -1
- package/dist/tool-shared.js +11 -4
- package/dist/tool-shared.js.map +1 -1
- package/dist/tools/ad-platforms.d.ts +1 -1
- package/dist/tools/ad-platforms.js +23 -23
- package/dist/tools/ad-platforms.js.map +1 -1
- package/dist/tools/adoption.js +2 -2
- package/dist/tools/adoption.js.map +1 -1
- package/dist/tools/context.d.ts.map +1 -1
- package/dist/tools/context.js +15 -33
- package/dist/tools/context.js.map +1 -1
- package/dist/tools/experiments.d.ts +2 -2
- package/dist/tools/experiments.d.ts.map +1 -1
- package/dist/tools/experiments.js +21 -21
- package/dist/tools/experiments.js.map +1 -1
- package/dist/tools/letters.d.ts +6 -6
- package/dist/tools/letters.js +1 -1
- package/dist/tools/letters.js.map +1 -1
- package/dist/tools/measurement.d.ts +2 -2
- package/dist/tools/measurement.d.ts.map +1 -1
- package/dist/tools/measurement.js +4 -3
- package/dist/tools/measurement.js.map +1 -1
- package/dist/tools.d.ts +307 -11
- package/dist/tools.d.ts.map +1 -1
- package/package.json +3 -2
- package/skills/apex-adoption/SKILL.md +1 -1
- package/skills/apex-communications/SKILL.md +2 -2
- package/skills/apex-experimentation/SKILL.md +6 -6
- package/skills/apex-growth-intelligence/SKILL.md +6 -6
- package/skills/apex-growth-tracking/SKILL.md +4 -4
- package/skills/apex-integration-cookbook/SKILL.md +11 -15
- package/skills/apex-journeys/SKILL.md +3 -3
- package/skills/apex-partner-network/SKILL.md +1 -1
- package/skills/apex-setup/SKILL.md +69 -0
|
@@ -4,6 +4,7 @@ import { join } from "node:path";
|
|
|
4
4
|
import { apiGet, apiPost, apiPatch, apiDelete, getActiveWorkspace, } from "../api-client.js";
|
|
5
5
|
import { resolveExperimentHypothesis } from "../experiment-copy.js";
|
|
6
6
|
import { activationLiveLine, shouldRefuseActivationWiring } from "../activation-wiring.js";
|
|
7
|
+
import { apexBaseUrl } from "../base-url.js";
|
|
7
8
|
import { fetchLiveTestHits, formatLiveTestStop, isFileStop, isObjectStop, } from "../live-test-guard.js";
|
|
8
9
|
import { hasConflictAdvisory, renderConflictAdvisory, } from "../conflict-advisory.js";
|
|
9
10
|
import { APEX, controlPctOf, daysRunningOf, findVariant, formatArmLine, titleCaseEvent, assertTargetUrlForActiveWorkspace, errMsg, resolveCreateSurface, } from "../tool-shared.js";
|
|
@@ -35,7 +36,7 @@ async function createAdHoldout(args) {
|
|
|
35
36
|
if (adSetIds.length > 20)
|
|
36
37
|
return textError("An Ad holdout covers at most 20 ad sets.");
|
|
37
38
|
// What the workspace has already set up for this network: the connected
|
|
38
|
-
// ad account, whether the
|
|
39
|
+
// ad account, whether the owner agreed to hashed emails going to the
|
|
39
40
|
// network, and (Meta) whether Apex proved it can hold people back there.
|
|
40
41
|
let prereqs = null;
|
|
41
42
|
try {
|
|
@@ -57,7 +58,7 @@ async function createAdHoldout(args) {
|
|
|
57
58
|
}
|
|
58
59
|
else {
|
|
59
60
|
if (!net.audienceAcked) {
|
|
60
|
-
blockers.push(`The
|
|
61
|
+
blockers.push(`The owner has not yet agreed to hashed emails going to ${networkWord}. Finish the "Let Apex hold people back from your ads" setup step.`);
|
|
61
62
|
}
|
|
62
63
|
if (net.attachAvailable && !net.accessProven) {
|
|
63
64
|
blockers.push(`Apex has not yet proved its ${networkWord} token can put a do-not-show list on this account. The same setup step runs the two checks.`);
|
|
@@ -98,7 +99,7 @@ async function createAdHoldout(args) {
|
|
|
98
99
|
what_is_measured: "App installs in both lanes (saw the ads vs held back). Purchases can be a secondary metric.",
|
|
99
100
|
hypothesis,
|
|
100
101
|
honesty: [
|
|
101
|
-
`Nothing counts until Apex confirms the do-not-show list is on every ad set${attachMode === "manual" ? ` — with manual attach the
|
|
102
|
+
`Nothing counts until Apex confirms the do-not-show list is on every ad set${attachMode === "manual" ? ` — with manual attach the owner adds the audience as an exclusion in ${networkWord} Ads` : ""}.`,
|
|
102
103
|
"Results freeze if the list comes off mid-run; start a new holdout to measure again.",
|
|
103
104
|
"Installs are counted for known people only (someone with an email or phone Apex can hash).",
|
|
104
105
|
],
|
|
@@ -149,7 +150,7 @@ async function createAdHoldout(args) {
|
|
|
149
150
|
experimentId: exp.id,
|
|
150
151
|
status: exp.status,
|
|
151
152
|
_next: [
|
|
152
|
-
`activate_experiment({ experimentId: "${exp.id}" }) — Apex draws the held-back list and ${attachMode === "apex" ? `puts it on the ${networkWord} ad sets` : `makes the audience for the
|
|
153
|
+
`activate_experiment({ experimentId: "${exp.id}" }) — Apex draws the held-back list and ${attachMode === "apex" ? `puts it on the ${networkWord} ad sets` : `makes the audience for the owner to add as an exclusion in ${networkWord} Ads`}.`,
|
|
153
154
|
`verify_ad_exclusion({ experiment_id: "${exp.id}" }) — confirm the list is on every ad set. Counting starts after the washout.`,
|
|
154
155
|
`get_results({ experimentId: "${exp.id}" }) — the two lanes, once counting.`,
|
|
155
156
|
],
|
|
@@ -323,14 +324,14 @@ export const experimentTools = {
|
|
|
323
324
|
guardrailType: z.enum(["rate", "revenue", "duration"]).optional().describe("How the guardrail is measured. 'rate' (default) = how often the harm event fires. 'revenue' = sum a value (e.g. refund $)."),
|
|
324
325
|
guardrailThreshold: z.number().optional().describe("Harm margin for the guardrail, RELATIVE (0.25 = flag a >25% rise). Defaults to 0.25."),
|
|
325
326
|
predictionMagnitude: z.string().optional().describe("Your committed prediction of the effect, e.g. '10% lift'. Recorded with 'agent' provenance and used for calibration. Required before the experiment can be ACTIVATED."),
|
|
326
|
-
surface: z.enum(["web", "mobile", "paid_channel"]).optional().describe("The PROPERTY the experiment runs in (not the SDK used): 'web' for a website, 'mobile' for a native / Capacitor / React-Native app, 'paid_channel' for an Ad holdout (Apex holds a share of known people back from the
|
|
327
|
+
surface: z.enum(["web", "mobile", "paid_channel"]).optional().describe("The PROPERTY the experiment runs in (not the SDK used): 'web' for a website, 'mobile' for a native / Capacitor / React-Native app, 'paid_channel' for an Ad holdout (Apex holds a share of known people back from the workspace's ads on one network and measures the installs the ads actually caused — pass paid_channel too). OPTIONAL for web/mobile — when omitted it is auto-detected from repo signals (capacitor.config.* / @apex-inc/capacitor-plugin → mobile) and the workspace's registered data sources; pass it explicitly only to override or when both web and mobile sources exist. Sets the dashboard data-source label (Website vs Mobile app vs Ads)."),
|
|
327
328
|
paid_channel: z
|
|
328
329
|
.object({
|
|
329
330
|
network: z.enum(["meta", "google", "tiktok", "linkedin"]).describe("Ad network the ad sets run on. Apex attaches the do-not-show list itself on Meta only; other networks are manual attach."),
|
|
330
331
|
ad_account_id: z.string().optional().describe("The connected ad account the ad sets belong to. Omit to use the account the workspace connected for this network."),
|
|
331
332
|
ad_set_ids: z.array(z.string()).min(1).describe("Ad set ids to hold people back from (max 20). Frozen at create; changing them is a new holdout."),
|
|
332
333
|
holdout_percent: z.number().min(0.05).max(0.5).describe("Share of the known population held back, 0.05–0.5 (0.2 = one in five)."),
|
|
333
|
-
attach_mode: z.enum(["apex", "manual"]).optional().describe("'apex' = Apex puts the list on the ad sets and checks daily (Meta only). 'manual' = the
|
|
334
|
+
attach_mode: z.enum(["apex", "manual"]).optional().describe("'apex' = Apex puts the list on the ad sets and checks daily (Meta only). 'manual' = the owner adds the audience as an exclusion by hand; Apex checks daily and counts nothing until it is on. Defaults to 'apex' on Meta, 'manual' elsewhere."),
|
|
334
335
|
washout_days: z.number().int().min(0).max(30).optional().describe("Days after the list lands before installs count, so pre-list ad exposure washes out. Default 3."),
|
|
335
336
|
population_segment_id: z.string().optional().describe("Optional Segment id narrowing who can be drawn."),
|
|
336
337
|
})
|
|
@@ -1212,8 +1213,7 @@ export const experimentTools = {
|
|
|
1212
1213
|
}
|
|
1213
1214
|
let isReachable = false;
|
|
1214
1215
|
try {
|
|
1215
|
-
const
|
|
1216
|
-
const res = await fetch(`${BASE_URL}/api/experiments/${experimentId}/assign`, {
|
|
1216
|
+
const res = await fetch(`${apexBaseUrl()}/api/experiments/${experimentId}/assign`, {
|
|
1217
1217
|
signal: AbortSignal.timeout(5000),
|
|
1218
1218
|
});
|
|
1219
1219
|
isReachable = res.ok;
|
|
@@ -1485,7 +1485,7 @@ export const experimentTools = {
|
|
|
1485
1485
|
blocker: d.blocker ?? null,
|
|
1486
1486
|
existing_belief_id: d.existingBeliefId ?? null,
|
|
1487
1487
|
in_plain_words: d.suggestions.length > 0
|
|
1488
|
-
? `${d.suggestions.length} verdict${d.suggestions.length === 1 ? "" : "s"} worth writing down. Show the
|
|
1488
|
+
? `${d.suggestions.length} verdict${d.suggestions.length === 1 ? "" : "s"} worth writing down. Show the owner the sentence; they can edit it before accept_belief_suggestion writes it as one study.`
|
|
1489
1489
|
: d.blocker
|
|
1490
1490
|
? blockerWords[d.blocker] ?? `Not offered: ${d.blocker}.`
|
|
1491
1491
|
: "Nothing worth writing down right now. Suggestions appear when an experiment settles cleanly.",
|
|
@@ -1499,10 +1499,10 @@ export const experimentTools = {
|
|
|
1499
1499
|
},
|
|
1500
1500
|
},
|
|
1501
1501
|
accept_belief_suggestion: {
|
|
1502
|
-
description: `${APEX} — Write a settled experiment's verdict down as one belief. Creates the Belief with the experiment as its supporting evidence and generatedFromExperimentId set; its evidence state starts at 'one study' and moves to 'validated' when a second test agrees. 'statement' is the
|
|
1502
|
+
description: `${APEX} — Write a settled experiment's verdict down as one belief. Creates the Belief with the experiment as its supporting evidence and generatedFromExperimentId set; its evidence state starts at 'one study' and moves to 'validated' when a second test agrees. 'statement' is the workspace's edit of the sentence (the numbers and evidence line stay Apex's). The gate re-runs here, so an experiment list_belief_suggestions would not offer is refused with its blocker. Writing it twice returns the existing belief. Admin only.`,
|
|
1503
1503
|
schema: z.object({
|
|
1504
1504
|
experimentId: z.string().describe("The settled experiment to write down."),
|
|
1505
|
-
statement: z.string().min(8).max(600).optional().describe("The
|
|
1505
|
+
statement: z.string().min(8).max(600).optional().describe("The owner's wording of the belief. Omit to accept Apex's sentence as written."),
|
|
1506
1506
|
}),
|
|
1507
1507
|
handler: async (args) => {
|
|
1508
1508
|
const id = args.experimentId.trim();
|
|
@@ -1521,7 +1521,7 @@ export const experimentTools = {
|
|
|
1521
1521
|
confidence: b.confidence,
|
|
1522
1522
|
evidence_state: "one_study",
|
|
1523
1523
|
generated_from_experiment_id: id,
|
|
1524
|
-
in_plain_words: "Written down. It is on the
|
|
1524
|
+
in_plain_words: "Written down. It is on the workspace's Belief Map as one study with this experiment as the evidence. A second test that agrees moves it to validated.",
|
|
1525
1525
|
}, null, 2),
|
|
1526
1526
|
}],
|
|
1527
1527
|
};
|
|
@@ -1539,7 +1539,7 @@ export const experimentTools = {
|
|
|
1539
1539
|
},
|
|
1540
1540
|
},
|
|
1541
1541
|
verify_ad_exclusion: {
|
|
1542
|
-
description: `${APEX} — Ad holdout only (surface paid_channel). Ask Apex to check, right now, whether the do-not-show list is on every ad set the holdout covers, and record the answer. Nothing is counted until this reads "attached"; the daily check runs on its own, so call this after the list lands or after the
|
|
1542
|
+
description: `${APEX} — Ad holdout only (surface paid_channel). Ask Apex to check, right now, whether the do-not-show list is on every ad set the holdout covers, and record the answer. Nothing is counted until this reads "attached"; the daily check runs on its own, so call this after the list lands or after the workspace attached it by hand. Rate-limited: a recent check returns the last reading instead of re-reading.`,
|
|
1543
1543
|
schema: z.object({
|
|
1544
1544
|
experimentId: z.string().describe("The Ad holdout experiment id."),
|
|
1545
1545
|
}),
|
|
@@ -1597,11 +1597,11 @@ export const experimentTools = {
|
|
|
1597
1597
|
},
|
|
1598
1598
|
},
|
|
1599
1599
|
attach_ad_exclusion: {
|
|
1600
|
-
description: `${APEX} — Ad holdout only (surface paid_channel, Meta only). Apex puts the held-back list on the holdout's ad sets itself: exactly one exclusion added per ad set, every other targeting key (locations, age, placements, ...) left as read, then read back to confirm. Two steps, always in this order: (1) call with preview=true (default) and SHOW the
|
|
1600
|
+
description: `${APEX} — Ad holdout only (surface paid_channel, Meta only). Apex puts the held-back list on the holdout's ad sets itself: exactly one exclusion added per ad set, every other targeting key (locations, age, placements, ...) left as read, then read back to confirm. Two steps, always in this order: (1) call with preview=true (default) and SHOW the workspace the before/after per ad set — which ad sets get the exclusion, which already have it, which are on another account and will not be touched; (2) once the workspace confirms, call with preview=false. The first real attach in a workspace needs acknowledge=true, which records the workspace's standing allowance for Apex to edit ad-set targeting (say so before passing it; never pass it without the workspace having seen the preview). Refuses with needs_targeting_ack when it is missing, list_not_on_network while the list is still syncing, and not for Google/TikTok/LinkedIn (there the owner adds the list in the network's tools and verify_ad_exclusion checks). Ending, pausing or archiving the holdout takes the list off again on its own. Not billed.`,
|
|
1601
1601
|
schema: z.object({
|
|
1602
1602
|
experimentId: z.string().describe("The Ad holdout experiment id."),
|
|
1603
1603
|
preview: z.boolean().optional().describe("true (default): read-only — return the before/after per ad set and whether the allowance is on file. false: make the edit."),
|
|
1604
|
-
acknowledge: z.boolean().optional().describe("Pass true on the first real attach in this workspace, after the
|
|
1604
|
+
acknowledge: z.boolean().optional().describe("Pass true on the first real attach in this workspace, after the owner has seen the preview and agreed that Apex may edit ad-set targeting. Recorded once per network."),
|
|
1605
1605
|
}),
|
|
1606
1606
|
handler: async (args) => {
|
|
1607
1607
|
const id = args.experimentId.trim();
|
|
@@ -1642,8 +1642,8 @@ export const experimentTools = {
|
|
|
1642
1642
|
ad_sets: rows,
|
|
1643
1643
|
_next: d.ready
|
|
1644
1644
|
? d.ack.present
|
|
1645
|
-
? `Show the
|
|
1646
|
-
: `Show the
|
|
1645
|
+
? `Show the owner this table. On their word, call attach_ad_exclusion({ experiment_id: "${id}", preview: false }).`
|
|
1646
|
+
: `Show the owner this table and say: confirming allows Apex to add the held-back list as one exclusion on holdout ad sets and remove it when a holdout ends; nothing else on the ad sets is changed. On their word, call attach_ad_exclusion({ experiment_id: "${id}", preview: false, acknowledge: true }).`
|
|
1647
1647
|
: "Fix the reason above, then preview again.",
|
|
1648
1648
|
}, null, 2),
|
|
1649
1649
|
}],
|
|
@@ -1686,17 +1686,17 @@ export const experimentTools = {
|
|
|
1686
1686
|
catch (err) {
|
|
1687
1687
|
const message = errMsg(err);
|
|
1688
1688
|
if (/needs_targeting_ack|\(409\)/.test(message)) {
|
|
1689
|
-
return textError(`Apex will not edit ad sets until the
|
|
1689
|
+
return textError(`Apex will not edit ad sets until the owner has allowed it. Call attach_ad_exclusion with preview=true, show the before/after, and on the owner's word call again with preview=false and acknowledge=true.`);
|
|
1690
1690
|
}
|
|
1691
1691
|
if (/network_not_supported/.test(message)) {
|
|
1692
|
-
return textError("Apex attaches the list itself on Meta only. On other networks the
|
|
1692
|
+
return textError("Apex attaches the list itself on Meta only. On other networks the owner adds the list in the network's own tools; verify_ad_exclusion checks it is on.");
|
|
1693
1693
|
}
|
|
1694
1694
|
return textError(`Could not attach the held-back list: ${message}`);
|
|
1695
1695
|
}
|
|
1696
1696
|
},
|
|
1697
1697
|
},
|
|
1698
1698
|
detach_ad_exclusion: {
|
|
1699
|
-
description: `${APEX} — Ad holdout only (surface paid_channel, Meta only). Take Apex's exclusion back off the holdout's ad sets and delete the held-back list from Meta. Removes only what Apex added: a list the
|
|
1699
|
+
description: `${APEX} — Ad holdout only (surface paid_channel, Meta only). Take Apex's exclusion back off the holdout's ad sets and delete the held-back list from Meta. Removes only what Apex added: a list the owner attached by hand is refused (not_apex_attached) and the owner removes it in Ads Manager. Ending, pausing or archiving the holdout does this on its own; call this only to undo an attach early. Admin only. Confirm with the workspace first: the held-back people start seeing the ads again and the holdout stops measuring.`,
|
|
1700
1700
|
schema: z.object({
|
|
1701
1701
|
experimentId: z.string().describe("The Ad holdout experiment id."),
|
|
1702
1702
|
}),
|
|
@@ -1728,7 +1728,7 @@ export const experimentTools = {
|
|
|
1728
1728
|
catch (err) {
|
|
1729
1729
|
const message = errMsg(err);
|
|
1730
1730
|
if (/not_apex_attached|\(409\)/.test(message)) {
|
|
1731
|
-
return textError("Apex did not put this list on the ad sets, so it will not take it off. The
|
|
1731
|
+
return textError("Apex did not put this list on the ad sets, so it will not take it off. The owner removes the exclusion in Ads Manager.");
|
|
1732
1732
|
}
|
|
1733
1733
|
return textError(`Could not take the held-back list off: ${message}`);
|
|
1734
1734
|
}
|