Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions GET_STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,96 @@ SpecLeft is passive by default — it won't modify anything until you ask.

---

## MCP Server Setup

SpecLeft includes an MCP server that connects directly to AI coding agents like
Claude Code, Cursor, and Windsurf. Once connected, your agent can read specs,
track coverage, and generate test scaffolding without leaving the conversation.

### Prerequisites

- Python 3.10+
- SpecLeft installed (`pip install specleft[mcp]`)

### Option 1: uvx (recommended)

If you have [uv](https://docs.astral.sh/uv/getting-started/installation/)
installed, this is the fastest path. No separate install step is required.

**Claude Code** (`.claude/settings.json`):

```json
{
"mcpServers": {
"specleft": {
"command": "uvx",
"args": ["specleft", "mcp"]
}
}
}
```

**Cursor** (`.cursor/mcp.json`):

```json
{
"mcpServers": {
"specleft": {
"command": "uvx",
"args": ["specleft", "mcp"]
}
}
}
```

### Option 2: pip install

If you do not have `uv`, install SpecLeft first and point your MCP client at
the CLI directly:

```bash
pip install specleft[mcp]
```

**Claude Code** (`.claude/settings.json`):

```json
{
"mcpServers": {
"specleft": {
"command": "specleft",
"args": ["mcp"]
}
}
}
```

**Cursor** (`.cursor/mcp.json`):

```json
{
"mcpServers": {
"specleft": {
"command": "specleft",
"args": ["mcp"]
}
}
}
```

### Verify the connection

After configuration, restart your editor. You should see SpecLeft listed as a
connected MCP server with 3 resources and 1 tool. If the connection fails, run:

```bash
specleft doctor
```

This checks Python version, dependencies, and plugin registration.

---

## 1. Write a PRD

Create a `prd.md` file in your repository root:
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,16 @@ pip install specleft
No config files required.
No test changes required.

---
## MCP Server Setup

SpecLeft includes an MCP server that connects directly to AI coding agents
like Claude Code, Cursor, Codex, and OpenCode. Once connected, your agent can
read specs, track coverage, and generate test scaffolding without leaving the
conversation.

See [GET_STARTED.md](https://github.com/SpecLeft/specleft/blob/main/GET_STARTED.md) for details.

---
## SpecLeft Agent Contract

Expand Down
38 changes: 38 additions & 0 deletions features/feature-mcp-server.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Feature: MCP Server for Agent Discovery
priority: high

## Scenarios

### Scenario: Expose exactly three resources and one tool
- Given the SpecLeft MCP server is running
- When an MCP client lists server resources and tools
- Then the server exposes resources `specleft://contract`, `specleft://guide`, and `specleft://status`
- And the server exposes exactly one tool named `specleft_init`

### Scenario: Contract and guide resources return machine-readable JSON
- Given the SpecLeft MCP server is running
- When an MCP client reads `specleft://contract` and `specleft://guide`
- Then both resources return valid JSON payloads
- And the contract payload includes safety and determinism guarantees
- And the guide payload includes workflow steps and skill file guidance

### Scenario: Status resource signals uninitialised project
- Given an empty workspace with no SpecLeft setup
- When an MCP client reads `specleft://status`
- Then the payload includes `initialised: false`
- And feature and scenario counts are zero

### Scenario: specleft_init bootstraps project safely
- Given an empty workspace with write permissions
- When an MCP client calls the `specleft_init` tool
- Then the tool runs health checks before writing files
- And it creates `.specleft/specs`, `.specleft/policies`, and `.specleft/SKILL.md`
- And repeated calls are idempotent

### Scenario: Agent discovery flow uses resources and init tool
- Given an agent connects to the MCP server
- When the agent reads contract, guide, and status resources
- And status reports `initialised: false`
- And the agent calls `specleft_init`
- Then the workspace is initialised
- And a subsequent status read reports `initialised: true`
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,14 @@ Documentation = "https://github.com/SpecLeft/specleft/tree/main/docs"
Repository = "https://github.com/SpecLeft/specleft"

[project.optional-dependencies]
mcp = [
"fastmcp<3",
]
dev = [
"pytest-cov",
"pytest-subtests",
"pytest-asyncio",
"tiktoken",
"black==26.1.0",
"ruff==0.8.3",
"mypy",
Expand Down
2 changes: 2 additions & 0 deletions src/specleft/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
guide,
init,
license_group,
mcp,
next_command,
plan,
skill_group,
Expand Down Expand Up @@ -53,6 +54,7 @@ def cli() -> None:
cli.add_command(license_group)
cli.add_command(skill_group)
cli.add_command(guide)
cli.add_command(mcp)


__all__ = ["cli"]
Expand Down
2 changes: 2 additions & 0 deletions src/specleft/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from specleft.commands.guide import guide
from specleft.commands.init import init
from specleft.commands.license import license_group
from specleft.commands.mcp import mcp
from specleft.commands.next import next_command
from specleft.commands.plan import plan
from specleft.commands.skill import skill_group
Expand All @@ -28,6 +29,7 @@
"guide",
"init",
"license_group",
"mcp",
"next_command",
"plan",
"skill_group",
Expand Down
23 changes: 23 additions & 0 deletions src/specleft/commands/mcp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) 2026 SpecLeft Contributors

"""MCP command."""

from __future__ import annotations

import click

from specleft.utils.messaging import print_support_footer


@click.command("mcp")
def mcp() -> None:
"""Run the SpecLeft MCP server over stdio."""
try:
from specleft.mcp.server import run_mcp_server

run_mcp_server()
except RuntimeError as exc:
click.secho(str(exc), fg="red", err=True)
print_support_footer()
raise SystemExit(1) from exc
2 changes: 1 addition & 1 deletion src/specleft/commands/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ def build_status_json(
"coverage_percent": summary.coverage_percent,
}
if not verbose:
return summary_payload
return {"initialised": True, **summary_payload}

features: list[dict[str, Any]] = []
by_priority: dict[str, dict[str, int]] = {}
Expand Down
8 changes: 8 additions & 0 deletions src/specleft/mcp/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) 2026 SpecLeft Contributors

"""SpecLeft MCP server package."""

from specleft.mcp.server import build_mcp_server, run_mcp_server

__all__ = ["build_mcp_server", "run_mcp_server"]
22 changes: 22 additions & 0 deletions src/specleft/mcp/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright (c) 2026 SpecLeft Contributors

"""Entrypoint for ``python -m specleft.mcp``."""

from __future__ import annotations

import sys

from specleft.mcp.server import run_mcp_server


def main() -> None:
try:
run_mcp_server()
except RuntimeError as exc:
print(str(exc), file=sys.stderr)
raise SystemExit(1) from exc


if __name__ == "__main__":
main()
Loading
Loading