@capivv/mcp-server 0.5.9 → 0.5.11

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 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, SetSubscriptionReviewScreenshotRequest, SetSubscriptionReviewScreenshotResult, SubscriptionStateInfo, TouchSubscriptionResult } 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, ProbeSubmissionRequest, ProbeSubmissionResult, SetAppPrivacyUrlRequest, SetAppPrivacyUrlResult, SetIapReviewScreenshotRequest, SetIapReviewScreenshotResult, IapStateInfo } from './types.js';
3
3
  export declare class ApiError extends Error {
4
4
  status: number;
5
5
  code: string;
@@ -94,4 +94,8 @@ export declare class CapivvClient {
94
94
  setSubscriptionReviewScreenshot(data: SetSubscriptionReviewScreenshotRequest): Promise<SetSubscriptionReviewScreenshotResult>;
95
95
  getSubscriptionState(appleSubscriptionId: string): Promise<SubscriptionStateInfo>;
96
96
  touchSubscription(appleSubscriptionId: string): Promise<TouchSubscriptionResult>;
97
+ probeAppleSubmission(req: ProbeSubmissionRequest): Promise<ProbeSubmissionResult>;
98
+ setAppPrivacyUrl(req: SetAppPrivacyUrlRequest): Promise<SetAppPrivacyUrlResult>;
99
+ setIapReviewScreenshot(req: SetIapReviewScreenshotRequest): Promise<SetIapReviewScreenshotResult>;
100
+ getIapState(appleIapId: string): Promise<IapStateInfo>;
97
101
  }
package/dist/client.js CHANGED
@@ -350,4 +350,19 @@ export class CapivvClient {
350
350
  async touchSubscription(appleSubscriptionId) {
351
351
  return this.post(`/dashboard/subscriptions/${encodeURIComponent(appleSubscriptionId)}/touch`);
352
352
  }
353
+ // ---- V0.5.10 — Submission probe / privacy URL / IAP screenshot ----
354
+ async probeAppleSubmission(req) {
355
+ return this.post(`/dashboard/store/apple/${encodeURIComponent(req.product_type)}/${encodeURIComponent(req.apple_product_id)}/probe-submission`);
356
+ }
357
+ async setAppPrivacyUrl(req) {
358
+ const { app_id, ...body } = req;
359
+ return this.post(`/dashboard/apps/${encodeURIComponent(app_id)}/privacy-policy-url`, body);
360
+ }
361
+ async setIapReviewScreenshot(req) {
362
+ const { apple_iap_id, ...body } = req;
363
+ return this.post(`/dashboard/iap/${encodeURIComponent(apple_iap_id)}/review-screenshot`, body);
364
+ }
365
+ async getIapState(appleIapId) {
366
+ return this.get(`/dashboard/iap/${encodeURIComponent(appleIapId)}/state`);
367
+ }
353
368
  }
@@ -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 registerGetIapStateTool(server: McpServer, client: CapivvClient): void;
@@ -0,0 +1,17 @@
1
+ import { z } from 'zod';
2
+ export function registerGetIapStateTool(server, client) {
3
+ server.tool('capivv_get_iap_state', [
4
+ "Read an Apple in-app purchase's lifecycle state from App Store Connect. Mirror of capivv_get_subscription_state for IAPs (consumable / non-consumable).",
5
+ '',
6
+ 'Returns the IAP\'s current `state` (MISSING_METADATA, READY_TO_SUBMIT, WAITING_FOR_REVIEW, IN_REVIEW, APPROVED, etc.), the productId, name, and whether it\'s CONSUMABLE or NON_CONSUMABLE. Useful after capivv_create_product / capivv_set_iap_review_screenshot to confirm the IAP is no longer stuck in MISSING_METADATA.',
7
+ '',
8
+ 'Get `apple_iap_id` from the `store_create.store_product_id` field of capivv_create_product or from App Store Connect.',
9
+ ].join(' '), {
10
+ apple_iap_id: z
11
+ .string()
12
+ .describe('Apple in-app purchase ID (e.g. "6765812345") — NOT the Capivv product UUID.'),
13
+ }, async ({ apple_iap_id }) => {
14
+ const result = await client.getIapState(apple_iap_id);
15
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
16
+ });
17
+ }
@@ -75,6 +75,10 @@ import { registerDisconnectIntegrationTool } from './disconnect-integration.js';
75
75
  import { registerSetSubscriptionReviewScreenshotTool } from './set-subscription-review-screenshot.js';
76
76
  import { registerGetSubscriptionStateTool } from './get-subscription-state.js';
77
77
  import { registerTouchSubscriptionTool } from './touch-subscription.js';
78
+ import { registerProbeSubmissionTool } from './probe-submission.js';
79
+ import { registerSetAppPrivacyUrlTool } from './set-app-privacy-url.js';
80
+ import { registerSetIapReviewScreenshotTool } from './set-iap-review-screenshot.js';
81
+ import { registerGetIapStateTool } from './get-iap-state.js';
78
82
  export function registerAllTools(server, client) {
79
83
  // Identity — call this first to verify which workspace you're connected to
80
84
  registerWhoamiTool(server, client);
@@ -185,4 +189,16 @@ export function registerAllTools(server, client) {
185
189
  // MISSING_METADATA after a successful create.)
186
190
  registerGetSubscriptionStateTool(server, client);
187
191
  registerTouchSubscriptionTool(server, client);
192
+ // V0.5.10 — diagnostic + app-info + IAP screenshot tools.
193
+ // - Submission probe surfaces Apple's exact "what's still missing"
194
+ // diagnostics so the agent stops surfacing misleading "Permission
195
+ // denied" errors.
196
+ // - App privacy URL fixes ENTITY_ERROR.APP_MISSING_PRIVACY_POLICY_URL
197
+ // without sending the user to ASC.
198
+ // - IAP review screenshot mirrors the subscription helper for IAPs.
199
+ registerProbeSubmissionTool(server, client);
200
+ registerSetAppPrivacyUrlTool(server, client);
201
+ registerSetIapReviewScreenshotTool(server, client);
202
+ // V0.5.11 — IAP state probe (mirror of get_subscription_state).
203
+ registerGetIapStateTool(server, client);
188
204
  }
@@ -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 registerProbeSubmissionTool(server: McpServer, client: CapivvClient): void;
@@ -0,0 +1,32 @@
1
+ import { z } from 'zod';
2
+ export function registerProbeSubmissionTool(server, client) {
3
+ server.tool('capivv_probe_submission', [
4
+ 'Probe an Apple subscription or in-app purchase for what is still gating App Store submission.',
5
+ '',
6
+ "Apple's `*Submissions` endpoint reliably returns 409 on a brand-new product with an `errors` body that says exactly what's missing. This tool POSTs to `/v1/subscriptionSubmissions` (or `/v1/inAppPurchaseSubmissions`) and returns a structured result:",
7
+ '- `ready`: submission accepted (rare on first probe; means metadata is complete).',
8
+ '- `missing_pricing_data` with a `territories` list: re-run pricing for those territories.',
9
+ '- `missing_privacy_policy_url`: call `capivv_set_app_privacy_url` to fix.',
10
+ '- `awaiting_first_app_version`: Apple policy — first sub/IAP on a new app must ship with the first version. Not an error from your side.',
11
+ '- `other` with `code` + `detail`: surface verbatim to the customer.',
12
+ '',
13
+ 'Get `apple_product_id` from the `store_create.store_product_id` field of capivv_create_product, or from App Store Connect.',
14
+ ].join(' '), {
15
+ apple_product_id: z
16
+ .string()
17
+ .describe('Apple product ID (e.g. "6764778053") — NOT the Capivv product UUID.'),
18
+ product_type: z
19
+ .enum(['subscription', 'iap'])
20
+ .describe('"subscription" for auto-renewing subscriptions; "iap" for consumables and non-consumables.'),
21
+ }, async (args) => {
22
+ const result = await client.probeAppleSubmission(args);
23
+ return {
24
+ content: [
25
+ {
26
+ type: 'text',
27
+ text: JSON.stringify(result, null, 2),
28
+ },
29
+ ],
30
+ };
31
+ });
32
+ }
@@ -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 registerSetAppPrivacyUrlTool(server: McpServer, client: CapivvClient): void;
@@ -0,0 +1,25 @@
1
+ import { z } from 'zod';
2
+ export function registerSetAppPrivacyUrlTool(server, client) {
3
+ server.tool('capivv_set_app_privacy_url', [
4
+ "Set the App Store Connect app's privacy policy URL on its in-progress en-US localization.",
5
+ '',
6
+ 'Call this when `capivv_probe_submission` returns `missing_privacy_policy_url`. The backend resolves the app via Capivv `app_id` → bundle id → ASC app, then PATCHes `appInfoLocalizations.privacyPolicyUrl` on the editable appInfo (states: PREPARE_FOR_SUBMISSION, REJECTED, METADATA_REJECTED, DEVELOPER_REJECTED).',
7
+ '',
8
+ 'URL must be HTTPS.',
9
+ ].join(' '), {
10
+ app_id: z.string().describe('Capivv app UUID.'),
11
+ privacy_policy_url: z
12
+ .string()
13
+ .describe('HTTPS URL pointing at the privacy policy.'),
14
+ }, async (args) => {
15
+ const result = await client.setAppPrivacyUrl(args);
16
+ return {
17
+ content: [
18
+ {
19
+ type: 'text',
20
+ text: JSON.stringify(result, null, 2),
21
+ },
22
+ ],
23
+ };
24
+ });
25
+ }
@@ -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 registerSetIapReviewScreenshotTool(server: McpServer, client: CapivvClient): void;
@@ -0,0 +1,47 @@
1
+ import { z } from 'zod';
2
+ export function registerSetIapReviewScreenshotTool(server, client) {
3
+ server.tool('capivv_set_iap_review_screenshot', [
4
+ 'Upload the App Store review screenshot for an in-app purchase (consumable or non-consumable). Mirrors capivv_set_subscription_review_screenshot but uses the IAP endpoint.',
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_iap_id` from the `store_create.store_product_id` field of capivv_create_product (when the product type is consumable or non_consumable), or from App Store Connect.',
9
+ ].join(' '), {
10
+ apple_iap_id: z
11
+ .string()
12
+ .describe('Apple in-app purchase ID (e.g. "6765812345") — 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.setIapReviewScreenshot(args);
38
+ return {
39
+ content: [
40
+ {
41
+ type: 'text',
42
+ text: JSON.stringify(result, null, 2),
43
+ },
44
+ ],
45
+ };
46
+ });
47
+ }
@@ -1,11 +1,11 @@
1
1
  import { z } from 'zod';
2
2
  export function registerTouchSubscriptionTool(server, client) {
3
3
  server.tool('capivv_touch_subscription', [
4
- 'BEST-EFFORT workaround for Apple\'s modern-vs-legacy view divergence.',
4
+ 'Nudge Apple to recompute the subscription\'s state after the screenshot has uploaded.',
5
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.',
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) until something writes to the modern resource. This tool does a no-op PATCH on the subscription (re-set name to its current value), which mirrors clicking "Save" in App Store Connect\'s web UI and reliably triggers the recompute.',
7
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.',
8
+ 'Call capivv_get_subscription_state 30-60 seconds afterwards to confirm the state has progressed.',
9
9
  ].join(' '), {
10
10
  apple_subscription_id: z
11
11
  .string()
package/dist/types.d.ts CHANGED
@@ -600,3 +600,63 @@ export interface TouchSubscriptionResult {
600
600
  touched: boolean;
601
601
  next_action: string;
602
602
  }
603
+ /**
604
+ * Result of POSTing a `*Submissions` record to App Store Connect. Apple
605
+ * reliably 409s on a brand-new product with an `errors` array describing
606
+ * exactly what's still missing — so we map known codes to actionable
607
+ * variants. The backend returns this discriminated union as JSON.
608
+ */
609
+ export type SubmissionProbeResult = {
610
+ status: 'ready';
611
+ } | {
612
+ status: 'missing_pricing_data';
613
+ territories: string[];
614
+ } | {
615
+ status: 'missing_privacy_policy_url';
616
+ } | {
617
+ status: 'awaiting_first_app_version';
618
+ } | {
619
+ status: 'other';
620
+ code: string;
621
+ detail: string;
622
+ };
623
+ export interface ProbeSubmissionRequest {
624
+ apple_product_id: string;
625
+ /** "subscription" or "iap". */
626
+ product_type: 'subscription' | 'iap';
627
+ }
628
+ export interface ProbeSubmissionResult {
629
+ apple_product_id: string;
630
+ product_type: string;
631
+ submission_status: SubmissionProbeResult;
632
+ }
633
+ export interface SetAppPrivacyUrlRequest {
634
+ app_id: string;
635
+ privacy_policy_url: string;
636
+ }
637
+ export interface SetAppPrivacyUrlResult {
638
+ app_id: string;
639
+ bundle_id: string;
640
+ privacy_policy_url: string;
641
+ localization_id: string;
642
+ app_store_state: string;
643
+ }
644
+ export interface SetIapReviewScreenshotRequest {
645
+ apple_iap_id: string;
646
+ image_url?: string;
647
+ image_base64?: string;
648
+ file_name?: string;
649
+ }
650
+ export interface SetIapReviewScreenshotResult {
651
+ apple_iap_id: string;
652
+ screenshot_id: string;
653
+ bytes_uploaded: number;
654
+ }
655
+ export interface IapStateInfo {
656
+ apple_iap_id: string;
657
+ product_id: string;
658
+ name: string | null;
659
+ state: string | null;
660
+ /** "CONSUMABLE" / "NON_CONSUMABLE". */
661
+ in_app_purchase_type: string | null;
662
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capivv/mcp-server",
3
- "version": "0.5.9",
3
+ "version": "0.5.11",
4
4
  "description": "MCP server for managing Capivv subscription platform via AI assistants",
5
5
  "type": "module",
6
6
  "bin": {