@certscore/mcp 0.2.7 → 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -11
- package/dist/certscore-mcp.mjs +278 -18
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -4
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +95 -6
- package/dist/tools.d.ts +64 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +67 -0
- package/package.json +12 -11
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
CertScore MCP exposes a focused Model Context Protocol server for CertScore Pulse workflows.
|
|
4
4
|
|
|
5
|
-
Status: public developer preview. Version 0.2.
|
|
5
|
+
Status: public developer preview. Version 0.2.11 is prepared for the hosted Streamable HTTP service and the next deterministic Homebrew release. Local WC01 development uses `pnpm mcp:certscore`.
|
|
6
6
|
|
|
7
7
|
Public docs:
|
|
8
8
|
|
|
@@ -14,11 +14,12 @@ Public docs:
|
|
|
14
14
|
## Tools
|
|
15
15
|
|
|
16
16
|
- `create_scan` - Deprecated compatibility alias of scan_site. Use scan_site for new integrations. Returns completed-limited no-go disposition and reason-specific guidance when applicable.
|
|
17
|
-
- `scan_site` -
|
|
17
|
+
- `scan_site` - Recommended first call. Starts or reuses a public-web scan, reports freshness and anonymous quota decisions, and waits up to 45 seconds by default. If still running, use get_scan_status; otherwise use get_scan_bundle. Completed no-go scans retain completed_limited status and reason-specific guidance.
|
|
18
18
|
- `get_scan` - Retrieve the API v2 public-safe scan resource, including completed-limited no-go disposition, reason-specific guidance, and timing when available.
|
|
19
19
|
- `get_scan_status` - Retrieve terminal status, including completed_limited no-go disposition and reason-specific guidance. Pass jobId only before a stable scanId is available.
|
|
20
20
|
- `get_report` - Retrieve a summary Pulse report, including customer-safe no-go messaging when coverage is completed-limited. Use get_evidence for the larger bounded packet.
|
|
21
21
|
- `get_evidence` - Retrieve the bounded structured Evidence JSON packet for a stable scan ID. Excludes raw cookie values, raw bodies, sensitive payloads, full DOM, and unredacted query values.
|
|
22
|
+
- `get_scan_bundle` - Recommended second call after scan_site. Returns the canonical scan state, compact report summary, findings, bounded evidence summary, and pre-consent inventory in one agent-friendly response.
|
|
22
23
|
- `export_findings` - Return structured findings plus completed-limited no-go disposition and guidance for downstream review or ticketing workflows.
|
|
23
24
|
- `list_findings` - List API v2 public-safe findings already projected for a scan.
|
|
24
25
|
- `get_pre_consent_cookies_trackers` - Retrieve the public-safe Cookies & Trackers (Pre-consent) report table as compact JSON for a scan.
|
|
@@ -59,6 +60,15 @@ https://certscore.ai/.well-known/oauth-authorization-server
|
|
|
59
60
|
|
|
60
61
|
The hosted service uses OAuth authorization code with PKCE. Default read access requests `scan:read mcp`; support-gated scan creation additionally requests `scan:create`. The same tool implementation and output contracts power stdio and hosted transports.
|
|
61
62
|
|
|
63
|
+
For low-volume agent discovery without account or OAuth setup, use the unauthenticated endpoint:
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
https://mcp.certscore.ai/mcp/anonymous
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
It exposes the same public-safe tool surface and preserves the existing limit of 10 new scans per requester IP per UTC day. Reusing an
|
|
70
|
+
eligible recent result does not consume the quota. Use the OAuth endpoint or a scoped API key for higher-volume workflows.
|
|
71
|
+
|
|
62
72
|
## Configuration
|
|
63
73
|
|
|
64
74
|
Install with Homebrew on macOS:
|
|
@@ -124,9 +134,22 @@ Stdio API keys use `pulse:read` and `mcp`; creating scans additionally requires
|
|
|
124
134
|
certscore-mcp --version
|
|
125
135
|
certscore-mcp --help
|
|
126
136
|
CERTSCORE_API_KEY=... certscore-mcp doctor
|
|
137
|
+
CERTSCORE_API_KEY=... certscore-mcp doctor --check-auth
|
|
127
138
|
```
|
|
128
139
|
|
|
129
|
-
The doctor command checks binary startup, version output, Node.js runtime compatibility, the configured CertScore base URL, API v2 health, and API key presence.
|
|
140
|
+
The doctor command checks binary startup, version output, Node.js runtime compatibility, the configured CertScore base URL, API v2 health, and API key presence. Add `--check-auth` to validate the credential against `/api/v2/auth/check` without creating a scan. It does not print secrets or inspect raw scanner artifacts.
|
|
141
|
+
|
|
142
|
+
## No-account agent scan path
|
|
143
|
+
|
|
144
|
+
Agents that cannot create an account or configure OAuth can use the public API v2 scan path without an `Authorization` header:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
curl -X POST https://certscore.ai/api/v2/scans \
|
|
148
|
+
-H "Content-Type: application/json" \
|
|
149
|
+
-d '{"url":"https://example.com","freshness":"latest","scanFrom":"eu_ie"}'
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
New anonymous scans are limited to 10 per requester IP per UTC day. Reusing an eligible recent result does not consume the quota. Poll the returned status resource, then retrieve findings or evidence. Use OAuth or a scoped API key for repeated or higher-volume workflows.
|
|
130
153
|
|
|
131
154
|
## MCP Client Examples
|
|
132
155
|
|
|
@@ -198,14 +221,16 @@ Local repo config for contributors:
|
|
|
198
221
|
|
|
199
222
|
## Agent Workflow
|
|
200
223
|
|
|
201
|
-
1. Call `scan_site` with a public URL.
|
|
202
|
-
2.
|
|
203
|
-
3. Call `
|
|
204
|
-
4. Call `list_findings`
|
|
205
|
-
5. Call `
|
|
206
|
-
6. Call `
|
|
207
|
-
|
|
208
|
-
|
|
224
|
+
1. Call `scan_site` with a public URL. It normally returns the completed scan resource in the same tool call.
|
|
225
|
+
2. Only if it returns a non-terminal job, call `get_scan_status` using the stable `scanId` until completion.
|
|
226
|
+
3. Call `get_scan_bundle` for the normal compact review handoff.
|
|
227
|
+
4. Call `get_report`, `get_evidence`, `list_findings`, or `get_pre_consent_cookies_trackers` only when the task needs a dedicated view.
|
|
228
|
+
5. Call `explain_finding` when a reviewer needs evidence and caveats for a specific finding.
|
|
229
|
+
6. Call `get_latest_domain_scan` or `get_latest_domain_pre_consent_cookies_trackers` when the user asks for latest eligible public data for a domain.
|
|
230
|
+
|
|
231
|
+
`scan_site` reports whether the result was reused, why the freshness decision was made, whether anonymous quota was consumed, the remaining daily allowance, the UTC reset time, and the recommended next tool.
|
|
232
|
+
|
|
233
|
+
With `freshness: "latest"`, CertScore reuses an eligible scan completed within the last 24 hours for the same normalized target and scan region. A reusable result must have completed usable page coverage and must not be an early-loss, no-page, or otherwise non-reusable limited result. Reuse does not consume anonymous quota. `freshnessDecision` states whether a recent result was reused or a new scan was queued; `reusedScanAgeSeconds` reports the reused result's age.
|
|
209
234
|
|
|
210
235
|
```json
|
|
211
236
|
{
|
package/dist/certscore-mcp.mjs
CHANGED
|
@@ -15386,6 +15386,9 @@ var StdioServerTransport = class {
|
|
|
15386
15386
|
import { realpathSync } from "node:fs";
|
|
15387
15387
|
import { fileURLToPath } from "node:url";
|
|
15388
15388
|
|
|
15389
|
+
// ../certscore-sdk/dist/client.js
|
|
15390
|
+
import { createHmac } from "node:crypto";
|
|
15391
|
+
|
|
15389
15392
|
// ../certscore-sdk/dist/errors.js
|
|
15390
15393
|
var CertScoreError = class extends Error {
|
|
15391
15394
|
status;
|
|
@@ -15587,6 +15590,9 @@ function isStatus(body) {
|
|
|
15587
15590
|
var CertScoreClient = class {
|
|
15588
15591
|
apiKey;
|
|
15589
15592
|
baseUrl;
|
|
15593
|
+
clientName;
|
|
15594
|
+
forwardedClientIp;
|
|
15595
|
+
anonymousRequesterSecret;
|
|
15590
15596
|
timeout;
|
|
15591
15597
|
scans;
|
|
15592
15598
|
findings;
|
|
@@ -15596,6 +15602,9 @@ var CertScoreClient = class {
|
|
|
15596
15602
|
constructor(options = {}) {
|
|
15597
15603
|
this.apiKey = options.apiKey;
|
|
15598
15604
|
this.baseUrl = normalizeBaseUrl(options.baseUrl);
|
|
15605
|
+
this.clientName = options.clientName ?? "sdk";
|
|
15606
|
+
this.forwardedClientIp = options.forwardedClientIp?.trim() || void 0;
|
|
15607
|
+
this.anonymousRequesterSecret = options.anonymousRequesterSecret?.trim() || void 0;
|
|
15599
15608
|
this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
15600
15609
|
this.scans = {
|
|
15601
15610
|
create: (url2, scanOptions) => this.createScanResource(url2, scanOptions),
|
|
@@ -16002,7 +16011,8 @@ var CertScoreClient = class {
|
|
|
16002
16011
|
}
|
|
16003
16012
|
headers(jsonBody = false) {
|
|
16004
16013
|
const headers = {
|
|
16005
|
-
Accept: "application/json, text/markdown;q=0.9"
|
|
16014
|
+
Accept: "application/json, text/markdown;q=0.9",
|
|
16015
|
+
"X-CertScore-Client": this.clientName
|
|
16006
16016
|
};
|
|
16007
16017
|
if (jsonBody) {
|
|
16008
16018
|
headers["Content-Type"] = "application/json; charset=utf-8";
|
|
@@ -16010,6 +16020,17 @@ var CertScoreClient = class {
|
|
|
16010
16020
|
if (this.apiKey) {
|
|
16011
16021
|
headers.Authorization = `Bearer ${this.apiKey}`;
|
|
16012
16022
|
}
|
|
16023
|
+
if (this.forwardedClientIp && !this.apiKey) {
|
|
16024
|
+
headers["X-Forwarded-For"] = this.forwardedClientIp;
|
|
16025
|
+
if (this.anonymousRequesterSecret) {
|
|
16026
|
+
const timestamp = String(Math.floor(Date.now() / 1e3));
|
|
16027
|
+
const message = `${timestamp}.${this.forwardedClientIp}`;
|
|
16028
|
+
const proof = createHmac("sha256", this.anonymousRequesterSecret).update(message).digest("base64url");
|
|
16029
|
+
headers["X-CertScore-Anonymous-Requester-IP"] = this.forwardedClientIp;
|
|
16030
|
+
headers["X-CertScore-Anonymous-Requester-Timestamp"] = timestamp;
|
|
16031
|
+
headers["X-CertScore-Anonymous-Requester-Proof"] = proof;
|
|
16032
|
+
}
|
|
16033
|
+
}
|
|
16013
16034
|
return headers;
|
|
16014
16035
|
}
|
|
16015
16036
|
url(path) {
|
|
@@ -20253,6 +20274,14 @@ var pulseStatusSchema = external_exports2.object({
|
|
|
20253
20274
|
resultUrl: external_exports2.string().nullable().optional(),
|
|
20254
20275
|
reportUrl: external_exports2.string().nullable().optional(),
|
|
20255
20276
|
retryAfterSeconds: external_exports2.number().int().nullable().optional(),
|
|
20277
|
+
recovery: external_exports2.object({
|
|
20278
|
+
alternateRegionAttempted: external_exports2.literal(true),
|
|
20279
|
+
fallbackScanFrom: external_exports2.string(),
|
|
20280
|
+
noGoReason: external_exports2.string(),
|
|
20281
|
+
primaryScanFrom: external_exports2.string(),
|
|
20282
|
+
primaryScanId: external_exports2.string(),
|
|
20283
|
+
claimedAt: external_exports2.string()
|
|
20284
|
+
}).passthrough().optional(),
|
|
20256
20285
|
capabilities: pulseCapabilitiesSchema.optional(),
|
|
20257
20286
|
agentInterpretation: pulseAgentInterpretationSchema.optional(),
|
|
20258
20287
|
disclaimer: external_exports2.string().optional()
|
|
@@ -20295,6 +20324,17 @@ var apiV2PreConsentInventoryKindSchema = external_exports2.enum(["cookie", "trac
|
|
|
20295
20324
|
var apiV2PreConsentInventoryPhaseSchema = external_exports2.literal("pre_consent");
|
|
20296
20325
|
var apiV2PreConsentInventoryPrioritySchema = external_exports2.enum(["high", "medium", "review_needed", "contextual", "unknown"]);
|
|
20297
20326
|
var apiV2PreConsentInventoryConfidenceSchema = external_exports2.enum(["high", "medium", "low", "unknown"]);
|
|
20327
|
+
var apiV2ScanCreationMetadataShape = {
|
|
20328
|
+
executionMode: external_exports2.enum(["new_scan", "reused_scan"]).optional(),
|
|
20329
|
+
reused: external_exports2.boolean().optional(),
|
|
20330
|
+
reusedScanAgeSeconds: external_exports2.number().int().min(0).nullable().optional(),
|
|
20331
|
+
freshnessDecision: external_exports2.string().optional(),
|
|
20332
|
+
quotaConsumed: external_exports2.boolean().optional(),
|
|
20333
|
+
anonymousQuotaLimit: external_exports2.number().int().min(0).nullable().optional(),
|
|
20334
|
+
anonymousQuotaRemaining: external_exports2.number().int().min(0).nullable().optional(),
|
|
20335
|
+
anonymousQuotaResetAt: external_exports2.string().nullable().optional(),
|
|
20336
|
+
recommendedNextTool: external_exports2.enum(["get_scan_status", "get_scan_bundle"]).optional()
|
|
20337
|
+
};
|
|
20298
20338
|
var apiV2LinksSchema = external_exports2.object({
|
|
20299
20339
|
self: external_exports2.string().optional(),
|
|
20300
20340
|
status: external_exports2.string().optional(),
|
|
@@ -20312,7 +20352,8 @@ var apiV2ErrorSchema = external_exports2.object({
|
|
|
20312
20352
|
retryAfterSeconds: external_exports2.number().int().nullable().optional()
|
|
20313
20353
|
}).passthrough(),
|
|
20314
20354
|
links: apiV2LinksSchema.optional(),
|
|
20315
|
-
disclaimer: external_exports2.string().optional()
|
|
20355
|
+
disclaimer: external_exports2.string().optional(),
|
|
20356
|
+
...apiV2ScanCreationMetadataShape
|
|
20316
20357
|
}).passthrough();
|
|
20317
20358
|
var apiV2CreateScanRequestSchema = external_exports2.object({
|
|
20318
20359
|
url: external_exports2.string().min(1),
|
|
@@ -20337,7 +20378,8 @@ var apiV2ScanJobSchema = external_exports2.object({
|
|
|
20337
20378
|
lastUpdatedAt: external_exports2.string().optional(),
|
|
20338
20379
|
retryAfterSeconds: external_exports2.number().int().nullable().optional(),
|
|
20339
20380
|
links: apiV2LinksSchema.optional(),
|
|
20340
|
-
disclaimer: external_exports2.string().optional()
|
|
20381
|
+
disclaimer: external_exports2.string().optional(),
|
|
20382
|
+
...apiV2ScanCreationMetadataShape
|
|
20341
20383
|
}).passthrough();
|
|
20342
20384
|
var apiV2ScanResourceSchema = external_exports2.object({
|
|
20343
20385
|
type: external_exports2.literal("certscore_scan"),
|
|
@@ -20526,6 +20568,13 @@ var mcpCreateScanInputSchema = {
|
|
|
20526
20568
|
freshness: mcpPulseFreshnessSchema.optional().describe("Use latest to reuse recent scans or refresh to request a new scan when eligible."),
|
|
20527
20569
|
scanFrom: mcpScanFromSchema.optional().describe("Optional scan execution context for newly queued scans.")
|
|
20528
20570
|
};
|
|
20571
|
+
var mcpScanSiteInputSchema = {
|
|
20572
|
+
url: mcpCreateScanInputSchema.url,
|
|
20573
|
+
freshness: mcpCreateScanInputSchema.freshness,
|
|
20574
|
+
scanFrom: mcpCreateScanInputSchema.scanFrom,
|
|
20575
|
+
waitForCompletion: external_exports2.boolean().optional().describe("Wait for a completed scan resource in this tool call. Defaults to true. Set false only for an explicitly asynchronous workflow."),
|
|
20576
|
+
maxWaitSeconds: external_exports2.number().int().min(1).max(45).optional().describe("Maximum time to wait before returning the still-running job. Defaults to 45 seconds; never turns an active scan into an error.")
|
|
20577
|
+
};
|
|
20529
20578
|
var mcpGetScanStatusInputSchema = {
|
|
20530
20579
|
jobId: external_exports2.string().min(1).optional().describe("Pulse job ID for a just-created scan that has not yet returned a scanId."),
|
|
20531
20580
|
scanId: external_exports2.string().min(1).optional().describe("Preferred stable CertScore scan ID for API v2 scan status.")
|
|
@@ -20541,6 +20590,11 @@ var mcpGetReportInputSchema = {
|
|
|
20541
20590
|
var mcpGetEvidenceInputSchema = {
|
|
20542
20591
|
scanId: external_exports2.string().min(1).describe("Stable CertScore scan ID.")
|
|
20543
20592
|
};
|
|
20593
|
+
var mcpGetScanBundleInputSchema = {
|
|
20594
|
+
scanId: external_exports2.string().min(1).describe("Stable CertScore scan ID."),
|
|
20595
|
+
maxFindings: external_exports2.number().int().min(1).max(50).optional().describe("Maximum compact findings to return. Defaults to 20."),
|
|
20596
|
+
maxPreConsentRows: external_exports2.number().int().min(1).max(50).optional().describe("Maximum pre-consent inventory rows to return. Defaults to 20.")
|
|
20597
|
+
};
|
|
20544
20598
|
var mcpExportFindingsInputSchema = {
|
|
20545
20599
|
scanId: external_exports2.string().min(1).describe("Stable CertScore scan ID.")
|
|
20546
20600
|
};
|
|
@@ -20634,6 +20688,21 @@ var mcpReportOutputSchema = external_exports2.object({
|
|
|
20634
20688
|
noGo: scanNoGoResultSchema.optional(),
|
|
20635
20689
|
value: external_exports2.string().optional()
|
|
20636
20690
|
}).passthrough();
|
|
20691
|
+
var mcpScanBundleOutputSchema = external_exports2.object({
|
|
20692
|
+
type: external_exports2.literal("certscore_scan_bundle"),
|
|
20693
|
+
scanId: external_exports2.string(),
|
|
20694
|
+
status: apiV2ScanStatusSchema,
|
|
20695
|
+
resultDisposition: scanResultDispositionSchema.nullable().optional(),
|
|
20696
|
+
noGo: scanNoGoResultSchema.nullable().optional(),
|
|
20697
|
+
scan: apiV2ScanResourceSchema,
|
|
20698
|
+
summary: external_exports2.unknown().nullable(),
|
|
20699
|
+
findings: external_exports2.array(external_exports2.unknown()),
|
|
20700
|
+
evidenceSummary: external_exports2.unknown().nullable(),
|
|
20701
|
+
preConsentCookiesTrackers: external_exports2.unknown().nullable(),
|
|
20702
|
+
links: external_exports2.record(external_exports2.string()).optional(),
|
|
20703
|
+
recommendedNextTool: external_exports2.string().nullable(),
|
|
20704
|
+
disclaimer: external_exports2.string().nullable()
|
|
20705
|
+
}).passthrough();
|
|
20637
20706
|
var mcpFindingsExportOutputSchema = external_exports2.object({
|
|
20638
20707
|
type: external_exports2.literal("certscore_mcp_findings_export"),
|
|
20639
20708
|
scanId: external_exports2.string().nullable(),
|
|
@@ -20676,7 +20745,7 @@ var mcpPreConsentCookiesTrackersOutputSchema = apiV2PreConsentCookiesTrackersSch
|
|
|
20676
20745
|
var certScoreMcpToolContracts = [
|
|
20677
20746
|
{
|
|
20678
20747
|
name: "create_scan",
|
|
20679
|
-
title: "Create CertScore Pulse scan",
|
|
20748
|
+
title: "Deprecated \u2014 Create CertScore Pulse scan",
|
|
20680
20749
|
description: "Deprecated compatibility alias of scan_site. Use scan_site for new integrations. Returns completed-limited no-go disposition and reason-specific guidance when applicable.",
|
|
20681
20750
|
inputSchema: mcpCreateScanInputSchema,
|
|
20682
20751
|
outputSchema: mcpCreateScanOutputSchema,
|
|
@@ -20685,8 +20754,8 @@ var certScoreMcpToolContracts = [
|
|
|
20685
20754
|
{
|
|
20686
20755
|
name: "scan_site",
|
|
20687
20756
|
title: "Scan site",
|
|
20688
|
-
description: "
|
|
20689
|
-
inputSchema:
|
|
20757
|
+
description: "Recommended first call. Starts or reuses a public-web scan, reports freshness and anonymous quota decisions, and waits up to 45 seconds by default. If still running, use get_scan_status; otherwise use get_scan_bundle. Completed no-go scans retain completed_limited status and reason-specific guidance.",
|
|
20758
|
+
inputSchema: mcpScanSiteInputSchema,
|
|
20690
20759
|
outputSchema: mcpScanSiteOutputSchema,
|
|
20691
20760
|
annotations: scanCreationAnnotations
|
|
20692
20761
|
},
|
|
@@ -20722,6 +20791,14 @@ var certScoreMcpToolContracts = [
|
|
|
20722
20791
|
outputSchema: pulseResponseSchema,
|
|
20723
20792
|
annotations: readOnlyOpenWorldAnnotations
|
|
20724
20793
|
},
|
|
20794
|
+
{
|
|
20795
|
+
name: "get_scan_bundle",
|
|
20796
|
+
title: "Get compact CertScore scan bundle",
|
|
20797
|
+
description: "Recommended second call after scan_site. Returns the canonical scan state, compact report summary, findings, bounded evidence summary, and pre-consent inventory in one agent-friendly response.",
|
|
20798
|
+
inputSchema: mcpGetScanBundleInputSchema,
|
|
20799
|
+
outputSchema: mcpScanBundleOutputSchema,
|
|
20800
|
+
annotations: readOnlyOpenWorldAnnotations
|
|
20801
|
+
},
|
|
20725
20802
|
{
|
|
20726
20803
|
name: "export_findings",
|
|
20727
20804
|
title: "Export CertScore findings",
|
|
@@ -32934,6 +33011,71 @@ function limitPreConsentRows(payload, options = {}) {
|
|
|
32934
33011
|
}
|
|
32935
33012
|
};
|
|
32936
33013
|
}
|
|
33014
|
+
function buildScanBundle(input) {
|
|
33015
|
+
const maxFindings = Math.min(50, Math.max(1, input.maxFindings ?? 20));
|
|
33016
|
+
const maxPreConsentRows = Math.min(50, Math.max(1, input.maxPreConsentRows ?? 20));
|
|
33017
|
+
const evidence = input.evidence;
|
|
33018
|
+
const report = input.report;
|
|
33019
|
+
const findings = Array.isArray(input.findings.findings) ? input.findings.findings.slice(0, maxFindings) : [];
|
|
33020
|
+
const preConsentRows = Array.isArray(input.preConsentCookiesTrackers.rows) ? input.preConsentCookiesTrackers.rows.slice(0, maxPreConsentRows) : [];
|
|
33021
|
+
const links = {
|
|
33022
|
+
...input.scan.links ?? {},
|
|
33023
|
+
...report.links && typeof report.links === "object" && !Array.isArray(report.links) ? report.links : {}
|
|
33024
|
+
};
|
|
33025
|
+
return {
|
|
33026
|
+
type: "certscore_scan_bundle",
|
|
33027
|
+
scanId: input.scan.scanId,
|
|
33028
|
+
status: input.scan.status,
|
|
33029
|
+
resultDisposition: input.scan.resultDisposition ?? null,
|
|
33030
|
+
noGo: input.scan.noGo ?? null,
|
|
33031
|
+
scan: input.scan,
|
|
33032
|
+
summary: {
|
|
33033
|
+
summary: report.summary ?? null,
|
|
33034
|
+
executiveSummary: report.executiveSummary ?? null,
|
|
33035
|
+
counts: report.counts ?? null,
|
|
33036
|
+
coverage: report.coverage ?? input.scan.coverage ?? null,
|
|
33037
|
+
agentInterpretation: report.agentInterpretation ?? null
|
|
33038
|
+
},
|
|
33039
|
+
findings,
|
|
33040
|
+
findingsMetadata: {
|
|
33041
|
+
shown: findings.length,
|
|
33042
|
+
total: Array.isArray(input.findings.findings) ? input.findings.findings.length : 0,
|
|
33043
|
+
truncated: Array.isArray(input.findings.findings) && input.findings.findings.length > findings.length
|
|
33044
|
+
},
|
|
33045
|
+
evidenceSummary: {
|
|
33046
|
+
evidenceSafetyNotes: evidence.evidenceSafetyNotes ?? null,
|
|
33047
|
+
projectionDiagnostics: evidence.projectionDiagnostics ?? null,
|
|
33048
|
+
projectedFindings: compactEvidenceValue(evidence.projectedFindings ?? [], {
|
|
33049
|
+
arrayItems: maxFindings,
|
|
33050
|
+
depth: 5,
|
|
33051
|
+
objectKeys: 40,
|
|
33052
|
+
stringChars: 1e3
|
|
33053
|
+
}),
|
|
33054
|
+
coverageDiagnostics: compactEvidenceValue(evidence.coverageDiagnostics ?? null, {
|
|
33055
|
+
arrayItems: 20,
|
|
33056
|
+
depth: 5,
|
|
33057
|
+
objectKeys: 40,
|
|
33058
|
+
stringChars: 1e3
|
|
33059
|
+
}),
|
|
33060
|
+
policySurfaceCoverage: compactEvidenceValue(evidence.policySurfaceCoverage ?? null, {
|
|
33061
|
+
arrayItems: 20,
|
|
33062
|
+
depth: 5,
|
|
33063
|
+
objectKeys: 40,
|
|
33064
|
+
stringChars: 1e3
|
|
33065
|
+
})
|
|
33066
|
+
},
|
|
33067
|
+
preConsentCookiesTrackers: {
|
|
33068
|
+
summary: input.preConsentCookiesTrackers.summary,
|
|
33069
|
+
rows: preConsentRows,
|
|
33070
|
+
shown: preConsentRows.length,
|
|
33071
|
+
total: Array.isArray(input.preConsentCookiesTrackers.rows) ? input.preConsentCookiesTrackers.rows.length : 0,
|
|
33072
|
+
truncated: Array.isArray(input.preConsentCookiesTrackers.rows) && input.preConsentCookiesTrackers.rows.length > preConsentRows.length
|
|
33073
|
+
},
|
|
33074
|
+
links,
|
|
33075
|
+
recommendedNextTool: findings.length > 0 ? "explain_finding" : null,
|
|
33076
|
+
disclaimer: input.scan.disclaimer ?? input.report.disclaimer ?? null
|
|
33077
|
+
};
|
|
33078
|
+
}
|
|
32937
33079
|
function explainFinding(report, findingId) {
|
|
32938
33080
|
const finding = findingsFromReport(report).find((candidate) => candidate.id === findingId);
|
|
32939
33081
|
if (!finding) {
|
|
@@ -32971,6 +33113,19 @@ function explainFinding(report, findingId) {
|
|
|
32971
33113
|
|
|
32972
33114
|
// src/server.ts
|
|
32973
33115
|
var createScanDeprecationWarningPrinted = false;
|
|
33116
|
+
var DEFAULT_MCP_SCAN_WAIT_MS = 45e3;
|
|
33117
|
+
function scanCreationMetadata(value) {
|
|
33118
|
+
return {
|
|
33119
|
+
executionMode: value.executionMode,
|
|
33120
|
+
reused: value.reused,
|
|
33121
|
+
reusedScanAgeSeconds: value.reusedScanAgeSeconds,
|
|
33122
|
+
freshnessDecision: value.freshnessDecision,
|
|
33123
|
+
quotaConsumed: value.quotaConsumed,
|
|
33124
|
+
anonymousQuotaLimit: value.anonymousQuotaLimit,
|
|
33125
|
+
anonymousQuotaRemaining: value.anonymousQuotaRemaining,
|
|
33126
|
+
anonymousQuotaResetAt: value.anonymousQuotaResetAt
|
|
33127
|
+
};
|
|
33128
|
+
}
|
|
32974
33129
|
function toolContract(name) {
|
|
32975
33130
|
const contract = certScoreMcpToolContracts.find((candidate) => candidate.name === name);
|
|
32976
33131
|
if (!contract) {
|
|
@@ -32988,6 +33143,9 @@ function createCertScoreMcpServer(options = {}) {
|
|
|
32988
33143
|
const client = new CertScoreClient({
|
|
32989
33144
|
apiKey: options.apiKey,
|
|
32990
33145
|
baseUrl: options.baseUrl,
|
|
33146
|
+
clientName: "mcp",
|
|
33147
|
+
forwardedClientIp: options.forwardedClientIp,
|
|
33148
|
+
anonymousRequesterSecret: options.anonymousRequesterSecret,
|
|
32991
33149
|
timeout: options.timeout
|
|
32992
33150
|
});
|
|
32993
33151
|
const server = new McpServer({
|
|
@@ -33023,7 +33181,7 @@ function createCertScoreMcpServer(options = {}) {
|
|
|
33023
33181
|
try {
|
|
33024
33182
|
if (!createScanDeprecationWarningPrinted) {
|
|
33025
33183
|
createScanDeprecationWarningPrinted = true;
|
|
33026
|
-
console.error("[certscore-mcp] create_scan is deprecated
|
|
33184
|
+
console.error("[certscore-mcp] create_scan is deprecated in the 0.2.x line. Use scan_site for new integrations.");
|
|
33027
33185
|
}
|
|
33028
33186
|
return toToolResult(await createPulseScanTool(input));
|
|
33029
33187
|
} catch (error2) {
|
|
@@ -33036,12 +33194,36 @@ function createCertScoreMcpServer(options = {}) {
|
|
|
33036
33194
|
toolContract("scan_site"),
|
|
33037
33195
|
async (input) => {
|
|
33038
33196
|
try {
|
|
33039
|
-
|
|
33040
|
-
|
|
33041
|
-
|
|
33042
|
-
|
|
33043
|
-
|
|
33044
|
-
|
|
33197
|
+
const created = await client.scans.create(input.url, {
|
|
33198
|
+
freshness: input.freshness ?? "latest",
|
|
33199
|
+
scanFrom: input.scanFrom
|
|
33200
|
+
});
|
|
33201
|
+
if (input.waitForCompletion === false || created.type === "certscore_scan") {
|
|
33202
|
+
return toToolResult(created);
|
|
33203
|
+
}
|
|
33204
|
+
try {
|
|
33205
|
+
const completed = await client.scans.wait(created, {
|
|
33206
|
+
maxWaitMs: Math.min(input.maxWaitSeconds ? input.maxWaitSeconds * 1e3 : DEFAULT_MCP_SCAN_WAIT_MS, DEFAULT_MCP_SCAN_WAIT_MS)
|
|
33207
|
+
});
|
|
33208
|
+
return toToolResult({
|
|
33209
|
+
...completed,
|
|
33210
|
+
...scanCreationMetadata(created),
|
|
33211
|
+
recommendedNextTool: "get_scan_bundle"
|
|
33212
|
+
});
|
|
33213
|
+
} catch (error2) {
|
|
33214
|
+
if (!(error2 instanceof CertScoreTimeoutError)) {
|
|
33215
|
+
throw error2;
|
|
33216
|
+
}
|
|
33217
|
+
const scanId = created.scanId ?? created.scan_id;
|
|
33218
|
+
if (scanId) {
|
|
33219
|
+
return toToolResult({
|
|
33220
|
+
...await client.scans.status(scanId),
|
|
33221
|
+
...scanCreationMetadata(created),
|
|
33222
|
+
recommendedNextTool: "get_scan_status"
|
|
33223
|
+
});
|
|
33224
|
+
}
|
|
33225
|
+
return toToolResult(created);
|
|
33226
|
+
}
|
|
33045
33227
|
} catch (error2) {
|
|
33046
33228
|
return toToolError(error2);
|
|
33047
33229
|
}
|
|
@@ -33064,7 +33246,27 @@ function createCertScoreMcpServer(options = {}) {
|
|
|
33064
33246
|
async ({ jobId, scanId }) => {
|
|
33065
33247
|
try {
|
|
33066
33248
|
if (scanId) {
|
|
33067
|
-
|
|
33249
|
+
const status2 = await client.scans.status(scanId);
|
|
33250
|
+
const needsTerminalHydration = status2.status === "completed" || status2.status === "completed_limited";
|
|
33251
|
+
if (needsTerminalHydration) {
|
|
33252
|
+
try {
|
|
33253
|
+
const scan = await client.scans.get(scanId);
|
|
33254
|
+
return toToolResult({
|
|
33255
|
+
...status2,
|
|
33256
|
+
type: "certscore_scan_job",
|
|
33257
|
+
status: scan.status,
|
|
33258
|
+
domain: scan.domain,
|
|
33259
|
+
resultDisposition: scan.resultDisposition,
|
|
33260
|
+
noGo: scan.noGo,
|
|
33261
|
+
startedAt: scan.startedAt,
|
|
33262
|
+
completedAt: scan.completedAt,
|
|
33263
|
+
scanTimeSeconds: scan.scanTimeSeconds,
|
|
33264
|
+
recommendedNextTool: "get_scan_bundle"
|
|
33265
|
+
});
|
|
33266
|
+
} catch {
|
|
33267
|
+
}
|
|
33268
|
+
}
|
|
33269
|
+
return toToolResult(status2);
|
|
33068
33270
|
}
|
|
33069
33271
|
if (!jobId) {
|
|
33070
33272
|
return toToolResult({
|
|
@@ -33114,6 +33316,32 @@ function createCertScoreMcpServer(options = {}) {
|
|
|
33114
33316
|
}
|
|
33115
33317
|
}
|
|
33116
33318
|
);
|
|
33319
|
+
registerTool(
|
|
33320
|
+
"get_scan_bundle",
|
|
33321
|
+
toolContract("get_scan_bundle"),
|
|
33322
|
+
async ({ scanId, maxFindings, maxPreConsentRows }) => {
|
|
33323
|
+
try {
|
|
33324
|
+
const [scan, report, evidence, findings, preConsentCookiesTrackers] = await Promise.all([
|
|
33325
|
+
client.scans.get(scanId),
|
|
33326
|
+
client.getScan(scanId, { detail: "summary", format: "json" }),
|
|
33327
|
+
client.getScan(scanId, { detail: "evidence", format: "json" }),
|
|
33328
|
+
client.findings.list(scanId),
|
|
33329
|
+
client.scans.preConsentCookiesTrackers(scanId)
|
|
33330
|
+
]);
|
|
33331
|
+
return toToolResult(buildScanBundle({
|
|
33332
|
+
evidence,
|
|
33333
|
+
findings,
|
|
33334
|
+
maxFindings,
|
|
33335
|
+
maxPreConsentRows,
|
|
33336
|
+
preConsentCookiesTrackers,
|
|
33337
|
+
report,
|
|
33338
|
+
scan
|
|
33339
|
+
}));
|
|
33340
|
+
} catch (error2) {
|
|
33341
|
+
return toToolError(error2);
|
|
33342
|
+
}
|
|
33343
|
+
}
|
|
33344
|
+
);
|
|
33117
33345
|
registerTool(
|
|
33118
33346
|
"export_findings",
|
|
33119
33347
|
toolContract("export_findings"),
|
|
@@ -33213,12 +33441,13 @@ async function main() {
|
|
|
33213
33441
|
"",
|
|
33214
33442
|
"Usage:",
|
|
33215
33443
|
" certscore-mcp",
|
|
33216
|
-
" certscore-mcp doctor"
|
|
33444
|
+
" certscore-mcp doctor",
|
|
33445
|
+
" certscore-mcp doctor --check-auth"
|
|
33217
33446
|
].join("\n"));
|
|
33218
33447
|
return;
|
|
33219
33448
|
}
|
|
33220
33449
|
if (process.argv.includes("doctor")) {
|
|
33221
|
-
const result = await getCertScoreMcpDoctorReport();
|
|
33450
|
+
const result = await getCertScoreMcpDoctorReport({ checkAuth: process.argv.includes("--check-auth") });
|
|
33222
33451
|
console.log(result.lines.join("\n"));
|
|
33223
33452
|
process.exitCode = result.exitCode;
|
|
33224
33453
|
return;
|
|
@@ -33274,10 +33503,41 @@ async function getCertScoreMcpDoctorReport(options = {}) {
|
|
|
33274
33503
|
}
|
|
33275
33504
|
if (env.CERTSCORE_API_KEY?.trim()) {
|
|
33276
33505
|
lines.push("[ok] CERTSCORE_API_KEY is present");
|
|
33277
|
-
|
|
33506
|
+
if (options.checkAuth) {
|
|
33507
|
+
if (!healthUrl) {
|
|
33508
|
+
lines.push("[error] Cannot check credentials until CERTSCORE_BASE_URL is valid");
|
|
33509
|
+
exitCode = 1;
|
|
33510
|
+
} else {
|
|
33511
|
+
const authUrl = new URL("/api/v2/auth/check", healthUrl.origin);
|
|
33512
|
+
try {
|
|
33513
|
+
const response = await fetchImpl(authUrl, {
|
|
33514
|
+
headers: {
|
|
33515
|
+
accept: "application/json",
|
|
33516
|
+
authorization: `Bearer ${env.CERTSCORE_API_KEY.trim()}`
|
|
33517
|
+
},
|
|
33518
|
+
signal: AbortSignal.timeout(1e4)
|
|
33519
|
+
});
|
|
33520
|
+
if (response.ok) {
|
|
33521
|
+
lines.push(`[ok] API key authenticated at ${authUrl.href}`);
|
|
33522
|
+
} else {
|
|
33523
|
+
lines.push(`[error] API key rejected with HTTP ${response.status} at ${authUrl.href}`);
|
|
33524
|
+
exitCode = 1;
|
|
33525
|
+
}
|
|
33526
|
+
} catch (error2) {
|
|
33527
|
+
const message = error2 instanceof Error && error2.message ? error2.message : "request failed";
|
|
33528
|
+
lines.push(`[error] API key check failed at ${authUrl.href}: ${message}`);
|
|
33529
|
+
exitCode = 1;
|
|
33530
|
+
}
|
|
33531
|
+
}
|
|
33532
|
+
} else {
|
|
33533
|
+
lines.push("[info] Run certscore-mcp doctor --check-auth to verify the credential without creating a scan.");
|
|
33534
|
+
}
|
|
33278
33535
|
} else {
|
|
33279
33536
|
lines.push("[warn] CERTSCORE_API_KEY is not set");
|
|
33280
|
-
lines.push("[info] Set CERTSCORE_API_KEY before connecting an MCP client or calling authenticated tools.");
|
|
33537
|
+
lines.push(options.checkAuth ? "[error] --check-auth requires CERTSCORE_API_KEY." : "[info] Set CERTSCORE_API_KEY before connecting an MCP client or calling authenticated tools.");
|
|
33538
|
+
if (options.checkAuth) {
|
|
33539
|
+
exitCode = 1;
|
|
33540
|
+
}
|
|
33281
33541
|
}
|
|
33282
33542
|
lines.push("CertScore outputs are automated public-web observations for review, not legal advice, certification, or a compliance determination.");
|
|
33283
33543
|
return { exitCode, lines };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { createCertScoreMcpServer } from "./server.js";
|
|
|
2
2
|
export { explainFinding, exportFindings, findingsFromReport } from "./tools.js";
|
|
3
3
|
export { CERTSCORE_MCP_VERSION } from "./version.js";
|
|
4
4
|
export interface CertScoreMcpDoctorOptions {
|
|
5
|
+
checkAuth?: boolean;
|
|
5
6
|
env?: Record<string, string | undefined>;
|
|
6
7
|
fetch?: typeof fetch;
|
|
7
8
|
nodeVersion?: string;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChF,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAMrD,MAAM,WAAW,yBAAyB;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChF,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAMrD,MAAM,WAAW,yBAAyB;IACxC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAgDD,wBAAsB,2BAA2B,CAAC,OAAO,GAAE,yBAA8B;;;GAyFxF"}
|
package/dist/index.js
CHANGED
|
@@ -34,12 +34,13 @@ async function main() {
|
|
|
34
34
|
"",
|
|
35
35
|
"Usage:",
|
|
36
36
|
" certscore-mcp",
|
|
37
|
-
" certscore-mcp doctor"
|
|
37
|
+
" certscore-mcp doctor",
|
|
38
|
+
" certscore-mcp doctor --check-auth"
|
|
38
39
|
].join("\n"));
|
|
39
40
|
return;
|
|
40
41
|
}
|
|
41
42
|
if (process.argv.includes("doctor")) {
|
|
42
|
-
const result = await getCertScoreMcpDoctorReport();
|
|
43
|
+
const result = await getCertScoreMcpDoctorReport({ checkAuth: process.argv.includes("--check-auth") });
|
|
43
44
|
console.log(result.lines.join("\n"));
|
|
44
45
|
process.exitCode = result.exitCode;
|
|
45
46
|
return;
|
|
@@ -99,11 +100,46 @@ export async function getCertScoreMcpDoctorReport(options = {}) {
|
|
|
99
100
|
}
|
|
100
101
|
if (env.CERTSCORE_API_KEY?.trim()) {
|
|
101
102
|
lines.push("[ok] CERTSCORE_API_KEY is present");
|
|
102
|
-
|
|
103
|
+
if (options.checkAuth) {
|
|
104
|
+
if (!healthUrl) {
|
|
105
|
+
lines.push("[error] Cannot check credentials until CERTSCORE_BASE_URL is valid");
|
|
106
|
+
exitCode = 1;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
const authUrl = new URL("/api/v2/auth/check", healthUrl.origin);
|
|
110
|
+
try {
|
|
111
|
+
const response = await fetchImpl(authUrl, {
|
|
112
|
+
headers: {
|
|
113
|
+
accept: "application/json",
|
|
114
|
+
authorization: `Bearer ${env.CERTSCORE_API_KEY.trim()}`
|
|
115
|
+
},
|
|
116
|
+
signal: AbortSignal.timeout(10_000)
|
|
117
|
+
});
|
|
118
|
+
if (response.ok) {
|
|
119
|
+
lines.push(`[ok] API key authenticated at ${authUrl.href}`);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
lines.push(`[error] API key rejected with HTTP ${response.status} at ${authUrl.href}`);
|
|
123
|
+
exitCode = 1;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
const message = error instanceof Error && error.message ? error.message : "request failed";
|
|
128
|
+
lines.push(`[error] API key check failed at ${authUrl.href}: ${message}`);
|
|
129
|
+
exitCode = 1;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
lines.push("[info] Run certscore-mcp doctor --check-auth to verify the credential without creating a scan.");
|
|
135
|
+
}
|
|
103
136
|
}
|
|
104
137
|
else {
|
|
105
138
|
lines.push("[warn] CERTSCORE_API_KEY is not set");
|
|
106
|
-
lines.push("[info] Set CERTSCORE_API_KEY before connecting an MCP client or calling authenticated tools.");
|
|
139
|
+
lines.push(options.checkAuth ? "[error] --check-auth requires CERTSCORE_API_KEY." : "[info] Set CERTSCORE_API_KEY before connecting an MCP client or calling authenticated tools.");
|
|
140
|
+
if (options.checkAuth) {
|
|
141
|
+
exitCode = 1;
|
|
142
|
+
}
|
|
107
143
|
}
|
|
108
144
|
lines.push("CertScore outputs are automated public-web observations for review, not legal advice, certification, or a compliance determination.");
|
|
109
145
|
return { exitCode, lines };
|
package/dist/server.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
export interface CertScoreMcpOptions {
|
|
3
3
|
apiKey?: string;
|
|
4
4
|
baseUrl?: string;
|
|
5
|
+
forwardedClientIp?: string | null;
|
|
6
|
+
anonymousRequesterSecret?: string | null;
|
|
5
7
|
timeout?: number;
|
|
6
8
|
}
|
|
7
9
|
export declare function createCertScoreMcpServer(options?: CertScoreMcpOptions): McpServer;
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,wBAAwB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAsDD,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,mBAAwB,aAoSzE"}
|
package/dist/server.js
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
|
-
import { CertScoreClient } from "@certscore/sdk";
|
|
1
|
+
import { CertScoreClient, CertScoreTimeoutError } from "@certscore/sdk";
|
|
2
2
|
import { certScoreMcpToolContracts } from "@certscore/api-contracts";
|
|
3
3
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
4
|
import { CERTSCORE_MCP_VERSION } from "./version.js";
|
|
5
|
-
import { boundEvidencePacket, exportFindings, limitPreConsentRows, normalizeDetail, normalizeFormat, paginateFindingList, scanIdFromStatus, toToolError, toToolResult } from "./tools.js";
|
|
5
|
+
import { boundEvidencePacket, buildScanBundle, exportFindings, limitPreConsentRows, normalizeDetail, normalizeFormat, paginateFindingList, scanIdFromStatus, toToolError, toToolResult } from "./tools.js";
|
|
6
6
|
let createScanDeprecationWarningPrinted = false;
|
|
7
|
+
const DEFAULT_MCP_SCAN_WAIT_MS = 45_000;
|
|
8
|
+
function scanCreationMetadata(value) {
|
|
9
|
+
return {
|
|
10
|
+
executionMode: value.executionMode,
|
|
11
|
+
reused: value.reused,
|
|
12
|
+
reusedScanAgeSeconds: value.reusedScanAgeSeconds,
|
|
13
|
+
freshnessDecision: value.freshnessDecision,
|
|
14
|
+
quotaConsumed: value.quotaConsumed,
|
|
15
|
+
anonymousQuotaLimit: value.anonymousQuotaLimit,
|
|
16
|
+
anonymousQuotaRemaining: value.anonymousQuotaRemaining,
|
|
17
|
+
anonymousQuotaResetAt: value.anonymousQuotaResetAt
|
|
18
|
+
};
|
|
19
|
+
}
|
|
7
20
|
function toolContract(name) {
|
|
8
21
|
const contract = certScoreMcpToolContracts.find((candidate) => candidate.name === name);
|
|
9
22
|
if (!contract) {
|
|
@@ -21,6 +34,9 @@ export function createCertScoreMcpServer(options = {}) {
|
|
|
21
34
|
const client = new CertScoreClient({
|
|
22
35
|
apiKey: options.apiKey,
|
|
23
36
|
baseUrl: options.baseUrl,
|
|
37
|
+
clientName: "mcp",
|
|
38
|
+
forwardedClientIp: options.forwardedClientIp,
|
|
39
|
+
anonymousRequesterSecret: options.anonymousRequesterSecret,
|
|
24
40
|
timeout: options.timeout
|
|
25
41
|
});
|
|
26
42
|
const server = new McpServer({
|
|
@@ -53,7 +69,7 @@ export function createCertScoreMcpServer(options = {}) {
|
|
|
53
69
|
try {
|
|
54
70
|
if (!createScanDeprecationWarningPrinted) {
|
|
55
71
|
createScanDeprecationWarningPrinted = true;
|
|
56
|
-
console.error("[certscore-mcp] create_scan is deprecated
|
|
72
|
+
console.error("[certscore-mcp] create_scan is deprecated in the 0.2.x line. Use scan_site for new integrations.");
|
|
57
73
|
}
|
|
58
74
|
return toToolResult(await createPulseScanTool(input));
|
|
59
75
|
}
|
|
@@ -63,10 +79,37 @@ export function createCertScoreMcpServer(options = {}) {
|
|
|
63
79
|
});
|
|
64
80
|
registerTool("scan_site", toolContract("scan_site"), async (input) => {
|
|
65
81
|
try {
|
|
66
|
-
|
|
82
|
+
const created = await client.scans.create(input.url, {
|
|
67
83
|
freshness: input.freshness ?? "latest",
|
|
68
84
|
scanFrom: input.scanFrom
|
|
69
|
-
})
|
|
85
|
+
});
|
|
86
|
+
if (input.waitForCompletion === false || created.type === "certscore_scan") {
|
|
87
|
+
return toToolResult(created);
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const completed = await client.scans.wait(created, {
|
|
91
|
+
maxWaitMs: Math.min(input.maxWaitSeconds ? input.maxWaitSeconds * 1_000 : DEFAULT_MCP_SCAN_WAIT_MS, DEFAULT_MCP_SCAN_WAIT_MS)
|
|
92
|
+
});
|
|
93
|
+
return toToolResult({
|
|
94
|
+
...completed,
|
|
95
|
+
...scanCreationMetadata(created),
|
|
96
|
+
recommendedNextTool: "get_scan_bundle"
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
if (!(error instanceof CertScoreTimeoutError)) {
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
const scanId = created.scanId ?? created.scan_id;
|
|
104
|
+
if (scanId) {
|
|
105
|
+
return toToolResult({
|
|
106
|
+
...(await client.scans.status(scanId)),
|
|
107
|
+
...scanCreationMetadata(created),
|
|
108
|
+
recommendedNextTool: "get_scan_status"
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return toToolResult(created);
|
|
112
|
+
}
|
|
70
113
|
}
|
|
71
114
|
catch (error) {
|
|
72
115
|
return toToolError(error);
|
|
@@ -83,7 +126,30 @@ export function createCertScoreMcpServer(options = {}) {
|
|
|
83
126
|
registerTool("get_scan_status", toolContract("get_scan_status"), async ({ jobId, scanId }) => {
|
|
84
127
|
try {
|
|
85
128
|
if (scanId) {
|
|
86
|
-
|
|
129
|
+
const status = await client.scans.status(scanId);
|
|
130
|
+
const needsTerminalHydration = status.status === "completed" || status.status === "completed_limited";
|
|
131
|
+
if (needsTerminalHydration) {
|
|
132
|
+
try {
|
|
133
|
+
const scan = await client.scans.get(scanId);
|
|
134
|
+
return toToolResult({
|
|
135
|
+
...status,
|
|
136
|
+
type: "certscore_scan_job",
|
|
137
|
+
status: scan.status,
|
|
138
|
+
domain: scan.domain,
|
|
139
|
+
resultDisposition: scan.resultDisposition,
|
|
140
|
+
noGo: scan.noGo,
|
|
141
|
+
startedAt: scan.startedAt,
|
|
142
|
+
completedAt: scan.completedAt,
|
|
143
|
+
scanTimeSeconds: scan.scanTimeSeconds,
|
|
144
|
+
recommendedNextTool: "get_scan_bundle"
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
// Preserve the API status response if the terminal scan resource is
|
|
149
|
+
// briefly unavailable during eventual-consistency windows.
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return toToolResult(status);
|
|
87
153
|
}
|
|
88
154
|
if (!jobId) {
|
|
89
155
|
return toToolResult({
|
|
@@ -129,6 +195,29 @@ export function createCertScoreMcpServer(options = {}) {
|
|
|
129
195
|
return toToolError(error);
|
|
130
196
|
}
|
|
131
197
|
});
|
|
198
|
+
registerTool("get_scan_bundle", toolContract("get_scan_bundle"), async ({ scanId, maxFindings, maxPreConsentRows }) => {
|
|
199
|
+
try {
|
|
200
|
+
const [scan, report, evidence, findings, preConsentCookiesTrackers] = await Promise.all([
|
|
201
|
+
client.scans.get(scanId),
|
|
202
|
+
client.getScan(scanId, { detail: "summary", format: "json" }),
|
|
203
|
+
client.getScan(scanId, { detail: "evidence", format: "json" }),
|
|
204
|
+
client.findings.list(scanId),
|
|
205
|
+
client.scans.preConsentCookiesTrackers(scanId)
|
|
206
|
+
]);
|
|
207
|
+
return toToolResult(buildScanBundle({
|
|
208
|
+
evidence,
|
|
209
|
+
findings,
|
|
210
|
+
maxFindings,
|
|
211
|
+
maxPreConsentRows,
|
|
212
|
+
preConsentCookiesTrackers,
|
|
213
|
+
report,
|
|
214
|
+
scan
|
|
215
|
+
}));
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
return toToolError(error);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
132
221
|
registerTool("export_findings", toolContract("export_findings"), async ({ scanId }) => {
|
|
133
222
|
try {
|
|
134
223
|
const report = await client.getScan(scanId, { detail: "full", format: "json" });
|
package/dist/tools.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
-
import { type JobStatus, type PulseDetail, type PulseFormat, type PulseResult, type TopFinding } from "@certscore/sdk";
|
|
2
|
+
import { type FindingList, type JobStatus, type PreConsentCookiesTrackers, type PulseDetail, type PulseFormat, type PulseResult, type ScanResource, type TopFinding } from "@certscore/sdk";
|
|
3
3
|
export declare const MAX_EVIDENCE_PACKET_CHARS = 250000;
|
|
4
4
|
export declare function toToolResult(payload: unknown): CallToolResult;
|
|
5
5
|
export declare function toToolError(error: unknown): CallToolResult;
|
|
@@ -37,6 +37,69 @@ export declare function paginateFindingList<T extends Record<string, unknown>>(p
|
|
|
37
37
|
export declare function limitPreConsentRows<T extends Record<string, unknown>>(payload: T, options?: {
|
|
38
38
|
maxRows?: number;
|
|
39
39
|
}): T;
|
|
40
|
+
export declare function buildScanBundle(input: {
|
|
41
|
+
evidence: PulseResult;
|
|
42
|
+
findings: FindingList;
|
|
43
|
+
maxFindings?: number;
|
|
44
|
+
maxPreConsentRows?: number;
|
|
45
|
+
preConsentCookiesTrackers: PreConsentCookiesTrackers;
|
|
46
|
+
report: PulseResult;
|
|
47
|
+
scan: ScanResource;
|
|
48
|
+
}): {
|
|
49
|
+
type: string;
|
|
50
|
+
scanId: string;
|
|
51
|
+
status: import("packages/certscore-sdk/dist/types.js").PulseJobStatus;
|
|
52
|
+
resultDisposition: "no_go" | null;
|
|
53
|
+
noGo: import("@certscore/sdk").ScanNoGoResult | null;
|
|
54
|
+
scan: ScanResource;
|
|
55
|
+
summary: {
|
|
56
|
+
summary: {} | null;
|
|
57
|
+
executiveSummary: {} | null;
|
|
58
|
+
counts: {} | null;
|
|
59
|
+
coverage: {} | null;
|
|
60
|
+
agentInterpretation: {} | null;
|
|
61
|
+
};
|
|
62
|
+
findings: import("packages/certscore-sdk/dist/types.js").FindingSummary[];
|
|
63
|
+
findingsMetadata: {
|
|
64
|
+
shown: number;
|
|
65
|
+
total: number;
|
|
66
|
+
truncated: boolean;
|
|
67
|
+
};
|
|
68
|
+
evidenceSummary: {
|
|
69
|
+
evidenceSafetyNotes: {} | null;
|
|
70
|
+
projectionDiagnostics: {} | null;
|
|
71
|
+
projectedFindings: unknown;
|
|
72
|
+
coverageDiagnostics: unknown;
|
|
73
|
+
policySurfaceCoverage: unknown;
|
|
74
|
+
};
|
|
75
|
+
preConsentCookiesTrackers: {
|
|
76
|
+
summary: {
|
|
77
|
+
[key: string]: unknown;
|
|
78
|
+
rowCount: number;
|
|
79
|
+
trackerCount: number;
|
|
80
|
+
cookieCount: number;
|
|
81
|
+
requestCount: number;
|
|
82
|
+
totalRowCount?: number;
|
|
83
|
+
truncated?: boolean;
|
|
84
|
+
};
|
|
85
|
+
rows: import("packages/certscore-sdk/dist/types.js").PreConsentCookiesTrackersRow[];
|
|
86
|
+
shown: number;
|
|
87
|
+
total: number;
|
|
88
|
+
truncated: boolean;
|
|
89
|
+
};
|
|
90
|
+
links: {
|
|
91
|
+
[key: string]: string | undefined;
|
|
92
|
+
self?: string;
|
|
93
|
+
status?: string;
|
|
94
|
+
findings?: string;
|
|
95
|
+
pulse?: string;
|
|
96
|
+
report?: string;
|
|
97
|
+
latestDomainScan?: string;
|
|
98
|
+
docs?: string;
|
|
99
|
+
};
|
|
100
|
+
recommendedNextTool: string | null;
|
|
101
|
+
disclaimer: string | null;
|
|
102
|
+
};
|
|
40
103
|
export declare function explainFinding(report: PulseResult, findingId: string): {
|
|
41
104
|
type: string;
|
|
42
105
|
scanId: string | null;
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,EAAkB,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,EAAkB,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,yBAAyB,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAG5M,eAAO,MAAM,yBAAyB,SAAU,CAAC;AAKjD,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CAa7D;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CA0B1D;AAED,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,kBAAkB,SAA4B,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA0C9H;AA6ID,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,WAAW,CAE5E;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,WAAW,CAE5E;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,iBAEjD;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,iBAGlD;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,UAAU,EAAE,CAQpE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW;;;;;;;;;;;;;;;;;;;;EAsBjD;AAED,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnE,OAAO,EAAE,CAAC,EACV,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAChD,CAAC,CAoBH;AAED,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACnE,OAAO,EAAE,CAAC,EACV,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GACjC,CAAC,CAiBH;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACrC,QAAQ,EAAE,WAAW,CAAC;IACtB,QAAQ,EAAE,WAAW,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,yBAAyB,EAAE,yBAAyB,CAAC;IACrD,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,YAAY,CAAC;CACpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmEA;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiCpE"}
|
package/dist/tools.js
CHANGED
|
@@ -297,6 +297,73 @@ export function limitPreConsentRows(payload, options = {}) {
|
|
|
297
297
|
}
|
|
298
298
|
};
|
|
299
299
|
}
|
|
300
|
+
export function buildScanBundle(input) {
|
|
301
|
+
const maxFindings = Math.min(50, Math.max(1, input.maxFindings ?? 20));
|
|
302
|
+
const maxPreConsentRows = Math.min(50, Math.max(1, input.maxPreConsentRows ?? 20));
|
|
303
|
+
const evidence = input.evidence;
|
|
304
|
+
const report = input.report;
|
|
305
|
+
const findings = Array.isArray(input.findings.findings) ? input.findings.findings.slice(0, maxFindings) : [];
|
|
306
|
+
const preConsentRows = Array.isArray(input.preConsentCookiesTrackers.rows)
|
|
307
|
+
? input.preConsentCookiesTrackers.rows.slice(0, maxPreConsentRows)
|
|
308
|
+
: [];
|
|
309
|
+
const links = {
|
|
310
|
+
...(input.scan.links ?? {}),
|
|
311
|
+
...(report.links && typeof report.links === "object" && !Array.isArray(report.links) ? report.links : {})
|
|
312
|
+
};
|
|
313
|
+
return {
|
|
314
|
+
type: "certscore_scan_bundle",
|
|
315
|
+
scanId: input.scan.scanId,
|
|
316
|
+
status: input.scan.status,
|
|
317
|
+
resultDisposition: input.scan.resultDisposition ?? null,
|
|
318
|
+
noGo: input.scan.noGo ?? null,
|
|
319
|
+
scan: input.scan,
|
|
320
|
+
summary: {
|
|
321
|
+
summary: report.summary ?? null,
|
|
322
|
+
executiveSummary: report.executiveSummary ?? null,
|
|
323
|
+
counts: report.counts ?? null,
|
|
324
|
+
coverage: report.coverage ?? input.scan.coverage ?? null,
|
|
325
|
+
agentInterpretation: report.agentInterpretation ?? null
|
|
326
|
+
},
|
|
327
|
+
findings,
|
|
328
|
+
findingsMetadata: {
|
|
329
|
+
shown: findings.length,
|
|
330
|
+
total: Array.isArray(input.findings.findings) ? input.findings.findings.length : 0,
|
|
331
|
+
truncated: Array.isArray(input.findings.findings) && input.findings.findings.length > findings.length
|
|
332
|
+
},
|
|
333
|
+
evidenceSummary: {
|
|
334
|
+
evidenceSafetyNotes: evidence.evidenceSafetyNotes ?? null,
|
|
335
|
+
projectionDiagnostics: evidence.projectionDiagnostics ?? null,
|
|
336
|
+
projectedFindings: compactEvidenceValue(evidence.projectedFindings ?? [], {
|
|
337
|
+
arrayItems: maxFindings,
|
|
338
|
+
depth: 5,
|
|
339
|
+
objectKeys: 40,
|
|
340
|
+
stringChars: 1_000
|
|
341
|
+
}),
|
|
342
|
+
coverageDiagnostics: compactEvidenceValue(evidence.coverageDiagnostics ?? null, {
|
|
343
|
+
arrayItems: 20,
|
|
344
|
+
depth: 5,
|
|
345
|
+
objectKeys: 40,
|
|
346
|
+
stringChars: 1_000
|
|
347
|
+
}),
|
|
348
|
+
policySurfaceCoverage: compactEvidenceValue(evidence.policySurfaceCoverage ?? null, {
|
|
349
|
+
arrayItems: 20,
|
|
350
|
+
depth: 5,
|
|
351
|
+
objectKeys: 40,
|
|
352
|
+
stringChars: 1_000
|
|
353
|
+
})
|
|
354
|
+
},
|
|
355
|
+
preConsentCookiesTrackers: {
|
|
356
|
+
summary: input.preConsentCookiesTrackers.summary,
|
|
357
|
+
rows: preConsentRows,
|
|
358
|
+
shown: preConsentRows.length,
|
|
359
|
+
total: Array.isArray(input.preConsentCookiesTrackers.rows) ? input.preConsentCookiesTrackers.rows.length : 0,
|
|
360
|
+
truncated: Array.isArray(input.preConsentCookiesTrackers.rows) && input.preConsentCookiesTrackers.rows.length > preConsentRows.length
|
|
361
|
+
},
|
|
362
|
+
links,
|
|
363
|
+
recommendedNextTool: findings.length > 0 ? "explain_finding" : null,
|
|
364
|
+
disclaimer: input.scan.disclaimer ?? input.report.disclaimer ?? null
|
|
365
|
+
};
|
|
366
|
+
}
|
|
300
367
|
export function explainFinding(report, findingId) {
|
|
301
368
|
const finding = findingsFromReport(report).find((candidate) => candidate.id === findingId);
|
|
302
369
|
if (!finding) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@certscore/mcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
4
|
"mcpName": "ai.certscore/mcp",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "MCP server for CertScore public website risk-signal workflows.",
|
|
@@ -46,6 +46,14 @@
|
|
|
46
46
|
"url": "https://certscore.ai/contact",
|
|
47
47
|
"email": "support@certscore.ai"
|
|
48
48
|
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsc -p tsconfig.json && esbuild src/index.ts --bundle --platform=node --format=esm --target=node20 --banner:js='#!/usr/bin/env node' --outfile=dist/certscore-mcp.mjs",
|
|
51
|
+
"bundle": "esbuild src/index.ts --bundle --platform=node --format=esm --target=node20 --banner:js='#!/usr/bin/env node' --outfile=dist/certscore-mcp.mjs",
|
|
52
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
53
|
+
"test": "node --import tsx --test src/*.test.ts",
|
|
54
|
+
"prepack": "pnpm run build",
|
|
55
|
+
"clean": "rm -rf dist"
|
|
56
|
+
},
|
|
49
57
|
"keywords": [
|
|
50
58
|
"certscore",
|
|
51
59
|
"pulse",
|
|
@@ -59,14 +67,7 @@
|
|
|
59
67
|
"zod": "^3.25.76"
|
|
60
68
|
},
|
|
61
69
|
"devDependencies": {
|
|
62
|
-
"@certscore/api-contracts": "
|
|
63
|
-
"@certscore/sdk": "
|
|
64
|
-
},
|
|
65
|
-
"scripts": {
|
|
66
|
-
"build": "tsc -p tsconfig.json && esbuild src/index.ts --bundle --platform=node --format=esm --target=node20 --banner:js='#!/usr/bin/env node' --outfile=dist/certscore-mcp.mjs",
|
|
67
|
-
"bundle": "esbuild src/index.ts --bundle --platform=node --format=esm --target=node20 --banner:js='#!/usr/bin/env node' --outfile=dist/certscore-mcp.mjs",
|
|
68
|
-
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
69
|
-
"test": "node --import tsx --test src/*.test.ts",
|
|
70
|
-
"clean": "rm -rf dist"
|
|
70
|
+
"@certscore/api-contracts": "workspace:*",
|
|
71
|
+
"@certscore/sdk": "workspace:*"
|
|
71
72
|
}
|
|
72
|
-
}
|
|
73
|
+
}
|
package/server.json
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
"url": "https://github.com/ergoveritas1-alt/certscore.ai",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "0.2.
|
|
10
|
+
"version": "0.2.11",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
15
15
|
"identifier": "@certscore/mcp",
|
|
16
|
-
"version": "0.2.
|
|
16
|
+
"version": "0.2.11",
|
|
17
17
|
"transport": {
|
|
18
18
|
"type": "stdio"
|
|
19
19
|
},
|