@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.
- package/README.md +73 -2
- package/dist/client.d.ts +15 -3
- package/dist/client.js +45 -5
- package/dist/config.js +1 -1
- package/dist/http.d.ts +12 -0
- package/dist/http.js +102 -0
- package/dist/resources/guides.d.ts +2 -0
- package/dist/resources/guides.js +81 -0
- package/dist/resources/index.js +4 -0
- package/dist/resources/quickstart.d.ts +2 -0
- package/dist/resources/quickstart.js +173 -0
- package/dist/resources/rules.js +8 -2
- package/dist/tools/activate-rule.d.ts +3 -0
- package/dist/tools/activate-rule.js +9 -0
- package/dist/tools/api-key-usage.d.ts +3 -0
- package/dist/tools/api-key-usage.js +70 -0
- package/dist/tools/apply-rule.js +54 -13
- package/dist/tools/autopilot-status.d.ts +3 -0
- package/dist/tools/autopilot-status.js +117 -0
- package/dist/tools/create-app.d.ts +3 -0
- package/dist/tools/create-app.js +13 -0
- package/dist/tools/create-entitlement.d.ts +3 -0
- package/dist/tools/create-entitlement.js +11 -0
- package/dist/tools/create-product.d.ts +3 -0
- package/dist/tools/create-product.js +42 -0
- package/dist/tools/delete-product.d.ts +3 -0
- package/dist/tools/delete-product.js +9 -0
- package/dist/tools/delete-rule.d.ts +3 -0
- package/dist/tools/delete-rule.js +9 -0
- package/dist/tools/import-products.js +7 -5
- package/dist/tools/index.js +40 -1
- package/dist/tools/list-entitlements.d.ts +3 -0
- package/dist/tools/list-entitlements.js +6 -0
- package/dist/tools/list-rules.js +4 -2
- package/dist/tools/next-step.d.ts +3 -0
- package/dist/tools/next-step.js +123 -0
- package/dist/tools/setup-wizard.d.ts +3 -0
- package/dist/tools/setup-wizard.js +259 -0
- package/dist/tools/status.js +25 -1
- package/dist/tools/update-product.d.ts +3 -0
- package/dist/tools/update-product.js +16 -0
- package/dist/tools/verify-setup.d.ts +3 -0
- package/dist/tools/verify-setup.js +200 -0
- package/dist/tools/whoami.d.ts +3 -0
- package/dist/tools/whoami.js +31 -0
- package/dist/types.d.ts +115 -79
- package/dist/types.js +0 -2
- package/mcp.json +89 -0
- 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://
|
|
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
|
-
|
|
25
|
-
validateRule(
|
|
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
|
-
|
|
57
|
-
|
|
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
|
|
98
|
+
async createRule(data) {
|
|
91
99
|
return this.post('/dashboard/rules', data);
|
|
92
100
|
}
|
|
93
|
-
async validateRule(
|
|
94
|
-
return this.post('/dashboard/rules/validate',
|
|
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://
|
|
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,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
|
+
}
|
package/dist/resources/index.js
CHANGED
|
@@ -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,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
|
+
}
|
package/dist/resources/rules.js
CHANGED
|
@@ -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.
|
|
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:
|
|
28
|
+
text: yamlContent,
|
|
23
29
|
},
|
|
24
30
|
],
|
|
25
31
|
};
|
|
@@ -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
|
+
}
|