@capivv/mcp-server 0.1.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.
Files changed (43) hide show
  1. package/README.md +124 -0
  2. package/dist/client.d.ts +31 -0
  3. package/dist/client.js +113 -0
  4. package/dist/config.d.ts +9 -0
  5. package/dist/config.js +16 -0
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.js +28 -0
  8. package/dist/resources/concepts.d.ts +9 -0
  9. package/dist/resources/concepts.js +92 -0
  10. package/dist/resources/index.d.ts +3 -0
  11. package/dist/resources/index.js +8 -0
  12. package/dist/resources/rules.d.ts +3 -0
  13. package/dist/resources/rules.js +27 -0
  14. package/dist/resources/status.d.ts +3 -0
  15. package/dist/resources/status.js +43 -0
  16. package/dist/server.d.ts +3 -0
  17. package/dist/server.js +11 -0
  18. package/dist/tools/apply-rule.d.ts +3 -0
  19. package/dist/tools/apply-rule.js +37 -0
  20. package/dist/tools/create-offering.d.ts +3 -0
  21. package/dist/tools/create-offering.js +24 -0
  22. package/dist/tools/get-analytics.d.ts +3 -0
  23. package/dist/tools/get-analytics.js +29 -0
  24. package/dist/tools/import-products.d.ts +3 -0
  25. package/dist/tools/import-products.js +18 -0
  26. package/dist/tools/index.d.ts +3 -0
  27. package/dist/tools/index.js +22 -0
  28. package/dist/tools/list-apps.d.ts +3 -0
  29. package/dist/tools/list-apps.js +6 -0
  30. package/dist/tools/list-experiments.d.ts +3 -0
  31. package/dist/tools/list-experiments.js +34 -0
  32. package/dist/tools/list-offerings.d.ts +3 -0
  33. package/dist/tools/list-offerings.js +6 -0
  34. package/dist/tools/list-products.d.ts +3 -0
  35. package/dist/tools/list-products.js +16 -0
  36. package/dist/tools/list-rules.d.ts +3 -0
  37. package/dist/tools/list-rules.js +13 -0
  38. package/dist/tools/status.d.ts +3 -0
  39. package/dist/tools/status.js +62 -0
  40. package/dist/types.d.ts +184 -0
  41. package/dist/types.js +3 -0
  42. package/mcp.json +149 -0
  43. package/package.json +55 -0
package/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # @capivv/mcp-server
2
+
3
+ MCP (Model Context Protocol) server for the [Capivv](https://github.com/capivv/capivv) subscription management platform. Enables AI assistants like Claude, Cursor, and other MCP clients to manage your in-app subscription configuration.
4
+
5
+ ## Installation
6
+
7
+ ```sh
8
+ # Run directly with npx (recommended)
9
+ npx @capivv/mcp-server
10
+
11
+ # Or install globally
12
+ npm install -g @capivv/mcp-server
13
+ capivv-mcp
14
+ ```
15
+
16
+ ## Configuration
17
+
18
+ The server requires an API key from your Capivv dashboard.
19
+
20
+ | Environment Variable | Required | Description |
21
+ |---------------------|----------|-------------|
22
+ | `CAPIVV_API_KEY` | Yes | API key from Settings > Developer > API Keys |
23
+ | `CAPIVV_API_URL` | No | API base URL (default: `https://api.capivv.com`) |
24
+ | `CAPIVV_ORG_ID` | No | Organization ID for multi-org accounts |
25
+
26
+ ## Setup
27
+
28
+ ### Claude Desktop
29
+
30
+ Add to your `claude_desktop_config.json`:
31
+
32
+ ```json
33
+ {
34
+ "mcpServers": {
35
+ "capivv": {
36
+ "command": "npx",
37
+ "args": ["-y", "@capivv/mcp-server"],
38
+ "env": {
39
+ "CAPIVV_API_KEY": "your-api-key-here"
40
+ }
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ **Config file location:**
47
+ - macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
48
+ - Windows: `%APPDATA%\Claude\claude_desktop_config.json`
49
+
50
+ ### Claude Code
51
+
52
+ ```sh
53
+ claude mcp add capivv -- npx -y @capivv/mcp-server
54
+ ```
55
+
56
+ Set the API key in your environment or `.env` file:
57
+
58
+ ```sh
59
+ export CAPIVV_API_KEY="your-api-key-here"
60
+ ```
61
+
62
+ ### Cursor
63
+
64
+ Add to `.cursor/mcp.json` in your project root:
65
+
66
+ ```json
67
+ {
68
+ "mcpServers": {
69
+ "capivv": {
70
+ "command": "npx",
71
+ "args": ["-y", "@capivv/mcp-server"],
72
+ "env": {
73
+ "CAPIVV_API_KEY": "your-api-key-here"
74
+ }
75
+ }
76
+ }
77
+ }
78
+ ```
79
+
80
+ ### Generic MCP Client
81
+
82
+ The server uses stdio transport. Start it with:
83
+
84
+ ```sh
85
+ CAPIVV_API_KEY=your-api-key npx @capivv/mcp-server
86
+ ```
87
+
88
+ ## Available Tools
89
+
90
+ | Tool | Description |
91
+ |------|-------------|
92
+ | `capivv_status` | Get setup progress, resource counts, and key metrics |
93
+ | `capivv_list_apps` | List all apps with platform and bundle ID |
94
+ | `capivv_list_products` | List products with store IDs and entitlements (optional `app_id` filter) |
95
+ | `capivv_import_products` | Preview store product import from App Store Connect / Google Play |
96
+ | `capivv_list_offerings` | List offerings with packages and products |
97
+ | `capivv_create_offering` | Create a new offering with packages |
98
+ | `capivv_list_rules` | List YAML business rules with status and priority |
99
+ | `capivv_apply_rule` | Validate and apply a YAML rule configuration |
100
+ | `capivv_list_experiments` | List A/B experiments with results and confidence |
101
+ | `capivv_get_analytics` | Get MRR, ARR, churn rate, ARPU, and period comparison |
102
+
103
+ ## Available Resources
104
+
105
+ | URI | Description |
106
+ |-----|-------------|
107
+ | `capivv://status` | Current platform status as JSON |
108
+ | `capivv://rules/{ruleId}` | Individual rule YAML content |
109
+ | `capivv://docs/concepts` | Glossary of subscription platform concepts |
110
+
111
+ ## Troubleshooting
112
+
113
+ **"CAPIVV_API_KEY is required"**
114
+ Set the `CAPIVV_API_KEY` environment variable. Get your API key from the Capivv dashboard under Settings > Developer > API Keys.
115
+
116
+ **Tools not appearing in Claude Desktop**
117
+ Restart Claude Desktop after updating `claude_desktop_config.json`. Check the MCP logs for errors.
118
+
119
+ **Connection errors**
120
+ Verify your API key is valid and your network can reach the Capivv API. If using a custom `CAPIVV_API_URL`, ensure the URL is correct.
121
+
122
+ ## License
123
+
124
+ MIT
@@ -0,0 +1,31 @@
1
+ import type { Config } from './config.js';
2
+ import type { App, Product, Entitlement, Offering, Rule, Experiment, ExperimentSummary, AnalyticsOverview, OnboardingResponse, ImportPreviewResponse, CreateOfferingRequest, CreateRuleRequest, ValidateRuleResponse } from './types.js';
3
+ export declare class ApiError extends Error {
4
+ status: number;
5
+ code: string;
6
+ details?: Record<string, unknown> | undefined;
7
+ constructor(status: number, code: string, message: string, details?: Record<string, unknown> | undefined);
8
+ }
9
+ export declare class CapivvClient {
10
+ private baseUrl;
11
+ private headers;
12
+ constructor(config: Config);
13
+ private request;
14
+ private get;
15
+ private post;
16
+ healthCheck(): Promise<boolean>;
17
+ listApps(): Promise<App[]>;
18
+ listProducts(appId?: string): Promise<Product[]>;
19
+ listEntitlements(): Promise<Entitlement[]>;
20
+ listOfferings(): Promise<Offering[]>;
21
+ createOffering(data: CreateOfferingRequest): Promise<Offering>;
22
+ listRules(): Promise<Rule[]>;
23
+ getRule(id: string): Promise<Rule>;
24
+ upsertRule(data: CreateRuleRequest): Promise<Rule>;
25
+ validateRule(content: string): Promise<ValidateRuleResponse>;
26
+ listExperiments(): Promise<Experiment[]>;
27
+ getExperimentSummaries(): Promise<ExperimentSummary[]>;
28
+ getAnalyticsOverview(): Promise<AnalyticsOverview>;
29
+ getOnboarding(): Promise<OnboardingResponse>;
30
+ getImportPreview(appId: string): Promise<ImportPreviewResponse>;
31
+ }
package/dist/client.js ADDED
@@ -0,0 +1,113 @@
1
+ export class ApiError extends Error {
2
+ status;
3
+ code;
4
+ details;
5
+ constructor(status, code, message, details) {
6
+ super(message);
7
+ this.status = status;
8
+ this.code = code;
9
+ this.details = details;
10
+ this.name = 'ApiError';
11
+ }
12
+ }
13
+ export class CapivvClient {
14
+ baseUrl;
15
+ headers;
16
+ constructor(config) {
17
+ this.baseUrl = `${config.apiUrl}/v1`;
18
+ this.headers = {
19
+ Authorization: `Bearer ${config.apiKey}`,
20
+ 'Content-Type': 'application/json',
21
+ ...(config.orgId ? { 'X-Organization-ID': config.orgId } : {}),
22
+ };
23
+ }
24
+ async request(method, path, body) {
25
+ const url = `${this.baseUrl}${path}`;
26
+ const options = {
27
+ method,
28
+ headers: this.headers,
29
+ ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
30
+ };
31
+ let response;
32
+ try {
33
+ response = await fetch(url, options);
34
+ }
35
+ catch (err) {
36
+ throw new ApiError(0, 'network_error', `Failed to connect to Capivv API at ${this.baseUrl}: ${err.message}`);
37
+ }
38
+ if (!response.ok) {
39
+ const errorBody = await response.json().catch(() => ({}));
40
+ const err = errorBody?.error;
41
+ throw new ApiError(response.status, err?.code ?? 'unknown', err?.message ?? `API request failed: ${response.status} ${response.statusText}`, err?.details);
42
+ }
43
+ if (response.status === 204) {
44
+ return undefined;
45
+ }
46
+ return response.json();
47
+ }
48
+ get(path) {
49
+ return this.request('GET', path);
50
+ }
51
+ post(path, body) {
52
+ return this.request('POST', path, body);
53
+ }
54
+ async healthCheck() {
55
+ try {
56
+ await this.get('/health');
57
+ return true;
58
+ }
59
+ catch {
60
+ return false;
61
+ }
62
+ }
63
+ async listApps() {
64
+ const resp = await this.get('/dashboard/apps');
65
+ return resp.apps;
66
+ }
67
+ async listProducts(appId) {
68
+ const path = appId ? `/dashboard/products?app_id=${encodeURIComponent(appId)}` : '/dashboard/products';
69
+ const resp = await this.get(path);
70
+ return resp.products;
71
+ }
72
+ async listEntitlements() {
73
+ const resp = await this.get('/dashboard/entitlements');
74
+ return resp.entitlements;
75
+ }
76
+ async listOfferings() {
77
+ const resp = await this.get('/dashboard/offerings');
78
+ return resp.offerings;
79
+ }
80
+ async createOffering(data) {
81
+ return this.post('/dashboard/offerings', data);
82
+ }
83
+ async listRules() {
84
+ const resp = await this.get('/dashboard/rules');
85
+ return resp.rules;
86
+ }
87
+ async getRule(id) {
88
+ return this.get(`/dashboard/rules/${encodeURIComponent(id)}`);
89
+ }
90
+ async upsertRule(data) {
91
+ return this.post('/dashboard/rules', data);
92
+ }
93
+ async validateRule(content) {
94
+ return this.post('/dashboard/rules/validate', { content });
95
+ }
96
+ async listExperiments() {
97
+ const resp = await this.get('/dashboard/experiments/active');
98
+ return resp.experiments;
99
+ }
100
+ async getExperimentSummaries() {
101
+ const resp = await this.get('/dashboard/experiments/summaries');
102
+ return resp.summaries;
103
+ }
104
+ async getAnalyticsOverview() {
105
+ return this.get('/dashboard/analytics/overview');
106
+ }
107
+ async getOnboarding() {
108
+ return this.get('/dashboard/onboarding');
109
+ }
110
+ async getImportPreview(appId) {
111
+ return this.get(`/dashboard/onboarding/import-preview?app_id=${encodeURIComponent(appId)}`);
112
+ }
113
+ }
@@ -0,0 +1,9 @@
1
+ export interface Config {
2
+ apiKey: string;
3
+ apiUrl: string;
4
+ orgId: string | undefined;
5
+ }
6
+ export declare class ConfigError extends Error {
7
+ constructor(message: string);
8
+ }
9
+ export declare function loadConfig(): Config;
package/dist/config.js ADDED
@@ -0,0 +1,16 @@
1
+ export class ConfigError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = 'ConfigError';
5
+ }
6
+ }
7
+ export function loadConfig() {
8
+ const apiKey = process.env.CAPIVV_API_KEY;
9
+ if (!apiKey) {
10
+ throw new ConfigError('CAPIVV_API_KEY environment variable is required. ' +
11
+ 'Create an API key in the Capivv dashboard under Settings > Developer > API Keys.');
12
+ }
13
+ const apiUrl = (process.env.CAPIVV_API_URL || 'https://api.capivv.com').replace(/\/+$/, '');
14
+ const orgId = process.env.CAPIVV_ORG_ID || undefined;
15
+ return { apiKey, apiUrl, orgId };
16
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { loadConfig, ConfigError } from './config.js';
4
+ import { createServer } from './server.js';
5
+ async function main() {
6
+ let config;
7
+ try {
8
+ config = loadConfig();
9
+ }
10
+ catch (err) {
11
+ if (err instanceof ConfigError) {
12
+ console.error(`[capivv-mcp] ${err.message}`);
13
+ process.exit(1);
14
+ }
15
+ throw err;
16
+ }
17
+ const server = createServer(config);
18
+ const transport = new StdioServerTransport();
19
+ process.on('SIGINT', async () => {
20
+ await server.close();
21
+ process.exit(0);
22
+ });
23
+ await server.connect(transport);
24
+ }
25
+ main().catch((err) => {
26
+ console.error('[capivv-mcp] Fatal error:', err);
27
+ process.exit(1);
28
+ });
@@ -0,0 +1,9 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ interface Concept {
3
+ term: string;
4
+ definition: string;
5
+ relatedTerms: string[];
6
+ }
7
+ declare const CONCEPTS: Concept[];
8
+ export declare function registerConceptsResource(server: McpServer): void;
9
+ export { CONCEPTS };
@@ -0,0 +1,92 @@
1
+ // Capivv platform concepts — mirrored from dashboard/src/lib/knowledge-base.ts GLOSSARY
2
+ const CONCEPTS = [
3
+ {
4
+ term: 'App',
5
+ definition: "A platform-specific application (iOS, Android, or Web) registered in Capivv. Each app has a unique Bundle ID and connects to a specific app store.",
6
+ relatedTerms: ['Bundle ID', 'Product'],
7
+ },
8
+ {
9
+ term: 'Bundle ID',
10
+ definition: "Your app's unique identifier (e.g. com.company.app). Must match the identifier registered in App Store Connect or Google Play Console.",
11
+ relatedTerms: ['App'],
12
+ },
13
+ {
14
+ term: 'Entitlement',
15
+ definition: "A feature or capability users can access (e.g. 'premium', 'no_ads'). Entitlements are granted when a user purchases a product that includes them.",
16
+ relatedTerms: ['Product', 'Offering'],
17
+ },
18
+ {
19
+ term: 'Experiment',
20
+ definition: 'An A/B test that compares different paywalls, prices, or offerings to find what converts best. Results include statistical confidence metrics.',
21
+ relatedTerms: ['Paywall', 'Offering'],
22
+ },
23
+ {
24
+ term: 'Offering',
25
+ definition: 'A bundle of products presented to users. Your paywall displays the current offering. Each offering contains one or more packages.',
26
+ relatedTerms: ['Package', 'Product', 'Paywall'],
27
+ },
28
+ {
29
+ term: 'Package',
30
+ definition: "A single purchase option within an offering (e.g. 'Monthly' or 'Annual' plan). Each package maps to a product.",
31
+ relatedTerms: ['Offering', 'Product'],
32
+ },
33
+ {
34
+ term: 'Paywall',
35
+ definition: 'A customizable screen presented to users when they hit a premium feature. Paywalls display your offering and drive purchase conversions.',
36
+ relatedTerms: ['Offering', 'Experiment'],
37
+ },
38
+ {
39
+ term: 'Product',
40
+ definition: 'A purchasable item from App Store or Play Store. Products link to the entitlements they grant when purchased.',
41
+ relatedTerms: ['Entitlement', 'Offering', 'Subscription'],
42
+ },
43
+ {
44
+ term: 'Promotion',
45
+ definition: 'A time-limited campaign offering discounts, extended trials, or special deals to targeted user segments.',
46
+ relatedTerms: ['Rule', 'Experiment'],
47
+ },
48
+ {
49
+ term: 'Public Key',
50
+ definition: 'An API key used in your mobile/web app to authenticate SDK calls. Safe to include in client-side code.',
51
+ relatedTerms: ['Secret Key'],
52
+ },
53
+ {
54
+ term: 'Rescue Flow',
55
+ definition: 'An automated workflow triggered when a user tries to cancel their subscription. Presents save offers to reduce churn.',
56
+ relatedTerms: ['Promotion', 'Subscription'],
57
+ },
58
+ {
59
+ term: 'Rule',
60
+ definition: 'A YAML-based condition that controls pricing, targeting, or churn recovery. Rules evaluate user attributes and trigger actions like discounts or paywall changes.',
61
+ relatedTerms: ['Promotion', 'Experiment'],
62
+ },
63
+ {
64
+ term: 'Secret Key',
65
+ definition: 'An API key used on your backend server for admin operations like managing users and subscriptions. Never expose to client code.',
66
+ relatedTerms: ['Public Key'],
67
+ },
68
+ {
69
+ term: 'Subscription',
70
+ definition: 'A recurring purchase that grants entitlements for a billing period. Subscriptions can be monthly, annual, or custom intervals.',
71
+ relatedTerms: ['Product', 'Entitlement'],
72
+ },
73
+ {
74
+ term: 'Webhook',
75
+ definition: 'An HTTP callback sent to your server when events occur in Capivv (purchases, cancellations, renewals, etc.). Used to keep your backend in sync.',
76
+ relatedTerms: ['Subscription'],
77
+ },
78
+ ];
79
+ export function registerConceptsResource(server) {
80
+ server.resource('concepts', 'capivv://docs/concepts', { mimeType: 'application/json' }, async (uri) => {
81
+ return {
82
+ contents: [
83
+ {
84
+ uri: uri.href,
85
+ text: JSON.stringify(CONCEPTS, null, 2),
86
+ },
87
+ ],
88
+ };
89
+ });
90
+ }
91
+ // Exported for tests
92
+ export { CONCEPTS };
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { CapivvClient } from '../client.js';
3
+ export declare function registerAllResources(server: McpServer, client: CapivvClient): void;
@@ -0,0 +1,8 @@
1
+ import { registerStatusResource } from './status.js';
2
+ import { registerRulesResource } from './rules.js';
3
+ import { registerConceptsResource } from './concepts.js';
4
+ export function registerAllResources(server, client) {
5
+ registerStatusResource(server, client);
6
+ registerRulesResource(server, client);
7
+ registerConceptsResource(server);
8
+ }
@@ -0,0 +1,3 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { CapivvClient } from '../client.js';
3
+ export declare function registerRulesResource(server: McpServer, client: CapivvClient): void;
@@ -0,0 +1,27 @@
1
+ import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export function registerRulesResource(server, client) {
3
+ server.resource('rule', new ResourceTemplate('capivv://rules/{ruleId}', {
4
+ list: async () => {
5
+ const rules = await client.listRules();
6
+ return {
7
+ resources: rules.map((r) => ({
8
+ uri: `capivv://rules/${r.id}`,
9
+ name: r.name,
10
+ description: `Rule: ${r.name} (priority: ${r.priority}, ${r.is_enabled ? 'enabled' : 'disabled'})`,
11
+ mimeType: 'text/yaml',
12
+ })),
13
+ };
14
+ },
15
+ }), { mimeType: 'text/yaml' }, async (uri, { ruleId }) => {
16
+ const id = Array.isArray(ruleId) ? ruleId[0] : ruleId;
17
+ const rule = await client.getRule(id);
18
+ return {
19
+ contents: [
20
+ {
21
+ uri: uri.href,
22
+ text: rule.content,
23
+ },
24
+ ],
25
+ };
26
+ });
27
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { CapivvClient } from '../client.js';
3
+ export declare function registerStatusResource(server: McpServer, client: CapivvClient): void;
@@ -0,0 +1,43 @@
1
+ export function registerStatusResource(server, client) {
2
+ server.resource('status', 'capivv://status', { mimeType: 'application/json' }, async (uri) => {
3
+ const [apps, products, entitlements, offerings, rules, onboarding, analytics] = await Promise.allSettled([
4
+ client.listApps(),
5
+ client.listProducts(),
6
+ client.listEntitlements(),
7
+ client.listOfferings(),
8
+ client.listRules(),
9
+ client.getOnboarding(),
10
+ client.getAnalyticsOverview(),
11
+ ]);
12
+ const count = (r) => r.status === 'fulfilled' ? r.value.length : 0;
13
+ const result = {
14
+ resources: {
15
+ apps: count(apps),
16
+ products: count(products),
17
+ entitlements: count(entitlements),
18
+ offerings: count(offerings),
19
+ rules: count(rules),
20
+ },
21
+ };
22
+ if (onboarding.status === 'fulfilled') {
23
+ const ob = onboarding.value;
24
+ result.onboarding = {
25
+ is_complete: ob.is_complete,
26
+ completed_steps: ob.completed_steps,
27
+ total_steps: ob.total_steps,
28
+ next_step: ob.next_step,
29
+ };
30
+ }
31
+ if (analytics.status === 'fulfilled') {
32
+ const a = analytics.value;
33
+ result.analytics = {
34
+ mrr: a.mrr_formatted,
35
+ arr: a.arr_formatted,
36
+ active_subscriptions: a.active_subscriptions,
37
+ churn_rate: a.churn_rate,
38
+ arpu: a.arpu_formatted,
39
+ };
40
+ }
41
+ return { contents: [{ uri: uri.href, text: JSON.stringify(result, null, 2) }] };
42
+ });
43
+ }
@@ -0,0 +1,3 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { Config } from './config.js';
3
+ export declare function createServer(config: Config): McpServer;
package/dist/server.js ADDED
@@ -0,0 +1,11 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { CapivvClient } from './client.js';
3
+ import { registerAllTools } from './tools/index.js';
4
+ import { registerAllResources } from './resources/index.js';
5
+ export function createServer(config) {
6
+ const server = new McpServer({ name: 'capivv-mcp', version: '0.1.0' }, { capabilities: { logging: {} } });
7
+ const client = new CapivvClient(config);
8
+ registerAllTools(server, client);
9
+ registerAllResources(server, client);
10
+ return server;
11
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { CapivvClient } from '../client.js';
3
+ export declare function registerApplyRuleTool(server: McpServer, client: CapivvClient): void;
@@ -0,0 +1,37 @@
1
+ import { z } from 'zod';
2
+ export function registerApplyRuleTool(server, client) {
3
+ server.tool('capivv_apply_rule', 'Apply a YAML rule configuration. Validates the rule first, then creates or updates it. Returns validation errors if the YAML is invalid.', {
4
+ name: z.string().describe('Rule name'),
5
+ content: z.string().describe('YAML rule content'),
6
+ priority: z.number().optional().default(100).describe('Rule priority (lower = higher priority)'),
7
+ is_enabled: z.boolean().optional().default(true).describe('Whether the rule is active'),
8
+ }, async ({ name, content, priority, is_enabled }) => {
9
+ // Validate first
10
+ const validation = await client.validateRule(content);
11
+ if (!validation.is_valid) {
12
+ const result = {
13
+ success: false,
14
+ validation_errors: validation.errors,
15
+ validation_warnings: validation.warnings,
16
+ };
17
+ return {
18
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
19
+ isError: true,
20
+ };
21
+ }
22
+ // Create/update the rule
23
+ const rule = await client.upsertRule({ name, content, priority, is_enabled });
24
+ const result = {
25
+ success: true,
26
+ rule: {
27
+ id: rule.id,
28
+ name: rule.name,
29
+ priority: rule.priority,
30
+ is_enabled: rule.is_enabled,
31
+ updated_at: rule.updated_at,
32
+ },
33
+ validation_warnings: validation.warnings.length > 0 ? validation.warnings : undefined,
34
+ };
35
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
36
+ });
37
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { CapivvClient } from '../client.js';
3
+ export declare function registerCreateOfferingTool(server: McpServer, client: CapivvClient): void;
@@ -0,0 +1,24 @@
1
+ import { z } from 'zod';
2
+ export function registerCreateOfferingTool(server, client) {
3
+ server.tool('capivv_create_offering', 'Create a new offering to bundle products for your paywall. Specify products and their display order.', {
4
+ app_id: z.string().describe('App ID this offering belongs to'),
5
+ identifier: z.string().describe('Unique identifier (e.g., "default", "premium_monthly")'),
6
+ display_name: z.string().describe('Human-readable name shown in the dashboard'),
7
+ description: z.string().optional().describe('Optional description'),
8
+ is_default: z.boolean().optional().describe('Whether this is the default offering'),
9
+ packages: z
10
+ .array(z.object({
11
+ identifier: z.string().describe('Package identifier (e.g., "$rc_monthly")'),
12
+ product_id: z.string().describe('Product ID to include'),
13
+ display_name: z.string().describe('Display name for this package'),
14
+ package_type: z
15
+ .string()
16
+ .describe('One of: monthly, annual, weekly, lifetime, custom'),
17
+ position: z.number().describe('Display order (0-based)'),
18
+ }))
19
+ .describe('Products to include in this offering'),
20
+ }, async (args) => {
21
+ const offering = await client.createOffering(args);
22
+ return { content: [{ type: 'text', text: JSON.stringify(offering, null, 2) }] };
23
+ });
24
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { CapivvClient } from '../client.js';
3
+ export declare function registerGetAnalyticsTool(server: McpServer, client: CapivvClient): void;
@@ -0,0 +1,29 @@
1
+ export function registerGetAnalyticsTool(server, client) {
2
+ server.tool('capivv_get_analytics', 'Get key subscription business metrics: MRR, ARR, active subscriptions, churn rate, ARPU, and period-over-period comparison.', async () => {
3
+ const overview = await client.getAnalyticsOverview();
4
+ const result = {
5
+ revenue: {
6
+ mrr: overview.mrr_formatted,
7
+ arr: overview.arr_formatted,
8
+ arpu: overview.arpu_formatted,
9
+ },
10
+ subscriptions: {
11
+ active: overview.active_subscriptions,
12
+ trialing: overview.trialing_subscriptions,
13
+ churn_rate: `${overview.churn_rate}%`,
14
+ },
15
+ };
16
+ if (overview.period_comparison) {
17
+ result.period_comparison = {
18
+ mrr_change: `${overview.period_comparison.mrr_change_percent >= 0 ? '+' : ''}${overview.period_comparison.mrr_change_percent}%`,
19
+ subscriptions_change: `${overview.period_comparison.subscriptions_change_percent >= 0 ? '+' : ''}${overview.period_comparison.subscriptions_change_percent}%`,
20
+ };
21
+ }
22
+ // E.4.3: Explain zero metrics and suggest next steps
23
+ if (overview.active_subscriptions === 0 && overview.trialing_subscriptions === 0) {
24
+ result._suggestion =
25
+ 'No subscription data yet. This is normal for new setups. To start seeing metrics: 1) Create products and offerings, 2) Integrate the SDK in your app, 3) Make a test purchase in sandbox mode.';
26
+ }
27
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
28
+ });
29
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { CapivvClient } from '../client.js';
3
+ export declare function registerImportProductsTool(server: McpServer, client: CapivvClient): void;