@capivv/mcp-server 0.5.4 → 0.5.6
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 +4 -1
- package/dist/client.js +11 -0
- package/dist/tools/create-product.js +8 -0
- package/dist/tools/get-subscription-state.d.ts +3 -0
- package/dist/tools/get-subscription-state.js +15 -0
- package/dist/tools/index.js +12 -0
- package/dist/tools/set-subscription-review-screenshot.d.ts +3 -0
- package/dist/tools/set-subscription-review-screenshot.js +47 -0
- package/dist/tools/touch-subscription.d.ts +3 -0
- package/dist/tools/touch-subscription.js +17 -0
- package/dist/types.d.ts +34 -0
- package/package.json +1 -1
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, CreateAppRequest, CreateEntitlementRequest, CreateProductRequest, CreateOfferingRequest, CreateRuleRequest, ValidateRuleRequest, ValidateRuleResponse, WhoamiResponse, ApiKeyUsageResponse, Paywall, CreatePaywallRequest, UpdatePaywallRequest, PaywallStats, Promotion, CreatePromotionRequest, UpdatePromotionRequest, RescueFlow, CreateRescueFlowRequest, UpdateRescueFlowRequest, RescueStats, PricingStrategy, CreatePricingStrategyRequest, PricingPreviewResult, PricingRecomputeRequest, PricingRecomputeResult, PricingPushResult, PricingChangeRequest, SetCountryPriceOverrideRequest, ExperimentWithVariants, CreateExperimentRequest, UpdateExperimentRequest, UpdateAppRequest, UpdateEntitlementRequest, UpdateOfferingRequest, AutopilotRunResult, SyncSuggestion, SyncSuggestionsCount, TriggerSyncResult, IntegrationSummary, ConnectAppleIntegrationRequest, ConnectAppleIntegrationResult, ConnectGoogleIntegrationRequest, ConnectGoogleIntegrationResult } 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, Paywall, CreatePaywallRequest, UpdatePaywallRequest, PaywallStats, Promotion, CreatePromotionRequest, UpdatePromotionRequest, RescueFlow, CreateRescueFlowRequest, UpdateRescueFlowRequest, RescueStats, PricingStrategy, CreatePricingStrategyRequest, PricingPreviewResult, PricingRecomputeRequest, PricingRecomputeResult, PricingPushResult, PricingChangeRequest, SetCountryPriceOverrideRequest, ExperimentWithVariants, CreateExperimentRequest, UpdateExperimentRequest, UpdateAppRequest, UpdateEntitlementRequest, UpdateOfferingRequest, AutopilotRunResult, SyncSuggestion, SyncSuggestionsCount, TriggerSyncResult, IntegrationSummary, ConnectAppleIntegrationRequest, ConnectAppleIntegrationResult, ConnectGoogleIntegrationRequest, ConnectGoogleIntegrationResult, SetSubscriptionReviewScreenshotRequest, SetSubscriptionReviewScreenshotResult, SubscriptionStateInfo, TouchSubscriptionResult } from './types.js';
|
|
3
3
|
export declare class ApiError extends Error {
|
|
4
4
|
status: number;
|
|
5
5
|
code: string;
|
|
@@ -91,4 +91,7 @@ export declare class CapivvClient {
|
|
|
91
91
|
connectAppleIntegration(data: ConnectAppleIntegrationRequest): Promise<ConnectAppleIntegrationResult>;
|
|
92
92
|
connectGoogleIntegration(data: ConnectGoogleIntegrationRequest): Promise<ConnectGoogleIntegrationResult>;
|
|
93
93
|
disconnectIntegration(provider: string): Promise<void>;
|
|
94
|
+
setSubscriptionReviewScreenshot(data: SetSubscriptionReviewScreenshotRequest): Promise<SetSubscriptionReviewScreenshotResult>;
|
|
95
|
+
getSubscriptionState(appleSubscriptionId: string): Promise<SubscriptionStateInfo>;
|
|
96
|
+
touchSubscription(appleSubscriptionId: string): Promise<TouchSubscriptionResult>;
|
|
94
97
|
}
|
package/dist/client.js
CHANGED
|
@@ -339,4 +339,15 @@ export class CapivvClient {
|
|
|
339
339
|
async disconnectIntegration(provider) {
|
|
340
340
|
await this.post(`/dashboard/integrations/${encodeURIComponent(provider)}/disconnect`);
|
|
341
341
|
}
|
|
342
|
+
// ---- Subscription review screenshot (V0.5.5) ----
|
|
343
|
+
async setSubscriptionReviewScreenshot(data) {
|
|
344
|
+
const { apple_subscription_id, ...body } = data;
|
|
345
|
+
return this.post(`/dashboard/subscriptions/${encodeURIComponent(apple_subscription_id)}/review-screenshot`, body);
|
|
346
|
+
}
|
|
347
|
+
async getSubscriptionState(appleSubscriptionId) {
|
|
348
|
+
return this.get(`/dashboard/subscriptions/${encodeURIComponent(appleSubscriptionId)}/state`);
|
|
349
|
+
}
|
|
350
|
+
async touchSubscription(appleSubscriptionId) {
|
|
351
|
+
return this.post(`/dashboard/subscriptions/${encodeURIComponent(appleSubscriptionId)}/touch`);
|
|
352
|
+
}
|
|
342
353
|
}
|
|
@@ -7,6 +7,8 @@ export function registerCreateProductTool(server, client) {
|
|
|
7
7
|
'Pass `skip_store_write: true` to opt out of the store-side write — only useful when migrating an existing store-side product into Capivv (rare).',
|
|
8
8
|
'',
|
|
9
9
|
'On success the response includes a `store_create` block with the store-side product ID and snapped prices. On store failure the Capivv-side record is rolled back and the verbatim store error is returned.',
|
|
10
|
+
'',
|
|
11
|
+
'KNOWN APPLE ISSUE — auto-renewing subscriptions: After this call succeeds, Apple may keep the subscription at MISSING_METADATA even when all metadata is uploaded. This is a known divergence between Apple\'s modern /v1/subscriptions API and its legacy /v1/inAppPurchases mirror. After uploading the review screenshot via capivv_set_subscription_review_screenshot, poll capivv_get_subscription_state — if state stays at MISSING_METADATA for more than a few minutes, call capivv_touch_subscription (best-effort) or have the customer click Save in the App Store Connect web UI.',
|
|
10
12
|
].join(' '), {
|
|
11
13
|
app_id: z.string().describe('App ID this product belongs to'),
|
|
12
14
|
external_id: z.string().describe('Store product ID (e.g., "com.example.pro_monthly")'),
|
|
@@ -69,6 +71,12 @@ function formatStoreCreateSummary(product) {
|
|
|
69
71
|
lines.push(` ${sp.currency}: used ${formatCents(sp.actual_cents, sp.currency)} — closest store tier to your ${formatCents(sp.requested_cents, sp.currency)} request.`);
|
|
70
72
|
}
|
|
71
73
|
}
|
|
74
|
+
if (sc.truncations && sc.truncations.length > 0) {
|
|
75
|
+
lines.push('Note: Apple length caps on subscription localization clipped the following fields — re-running with shorter values is fine but the existing values above are what was posted.');
|
|
76
|
+
for (const t of sc.truncations) {
|
|
77
|
+
lines.push(` ${t.locale} ${t.field}: shortened from ${t.original_chars} to ${t.truncated_chars} chars (Apple max ${t.limit}).`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
72
80
|
return lines.join('\n');
|
|
73
81
|
}
|
|
74
82
|
function formatCents(cents, currency) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerGetSubscriptionStateTool(server, client) {
|
|
3
|
+
server.tool('capivv_get_subscription_state', [
|
|
4
|
+
'Read the current Apple-side state of a subscription. Returns name, productId, and Apple\'s lifecycle state (MISSING_METADATA / READY_TO_SUBMIT / WAITING_FOR_REVIEW / IN_REVIEW / APPROVED / REJECTED / etc).',
|
|
5
|
+
'',
|
|
6
|
+
'Use this to verify a subscription is actually ready after capivv_create_product + capivv_set_subscription_review_screenshot. Apple\'s modern /v1/subscriptions and legacy /v1/inAppPurchases views can diverge — a subscription with all metadata + screenshot uploaded sometimes stays at MISSING_METADATA because the legacy mirror lags. Poll this every 30-60 seconds; if state doesn\'t progress within a few minutes, call capivv_touch_subscription or click Save in App Store Connect\'s web UI.',
|
|
7
|
+
].join(' '), {
|
|
8
|
+
apple_subscription_id: z
|
|
9
|
+
.string()
|
|
10
|
+
.describe('Apple subscription ID (e.g. "6764778053") — the store-side ID, not the Capivv UUID.'),
|
|
11
|
+
}, async ({ apple_subscription_id }) => {
|
|
12
|
+
const info = await client.getSubscriptionState(apple_subscription_id);
|
|
13
|
+
return { content: [{ type: 'text', text: JSON.stringify(info, null, 2) }] };
|
|
14
|
+
});
|
|
15
|
+
}
|
package/dist/tools/index.js
CHANGED
|
@@ -72,6 +72,9 @@ import { registerListIntegrationsTool } from './list-integrations.js';
|
|
|
72
72
|
import { registerConnectAppleIntegrationTool } from './connect-apple-integration.js';
|
|
73
73
|
import { registerConnectGoogleIntegrationTool } from './connect-google-integration.js';
|
|
74
74
|
import { registerDisconnectIntegrationTool } from './disconnect-integration.js';
|
|
75
|
+
import { registerSetSubscriptionReviewScreenshotTool } from './set-subscription-review-screenshot.js';
|
|
76
|
+
import { registerGetSubscriptionStateTool } from './get-subscription-state.js';
|
|
77
|
+
import { registerTouchSubscriptionTool } from './touch-subscription.js';
|
|
75
78
|
export function registerAllTools(server, client) {
|
|
76
79
|
// Identity — call this first to verify which workspace you're connected to
|
|
77
80
|
registerWhoamiTool(server, client);
|
|
@@ -173,4 +176,13 @@ export function registerAllTools(server, client) {
|
|
|
173
176
|
registerConnectAppleIntegrationTool(server, client);
|
|
174
177
|
registerConnectGoogleIntegrationTool(server, client);
|
|
175
178
|
registerDisconnectIntegrationTool(server, client);
|
|
179
|
+
// Subscription review screenshot (v0.5.5 — Apple's 3-step S3 upload flow
|
|
180
|
+
// wrapped in a single MCP call so the agent can take a subscription from
|
|
181
|
+
// MISSING_METADATA to READY_FOR_REVIEW in one step.)
|
|
182
|
+
registerSetSubscriptionReviewScreenshotTool(server, client);
|
|
183
|
+
// Subscription state probe + touch (v0.5.6 — workaround for Apple's
|
|
184
|
+
// modern-vs-legacy view divergence keeping subscriptions in
|
|
185
|
+
// MISSING_METADATA after a successful create.)
|
|
186
|
+
registerGetSubscriptionStateTool(server, client);
|
|
187
|
+
registerTouchSubscriptionTool(server, client);
|
|
176
188
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerSetSubscriptionReviewScreenshotTool(server, client) {
|
|
3
|
+
server.tool('capivv_set_subscription_review_screenshot', [
|
|
4
|
+
'Upload the App Store review screenshot for a subscription. Apple requires this before an auto-renewing subscription can leave MISSING_METADATA and be submitted for review.',
|
|
5
|
+
'',
|
|
6
|
+
'Apple expects 640x920 (or larger) PNG / JPEG. Provide either `image_url` (Capivv backend fetches it via HTTPS) or `image_base64` (raw bytes inline). The backend handles the 3-step Apple flow: reserve → PUT bytes to S3 → PATCH uploaded=true with MD5.',
|
|
7
|
+
'',
|
|
8
|
+
'Get `apple_subscription_id` from the `store_create` block of capivv_create_product, from capivv_check_drift, or from App Store Connect.',
|
|
9
|
+
].join(' '), {
|
|
10
|
+
apple_subscription_id: z
|
|
11
|
+
.string()
|
|
12
|
+
.describe('Apple subscription ID (e.g. "6764778053") — NOT the Capivv product UUID.'),
|
|
13
|
+
image_url: z
|
|
14
|
+
.string()
|
|
15
|
+
.optional()
|
|
16
|
+
.describe('Public HTTPS URL of the screenshot. Mutually exclusive with image_base64.'),
|
|
17
|
+
image_base64: z
|
|
18
|
+
.string()
|
|
19
|
+
.optional()
|
|
20
|
+
.describe('Base64-encoded raw image bytes. Use for non-HTTPS sources. Mutually exclusive with image_url.'),
|
|
21
|
+
file_name: z
|
|
22
|
+
.string()
|
|
23
|
+
.optional()
|
|
24
|
+
.describe('Filename Apple stores on the screenshot resource. Defaults to "screenshot.png".'),
|
|
25
|
+
}, async (args) => {
|
|
26
|
+
if (!args.image_url && !args.image_base64) {
|
|
27
|
+
return {
|
|
28
|
+
isError: true,
|
|
29
|
+
content: [
|
|
30
|
+
{
|
|
31
|
+
type: 'text',
|
|
32
|
+
text: 'Provide either image_url (HTTPS) or image_base64 (raw bytes).',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const result = await client.setSubscriptionReviewScreenshot(args);
|
|
38
|
+
return {
|
|
39
|
+
content: [
|
|
40
|
+
{
|
|
41
|
+
type: 'text',
|
|
42
|
+
text: JSON.stringify(result, null, 2),
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerTouchSubscriptionTool(server, client) {
|
|
3
|
+
server.tool('capivv_touch_subscription', [
|
|
4
|
+
'BEST-EFFORT workaround for Apple\'s modern-vs-legacy view divergence.',
|
|
5
|
+
'',
|
|
6
|
+
'After capivv_create_product creates the subscription via Apple\'s /v1/subscriptions endpoint, a legacy /v1/inAppPurchases mirror is also created with its own state machine. The legacy record can stay at WAITING_FOR_SCREENSHOT (which keeps the modern view at MISSING_METADATA) even after the screenshot uploads cleanly, because Apple\'s state recompute pipeline still reads from the legacy view and the modern API doesn\'t reliably trigger that recompute.',
|
|
7
|
+
'',
|
|
8
|
+
'This tool does a no-op PATCH on the subscription (re-set name to its current value), which mirrors what clicking "Save" in App Store Connect\'s web UI appears to trigger. Whether it actually fires the recompute hasn\'t been confirmed in every state — call capivv_get_subscription_state 30-60 seconds afterwards to check. If state still doesn\'t progress, fall back to manual ASC web UI Save.',
|
|
9
|
+
].join(' '), {
|
|
10
|
+
apple_subscription_id: z
|
|
11
|
+
.string()
|
|
12
|
+
.describe('Apple subscription ID (e.g. "6764778053") — the store-side ID, not the Capivv UUID.'),
|
|
13
|
+
}, async ({ apple_subscription_id }) => {
|
|
14
|
+
const result = await client.touchSubscription(apple_subscription_id);
|
|
15
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
16
|
+
});
|
|
17
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -68,6 +68,15 @@ export interface StoreCreateSummary {
|
|
|
68
68
|
actual_cents: number;
|
|
69
69
|
price_point_id?: string;
|
|
70
70
|
}>;
|
|
71
|
+
/** Fields the orchestrator shortened to fit Apple's per-field length cap.
|
|
72
|
+
* Empty / undefined when nothing was clipped. */
|
|
73
|
+
truncations?: Array<{
|
|
74
|
+
field: string;
|
|
75
|
+
original_chars: number;
|
|
76
|
+
truncated_chars: number;
|
|
77
|
+
limit: number;
|
|
78
|
+
locale: string;
|
|
79
|
+
}>;
|
|
71
80
|
}
|
|
72
81
|
export interface Entitlement {
|
|
73
82
|
id: string;
|
|
@@ -554,3 +563,28 @@ export interface ConnectGoogleIntegrationResult {
|
|
|
554
563
|
error: string;
|
|
555
564
|
}>;
|
|
556
565
|
}
|
|
566
|
+
export interface SetSubscriptionReviewScreenshotRequest {
|
|
567
|
+
apple_subscription_id: string;
|
|
568
|
+
/** Public HTTPS URL the backend fetches. Either this or image_base64 is required. */
|
|
569
|
+
image_url?: string;
|
|
570
|
+
/** Base64-encoded raw image bytes. Either this or image_url is required. */
|
|
571
|
+
image_base64?: string;
|
|
572
|
+
/** Filename Apple records (defaults to screenshot.png). */
|
|
573
|
+
file_name?: string;
|
|
574
|
+
}
|
|
575
|
+
export interface SetSubscriptionReviewScreenshotResult {
|
|
576
|
+
apple_subscription_id: string;
|
|
577
|
+
screenshot_id: string;
|
|
578
|
+
bytes_uploaded: number;
|
|
579
|
+
}
|
|
580
|
+
export interface SubscriptionStateInfo {
|
|
581
|
+
apple_subscription_id: string;
|
|
582
|
+
product_id: string;
|
|
583
|
+
name: string | null;
|
|
584
|
+
state: string | null;
|
|
585
|
+
}
|
|
586
|
+
export interface TouchSubscriptionResult {
|
|
587
|
+
apple_subscription_id: string;
|
|
588
|
+
touched: boolean;
|
|
589
|
+
next_action: string;
|
|
590
|
+
}
|