Skip to content

Commit 0b4044d

Browse files
ryderstormclaude
andauthored
Feat/implement mcp (#2)
* build: configure dev tooling for pre-commit - add pre-commit config with ruff, ty, and markdownlint hooks - pin dev toolchain versions in pyproject and lockfile * docs: add spec for initial MCP implementation * feat: establish FastMCP server foundation - Implement config module for workspace paths, transports, and logging - Build prompts loader to ingest Markdown prompts with YAML frontmatter - Create application factory registering prompts with FastMCP - Add pytest fixtures and tests for prompt loading - Configure hatchling build system for package distribution Completes tasks 1.1-1.5 from spec 0001 * docs: add operations guide and update README - Create comprehensive operations.md with deployment and configuration details - Add Quick Start section to README with installation and usage examples - Document STDIO and HTTP transport options - Include MCP client integration examples for Claude Desktop and VS Code Completes task 1.6 from spec 0001 * chore: mark task 1.0 as complete All subtasks of 1.0 (Establish FastMCP server foundation) are now complete: - Package layout and configuration - Config module with environment overrides - Prompts loader with YAML frontmatter parsing - Application factory with FastMCP integration - Pytest fixtures and comprehensive tests - Documentation for operations and quick start * refactor: adopt standard FastMCP server.py convention - Create server.py as the standard entrypoint (follows FastMCP best practices) - CLI now auto-discovers 'mcp' instance without explicit path - Update all documentation to use 'fastmcp run server.py' instead of '__init__.py:app' - Simplifies command from 'uvx fastmcp dev mcp_server/__init__.py:app' to 'uvx fastmcp dev server.py' This aligns with FastMCP conventions where the CLI looks for 'mcp', 'server', or 'app' instances at module level. * fix: remove unnecessary bootstrap file * docs: update task status and mark deferred features Update task documentation to reflect current implementation status: - Mark Task 2.0 as unnecessary (ResourceTemplates not required) - Document deferral of HTTP hardening to issue #3 - Document deferral of K8s packaging to issue #4 - Document deferral of protocol extensions to issue #5 - Add status notes to specification slices for clarity These updates provide clear tracking of completed work versus features deferred to focused future implementations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * fix(prompts): construct prompt content with TextContent * chore: rebase cleanup * refactor(prompts): model markdown prompt metadata - Introduce a MarkdownPrompt helper that normalizes frontmatter metadata - Ensures prompt registrations emit deterministic kwargs - Update the loader, prompts, and tests to consume the new structure * chore: linter fix * fix: address coderabbit review feedback * chore: remove pytest-anyio package, it's not necessary see https://pypi.org/project/pytest-anyio/ --------- Co-authored-by: Claude <[email protected]>
1 parent 4a6bfea commit 0b4044d

18 files changed

+1672
-696
lines changed

docs/operations.md

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
# Operations Guide
2+
3+
This guide covers deployment, configuration, and operation of the Spec-Driven Development MCP server.
4+
5+
## Local Development
6+
7+
### Prerequisites
8+
9+
- Python 3.12 or higher
10+
- [uv](https://docs.astral.sh/uv/) package manager
11+
12+
### Setup
13+
14+
1. Clone the repository and navigate to the project directory
15+
2. Install dependencies:
16+
17+
```bash
18+
uv sync
19+
```
20+
21+
3. Run tests to verify setup:
22+
23+
```bash
24+
uv run pytest
25+
```
26+
27+
### Running the Server
28+
29+
#### STDIO Transport (Default)
30+
31+
The STDIO transport is ideal for local development and integration with MCP clients like Claude Desktop:
32+
33+
```bash
34+
uvx fastmcp run server.py
35+
```
36+
37+
Or using the development server with the MCP Inspector:
38+
39+
```bash
40+
uvx fastmcp dev server.py
41+
```
42+
43+
This will start the server and open the MCP Inspector in your browser, allowing you to:
44+
45+
- Browse available prompts, resources, and tools
46+
- Test prompt invocations
47+
- View server logs and metrics
48+
49+
#### HTTP Transport
50+
51+
For remote access or integration with web-based clients:
52+
53+
```bash
54+
uvx fastmcp run server.py --transport http --port 8000
55+
```
56+
57+
The server will be available at `http://localhost:8000`.
58+
59+
## Configuration
60+
61+
The server can be configured via environment variables:
62+
63+
### Workspace Configuration
64+
65+
- `SDD_WORKSPACE_ROOT`: Root directory for generated specs and tasks (default: `/workspace`)
66+
- `SDD_PROMPTS_DIR`: Directory containing prompt templates (default: `./prompts`)
67+
68+
### Transport Configuration
69+
70+
- `SDD_TRANSPORT`: Transport type - `stdio` or `http` (default: `stdio`)
71+
- `SDD_HTTP_HOST`: HTTP server host (default: `0.0.0.0`)
72+
- `SDD_HTTP_PORT`: HTTP server port (default: `8000`)
73+
74+
### Logging Configuration
75+
76+
- `SDD_LOG_LEVEL`: Logging level - `DEBUG`, `INFO`, `WARNING`, `ERROR` (default: `INFO`)
77+
- `SDD_LOG_FORMAT`: Log format - `json` or `text` (default: `json`)
78+
79+
### CORS Configuration (HTTP only)
80+
81+
- `SDD_CORS_ENABLED`: Enable CORS (default: `true`)
82+
- `SDD_CORS_ORIGINS`: Comma-separated list of allowed origins (default: `*`)
83+
84+
### Example
85+
86+
```bash
87+
export SDD_WORKSPACE_ROOT=/home/user/workspace
88+
export SDD_LOG_LEVEL=DEBUG
89+
uvx fastmcp run server.py
90+
```
91+
92+
## MCP Client Integration
93+
94+
### Claude Desktop
95+
96+
Add the following to your Claude Desktop configuration (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
97+
98+
```json
99+
{
100+
"mcpServers": {
101+
"spec-driven-development": {
102+
"command": "uvx",
103+
"args": ["fastmcp", "run", "/path/to/spec-driven-development-mcp/server.py"]
104+
}
105+
}
106+
}
107+
```
108+
109+
### VS Code MCP Plugin
110+
111+
1. Install the MCP plugin for VS Code
112+
2. Add the server configuration to your workspace settings:
113+
114+
```json
115+
{
116+
"mcp.servers": {
117+
"spec-driven-development": {
118+
"command": "uvx",
119+
"args": ["fastmcp", "run", "/path/to/spec-driven-development-mcp/server.py"]
120+
}
121+
}
122+
}
123+
```
124+
125+
### FastMCP Inspector
126+
127+
The FastMCP Inspector provides a web-based interface for testing and debugging:
128+
129+
```bash
130+
uvx fastmcp dev server.py
131+
```
132+
133+
This will:
134+
135+
1. Start the MCP server
136+
2. Start the Inspector proxy
137+
3. Open the Inspector UI in your browser
138+
139+
## Testing
140+
141+
### Run All Tests
142+
143+
```bash
144+
uv run pytest
145+
```
146+
147+
### Run with Coverage
148+
149+
```bash
150+
uv run pytest --cov=mcp_server --cov-report=html
151+
```
152+
153+
### Run Specific Test File
154+
155+
```bash
156+
uv run pytest tests/test_prompts.py -v
157+
```
158+
159+
## Troubleshooting
160+
161+
### Server Won't Start
162+
163+
1. Verify Python version: `python --version` (should be 3.12+)
164+
2. Reinstall dependencies: `uv sync`
165+
3. Check for port conflicts (if using HTTP transport)
166+
167+
### Prompts Not Loading
168+
169+
1. Verify prompts directory exists and contains `.md` files
170+
2. Check that prompt files have valid YAML frontmatter
171+
3. Review server logs for parsing errors
172+
173+
### Tests Failing
174+
175+
1. Ensure all dependencies are installed: `uv sync`
176+
2. Run tests with verbose output: `uv run pytest -v`
177+
3. Check for environment variable conflicts

hello.py

Lines changed: 0 additions & 6 deletions
This file was deleted.

mcp_server/__init__.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
"""Spec-Driven Development MCP Server.
2+
3+
A FastMCP-based server providing prompts, resources, and tools for
4+
spec-driven development workflows.
5+
"""
6+
7+
from fastmcp import FastMCP
8+
9+
from .config import config
10+
from .prompts_loader import register_prompts
11+
12+
__version__ = "0.1.0"
13+
14+
15+
def create_app() -> FastMCP:
16+
"""Create and configure the FastMCP application.
17+
18+
Returns:
19+
Configured FastMCP server instance
20+
"""
21+
# Initialize FastMCP server
22+
mcp = FastMCP(name="spec-driven-development-mcp")
23+
24+
# Load prompts from the prompts directory and register them
25+
register_prompts(mcp, config.prompts_dir)
26+
27+
@mcp.tool(name="basic-example", description="Return a static message for testing.")
28+
def basic_example_tool() -> str:
29+
"""Basic example tool used to verify MCP tool registration."""
30+
31+
return "Basic example tool invoked successfully."
32+
33+
# TODO: Register resources (Task 2.1)
34+
# TODO: Register tools (Task 5.1)
35+
# TODO: Setup notifications (Task 5.2)
36+
# TODO: Setup sampling (Task 5.3)
37+
# TODO: Setup logging (Task 5.4)
38+
39+
return mcp

mcp_server/config.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
"""Runtime configuration for the SDD MCP server.
2+
3+
Provides testable defaults with environment variable overrides for:
4+
- Workspace paths
5+
- Transport options (STDIO/HTTP)
6+
- Logging configuration
7+
"""
8+
9+
import os
10+
from pathlib import Path
11+
from typing import Literal
12+
13+
TransportType = Literal["stdio", "http"]
14+
15+
16+
class Config:
17+
"""Runtime configuration with environment overrides."""
18+
19+
def __init__(self) -> None:
20+
"""Initialize configuration with defaults and environment overrides."""
21+
# Workspace paths
22+
self.workspace_root = Path(os.getenv("SDD_WORKSPACE_ROOT", "/workspace")).resolve()
23+
self.prompts_dir = Path(
24+
os.getenv("SDD_PROMPTS_DIR", str(Path(__file__).parent.parent / "prompts"))
25+
).resolve()
26+
27+
# Transport configuration
28+
self.transport: TransportType = os.getenv("SDD_TRANSPORT", "stdio") # type: ignore
29+
self.http_host = os.getenv("SDD_HTTP_HOST", "0.0.0.0")
30+
port_str = os.getenv("SDD_HTTP_PORT", "8000")
31+
try:
32+
self.http_port = int(port_str)
33+
if not 1 <= self.http_port <= 65535:
34+
raise ValueError(f"Port must be between 1 and 65535, got {self.http_port}")
35+
except ValueError as exc:
36+
raise ValueError(f"Invalid SDD_HTTP_PORT value '{port_str}': {exc}") from exc
37+
38+
# Logging configuration
39+
self.log_level = os.getenv("SDD_LOG_LEVEL", "INFO")
40+
self.log_format = os.getenv("SDD_LOG_FORMAT", "json") # json or text
41+
42+
# CORS configuration for HTTP transport
43+
self.cors_enabled = os.getenv("SDD_CORS_ENABLED", "true").lower() == "true"
44+
self.cors_origins = [
45+
origin.strip()
46+
for origin in os.getenv("SDD_CORS_ORIGINS", "*").split(",")
47+
if origin.strip()
48+
]
49+
50+
def ensure_workspace_dirs(self) -> None:
51+
"""Create workspace directories if they don't exist."""
52+
self.workspace_root.mkdir(parents=True, exist_ok=True)
53+
(self.workspace_root / "specs").mkdir(exist_ok=True)
54+
(self.workspace_root / "tasks").mkdir(exist_ok=True)
55+
56+
def __repr__(self) -> str:
57+
"""Return string representation of configuration."""
58+
return (
59+
f"Config(workspace_root={self.workspace_root}, "
60+
f"prompts_dir={self.prompts_dir}, "
61+
f"transport={self.transport}, "
62+
f"http_host={self.http_host}, "
63+
f"http_port={self.http_port}, "
64+
f"log_level={self.log_level})"
65+
)
66+
67+
68+
# Global configuration instance
69+
config = Config()

0 commit comments

Comments
 (0)