Skip to content
DocumentationReferencev1.2.1

Response schema

Schema 1.4 clusters, flat remaining issues, compact keys, and reference identity limits.

Signalint 1.2.1 normally emits schemaVersion: "1.4" for project and file checks. Its validator also accepts historical 1.3 data; that does not mean current results keep the old shape. Early refusals and other tools have separate contracts. MCP payload modes determine how the data is transported, independently of compact keys.

type BuiltinEngine = "oxlint" | "tsc" | "biome" | "eslint";
type IssueEngine = BuiltinEngine | "external-lsp" | "vscode";
interface CheckResponse {
schemaVersion: "1.4";
status: "clean" | "issues_found" | "error";
projectRoot: string;
engines: Partial<Record<IssueEngine, EngineStatus>>;
totalIssues: number;
clusters: Cluster[];
remainingIssues: RemainingIssue[];
omittedIssueCount: number;
filteredOutIssueCount?: number;
filteredOutCount?: number;
nextStep?: string;
truncated: boolean;
loopWarning?: LoopWarning;
fileRuleChurnWarning?: FileRuleChurnWarning;
checkId?: string;
code?: string;
message?: string;
}
interface EngineStatus {
status: "ok" | "error" | "disabled";
message?: string;
}

This describes normal non-compact runtime output, not every shape accepted by the broader validator or output schema.

FieldMeaning
statusEngine failures take precedence; no successful task yields error / nothing_checked
projectRootCanonical absolute root in normal full checks; omitted by compact check output
enginesPresent outcomes; disabled entries without messages can be omitted
totalIssuesCollected diagnostic count after provider filtering, before response caps
clustersUp to 10 ranked groups with at least 2 issues each
remainingIssuesUp to 100 flat records: single-rule occurrences and demoted overflow-group issues
omittedIssueCountNumber of flat records omitted by the 100-entry cap; normally 0 when none
filteredOutIssueCount, filteredOutCountEqual aliases emitted when a file check excludes diagnostics outside requested paths
nextStepGuidance when flat records are omitted or file-scope filtering hides other diagnostics
truncatedFlat records exceeded 100—not a repository coverage certificate or simply an overflow-cluster flag
checkIdAn 8-hex checksum of selected diagnostic fields, not a unique run ID
Warning fieldsObjects only when active; inactive warnings are omitted, not emitted as null

An error check can still include useful diagnostics from completed engines. ok can mean cached results or no relevant files; disabled/missing entries do not imply successful coverage.

interface Cluster {
clusterId: string;
rootCauseSummary: string;
ruleIds: string[];
issueCount: number;
fileCount: number;
priority: number;
suggestedAction?: string;
sampleIssueIds?: string[];
}
interface RemainingIssue {
issueId: string;
file: string;
line: number;
col: number;
rule: string;
severity: "error" | "warning";
fixable: boolean;
}

Groups of 2 or more same-rule issues become clusters. The systemic threshold still requires more than 3 issues across more than 2 files. Non-systemic groups of 2–3 issues can therefore be clusters too. Single issues and overflow groups go flat; see clustering.

Clusters with more than 2 issues include up to 2 distinct sample IDs. Two-issue clusters omit sampleIssueIds; use clusterId for detail. suggestedAction appears only when all group issues are marked fixable. Its wording is not a returned patch or proof of a safe edit.

Flat records deliberately omit engine and message. Use their emitted issue IDs to retrieve matching normalized detail.

signalint check --compact or SIGNALINT_COMPACT=1 changes check keys:

NormalCompact
schemaVersionv
totalIssuestotal
remainingIssuesremaining
omittedIssueCountomitted
Both filtered-count aliasesfilteredOut

Compact checks omit projectRoot. clusters, status, engines, checkId, and active warning names stay the same. An environment-enabled compact mode also affects MCP checks, but not every early error or other tool. Prefer the default full shape unless your consumer supports both.

{
"v": "1.4",
"status": "issues_found",
"engines": { "tsc": { "status": "ok" } },
"total": 1,
"clusters": [],
"remaining": [
{
"issueId": "b2dbbc348dd5",
"file": "src/file01.ts",
"line": 3,
"col": 14,
"rule": "TS2322",
"severity": "error",
"fixable": false
}
],
"omitted": 0,
"truncated": false,
"checkId": "7e3c2bd2"
}

This is an illustrative shape, not a captured result. mcpPayload: "text" does not enable compact keys.

interface NormalizedIssue {
issueId: string;
file: string;
line: number;
col: number;
engine: IssueEngine;
rule: string;
severity: "error" | "warning";
message: string;
fixable: boolean;
clusterId?: string;
serverName?: string;
sourceKind?: "compiler" | "linter" | "lsp" | "editor";
provenance?: {
kind: "compiler" | "linter" | "lsp" | "editor";
engine: IssueEngine;
serverName?: string;
timestamp: number;
};
}

Messages are collapsed to one line and capped at approximately 120 characters. Detail retains full issue hashes but does not restore original unnormalized messages or edit patches. Successful detail uses { "issues": [...] } in both/structured channels with matching first JSON text. In text-only mode it is an array. get_issue_detail requires a check ID.

  • Full issue IDs hash file, rule, line, and quoted-value-normalized message. They omit engine and column; duplicate issue IDs remain possible for distinct diagnostics.
  • Summary IDs use 12-character prefixes, extended when distinct full hashes share that prefix in the same check. Equal full hashes are not made distinct by prefix extension.
  • Detail lookup first tries exact IDs, then returns all prefix matches. Use emitted prefixes rather than guessing shorter ones; a manually shortened prefix can match multiple diagnostics.
  • Same-rule multi-issue groups now share one cluster. The old 1.0 behavior that could make a local cluster stale after a same-ID assignment collision is not carried forward as an unresolved guarantee-breaking case.
  • Cluster IDs hash rule/severity/systemic scope and can be reused across changed results; they are not permanent global identities.
  • checkId hashes engine, file, line, column, rule, severity, and message. It omits fixable, project root, and invocation time. It is not a unique invocation ID, security token, or complete freshness check.

In 1.2.1, required checkId selects a retained snapshot (default 50 / 30 minutes). Missing it is an argument refusal; the latest-result fallback has been removed. A later check no longer inherently invalidates an older retained snapshot. Unknown/expired IDs or unmatched references can produce:

{
"status": "stale",
"message": "This cluster/issue no longer exists; run check_project again."
}

All diagnostics passed into a check snapshot are retained in its normalized issue array, but omitted flat records do not expose their IDs or paths in the summary. Demoted overflow groups do not receive returned cluster IDs. get_issue_detail needs a known reference: it is not an enumeration or pagination API for every omitted item.

get_live_diagnostics can enumerate the full newest non-LSP-snapshot plus buffered normalized array, including currently omitted IDs. It is not pagination of every retained historical snapshot. get_diagnostic_snapshot returns bounded summaries and metadata, not its full issue array. If you cannot identify an older omitted issue, narrow check_files to known paths, make a suitably scoped project check, or run native engines. A nextStep hint does not by itself make every hidden diagnostic discoverable. The caps bound record counts, not a fixed serialized-byte ceiling; long rules, paths, messages, and detail arrays can change payload size.

Snapshot metadata, delta, live ingestion and transaction results have separate shapes, not a new check-schema number. Optional provenance fields are accepted by the normalized issue schema; they are not populated identically by every handler. Read MCP tools, snapshots and live diagnostics.

Check the shape before parsing a tool result as a normal check:

{
"status": "error",
"code": "project_not_initialized",
"message": "Run 'npx signalint-mcp init' in /path/to/project before checking it.",
"projectRoot": "/path/to/project"
}

Source: schema.ts, clusterEngine.ts, and toolSchemas.ts.

A thrown provider/engine failure can return a different error object, with isError: true on the MCP result:

{
"status": "error",
"code": "engine_timeout",
"engine": "tsc",
"message": "TypeScript timed out",
"retryable": true,
"nextStep": "Increase the engine timeout in signalint.config.json or check for hanging processes."
}

This is illustrative, not captured output. output_limit_exceeded and engine_execution_failed are non-retryable classifications. Normal settled engine outcomes can still produce a full diagnostic check with status: error, rather than this thrown-provider shape. Inspect both shape and engine coverage.

Strict advertised schemas do not make every raw return valid: compact check aliases and post-apply delta.error need the documented combination caveats. The server parses inputs with Zod; upstream/client Ajv validation is not proof of a universal server-side output validation gate.