An MCP server that gives an AI agent your software delivery standard: artifact templates, a domain glossary, and a machine verdict on whether an artifact is ready to move on.
A prompt can tell an agent what shape a user story should have. What a prompt cannot do is tell it whether the story it just wrote is actually acceptable. That is what this server is for.
validate_artifact("US", content) -> { "verdict": "fail", "blockers": [ ... ] }
Three rules hold the whole thing together:
- Every task produces an artifact, and every artifact has an owning role. Roles are defaults, not requirements: a project that does not staff an architect reassigns the architect's tasks down a declared fallback chain. The task still has to happen.
- Readiness is data, not opinion. Definition of Ready and Definition of Done
live in
standard/rules.yamlas declarative records. Adding a rule means editing YAML. Adding a kind of rule means writing one function. - The standard lives in version control, so the same files that a human reviews are the context an agent reads. There is no second, prettier copy that drifts.
This is spec-driven development taken literally: the specification in the repository is the source of truth an agent implements from, and the gate it has to pass on the way out.
git clone https://github.com/<you>/sdlc-mcp && cd sdlc-mcp
python -m venv .venv && . .venv/Scripts/activate # POSIX: . .venv/bin/activate
pip install -e ".[dev]"
python -m sdlc_mcp --self-check # verify the standard is consistent
python -m sdlc_mcp --overview # print the standard as a Markdown table
pytest -q # 40 tests
python -m sdlc_mcp # serve over stdio--self-check is the interesting one. It asserts that the standard does not
contradict itself: every artifact type points at a real category and a real role,
every task produces a known artifact type, every rule kind is implemented, every
template file exists, every template satisfies its own required-section rules,
and every bundled example produces the verdict it is supposed to. A standard that
contradicts itself is worse than no standard, because an agent reading it cannot
tell the difference. It runs in CI.
Claude Code (.mcp.json in your project, or claude mcp add):
{
"mcpServers": {
"sdlc": {
"command": "python",
"args": ["-m", "sdlc_mcp"]
}
}
}Cursor (.cursor/mcp.json) uses the same shape. Any MCP client that speaks stdio
will work; --transport streamable-http and --transport sse are also available.
| Tool | What an agent uses it for |
|---|---|
list_work_categories |
See the phases of delivery and what each must produce. |
get_work_category |
Get one phase in full: purpose, exit artifacts, tasks, owners. |
list_artifact_types |
See the artifact catalog with id conventions and owners. |
get_artifact_template |
Fetch the template before writing, plus the sections the rules will require. |
validate_artifact |
Get a pass/fail verdict with blockers and warnings, at the ready or done gate. |
lookup_glossary_term |
Resolve a domain term instead of inventing a meaning. On a miss, returns every known term so the agent can see what it should have asked for. |
next_tasks |
Given what already exists, what is still outstanding and who owns it. |
list_examples |
Find the reference artifacts, each declaring its expected verdict. |
Resources: sdlc://standard/overview, sdlc://glossary,
sdlc://template/{artifact_type}, sdlc://example/{example_id}.
Prompts: draft_artifact and review_artifact - workflows that put the tools
in the right order (template first, glossary second, validate last) so the agent
does not have to be reminded every session.
examples/US-LEND-009-not-ready.md is a
deliberately bad user story. Real output:
{
"artifact_type": "US",
"gate": "ready",
"verdict": "fail",
"rules_checked": 13,
"blockers": [
{
"rule": "no_placeholder",
"message": "Placeholders left in the text mean the artifact is not ready, whoever wrote it.",
"detail": "'TODO'"
},
{
"rule": "frontmatter_enum",
"message": "front matter 'priority' has a value outside the allowed set",
"detail": "got 'high', allowed: must, should, could, wont"
},
{
"rule": "section_min_items",
"message": "A story with fewer than two acceptance criteria is a title, not a requirement.",
"detail": "found 1"
}
],
"warnings": [
{
"rule": "section_forbids_text",
"message": "Unfalsifiable acceptance criteria cannot be tested and cannot be implemented by an agent.",
"detail": "found: 'as appropriate', 'etc.'"
},
{
"rule": "section_required",
"message": "required section 'Open questions' is missing"
}
]
}Blockers fail the gate. Warnings do not - they are the things worth arguing about rather than the things worth refusing.
The Python in src/ contains no process knowledge. Fork the repository, edit the
YAML in standard/, and you are serving your own standard over the
same protocol:
| File | What it defines |
|---|---|
categories.yaml |
Phases of delivery, their exit artifacts and their tasks. |
artifact-types.yaml |
The artifact catalog: owning category, owner role, id convention, template. |
roles.yaml |
Roles and the fallback chain used when one is not staffed. |
rules.yaml |
Definition of Ready and Definition of Done, per artifact type. |
glossary.yaml |
The domain terms and their agreed meaning. |
templates/ |
One Markdown template per artifact type. |
Run python -m sdlc_mcp --self-check after editing. It will tell you what you
broke.
| Kind | Fields | Checks |
|---|---|---|
frontmatter_required |
key |
The key exists and is not empty. |
frontmatter_pattern |
key, pattern |
The value matches a regex - used for id conventions. |
frontmatter_enum |
key, values |
The value is one of an allowed set. |
section_required |
heading |
A section with that heading exists (case- and punctuation-insensitive). |
section_min_items |
heading, min |
The section has at least N list items or table rows. |
section_forbids_text |
heading, tokens |
The section avoids named weasel phrases. |
no_placeholder |
tokens |
No TODO/TBD and no unreplaced <angle bracket> template slots anywhere. |
links_resolve |
- | Outbound artifact references resolve against the ids you pass in. |
Every rule carries a severity (blocker or warning) and an optional message
that replaces the generic one. The messages in rules.yaml are written to be read
by whoever has to fix the artifact, human or otherwise.
Adding a kind: write a checker in
validation.py and register it in
KNOWN_RULE_KINDS. An unregistered kind referenced from YAML is reported by
--self-check and, at runtime, degrades to a loud warning rather than a silent
pass.
standard/ the standard itself, as data
templates/ one Markdown template per artifact type
examples/ filled-in reference artifacts in a demo domain
src/sdlc_mcp/
markdown.py front matter, sections, list items, references
validation.py the rule engine: kinds, findings, verdicts
catalog.py loads the standard, answers questions, self-checks
model.py immutable value types
server.py MCP tools, resources and prompts
tests/ 40 tests, no network, no fixtures beyond the repo
links_resolvedoes nothing when you pass no ids. Without a universe of known artifacts there is nothing to resolve against, and reporting every reference as broken would teach callers to ignore the rule. Passknown_idsand it starts working.- Unknown rule kinds warn, they do not pass quietly. A typo in the standard should be visible at the point of use, not discovered when a bad artifact reaches implementation.
- Templates are validated against their own rules. A template that cannot pass the gate teaches the agent to produce failures, which is a subtle and expensive way to break a standard.
- The owner fallback is transitive and terminates. If nobody in the chain is staffed, the default role is returned unchanged: the task still belongs to somebody, which is exactly the conversation the fallback is meant to force.
- No Markdown library. Artifacts follow the bundled templates, so heading and list detection is sufficient; a parser would be a dependency for no gain.
The glossary and examples describe BookLoop, a fictional community
book-lending platform. It exists only to make the artifacts concrete - loans,
copies, branches and due dates are small enough to hold in your head and rich
enough to show what a real use case and a real ADR look like. Replace
standard/glossary.yaml and examples/ with your own and nothing else changes.
The standard shipped here is a generic reference implementation written for this repository.
MIT - see LICENSE.