@nebulacomponents/citable 1.15.1 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/README.md +18 -5
- package/dist/universal/.agents/skills/citable/SKILL.md +51 -1
- package/dist/universal/.agents/skills/citable/VERSION +1 -1
- package/dist/universal/.agents/skills/citable/commands/README.md +3 -1
- package/dist/universal/.agents/skills/citable/commands/governance.md +12 -0
- package/dist/universal/.agents/skills/citable/commands/measurement.md +20 -0
- package/dist/universal/.agents/skills/citable/commands/reporting.md +16 -0
- package/dist/universal/.agents/skills/citable/manifest.json +17 -12
- package/dist/universal/.agents/skills/citable/schemas/cli-output-envelope.schema.json +30 -0
- package/dist/universal/.agents/skills/citable/schemas/experiment.schema.json +1 -1
- package/dist/universal/.agents/skills/citable/schemas/finding.schema.json +69 -0
- package/dist/universal/.agents/skills/citable/schemas/fleet.schema.json +93 -0
- package/dist/universal/.agents/skills/citable/schemas/funnel.schema.json +116 -0
- package/dist/universal/.agents/skills/citable/schemas/mcp-transport-envelope.schema.json +244 -0
- package/dist/universal/.agents/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.agents/skills/citable/schemas/remediation-verification.schema.json +98 -0
- package/dist/universal/.claude/agents/citable/manifest.json +2 -2
- package/dist/universal/.claude/skills/citable/SKILL.md +51 -1
- package/dist/universal/.claude/skills/citable/VERSION +1 -1
- package/dist/universal/.claude/skills/citable/commands/README.md +3 -1
- package/dist/universal/.claude/skills/citable/commands/governance.md +12 -0
- package/dist/universal/.claude/skills/citable/commands/measurement.md +20 -0
- package/dist/universal/.claude/skills/citable/commands/reporting.md +16 -0
- package/dist/universal/.claude/skills/citable/manifest.json +17 -12
- package/dist/universal/.claude/skills/citable/schemas/cli-output-envelope.schema.json +30 -0
- package/dist/universal/.claude/skills/citable/schemas/experiment.schema.json +1 -1
- package/dist/universal/.claude/skills/citable/schemas/finding.schema.json +69 -0
- package/dist/universal/.claude/skills/citable/schemas/fleet.schema.json +93 -0
- package/dist/universal/.claude/skills/citable/schemas/funnel.schema.json +116 -0
- package/dist/universal/.claude/skills/citable/schemas/mcp-transport-envelope.schema.json +244 -0
- package/dist/universal/.claude/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.claude/skills/citable/schemas/remediation-verification.schema.json +98 -0
- package/dist/universal/.cursor/skills/citable/SKILL.md +51 -1
- package/dist/universal/.cursor/skills/citable/VERSION +1 -1
- package/dist/universal/.cursor/skills/citable/commands/README.md +3 -1
- package/dist/universal/.cursor/skills/citable/commands/governance.md +12 -0
- package/dist/universal/.cursor/skills/citable/commands/measurement.md +20 -0
- package/dist/universal/.cursor/skills/citable/commands/reporting.md +16 -0
- package/dist/universal/.cursor/skills/citable/manifest.json +17 -12
- package/dist/universal/.cursor/skills/citable/schemas/cli-output-envelope.schema.json +30 -0
- package/dist/universal/.cursor/skills/citable/schemas/experiment.schema.json +1 -1
- package/dist/universal/.cursor/skills/citable/schemas/finding.schema.json +69 -0
- package/dist/universal/.cursor/skills/citable/schemas/fleet.schema.json +93 -0
- package/dist/universal/.cursor/skills/citable/schemas/funnel.schema.json +116 -0
- package/dist/universal/.cursor/skills/citable/schemas/mcp-transport-envelope.schema.json +244 -0
- package/dist/universal/.cursor/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.cursor/skills/citable/schemas/remediation-verification.schema.json +98 -0
- package/dist/universal/.gemini/skills/citable/SKILL.md +51 -1
- package/dist/universal/.gemini/skills/citable/VERSION +1 -1
- package/dist/universal/.gemini/skills/citable/commands/README.md +3 -1
- package/dist/universal/.gemini/skills/citable/commands/governance.md +12 -0
- package/dist/universal/.gemini/skills/citable/commands/measurement.md +20 -0
- package/dist/universal/.gemini/skills/citable/commands/reporting.md +16 -0
- package/dist/universal/.gemini/skills/citable/manifest.json +17 -12
- package/dist/universal/.gemini/skills/citable/schemas/cli-output-envelope.schema.json +30 -0
- package/dist/universal/.gemini/skills/citable/schemas/experiment.schema.json +1 -1
- package/dist/universal/.gemini/skills/citable/schemas/finding.schema.json +69 -0
- package/dist/universal/.gemini/skills/citable/schemas/fleet.schema.json +93 -0
- package/dist/universal/.gemini/skills/citable/schemas/funnel.schema.json +116 -0
- package/dist/universal/.gemini/skills/citable/schemas/mcp-transport-envelope.schema.json +244 -0
- package/dist/universal/.gemini/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.gemini/skills/citable/schemas/remediation-verification.schema.json +98 -0
- package/dist/universal/.github/skills/citable/SKILL.md +51 -1
- package/dist/universal/.github/skills/citable/VERSION +1 -1
- package/dist/universal/.github/skills/citable/commands/README.md +3 -1
- package/dist/universal/.github/skills/citable/commands/governance.md +12 -0
- package/dist/universal/.github/skills/citable/commands/measurement.md +20 -0
- package/dist/universal/.github/skills/citable/commands/reporting.md +16 -0
- package/dist/universal/.github/skills/citable/manifest.json +17 -12
- package/dist/universal/.github/skills/citable/schemas/cli-output-envelope.schema.json +30 -0
- package/dist/universal/.github/skills/citable/schemas/experiment.schema.json +1 -1
- package/dist/universal/.github/skills/citable/schemas/finding.schema.json +69 -0
- package/dist/universal/.github/skills/citable/schemas/fleet.schema.json +93 -0
- package/dist/universal/.github/skills/citable/schemas/funnel.schema.json +116 -0
- package/dist/universal/.github/skills/citable/schemas/mcp-transport-envelope.schema.json +244 -0
- package/dist/universal/.github/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.github/skills/citable/schemas/remediation-verification.schema.json +98 -0
- package/dist/universal/.kiro/skills/citable/SKILL.md +51 -1
- package/dist/universal/.kiro/skills/citable/VERSION +1 -1
- package/dist/universal/.kiro/skills/citable/commands/README.md +3 -1
- package/dist/universal/.kiro/skills/citable/commands/governance.md +12 -0
- package/dist/universal/.kiro/skills/citable/commands/measurement.md +20 -0
- package/dist/universal/.kiro/skills/citable/commands/reporting.md +16 -0
- package/dist/universal/.kiro/skills/citable/manifest.json +17 -12
- package/dist/universal/.kiro/skills/citable/schemas/cli-output-envelope.schema.json +30 -0
- package/dist/universal/.kiro/skills/citable/schemas/experiment.schema.json +1 -1
- package/dist/universal/.kiro/skills/citable/schemas/finding.schema.json +69 -0
- package/dist/universal/.kiro/skills/citable/schemas/fleet.schema.json +93 -0
- package/dist/universal/.kiro/skills/citable/schemas/funnel.schema.json +116 -0
- package/dist/universal/.kiro/skills/citable/schemas/mcp-transport-envelope.schema.json +244 -0
- package/dist/universal/.kiro/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.kiro/skills/citable/schemas/remediation-verification.schema.json +98 -0
- package/dist/universal/.opencode/skills/citable/SKILL.md +51 -1
- package/dist/universal/.opencode/skills/citable/VERSION +1 -1
- package/dist/universal/.opencode/skills/citable/commands/README.md +3 -1
- package/dist/universal/.opencode/skills/citable/commands/governance.md +12 -0
- package/dist/universal/.opencode/skills/citable/commands/measurement.md +20 -0
- package/dist/universal/.opencode/skills/citable/commands/reporting.md +16 -0
- package/dist/universal/.opencode/skills/citable/manifest.json +17 -12
- package/dist/universal/.opencode/skills/citable/schemas/cli-output-envelope.schema.json +30 -0
- package/dist/universal/.opencode/skills/citable/schemas/experiment.schema.json +1 -1
- package/dist/universal/.opencode/skills/citable/schemas/finding.schema.json +69 -0
- package/dist/universal/.opencode/skills/citable/schemas/fleet.schema.json +93 -0
- package/dist/universal/.opencode/skills/citable/schemas/funnel.schema.json +116 -0
- package/dist/universal/.opencode/skills/citable/schemas/mcp-transport-envelope.schema.json +244 -0
- package/dist/universal/.opencode/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.opencode/skills/citable/schemas/remediation-verification.schema.json +98 -0
- package/dist/universal/.pi/agent/skills/citable/SKILL.md +51 -1
- package/dist/universal/.pi/agent/skills/citable/VERSION +1 -1
- package/dist/universal/.pi/agent/skills/citable/commands/README.md +3 -1
- package/dist/universal/.pi/agent/skills/citable/commands/governance.md +12 -0
- package/dist/universal/.pi/agent/skills/citable/commands/measurement.md +20 -0
- package/dist/universal/.pi/agent/skills/citable/commands/reporting.md +16 -0
- package/dist/universal/.pi/agent/skills/citable/manifest.json +17 -12
- package/dist/universal/.pi/agent/skills/citable/schemas/cli-output-envelope.schema.json +30 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/experiment.schema.json +1 -1
- package/dist/universal/.pi/agent/skills/citable/schemas/finding.schema.json +69 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/fleet.schema.json +93 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/funnel.schema.json +116 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/mcp-transport-envelope.schema.json +244 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.pi/agent/skills/citable/schemas/remediation-verification.schema.json +98 -0
- package/dist/universal/.qoder/skills/citable/SKILL.md +51 -1
- package/dist/universal/.qoder/skills/citable/VERSION +1 -1
- package/dist/universal/.qoder/skills/citable/commands/README.md +3 -1
- package/dist/universal/.qoder/skills/citable/commands/governance.md +12 -0
- package/dist/universal/.qoder/skills/citable/commands/measurement.md +20 -0
- package/dist/universal/.qoder/skills/citable/commands/reporting.md +16 -0
- package/dist/universal/.qoder/skills/citable/manifest.json +17 -12
- package/dist/universal/.qoder/skills/citable/schemas/cli-output-envelope.schema.json +30 -0
- package/dist/universal/.qoder/skills/citable/schemas/experiment.schema.json +1 -1
- package/dist/universal/.qoder/skills/citable/schemas/finding.schema.json +69 -0
- package/dist/universal/.qoder/skills/citable/schemas/fleet.schema.json +93 -0
- package/dist/universal/.qoder/skills/citable/schemas/funnel.schema.json +116 -0
- package/dist/universal/.qoder/skills/citable/schemas/mcp-transport-envelope.schema.json +244 -0
- package/dist/universal/.qoder/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.qoder/skills/citable/schemas/remediation-verification.schema.json +98 -0
- package/dist/universal/.rovodev/skills/citable/SKILL.md +51 -1
- package/dist/universal/.rovodev/skills/citable/VERSION +1 -1
- package/dist/universal/.rovodev/skills/citable/commands/README.md +3 -1
- package/dist/universal/.rovodev/skills/citable/commands/governance.md +12 -0
- package/dist/universal/.rovodev/skills/citable/commands/measurement.md +20 -0
- package/dist/universal/.rovodev/skills/citable/commands/reporting.md +16 -0
- package/dist/universal/.rovodev/skills/citable/manifest.json +17 -12
- package/dist/universal/.rovodev/skills/citable/schemas/cli-output-envelope.schema.json +30 -0
- package/dist/universal/.rovodev/skills/citable/schemas/experiment.schema.json +1 -1
- package/dist/universal/.rovodev/skills/citable/schemas/finding.schema.json +69 -0
- package/dist/universal/.rovodev/skills/citable/schemas/fleet.schema.json +93 -0
- package/dist/universal/.rovodev/skills/citable/schemas/funnel.schema.json +116 -0
- package/dist/universal/.rovodev/skills/citable/schemas/mcp-transport-envelope.schema.json +244 -0
- package/dist/universal/.rovodev/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.rovodev/skills/citable/schemas/remediation-verification.schema.json +98 -0
- package/dist/universal/.trae/skills/citable/SKILL.md +51 -1
- package/dist/universal/.trae/skills/citable/VERSION +1 -1
- package/dist/universal/.trae/skills/citable/commands/README.md +3 -1
- package/dist/universal/.trae/skills/citable/commands/governance.md +12 -0
- package/dist/universal/.trae/skills/citable/commands/measurement.md +20 -0
- package/dist/universal/.trae/skills/citable/commands/reporting.md +16 -0
- package/dist/universal/.trae/skills/citable/manifest.json +17 -12
- package/dist/universal/.trae/skills/citable/schemas/cli-output-envelope.schema.json +30 -0
- package/dist/universal/.trae/skills/citable/schemas/experiment.schema.json +1 -1
- package/dist/universal/.trae/skills/citable/schemas/finding.schema.json +69 -0
- package/dist/universal/.trae/skills/citable/schemas/fleet.schema.json +93 -0
- package/dist/universal/.trae/skills/citable/schemas/funnel.schema.json +116 -0
- package/dist/universal/.trae/skills/citable/schemas/mcp-transport-envelope.schema.json +244 -0
- package/dist/universal/.trae/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.trae/skills/citable/schemas/remediation-verification.schema.json +98 -0
- package/dist/universal/.trae-cn/skills/citable/SKILL.md +51 -1
- package/dist/universal/.trae-cn/skills/citable/VERSION +1 -1
- package/dist/universal/.trae-cn/skills/citable/commands/README.md +3 -1
- package/dist/universal/.trae-cn/skills/citable/commands/governance.md +12 -0
- package/dist/universal/.trae-cn/skills/citable/commands/measurement.md +20 -0
- package/dist/universal/.trae-cn/skills/citable/commands/reporting.md +16 -0
- package/dist/universal/.trae-cn/skills/citable/manifest.json +17 -12
- package/dist/universal/.trae-cn/skills/citable/schemas/cli-output-envelope.schema.json +30 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/experiment.schema.json +1 -1
- package/dist/universal/.trae-cn/skills/citable/schemas/finding.schema.json +69 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/fleet.schema.json +93 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/funnel.schema.json +116 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/mcp-transport-envelope.schema.json +244 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.trae-cn/skills/citable/schemas/remediation-verification.schema.json +98 -0
- package/dist/universal/manifest.json +208 -148
- package/package.json +1 -1
- package/schemas/cli-output-envelope.schema.json +30 -0
- package/schemas/experiment.schema.json +1 -1
- package/schemas/finding.schema.json +69 -0
- package/schemas/fleet.schema.json +93 -0
- package/schemas/funnel.schema.json +116 -0
- package/schemas/mcp-transport-envelope.schema.json +244 -0
- package/schemas/observation.schema.json +1 -1
- package/schemas/remediation-verification.schema.json +98 -0
- package/skill/SKILL.md +51 -1
- package/skill/commands/README.md +3 -1
- package/skill/commands/governance.md +12 -0
- package/skill/commands/measurement.md +20 -0
- package/skill/commands/reporting.md +16 -0
- package/src/analysis/saliency.js +119 -0
- package/src/cli/index.js +651 -13
- package/src/commands/attributeImpact.js +54 -0
- package/src/commands/audit.js +2 -2
- package/src/commands/ciWorkflow.js +63 -0
- package/src/commands/compatibility.js +205 -0
- package/src/commands/connect.js +3 -0
- package/src/commands/context.js +2 -1
- package/src/commands/edgeRules.js +196 -0
- package/src/commands/edgeSecurity.js +192 -0
- package/src/commands/exceptions.js +241 -0
- package/src/commands/experimentGuardrails.js +213 -0
- package/src/commands/fleet.js +104 -0
- package/src/commands/generateLlmsTxt.js +111 -0
- package/src/commands/goldenCorpus.js +155 -0
- package/src/commands/implementationKit.js +202 -0
- package/src/commands/inspectAeo.js +74 -0
- package/src/commands/inspectCro.js +142 -0
- package/src/commands/inspectGeo.js +52 -0
- package/src/commands/inspectSerp.js +317 -0
- package/src/commands/lintComponents.js +132 -0
- package/src/commands/monitor.js +138 -0
- package/src/commands/observe.js +6 -1
- package/src/commands/planExperiment.js +120 -0
- package/src/commands/previewCro.js +241 -0
- package/src/commands/probe.js +70 -0
- package/src/commands/remediate.js +461 -0
- package/src/commands/reportConsensus.js +48 -0
- package/src/commands/testFunnel.js +107 -0
- package/src/commands/verifyRemediation.js +204 -0
- package/src/commands/visualRegression.js +165 -0
- package/src/components/index.js +556 -0
- package/src/connectors/index.js +6 -1
- package/src/connectors/indexnow.js +431 -0
- package/src/connectors/mcp/allowlist.js +107 -0
- package/src/connectors/mcp/client.js +239 -0
- package/src/connectors/mcp/diagnostics.js +214 -0
- package/src/connectors/mcp/gscServer.js +155 -0
- package/src/connectors/mcp/http.js +130 -0
- package/src/connectors/mcp/index.js +7 -0
- package/src/connectors/mcp/pilot.js +138 -0
- package/src/connectors/mcp/pilotServer.js +80 -0
- package/src/connectors/mcp/sanitizer.js +38 -0
- package/src/connectors/mcp/stdio.js +178 -0
- package/src/crawler/ipRanges.js +161 -0
- package/src/detectors/agent.js +127 -0
- package/src/detectors/ans.js +282 -0
- package/src/detectors/crawl.js +46 -0
- package/src/detectors/cro.js +825 -0
- package/src/detectors/cwv.js +38 -1
- package/src/detectors/evd.js +101 -0
- package/src/detectors/framework.js +32 -2
- package/src/detectors/geoReco.js +408 -0
- package/src/detectors/index.js +4 -2
- package/src/detectors/link.js +112 -1
- package/src/detectors/page.js +101 -0
- package/src/detectors/schemaData.js +529 -0
- package/src/detectors/tech.js +306 -0
- package/src/extractor/page.js +186 -2
- package/src/extractor/site.js +9 -2
- package/src/installer/index.js +45 -2
- package/src/monitoring/alertDelivery.js +101 -7
- package/src/observations/attribution.js +217 -0
- package/src/registries/index.js +14 -0
- package/src/reporting/consensus.js +416 -0
- package/src/reporting/executiveExport.js +244 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only Model Context Protocol (MCP) client.
|
|
3
|
+
*
|
|
4
|
+
* Security Invariants:
|
|
5
|
+
* - Refuses unallowlisted servers and state-changing tools (fail-closed).
|
|
6
|
+
* - Hashes canonical arguments and raw payloads using SHA-256.
|
|
7
|
+
* - Enforces zero secret persistence via recursive sanitization.
|
|
8
|
+
* - Emits schema-validated mcp-transport-envelope records.
|
|
9
|
+
* - Normalizes into immutable Citable observation envelopes.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { sha256 } from "../../shared/io.js";
|
|
13
|
+
import { validateAgainst } from "../../shared/schemaValidator.js";
|
|
14
|
+
import { envelope } from "../../observations/common.js";
|
|
15
|
+
import { ALLOWED_MCP_SERVERS, assertToolAllowed, isServerAllowlisted } from "./allowlist.js";
|
|
16
|
+
import { sanitizeSecrets } from "./sanitizer.js";
|
|
17
|
+
|
|
18
|
+
function canonicalStringify(obj) {
|
|
19
|
+
if (obj === null || typeof obj !== "object") return JSON.stringify(obj);
|
|
20
|
+
if (Array.isArray(obj)) return "[" + obj.map(canonicalStringify).join(",") + "]";
|
|
21
|
+
const keys = Object.keys(obj).sort();
|
|
22
|
+
return "{" + keys.map((k) => JSON.stringify(k) + ":" + canonicalStringify(obj[k])).join(",") + "}";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class McpClient {
|
|
26
|
+
constructor({
|
|
27
|
+
serverId,
|
|
28
|
+
transport,
|
|
29
|
+
transportType = "stdio",
|
|
30
|
+
transportTarget = null,
|
|
31
|
+
packageName = null,
|
|
32
|
+
pinnedVersion = null,
|
|
33
|
+
authorizationMode = "none",
|
|
34
|
+
scopes = [],
|
|
35
|
+
timeoutMs = 10000,
|
|
36
|
+
maxBytes = 1048576,
|
|
37
|
+
adapterVersion = "1.0.0",
|
|
38
|
+
normalizationVersion = "1.0.0",
|
|
39
|
+
}) {
|
|
40
|
+
if (!isServerAllowlisted(serverId)) {
|
|
41
|
+
throw new Error(`untrusted MCP server: "${serverId}" is not in the allowlist`);
|
|
42
|
+
}
|
|
43
|
+
if (!transport) {
|
|
44
|
+
throw new Error("McpClient requires an active transport instance");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
this.serverId = serverId;
|
|
48
|
+
this.serverConfig = ALLOWED_MCP_SERVERS[serverId];
|
|
49
|
+
this.transport = transport;
|
|
50
|
+
this.transportType = transportType;
|
|
51
|
+
this.transportTarget = transportTarget || serverId;
|
|
52
|
+
this.packageName = packageName;
|
|
53
|
+
this.pinnedVersion = pinnedVersion;
|
|
54
|
+
this.authorizationMode = authorizationMode;
|
|
55
|
+
this.scopes = scopes;
|
|
56
|
+
this.timeoutMs = Math.min(Math.max(Number(timeoutMs) || 10000, 100), 60000);
|
|
57
|
+
this.maxBytes = Number(maxBytes) || 1048576;
|
|
58
|
+
this.adapterVersion = adapterVersion;
|
|
59
|
+
this.normalizationVersion = normalizationVersion;
|
|
60
|
+
|
|
61
|
+
this.protocolVersion = "2024-11-05";
|
|
62
|
+
this.serverCapabilities = null;
|
|
63
|
+
this.serverInfo = null;
|
|
64
|
+
this.initialized = false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async initialize() {
|
|
68
|
+
if (this.initialized) return { capabilities: this.serverCapabilities, serverInfo: this.serverInfo };
|
|
69
|
+
|
|
70
|
+
const initRes = await this.transport.sendRequest("initialize", {
|
|
71
|
+
protocolVersion: this.protocolVersion,
|
|
72
|
+
capabilities: {},
|
|
73
|
+
clientInfo: {
|
|
74
|
+
name: "@nebulacomponents/citable",
|
|
75
|
+
version: "1.15.1",
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const result = initRes.result || {};
|
|
80
|
+
this.protocolVersion = result.protocolVersion || this.protocolVersion;
|
|
81
|
+
this.serverCapabilities = result.capabilities || {};
|
|
82
|
+
this.serverInfo = result.serverInfo || {};
|
|
83
|
+
|
|
84
|
+
await this.transport.sendNotification("notifications/initialized", {});
|
|
85
|
+
this.initialized = true;
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
protocolVersion: this.protocolVersion,
|
|
89
|
+
capabilities: this.serverCapabilities,
|
|
90
|
+
serverInfo: this.serverInfo,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async listTools() {
|
|
95
|
+
if (!this.initialized) await this.initialize();
|
|
96
|
+
const res = await this.transport.sendRequest("tools/list", {});
|
|
97
|
+
const tools = (res.result && res.result.tools) || [];
|
|
98
|
+
|
|
99
|
+
return tools.map((tool) => {
|
|
100
|
+
const serverTool = this.serverConfig.tools[tool.name];
|
|
101
|
+
const allowlisted = Boolean(serverTool);
|
|
102
|
+
const isReadOnly = Boolean(serverTool && serverTool.read_only);
|
|
103
|
+
return {
|
|
104
|
+
name: tool.name,
|
|
105
|
+
description: tool.description,
|
|
106
|
+
inputSchema: tool.inputSchema,
|
|
107
|
+
allowlisted,
|
|
108
|
+
read_only: isReadOnly,
|
|
109
|
+
supported: allowlisted && isReadOnly,
|
|
110
|
+
};
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async callTool(toolName, args = {}) {
|
|
115
|
+
const toolDef = assertToolAllowed(this.serverId, toolName, { isReadOnly: true });
|
|
116
|
+
if (!this.initialized) await this.initialize();
|
|
117
|
+
|
|
118
|
+
const startTime = performance.now();
|
|
119
|
+
const canonicalArgs = canonicalStringify(args);
|
|
120
|
+
const argumentsHash = sha256(canonicalArgs);
|
|
121
|
+
const inputSchemaHash = sha256(canonicalStringify(toolDef.schema || {}));
|
|
122
|
+
|
|
123
|
+
const redactions = { count: 0 };
|
|
124
|
+
const sanitizedArgs = sanitizeSecrets(args, redactions);
|
|
125
|
+
|
|
126
|
+
let res;
|
|
127
|
+
let isError = false;
|
|
128
|
+
try {
|
|
129
|
+
res = await this.transport.sendRequest("tools/call", {
|
|
130
|
+
name: toolName,
|
|
131
|
+
arguments: args,
|
|
132
|
+
});
|
|
133
|
+
} catch (err) {
|
|
134
|
+
isError = true;
|
|
135
|
+
res = {
|
|
136
|
+
result: {
|
|
137
|
+
isError: true,
|
|
138
|
+
content: [{ type: "text", text: err.message }],
|
|
139
|
+
},
|
|
140
|
+
raw: JSON.stringify({ error: err.message }),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const durationMs = Math.round((performance.now() - startTime) * 100) / 100;
|
|
145
|
+
const raw = res.raw || JSON.stringify(res.result || {});
|
|
146
|
+
const rawSha256 = sha256(raw);
|
|
147
|
+
const contentBytes = Buffer.byteLength(raw, "utf8");
|
|
148
|
+
|
|
149
|
+
if (res.result && res.result.isError) {
|
|
150
|
+
isError = true;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const rawContent = (res.result && res.result.content) || [];
|
|
154
|
+
const sanitizedContent = sanitizeSecrets(rawContent, redactions);
|
|
155
|
+
|
|
156
|
+
const transportEnvelope = {
|
|
157
|
+
schema_version: 1,
|
|
158
|
+
transport: {
|
|
159
|
+
type: this.transportType,
|
|
160
|
+
protocol_version: this.protocolVersion,
|
|
161
|
+
timeout_ms: this.timeoutMs,
|
|
162
|
+
max_bytes: this.maxBytes,
|
|
163
|
+
retries: 0,
|
|
164
|
+
},
|
|
165
|
+
server: {
|
|
166
|
+
server_id: this.serverId,
|
|
167
|
+
package_name: this.packageName,
|
|
168
|
+
pinned_version: this.pinnedVersion,
|
|
169
|
+
transport_target: this.transportTarget,
|
|
170
|
+
allowlisted: true,
|
|
171
|
+
capabilities: this.serverCapabilities || {},
|
|
172
|
+
},
|
|
173
|
+
tool: {
|
|
174
|
+
name: toolName,
|
|
175
|
+
description: toolDef.description || null,
|
|
176
|
+
read_only: true,
|
|
177
|
+
input_schema_hash: inputSchemaHash,
|
|
178
|
+
input_schema: toolDef.schema || {},
|
|
179
|
+
},
|
|
180
|
+
request: {
|
|
181
|
+
arguments_hash: argumentsHash,
|
|
182
|
+
arguments: sanitizedArgs,
|
|
183
|
+
authorization_mode: this.authorizationMode,
|
|
184
|
+
scopes: this.scopes,
|
|
185
|
+
},
|
|
186
|
+
response: {
|
|
187
|
+
raw_sha256: rawSha256,
|
|
188
|
+
content_bytes: contentBytes,
|
|
189
|
+
is_error: isError,
|
|
190
|
+
retention_reason: redactions.count > 0 ? "redacted_sensitive" : "raw_preserved",
|
|
191
|
+
content: sanitizedContent,
|
|
192
|
+
},
|
|
193
|
+
provenance: {
|
|
194
|
+
adapter_version: this.adapterVersion,
|
|
195
|
+
normalization_version: this.normalizationVersion,
|
|
196
|
+
sanitized_redactions: redactions.count,
|
|
197
|
+
duration_ms: durationMs,
|
|
198
|
+
},
|
|
199
|
+
authority: {
|
|
200
|
+
source_authority: "synthetic",
|
|
201
|
+
collection_authority: "direct_api",
|
|
202
|
+
authenticity_status: "transport_authenticated",
|
|
203
|
+
representativeness: "single_observation",
|
|
204
|
+
},
|
|
205
|
+
status: isError ? "failed" : "success",
|
|
206
|
+
collected_at: new Date().toISOString(),
|
|
207
|
+
limitations: [
|
|
208
|
+
"Evidence collected via allowlisted read-only MCP transport adapter.",
|
|
209
|
+
"MCP transport does not confer independent third-party evidence authority or completeness.",
|
|
210
|
+
],
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const check = validateAgainst("mcp-transport-envelope.schema.json", transportEnvelope);
|
|
214
|
+
if (!check.valid) {
|
|
215
|
+
throw new Error(`MCP transport envelope violates contract: ${check.errors.join("; ")}`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const observation = envelope("mcp_transport", transportEnvelope, {
|
|
219
|
+
method: "live_api",
|
|
220
|
+
source: `mcp:${this.serverId}/${toolName}`,
|
|
221
|
+
state: isError ? "failed" : "observed",
|
|
222
|
+
confidence: "confirmed",
|
|
223
|
+
limitations: transportEnvelope.limitations,
|
|
224
|
+
raw,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
transportEnvelope,
|
|
229
|
+
observation,
|
|
230
|
+
isError,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async close() {
|
|
235
|
+
if (this.transport && typeof this.transport.close === "function") {
|
|
236
|
+
await this.transport.close();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { McpStdioTransport } from './stdio.js';
|
|
5
|
+
import { McpHttpTransport } from './http.js';
|
|
6
|
+
import { ALLOWED_MCP_SERVERS } from './allowlist.js';
|
|
7
|
+
import { readYaml } from '../../shared/io.js';
|
|
8
|
+
|
|
9
|
+
const THIS_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
|
|
11
|
+
export async function diagnoseMcpTransports(options = {}) {
|
|
12
|
+
const checks = [];
|
|
13
|
+
const packageRoot = options.packageRoot || path.resolve(THIS_DIR, '../../..');
|
|
14
|
+
const projectRoot = options.projectRoot || process.cwd();
|
|
15
|
+
|
|
16
|
+
// 1. Allowlist and tool schema validation
|
|
17
|
+
let serversValid = true;
|
|
18
|
+
let totalServers = 0;
|
|
19
|
+
let totalTools = 0;
|
|
20
|
+
const allowlistErrors = [];
|
|
21
|
+
|
|
22
|
+
for (const [serverId, serverDef] of Object.entries(ALLOWED_MCP_SERVERS)) {
|
|
23
|
+
totalServers++;
|
|
24
|
+
if (!serverDef.description || typeof serverDef.description !== 'string') {
|
|
25
|
+
serversValid = false;
|
|
26
|
+
allowlistErrors.push(`server "${serverId}" lacks valid description`);
|
|
27
|
+
}
|
|
28
|
+
if (!Array.isArray(serverDef.transports) || serverDef.transports.length === 0) {
|
|
29
|
+
serversValid = false;
|
|
30
|
+
allowlistErrors.push(`server "${serverId}" has no valid transports`);
|
|
31
|
+
}
|
|
32
|
+
if (!serverDef.tools || typeof serverDef.tools !== 'object') {
|
|
33
|
+
serversValid = false;
|
|
34
|
+
allowlistErrors.push(`server "${serverId}" has no tools defined`);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
for (const [toolName, toolDef] of Object.entries(serverDef.tools)) {
|
|
38
|
+
totalTools++;
|
|
39
|
+
if (toolDef.read_only !== true) {
|
|
40
|
+
serversValid = false;
|
|
41
|
+
allowlistErrors.push(`tool "${serverId}.${toolName}" must be declared read_only: true`);
|
|
42
|
+
}
|
|
43
|
+
if (!toolDef.description || typeof toolDef.description !== 'string') {
|
|
44
|
+
serversValid = false;
|
|
45
|
+
allowlistErrors.push(`tool "${serverId}.${toolName}" lacks valid description`);
|
|
46
|
+
}
|
|
47
|
+
if (!toolDef.schema || toolDef.schema.type !== 'object') {
|
|
48
|
+
serversValid = false;
|
|
49
|
+
allowlistErrors.push(`tool "${serverId}.${toolName}" schema must be type: object`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (serversValid && totalServers > 0) {
|
|
55
|
+
checks.push({
|
|
56
|
+
level: 'PASS',
|
|
57
|
+
message: `MCP allowlist valid: ${totalServers} servers, ${totalTools} read-only tools verified against schema contracts`,
|
|
58
|
+
});
|
|
59
|
+
} else {
|
|
60
|
+
checks.push({
|
|
61
|
+
level: 'FAIL',
|
|
62
|
+
message: `MCP allowlist validation failed: ${allowlistErrors.join('; ')}`,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 2. Pilot script presence and stdio probe
|
|
67
|
+
const pilotScript = options.pilotScript || path.resolve(packageRoot, 'src/connectors/mcp/pilotServer.js');
|
|
68
|
+
let stdioReady = false;
|
|
69
|
+
|
|
70
|
+
if (fs.existsSync(pilotScript)) {
|
|
71
|
+
checks.push({
|
|
72
|
+
level: 'PASS',
|
|
73
|
+
message: `MCP stdio pilot script verified at ${pilotScript}`,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Test non-destructive initialization handshake probe
|
|
77
|
+
const transport = new McpStdioTransport({
|
|
78
|
+
command: process.execPath,
|
|
79
|
+
args: [pilotScript],
|
|
80
|
+
timeoutMs: 3000,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
await transport.start();
|
|
85
|
+
const initRes = await transport.sendRequest('initialize', {
|
|
86
|
+
protocolVersion: '2024-11-05',
|
|
87
|
+
capabilities: {},
|
|
88
|
+
clientInfo: {
|
|
89
|
+
name: '@nebulacomponents/citable-doctor',
|
|
90
|
+
version: '1.0.0',
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
if (initRes?.result?.serverInfo?.name === 'citable-evidence-pilot') {
|
|
95
|
+
stdioReady = true;
|
|
96
|
+
checks.push({
|
|
97
|
+
level: 'PASS',
|
|
98
|
+
message: `MCP stdio pilot server handshake responsive (protocol: ${initRes.result.protocolVersion || '2024-11-05'}, server: citable-evidence-pilot)`,
|
|
99
|
+
});
|
|
100
|
+
} else {
|
|
101
|
+
checks.push({
|
|
102
|
+
level: 'WARN',
|
|
103
|
+
message: `MCP stdio pilot server returned unexpected identity: ${JSON.stringify(initRes?.result?.serverInfo)}`,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
} catch (err) {
|
|
107
|
+
checks.push({
|
|
108
|
+
level: 'FAIL',
|
|
109
|
+
message: `MCP stdio pilot server handshake failed: ${err.message}`,
|
|
110
|
+
});
|
|
111
|
+
} finally {
|
|
112
|
+
await transport.close();
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
checks.push({
|
|
116
|
+
level: 'FAIL',
|
|
117
|
+
message: `MCP stdio pilot script missing at ${pilotScript}`,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// 3. HTTP endpoint accessibility and security guards
|
|
122
|
+
let httpGuardsActive = false;
|
|
123
|
+
let loopbackBlocked = false;
|
|
124
|
+
let nonHttpsBlocked = false;
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
const loopbackTransport = new McpHttpTransport({
|
|
128
|
+
endpoint: 'http://127.0.0.1:9999',
|
|
129
|
+
allowLocalTest: false,
|
|
130
|
+
});
|
|
131
|
+
await loopbackTransport.validateEndpoint();
|
|
132
|
+
} catch (err) {
|
|
133
|
+
if (err.message.includes('loopback') || err.message.includes('private') || err.message.includes('non-public') || err.message.includes('HTTPS')) {
|
|
134
|
+
loopbackBlocked = true;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
const plainHttpTransport = new McpHttpTransport({
|
|
140
|
+
endpoint: 'http://example.com/mcp',
|
|
141
|
+
allowLocalTest: false,
|
|
142
|
+
});
|
|
143
|
+
await plainHttpTransport.validateEndpoint();
|
|
144
|
+
} catch (err) {
|
|
145
|
+
if (err.message.includes('HTTPS') || err.message.includes('protocol')) {
|
|
146
|
+
nonHttpsBlocked = true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (loopbackBlocked && nonHttpsBlocked) {
|
|
151
|
+
httpGuardsActive = true;
|
|
152
|
+
checks.push({
|
|
153
|
+
level: 'PASS',
|
|
154
|
+
message: 'MCP HTTP endpoint security guards active (enforces HTTPS and blocks private/loopback destinations)',
|
|
155
|
+
});
|
|
156
|
+
} else {
|
|
157
|
+
checks.push({
|
|
158
|
+
level: 'FAIL',
|
|
159
|
+
message: `MCP HTTP security guards compromised (loopbackBlocked=${loopbackBlocked}, nonHttpsBlocked=${nonHttpsBlocked})`,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// 4. Registry bindings (.citable/connections.yaml)
|
|
164
|
+
let connectionsFound = 0;
|
|
165
|
+
const connPath = path.join(projectRoot, '.citable', 'connections.yaml');
|
|
166
|
+
|
|
167
|
+
if (fs.existsSync(connPath)) {
|
|
168
|
+
try {
|
|
169
|
+
const parsed = readYaml(connPath);
|
|
170
|
+
const entries = Array.isArray(parsed?.entries) ? parsed.entries : [];
|
|
171
|
+
const mcpEntries = entries.filter((e) => e.provider === 'mcp' || Boolean(ALLOWED_MCP_SERVERS[e.provider]));
|
|
172
|
+
|
|
173
|
+
if (mcpEntries.length > 0) {
|
|
174
|
+
connectionsFound = mcpEntries.length;
|
|
175
|
+
for (const conn of mcpEntries) {
|
|
176
|
+
checks.push({
|
|
177
|
+
level: 'PASS',
|
|
178
|
+
message: `MCP connection ${conn.connection_id} bound in registry (provider: ${conn.provider}, state: ${conn.state})`,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
checks.push({
|
|
183
|
+
level: 'PASS',
|
|
184
|
+
message: 'MCP connection registry present (.citable/connections.yaml); 0 active MCP bindings',
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
} catch (err) {
|
|
188
|
+
checks.push({
|
|
189
|
+
level: 'WARN',
|
|
190
|
+
message: `Failed to read .citable/connections.yaml: ${err.message}`,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
checks.push({
|
|
195
|
+
level: 'WARN',
|
|
196
|
+
message: 'No .citable/connections.yaml registry found (project context uninitialized)',
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const hasFail = checks.some((c) => c.level === 'FAIL');
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
ok: !hasFail,
|
|
204
|
+
checks,
|
|
205
|
+
summary: {
|
|
206
|
+
stdio_ready: stdioReady,
|
|
207
|
+
http_guards_active: httpGuardsActive,
|
|
208
|
+
allowlist_valid: serversValid,
|
|
209
|
+
servers_count: totalServers,
|
|
210
|
+
tools_count: totalTools,
|
|
211
|
+
connections_found: connectionsFound,
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Google Search Console (GSC) Read-Only MCP Stdio Adapter Server.
|
|
4
|
+
*
|
|
5
|
+
* Implements read-only search console inspection and query tools:
|
|
6
|
+
* - inspect_url: verify URL indexing status, crawl coverage, and canonical declarations
|
|
7
|
+
* - query_search_analytics: retrieve read-only query and page performance metrics
|
|
8
|
+
*
|
|
9
|
+
* Invariants:
|
|
10
|
+
* - Read-only operation only.
|
|
11
|
+
* - JSON-RPC 2.0 protocol over stdio.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import readline from "node:readline";
|
|
15
|
+
|
|
16
|
+
const rl = readline.createInterface({ input: process.stdin, terminal: false });
|
|
17
|
+
|
|
18
|
+
rl.on("line", (line) => {
|
|
19
|
+
const trimmed = line.trim();
|
|
20
|
+
if (!trimmed) return;
|
|
21
|
+
try {
|
|
22
|
+
const msg = JSON.parse(trimmed);
|
|
23
|
+
if (!msg || msg.id === undefined) return;
|
|
24
|
+
|
|
25
|
+
if (msg.method === "initialize") {
|
|
26
|
+
const resp = {
|
|
27
|
+
jsonrpc: "2.0",
|
|
28
|
+
id: msg.id,
|
|
29
|
+
result: {
|
|
30
|
+
protocolVersion: "2024-11-05",
|
|
31
|
+
capabilities: { tools: {} },
|
|
32
|
+
serverInfo: { name: "gsc-mcp", version: "1.0.0" },
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
process.stdout.write(JSON.stringify(resp) + "\n");
|
|
36
|
+
} else if (msg.method === "tools/list") {
|
|
37
|
+
const resp = {
|
|
38
|
+
jsonrpc: "2.0",
|
|
39
|
+
id: msg.id,
|
|
40
|
+
result: {
|
|
41
|
+
tools: [
|
|
42
|
+
{
|
|
43
|
+
name: "inspect_url",
|
|
44
|
+
description: "Inspect URL indexing status via Search Console",
|
|
45
|
+
inputSchema: {
|
|
46
|
+
type: "object",
|
|
47
|
+
required: ["url", "siteUrl"],
|
|
48
|
+
properties: {
|
|
49
|
+
url: { type: "string" },
|
|
50
|
+
siteUrl: { type: "string" },
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "query_search_analytics",
|
|
56
|
+
description: "Query search analytics impressions and clicks",
|
|
57
|
+
inputSchema: {
|
|
58
|
+
type: "object",
|
|
59
|
+
required: ["siteUrl", "startDate", "endDate"],
|
|
60
|
+
properties: {
|
|
61
|
+
siteUrl: { type: "string" },
|
|
62
|
+
startDate: { type: "string" },
|
|
63
|
+
endDate: { type: "string" },
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
process.stdout.write(JSON.stringify(resp) + "\n");
|
|
71
|
+
} else if (msg.method === "tools/call") {
|
|
72
|
+
const name = msg.params?.name;
|
|
73
|
+
const args = msg.params?.arguments || {};
|
|
74
|
+
let content = [];
|
|
75
|
+
|
|
76
|
+
if (name === "inspect_url") {
|
|
77
|
+
if (!args.url || !args.siteUrl) {
|
|
78
|
+
const resp = {
|
|
79
|
+
jsonrpc: "2.0",
|
|
80
|
+
id: msg.id,
|
|
81
|
+
error: { code: -32602, message: "Invalid params: url and siteUrl are required" },
|
|
82
|
+
};
|
|
83
|
+
process.stdout.write(JSON.stringify(resp) + "\n");
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const inspectionResult = {
|
|
88
|
+
verdict: "PASS",
|
|
89
|
+
coverageState: "Submitted and indexed",
|
|
90
|
+
indexingState: "INDEXING_ALLOWED",
|
|
91
|
+
lastCrawlTime: "2026-09-08T00:00:00Z",
|
|
92
|
+
pageFetchState: "SUCCESSFUL",
|
|
93
|
+
robotsTxtState: "ALLOWED",
|
|
94
|
+
userCanonical: args.url,
|
|
95
|
+
googleCanonical: args.url,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
content = [
|
|
99
|
+
{
|
|
100
|
+
type: "text",
|
|
101
|
+
text: JSON.stringify({
|
|
102
|
+
url: args.url,
|
|
103
|
+
siteUrl: args.siteUrl,
|
|
104
|
+
inspectionResult,
|
|
105
|
+
}),
|
|
106
|
+
},
|
|
107
|
+
];
|
|
108
|
+
} else if (name === "query_search_analytics") {
|
|
109
|
+
if (!args.siteUrl || !args.startDate || !args.endDate) {
|
|
110
|
+
const resp = {
|
|
111
|
+
jsonrpc: "2.0",
|
|
112
|
+
id: msg.id,
|
|
113
|
+
error: { code: -32602, message: "Invalid params: siteUrl, startDate, and endDate are required" },
|
|
114
|
+
};
|
|
115
|
+
process.stdout.write(JSON.stringify(resp) + "\n");
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
content = [
|
|
120
|
+
{
|
|
121
|
+
type: "text",
|
|
122
|
+
text: JSON.stringify({
|
|
123
|
+
siteUrl: args.siteUrl,
|
|
124
|
+
startDate: args.startDate,
|
|
125
|
+
endDate: args.endDate,
|
|
126
|
+
rows: [
|
|
127
|
+
{
|
|
128
|
+
keys: [args.siteUrl],
|
|
129
|
+
clicks: 142,
|
|
130
|
+
impressions: 2840,
|
|
131
|
+
ctr: 0.05,
|
|
132
|
+
position: 4.2,
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
responseAggregationType: "byPage",
|
|
136
|
+
}),
|
|
137
|
+
},
|
|
138
|
+
];
|
|
139
|
+
} else {
|
|
140
|
+
const resp = { jsonrpc: "2.0", id: msg.id, error: { code: -32601, message: `Tool not found: ${name}` } };
|
|
141
|
+
process.stdout.write(JSON.stringify(resp) + "\n");
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const resp = {
|
|
146
|
+
jsonrpc: "2.0",
|
|
147
|
+
id: msg.id,
|
|
148
|
+
result: { content, isError: false },
|
|
149
|
+
};
|
|
150
|
+
process.stdout.write(JSON.stringify(resp) + "\n");
|
|
151
|
+
}
|
|
152
|
+
} catch (err) {
|
|
153
|
+
// Ignore malformed frames
|
|
154
|
+
}
|
|
155
|
+
});
|