@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
@@ -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 registerApiKeyUsageTool(server: McpServer, client: CapivvClient): void;
@@ -0,0 +1,70 @@
1
+ import { z } from 'zod';
2
+ // V7 Phase C.5 — AI-callable SDK usage telemetry.
3
+ //
4
+ // Same backend endpoint the dashboard Usage dialog uses
5
+ // (/dashboard/settings/api-keys/:id/usage). The MCP surface exists because
6
+ // "is this key compromised?" is a question AI assistants are often asked —
7
+ // they can fetch the data, render it inline, and compare against the
8
+ // anomaly-detection rules documented in the runbook without the user
9
+ // context-switching to the dashboard.
10
+ export function registerApiKeyUsageTool(server, client) {
11
+ server.tool('capivv_api_key_usage', `Fetch request-level telemetry for a specific publishable API key (pk_live_* / pk_test_*). Returns hourly buckets of request counts, error counts, distinct IPs, SDK versions, and country codes — the same data the dashboard's Usage tab renders. Use this to investigate a suspected key leak: look for sudden IP spread, unexpected country codes, or rate spikes vs. a prior baseline.`, {
12
+ api_key_id: z
13
+ .string()
14
+ .describe('The api_keys.id of the key to inspect (UUID). Use capivv_whoami to see available apps — key IDs appear under each app in the dashboard Settings → Developer page.'),
15
+ hours: z
16
+ .number()
17
+ .int()
18
+ .min(1)
19
+ .max(168)
20
+ .optional()
21
+ .describe('Window in hours to summarize over (default 24, max 168 = 7 days).'),
22
+ }, async ({ api_key_id, hours }) => {
23
+ const usage = await client.getApiKeyUsage(api_key_id, hours ?? 24);
24
+ const lines = [];
25
+ lines.push(`Usage for key ${api_key_id.slice(0, 8)}… over the last ${usage.hours} hour(s):`);
26
+ lines.push('');
27
+ lines.push(` Requests: ${usage.summary.total_requests.toLocaleString()}`);
28
+ lines.push(` Errors: ${usage.summary.total_errors.toLocaleString()}`);
29
+ lines.push(` Unique IPs: ${usage.summary.distinct_ips}`);
30
+ lines.push(` Countries: ${usage.summary.distinct_countries}`);
31
+ lines.push(` SDK versions: ${usage.summary.distinct_sdk_versions}`);
32
+ lines.push('');
33
+ // Anomaly signals inline — surface the same ones the hourly scanner
34
+ // checks in capivv-workers::api_key_anomaly. AI assistant can quote
35
+ // these directly when answering "is this key compromised?"
36
+ const signals = [];
37
+ if (usage.summary.distinct_ips >= 5) {
38
+ signals.push(` • ${usage.summary.distinct_ips} distinct IPs is high for a single app — investigate if this is expected.`);
39
+ }
40
+ if (usage.summary.distinct_countries >= 3) {
41
+ signals.push(` • Traffic from ${usage.summary.distinct_countries} countries. If your app isn't multi-region, consider revoking.`);
42
+ }
43
+ if (usage.summary.total_requests > 10_000 && usage.hours <= 24) {
44
+ signals.push(` • ${usage.summary.total_requests.toLocaleString()} requests in ${usage.hours}h crosses the 10k/hour absolute-volume threshold.`);
45
+ }
46
+ if (signals.length > 0) {
47
+ lines.push('Anomaly signals:');
48
+ lines.push(...signals);
49
+ lines.push('');
50
+ }
51
+ if (usage.samples.length === 0) {
52
+ lines.push('No traffic recorded in the selected window.');
53
+ }
54
+ else {
55
+ lines.push(`Sample buckets (showing up to 10 of ${usage.samples.length}):`);
56
+ for (const s of usage.samples.slice(0, 10)) {
57
+ const ip = s.client_ip ?? 'unknown';
58
+ const sdk = s.sdk_version ?? 'unknown';
59
+ const country = s.country_code ?? '??';
60
+ lines.push(` ${s.bucket_start} ${s.request_count}req/${s.error_count}err ${ip} ${sdk} ${country}`);
61
+ }
62
+ }
63
+ return {
64
+ content: [
65
+ { type: 'text', text: lines.join('\n') },
66
+ { type: 'text', text: JSON.stringify(usage, null, 2) },
67
+ ],
68
+ };
69
+ });
70
+ }
@@ -1,33 +1,74 @@
1
1
  import { z } from 'zod';
2
+ import YAML from 'yaml';
2
3
  export function registerApplyRuleTool(server, client) {
3
- server.tool('capivv_apply_rule', 'Apply a YAML rule configuration. Validates the rule first, then creates or updates it. Returns validation errors if the YAML is invalid.', {
4
- name: z.string().describe('Rule name'),
5
- content: z.string().describe('YAML rule content'),
4
+ server.tool('capivv_apply_rule', `Apply a YAML rule configuration. Validates the rule first, then creates or updates it.
5
+
6
+ Example YAML:
7
+ conditions:
8
+ - type: never_subscribed
9
+ actions:
10
+ - type: grant_entitlement
11
+ identifier: "trial_access"
12
+ expires_in_days: 7
13
+ reason: "New user trial"
14
+
15
+ Condition types: audience, attribute, date_range, percentage, has_entitlement, has_product, subscription_status, renewal_window, is_trial_period, never_subscribed, churned_subscriber, country, platform, app_version
16
+ Action types: override_price, discount_percent, discount_fixed, grant_entitlement, set_paywall, show_offering, cancel_offer, track_event, set_attribute`, {
17
+ name: z.string().describe('Rule name (used as both identifier and display name)'),
18
+ content: z.string().describe('YAML rule content with conditions and actions arrays'),
6
19
  priority: z.number().optional().default(100).describe('Rule priority (lower = higher priority)'),
7
20
  is_enabled: z.boolean().optional().default(true).describe('Whether the rule is active'),
8
21
  }, async ({ name, content, priority, is_enabled }) => {
22
+ // Parse YAML to extract conditions, actions, guardrails
23
+ let parsed;
24
+ try {
25
+ parsed = YAML.parse(content);
26
+ if (!parsed || typeof parsed !== 'object') {
27
+ throw new Error('YAML must be an object with conditions and/or actions');
28
+ }
29
+ }
30
+ catch (e) {
31
+ return {
32
+ content: [{ type: 'text', text: JSON.stringify({
33
+ success: false,
34
+ error: `Invalid YAML: ${e instanceof Error ? e.message : String(e)}`,
35
+ }, null, 2) }],
36
+ isError: true,
37
+ };
38
+ }
39
+ const conditions = parsed.conditions ?? [];
40
+ const actions = parsed.actions ?? [];
41
+ const guardrails = parsed.guardrails;
9
42
  // Validate first
10
- const validation = await client.validateRule(content);
43
+ const validation = await client.validateRule({ conditions, actions, guardrails });
11
44
  if (!validation.is_valid) {
12
- const result = {
13
- success: false,
14
- validation_errors: validation.errors,
15
- validation_warnings: validation.warnings,
16
- };
17
45
  return {
18
- content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
46
+ content: [{ type: 'text', text: JSON.stringify({
47
+ success: false,
48
+ validation_errors: validation.errors,
49
+ validation_warnings: validation.warnings,
50
+ }, null, 2) }],
19
51
  isError: true,
20
52
  };
21
53
  }
22
- // Create/update the rule
23
- const rule = await client.upsertRule({ name, content, priority, is_enabled });
54
+ // Create the rule
55
+ const identifier = name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
56
+ const rule = await client.createRule({
57
+ identifier,
58
+ name,
59
+ conditions,
60
+ actions,
61
+ guardrails,
62
+ priority,
63
+ });
24
64
  const result = {
25
65
  success: true,
26
66
  rule: {
27
67
  id: rule.id,
68
+ identifier: rule.identifier,
28
69
  name: rule.name,
29
70
  priority: rule.priority,
30
- is_enabled: rule.is_enabled,
71
+ status: rule.status,
31
72
  updated_at: rule.updated_at,
32
73
  },
33
74
  validation_warnings: validation.warnings.length > 0 ? validation.warnings : undefined,
@@ -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 registerAutopilotStatusTool(server: McpServer, client: CapivvClient): void;
@@ -0,0 +1,117 @@
1
+ import { z } from 'zod';
2
+ // V7 B.5 — capivv_autopilot_status
3
+ //
4
+ // Answers "show me everything autopilot did for this app" from one MCP call.
5
+ // Composed from the existing listOfferings / listRules / listExperiments
6
+ // client methods plus the listApps lookup to resolve the app name — no new
7
+ // backend endpoint. Paywalls + rescue flows + promotions live in the same
8
+ // autopilot provenance story but don't have MCP list methods today; the tool
9
+ // surfaces a link to the dashboard pages where those badges also render.
10
+ export function registerAutopilotStatusTool(server, client) {
11
+ server.tool('capivv_autopilot_status', `Show everything autopilot generated for a specific app — offerings, entitlement rules, and experiments, filtered by the generated_by provenance marker. Useful right after capivv_setup_wizard or import-apps to confirm what was auto-created, or when a user asks "what did autopilot do for app X?"`, {
12
+ app_id: z
13
+ .string()
14
+ .describe('The Capivv app ID to inspect. Use capivv_list_apps to find it, or capivv_whoami to see all apps reachable from your API key.'),
15
+ }, async ({ app_id }) => {
16
+ const [apps, offerings, rules, experiments] = await Promise.all([
17
+ client.listApps(),
18
+ client.listOfferings(),
19
+ client.listRules(),
20
+ client.listExperiments(),
21
+ ]);
22
+ const app = apps.find((a) => a.id === app_id);
23
+ if (!app) {
24
+ return {
25
+ content: [
26
+ {
27
+ type: 'text',
28
+ text: `No app found with id ${app_id}. Available apps: ${apps.map((a) => `${a.name} (${a.id})`).join(', ') || 'none'}`,
29
+ },
30
+ ],
31
+ isError: true,
32
+ };
33
+ }
34
+ // generated_by uses an `autopilot_v` prefix across versions. Matching
35
+ // on the prefix lets the tool show v1 + v2 resources without hard-
36
+ // coding version strings that'll drift.
37
+ const autopilotStamp = (gb) => typeof gb === 'string' && gb.startsWith('autopilot_');
38
+ const autopilotOfferings = offerings.filter((o) => o.app_id === app_id && autopilotStamp(o.generated_by));
39
+ const autopilotRules = rules.filter((r) => {
40
+ if (!autopilotStamp(r.generated_by))
41
+ return false;
42
+ // Rules are tenant-scoped; we tagged the source app_id in metadata.
43
+ const md = r.metadata;
44
+ return md?.generated_for_app_id === app_id;
45
+ });
46
+ const autopilotExperiments = experiments.filter((e) => {
47
+ if (!autopilotStamp(e.generated_by))
48
+ return false;
49
+ const md = e.metadata;
50
+ return md?.generated_for_app_id === app_id;
51
+ });
52
+ const totals = {
53
+ offerings: autopilotOfferings.length,
54
+ rules: autopilotRules.length,
55
+ experiments: autopilotExperiments.length,
56
+ };
57
+ const nothing = totals.offerings + totals.rules + totals.experiments === 0;
58
+ const lines = [];
59
+ lines.push(`Autopilot status for ${app.name} (${app.bundle_id ?? app_id})`);
60
+ lines.push('');
61
+ if (nothing) {
62
+ lines.push("No autopilot-generated resources for this app (yet). If you just ran capivv_setup_wizard or imported products, try again — autopilot runs inline during import.");
63
+ }
64
+ else {
65
+ lines.push(`Summary: ${totals.offerings} offering, ${totals.rules} rule(s), ${totals.experiments} experiment(s).`);
66
+ lines.push('');
67
+ if (autopilotOfferings.length > 0) {
68
+ lines.push('Offerings:');
69
+ for (const o of autopilotOfferings) {
70
+ lines.push(` • ${o.identifier} — ${o.display_name} [${o.generated_by}]`);
71
+ }
72
+ }
73
+ if (autopilotRules.length > 0) {
74
+ lines.push('Rules:');
75
+ for (const r of autopilotRules) {
76
+ lines.push(` • ${r.identifier} — ${r.name} [${r.generated_by}]`);
77
+ }
78
+ }
79
+ if (autopilotExperiments.length > 0) {
80
+ lines.push('Experiments:');
81
+ for (const e of autopilotExperiments) {
82
+ lines.push(` • ${e.name} — status: ${e.status} [${e.generated_by}]`);
83
+ }
84
+ }
85
+ lines.push('');
86
+ lines.push('Paywalls + rescue flows + promotions also carry the "Autopilot" badge — open /paywalls, /rescue, /promotions in the dashboard to see them there.');
87
+ }
88
+ return {
89
+ content: [
90
+ { type: 'text', text: lines.join('\n') },
91
+ {
92
+ type: 'text',
93
+ text: JSON.stringify({
94
+ app: { id: app_id, name: app.name, bundle_id: app.bundle_id },
95
+ totals,
96
+ offerings: autopilotOfferings.map((o) => ({
97
+ id: o.id,
98
+ identifier: o.identifier,
99
+ generated_by: o.generated_by,
100
+ })),
101
+ rules: autopilotRules.map((r) => ({
102
+ id: r.id,
103
+ identifier: r.identifier,
104
+ generated_by: r.generated_by,
105
+ })),
106
+ experiments: autopilotExperiments.map((e) => ({
107
+ id: e.id,
108
+ name: e.name,
109
+ status: e.status,
110
+ generated_by: e.generated_by,
111
+ })),
112
+ }, null, 2),
113
+ },
114
+ ],
115
+ };
116
+ });
117
+ }
@@ -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 registerCreateAppTool(server: McpServer, client: CapivvClient): void;
@@ -0,0 +1,13 @@
1
+ import { z } from 'zod';
2
+ export function registerCreateAppTool(server, client) {
3
+ server.tool('capivv_create_app', 'Create a new app in Capivv with an explicit bundle ID. Prefer the dashboard\'s store-connect flow (which imports apps with real bundle IDs automatically); this tool is the manual fallback for web apps or apps without a store presence.', {
4
+ name: z.string().describe('App name (e.g., "My App")'),
5
+ platform: z.string().describe('Platform: ios, android, or web'),
6
+ bundle_id: z
7
+ .string()
8
+ .describe('Bundle identifier. Use your real reverse-DNS ID (e.g., "com.yourcompany.yourapp") — do NOT use com.example.myapp or other placeholder values.'),
9
+ }, async (args) => {
10
+ const app = await client.createApp(args);
11
+ return { content: [{ type: 'text', text: JSON.stringify(app, null, 2) }] };
12
+ });
13
+ }
@@ -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 registerCreateEntitlementTool(server: McpServer, client: CapivvClient): void;
@@ -0,0 +1,11 @@
1
+ import { z } from 'zod';
2
+ export function registerCreateEntitlementTool(server, client) {
3
+ server.tool('capivv_create_entitlement', 'Create a new entitlement (feature access identifier). Products grant entitlements to users upon purchase.', {
4
+ identifier: z.string().describe('Unique identifier (e.g., "premium", "pro_features")'),
5
+ display_name: z.string().describe('Human-readable name (e.g., "Premium Access")'),
6
+ description: z.string().optional().describe('Optional description of what this entitlement unlocks'),
7
+ }, async (args) => {
8
+ const entitlement = await client.createEntitlement(args);
9
+ return { content: [{ type: 'text', text: JSON.stringify(entitlement, null, 2) }] };
10
+ });
11
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { CapivvClient } from '../client.js';
3
+ export declare function registerCreateProductTool(server: McpServer, client: CapivvClient): void;
@@ -0,0 +1,42 @@
1
+ import { z } from 'zod';
2
+ export function registerCreateProductTool(server, client) {
3
+ server.tool('capivv_create_product', [
4
+ 'Create a Capivv-side product record (subscription, consumable, or non-consumable).',
5
+ 'Sets pricing, subscription config, and entitlement links in one call.',
6
+ '',
7
+ 'IMPORTANT: this creates the record inside Capivv only. Capivv does not write to App Store Connect or Google Play.',
8
+ 'For purchases to validate, a product with the SAME `external_id` must also exist in App Store Connect (and/or Google Play) at the same price you set here.',
9
+ 'Typical flow: create or confirm the product in App Store Connect at $X.XX, then call this tool with `external_id` matching the App Store Connect product ID.',
10
+ 'Skip-to-import path: if products already exist in App Store Connect and your tenant has store credentials connected, use capivv_import_products instead — it pulls them in automatically.',
11
+ ].join(' '), {
12
+ app_id: z.string().describe('App ID this product belongs to'),
13
+ external_id: z.string().describe('Store product ID (e.g., "com.example.pro_monthly")'),
14
+ product_type: z.string().describe('One of: subscription, consumable, non_consumable'),
15
+ display_name: z.string().describe('Human-readable product name'),
16
+ description: z.string().optional().describe('Optional product description'),
17
+ entitlement_ids: z
18
+ .array(z.string())
19
+ .optional()
20
+ .describe('Entitlement IDs this product grants upon purchase'),
21
+ subscription: z
22
+ .object({
23
+ billing_period: z
24
+ .string()
25
+ .describe('Billing period: week, month, three_months, six_months, or year'),
26
+ grace_period_enabled: z.boolean().optional().describe('Enable grace period for failed renewals'),
27
+ })
28
+ .optional()
29
+ .describe('Subscription configuration (required for subscription products)'),
30
+ prices: z
31
+ .array(z.object({
32
+ currency: z.string().describe('ISO 4217 currency code (e.g., "USD")'),
33
+ amount_cents: z.number().describe('Price in cents (e.g., 1099 for $10.99)'),
34
+ is_default: z.boolean().optional().describe('Whether this is the default price'),
35
+ }))
36
+ .optional()
37
+ .describe('Product prices'),
38
+ }, async (args) => {
39
+ const product = await client.createProduct(args);
40
+ return { content: [{ type: 'text', text: JSON.stringify(product, null, 2) }] };
41
+ });
42
+ }
@@ -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 registerDeleteProductTool(server: McpServer, client: CapivvClient): void;
@@ -0,0 +1,9 @@
1
+ import { z } from 'zod';
2
+ export function registerDeleteProductTool(server, client) {
3
+ server.tool('capivv_delete_product', 'Delete a product by ID. This is irreversible.', {
4
+ product_id: z.string().describe('Product ID to delete'),
5
+ }, async ({ product_id }) => {
6
+ await client.deleteProduct(product_id);
7
+ return { content: [{ type: 'text', text: `Product ${product_id} deleted.` }] };
8
+ });
9
+ }
@@ -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 registerDeleteRuleTool(server: McpServer, client: CapivvClient): void;
@@ -0,0 +1,9 @@
1
+ import { z } from 'zod';
2
+ export function registerDeleteRuleTool(server, client) {
3
+ server.tool('capivv_delete_rule', 'Delete a business rule by ID. This is irreversible.', {
4
+ rule_id: z.string().describe('Rule ID to delete'),
5
+ }, async ({ rule_id }) => {
6
+ await client.deleteRule(rule_id);
7
+ return { content: [{ type: 'text', text: `Rule ${rule_id} deleted.` }] };
8
+ });
9
+ }
@@ -1,17 +1,19 @@
1
1
  import { z } from 'zod';
2
2
  export function registerImportProductsTool(server, client) {
3
3
  server.tool('capivv_import_products', 'Import products from connected App Store Connect or Google Play Console into Capivv. Returns a preview of what will be imported including suggested products, entitlements, and offerings.', { app_id: z.string().describe('App ID to import products for') }, async ({ app_id }) => {
4
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4
5
  const preview = await client.getImportPreview(app_id);
5
- const newProducts = preview.suggestion.products.filter((p) => !p.already_exists);
6
- const newEntitlements = preview.suggestion.entitlements.filter((e) => !e.already_exists);
6
+ const suggestion = preview.suggestion ?? {};
7
+ const newProducts = (suggestion.products ?? []).filter((p) => !p.already_exists);
8
+ const newEntitlements = (suggestion.entitlements ?? []).filter((e) => !e.already_exists);
7
9
  const summary = {
8
10
  new_products: newProducts.length,
9
11
  new_entitlements: newEntitlements.length,
10
- new_offerings: preview.suggestion.offerings.length,
12
+ new_offerings: (suggestion.offerings ?? []).length,
11
13
  existing_counts: preview.existing_counts,
12
- warnings: preview.suggestion.warnings,
14
+ warnings: suggestion.warnings,
13
15
  store_errors: preview.store_errors,
14
- details: preview.suggestion,
16
+ details: suggestion,
15
17
  };
16
18
  return { content: [{ type: 'text', text: JSON.stringify(summary, null, 2) }] };
17
19
  });
@@ -1,22 +1,61 @@
1
1
  import { registerStatusTool } from './status.js';
2
2
  import { registerListAppsTool } from './list-apps.js';
3
+ import { registerCreateAppTool } from './create-app.js';
4
+ import { registerListEntitlementsTool } from './list-entitlements.js';
5
+ import { registerCreateEntitlementTool } from './create-entitlement.js';
3
6
  import { registerListProductsTool } from './list-products.js';
7
+ import { registerCreateProductTool } from './create-product.js';
8
+ import { registerUpdateProductTool } from './update-product.js';
9
+ import { registerDeleteProductTool } from './delete-product.js';
4
10
  import { registerImportProductsTool } from './import-products.js';
5
11
  import { registerListOfferingsTool } from './list-offerings.js';
6
12
  import { registerCreateOfferingTool } from './create-offering.js';
7
13
  import { registerListRulesTool } from './list-rules.js';
8
14
  import { registerApplyRuleTool } from './apply-rule.js';
15
+ import { registerActivateRuleTool } from './activate-rule.js';
16
+ import { registerDeleteRuleTool } from './delete-rule.js';
9
17
  import { registerListExperimentsTool } from './list-experiments.js';
10
18
  import { registerGetAnalyticsTool } from './get-analytics.js';
19
+ import { registerNextStepTool } from './next-step.js';
20
+ import { registerSetupWizardTool } from './setup-wizard.js';
21
+ import { registerVerifySetupTool } from './verify-setup.js';
22
+ import { registerWhoamiTool } from './whoami.js';
23
+ import { registerAutopilotStatusTool } from './autopilot-status.js';
24
+ import { registerApiKeyUsageTool } from './api-key-usage.js';
11
25
  export function registerAllTools(server, client) {
26
+ // Identity — call this first to verify which workspace you're connected to
27
+ registerWhoamiTool(server, client);
28
+ // Autopilot diagnostics — what did autopilot do for a given app?
29
+ registerAutopilotStatusTool(server, client);
30
+ // SDK telemetry — investigate suspected key leaks inline.
31
+ registerApiKeyUsageTool(server, client);
32
+ // Onboarding & guidance
33
+ registerNextStepTool(server, client);
34
+ registerSetupWizardTool(server, client);
35
+ registerVerifySetupTool(server, client);
36
+ // Status & analytics
12
37
  registerStatusTool(server, client);
38
+ registerGetAnalyticsTool(server, client);
39
+ // Apps
13
40
  registerListAppsTool(server, client);
41
+ registerCreateAppTool(server, client);
42
+ // Entitlements
43
+ registerListEntitlementsTool(server, client);
44
+ registerCreateEntitlementTool(server, client);
45
+ // Products
14
46
  registerListProductsTool(server, client);
47
+ registerCreateProductTool(server, client);
48
+ registerUpdateProductTool(server, client);
49
+ registerDeleteProductTool(server, client);
15
50
  registerImportProductsTool(server, client);
51
+ // Offerings
16
52
  registerListOfferingsTool(server, client);
17
53
  registerCreateOfferingTool(server, client);
54
+ // Rules
18
55
  registerListRulesTool(server, client);
19
56
  registerApplyRuleTool(server, client);
57
+ registerActivateRuleTool(server, client);
58
+ registerDeleteRuleTool(server, client);
59
+ // Experiments
20
60
  registerListExperimentsTool(server, client);
21
- registerGetAnalyticsTool(server, client);
22
61
  }
@@ -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 registerListEntitlementsTool(server: McpServer, client: CapivvClient): void;
@@ -0,0 +1,6 @@
1
+ export function registerListEntitlementsTool(server, client) {
2
+ server.tool('capivv_list_entitlements', 'List all entitlements (feature access identifiers) configured in your Capivv account.', async () => {
3
+ const entitlements = await client.listEntitlements();
4
+ return { content: [{ type: 'text', text: JSON.stringify(entitlements, null, 2) }] };
5
+ });
6
+ }
@@ -1,11 +1,13 @@
1
1
  export function registerListRulesTool(server, client) {
2
- server.tool('capivv_list_rules', 'List all YAML-based business rules with their status, priority, and last update time.', async () => {
2
+ server.tool('capivv_list_rules', 'List all business rules with their status, priority, and last update time.', async () => {
3
3
  const rules = await client.listRules();
4
4
  const summary = rules.map((r) => ({
5
5
  id: r.id,
6
+ identifier: r.identifier,
6
7
  name: r.name,
7
8
  priority: r.priority,
8
- is_enabled: r.is_enabled,
9
+ status: r.status,
10
+ version: r.version,
9
11
  updated_at: r.updated_at,
10
12
  }));
11
13
  return { content: [{ type: 'text', text: JSON.stringify(summary, null, 2) }] };
@@ -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 registerNextStepTool(server: McpServer, client: CapivvClient): void;