sdlc-mcp
# sdlc-mcp
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": [ ... ] }
```
## The idea
Three rules hold the whole thing together:
1. **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.
2. **Readiness is data, not opinion.** Definition of Ready and Definition of Done
live in [`standard/rules.yaml`](standard/rules.yaml) as declarative records.
Adding a rule means editing YAML. Adding a *kind* of rule means writing one
function.
3. **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.
## Quickstart
```bash
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.
## Wiring it into an MCP client
Claude Code (`.mcp.json` in your project, or `claude mcp add`):
```json
{
"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.
## Tools
| 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.
## What a verdict looks like
[`examples/US-LEND-009-not-ready.md`](examples/US-LEND-009-not-ready.md) is a
deliberately bad user story. Real output:
```json
{
"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.
## Making it your standard
The Python in `src/` contains no process knowledge. Fork the repository, edit the
YAML in [`standard/`](standard/), and you are serving your own standard over the
same protocol:
| File | What it defines |
|---|---|
| [`categories.yaml`](standard/categories.yaml) | Phases of delivery, their exit artifacts and their tasks. |
| [`artifact-types.yaml`](standard/artifact-types.yaml) | The artifact catalog: owning category, owner role, id convention, template. |
| [`roles.yaml`](standard/roles.yaml) | Roles and the fallback chain used when one is not staffed. |
| [`rules.yaml`](standard/rules.yaml) | Definition of Ready and Definition of Done, per artifact type. |
| [`glossary.yaml`](standard/glossary.yaml) | The domain terms and their agreed meaning. |
| [`templates/`](standard/templates) | One Markdown template per artifact type. |
Run `python -m sdlc_mcp --self-check` after editing. It will tell you what you
broke.
### Rule kinds
| 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`](src/sdlc_mcp/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.
## Layout
```
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
```
## Design notes
- **`links_resolve` does 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. Pass `known_ids` and
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 demo domain
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.
## Licence
MIT - see [LICENSE](LICENSE).
TDQS
Scored across 8 tools
Each tool targets a distinct operation: category lookup, artifact type/template retrieval, validation, glossary lookup, task planning, and example listing. The list/get pairs are clearly separated by singular detail vs. plural overview.
Most tools follow a clear verb_noun pattern such as get_work_category, list_artifact_types, validate_artifact, and lookup_glossary_term. The outlier is next_tasks, which is not verb-led and breaks the otherwise consistent naming convention.
Eight tools is a well-scoped size for an SDLC standard assistant. Each tool covers a meaningful part of the workflow without redundancy or bloat.
The core standard, template, validation, glossary, and task-planning capabilities are present. However, the set tells agents to read example artifacts before writing their own, yet only offers list_examples with no way to retrieve a specific example, leaving an obvious gap in that workflow.