@capivv/mcp-server 0.5.51 → 0.5.54
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/dist/client.d.ts +23 -0
- package/dist/client.js +32 -0
- package/dist/resources/experiment-posterior.d.ts +14 -0
- package/dist/resources/experiment-posterior.js +38 -0
- package/dist/resources/index.js +2 -0
- package/dist/tools/get-experiment-posterior-cuped.d.ts +23 -0
- package/dist/tools/get-experiment-posterior-cuped.js +48 -0
- package/dist/tools/get-experiment-posterior-revenue.d.ts +23 -0
- package/dist/tools/get-experiment-posterior-revenue.js +48 -0
- package/dist/tools/index.js +4 -0
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -65,6 +65,29 @@ export declare class CapivvClient {
|
|
|
65
65
|
prior_alpha?: number;
|
|
66
66
|
prior_beta?: number;
|
|
67
67
|
}): Promise<unknown>;
|
|
68
|
+
/**
|
|
69
|
+
* V0.5.52 — issue #18 CUPED. Bayesian posterior with variance
|
|
70
|
+
* reduction using a named pre-experiment covariate.
|
|
71
|
+
*/
|
|
72
|
+
getExperimentPosteriorCuped(input: {
|
|
73
|
+
experiment_id: string;
|
|
74
|
+
covariate_name: string;
|
|
75
|
+
decision_threshold_pct?: number;
|
|
76
|
+
prior_alpha?: number;
|
|
77
|
+
prior_beta?: number;
|
|
78
|
+
}): Promise<unknown>;
|
|
79
|
+
/**
|
|
80
|
+
* V0.5.53 — issue #18 slice 5. Revenue posterior with optional
|
|
81
|
+
* trim/winsorize for heavy-tailed ARPU distributions.
|
|
82
|
+
*/
|
|
83
|
+
getExperimentPosteriorRevenue(input: {
|
|
84
|
+
experiment_id: string;
|
|
85
|
+
decision_threshold?: number;
|
|
86
|
+
prior_alpha?: number;
|
|
87
|
+
prior_beta?: number;
|
|
88
|
+
trim_top_pct?: number;
|
|
89
|
+
winsorize_top_pct?: number;
|
|
90
|
+
}): Promise<unknown>;
|
|
68
91
|
/**
|
|
69
92
|
* V0.5.48 — issue #18 Primitive 3 follow-up. Upload the Apple
|
|
70
93
|
* promotional-offer signing key. Distinct from the ASC API key
|
package/dist/client.js
CHANGED
|
@@ -170,6 +170,38 @@ export class CapivvClient {
|
|
|
170
170
|
const qs = params.toString() ? `?${params.toString()}` : '';
|
|
171
171
|
return this.get(`/dashboard/experiments/${encodeURIComponent(input.experiment_id)}/posterior${qs}`);
|
|
172
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* V0.5.52 — issue #18 CUPED. Bayesian posterior with variance
|
|
175
|
+
* reduction using a named pre-experiment covariate.
|
|
176
|
+
*/
|
|
177
|
+
async getExperimentPosteriorCuped(input) {
|
|
178
|
+
const params = new URLSearchParams({ covariate_name: input.covariate_name });
|
|
179
|
+
if (input.decision_threshold_pct !== undefined) {
|
|
180
|
+
params.set('decision_threshold_pct', String(input.decision_threshold_pct));
|
|
181
|
+
}
|
|
182
|
+
if (input.prior_alpha !== undefined) {
|
|
183
|
+
params.set('prior_alpha', String(input.prior_alpha));
|
|
184
|
+
}
|
|
185
|
+
if (input.prior_beta !== undefined) {
|
|
186
|
+
params.set('prior_beta', String(input.prior_beta));
|
|
187
|
+
}
|
|
188
|
+
return this.get(`/dashboard/experiments/${encodeURIComponent(input.experiment_id)}/posterior/cuped?${params.toString()}`);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* V0.5.53 — issue #18 slice 5. Revenue posterior with optional
|
|
192
|
+
* trim/winsorize for heavy-tailed ARPU distributions.
|
|
193
|
+
*/
|
|
194
|
+
async getExperimentPosteriorRevenue(input) {
|
|
195
|
+
const params = new URLSearchParams();
|
|
196
|
+
for (const [k, v] of Object.entries(input)) {
|
|
197
|
+
if (k === 'experiment_id')
|
|
198
|
+
continue;
|
|
199
|
+
if (v !== undefined)
|
|
200
|
+
params.set(k, String(v));
|
|
201
|
+
}
|
|
202
|
+
const qs = params.toString() ? `?${params.toString()}` : '';
|
|
203
|
+
return this.get(`/dashboard/experiments/${encodeURIComponent(input.experiment_id)}/posterior/revenue${qs}`);
|
|
204
|
+
}
|
|
173
205
|
/**
|
|
174
206
|
* V0.5.48 — issue #18 Primitive 3 follow-up. Upload the Apple
|
|
175
207
|
* promotional-offer signing key. Distinct from the ASC API key
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { CapivvClient } from '../client.js';
|
|
3
|
+
/**
|
|
4
|
+
* Bayesian posterior as an MCP resource (issue #18 follow-up). Agents
|
|
5
|
+
* that auto-attach matching resources get the experiment's posterior +
|
|
6
|
+
* recommendation in context without an explicit tool call. The list()
|
|
7
|
+
* walks the active experiments so a "list resources" call surfaces one
|
|
8
|
+
* URI per experiment.
|
|
9
|
+
*
|
|
10
|
+
* Companion tools (capivv_get_experiment_posterior, _cuped, _revenue)
|
|
11
|
+
* still cover the what-if + CUPED + revenue paths since those take
|
|
12
|
+
* arguments resources can't express.
|
|
13
|
+
*/
|
|
14
|
+
export declare function registerExperimentPosteriorResource(server: McpServer, client: CapivvClient): void;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
/**
|
|
3
|
+
* Bayesian posterior as an MCP resource (issue #18 follow-up). Agents
|
|
4
|
+
* that auto-attach matching resources get the experiment's posterior +
|
|
5
|
+
* recommendation in context without an explicit tool call. The list()
|
|
6
|
+
* walks the active experiments so a "list resources" call surfaces one
|
|
7
|
+
* URI per experiment.
|
|
8
|
+
*
|
|
9
|
+
* Companion tools (capivv_get_experiment_posterior, _cuped, _revenue)
|
|
10
|
+
* still cover the what-if + CUPED + revenue paths since those take
|
|
11
|
+
* arguments resources can't express.
|
|
12
|
+
*/
|
|
13
|
+
export function registerExperimentPosteriorResource(server, client) {
|
|
14
|
+
server.resource('experiment-posterior', new ResourceTemplate('capivv://experiments/{experimentId}/posterior', {
|
|
15
|
+
list: async () => {
|
|
16
|
+
const experiments = await client.listExperiments();
|
|
17
|
+
return {
|
|
18
|
+
resources: experiments.map((e) => ({
|
|
19
|
+
uri: `capivv://experiments/${e.id}/posterior`,
|
|
20
|
+
name: `${e.name} — posterior`,
|
|
21
|
+
description: `Bayesian posterior (P(B>A), expected loss, credible interval, recommendation) for "${e.name}" [${e.status}]`,
|
|
22
|
+
mimeType: 'application/json',
|
|
23
|
+
})),
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
}), { mimeType: 'application/json' }, async (uri, { experimentId }) => {
|
|
27
|
+
const id = Array.isArray(experimentId) ? experimentId[0] : experimentId;
|
|
28
|
+
const posterior = await client.getExperimentPosterior({ experiment_id: id });
|
|
29
|
+
return {
|
|
30
|
+
contents: [
|
|
31
|
+
{
|
|
32
|
+
uri: uri.href,
|
|
33
|
+
text: JSON.stringify(posterior, null, 2),
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
}
|
package/dist/resources/index.js
CHANGED
|
@@ -3,10 +3,12 @@ import { registerRulesResource } from './rules.js';
|
|
|
3
3
|
import { registerConceptsResource } from './concepts.js';
|
|
4
4
|
import { registerQuickstartResource } from './quickstart.js';
|
|
5
5
|
import { registerGuideResources } from './guides.js';
|
|
6
|
+
import { registerExperimentPosteriorResource } from './experiment-posterior.js';
|
|
6
7
|
export function registerAllResources(server, client) {
|
|
7
8
|
registerStatusResource(server, client);
|
|
8
9
|
registerRulesResource(server, client);
|
|
9
10
|
registerConceptsResource(server);
|
|
10
11
|
registerQuickstartResource(server);
|
|
11
12
|
registerGuideResources(server);
|
|
13
|
+
registerExperimentPosteriorResource(server, client);
|
|
12
14
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { CapivvClient } from '../client.js';
|
|
3
|
+
/**
|
|
4
|
+
* V0.5.52 — CUPED-adjusted Bayesian posterior. Issue #18 analysis
|
|
5
|
+
* surface slice 4.
|
|
6
|
+
*
|
|
7
|
+
* Variance reduction using a per-user pre-experiment covariate
|
|
8
|
+
* ingested via `Capivv.identify({ preExperimentCovariates: {...} })`
|
|
9
|
+
* (the v0.5.51 ingest surface). When the covariate is predictive of
|
|
10
|
+
* the outcome, CUPED produces tighter posterior credible intervals
|
|
11
|
+
* for the same N — "free power" for small-N mobile experiments.
|
|
12
|
+
*
|
|
13
|
+
* Math: subtract the regression-estimated contribution of the
|
|
14
|
+
* covariate from each user's outcome, then run a Normal-Normal
|
|
15
|
+
* posterior on the difference of adjusted means. Falls back to the
|
|
16
|
+
* unadjusted binary posterior when < 30 users have the covariate
|
|
17
|
+
* (not enough data to fit a stable regression coefficient).
|
|
18
|
+
*
|
|
19
|
+
* Response includes the per-arm count of users with covariate
|
|
20
|
+
* observations so the operator can see how much CUPED actually had
|
|
21
|
+
* to work with.
|
|
22
|
+
*/
|
|
23
|
+
export declare function registerGetExperimentPosteriorCupedTool(server: McpServer, client: CapivvClient): void;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* V0.5.52 — CUPED-adjusted Bayesian posterior. Issue #18 analysis
|
|
4
|
+
* surface slice 4.
|
|
5
|
+
*
|
|
6
|
+
* Variance reduction using a per-user pre-experiment covariate
|
|
7
|
+
* ingested via `Capivv.identify({ preExperimentCovariates: {...} })`
|
|
8
|
+
* (the v0.5.51 ingest surface). When the covariate is predictive of
|
|
9
|
+
* the outcome, CUPED produces tighter posterior credible intervals
|
|
10
|
+
* for the same N — "free power" for small-N mobile experiments.
|
|
11
|
+
*
|
|
12
|
+
* Math: subtract the regression-estimated contribution of the
|
|
13
|
+
* covariate from each user's outcome, then run a Normal-Normal
|
|
14
|
+
* posterior on the difference of adjusted means. Falls back to the
|
|
15
|
+
* unadjusted binary posterior when < 30 users have the covariate
|
|
16
|
+
* (not enough data to fit a stable regression coefficient).
|
|
17
|
+
*
|
|
18
|
+
* Response includes the per-arm count of users with covariate
|
|
19
|
+
* observations so the operator can see how much CUPED actually had
|
|
20
|
+
* to work with.
|
|
21
|
+
*/
|
|
22
|
+
export function registerGetExperimentPosteriorCupedTool(server, client) {
|
|
23
|
+
server.tool('capivv_get_experiment_posterior_cuped', "CUPED-adjusted Bayesian posterior. Pass the same covariate_name you use in Capivv.identify({ preExperimentCovariates: {...} }) — capivv joins each variant's assignments against your covariate data and returns the variance-reduced posterior. Use this when a pre-experiment baseline (e.g. lifetime_revenue_28d) is predictive of conversion.", {
|
|
24
|
+
experiment_id: z.string().uuid().describe('The experiment to analyze'),
|
|
25
|
+
covariate_name: z
|
|
26
|
+
.string()
|
|
27
|
+
.min(1)
|
|
28
|
+
.describe('The name of the covariate to adjust against. Must match what the SDK has been writing via `preExperimentCovariates`. Common names: "lifetime_revenue_28d", "session_count_7d", "previous_purchase_count".'),
|
|
29
|
+
decision_threshold_pct: z
|
|
30
|
+
.number()
|
|
31
|
+
.min(0)
|
|
32
|
+
.optional()
|
|
33
|
+
.describe('Minimum lift in percentage points to count as "worth shipping." Default 0.'),
|
|
34
|
+
prior_alpha: z
|
|
35
|
+
.number()
|
|
36
|
+
.positive()
|
|
37
|
+
.optional()
|
|
38
|
+
.describe('Beta prior α₀. Default 1 (uniform).'),
|
|
39
|
+
prior_beta: z
|
|
40
|
+
.number()
|
|
41
|
+
.positive()
|
|
42
|
+
.optional()
|
|
43
|
+
.describe('Beta prior β₀. Default 1 (uniform).'),
|
|
44
|
+
}, async (args) => {
|
|
45
|
+
const result = await client.getExperimentPosteriorCuped(args);
|
|
46
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
47
|
+
});
|
|
48
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { CapivvClient } from '../client.js';
|
|
3
|
+
/**
|
|
4
|
+
* V0.5.53 — Bayesian posterior on revenue outcomes with optional
|
|
5
|
+
* trim/winsorize for heavy-tailed ARPU distributions. Issue #18
|
|
6
|
+
* analysis surface slice 5.
|
|
7
|
+
*
|
|
8
|
+
* Revenue is dominated by whales — standard variance estimators
|
|
9
|
+
* assume thin tails and produce nonsense credible intervals on
|
|
10
|
+
* small-N mobile data. Trimming (drop top K%) or winsorization (cap
|
|
11
|
+
* top K% at the threshold percentile) lets the posterior reflect
|
|
12
|
+
* the typical user without one whale skewing the entire arm.
|
|
13
|
+
*
|
|
14
|
+
* Reads conversion_value_micros per user from experiment_assignments.
|
|
15
|
+
* Non-converters contribute 0 micros (this is the per-user ARPU
|
|
16
|
+
* metric, not converters-only revenue). All thresholds and outputs
|
|
17
|
+
* are in micros (1 USD = 1_000_000 micros).
|
|
18
|
+
*
|
|
19
|
+
* Recommendation: trim_top_pct=5 or winsorize_top_pct=5 is a
|
|
20
|
+
* reasonable starting point for typical mobile-subscription revenue.
|
|
21
|
+
* Set both to 0 for the unadjusted posterior.
|
|
22
|
+
*/
|
|
23
|
+
export declare function registerGetExperimentPosteriorRevenueTool(server: McpServer, client: CapivvClient): void;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* V0.5.53 — Bayesian posterior on revenue outcomes with optional
|
|
4
|
+
* trim/winsorize for heavy-tailed ARPU distributions. Issue #18
|
|
5
|
+
* analysis surface slice 5.
|
|
6
|
+
*
|
|
7
|
+
* Revenue is dominated by whales — standard variance estimators
|
|
8
|
+
* assume thin tails and produce nonsense credible intervals on
|
|
9
|
+
* small-N mobile data. Trimming (drop top K%) or winsorization (cap
|
|
10
|
+
* top K% at the threshold percentile) lets the posterior reflect
|
|
11
|
+
* the typical user without one whale skewing the entire arm.
|
|
12
|
+
*
|
|
13
|
+
* Reads conversion_value_micros per user from experiment_assignments.
|
|
14
|
+
* Non-converters contribute 0 micros (this is the per-user ARPU
|
|
15
|
+
* metric, not converters-only revenue). All thresholds and outputs
|
|
16
|
+
* are in micros (1 USD = 1_000_000 micros).
|
|
17
|
+
*
|
|
18
|
+
* Recommendation: trim_top_pct=5 or winsorize_top_pct=5 is a
|
|
19
|
+
* reasonable starting point for typical mobile-subscription revenue.
|
|
20
|
+
* Set both to 0 for the unadjusted posterior.
|
|
21
|
+
*/
|
|
22
|
+
export function registerGetExperimentPosteriorRevenueTool(server, client) {
|
|
23
|
+
server.tool('capivv_get_experiment_posterior_revenue', 'Bayesian posterior on revenue per assigned user (ARPU). Optional trim or winsorize handles whale-skew. Input + output are in micros (1 USD = 1_000_000 micros). Use trim_top_pct=5 or winsorize_top_pct=5 as a robust default for small-N mobile experiments.', {
|
|
24
|
+
experiment_id: z.string().uuid().describe('The experiment to analyze'),
|
|
25
|
+
decision_threshold: z
|
|
26
|
+
.number()
|
|
27
|
+
.min(0)
|
|
28
|
+
.optional()
|
|
29
|
+
.describe('Minimum lift in micros to count as "worth shipping." Default 0.'),
|
|
30
|
+
prior_alpha: z.number().positive().optional().describe('Beta prior α₀. Default 1.'),
|
|
31
|
+
prior_beta: z.number().positive().optional().describe('Beta prior β₀. Default 1.'),
|
|
32
|
+
trim_top_pct: z
|
|
33
|
+
.number()
|
|
34
|
+
.min(0)
|
|
35
|
+
.max(50)
|
|
36
|
+
.optional()
|
|
37
|
+
.describe('Drop the top K% by revenue before computing means. Default 0 (no trim). Mutually exclusive with winsorize_top_pct — trim wins if both set.'),
|
|
38
|
+
winsorize_top_pct: z
|
|
39
|
+
.number()
|
|
40
|
+
.min(0)
|
|
41
|
+
.max(50)
|
|
42
|
+
.optional()
|
|
43
|
+
.describe('Cap the top K% at the percentile threshold (preserves N). Default 0 (no winsorize). Use when sample size matters more than tail-handling.'),
|
|
44
|
+
}, async (args) => {
|
|
45
|
+
const result = await client.getExperimentPosteriorRevenue(args);
|
|
46
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
47
|
+
});
|
|
48
|
+
}
|
package/dist/tools/index.js
CHANGED
|
@@ -54,6 +54,8 @@ import { registerStartExperimentTool } from './start-experiment.js';
|
|
|
54
54
|
import { registerStopExperimentTool } from './stop-experiment.js';
|
|
55
55
|
import { registerGetExperimentSummaryTool } from './get-experiment-summary.js';
|
|
56
56
|
import { registerGetExperimentPosteriorTool } from './get-experiment-posterior.js';
|
|
57
|
+
import { registerGetExperimentPosteriorCupedTool } from './get-experiment-posterior-cuped.js';
|
|
58
|
+
import { registerGetExperimentPosteriorRevenueTool } from './get-experiment-posterior-revenue.js';
|
|
57
59
|
import { registerUpdateAppTool } from './update-app.js';
|
|
58
60
|
import { registerDeleteAppTool } from './delete-app.js';
|
|
59
61
|
import { registerArchiveAppTool } from './archive-app.js';
|
|
@@ -172,6 +174,8 @@ export function registerAllTools(server, client) {
|
|
|
172
174
|
registerStopExperimentTool(server, client);
|
|
173
175
|
registerGetExperimentSummaryTool(server, client);
|
|
174
176
|
registerGetExperimentPosteriorTool(server, client);
|
|
177
|
+
registerGetExperimentPosteriorCupedTool(server, client);
|
|
178
|
+
registerGetExperimentPosteriorRevenueTool(server, client);
|
|
175
179
|
// Apps gap-fillers (V8 Phase B.6)
|
|
176
180
|
registerUpdateAppTool(server, client);
|
|
177
181
|
registerDeleteAppTool(server, client);
|