@certscore/mcp 0.2.21 → 0.2.23
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/README.md +47 -8
- package/dist/adoption.d.ts +7 -0
- package/dist/adoption.d.ts.map +1 -0
- package/dist/adoption.js +23 -0
- package/dist/certscore-mcp.mjs +689 -255
- package/dist/response-capture.d.ts +3 -0
- package/dist/response-capture.d.ts.map +1 -1
- package/dist/response-capture.js +29 -4
- package/dist/response-guidance.d.ts +4 -0
- package/dist/response-guidance.d.ts.map +1 -0
- package/dist/response-guidance.js +100 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +120 -27
- package/dist/tools.d.ts +7 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +92 -21
- package/package.json +1 -1
- package/server-light.json +1 -1
- package/server.json +2 -2
package/dist/tools.js
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import { apiV2GpcResponseSchema } from "@certscore/api-contracts";
|
|
1
|
+
import { apiV2GpcResponseSchema, apiV2ChoicePathExecutionSchema } from "@certscore/api-contracts";
|
|
2
2
|
import { withResponseCapture, transferResponseCapture } from "./response-capture.js";
|
|
3
3
|
import { getCertScoreErrorContext, CertScoreError } from "@certscore/sdk";
|
|
4
4
|
function canonicalGpcObservationSummary(value) {
|
|
5
5
|
const parsed = apiV2GpcResponseSchema.safeParse(value);
|
|
6
6
|
return parsed.success && parsed.data.contractVersion === "certscore.gpc-response-assessment.v3" ? parsed.data.summary : null;
|
|
7
7
|
}
|
|
8
|
+
function canonicalChoicePathExecutionText(observation) {
|
|
9
|
+
const parsed = apiV2ChoicePathExecutionSchema.safeParse(observation.execution);
|
|
10
|
+
if (!parsed.success)
|
|
11
|
+
return null;
|
|
12
|
+
const execution = parsed.data;
|
|
13
|
+
return `execution=${execution.status}; click completed=${execution.clickCompleted}; observation completed=${execution.observationCompleted}; consent confirmed=${execution.consentConfirmed}.`;
|
|
14
|
+
}
|
|
8
15
|
const MAX_ERROR_RESPONSE_BODY_CHARS = 2_000;
|
|
9
16
|
export const MAX_EVIDENCE_PACKET_CHARS = 250_000;
|
|
10
17
|
const EVIDENCE_STRING_CHARS = 4_000;
|
|
@@ -15,12 +22,13 @@ const LEGAL_REVIEW_DISCLAIMER = "CertScore results are automated public-web obse
|
|
|
15
22
|
const SCAN_PROVENANCE_GROUNDING = "retrievalMode describes how the current tool response obtained the scan; creationDecision describes whether the original scan request created or reused a scan only when that decision is retained. Never infer an unknown creationDecision from scan_id_lookup. For a reused or retrieved existing scan, use only persisted scanFrom and timestamps. Never infer its original scan region from the current request, the user's location, or a default execution region. If persisted region or timestamps are unavailable, report them as unavailable.";
|
|
16
23
|
const INTERPRETATION_STATEMENT = "The CertScore score covers observable public-web scan signals only. Do not infer technologies that are not listed in the returned evidence or any legal compliance status.";
|
|
17
24
|
const SCAN_BUNDLE_RESPONSE_CONTRACT = `Response contract: Report only observed CertScore evidence and CertScore classifications. criticality, priority, and confidence are CertScore metadata; regulatory review lenses are non-determinative CertScore review context—not legal severity, legal exposure, or a compliance determination. Absence of captured consent-action evidence does not establish what happens after Accept, Reject, or Decline. A confirmed post-action observation with termination.kind=evidence_satisfied means the observer intentionally stopped after retaining qualifying evidence; do not treat that termination as uncertainty about the returned observation. Keep any separately returned coverage limitation scoped to what was not measured. Do not extrapolate an observed embed, vendor, or request into unobserved cookies, fingerprinting, tracking, or processing, and do not infer violations or compliance beyond what CertScore observed. ${SCAN_PROVENANCE_GROUNDING}`;
|
|
25
|
+
const CHOICE_PATH_INTERPRETATION_STATEMENT = "Count execution.status succeeded and succeeded_with_confirmation as successful paths, with confirmation as a separate subset. A click alone is insufficient; registered paths may omit afterAction. Missing legacy execution remains unavailable. ";
|
|
18
26
|
const SCAN_BUNDLE_INTERPRETATION_STATEMENT = "Report only observed CertScore evidence and persisted CertScore classifications. For gpcResponse, use only GPC response, No observable GPC response, or indeterminate; do not call the result a GPC violation or say GPC was not honored. Keep its jurisdiction-neutral comparison separate from any explicitly returned California scoring policy. Without corresponding captured post-action evidence, do not infer what Accept, Reject, Decline, or another consent action would do; say the scan does not establish what happens after that action. When postAcceptObservation or postRefusalObservation is confirmed and termination.kind is evidence_satisfied, state the returned observation directly and explain that observation stopped intentionally after qualifying evidence was retained. Do not characterize that termination as uncertainty about the observation; mention unmeasured longer-term persistence only when relevant. Treat post-Accept activity as a score-neutral behavior baseline unless a separately projected finding says otherwise. Do not speculate that an observed embed, vendor, or request may cause additional cookies, fingerprinting, tracking, or processing unless CertScore observed that behavior. Treat returned priority or severity as a CertScore classification, not regulatory criticality or legal exposure; prefer ‘observed privacy risk signal’ or ‘CertScore finding’. Do not infer unobserved technologies, legal compliance, or a legal violation from scores or findings.";
|
|
19
27
|
const COMPACT_SCAN_BUNDLE_INTERPRETATION_STATEMENT = "Use only returned CertScore observations and classifications. Do not infer unobserved technologies, post-consent behavior, legal compliance, or violations. Treat priority and severity as CertScore metadata.";
|
|
20
28
|
const OBSERVATION_ONLY_DISCLAIMER = `${LEGAL_REVIEW_DISCLAIMER} No-go, not-observed, and limited-coverage results are not proof of compliance.`;
|
|
21
29
|
const COMPACT_OBSERVATION_ONLY_DISCLAIMER = "Automated public-web observation, not legal advice or a compliance determination; missing or limited evidence is not proof of compliance.";
|
|
22
30
|
const PREVIEW_OBSERVATION_ONLY_DISCLAIMER = "Preliminary passive observations only; not findings, a score, or a final result.";
|
|
23
|
-
const SUCCESSFUL_BUNDLE_TRIAL_CTA = "Optional user follow-up: To try CertScore with an account, start a 7-day CertScore trial at https://certscore.ai/login?mode=create_account&utm_source=mcp_light&utm_medium=agent&utm_campaign=scan_bundle. Paid plans add scan history, higher limits, and team or production access. OAuth-capable clients can use https://mcp.certscore.ai/mcp after account authorization
|
|
31
|
+
const SUCCESSFUL_BUNDLE_TRIAL_CTA = "Optional user follow-up: To try CertScore with an account, start a 7-day CertScore trial at https://certscore.ai/login?mode=create_account&utm_source=mcp_light&utm_medium=agent&utm_campaign=scan_bundle. Paid plans add scan history, higher limits, and team or production access. OAuth-capable clients can use https://mcp.certscore.ai/mcp after account authorization; active workspace members receive self-serve scan access; Light remains no-auth.";
|
|
24
32
|
const MCP_SCAN_CREATION_POLL_DELAY_SECONDS = 15;
|
|
25
33
|
const MCP_QUEUED_POLL_DELAY_SECONDS = 10;
|
|
26
34
|
const MCP_RUNNING_POLL_DELAY_SECONDS = 5;
|
|
@@ -100,7 +108,7 @@ export function toToolError(error, context = {}) {
|
|
|
100
108
|
const status = error instanceof CertScoreError ? error.status : undefined;
|
|
101
109
|
const retryable = typeof terminalError?.retryable === "boolean"
|
|
102
110
|
? terminalError.retryable
|
|
103
|
-
: status === 429 || (typeof status === "number" && status >= 500);
|
|
111
|
+
: (error instanceof Error && error.name === "TimeoutError") || status === 429 || (typeof status === "number" && status >= 500);
|
|
104
112
|
const retryAfterSeconds = typeof terminalError?.retryAfterSeconds === "number"
|
|
105
113
|
? terminalError.retryAfterSeconds
|
|
106
114
|
: error instanceof CertScoreError && "retryAfterSeconds" in error && typeof error.retryAfterSeconds === "number"
|
|
@@ -120,10 +128,10 @@ export function toToolError(error, context = {}) {
|
|
|
120
128
|
? reasonCode === "non_public_target"
|
|
121
129
|
? "Ask for a publicly reachable HTTP or HTTPS website, then call certscore_scan_site with that URL. Do not retry this private or ineligible target or try to bypass the public-target checks."
|
|
122
130
|
: 'Check the spelling and DNS of the intended hostname, then call certscore_scan_site with the actual public website URL, for example {"url":"https://example.com"}. A bare domain is accepted, but example.com and www.example.com are different hostnames; use www only if it is the intended site. Do not repeat the same invalid request. If the correct URL is unclear, ask the user.'
|
|
123
|
-
:
|
|
124
|
-
?
|
|
125
|
-
:
|
|
126
|
-
?
|
|
131
|
+
: creationRateLimit
|
|
132
|
+
? `No scan was created. Wait at least ${retryAfterSeconds ?? 30} seconds before retrying. To answer now, use certscore_get_latest_domain_scan if an existing scan meets the user's needs. Do not reconnect or create duplicate requests to bypass a quota.`
|
|
133
|
+
: typeof terminalError?.recommendedNextAction === "string"
|
|
134
|
+
? terminalError.recommendedNextAction
|
|
127
135
|
: retryable
|
|
128
136
|
? `Wait ${retryAfterSeconds ?? 30} seconds, then retry the same request. Stop and contact support@certscore.ai if the error repeats.`
|
|
129
137
|
: "Correct the request using the error details, then retry only if the requested operation is still appropriate.";
|
|
@@ -135,9 +143,13 @@ export function toToolError(error, context = {}) {
|
|
|
135
143
|
retryable: targetRejected ? false : retryable,
|
|
136
144
|
retryAfterSeconds: targetRejected ? null : retryAfterSeconds,
|
|
137
145
|
recommendedNextAction,
|
|
146
|
+
...((status === 403 || status === 429) ? { upgradeSupportEmail: "support@certscore.ai" } : {}),
|
|
147
|
+
...(context.scanCreation && status === 403 ? { scanStarted: false, alternativeTool: "certscore_get_latest_domain_scan" } : {}),
|
|
138
148
|
...(targetRejected ? { field: "url", scanStarted: false, inputCorrectionRequired: true } : {}),
|
|
139
149
|
...(creationRateLimit
|
|
140
|
-
? { creationRateLimit
|
|
150
|
+
? { creationRateLimit, scanStarted: false, quotaConsumed: false,
|
|
151
|
+
alternativeTool: "certscore_get_latest_domain_scan",
|
|
152
|
+
recovery: { action: "wait_for_quota", retryAfterSeconds, requiresReauthorization: false } }
|
|
141
153
|
: {}),
|
|
142
154
|
...(error instanceof CertScoreError ? {
|
|
143
155
|
name: error.name,
|
|
@@ -597,6 +609,17 @@ function compactPriorityEvidenceSummary(summary) {
|
|
|
597
609
|
};
|
|
598
610
|
}
|
|
599
611
|
function bundleEvidenceSummary(evidence, findings, links, includeDiagnostics) {
|
|
612
|
+
const inventory = evidence.unclassifiedExternalRequests;
|
|
613
|
+
const requestRows = Array.isArray(inventory)
|
|
614
|
+
? inventory
|
|
615
|
+
: inventory && typeof inventory === "object" && Array.isArray(inventory.items)
|
|
616
|
+
? inventory.items
|
|
617
|
+
: [];
|
|
618
|
+
const externalRequests = requestRows.filter((value) => Boolean(value) && typeof value === "object" && !Array.isArray(value) && typeof value.hostname === "string");
|
|
619
|
+
const policyInventory = evidence.policySurfaceCoverage;
|
|
620
|
+
const policyRows = policyInventory && typeof policyInventory === "object" && Array.isArray(policyInventory.items)
|
|
621
|
+
? policyInventory.items
|
|
622
|
+
: [];
|
|
600
623
|
const digests = findings.slice(0, 3).map((finding) => {
|
|
601
624
|
const findingEvidence = finding.evidence && typeof finding.evidence === "object" && !Array.isArray(finding.evidence)
|
|
602
625
|
? finding.evidence
|
|
@@ -617,7 +640,20 @@ function bundleEvidenceSummary(evidence, findings, links, includeDiagnostics) {
|
|
|
617
640
|
});
|
|
618
641
|
return {
|
|
619
642
|
digests,
|
|
620
|
-
evidenceAvailable: digests.length > 0 || Object.keys(evidence).length > 0,
|
|
643
|
+
evidenceAvailable: digests.length > 0 || externalRequests.length > 0 || policyRows.length > 0 || Object.keys(evidence).length > 0,
|
|
644
|
+
...(externalRequests.length ? { externalRequestSample: externalRequests.slice(0, 3).map(row => ({
|
|
645
|
+
host: row.hostname ?? null,
|
|
646
|
+
path: row.path ?? null,
|
|
647
|
+
resourceType: row.resourceType ?? null,
|
|
648
|
+
observedAtMs: row.observedAtMs ?? null,
|
|
649
|
+
interpretation: "External request observed; purpose and data transmission not established by this row."
|
|
650
|
+
})), externalRequestSampleTotal: externalRequests.length } : {}),
|
|
651
|
+
...(policyRows.length ? { policySurfaceCandidates: policyRows.slice(0, 3).flatMap(value => {
|
|
652
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
653
|
+
return [];
|
|
654
|
+
const row = value;
|
|
655
|
+
return typeof row.url === "string" ? [{ type: row.type ?? null, url: row.url }] : [];
|
|
656
|
+
}), policyEvidenceNextStep: "For a policy passage, use certscore_get_report_evidence_page with this scanId and inspect the report's retained policy evidence." } : {}),
|
|
621
657
|
evidenceSafetyNotes: Array.isArray(evidence.evidenceSafetyNotes)
|
|
622
658
|
? evidence.evidenceSafetyNotes.slice(0, 3).map((note) => boundedText(note, 300))
|
|
623
659
|
: [],
|
|
@@ -795,6 +831,10 @@ export function findingsFromReport(report) {
|
|
|
795
831
|
export function exportFindings(report) {
|
|
796
832
|
return {
|
|
797
833
|
type: "certscore_mcp_findings_export",
|
|
834
|
+
exportVersion: "certscore.findings-export.v1",
|
|
835
|
+
exportedAt: new Date().toISOString(),
|
|
836
|
+
returnedFindingCount: findingsFromReport(report).length,
|
|
837
|
+
completeness: "canonical findings returned by the report; not a complete inventory of website behavior",
|
|
798
838
|
scanId: scanIdFromPulse(report),
|
|
799
839
|
domain: report.domain ?? report.request?.domain ?? null,
|
|
800
840
|
summary: report.summary ?? null,
|
|
@@ -973,7 +1013,11 @@ function findingText(finding, priorityLabel = "criticality") {
|
|
|
973
1013
|
const nextStep = typeof finding.nextStep === "string" && finding.nextStep.trim()
|
|
974
1014
|
? `; canonical next step=${boundedText(finding.nextStep.trim(), 180)}`
|
|
975
1015
|
: "";
|
|
976
|
-
|
|
1016
|
+
const identity = typeof finding.id === "string" ? `; findingId=${finding.id}` : "";
|
|
1017
|
+
if (finding.plainEnglish == null && evidence.summary == null) {
|
|
1018
|
+
return `- ${finding.label ?? finding.id ?? "Projected finding"}${identity}; ${priorityLabel}=${finding.criticality ?? "unknown"}; confidence=${finding.confidence ?? "unknown"}; description and evidence detail are not included in this response tier.`;
|
|
1019
|
+
}
|
|
1020
|
+
return `- ${finding.label ?? finding.id ?? "Projected finding"}${identity}; ${priorityLabel}=${finding.criticality ?? "unknown"}; confidence=${finding.confidence ?? "unknown"}; observation=${finding.plainEnglish ?? evidence.summary ?? "No compact description available"}; evidence=${evidence.basis ?? "unknown"}/${evidence.phase ?? "phase unknown"}: ${evidence.summary ?? "No compact evidence summary available"}; review lenses=${lenses}${nextStep}.`;
|
|
977
1021
|
}
|
|
978
1022
|
function executiveOverviewText(summary) {
|
|
979
1023
|
if (!summary)
|
|
@@ -1109,6 +1153,9 @@ function terminalLaneResultTextLines(value) {
|
|
|
1109
1153
|
? value[field]
|
|
1110
1154
|
: null;
|
|
1111
1155
|
if (observation && typeof observation.interpretation === "string") {
|
|
1156
|
+
const execution = canonicalChoicePathExecutionText(observation);
|
|
1157
|
+
if (execution)
|
|
1158
|
+
lines.push(`${label} execution: ${execution}`);
|
|
1112
1159
|
lines.push(`${label}: ${observation.interpretation}`);
|
|
1113
1160
|
}
|
|
1114
1161
|
}
|
|
@@ -1298,7 +1345,10 @@ export function pulseReportText(value, label = "CertScore report") {
|
|
|
1298
1345
|
const body = [
|
|
1299
1346
|
...(overview ? [overview] : []),
|
|
1300
1347
|
`Canonical projected findings returned in this ${label.toLocaleLowerCase()}: ${findings.length}.`,
|
|
1301
|
-
...findings.map((finding) => findingText(finding))
|
|
1348
|
+
...findings.map((finding) => findingText(finding)),
|
|
1349
|
+
...(findings.some((finding) => finding.plainEnglish == null && finding.evidence?.summary == null)
|
|
1350
|
+
? [`For finding descriptions and evidence, call certscore_list_findings with scanId=${scanId}, or certscore_explain_finding with that scanId and a returned findingId. No new scan is needed.`]
|
|
1351
|
+
: [])
|
|
1302
1352
|
];
|
|
1303
1353
|
return boundedResultText(`${label} for ${domain}; scanId=${scanId}${score === null ? "" : `; CertScore score=${score}`}.`, body, value);
|
|
1304
1354
|
}
|
|
@@ -1308,15 +1358,19 @@ export function markdownReportText(value) {
|
|
|
1308
1358
|
const excerpt = markdown.length > excerptLimit ? `${markdown.slice(0, excerptLimit)}\n…[markdown truncated for MCP TextContent]` : markdown;
|
|
1309
1359
|
return boundedResultText(`CertScore report for scanId=${extractScanId(value) ?? "unknown"}. The following is a bounded canonical report excerpt.`, excerpt ? [excerpt] : ["No Markdown report body was returned."], value);
|
|
1310
1360
|
}
|
|
1311
|
-
export function scanBundleText(bundle) {
|
|
1361
|
+
export function scanBundleText(bundle, options = {}) {
|
|
1312
1362
|
const noGoText = canonicalNoGoText(bundle);
|
|
1313
1363
|
if (noGoText)
|
|
1314
1364
|
return noGoText;
|
|
1315
1365
|
const score = typeof bundle.score === "number" ? `; CertScore score=${bundle.score}` : "";
|
|
1316
|
-
const footer = [SUCCESSFUL_BUNDLE_TRIAL_CTA, OBSERVATION_ONLY_DISCLAIMER, SCAN_BUNDLE_INTERPRETATION_STATEMENT];
|
|
1366
|
+
const footer = [...(options.lightTrialCta ? [SUCCESSFUL_BUNDLE_TRIAL_CTA] : []), OBSERVATION_ONLY_DISCLAIMER, SCAN_BUNDLE_INTERPRETATION_STATEMENT];
|
|
1317
1367
|
const lines = [
|
|
1318
1368
|
SCAN_BUNDLE_RESPONSE_CONTRACT,
|
|
1319
1369
|
`CertScore scan bundle for ${bundle.domain ?? "unknown domain"}; status=${bundle.status ?? "unknown"}${score}; scanId=${bundle.scanId ?? "unknown"}.`,
|
|
1370
|
+
`Risk: ${bundle.riskLevel ?? "unknown"}. Finding IDs (returned): ${Array.isArray(bundle.findings) ? bundle.findings.slice(0, 20).map((finding) => String(finding.id ?? "unknown").slice(0, 120)).join(", ") || "none" : "unavailable"}.`,
|
|
1371
|
+
bundle.preConsentCookiesTrackers
|
|
1372
|
+
? `Pre-consent inventory: total=${bundle.preConsentCookiesTrackers.total ?? "unknown"}; returned=${bundle.preConsentCookiesTrackers.rows?.length ?? "unknown"}. Counts describe retained coverage, not consent compliance.`
|
|
1373
|
+
: `Pre-consent inventory: ${bundle.mcpMetadata?.omittedSections?.includes("preConsentCookiesTrackers") ? "omitted to fit the response byte limit" : "not included in this response"}. Call certscore_get_pre_consent_cookies_trackers with scanId=${bundle.scanId ?? "unknown"} for retained rows and counts; no new scan is needed.`,
|
|
1320
1374
|
canonicalScanProvenanceText(bundle),
|
|
1321
1375
|
`Full report: ${bundle.reportUrl ?? (bundle.scanId ? `https://certscore.ai/scan/${encodeURIComponent(String(bundle.scanId))}` : "not available")}.`
|
|
1322
1376
|
];
|
|
@@ -1331,7 +1385,18 @@ export function scanBundleText(bundle) {
|
|
|
1331
1385
|
? bundle.coverage
|
|
1332
1386
|
: null;
|
|
1333
1387
|
if (coverage) {
|
|
1334
|
-
append(`Coverage:
|
|
1388
|
+
append(`Coverage: ${coverage.scopeSummary ?? coverage.summary ?? "Review limitations before interpreting absence."}`);
|
|
1389
|
+
}
|
|
1390
|
+
const counts = bundle.summary?.counts;
|
|
1391
|
+
if (counts && typeof counts === "object" && Number.isInteger(counts.thirdPartyDomainsObserved) && Number.isInteger(counts.classifiedTrackerVendors)) {
|
|
1392
|
+
append(`Observed external domains: ${counts.thirdPartyDomainsObserved}; classified tracker vendors: ${counts.classifiedTrackerVendors}. External contact alone does not establish tracking.`);
|
|
1393
|
+
}
|
|
1394
|
+
if (bundle.detail === "evidence" || bundle.detail === "full") {
|
|
1395
|
+
const sample = bundle.evidenceSummary?.externalRequestSample;
|
|
1396
|
+
if (Array.isArray(sample) && sample.length > 0) {
|
|
1397
|
+
const first = sample[0];
|
|
1398
|
+
append(`Retained external request: ${first.host ?? "unknown host"}${first.path ?? ""}${first.resourceType ? ` (${first.resourceType})` : ""}. Purpose was not established by this request.`);
|
|
1399
|
+
}
|
|
1335
1400
|
}
|
|
1336
1401
|
const gpcResponse = bundle.gpcResponse && typeof bundle.gpcResponse === "object" && !Array.isArray(bundle.gpcResponse)
|
|
1337
1402
|
? bundle.gpcResponse
|
|
@@ -1357,6 +1422,9 @@ export function scanBundleText(bundle) {
|
|
|
1357
1422
|
const intentionalEvidenceStop = termination?.kind === "evidence_satisfied" && termination.intentional === true
|
|
1358
1423
|
? " The observation then stopped intentionally because qualifying evidence had been captured."
|
|
1359
1424
|
: "";
|
|
1425
|
+
const execution = canonicalChoicePathExecutionText(postAccept);
|
|
1426
|
+
if (execution)
|
|
1427
|
+
append(`Accept Path execution: ${execution}`);
|
|
1360
1428
|
append(`Accept Path: ${postAccept.interpretation}${intentionalEvidenceStop}`);
|
|
1361
1429
|
for (const limitation of Array.isArray(postAccept.coverageLimitations)
|
|
1362
1430
|
? postAccept.coverageLimitations.slice(0, 3)
|
|
@@ -1374,6 +1442,9 @@ export function scanBundleText(bundle) {
|
|
|
1374
1442
|
const intentionalEvidenceStop = termination?.kind === "evidence_satisfied" && termination.intentional === true
|
|
1375
1443
|
? " The observation then stopped intentionally because qualifying evidence had been captured."
|
|
1376
1444
|
: "";
|
|
1445
|
+
const execution = canonicalChoicePathExecutionText(postRefusal);
|
|
1446
|
+
if (execution)
|
|
1447
|
+
append(`Reject Path execution: ${execution}`);
|
|
1377
1448
|
append(`Reject Path: ${postRefusal.interpretation}${intentionalEvidenceStop}`);
|
|
1378
1449
|
for (const limitation of Array.isArray(postRefusal.coverageLimitations)
|
|
1379
1450
|
? postRefusal.coverageLimitations.slice(0, 3)
|
|
@@ -1438,9 +1509,6 @@ export function scanBundleText(bundle) {
|
|
|
1438
1509
|
append(`${rows.length - rowsRendered} additional returned pre-consent row${rows.length - rowsRendered === 1 ? " was" : "s were"} omitted from TextContent to preserve the size limit; see structuredContent or the report URL.`);
|
|
1439
1510
|
}
|
|
1440
1511
|
}
|
|
1441
|
-
else {
|
|
1442
|
-
append("No row-level pre-consent inventory was available for this result; review coverage and limitations before interpreting absence.");
|
|
1443
|
-
}
|
|
1444
1512
|
lines.push(...footer);
|
|
1445
1513
|
return lines.join("\n");
|
|
1446
1514
|
}
|
|
@@ -1572,9 +1640,10 @@ export function buildScanBundle(input) {
|
|
|
1572
1640
|
postAcceptObservation: input.scan.postAcceptObservation ?? null,
|
|
1573
1641
|
postRefusalObservation: input.scan.postRefusalObservation ?? null,
|
|
1574
1642
|
provenance: scanProvenance(input.scan, "existing_scan_retrieved"),
|
|
1575
|
-
interpretationGuidance: interpretationGuidance(input.scan.
|
|
1576
|
-
|
|
1577
|
-
|
|
1643
|
+
interpretationGuidance: interpretationGuidance((input.scan.postAcceptObservation || input.scan.postRefusalObservation ? CHOICE_PATH_INTERPRETATION_STATEMENT : "") +
|
|
1644
|
+
(input.scan.gpcResponse?.contractVersion === "certscore.gpc-response-assessment.v3"
|
|
1645
|
+
? SCAN_BUNDLE_INTERPRETATION_STATEMENT.replace("For gpcResponse,", "For the paired gpcResponse.status,") + " Report the separate bounded observation, CMP-recorded state and directly observed requests. Observation completion does not mean GPC was honored."
|
|
1646
|
+
: SCAN_BUNDLE_INTERPRETATION_STATEMENT)),
|
|
1578
1647
|
resultDisposition: input.scan.resultDisposition ?? null,
|
|
1579
1648
|
noGo: input.scan.noGo ?? null,
|
|
1580
1649
|
coverage: input.scan.coverage ?? null,
|
|
@@ -1755,7 +1824,9 @@ export function buildScanBundle(input) {
|
|
|
1755
1824
|
}
|
|
1756
1825
|
if (bundle.mcpMetadata.actualBytes > maxBytes) {
|
|
1757
1826
|
markBudgetOmitted("duplicateGuidance", "guidance_compacted_to_preserve_priority_content");
|
|
1758
|
-
bundle.interpretationGuidance = interpretationGuidance(
|
|
1827
|
+
bundle.interpretationGuidance = interpretationGuidance((bundle.postAcceptObservation || bundle.postRefusalObservation
|
|
1828
|
+
? "Both execution success statuses count; confirmation is separate. Missing execution is unavailable. " : "") +
|
|
1829
|
+
COMPACT_SCAN_BUNDLE_INTERPRETATION_STATEMENT);
|
|
1759
1830
|
bundle.observationOnlyDisclaimer = COMPACT_OBSERVATION_ONLY_DISCLAIMER;
|
|
1760
1831
|
bundle.disclaimer = null;
|
|
1761
1832
|
refresh();
|
package/package.json
CHANGED
package/server-light.json
CHANGED
package/server.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"url": "https://github.com/ergoveritas1-alt/certscore.ai",
|
|
9
9
|
"source": "github"
|
|
10
10
|
},
|
|
11
|
-
"version": "0.2.
|
|
11
|
+
"version": "0.2.23",
|
|
12
12
|
"remotes": [
|
|
13
13
|
{
|
|
14
14
|
"type": "streamable-http",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"registryType": "npm",
|
|
21
21
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
22
22
|
"identifier": "@certscore/mcp",
|
|
23
|
-
"version": "0.2.
|
|
23
|
+
"version": "0.2.23",
|
|
24
24
|
"transport": {
|
|
25
25
|
"type": "stdio"
|
|
26
26
|
},
|