@checkbox-site/mcp 0.2.5 → 0.2.7

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/server.d.ts CHANGED
@@ -5,7 +5,7 @@ import type { KeyContext } from './permissions.ts';
5
5
  import { type IssuedKeys, type ToolContext, type ToolDef } from './tools.ts';
6
6
  export declare const SERVER_NAME = "checkbox";
7
7
  /** Kept equal to packages/mcp/package.json "version" (@checkbox-site/mcp); mcp.test.ts checks it. */
8
- export declare const SERVER_VERSION = "0.2.5";
8
+ export declare const SERVER_VERSION = "0.2.7";
9
9
  export declare const DEFAULT_MCP_URL = "https://app.checkbox.site/mcp";
10
10
  /** The coverage phrase used across Checkbox (the API docs tests check the count against the rules catalog). */
11
11
  export declare const COVERAGE_PHRASE = "federal law, 27 states, DC and New York City";
package/dist/server.js CHANGED
@@ -9,7 +9,7 @@ import { likelyMisspelling, unknownArgumentsMessage, unknownKeysParams } from ".
9
9
  import { outdatedNotice } from "./version-check.js";
10
10
  export const SERVER_NAME = 'checkbox';
11
11
  /** Kept equal to packages/mcp/package.json "version" (@checkbox-site/mcp); mcp.test.ts checks it. */
12
- export const SERVER_VERSION = '0.2.5';
12
+ export const SERVER_VERSION = '0.2.7';
13
13
  export const DEFAULT_MCP_URL = 'https://app.checkbox.site/mcp';
14
14
  /** The coverage phrase used across Checkbox (the API docs tests check the count against the rules catalog). */
15
15
  export const COVERAGE_PHRASE = 'federal law, 27 states, DC and New York City';
package/dist/tools.d.ts CHANGED
@@ -133,6 +133,12 @@ export declare function clipWords(v: unknown, n: number): unknown;
133
133
  export declare function unverifiedScanNote(u: unknown): string | null;
134
134
  /** Round 20: one line with the plans Checkbox judges the site against (setup's site_plans), so no second call is needed. */
135
135
  export declare function sitePlansNote(plans: unknown): string | null;
136
+ /**
137
+ * Round 33 (pilot 32 item 22, new-app: setup_checkbox answered with about 30 KB on the first call): the first setup
138
+ * response leaves out what the agent does not need yet. The go-live steps become their one-line summary (get_compliance_plan
139
+ * and complete_setup carry them in full), and once the Stripe key is stored its permission checklist is dropped. Pure.
140
+ */
141
+ export declare function compactSetupDoc<T>(doc: T): T;
136
142
  /**
137
143
  * Pilot round 9 (nextjs): setup_checkbox still showed `insurable: false` and `score: 26.8` next to the status label.
138
144
  * The API keeps both (deprecated) for backward compatibility; agents see only the one status.
@@ -211,6 +217,21 @@ interface PrecheckLite {
211
217
  }
212
218
  /** Audit: the pre-check's per-check results, one compact line each (agents only saw the overall verdict). */
213
219
  export declare function precheckLines(pre: PrecheckLite | null | undefined, max?: number): string[];
220
+ interface FinalCheckLite {
221
+ check_id: string;
222
+ observed: string;
223
+ detail?: string;
224
+ }
225
+ /**
226
+ * Round 33 (pilot 32 item 10, new-app: the PASSED_WITH_NOTES text listed the stale pre-check bullets (needs_review)
227
+ * while the final checks lived only in the JSON): a finished verification is summarized from its FINAL checks; the
228
+ * pre-check lines show only while it is pending or when it has no final checks. Pure, exported for tests.
229
+ */
230
+ export declare function verificationLines(v: {
231
+ status: string;
232
+ precheck?: PrecheckLite | null;
233
+ checks?: FinalCheckLite[] | null;
234
+ }, max?: number): string[];
214
235
  /** createTestRun body from the tool's arguments (top-level shortcuts merged into `stripe`). Exported for tests. */
215
236
  export declare function startTestRunBody(args: Record<string, unknown>): Record<string, unknown>;
216
237
  interface ScheduledNotice {
package/dist/tools.js CHANGED
@@ -461,10 +461,29 @@ async function setupOverOAuth(api, session, ctx) {
461
461
  data: (() => {
462
462
  const doc = { ...session, ...(fresh ?? {}) };
463
463
  const keys = labelledPublicKeys(doc.install?.snippet_key, doc.publishable_key);
464
- return withoutLegacyScore({ ...doc, ...(keys.length ? { public_keys: keys } : {}), ...(firstCheck ? { first_check: firstCheck } : {}) });
464
+ return compactSetupDoc(withoutLegacyScore({ ...doc, ...(keys.length ? { public_keys: keys } : {}), ...(firstCheck ? { first_check: firstCheck } : {}) }));
465
465
  })(),
466
466
  };
467
467
  }
468
+ /**
469
+ * Round 33 (pilot 32 item 22, new-app: setup_checkbox answered with about 30 KB on the first call): the first setup
470
+ * response leaves out what the agent does not need yet. The go-live steps become their one-line summary (get_compliance_plan
471
+ * and complete_setup carry them in full), and once the Stripe key is stored its permission checklist is dropped. Pure.
472
+ */
473
+ export function compactSetupDoc(doc) {
474
+ if (!doc || typeof doc !== 'object')
475
+ return doc;
476
+ const d = { ...doc };
477
+ const g = d.go_live;
478
+ if (g && typeof g === 'object' && Array.isArray(g.steps))
479
+ d.go_live = { summary: g.summary ?? null, steps: g.steps.length, full: 'get_compliance_plan (go_live) or complete_setup' };
480
+ const st = d.stripe;
481
+ if (st && st.test_credential_stored === true && st.restricted_key && Array.isArray(st.restricted_key.permissions)) {
482
+ const { permissions: _p, ...rk } = st.restricted_key;
483
+ d.stripe = { ...st, restricted_key: rk };
484
+ }
485
+ return d;
486
+ }
468
487
  /**
469
488
  * Pilot round 9 (nextjs): setup_checkbox still showed `insurable: false` and `score: 26.8` next to the status label.
470
489
  * The API keeps both (deprecated) for backward compatibility; agents see only the one status.
@@ -647,7 +666,7 @@ const setupCheckbox = {
647
666
  notes: [
648
667
  'Tell the human exactly one line, e.g. "Open <link> and click Approve to connect Checkbox (free, no card)." Do not ask them to copy keys.',
649
668
  // Round 32 (pilot 31 item 13): the Stripe key box and the restricted key in the same message (one approval).
650
- `${args.billing_platform === 'stripe' ? 'In the same message' : 'If the site sells through Stripe, in the same message'}, ask them to tick “Let your agent store a Stripe test-mode key” on that page and, if the project has no rk_test_ key yet, to create a restricted key in Stripe Test mode (https://dashboard.stripe.com/test/apikeys → Create restricted key, with the permissions the approval page lists) and put it in the project's env file (never in the chat): one approval then covers the key and the test subscription.`,
669
+ `${args.billing_platform === 'stripe' ? 'In the same message' : 'If the site sells through Stripe, in the same message'}, ask them to tick the box “Let your agent store a Stripe test-mode restricted key for this project” on that page and, if the project has no rk_test_ key yet, to create a restricted key in Stripe Test mode (https://dashboard.stripe.com/test/apikeys → Create restricted key, with the permissions the approval page lists) and put it in the project's env file (never in the chat): one approval then covers the key and the test subscription.`,
651
670
  `Then call setup_checkbox again with device_code "${auth.device_code}" AND the same facts (site_url, checkout_url, merchant_name, plans, policy URLs): after the approval Checkbox applies them and runs the first checks in that call. Keep the device_code private; it is not shown to the human.`,
652
671
  ],
653
672
  next: { type: 'wait_for_approval', message: `Call setup_checkbox with device_code "${auth.device_code}" to wait for the approval.`, operation_id: 'createDeviceToken' },
@@ -670,7 +689,7 @@ const setupCheckbox = {
670
689
  notes,
671
690
  data: (() => {
672
691
  const keys = labelledPublicKeys(session.install?.snippet_key, session.publishable_key);
673
- return keys.length ? { ...withoutLegacyScore(session), public_keys: keys } : withoutLegacyScore(session);
692
+ return compactSetupDoc(keys.length ? { ...withoutLegacyScore(session), public_keys: keys } : withoutLegacyScore(session));
674
693
  })(),
675
694
  };
676
695
  },
@@ -957,6 +976,21 @@ export function precheckLines(pre, max = 12) {
957
976
  const lines = sorted.slice(0, max).map((c) => `${c.check_id}: ${c.status}${c.reason ? ` — ${untrusted(c.reason, 180)}` : ''}${c.fix ? ` Fix: ${untrusted(c.fix, 200)}` : ''}`);
958
977
  return [`Pre-check (${pre?.verdict ?? 'n/a'}), per check:`, ...lines, ...(checks.length > max ? [`and ${checks.length - max} more (data.precheck.checks)`] : [])];
959
978
  }
979
+ /**
980
+ * Round 33 (pilot 32 item 10, new-app: the PASSED_WITH_NOTES text listed the stale pre-check bullets (needs_review)
981
+ * while the final checks lived only in the JSON): a finished verification is summarized from its FINAL checks; the
982
+ * pre-check lines show only while it is pending or when it has no final checks. Pure, exported for tests.
983
+ */
984
+ export function verificationLines(v, max = 12) {
985
+ const checks = v.checks ?? [];
986
+ if (v.status === 'pending' || !checks.length)
987
+ return precheckLines(v.precheck, max);
988
+ const order = { fail: 0, needs_review: 1, missing_data: 1, pass: 2, informational: 2, not_applicable: 3 };
989
+ const sorted = [...checks].sort((a, b) => (order[a.observed] ?? 1) - (order[b.observed] ?? 1));
990
+ const lines = sorted.slice(0, max).map((c) => `${c.check_id}: ${c.observed}${c.detail ? ` — ${untrusted(c.detail, 180)}` : ''}`);
991
+ const pre = v.precheck?.verdict && v.precheck.checks?.length ? [`(The pre-check's estimate, ${v.precheck.verdict}, is superseded by these final checks: data.precheck keeps it.)`] : [];
992
+ return ['Final checks:', ...lines, ...(checks.length > max ? [`and ${checks.length - max} more (data.checks)`] : []), ...pre];
993
+ }
960
994
  const verifyTask = {
961
995
  name: 'verify_task',
962
996
  title: 'Verify a task after applying its change',
@@ -966,7 +1000,7 @@ const verifyTask = {
966
1000
  '- page tasks: html = the rendered page HTML (before or after deploy; ≤ ~900 KB). Without it Checkbox fetches the live page.',
967
1001
  '- Stripe tasks: stripe_checkout_session = exactly the object your code now passes to stripe.checkout.sessions.create.',
968
1002
  '- email tasks: email = { subject, text and/or html } of the new template.',
969
- 'Result: verification status passed / failed (with a diff of expected vs observed) / pending (waiting for the confirming scan). Waits up to `wait` seconds. Then follow next_action (usually get_compliance_plan). A verification with checks still needs_review is reported as failed with next_action type needs_review: it is not done. "informational" checks are notes on Stripe\'s own layout: nothing to change.',
1003
+ 'Result: verification status passed / needs_review / failed (with a diff of expected vs observed) / pending (waiting for the confirming scan). Waits up to `wait` seconds. Then follow next_action (usually get_compliance_plan). Status needs_review: nothing failed, but a check is still undecided; it is not done: follow next_action. "informational" checks are notes on Stripe\'s own layout: nothing to change.',
970
1004
  ].join('\n'),
971
1005
  input: {
972
1006
  task: z.string().max(100).describe('Task id (task_…).'),
@@ -1001,7 +1035,7 @@ const verifyTask = {
1001
1035
  return {
1002
1036
  headline: `Verification ${v.id} of ${String(args.task)}: ${statusLine(v.status)}${v.plan_status ? `; plan is now ${planStatusWords(v.plan_status, v.plan_status_label)}` : ''}.${v.checked ? ` Checked: ${untrusted(v.checked, 300)}.` : ''}`,
1003
1037
  notes: [
1004
- ...precheckLines(v.precheck),
1038
+ ...verificationLines(v),
1005
1039
  ...((v.status === 'failed' || v.status === 'not_checked') && v.next_action?.type === 'verify_domain'
1006
1040
  ? ['The domain is not verified yet. Waiting for the human to publish the DNS record / file / meta tag or install the snippet; don\'t retry until they say it\'s done. Nothing to change in the code.']
1007
1041
  : v.next_action?.type === 'retry_later'
@@ -1011,15 +1045,17 @@ const verifyTask = {
1011
1045
  [v.next_action.description ?? 'Checkbox Notices sends this email: nothing to add to your code. Continue with the next task.']
1012
1046
  : v.next_action?.type === 'bcc_then_continue'
1013
1047
  ? ['The copy is checked; the task closes when the first real email arrives with the BCC. Add the BCC to your sending code and continue with the next task (no need to poll).']
1014
- : v.status === 'failed'
1015
- ? ['The change did not satisfy the check: read diff (expected vs observed), fix the code, and call verify_task again.']
1016
- : v.status === 'not_checked'
1017
- ? ['Not checked yet: Checkbox is missing evidence (see next_action), not a code problem. Do not change the code for this; provide what next_action asks for.']
1018
- : v.status === 'pending_live_check'
1019
- ? ['Live check pending: the email content passes; Checkbox has not seen it sent for a real event yet. Nothing to change in the code: it completes on the first real send (or a proof run).']
1020
- : v.status === 'pending'
1021
- ? ['Waiting for a confirming observation; call get_verification later (or continue with other tasks).']
1022
- : []),
1048
+ : v.status === 'needs_review'
1049
+ ? ['Nothing failed, but a check is still undecided: it is not done. Follow Next (it says what decides it).']
1050
+ : v.status === 'failed'
1051
+ ? ['The change did not satisfy the check: read diff (expected vs observed), fix the code, and call verify_task again.']
1052
+ : v.status === 'not_checked'
1053
+ ? ['Not checked yet: Checkbox is missing evidence (see next_action), not a code problem. Do not change the code for this; provide what next_action asks for.']
1054
+ : v.status === 'pending_live_check'
1055
+ ? ['Live check pending: the email content passes; Checkbox has not seen it sent for a real event yet. Nothing to change in the code: it completes on the first real send (or a proof run).']
1056
+ : v.status === 'pending'
1057
+ ? ['Waiting for a confirming observation; call get_verification later (or continue with other tasks).']
1058
+ : []),
1023
1059
  ...hint,
1024
1060
  ],
1025
1061
  data: v,
@@ -1076,7 +1112,7 @@ const getVerification = {
1076
1112
  annotations: { ...ro, title: 'Verification' },
1077
1113
  async run(args, { api }) {
1078
1114
  const v = await api.request('GET', `/v1/verifications/${enc(String(args.verification))}`, { query: { wait: args.wait ?? 0 } });
1079
- const notes = precheckLines(v.precheck);
1115
+ const notes = verificationLines(v);
1080
1116
  return { headline: `Verification ${v.id}: ${statusLine(v.status)}.`, ...(notes.length ? { notes } : {}), data: v };
1081
1117
  },
1082
1118
  };
@@ -1184,14 +1220,24 @@ const startTestRun = {
1184
1220
  };
1185
1221
  },
1186
1222
  };
1223
+ const TEST_RUN_MAX_WAIT = 55;
1224
+ const TEST_RUN_DONE = new Set(['succeeded', 'incomplete', 'failed', 'canceled']);
1187
1225
  const getTestRun = {
1188
1226
  name: 'get_test_run',
1189
1227
  title: 'Get a test run (steps, observations, fixes)',
1190
- description: 'A synthetic test subscription with every step (checkout, acknowledgment email, reminders at each legal window, cancellation, confirmation): what was observed, check results, evidence ids and fixes. While running, next_action.type is "wait" and waiting_for says what it waits for (pass wait: 30 to get the answer as soon as the run moves on). When finished, apply the fixes (usually via get_compliance_plan) and start another run to verify. Without test_run, returns the newest run of the site.',
1228
+ description: 'A synthetic test subscription with every step (checkout, acknowledgment email, reminders at each legal window, cancellation, confirmation): what was observed, check results, evidence ids and fixes. While running, next_action.type is "wait" and waiting_for says what it waits for: pass wait: 55 to get the answer as soon as the run finishes (one or two calls per run). When finished, apply the fixes (usually via get_compliance_plan) and start another run to verify. Without test_run, returns the newest run of the site.',
1191
1229
  input: {
1192
1230
  test_run: z.string().max(100).optional().describe('Test run id (run_…). Default: the newest run of the site.'),
1193
1231
  site: siteId.optional(),
1194
- wait: z.number().int().min(0).max(30).optional().describe('Long-poll: wait up to this many seconds for the run to move on before answering.'),
1232
+ // Round 34 (pilot 33 item 22, core-codex: 25 polls with wait=30 for runs of 1-2 minutes, because each poll answered
1233
+ // on every step): wait up to 55 s (under common 60 s client tool timeouts), answering when the run finishes.
1234
+ wait: z
1235
+ .number()
1236
+ .int()
1237
+ .min(0)
1238
+ .transform((n) => Math.min(TEST_RUN_MAX_WAIT, n))
1239
+ .optional()
1240
+ .describe(`Seconds to wait for the run to finish before answering (0-${TEST_RUN_MAX_WAIT}; larger values wait ${TEST_RUN_MAX_WAIT}). Answers at once when it finishes.`),
1195
1241
  },
1196
1242
  scopes: ['test_runs:read'],
1197
1243
  annotations: { ...ro, title: 'Test run' },
@@ -1204,8 +1250,15 @@ const getTestRun = {
1204
1250
  throw new ToolError(`Site ${site} has no test run yet.`, { type: 'start_test_run', message: 'Call start_test_run.', operation_id: 'createTestRun' });
1205
1251
  id = list.data[0].id;
1206
1252
  }
1207
- const wait = typeof args.wait === 'number' && args.wait > 0 ? Math.min(30, Math.floor(args.wait)) : undefined;
1208
- const run = await api.request('GET', `/v1/test_runs/${enc(id)}`, wait ? { query: { wait } } : undefined);
1253
+ const wait = typeof args.wait === 'number' && args.wait > 0 ? Math.min(TEST_RUN_MAX_WAIT, Math.floor(args.wait)) : 0;
1254
+ const deadline = Date.now() + wait * 1000;
1255
+ // The API long-polls up to 30 s per call and answers on every step; keep asking until the run finishes or time is up.
1256
+ let run = await api.request('GET', `/v1/test_runs/${enc(id)}`, wait ? { query: { wait: Math.min(30, wait) } } : undefined);
1257
+ for (let left = Math.floor((deadline - Date.now()) / 1000); wait && !TEST_RUN_DONE.has(run.status) && left >= 1; left = Math.floor((deadline - Date.now()) / 1000)) {
1258
+ run = await api.request('GET', `/v1/test_runs/${enc(id)}`, { query: { wait: Math.min(30, left) } });
1259
+ }
1260
+ const running = !TEST_RUN_DONE.has(run.status);
1261
+ const next = running && run.next_action?.type === 'wait' ? { ...run.next_action, message: `${(run.next_action.message ?? '').replace(/\s*Call retrieveTestRun with \?wait=30[^.]*\.?/, '').trim()} Call get_test_run again with wait: ${TEST_RUN_MAX_WAIT}: it answers as soon as the run finishes.`.trim() } : undefined;
1209
1262
  const w = run.waiting_for;
1210
1263
  const progress = w ? [w.step && w.steps_total ? `step ${w.step} of ${w.steps_total}` : '', w.minutes_left ? `about ${w.minutes_left} min left` : ''].filter(Boolean).join(', ') : '';
1211
1264
  const waiting = w ? `Waiting for: ${w.what}${w.until ? ` (until ${new Date(w.until * 1000).toISOString().slice(0, 16).replace('T', ' ')} UTC at the latest)` : ''}${progress ? ` [${progress}]` : ''}` : null;
@@ -1213,6 +1266,7 @@ const getTestRun = {
1213
1266
  headline: `Test run ${run.id}: ${run.status_label ?? statusLine(run.status)}${run.phase && run.status !== 'succeeded' && run.status !== 'failed' ? ` (phase ${run.phase})` : ''}.`,
1214
1267
  notes: [...(waiting ? [waiting] : []), ...(run.steps ?? []).map((s) => `${s.status ?? '?'}: ${s.title ?? s.key}${s.code ? ` (${s.code})` : ''}`)].slice(0, 13),
1215
1268
  data: run,
1269
+ ...(next ? { next } : {}),
1216
1270
  };
1217
1271
  },
1218
1272
  };
@@ -1224,8 +1278,8 @@ const checkDisclosure = {
1224
1278
  title: 'Check checkout disclosure text',
1225
1279
  description: [
1226
1280
  'Before shipping a checkout: checks the automatic-renewal disclosure shown next to the purchase button against every applicable law (ROSCA, CA, NY, …) and returns per-check verdicts with citations plus a REPLACEMENT verified by the rules engine (disclosure_text, consent_label, button_text, drop-in html and jsx).',
1227
- 'Pass the text exactly as rendered plus the plan terms. Example: {"text":"Renews monthly at $19.99 until you cancel.","price":19.99,"interval":"month","trial_days":14,"cancel_url":"https://acme.example/account/cancel","merchant_name":"Acme","consent":{"checkbox":true,"prechecked":false}}.',
1228
- 'If verdict is fail, put replacement.jsx (React) or .html above the button, then call again with the new text, consent and placement.bold: true (it is semibold). needs_review on styling means a rendered scan must confirm.',
1281
+ 'Pass the text exactly as rendered plus the plan terms. Example: {"text":"Renews monthly at $19.99 until you cancel.","price":19.99,"interval":"month","cancel_url":"https://acme.example/account/cancel","consent":{"checkbox":true,"prechecked":false}}.',
1282
+ 'If verdict is fail, put replacement.jsx (React) or .html above the button, then call again with the new text, consent and placement.bold: true. needs_review on styling means a rendered scan must confirm.',
1229
1283
  ].join('\n'),
1230
1284
  input: {
1231
1285
  text: z.string().min(1).max(5000).describe('The disclosure shown next to the purchase button, exactly as rendered.'),
@@ -1245,7 +1299,7 @@ const checkDisclosure = {
1245
1299
  body_font_size_px: z.number().min(4).max(96).optional(),
1246
1300
  }, 'placement')
1247
1301
  .optional()
1248
- .describe('Where and how the disclosure is shown (defaults: above the button, same page, 14px, high contrast).'),
1302
+ .describe('Defaults: above the button, same page, 14px, high contrast.'),
1249
1303
  consent: strictObject({
1250
1304
  checkbox: z.boolean(),
1251
1305
  prechecked: z.boolean().optional(),
@@ -1253,7 +1307,8 @@ const checkDisclosure = {
1253
1307
  required: z.boolean().optional(),
1254
1308
  }, 'consent')
1255
1309
  .optional()
1256
- .describe('The consent checkbox, if the page has one. Omit when there is none.'),
1310
+ .describe('The consent checkbox, if any.'),
1311
+ checkout_type: z.enum(['own_page', 'stripe_checkout', 'payment_link']).optional().describe('Stripe-hosted: no box here'),
1257
1312
  jurisdictions: jurisdictions.optional(),
1258
1313
  as_of: asOf.optional(),
1259
1314
  },
@@ -1654,7 +1709,7 @@ const updateSite = {
1654
1709
  test_account: strictObject({
1655
1710
  login_url: z.string().max(2048),
1656
1711
  email: z.string().max(320),
1657
- password: z.string().max(200),
1712
+ password: z.string().max(200).optional(),
1658
1713
  reactivate_url: z.string().max(2048).optional().describe('Test-only page that re-subscribes the test account.'),
1659
1714
  }, 'test_account')
1660
1715
  .optional()
@@ -1796,8 +1851,11 @@ const listFindings = {
1796
1851
  starting_after: args.starting_after,
1797
1852
  },
1798
1853
  });
1854
+ // Round 34 (pilot 33 item 11, core-new-app: status=fail listed 0 while the plan said "2 fixes needed"): status is
1855
+ // the engine's verdict; the fix count is the status label (a needs_review finding can need a fix).
1856
+ const failNote = args.status === 'fail' && !list.data.length ? ' status=fail is the engine verdict only: a needs_review finding can still be counted as a fix (its label says "Needs a fix"). The fixes to make are the tasks of get_compliance_plan.' : '';
1799
1857
  return {
1800
- headline: `${list.data.length}${list.has_more ? '+' : ''} finding(s).`,
1858
+ headline: `${list.data.length}${list.has_more ? '+' : ''} finding(s).${failNote}`,
1801
1859
  notes: list.data.map((f) => `${f.severity.toUpperCase()} ${f.id} [${f.compliance?.label ?? f.status}] ${f.check_id}: ${f.title}${f.requirements?.[0] ? ` [${f.requirements[0].citation}]` : ''}`),
1802
1860
  data: list,
1803
1861
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkbox-site/mcp",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "Checkbox MCP server: auto-renewal law and subscription compliance checks for AI agents (FTC ROSCA, California ARL, New York and other states) on Stripe Checkout params, pricing pages, disclosures and emails. Remote at https://app.checkbox.site/mcp, or local stdio.",
5
5
  "keywords": [
6
6
  "checkbox",
@@ -77,7 +77,7 @@
77
77
  "prepack": "npm run build"
78
78
  },
79
79
  "dependencies": {
80
- "@checkbox-site/node": "^0.2.5",
80
+ "@checkbox-site/node": "^0.2.7",
81
81
  "@modelcontextprotocol/sdk": "^1.32.2",
82
82
  "zod": "^4.6.7"
83
83
  }