spec-gate
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@spec-gateScore this request against our profile: add SSO to admin dashboard"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Coding agents start as soon as they are asked, whatever the request leaves out. The omissions are not random. Security posture, compliance constraints and operational requirements are the least covered dimensions in real requests, and the code that comes back looks finished either way.
Most tooling helps you write a specification. spec-gate decides whether the specification is sufficient to start, and writes down the decision.
request ──▶ score against profile ──▶ below threshold? ──▶ BLOCK + the questions that close the gap
│
└─ at or above ──▶ PROCEED + a committed record of whyIt refuses. Below the threshold it returns a block, not a warning. An override is allowed and must carry a written reason, which lands in the record.
Why a threshold, and not just more questions
Asking the model to clarify sounds like the safe option. Measured across five models on 100 deliberately underspecified requests, clarification without a stopping rule made critical omissions worse on four of them. Gating on a sufficiency threshold reduced them on all five.
Model | Direct generation | Clarification, no gate | Gated | Reduction |
GPT-OSS 20B | 10.9% | 11.7% | 5.4% | −50% |
Qwen 2.5 7B | 28.2% | 36.0% | 14.6% | −48% |
Mistral 7B | 31.4% | 45.3% | 17.3% | −45% |
GPT-4o-mini | 18.5% | 36.0% | 11.7% | −37% |
Llama 3.2 3B | 34.8% | 38.2% | 24.8% | −29% |
Critical omission rate: the share of safety, compliance and operational requirements the output left out. Lower is better. Method, baselines and full results: IEEE RE 2026.
Two caveats the paper states and this README will not bury. Answers came from a simulated user who always knew the right answer, so those reductions are an upper bound. Scorer agreement with human annotation was 0.71 (Cohen's kappa): good enough to gate on, not good enough to trust silently, which is why every score carries its evidence.
Related MCP server: docscanner-mcp
Interface
Two tools over MCP. One implementation, usable from any client that speaks the protocol.
score_request
Scores a request, returns a verdict. Writes nothing.
// in
{
"request": "Add SSO to the admin dashboard",
"context": ["repo:acme/admin", "file:docs/auth.md"] // optional
}
// out
{
"verdict": "block", // "proceed" | "block"
"sufficiency": 0.62, // weighted coverage, 0 to 1
"threshold": 0.8,
"profile": {"id": "agent-task", "version": "0.1.0", "source": ".spec-gate.yml"},
"scorer": {"type": "deterministic+declared", "independent": false},
"dimensions": [
{"id": "must_not_change", "coverage": 0.4, "method": "model",
"evidence": "mentions SSO, says nothing about existing sessions",
"ask": "Which current behaviour must keep working exactly as it does now?"},
{"id": "test_contract", "coverage": 0.0, "method": "deterministic",
"evidence": "no test file or assertion named in the request",
"ask": "Which tests already assert behaviour this change must not break?"}
],
"blocking": ["must_not_change", "test_contract"]
}One question per uncovered dimension, ordered by how much it moves the verdict. The caller decides whether to put them to the user.
The profile is not a parameter. It comes from .spec-gate.yml in the repo, so a caller cannot quietly choose a softer one.
write_record
Stores the decision. This is the artifact, and the reason the tool exists.
// in
{"request": "...", "answers": [...], "decision": "proceed"}
// or
{"request": "...", "decision": "override", "override": {"reason": "hotfix, gap accepted", "by": "pedram"}}
// out
{"path": ".spec-gate/2026-09-16-add-sso.json", "hash": "sha256:9f2c...", "decision": "proceed"}The record
A decision record answers one question: why was this allowed to proceed? It holds the request, the profile and its version, per-dimension coverage with the evidence behind each score and whether a rule or a model produced it, every question asked and the answer given, the constraints and assumptions that resulted, any override with its reason and author, which scorer ran, and a timestamp. Full contract: schema/record.schema.json.
Every record gets a Markdown sibling, so a reviewer who does not read JSON can still read the decision in a pull request:
### Add SSO to the admin dashboard
**Proceeded** at 0.86 against a threshold of 0.80 · profile `agent-task@0.1.0` · scorer: rules + declared (not independent)
**Constraints** existing sessions stay valid through rollout · tokens never written to logs
**Assumptions** IdP is the existing Entra tenant (unconfirmed)
**Asked** Which current behaviour must keep working? → "existing sessions must not drop"Records are committed to the repository they belong to. Uncommitted evidence is not evidence, and a record next to the change it authorised is reviewable where the change is reviewed.
Assert on the stored record, never on console output. Tests read the JSON fields.
Profiles
Dimensions are data, so a team can fit them to its own failures. A profile sets the dimensions, their weights, the coverage rubric and the threshold.
Profile | For | Dimensions | Status |
Autonomous queues, where an item runs with no human in the loop | What must not change · what the tests assert · blast radius · definition of done | v0.1 | |
| Security, compliance and operations-critical work | The paper's ten, grounded in ISO/IEC/IEEE 29148 and Volere | v0.2 |
Where a dimension can be checked without a model, it is. A rule cannot be talked out of its answer.
Scoring
Rules first, declaration second, and a rule always wins.
A rule that misses is decisive: if nothing in the request, the target path or the user's answer names a test, test_contract is capped at zero and no declaration can lift it. A rule that matches is not: seeing a test path proves a test was named, not that it binds this change, so the cap lifts and judgement goes back to the caller. Whatever the rules cannot answer, the caller declares against the rubric with evidence, and a declared score is marked as declared in the record.
The rules read the request, the target path and what the user actually answered. They do not read the caller's own reasoning, or the veto would be advisory. "I checked the tests and they are fine" scores zero; a named test file does not.
An independent scorer is configurable for anyone who wants the judge separated from the judged.
This is deliberately not MCP sampling. Sampling was deprecated in the 2026-07-28 spec revision, and of the clients people actually use only VS Code implements it, so a design resting on it would not run where it is needed. Blocking questions are put to the user through elicitation instead, which is supported in Claude Code, Cursor, VS Code and Codex.
Scorer agreement with human annotation was 0.71 (Cohen's kappa) in the paper. Good enough to gate on, not good enough to trust silently, which is why every score carries its evidence.
Design decisions
The reasoning, and what was deliberately excluded, is in DECISIONS.md. In short:
It refuses rather than warns. A gate with an escape hatch is a warning.
Overrides are never silent. A reason, an author and a timestamp, in the record.
The repo picks the profile, not the caller.
Records are committed, JSON as the source of truth, Markdown for humans.
No signing or hash chaining. Git history already gives tamper evidence here.
Not a spec generator, not a plan mode, not a linter. It runs once, before the work starts, and anything outside the profile passes untouched.
Enforcement
Calling the gate is voluntary until a hook makes it not. .claude/settings.json registers a PreToolUse hook that denies file edits in a gated repository until a record authorises the work:
Write src/auth/session.ts
└─ hook: no decision record authorises this work → denied, with instructionsAn authorisation is for a piece of work, not a standing permission: it is scoped to the session that earned it and goes stale after twelve hours. A blocked record authorises nothing. A record whose hash no longer matches its content authorises nothing either. A repository with no .spec-gate.yml is not gated at all, so the hook is silent everywhere else.
An override still works, and still costs a written reason on the record.
Shadow mode
Before a gate is allowed to stop anything, it should prove it would stop the right things. enforce: shadow in .spec-gate.yml lets every edit through, tells the agent what a real gate would have said, and appends the would-have-blocked decision to .spec-gate/shadow.jsonl:
enforce: shadow # deny (default) · shadow · off{"at":"2026-09-16T09:12:04Z","session":"…","tool":"Write","file":"src/auth/session.ts","would_block":true,"reason":"spec-gate: no decision record authorises this work…"}Run it that way in a repo where an autonomous agent is working and you learn two things nothing else tells you: how often it would fire, and whether it would fire on the right things. Enforce afterwards, on evidence.
A broken config blocks rather than disabling the gate. A typo in .spec-gate.yml is not permission to skip it, and enforce: off is a decision someone has to write down. Editing .spec-gate.yml itself is always allowed, so a bad config can always be repaired.
Run it
git clone https://github.com/PedramMadani/spec-gate && cd spec-gate && npm install
claude mcp add spec-gate -- npx tsx /absolute/path/to/spec-gate/src/index.tsOr in .mcp.json (Claude Code) or .cursor/mcp.json (Cursor):
{ "mcpServers": { "spec-gate": { "command": "npx", "args": ["tsx", "/absolute/path/to/spec-gate/src/index.ts"] } } }Then put a .spec-gate.yml in the repo you want gated.
Not on npm yet.
Status
Working. The gate refuses, rules veto declared scores, records are written and hashed, and a hook enforces them. What is left is elicitation for the blocking questions, the sdlc-critical profile, and real use.
Scope | ||
v0.0.1 | Server over stdio, both tools working, deterministic rule veto, records written and hashed, PreToolUse hook with deny / shadow / off, 47 tests | ✅ |
v0.1 | Elicitation for blocking questions, published to npm, running against one real autonomous queue | |
v0.2 |
|
Open tasks: TASKS.md. Omissions it has caught in real use: CATCHES.md.
This is a reference implementation of a published method, maintained as time allows. No support commitment and no roadmap. Issues are welcome, answers are not guaranteed.
Citation
@inproceedings{madani2026rsga,
author = {Madani, Mohammadamin and Nahhas, Abdulrahman and Chernigovskaya, Maria and Turowski, Klaus},
title = {Requirements Sufficiency Gating for {LLM}-Assisted Automation: When Should Generation Proceed?},
booktitle = {2026 IEEE 34th International Requirements Engineering Conference (RE)},
year = {2026},
pages = {262--272},
address = {Montreal, QC, Canada},
publisher = {IEEE},
doi = {10.1109/RE68928.2026.00028}
}Licence
Code Apache-2.0. Dimension spec, profiles and schema CC-BY-4.0.
Available Tools
2 toolsscore_requestScore a request before starting workA
Scores a request against the repository profile and returns a proceed or block verdict with the questions that close the gap. Writes nothing.
| Name | Required | Description | Default |
|---|---|---|---|
| cwd | No | Directory to resolve .spec-gate.yml from. Defaults to the process cwd. | |
| target | No | Path the work will touch, used to select the profile. Does not choose the profile directly. | |
| request | Yes | The request as received, verbatim. |
Output Schema
| Name | Required | Description |
|---|---|---|
| rubric | Yes | |
| scorer | Yes | |
| profile | Yes | |
| verdict | Yes | |
| blocking | Yes | |
| threshold | Yes | |
| dimensions | Yes | |
| sufficiency | Yes | |
| instructions | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It explicitly states 'Writes nothing,' which is a key behavioral trait (non-destructive), and mentions the output (proceed or block verdict). This adds value beyond the schema, though it does not detail other side effects like logging or rate limits, which is acceptable for a read-only evaluation tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, efficient sentence that front-loads the core action and output, then appends the non-mutation note. Every word earns its place with zero redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The output schema exists, so return values need no explanation. The description covers the purpose, non-mutating behavior, and output type. The only minor gap is not explicitly stating when to use this versus write_record, but the sibling name and 'Writes nothing' make the distinction clear enough.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description does not add meaningful parameter semantics beyond what the schema already provides; it mentions 'request' but the schema already describes it as 'The request as received, verbatim.' No extra context is given for cwd or target.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb (scores) and resource (request against repository profile), and clearly distinguishes from the sibling tool by noting 'Writes nothing.' This makes the tool's function unambiguous and differentiates it from write_record.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The title 'Score a request before starting work' gives clear temporal context, and the description's 'Writes nothing' implies it is for evaluation rather than mutation. However, it does not explicitly mention when not to use it or name write_record as the alternative, so it falls short of fully explicit guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
write_recordWrite the decision recordB
Stores the decision as a committed record: what was missing, what was assumed, who authorised proceeding. The verdict is computed from the declared coverage, not accepted from the caller.
| Name | Required | Description | Default |
|---|---|---|---|
| cwd | No | ||
| target | No | ||
| request | Yes | The request as received, verbatim. | |
| session | No | Session id, so the pre-work hook can scope authorisation to this run. | |
| override | No | Only to proceed below the threshold. The reason is stored verbatim and is not optional. | |
| dimensions | Yes | One entry per profile dimension, scored against the rubric. | |
| assumptions | No | Carried but not established. Listing them is what makes them visible. | |
| constraints | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| hash | Yes | |
| path | Yes | |
| vetoed | Yes | Dimensions where a rule capped the declared score. |
| missing | Yes | |
| decision | Yes | |
| threshold | Yes | |
| sufficiency | Yes | |
| markdown_path | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden. It discloses that the verdict is computed from declared coverage rather than accepted from the caller, which is a key behavioral trait. However, it omits details on side effects (e.g., persistence, idempotency) or error behavior, leaving gaps for a mutation tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences, front-loaded with the core action, and contains no fluff. Every word contributes to understanding the tool's purpose and a critical behavioral detail.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite having an output schema, the description is too sparse for a tool with 8 parameters, nested objects, and a sibling tool. It lacks usage context, parameter guidance, and behavioral completeness, leaving an agent with insufficient information to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 63%, moderate. The description adds no parameter-level meaning; it does not explain the missing cwd, target, or constraints fields or clarify their roles. The schema provides descriptions for several fields, but the description fails to compensate for the uncovered parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool stores a decision record and lists what is recorded (missing, assumed, authorised). However, it does not differentiate from the sibling score_request, leaving the distinction to inference.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance is given on when to use this tool versus score_request. The description implies it operates after scoring but provides no explicit when-to-use or when-not-to-use instructions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
2 tool updates
v0.0.1- First observed
score_request - First observed
write_record
TDQS
Scored across 2 tools
The two tools have clearly distinct purposes: score_request evaluates and returns a verdict, while write_record persists the decision. No overlap in functionality or ambiguity in selection.
Both tools follow a consistent verb_noun pattern (score_request, write_record), making the naming predictable and easy to infer for an agent.
With only 2 tools, the server feels thin for a domain like spec gating, but the tools cover the core workflow (scoring and recording). It's borderline but not unreasonable for a narrow purpose.
The tool set covers scoring and storing decisions but lacks obvious operations like retrieving past records or updating profiles. These gaps are notable but can be worked around for the primary gating use case.
Maintenance
Related MCP Connectors
Evidence-readiness MCP server: validate, audit, and score briefs, memos, and evidence packs.
Append-only decisions with provenance, supersession, retrieval, and audited MCP actions.
Governed data discovery, exact queries, decisions, simulations, and runtime utilities over MCP.
A paid remote MCP for Equibles, built to return verdicts, receipts, usage logs, and audit-ready JSON
Related MCP Servers
- AlicenseAqualityCmaintenanceExposes the squad-dev workflow as deterministic MCP tools, enabling task classification, risk scoring, specialist reviewer selection, and advisory verdict consolidation for code changes.2743 npm4Apache 2.0
- FlicenseNot gradedqualityDmaintenanceProvides lightweight documentation review tools including issue detection, readability scoring, style checking, and document summarization for integration with MCP-compatible clients.-
- FlicenseNot gradedqualityCmaintenanceMCP server that enforces governance on agentic decisions with auditable evidence records, providing tools for understanding, calibrating confidence, and navigating handoffs based on policy.1-
- AlicenseNot gradedqualityCmaintenanceEvidence-first delivery audit MCP server that evaluates task requirements against delivery evidence and returns a reproducible pass/needs_review/fail decision with a deterministic receipt.MIT