/*
observability.mcp.validate — offline lint for the SigNoz MCP server entry (RFC-0344).
- Do not introduce app-specific runtime composition or deployment behavior into this reusable package source file.
- RFC-0344: initial implementation.
*/
import { join } from "node:path";
import type {
CheckResult,
Diagnostic,
KernelCommandInput,
KernelCommandResult,
KernelRuntimeContext,
} from "@warpgogol/werkstatt-engine/kernel";
import { diagnosticsResult } from "@warpgogol/werkstatt-shared/checks";
const SIGNOZ_KEY_PATTERN = /[a-f0-9]{32,}/gi;
export async function runObservabilityMcpValidate(
_input: KernelCommandInput,
context: KernelRuntimeContext,
): Promise> {
const diagnostics: Diagnostic[] = [];
const root = context.workspaceRoot;
const mcpPath = join(root, ".mcp.json");
// OBS-MCP-01: .mcp.json must have signoz entry with env-referenced token
if (!(await context.io.exists(mcpPath))) {
diagnostics.push({
ruleId: "OBS-MCP-01",
severity: "error",
file: ".mcp.json",
message: ".mcp.json does not exist — the signoz MCP server entry is required (RFC-0344).",
fixHint: "Create .mcp.json with a signoz server entry using ${WARPGOGOL_SIGNOZ_MCP_TOKEN}.",
});
} else {
const raw = await context.io.readFile(mcpPath);
try {
const parsed = JSON.parse(raw) as Record;
const servers = (parsed as { mcpServers?: Record }).mcpServers;
if (!servers || !servers["signoz"]) {
diagnostics.push({
ruleId: "OBS-MCP-01",
severity: "error",
file: ".mcp.json",
message: ".mcp.json lacks the 'signoz' MCP server entry.",
fixHint: 'Add a "signoz" entry under "mcpServers" pointing at the SigNoz MCP endpoint.',
});
} else {
const signozEntry = JSON.stringify(servers["signoz"]);
if (!signozEntry.includes("${WARPGOGOL_SIGNOZ_MCP_TOKEN}")) {
diagnostics.push({
ruleId: "OBS-MCP-01",
severity: "error",
file: ".mcp.json",
message:
"signoz MCP entry embeds a literal token instead of the env reference ${WARPGOGOL_SIGNOZ_MCP_TOKEN}.",
fixHint: "Replace any literal token with ${WARPGOGOL_SIGNOZ_MCP_TOKEN}.",
});
}
}
} catch {
diagnostics.push({
ruleId: "OBS-MCP-01",
severity: "error",
file: ".mcp.json",
message: ".mcp.json is not valid JSON.",
fixHint: "Fix the JSON syntax in .mcp.json.",
});
}
}
// OBS-MCP-02: scan for SigNoz API key patterns in .mcp.json and nearby config files
const scanFiles = [".mcp.json", "services/observability-stack/.env.example"];
for (const relPath of scanFiles) {
const absPath = join(root, relPath);
if (!(await context.io.exists(absPath))) continue;
const text = await context.io.readFile(absPath);
const matches = text.match(SIGNOZ_KEY_PATTERN);
if (matches) {
for (const match of matches) {
// Skip env var references and placeholder patterns
if (match.includes("$") || match.toLowerCase().includes("replace")) continue;
diagnostics.push({
ruleId: "OBS-MCP-02",
severity: "error",
file: relPath,
message: `Potential SigNoz API key found in committed file: ${match.slice(0, 8)}...`,
fixHint: "Remove the secret and use an env var reference instead.",
});
}
}
}
// OBS-MCP-03: incidents README must exist
const incidentsPath = join(root, "docs", "observability", "incidents", "README.md");
if (!(await context.io.exists(incidentsPath))) {
diagnostics.push({
ruleId: "OBS-MCP-03",
severity: "warning",
file: "docs/observability/incidents/README.md",
message: "Incident note template is missing.",
fixHint: "Create docs/observability/incidents/README.md with the incident note template.",
});
}
return diagnosticsResult("observability.mcp.validate", diagnostics);
}