feat: Add Sequential Thinking to Code Review + Frontend Validation

Enhanced code review and frontend validation with intelligent triggers:

Code Review Agent Enhancement:
- Added Sequential Thinking MCP integration for complex issues
- Triggers on 2+ rejections or 3+ critical issues
- New escalation format with root cause analysis
- Comprehensive solution strategies with trade-off evaluation
- Educational feedback to break rejection cycles
- Files: .claude/agents/code-review.md (+308 lines)
- Docs: CODE_REVIEW_ST_ENHANCEMENT.md, CODE_REVIEW_ST_TESTING.md

Frontend Design Skill Enhancement:
- Automatic invocation for ANY UI change
- Comprehensive validation checklist (200+ checkpoints)
- 8 validation categories (visual, interactive, responsive, a11y, etc.)
- 3 validation levels (quick, standard, comprehensive)
- Integration with code review workflow
- Files: .claude/skills/frontend-design/SKILL.md (+120 lines)
- Docs: UI_VALIDATION_CHECKLIST.md (462 lines), AUTOMATIC_VALIDATION_ENHANCEMENT.md (587 lines)

Settings Optimization:
- Repaired .claude/settings.local.json (fixed m365 pattern)
- Reduced permissions from 49 to 33 (33% reduction)
- Removed duplicates, sorted alphabetically
- Created SETTINGS_PERMISSIONS.md documentation

Checkpoint Command Enhancement:
- Dual checkpoint system (git + database)
- Saves session context to API for cross-machine recall
- Includes git metadata in database context
- Files: .claude/commands/checkpoint.md (+139 lines)

Decision Rationale:
- Sequential Thinking MCP breaks rejection cycles by identifying root causes
- Automatic frontend validation catches UI issues before code review
- Dual checkpoints enable complete project memory across machines
- Settings optimization improves maintainability

Total: 1,200+ lines of documentation and enhancements

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-17 16:23:52 -07:00
parent 359c2cf1b4
commit 75ce1c2fd5
1089 changed files with 149506 additions and 5 deletions

View File

@@ -0,0 +1,447 @@
# Feature Management MCP Server
**Source:** AutoCoder project
**Type:** Model Context Protocol (MCP) Server
**Purpose:** Manage autonomous coding features with priority-based workflow
---
## Overview
The Feature Management MCP Server provides tools for managing a feature queue in an autonomous coding workflow. It replaces the previous FastAPI-based REST API with a native MCP implementation that integrates directly with Claude Code.
**Key Capabilities:**
- Track feature implementation progress
- Priority-based feature queue management
- Mark features as passing/in-progress/skipped
- Regression testing support
- Bulk feature creation from app specifications
---
## Architecture
### Database
- **Backend:** SQLite (via SQLAlchemy)
- **Migration:** Auto-migrates legacy JSON to SQLite
- **Storage:** `{PROJECT_DIR}/features.db`
### Feature Model
```python
{
"id": int, # Auto-increment primary key
"priority": int, # Lower = higher priority
"category": str, # Feature category (e.g., "authentication", "ui")
"name": str, # Short feature name
"description": str, # Detailed description
"steps": list[str], # Implementation/test steps
"passes": bool, # True if feature is implemented and working
"in_progress": bool # True if currently being worked on
}
```
---
## Available Tools
### 1. feature_get_stats
**Purpose:** Get overall progress statistics
**Usage:**
```python
feature_get_stats()
```
**Returns:**
```json
{
"passing": 42,
"in_progress": 1,
"total": 100,
"percentage": 42.0
}
```
**When to use:** At the start/end of coding sessions to track progress
---
### 2. feature_get_next
**Purpose:** Get the next feature to implement
**Usage:**
```python
feature_get_next()
```
**Returns:**
```json
{
"id": 15,
"priority": 15,
"category": "authentication",
"name": "User login endpoint",
"description": "Create POST /api/auth/login endpoint...",
"steps": [
"Create SQLAlchemy User model",
"Implement login route",
"Add JWT token generation",
"Write tests"
],
"passes": false,
"in_progress": false
}
```
**When to use:** At the start of each implementation cycle
---
### 3. feature_mark_passing
**Purpose:** Mark a feature as successfully implemented
**Usage:**
```python
feature_mark_passing(feature_id=15)
```
**Returns:** Updated feature object with `passes=true`, `in_progress=false`
**When to use:** After implementing and verifying a feature works
---
### 4. feature_mark_in_progress
**Purpose:** Mark a feature as being worked on
**Usage:**
```python
feature_mark_in_progress(feature_id=15)
```
**Returns:** Updated feature object with `in_progress=true`
**When to use:** Immediately after `feature_get_next()` to claim the feature
**Important:** Prevents other parallel agent sessions from working on the same feature
---
### 5. feature_skip
**Purpose:** Move a feature to the end of the queue
**Usage:**
```python
feature_skip(feature_id=15)
```
**Returns:**
```json
{
"id": 15,
"name": "User login endpoint",
"old_priority": 15,
"new_priority": 101,
"message": "Feature 'User login endpoint' moved to end of queue"
}
```
**When to use:**
- Feature has unmet dependencies
- External blockers (missing assets, unclear requirements)
- Technical prerequisites needed
---
### 6. feature_clear_in_progress
**Purpose:** Reset in-progress status
**Usage:**
```python
feature_clear_in_progress(feature_id=15)
```
**Returns:** Updated feature object with `in_progress=false`
**When to use:**
- Abandoning a feature midway
- Manually unsticking a stuck feature
- Resetting state after errors
---
### 7. feature_get_for_regression
**Purpose:** Get random passing features for regression testing
**Usage:**
```python
feature_get_for_regression(limit=3) # 1-10, default 3
```
**Returns:**
```json
{
"features": [
{"id": 3, "name": "User registration", "passes": true, ...},
{"id": 7, "name": "Login validation", "passes": true, ...},
{"id": 12, "name": "Password reset", "passes": true, ...}
],
"count": 3
}
```
**When to use:** After implementing new features to verify no regressions
---
### 8. feature_create_bulk
**Purpose:** Create multiple features at once
**Usage:**
```python
feature_create_bulk(features=[
{
"category": "authentication",
"name": "User registration",
"description": "Allow users to create accounts...",
"steps": ["Create User model", "Create POST /register", "Add validation"]
},
{
"category": "authentication",
"name": "User login",
"description": "Allow users to log in...",
"steps": ["Create POST /login", "Add JWT tokens", "Write tests"]
}
])
```
**Returns:**
```json
{
"created": 2
}
```
**When to use:** Initializing a new project from an app specification
---
## Installation & Configuration
### 1. Install Dependencies
The MCP server requires SQLAlchemy and FastMCP. In the AutoCoder project these are already included, but for standalone use:
```bash
pip install sqlalchemy fastmcp pydantic
```
### 2. Configure Claude Desktop
Add to `~/.config/claude/claude_desktop_config.json` (macOS/Linux) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
```json
{
"mcpServers": {
"features": {
"command": "python",
"args": ["D:\\ClaudeTools\\mcp-servers\\feature-management\\feature_mcp.py"],
"env": {
"PROJECT_DIR": "D:\\ClaudeTools\\projects\\your-project"
}
}
}
}
```
### 3. Set PROJECT_DIR
The `PROJECT_DIR` environment variable determines where the SQLite database is stored:
```bash
# Windows (PowerShell)
$env:PROJECT_DIR="D:\ClaudeTools\projects\your-project"
# Linux/macOS
export PROJECT_DIR="/path/to/your/project"
```
The database will be created at `{PROJECT_DIR}/features.db`
---
## Typical Workflow
### Autonomous Coding Agent Session
```python
# 1. Check progress at start of session
stats = feature_get_stats()
# Output: 42/100 features passing (42.0%)
# 2. Get next feature to work on
next_feature = feature_get_next()
# Output: Feature #15 - "User login endpoint"
# 3. Mark it in-progress (claim it)
feature_mark_in_progress(feature_id=15)
# 4. Implement the feature
# ... write code, run tests, verify ...
# 5. Mark as passing when done
feature_mark_passing(feature_id=15)
# 6. Optional: Run regression tests
regression = feature_get_for_regression(limit=5)
# Output: 5 random passing features to re-test
```
### Handling Blockers
```python
# Get next feature
next_feature = feature_get_next()
# Feature #20 - "OAuth integration"
# Realize it depends on feature #25 which isn't done yet
feature_skip(feature_id=20)
# Feature moved to end of queue (priority 101)
# Get actual next feature
next_feature = feature_get_next()
# Feature #21 - "Email validation"
```
---
## Database Schema
```sql
CREATE TABLE features (
id INTEGER PRIMARY KEY AUTOINCREMENT,
priority INTEGER NOT NULL,
category VARCHAR(100) NOT NULL,
name VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
steps TEXT NOT NULL, -- JSON array of strings
passes BOOLEAN DEFAULT 0,
in_progress BOOLEAN DEFAULT 0
);
CREATE INDEX idx_priority ON features(priority);
CREATE INDEX idx_passes ON features(passes);
```
---
## Integration with ClaudeTools
This MCP server can be integrated with the ClaudeTools context recall system:
1. Store feature completion decisions in `decision_logs` table
2. Save feature implementation context in `conversation_contexts`
3. Track feature work sessions in `sessions` table
4. Log feature-related errors in `problem_solutions`
**Example:**
```python
# After marking feature passing, log the decision
POST /api/decision-logs
{
"project_id": "uuid",
"decision_type": "technical",
"decision_text": "Implemented user login endpoint",
"rationale": "Feature #15 completed with JWT authentication",
"tags": ["authentication", "feature-15", "jwt"]
}
```
---
## Troubleshooting
### Database not found
**Error:** `Database not initialized`
**Solution:** Ensure `PROJECT_DIR` environment variable is set and writable
### Migration issues
**Error:** `Migration failed`
**Solution:** The server auto-migrates legacy JSON (`features.json`) to SQLite. If you have custom JSON structure, you may need to adjust the migration in `api/migration.py`
### All features showing in-progress
**Problem:** Features stuck in `in_progress=true` state
**Solution:**
```python
# Clear all stuck features
for feature_id in [1, 2, 3, ...]:
feature_clear_in_progress(feature_id=feature_id)
```
Or directly in SQLite:
```sql
UPDATE features SET in_progress = 0 WHERE in_progress = 1;
```
---
## Differences from REST API
The original AutoCoder project used a FastAPI REST server. This MCP server provides the same functionality with these improvements:
| Feature | REST API | MCP Server |
|---------|----------|------------|
| Integration | HTTP requests | Native Claude MCP tools |
| Authentication | API keys | Built into MCP protocol |
| Type safety | Manual validation | Pydantic models |
| Error handling | HTTP status codes | JSON error responses |
| Database | JSON files | SQLite (with JSON migration) |
| Startup time | ~1-2 seconds | ~100ms |
---
## File Structure
```
mcp-servers/feature-management/
├── feature_mcp.py # Main MCP server implementation
├── __init__.py # Python module marker
├── README.md # This file
└── config.example.json # Example configuration
```
---
## Dependencies
**Required:**
- `fastmcp` - MCP protocol implementation
- `sqlalchemy` - Database ORM
- `pydantic` - Data validation
**Optional:**
- `api.database` - Feature model and database setup (from AutoCoder)
- `api.migration` - JSON to SQLite migration (from AutoCoder)
---
## License
Same license as the AutoCoder project (see LICENSE.txt in AutoCoder repository)
---
## See Also
- `.claude/commands/create-spec.md` - Create app specification
- `.claude/commands/checkpoint.md` - Create development checkpoint
- `.claude/skills/frontend-design/` - Frontend design skill
- `.claude/templates/` - Prompt templates for autonomous coding
---
**Last Updated:** 2026-01-17
**Status:** Production-ready (ported from AutoCoder)

View File

@@ -0,0 +1 @@
"""MCP Server Package for Feature Management."""

View File

@@ -0,0 +1,81 @@
{
"_comment": "Example MCP server configuration for Feature Management",
"_usage": "Add this to your Claude Desktop configuration file",
"_config_locations": {
"windows": "%APPDATA%\\Claude\\claude_desktop_config.json",
"macos": "~/Library/Application Support/Claude/claude_desktop_config.json",
"linux": "~/.config/claude/claude_desktop_config.json"
},
"mcpServers": {
"features": {
"command": "python",
"args": ["D:\\ClaudeTools\\mcp-servers\\feature-management\\feature_mcp.py"],
"env": {
"PROJECT_DIR": "D:\\ClaudeTools\\projects\\your-project-name",
"_comment_PROJECT_DIR": "Directory where features.db will be created/stored"
}
}
},
"_notes": [
"Replace 'D:\\ClaudeTools' with your actual ClaudeTools installation path",
"Replace 'your-project-name' with your actual project name",
"The PROJECT_DIR must be writable by the user running Claude",
"Database will be created at {PROJECT_DIR}/features.db",
"On Unix systems, use forward slashes: /path/to/ClaudeTools",
"After editing, restart Claude Desktop for changes to take effect"
],
"_examples": {
"windows_absolute": {
"command": "python",
"args": ["D:\\ClaudeTools\\mcp-servers\\feature-management\\feature_mcp.py"],
"env": {
"PROJECT_DIR": "D:\\ClaudeTools\\projects\\my-web-app"
}
},
"macos_absolute": {
"command": "python3",
"args": ["/Users/username/ClaudeTools/mcp-servers/feature-management/feature_mcp.py"],
"env": {
"PROJECT_DIR": "/Users/username/ClaudeTools/projects/my-web-app"
}
},
"linux_absolute": {
"command": "python3",
"args": ["/home/username/ClaudeTools/mcp-servers/feature-management/feature_mcp.py"],
"env": {
"PROJECT_DIR": "/home/username/ClaudeTools/projects/my-web-app"
}
},
"with_venv": {
"_comment": "Using a virtual environment",
"command": "D:\\ClaudeTools\\venv\\Scripts\\python.exe",
"args": ["D:\\ClaudeTools\\mcp-servers\\feature-management\\feature_mcp.py"],
"env": {
"PROJECT_DIR": "D:\\ClaudeTools\\projects\\my-web-app"
}
}
},
"_full_config_example": {
"mcpServers": {
"features": {
"command": "python",
"args": ["D:\\ClaudeTools\\mcp-servers\\feature-management\\feature_mcp.py"],
"env": {
"PROJECT_DIR": "D:\\ClaudeTools\\projects\\my-web-app"
}
},
"other-mcp-server": {
"command": "python",
"args": ["path/to/other/server.py"]
}
},
"globalShortcut": "Ctrl+Space"
}
}

View File

@@ -0,0 +1,417 @@
#!/usr/bin/env python3
"""
MCP Server for Feature Management
==================================
Provides tools to manage features in the autonomous coding system,
replacing the previous FastAPI-based REST API.
Tools:
- feature_get_stats: Get progress statistics
- feature_get_next: Get next feature to implement
- feature_get_for_regression: Get random passing features for testing
- feature_mark_passing: Mark a feature as passing
- feature_skip: Skip a feature (move to end of queue)
- feature_mark_in_progress: Mark a feature as in-progress
- feature_clear_in_progress: Clear in-progress status
- feature_create_bulk: Create multiple features at once
"""
import json
import os
import sys
from contextlib import asynccontextmanager
from pathlib import Path
from typing import Annotated
from mcp.server.fastmcp import FastMCP
from pydantic import BaseModel, Field
from sqlalchemy.sql.expression import func
# Add parent directory to path so we can import from api module
sys.path.insert(0, str(Path(__file__).parent.parent))
from api.database import Feature, create_database
from api.migration import migrate_json_to_sqlite
# Configuration from environment
PROJECT_DIR = Path(os.environ.get("PROJECT_DIR", ".")).resolve()
# Pydantic models for input validation
class MarkPassingInput(BaseModel):
"""Input for marking a feature as passing."""
feature_id: int = Field(..., description="The ID of the feature to mark as passing", ge=1)
class SkipFeatureInput(BaseModel):
"""Input for skipping a feature."""
feature_id: int = Field(..., description="The ID of the feature to skip", ge=1)
class MarkInProgressInput(BaseModel):
"""Input for marking a feature as in-progress."""
feature_id: int = Field(..., description="The ID of the feature to mark as in-progress", ge=1)
class ClearInProgressInput(BaseModel):
"""Input for clearing in-progress status."""
feature_id: int = Field(..., description="The ID of the feature to clear in-progress status", ge=1)
class RegressionInput(BaseModel):
"""Input for getting regression features."""
limit: int = Field(default=3, ge=1, le=10, description="Maximum number of passing features to return")
class FeatureCreateItem(BaseModel):
"""Schema for creating a single feature."""
category: str = Field(..., min_length=1, max_length=100, description="Feature category")
name: str = Field(..., min_length=1, max_length=255, description="Feature name")
description: str = Field(..., min_length=1, description="Detailed description")
steps: list[str] = Field(..., min_length=1, description="Implementation/test steps")
class BulkCreateInput(BaseModel):
"""Input for bulk creating features."""
features: list[FeatureCreateItem] = Field(..., min_length=1, description="List of features to create")
# Global database session maker (initialized on startup)
_session_maker = None
_engine = None
@asynccontextmanager
async def server_lifespan(server: FastMCP):
"""Initialize database on startup, cleanup on shutdown."""
global _session_maker, _engine
# Create project directory if it doesn't exist
PROJECT_DIR.mkdir(parents=True, exist_ok=True)
# Initialize database
_engine, _session_maker = create_database(PROJECT_DIR)
# Run migration if needed (converts legacy JSON to SQLite)
migrate_json_to_sqlite(PROJECT_DIR, _session_maker)
yield
# Cleanup
if _engine:
_engine.dispose()
# Initialize the MCP server
mcp = FastMCP("features", lifespan=server_lifespan)
def get_session():
"""Get a new database session."""
if _session_maker is None:
raise RuntimeError("Database not initialized")
return _session_maker()
@mcp.tool()
def feature_get_stats() -> str:
"""Get statistics about feature completion progress.
Returns the number of passing features, in-progress features, total features,
and completion percentage. Use this to track overall progress of the implementation.
Returns:
JSON with: passing (int), in_progress (int), total (int), percentage (float)
"""
session = get_session()
try:
total = session.query(Feature).count()
passing = session.query(Feature).filter(Feature.passes == True).count()
in_progress = session.query(Feature).filter(Feature.in_progress == True).count()
percentage = round((passing / total) * 100, 1) if total > 0 else 0.0
return json.dumps({
"passing": passing,
"in_progress": in_progress,
"total": total,
"percentage": percentage
}, indent=2)
finally:
session.close()
@mcp.tool()
def feature_get_next() -> str:
"""Get the highest-priority pending feature to work on.
Returns the feature with the lowest priority number that has passes=false.
Use this at the start of each coding session to determine what to implement next.
Returns:
JSON with feature details (id, priority, category, name, description, steps, passes, in_progress)
or error message if all features are passing.
"""
session = get_session()
try:
feature = (
session.query(Feature)
.filter(Feature.passes == False)
.order_by(Feature.priority.asc(), Feature.id.asc())
.first()
)
if feature is None:
return json.dumps({"error": "All features are passing! No more work to do."})
return json.dumps(feature.to_dict(), indent=2)
finally:
session.close()
@mcp.tool()
def feature_get_for_regression(
limit: Annotated[int, Field(default=3, ge=1, le=10, description="Maximum number of passing features to return")] = 3
) -> str:
"""Get random passing features for regression testing.
Returns a random selection of features that are currently passing.
Use this to verify that previously implemented features still work
after making changes.
Args:
limit: Maximum number of features to return (1-10, default 3)
Returns:
JSON with: features (list of feature objects), count (int)
"""
session = get_session()
try:
features = (
session.query(Feature)
.filter(Feature.passes == True)
.order_by(func.random())
.limit(limit)
.all()
)
return json.dumps({
"features": [f.to_dict() for f in features],
"count": len(features)
}, indent=2)
finally:
session.close()
@mcp.tool()
def feature_mark_passing(
feature_id: Annotated[int, Field(description="The ID of the feature to mark as passing", ge=1)]
) -> str:
"""Mark a feature as passing after successful implementation.
Updates the feature's passes field to true and clears the in_progress flag.
Use this after you have implemented the feature and verified it works correctly.
Args:
feature_id: The ID of the feature to mark as passing
Returns:
JSON with the updated feature details, or error if not found.
"""
session = get_session()
try:
feature = session.query(Feature).filter(Feature.id == feature_id).first()
if feature is None:
return json.dumps({"error": f"Feature with ID {feature_id} not found"})
feature.passes = True
feature.in_progress = False
session.commit()
session.refresh(feature)
return json.dumps(feature.to_dict(), indent=2)
finally:
session.close()
@mcp.tool()
def feature_skip(
feature_id: Annotated[int, Field(description="The ID of the feature to skip", ge=1)]
) -> str:
"""Skip a feature by moving it to the end of the priority queue.
Use this when a feature cannot be implemented yet due to:
- Dependencies on other features that aren't implemented yet
- External blockers (missing assets, unclear requirements)
- Technical prerequisites that need to be addressed first
The feature's priority is set to max_priority + 1, so it will be
worked on after all other pending features. Also clears the in_progress
flag so the feature returns to "pending" status.
Args:
feature_id: The ID of the feature to skip
Returns:
JSON with skip details: id, name, old_priority, new_priority, message
"""
session = get_session()
try:
feature = session.query(Feature).filter(Feature.id == feature_id).first()
if feature is None:
return json.dumps({"error": f"Feature with ID {feature_id} not found"})
if feature.passes:
return json.dumps({"error": "Cannot skip a feature that is already passing"})
old_priority = feature.priority
# Get max priority and set this feature to max + 1
max_priority_result = session.query(Feature.priority).order_by(Feature.priority.desc()).first()
new_priority = (max_priority_result[0] + 1) if max_priority_result else 1
feature.priority = new_priority
feature.in_progress = False
session.commit()
session.refresh(feature)
return json.dumps({
"id": feature.id,
"name": feature.name,
"old_priority": old_priority,
"new_priority": new_priority,
"message": f"Feature '{feature.name}' moved to end of queue"
}, indent=2)
finally:
session.close()
@mcp.tool()
def feature_mark_in_progress(
feature_id: Annotated[int, Field(description="The ID of the feature to mark as in-progress", ge=1)]
) -> str:
"""Mark a feature as in-progress. Call immediately after feature_get_next().
This prevents other agent sessions from working on the same feature.
Use this as soon as you retrieve a feature to work on.
Args:
feature_id: The ID of the feature to mark as in-progress
Returns:
JSON with the updated feature details, or error if not found or already in-progress.
"""
session = get_session()
try:
feature = session.query(Feature).filter(Feature.id == feature_id).first()
if feature is None:
return json.dumps({"error": f"Feature with ID {feature_id} not found"})
if feature.passes:
return json.dumps({"error": f"Feature with ID {feature_id} is already passing"})
if feature.in_progress:
return json.dumps({"error": f"Feature with ID {feature_id} is already in-progress"})
feature.in_progress = True
session.commit()
session.refresh(feature)
return json.dumps(feature.to_dict(), indent=2)
finally:
session.close()
@mcp.tool()
def feature_clear_in_progress(
feature_id: Annotated[int, Field(description="The ID of the feature to clear in-progress status", ge=1)]
) -> str:
"""Clear in-progress status from a feature.
Use this when abandoning a feature or manually unsticking a stuck feature.
The feature will return to the pending queue.
Args:
feature_id: The ID of the feature to clear in-progress status
Returns:
JSON with the updated feature details, or error if not found.
"""
session = get_session()
try:
feature = session.query(Feature).filter(Feature.id == feature_id).first()
if feature is None:
return json.dumps({"error": f"Feature with ID {feature_id} not found"})
feature.in_progress = False
session.commit()
session.refresh(feature)
return json.dumps(feature.to_dict(), indent=2)
finally:
session.close()
@mcp.tool()
def feature_create_bulk(
features: Annotated[list[dict], Field(description="List of features to create, each with category, name, description, and steps")]
) -> str:
"""Create multiple features in a single operation.
Features are assigned sequential priorities based on their order.
All features start with passes=false.
This is typically used by the initializer agent to set up the initial
feature list from the app specification.
Args:
features: List of features to create, each with:
- category (str): Feature category
- name (str): Feature name
- description (str): Detailed description
- steps (list[str]): Implementation/test steps
Returns:
JSON with: created (int) - number of features created
"""
session = get_session()
try:
# Get the starting priority
max_priority_result = session.query(Feature.priority).order_by(Feature.priority.desc()).first()
start_priority = (max_priority_result[0] + 1) if max_priority_result else 1
created_count = 0
for i, feature_data in enumerate(features):
# Validate required fields
if not all(key in feature_data for key in ["category", "name", "description", "steps"]):
return json.dumps({
"error": f"Feature at index {i} missing required fields (category, name, description, steps)"
})
db_feature = Feature(
priority=start_priority + i,
category=feature_data["category"],
name=feature_data["name"],
description=feature_data["description"],
steps=feature_data["steps"],
passes=False,
)
session.add(db_feature)
created_count += 1
session.commit()
return json.dumps({"created": created_count}, indent=2)
except Exception as e:
session.rollback()
return json.dumps({"error": str(e)})
finally:
session.close()
if __name__ == "__main__":
mcp.run()