@capivv/mcp-server 0.1.3 → 0.2.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 (49) hide show
  1. package/README.md +73 -2
  2. package/dist/client.d.ts +15 -3
  3. package/dist/client.js +45 -5
  4. package/dist/config.js +1 -1
  5. package/dist/http.d.ts +12 -0
  6. package/dist/http.js +102 -0
  7. package/dist/resources/guides.d.ts +2 -0
  8. package/dist/resources/guides.js +81 -0
  9. package/dist/resources/index.js +4 -0
  10. package/dist/resources/quickstart.d.ts +2 -0
  11. package/dist/resources/quickstart.js +173 -0
  12. package/dist/resources/rules.js +8 -2
  13. package/dist/tools/activate-rule.d.ts +3 -0
  14. package/dist/tools/activate-rule.js +9 -0
  15. package/dist/tools/api-key-usage.d.ts +3 -0
  16. package/dist/tools/api-key-usage.js +70 -0
  17. package/dist/tools/apply-rule.js +54 -13
  18. package/dist/tools/autopilot-status.d.ts +3 -0
  19. package/dist/tools/autopilot-status.js +117 -0
  20. package/dist/tools/create-app.d.ts +3 -0
  21. package/dist/tools/create-app.js +13 -0
  22. package/dist/tools/create-entitlement.d.ts +3 -0
  23. package/dist/tools/create-entitlement.js +11 -0
  24. package/dist/tools/create-product.d.ts +3 -0
  25. package/dist/tools/create-product.js +42 -0
  26. package/dist/tools/delete-product.d.ts +3 -0
  27. package/dist/tools/delete-product.js +9 -0
  28. package/dist/tools/delete-rule.d.ts +3 -0
  29. package/dist/tools/delete-rule.js +9 -0
  30. package/dist/tools/import-products.js +7 -5
  31. package/dist/tools/index.js +40 -1
  32. package/dist/tools/list-entitlements.d.ts +3 -0
  33. package/dist/tools/list-entitlements.js +6 -0
  34. package/dist/tools/list-rules.js +4 -2
  35. package/dist/tools/next-step.d.ts +3 -0
  36. package/dist/tools/next-step.js +123 -0
  37. package/dist/tools/setup-wizard.d.ts +3 -0
  38. package/dist/tools/setup-wizard.js +259 -0
  39. package/dist/tools/status.js +25 -1
  40. package/dist/tools/update-product.d.ts +3 -0
  41. package/dist/tools/update-product.js +16 -0
  42. package/dist/tools/verify-setup.d.ts +3 -0
  43. package/dist/tools/verify-setup.js +200 -0
  44. package/dist/tools/whoami.d.ts +3 -0
  45. package/dist/tools/whoami.js +31 -0
  46. package/dist/types.d.ts +115 -79
  47. package/dist/types.js +0 -2
  48. package/mcp.json +89 -0
  49. package/package.json +8 -2
package/README.md CHANGED
@@ -20,7 +20,7 @@ The server requires an API key from your Capivv dashboard.
20
20
  | Environment Variable | Required | Description |
21
21
  |---------------------|----------|-------------|
22
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`) |
23
+ | `CAPIVV_API_URL` | No | API base URL (default: `https://app.capivv.com`) |
24
24
  | `CAPIVV_ORG_ID` | No | Organization ID for multi-org accounts |
25
25
 
26
26
  ## Setup
@@ -85,25 +85,96 @@ The server uses stdio transport. Start it with:
85
85
  CAPIVV_API_KEY=your-api-key npx @capivv/mcp-server
86
86
  ```
87
87
 
88
+ ## Getting Started
89
+
90
+ After installing, ask your AI assistant:
91
+
92
+ > "Set up Capivv for my iOS app com.mycompany.myapp with a monthly ($7.99) and annual ($49.99) subscription"
93
+
94
+ The assistant will use `capivv_setup_wizard` to create everything in one shot. Or go step-by-step:
95
+
96
+ 1. **`capivv_next_step`** — tells you exactly what to do next based on your current setup
97
+ 2. **`capivv_setup_wizard`** — creates app + entitlement + products + offering in one call
98
+ 3. **`capivv_verify_setup`** — checks everything is configured correctly
99
+
100
+ Read the `capivv://docs/quickstart` resource for a full walkthrough.
101
+
88
102
  ## Available Tools
89
103
 
104
+ ### Identity
105
+
106
+ | Tool | Description |
107
+ |------|-------------|
108
+ | `capivv_whoami` | Show which workspace (org) and apps your API key reaches. Call this first if anything looks wrong |
109
+
110
+ ### Onboarding & Guidance
111
+
112
+ | Tool | Description |
113
+ |------|-------------|
114
+ | `capivv_next_step` | Analyze your setup and get exact instructions for the next step |
115
+ | `capivv_setup_wizard` | One-shot setup: app + entitlement + products + offering in one call |
116
+ | `capivv_verify_setup` | Pass/fail checklist verifying your entire setup with fix instructions |
117
+
118
+ ### Status & Analytics
119
+
90
120
  | Tool | Description |
91
121
  |------|-------------|
92
122
  | `capivv_status` | Get setup progress, resource counts, and key metrics |
123
+ | `capivv_get_analytics` | Get MRR, ARR, churn rate, ARPU, and period comparison |
124
+
125
+ ### Apps
126
+
127
+ | Tool | Description |
128
+ |------|-------------|
93
129
  | `capivv_list_apps` | List all apps with platform and bundle ID |
130
+ | `capivv_create_app` | Register a new app (iOS, Android, or Web) |
131
+
132
+ ### Entitlements
133
+
134
+ | Tool | Description |
135
+ |------|-------------|
136
+ | `capivv_list_entitlements` | List all entitlements (feature access identifiers) |
137
+ | `capivv_create_entitlement` | Create a new entitlement |
138
+
139
+ ### Products
140
+
141
+ | Tool | Description |
142
+ |------|-------------|
94
143
  | `capivv_list_products` | List products with store IDs and entitlements (optional `app_id` filter) |
144
+ | `capivv_create_product` | Create a subscription, consumable, or non-consumable product |
145
+ | `capivv_update_product` | Update product fields (name, entitlements, external ID) |
146
+ | `capivv_delete_product` | Delete a product (irreversible) |
95
147
  | `capivv_import_products` | Preview store product import from App Store Connect / Google Play |
148
+
149
+ ### Offerings
150
+
151
+ | Tool | Description |
152
+ |------|-------------|
96
153
  | `capivv_list_offerings` | List offerings with packages and products |
97
154
  | `capivv_create_offering` | Create a new offering with packages |
155
+
156
+ ### Rules
157
+
158
+ | Tool | Description |
159
+ |------|-------------|
98
160
  | `capivv_list_rules` | List YAML business rules with status and priority |
99
161
  | `capivv_apply_rule` | Validate and apply a YAML rule configuration |
162
+ | `capivv_activate_rule` | Activate an existing rule |
163
+ | `capivv_delete_rule` | Delete a rule (irreversible) |
164
+
165
+ ### Experiments
166
+
167
+ | Tool | Description |
168
+ |------|-------------|
100
169
  | `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
170
 
103
171
  ## Available Resources
104
172
 
105
173
  | URI | Description |
106
174
  |-----|-------------|
175
+ | `capivv://docs/quickstart` | Step-by-step setup guide — **start here** |
176
+ | `capivv://docs/guides/ios` | Complete iOS integration guide with code examples |
177
+ | `capivv://docs/guides/ai-prompts` | Ready-to-use prompts for AI code generators (Lovable, Bolt, Cursor) |
107
178
  | `capivv://status` | Current platform status as JSON |
108
179
  | `capivv://rules/{ruleId}` | Individual rule YAML content |
109
180
  | `capivv://docs/concepts` | Glossary of subscription platform concepts |
package/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
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';
2
+ import type { App, Product, Entitlement, Offering, Rule, Experiment, ExperimentSummary, AnalyticsOverview, OnboardingResponse, ImportPreviewResponse, CreateAppRequest, CreateEntitlementRequest, CreateProductRequest, CreateOfferingRequest, CreateRuleRequest, ValidateRuleRequest, ValidateRuleResponse, WhoamiResponse, ApiKeyUsageResponse } from './types.js';
3
3
  export declare class ApiError extends Error {
4
4
  status: number;
5
5
  code: string;
@@ -14,6 +14,8 @@ export declare class CapivvClient {
14
14
  private get;
15
15
  private post;
16
16
  healthCheck(): Promise<boolean>;
17
+ whoami(): Promise<WhoamiResponse>;
18
+ getApiKeyUsage(apiKeyId: string, hours?: number): Promise<ApiKeyUsageResponse>;
17
19
  listApps(): Promise<App[]>;
18
20
  listProducts(appId?: string): Promise<Product[]>;
19
21
  listEntitlements(): Promise<Entitlement[]>;
@@ -21,11 +23,21 @@ export declare class CapivvClient {
21
23
  createOffering(data: CreateOfferingRequest): Promise<Offering>;
22
24
  listRules(): Promise<Rule[]>;
23
25
  getRule(id: string): Promise<Rule>;
24
- upsertRule(data: CreateRuleRequest): Promise<Rule>;
25
- validateRule(content: string): Promise<ValidateRuleResponse>;
26
+ createRule(data: CreateRuleRequest): Promise<Rule>;
27
+ validateRule(data: ValidateRuleRequest): Promise<ValidateRuleResponse>;
26
28
  listExperiments(): Promise<Experiment[]>;
27
29
  getExperimentSummaries(): Promise<ExperimentSummary[]>;
28
30
  getAnalyticsOverview(): Promise<AnalyticsOverview>;
29
31
  getOnboarding(): Promise<OnboardingResponse>;
30
32
  getImportPreview(appId: string): Promise<ImportPreviewResponse>;
33
+ createApp(data: CreateAppRequest): Promise<App>;
34
+ createEntitlement(data: CreateEntitlementRequest): Promise<Entitlement>;
35
+ createProduct(data: CreateProductRequest): Promise<Product>;
36
+ private patch;
37
+ private delete;
38
+ updateProduct(id: string, data: Partial<Omit<CreateProductRequest, 'app_id'>>): Promise<Product>;
39
+ deleteProduct(id: string): Promise<void>;
40
+ deleteRule(id: string): Promise<void>;
41
+ activateRule(id: string): Promise<Rule>;
42
+ deactivateRule(id: string): Promise<Rule>;
31
43
  }
package/dist/client.js CHANGED
@@ -53,13 +53,21 @@ export class CapivvClient {
53
53
  }
54
54
  async healthCheck() {
55
55
  try {
56
- await this.get('/health');
57
- return true;
56
+ // Health endpoint is at root, not under /v1
57
+ const url = this.baseUrl.replace(/\/v1$/, '') + '/health';
58
+ const response = await fetch(url);
59
+ return response.ok;
58
60
  }
59
61
  catch {
60
62
  return false;
61
63
  }
62
64
  }
65
+ async whoami() {
66
+ return this.get('/dashboard/whoami');
67
+ }
68
+ async getApiKeyUsage(apiKeyId, hours = 24) {
69
+ return this.get(`/dashboard/settings/api-keys/${encodeURIComponent(apiKeyId)}/usage?hours=${hours}`);
70
+ }
63
71
  async listApps() {
64
72
  const resp = await this.get('/dashboard/apps');
65
73
  return resp.apps;
@@ -87,11 +95,11 @@ export class CapivvClient {
87
95
  async getRule(id) {
88
96
  return this.get(`/dashboard/rules/${encodeURIComponent(id)}`);
89
97
  }
90
- async upsertRule(data) {
98
+ async createRule(data) {
91
99
  return this.post('/dashboard/rules', data);
92
100
  }
93
- async validateRule(content) {
94
- return this.post('/dashboard/rules/validate', { content });
101
+ async validateRule(data) {
102
+ return this.post('/dashboard/rules/validate', data);
95
103
  }
96
104
  async listExperiments() {
97
105
  const resp = await this.get('/dashboard/experiments/active');
@@ -110,4 +118,36 @@ export class CapivvClient {
110
118
  async getImportPreview(appId) {
111
119
  return this.get(`/dashboard/onboarding/import-preview?app_id=${encodeURIComponent(appId)}`);
112
120
  }
121
+ // ---- Create methods ----
122
+ async createApp(data) {
123
+ return this.post('/dashboard/apps', data);
124
+ }
125
+ async createEntitlement(data) {
126
+ return this.post('/dashboard/entitlements', data);
127
+ }
128
+ async createProduct(data) {
129
+ return this.post('/dashboard/products', data);
130
+ }
131
+ // ---- Update methods ----
132
+ patch(path, body) {
133
+ return this.request('PATCH', path, body);
134
+ }
135
+ delete(path) {
136
+ return this.request('DELETE', path);
137
+ }
138
+ async updateProduct(id, data) {
139
+ return this.patch(`/dashboard/products/${encodeURIComponent(id)}`, data);
140
+ }
141
+ async deleteProduct(id) {
142
+ await this.delete(`/dashboard/products/${encodeURIComponent(id)}`);
143
+ }
144
+ async deleteRule(id) {
145
+ await this.delete(`/dashboard/rules/${encodeURIComponent(id)}`);
146
+ }
147
+ async activateRule(id) {
148
+ return this.post(`/dashboard/rules/${encodeURIComponent(id)}/activate`);
149
+ }
150
+ async deactivateRule(id) {
151
+ return this.post(`/dashboard/rules/${encodeURIComponent(id)}/deactivate`);
152
+ }
113
153
  }
package/dist/config.js CHANGED
@@ -10,7 +10,7 @@ export function loadConfig() {
10
10
  throw new ConfigError('CAPIVV_API_KEY environment variable is required. ' +
11
11
  'Create an API key in the Capivv dashboard under Settings > Developer > API Keys.');
12
12
  }
13
- const apiUrl = (process.env.CAPIVV_API_URL || 'https://api.capivv.com').replace(/\/+$/, '');
13
+ const apiUrl = (process.env.CAPIVV_API_URL || 'https://app.capivv.com').replace(/\/+$/, '');
14
14
  const orgId = process.env.CAPIVV_ORG_ID || undefined;
15
15
  return { apiKey, apiUrl, orgId };
16
16
  }
package/dist/http.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MCP Remote HTTP Server
4
+ *
5
+ * Exposes the Capivv MCP server over Streamable HTTP transport.
6
+ * Clients authenticate with their Capivv API key as a Bearer token.
7
+ *
8
+ * Usage in Lovable / other MCP clients:
9
+ * Server URL: https://app.capivv.com/mcp
10
+ * Auth: Bearer token → paste your Capivv secret API key
11
+ */
12
+ export {};
package/dist/http.js ADDED
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MCP Remote HTTP Server
4
+ *
5
+ * Exposes the Capivv MCP server over Streamable HTTP transport.
6
+ * Clients authenticate with their Capivv API key as a Bearer token.
7
+ *
8
+ * Usage in Lovable / other MCP clients:
9
+ * Server URL: https://app.capivv.com/mcp
10
+ * Auth: Bearer token → paste your Capivv secret API key
11
+ */
12
+ import express from 'express';
13
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
14
+ import { createServer } from './server.js';
15
+ const PORT = parseInt(process.env.MCP_PORT || '3100', 10);
16
+ const HOST = process.env.MCP_HOST || '0.0.0.0';
17
+ const API_URL = (process.env.CAPIVV_API_URL || 'https://app.capivv.com').replace(/\/+$/, '');
18
+ const app = express();
19
+ app.use(express.json());
20
+ // Health check
21
+ app.get('/health', (_req, res) => {
22
+ res.json({ status: 'ok', service: 'capivv-mcp' });
23
+ });
24
+ // Public origin used in the WWW-Authenticate header so OAuth-aware MCP clients
25
+ // (Cursor, Claude Desktop) can discover our authorization server. Falls back to
26
+ // CAPIVV_API_URL because in production the MCP and API share the same hostname
27
+ // behind Caddy.
28
+ const PUBLIC_URL = (process.env.CAPIVV_PUBLIC_URL || API_URL).replace(/\/+$/, '');
29
+ function unauthorized(res, description) {
30
+ // RFC 9728: point the client at the protected-resource metadata so it can
31
+ // discover our authorization server and start the OAuth flow.
32
+ res.setHeader('WWW-Authenticate', `Bearer realm="capivv-mcp", error="invalid_token", error_description="${description}", resource_metadata="${PUBLIC_URL}/.well-known/oauth-protected-resource"`);
33
+ res.status(401).json({
34
+ jsonrpc: '2.0',
35
+ error: { code: -32000, message: description },
36
+ id: null,
37
+ });
38
+ }
39
+ // MCP endpoint — Streamable HTTP
40
+ app.post('/mcp', async (req, res) => {
41
+ try {
42
+ // Extract API key from Authorization header
43
+ const authHeader = req.headers.authorization;
44
+ if (!authHeader) {
45
+ unauthorized(res, 'Missing Authorization header. Sign in via your MCP client or set Bearer <your-capivv-api-key>.');
46
+ return;
47
+ }
48
+ const apiKey = authHeader.startsWith('Bearer ')
49
+ ? authHeader.slice(7).trim()
50
+ : authHeader.trim();
51
+ if (!apiKey) {
52
+ unauthorized(res, 'Empty bearer token.');
53
+ return;
54
+ }
55
+ // Create a config using the client's API key
56
+ const config = {
57
+ apiKey,
58
+ apiUrl: API_URL,
59
+ orgId: undefined,
60
+ };
61
+ // Create a fresh server + transport per request (stateless)
62
+ const server = createServer(config);
63
+ const transport = new StreamableHTTPServerTransport({
64
+ sessionIdGenerator: undefined, // stateless — no session tracking
65
+ });
66
+ await server.connect(transport);
67
+ await transport.handleRequest(req, res, req.body);
68
+ }
69
+ catch (error) {
70
+ console.error('[mcp-http] Error handling request:', error);
71
+ if (!res.headersSent) {
72
+ res.status(500).json({
73
+ jsonrpc: '2.0',
74
+ error: { code: -32603, message: 'Internal server error' },
75
+ id: null,
76
+ });
77
+ }
78
+ }
79
+ });
80
+ // Handle GET for SSE streams (server-initiated messages)
81
+ app.get('/mcp', async (_req, res) => {
82
+ // In stateless mode, we don't support server-initiated SSE
83
+ res.status(405).json({
84
+ jsonrpc: '2.0',
85
+ error: { code: -32000, message: 'Server-initiated SSE not supported in stateless mode. Use POST.' },
86
+ id: null,
87
+ });
88
+ });
89
+ // Handle DELETE for session termination
90
+ app.delete('/mcp', async (_req, res) => {
91
+ // Stateless — no sessions to terminate
92
+ res.status(405).json({
93
+ jsonrpc: '2.0',
94
+ error: { code: -32000, message: 'No sessions to terminate (stateless server).' },
95
+ id: null,
96
+ });
97
+ });
98
+ app.listen(PORT, HOST, () => {
99
+ console.log(`[capivv-mcp] HTTP server listening on http://${HOST}:${PORT}/mcp`);
100
+ console.log(`[capivv-mcp] API URL: ${API_URL}`);
101
+ console.log(`[capivv-mcp] Auth: Bearer token (use your Capivv API key)`);
102
+ });
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerGuideResources(server: McpServer): void;
@@ -0,0 +1,81 @@
1
+ import { readFile } from 'fs/promises';
2
+ import { resolve, dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ // Resolve path to the docs/ directory relative to the package root
5
+ function docsDir() {
6
+ // packages/mcp-server/src/resources/guides.ts → packages/mcp-server/
7
+ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
8
+ // packages/mcp-server/ → project root docs/
9
+ return resolve(packageRoot, '..', '..', 'docs');
10
+ }
11
+ const GUIDES = [
12
+ {
13
+ name: 'guide-ios',
14
+ uri: 'capivv://docs/guides/ios',
15
+ file: 'IOS_INTEGRATION_GUIDE.md',
16
+ description: 'Step-by-step iOS integration guide: App Store Connect setup, SDK installation, purchases, entitlements, testing, and troubleshooting.',
17
+ },
18
+ {
19
+ name: 'guide-ai-prompts',
20
+ uri: 'capivv://docs/guides/ai-prompts',
21
+ file: 'AI_INTEGRATION_PROMPTS.md',
22
+ description: 'Ready-to-use prompts for AI code generators (Lovable, Bolt, Cursor) to integrate Capivv on iOS, Android, Flutter, Web, and Ionic.',
23
+ },
24
+ ];
25
+ // Fallback content if the file can't be read (e.g., when running from npm package without docs/)
26
+ const FALLBACK = {
27
+ 'IOS_INTEGRATION_GUIDE.md': `# iOS Integration Guide
28
+
29
+ This guide is available at https://docs.capivv.com/guides/ios
30
+
31
+ ## Quick Steps
32
+
33
+ 1. **App Store Connect**: Create subscription products, set pricing, enable Server Notifications V2 pointing to \`https://app.capivv.com/v1/webhooks/apple\`
34
+ 2. **Capivv Dashboard**: Create app (iOS, your bundle ID), create entitlements (e.g., "pro"), create products matching your store IDs, link products to entitlements
35
+ 3. **Install SDK**: Add \`https://github.com/capivv/capivv-ios\` via Swift Package Manager
36
+ 4. **Initialize**: Call \`Capivv.shared.configure(apiKey: "capivv_pk_...")\` in your App.init()
37
+ 5. **Identify users**: Call \`Capivv.shared.login(userId: "...")\` after authentication
38
+ 6. **Check entitlements**: Use \`Capivv.shared.hasEntitlement("pro")\` to gate features
39
+ 7. **Show paywall**: Use \`PaywallView(identifier: "default")\` or build a custom one with \`Capivv.shared.getOfferings()\`
40
+ 8. **Test**: Use StoreKit Configuration files in Xcode, sandbox Apple IDs
41
+
42
+ For the full guide with code examples, read capivv://docs/quickstart or visit https://docs.capivv.com/guides/ios`,
43
+ 'AI_INTEGRATION_PROMPTS.md': `# AI Integration Prompts
44
+
45
+ Ready-to-use prompts for AI code generators are available at https://docs.capivv.com/guides/ai-prompts
46
+
47
+ Supported platforms: iOS (Swift), Android (Kotlin), Flutter, Web (React), Ionic/Capacitor
48
+
49
+ For each platform, the prompt covers:
50
+ 1. SDK installation
51
+ 2. Initialization with API key
52
+ 3. User identification
53
+ 4. Entitlement checking
54
+ 5. Paywall display
55
+ 6. Purchase processing
56
+ 7. Sync on launch
57
+
58
+ Visit https://docs.capivv.com/guides/ai-prompts for the full prompts.`,
59
+ };
60
+ export function registerGuideResources(server) {
61
+ for (const guide of GUIDES) {
62
+ server.resource(guide.name, guide.uri, { mimeType: 'text/markdown' }, async (uri) => {
63
+ let content;
64
+ try {
65
+ const filePath = resolve(docsDir(), guide.file);
66
+ content = await readFile(filePath, 'utf-8');
67
+ }
68
+ catch {
69
+ content = FALLBACK[guide.file] ?? `Guide not found. Visit https://docs.capivv.com for documentation.`;
70
+ }
71
+ return {
72
+ contents: [
73
+ {
74
+ uri: uri.href,
75
+ text: content,
76
+ },
77
+ ],
78
+ };
79
+ });
80
+ }
81
+ }
@@ -1,8 +1,12 @@
1
1
  import { registerStatusResource } from './status.js';
2
2
  import { registerRulesResource } from './rules.js';
3
3
  import { registerConceptsResource } from './concepts.js';
4
+ import { registerQuickstartResource } from './quickstart.js';
5
+ import { registerGuideResources } from './guides.js';
4
6
  export function registerAllResources(server, client) {
5
7
  registerStatusResource(server, client);
6
8
  registerRulesResource(server, client);
7
9
  registerConceptsResource(server);
10
+ registerQuickstartResource(server);
11
+ registerGuideResources(server);
8
12
  }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerQuickstartResource(server: McpServer): void;
@@ -0,0 +1,173 @@
1
+ const QUICKSTART_GUIDE = `# Capivv Quickstart Guide
2
+
3
+ Get your app's subscriptions working in 5 steps. Follow them **in order** — each step depends on the previous one.
4
+
5
+ ---
6
+
7
+ ## Step 1: Register Your App
8
+
9
+ Every app needs to be registered in Capivv with its platform and bundle ID.
10
+
11
+ **Tool:** \`capivv_create_app\`
12
+ **Example:**
13
+ - name: "My App"
14
+ - platform: "ios" (or "android", "web")
15
+ - bundle_id: "com.yourcompany.yourapp"
16
+
17
+ **Save the returned \`id\`** — you'll need it for products and offerings.
18
+
19
+ ---
20
+
21
+ ## Step 2: Create Entitlements
22
+
23
+ Entitlements define **what features** users unlock when they purchase. Think of them as feature flags tied to purchases.
24
+
25
+ **Tool:** \`capivv_create_entitlement\`
26
+ **Example:**
27
+ - identifier: "pro"
28
+ - display_name: "Pro Access"
29
+ - description: "Unlocks all premium features"
30
+
31
+ Most apps need just one entitlement (e.g., "pro" or "premium"). Create more if you have tiered access.
32
+
33
+ **Save the returned \`id\`** — you'll link it to products next.
34
+
35
+ ---
36
+
37
+ ## Step 3: Create Products
38
+
39
+ Products are the actual purchasable items (subscriptions, one-time purchases). They map 1:1 to your App Store / Google Play products.
40
+
41
+ **Tool:** \`capivv_create_product\`
42
+ **Example (monthly subscription):**
43
+ - app_id: (from Step 1)
44
+ - external_id: "com.yourcompany.yourapp.monthly" (must match your store product ID)
45
+ - product_type: "subscription"
46
+ - display_name: "Monthly Plan"
47
+ - entitlement_ids: [(entitlement ID from Step 2)]
48
+ - subscription: { billing_period: "month" }
49
+ - prices: [{ currency: "USD", amount_cents: 799, is_default: true }]
50
+
51
+ **Example (annual subscription):**
52
+ - app_id: (from Step 1)
53
+ - external_id: "com.yourcompany.yourapp.yearly"
54
+ - product_type: "subscription"
55
+ - display_name: "Annual Plan"
56
+ - entitlement_ids: [(entitlement ID from Step 2)]
57
+ - subscription: { billing_period: "year" }
58
+ - prices: [{ currency: "USD", amount_cents: 4999, is_default: true }]
59
+
60
+ **Important:** The \`external_id\` must exactly match the product ID in App Store Connect / Google Play Console.
61
+
62
+ ---
63
+
64
+ ## Step 4: Create an Offering
65
+
66
+ An offering bundles your products into a group that your paywall displays. This is what your SDK fetches to show purchase options.
67
+
68
+ **Tool:** \`capivv_create_offering\`
69
+ **Example:**
70
+ - app_id: (from Step 1)
71
+ - identifier: "default" (or a custom name like "pro_plans")
72
+ - display_name: "Pro Plans"
73
+ - is_default: true
74
+ - packages:
75
+ - { identifier: "monthly", product_id: (monthly product ID), display_name: "Monthly", package_type: "monthly", position: 0 }
76
+ - { identifier: "annual", product_id: (annual product ID), display_name: "Annual", package_type: "annual", position: 1 }
77
+
78
+ ---
79
+
80
+ ## Step 5: Integrate the SDK
81
+
82
+ With your Capivv backend configured, integrate the SDK into your app:
83
+
84
+ ### Get Your API Key
85
+ Go to the **Capivv dashboard → Settings → Developer → API Keys** and copy your **public key** (starts with \`capivv_pk_\`).
86
+
87
+ ### iOS (Swift)
88
+ \`\`\`swift
89
+ import Capivv
90
+
91
+ // In your App init or AppDelegate
92
+ Capivv.configure(apiKey: "capivv_pk_...")
93
+
94
+ // Identify the user
95
+ Capivv.shared.login(userId: "user_123")
96
+
97
+ // Check entitlements
98
+ let hasAccess = try await Capivv.shared.checkEntitlement("pro")
99
+
100
+ // Fetch offerings for your paywall
101
+ let offerings = try await Capivv.shared.getOfferings()
102
+ \`\`\`
103
+
104
+ ### Web (JavaScript/TypeScript)
105
+ \`\`\`typescript
106
+ import { Capivv } from '@capivv/web-sdk';
107
+
108
+ const capivv = new Capivv({ apiKey: 'capivv_pk_...' });
109
+ await capivv.login('user_123');
110
+
111
+ const hasAccess = await capivv.checkEntitlement('pro');
112
+ const offerings = await capivv.getOfferings();
113
+ \`\`\`
114
+
115
+ ---
116
+
117
+ ## Verify Your Setup
118
+
119
+ Run \`capivv_status\` to confirm everything is configured. You should see:
120
+ - 1+ apps
121
+ - 1+ entitlements
122
+ - 1+ products
123
+ - 1+ offerings
124
+
125
+ Or use \`capivv_verify_setup\` for a detailed checklist of what's ready and what's missing.
126
+
127
+ ---
128
+
129
+ ## What About Pricing in Other Countries?
130
+
131
+ You do **not** hardcode international prices. Here's how it works:
132
+ 1. Set your **base USD price** when creating products in App Store Connect / Google Play Console
133
+ 2. Apple and Google automatically calculate equivalent prices for other regions (you can override per-country in each console)
134
+ 3. The Capivv SDK returns **localized price strings** from the store — your paywall shows those, not your env file values
135
+
136
+ The prices in Capivv are for **analytics and reference only**. The store is the source of truth for what users actually pay.
137
+
138
+ ---
139
+
140
+ ## Common Patterns
141
+
142
+ ### Trial Offer
143
+ Use \`capivv_apply_rule\` to create a rule that grants a free trial:
144
+ - condition: \`never_subscribed\`
145
+ - action: offer a discounted or free introductory period
146
+
147
+ ### Multiple Tiers (Free / Pro / Business)
148
+ Create separate entitlements for each tier, then create products that grant the appropriate entitlement.
149
+
150
+ ### Churn Recovery
151
+ Use rules with the \`churned_subscriber\` condition to show special rescue offers to users who cancelled.
152
+
153
+ ---
154
+
155
+ ## Need Help?
156
+
157
+ - **Check setup status:** \`capivv_status\` or \`capivv_next_step\`
158
+ - **Verify everything works:** \`capivv_verify_setup\`
159
+ - **Browse concepts:** Read the \`capivv://docs/concepts\` resource
160
+ - **Platform guides:** Read \`capivv://docs/guides/ios\`, \`capivv://docs/guides/android\`, or \`capivv://docs/guides/web\`
161
+ `;
162
+ export function registerQuickstartResource(server) {
163
+ server.resource('quickstart', 'capivv://docs/quickstart', { mimeType: 'text/markdown' }, async (uri) => {
164
+ return {
165
+ contents: [
166
+ {
167
+ uri: uri.href,
168
+ text: QUICKSTART_GUIDE,
169
+ },
170
+ ],
171
+ };
172
+ });
173
+ }
@@ -1,4 +1,5 @@
1
1
  import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import YAML from 'yaml';
2
3
  export function registerRulesResource(server, client) {
3
4
  server.resource('rule', new ResourceTemplate('capivv://rules/{ruleId}', {
4
5
  list: async () => {
@@ -7,7 +8,7 @@ export function registerRulesResource(server, client) {
7
8
  resources: rules.map((r) => ({
8
9
  uri: `capivv://rules/${r.id}`,
9
10
  name: r.name,
10
- description: `Rule: ${r.name} (priority: ${r.priority}, ${r.is_enabled ? 'enabled' : 'disabled'})`,
11
+ description: `Rule: ${r.name} (priority: ${r.priority}, ${r.status})`,
11
12
  mimeType: 'text/yaml',
12
13
  })),
13
14
  };
@@ -15,11 +16,16 @@ export function registerRulesResource(server, client) {
15
16
  }), { mimeType: 'text/yaml' }, async (uri, { ruleId }) => {
16
17
  const id = Array.isArray(ruleId) ? ruleId[0] : ruleId;
17
18
  const rule = await client.getRule(id);
19
+ const yamlContent = YAML.stringify({
20
+ conditions: rule.conditions,
21
+ actions: rule.actions,
22
+ guardrails: rule.guardrails,
23
+ });
18
24
  return {
19
25
  contents: [
20
26
  {
21
27
  uri: uri.href,
22
- text: rule.content,
28
+ text: yamlContent,
23
29
  },
24
30
  ],
25
31
  };
@@ -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 registerActivateRuleTool(server: McpServer, client: CapivvClient): void;
@@ -0,0 +1,9 @@
1
+ import { z } from 'zod';
2
+ export function registerActivateRuleTool(server, client) {
3
+ server.tool('capivv_activate_rule', 'Activate a business rule so it starts being evaluated for users.', {
4
+ rule_id: z.string().describe('Rule ID to activate'),
5
+ }, async ({ rule_id }) => {
6
+ const rule = await client.activateRule(rule_id);
7
+ return { content: [{ type: 'text', text: JSON.stringify(rule, null, 2) }] };
8
+ });
9
+ }