@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 +1 -1
- package/dist/server.js +1 -1
- package/dist/tools.d.ts +21 -0
- package/dist/tools.js +83 -25
- package/package.json +2 -2
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.
|
|
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.
|
|
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).
|
|
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
|
-
...
|
|
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 === '
|
|
1015
|
-
? ['
|
|
1016
|
-
: v.status === '
|
|
1017
|
-
? ['
|
|
1018
|
-
: v.status === '
|
|
1019
|
-
? ['
|
|
1020
|
-
: v.status === '
|
|
1021
|
-
? ['
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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(
|
|
1208
|
-
const
|
|
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","
|
|
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
|
|
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('
|
|
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
|
|
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.
|
|
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.
|
|
80
|
+
"@checkbox-site/node": "^0.2.7",
|
|
81
81
|
"@modelcontextprotocol/sdk": "^1.32.2",
|
|
82
82
|
"zod": "^4.6.7"
|
|
83
83
|
}
|