Initial commit: Clean DSS implementation
Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm
Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)
Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability
Migration completed: $(date)
🤖 Clean migration with full functionality preserved
This commit is contained in:
621
MCP_MIGRATION_GUIDE.md
Normal file
621
MCP_MIGRATION_GUIDE.md
Normal file
@@ -0,0 +1,621 @@
|
||||
# MCP Migration Implementation Guide
|
||||
|
||||
## Phase-by-Phase Implementation Plan
|
||||
|
||||
### Overview
|
||||
|
||||
This guide walks through converting DSS from REST-based to MCP-first architecture with persistent state management.
|
||||
|
||||
**Timeline:** 7-8 weeks
|
||||
**Current Status:** Architecture documented, components audited
|
||||
**Next Step:** Begin Phase 1 - MCP Tool Suite
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Complete MCP Tool Suite (Weeks 1-3)
|
||||
|
||||
### 1.1 Expand MCP Server
|
||||
|
||||
**File:** `tools/dss_mcp/server.py`
|
||||
|
||||
**Current Tools:** 2 (ingest_figma_file, get_status)
|
||||
|
||||
**Target Tools:** 15+
|
||||
|
||||
```python
|
||||
# Project Management
|
||||
- dss_create_project(name, root_path, description) → project_id
|
||||
- dss_list_projects() → [projects]
|
||||
- dss_get_project(project_id) → project
|
||||
- dss_update_project(project_id, updates) → project
|
||||
- dss_delete_project(project_id) → bool
|
||||
|
||||
# Figma Integration
|
||||
- dss_setup_figma_credentials(api_token) → bool
|
||||
- dss_discover_figma_files(project_id) → [files]
|
||||
- dss_add_figma_file(project_id, file_key, name) → manifest
|
||||
- dss_list_figma_files(project_id) → [files]
|
||||
|
||||
# Token Management
|
||||
- dss_sync_tokens(project_id, output_format) → tokens
|
||||
- dss_extract_tokens(figma_file_key) → tokens
|
||||
- dss_validate_tokens(tokens) → report
|
||||
- dss_detect_token_drift(project_id) → drift_report
|
||||
|
||||
# Component Analysis
|
||||
- dss_discover_components(project_id, path) → components
|
||||
- dss_analyze_components(project_id) → analysis
|
||||
- dss_get_quick_wins(project_id, path) → [wins]
|
||||
|
||||
# Status & Info
|
||||
- dss_get_project_status(project_id) → status
|
||||
- dss_get_system_health() → health
|
||||
```
|
||||
|
||||
### 1.2 Credential Management
|
||||
|
||||
**Requirement:** Encrypt sensitive data at rest
|
||||
|
||||
```python
|
||||
# In dss_mcp/security.py
|
||||
class CredentialVault:
|
||||
"""Manages encrypted credential storage"""
|
||||
|
||||
def encrypt_figma_token(token: str) -> str:
|
||||
"""Encrypt token for storage"""
|
||||
# Use cryptography library
|
||||
# Store in database with salt
|
||||
|
||||
def decrypt_figma_token(encrypted: str) -> str:
|
||||
"""Decrypt token for use"""
|
||||
|
||||
def rotate_encryption_key() -> bool:
|
||||
"""Support key rotation"""
|
||||
```
|
||||
|
||||
### 1.3 Operation Queuing
|
||||
|
||||
**Requirement:** Handle long-running operations asynchronously
|
||||
|
||||
```python
|
||||
# In dss_mcp/operations.py
|
||||
class OperationQueue:
|
||||
"""Manages async operations with status tracking"""
|
||||
|
||||
def enqueue(operation_type, args) -> operation_id
|
||||
def get_status(operation_id) -> status
|
||||
def get_result(operation_id) -> result
|
||||
def cancel(operation_id) -> bool
|
||||
```
|
||||
|
||||
### 1.4 Audit Logging
|
||||
|
||||
**Requirement:** Track all operations for compliance and debugging
|
||||
|
||||
```python
|
||||
# In dss_mcp/audit.py
|
||||
class AuditLog:
|
||||
"""Persistent operation history"""
|
||||
|
||||
def log_operation(
|
||||
operation_type: str,
|
||||
args: dict,
|
||||
result: dict,
|
||||
user_id: str,
|
||||
timestamp: datetime
|
||||
) -> log_id
|
||||
|
||||
def get_operation_history(project_id, limit=100) -> [logs]
|
||||
def get_audit_trail(start_date, end_date) -> [logs]
|
||||
```
|
||||
|
||||
### 1.5 Testing MCP Tools
|
||||
|
||||
```bash
|
||||
# Test each tool independently
|
||||
python -m pytest tools/dss_mcp/tests/test_project_tools.py
|
||||
python -m pytest tools/dss_mcp/tests/test_figma_tools.py
|
||||
python -m pytest tools/dss_mcp/tests/test_token_tools.py
|
||||
|
||||
# Integration tests
|
||||
python -m pytest tools/dss_mcp/tests/test_workflows.py
|
||||
|
||||
# End-to-end tests with MCP server
|
||||
python -m pytest tools/dss_mcp/tests/test_e2e.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Persistence Layer (Week 4)
|
||||
|
||||
### 2.1 State Machine for Projects
|
||||
|
||||
**Requirement:** Track project state through workflow
|
||||
|
||||
```python
|
||||
# In tools/storage/state_machine.py
|
||||
from enum import Enum
|
||||
|
||||
class ProjectState(Enum):
|
||||
CREATED = "created"
|
||||
FIGMA_CONFIGURED = "figma_configured"
|
||||
TOKENS_EXTRACTED = "tokens_extracted"
|
||||
COMPONENTS_ANALYZED = "components_analyzed"
|
||||
READY = "ready"
|
||||
|
||||
class ProjectStateMachine:
|
||||
"""Manages valid state transitions"""
|
||||
|
||||
def __init__(self, project_id):
|
||||
self.project_id = project_id
|
||||
self.state = self.load_state()
|
||||
|
||||
def transition(self, action: str) -> bool:
|
||||
"""Validate and apply state transition"""
|
||||
# Only allow valid transitions
|
||||
# Log transition
|
||||
# Update database
|
||||
|
||||
def can_transition(self, action: str) -> bool:
|
||||
"""Check if action is valid for current state"""
|
||||
```
|
||||
|
||||
### 2.2 Operation History
|
||||
|
||||
**Requirement:** Support undo/rollback
|
||||
|
||||
```python
|
||||
# In tools/storage/operation_history.py
|
||||
class OperationHistory:
|
||||
"""Tracks all state-changing operations"""
|
||||
|
||||
def record_operation(
|
||||
project_id,
|
||||
operation: Operation,
|
||||
before_state: dict,
|
||||
after_state: dict
|
||||
) -> history_id
|
||||
|
||||
def get_operation(operation_id) -> Operation
|
||||
def rollback(operation_id) -> bool
|
||||
def get_timeline(project_id) -> [operations]
|
||||
```
|
||||
|
||||
### 2.3 Concurrent Operation Handling
|
||||
|
||||
**Requirement:** Handle multiple operations safely
|
||||
|
||||
```python
|
||||
# In dss_mcp/concurrency.py
|
||||
import asyncio
|
||||
from threading import Lock
|
||||
|
||||
class OperationLock:
|
||||
"""Prevents concurrent modifications to same resource"""
|
||||
|
||||
def acquire(project_id, resource_type) -> lock
|
||||
def release(lock) -> bool
|
||||
|
||||
class OperationScheduler:
|
||||
"""Queues operations and executes sequentially per resource"""
|
||||
|
||||
async def queue_operation(operation) -> operation_id
|
||||
async def process_queue(project_id, resource_type)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: JS UI Migration (Weeks 5-6)
|
||||
|
||||
### 3.1 Remove REST Calls
|
||||
|
||||
**Before:**
|
||||
```javascript
|
||||
// In admin-ui/js/core/ai.js
|
||||
async sendMessage(message) {
|
||||
const response = await fetch('/api/claude/chat', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ message })
|
||||
});
|
||||
const data = await response.json();
|
||||
return data.response;
|
||||
}
|
||||
```
|
||||
|
||||
**After:**
|
||||
```javascript
|
||||
// Delegate to Claude via MCP
|
||||
async sendMessage(message) {
|
||||
return {
|
||||
action: 'mcp_tool_call',
|
||||
tool: 'chat',
|
||||
args: { message }
|
||||
};
|
||||
// Claude handles the REST call or MCP tool call
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 MCP Tool Invocation Layer
|
||||
|
||||
**File:** `admin-ui/js/core/mcp-client.js`
|
||||
|
||||
```javascript
|
||||
class MCPClient {
|
||||
/**
|
||||
* Calls an MCP tool and waits for result
|
||||
*/
|
||||
async callTool(toolName, args) {
|
||||
const response = await fetch('/api/mcp/call', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
tool: toolName,
|
||||
args: args
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Tool ${toolName} failed`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
async listTools() {
|
||||
const response = await fetch('/api/mcp/tools');
|
||||
return await response.json();
|
||||
}
|
||||
}
|
||||
|
||||
// Usage in components
|
||||
const mcp = new MCPClient();
|
||||
const project = await mcp.callTool('dss_create_project', {
|
||||
name: 'web-ui',
|
||||
root_path: './packages/design'
|
||||
});
|
||||
```
|
||||
|
||||
### 3.3 Update AI Chat Component
|
||||
|
||||
**File:** `admin-ui/js/core/ai.js`
|
||||
|
||||
```javascript
|
||||
// Replace REST calls with MCP
|
||||
async handleSendCommand(command) {
|
||||
const parsed = this.ai.parseCommand(command);
|
||||
|
||||
if (parsed) {
|
||||
// Delegate to Claude with MCP tools
|
||||
const result = await this.ai.sendMessage(command, {
|
||||
enableTools: true // Use MCP tools
|
||||
});
|
||||
|
||||
// Claude will call appropriate MCP tools
|
||||
// and return formatted response
|
||||
this.addMessage('assistant', result);
|
||||
return;
|
||||
}
|
||||
|
||||
// Regular chat
|
||||
const response = await this.ai.sendMessage(command);
|
||||
this.addMessage('assistant', response);
|
||||
}
|
||||
```
|
||||
|
||||
### 3.4 Update Component Data Binding
|
||||
|
||||
**File:** `admin-ui/js/components/ds-project-card.js`
|
||||
|
||||
```javascript
|
||||
class DsProjectCard extends HTMLElement {
|
||||
async connectedCallback() {
|
||||
const projectId = this.getAttribute('data-project-id');
|
||||
|
||||
// Fetch project data via MCP
|
||||
const mcp = new MCPClient();
|
||||
const project = await mcp.callTool(
|
||||
'dss_get_project',
|
||||
{ project_id: projectId }
|
||||
);
|
||||
|
||||
this.render(project);
|
||||
}
|
||||
|
||||
render(project) {
|
||||
this.shadowRoot.innerHTML = `
|
||||
<div class="project-card">
|
||||
<h3>${project.name}</h3>
|
||||
<p>${project.description}</p>
|
||||
<span class="status ${project.state}">
|
||||
${project.state}
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Testing & Documentation (Week 7)
|
||||
|
||||
### 4.1 Integration Tests
|
||||
|
||||
```python
|
||||
# tests/integration/test_mcp_workflows.py
|
||||
import pytest
|
||||
from dss_mcp.server import mcp_server
|
||||
|
||||
class TestProjectWorkflow:
|
||||
"""End-to-end project creation workflow"""
|
||||
|
||||
async def test_create_project_full_workflow(self):
|
||||
"""Test: Create → Configure Figma → Extract Tokens"""
|
||||
|
||||
# Step 1: Create project
|
||||
project = await mcp_server.call_tool('dss_create_project', {
|
||||
name: 'test-project',
|
||||
root_path: '/tmp/test'
|
||||
})
|
||||
assert project['id']
|
||||
|
||||
# Step 2: Setup Figma
|
||||
result = await mcp_server.call_tool('dss_setup_figma_credentials', {
|
||||
api_token: TEST_FIGMA_TOKEN
|
||||
})
|
||||
assert result['configured']
|
||||
|
||||
# Step 3: Discover files
|
||||
files = await mcp_server.call_tool('dss_discover_figma_files', {
|
||||
project_id: project['id']
|
||||
})
|
||||
assert len(files) > 0
|
||||
|
||||
# Step 4: Sync tokens
|
||||
tokens = await mcp_server.call_tool('dss_sync_tokens', {
|
||||
project_id: project['id']
|
||||
})
|
||||
assert tokens['count'] > 0
|
||||
```
|
||||
|
||||
### 4.2 API Documentation
|
||||
|
||||
**File:** `docs/MCP_API_REFERENCE.md`
|
||||
|
||||
```markdown
|
||||
# MCP Tools Reference
|
||||
|
||||
## dss_create_project
|
||||
|
||||
Creates a new design system project.
|
||||
|
||||
**Parameters:**
|
||||
- `name` (string, required): Project name
|
||||
- `root_path` (string, required): Root directory path
|
||||
- `description` (string, optional): Project description
|
||||
|
||||
**Returns:**
|
||||
```json
|
||||
{
|
||||
"id": "proj_12345",
|
||||
"name": "web-ui",
|
||||
"root_path": "/projects/web-ui",
|
||||
"created_at": "2025-12-06T00:00:00Z",
|
||||
"state": "created"
|
||||
}
|
||||
```
|
||||
|
||||
**Errors:**
|
||||
- `ProjectAlreadyExists`: Project with this name exists
|
||||
- `InvalidPath`: Root path is invalid
|
||||
- `PermissionDenied`: No permission to create project
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Architecture Workflows
|
||||
|
||||
### Workflow 1: Create Project with Figma
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ User Input: │
|
||||
│ "Create web-ui │
|
||||
│ project with │
|
||||
│ Figma" │
|
||||
└────────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ JavaScript │
|
||||
│ Parse command │
|
||||
└────────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Send to Claude │
|
||||
│ with MCP tools │
|
||||
└────────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ Claude orchestrates: │
|
||||
│ 1. Check Figma token configured │
|
||||
│ 2. Create project (MCP tool) │
|
||||
│ 3. Setup Figma (MCP tool) │
|
||||
│ 4. Discover files (MCP tool) │
|
||||
│ 5. Return summary │
|
||||
└────────┬────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ Each MCP Tool: │
|
||||
│ - Validates input │
|
||||
│ - Updates database │
|
||||
│ - Updates project state │
|
||||
│ - Logs operation │
|
||||
│ - Returns result │
|
||||
└────────┬────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Update UI │
|
||||
│ with results │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
### Workflow 2: Sync Tokens from Figma
|
||||
|
||||
```
|
||||
User: "Sync tokens from Figma"
|
||||
│
|
||||
▼
|
||||
JavaScript → Claude
|
||||
│
|
||||
▼
|
||||
Claude calls MCP tools sequentially:
|
||||
1. dss_get_project(current_project_id)
|
||||
→ Get current project
|
||||
|
||||
2. dss_list_figma_files(project_id)
|
||||
→ Get linked Figma files
|
||||
|
||||
3. For each file:
|
||||
dss_sync_tokens(project_id, file_id)
|
||||
→ Extract and sync tokens
|
||||
|
||||
4. dss_get_token_drift(project_id)
|
||||
→ Check for inconsistencies
|
||||
│
|
||||
▼
|
||||
Return results → Update UI → Show to user
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Configuration Files
|
||||
|
||||
### MCP Server Config
|
||||
|
||||
**File:** `tools/dss_mcp/config.py`
|
||||
|
||||
```python
|
||||
MCP_CONFIG = {
|
||||
'server_name': 'dss',
|
||||
'version': '1.0.0',
|
||||
'tools': [
|
||||
'dss_create_project',
|
||||
'dss_list_projects',
|
||||
# ... all 15+ tools
|
||||
],
|
||||
'database': {
|
||||
'type': 'sqlite',
|
||||
'path': '.dss/dss.db'
|
||||
},
|
||||
'credentials': {
|
||||
'encryption_key': os.environ.get('DSS_ENCRYPTION_KEY'),
|
||||
'salt': os.environ.get('DSS_ENCRYPTION_SALT')
|
||||
},
|
||||
'logging': {
|
||||
'level': 'INFO',
|
||||
'file': '.dss/logs/dss.log'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### REST Bridge (Temporary)
|
||||
|
||||
**File:** `tools/api/mcp_bridge.py`
|
||||
|
||||
```python
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from dss_mcp.server import mcp_server
|
||||
|
||||
router = APIRouter(prefix="/api/mcp")
|
||||
|
||||
@router.post("/call")
|
||||
async def call_mcp_tool(request: ToolCallRequest):
|
||||
"""
|
||||
Bridge REST calls to MCP tools.
|
||||
Temporary during migration.
|
||||
Will be removed once all clients use MCP directly.
|
||||
"""
|
||||
try:
|
||||
result = await mcp_server.call_tool(
|
||||
request.tool,
|
||||
request.args
|
||||
)
|
||||
return {'success': True, 'result': result}
|
||||
except Exception as e:
|
||||
logger.error(f"MCP tool error: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.get("/tools")
|
||||
async def list_mcp_tools():
|
||||
"""List all available MCP tools"""
|
||||
return {'tools': mcp_server.get_tools()}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rollout Strategy
|
||||
|
||||
### Week 1-3: Backend Only
|
||||
- Implement MCP tools
|
||||
- Test tools thoroughly
|
||||
- Deploy MCP server
|
||||
- REST still primary
|
||||
|
||||
### Week 4-5: Parallel Operation
|
||||
- Update JavaScript to use MCP tools
|
||||
- REST bridge for compatibility
|
||||
- All new features use MCP only
|
||||
- Gradual migration of old features
|
||||
|
||||
### Week 6+: MCP Primary
|
||||
- Most operations via MCP
|
||||
- REST endpoints deprecated
|
||||
- Documentation updated
|
||||
- Monitoring in place
|
||||
|
||||
---
|
||||
|
||||
## Monitoring & Metrics
|
||||
|
||||
### Key Metrics to Track
|
||||
|
||||
```python
|
||||
# In dss_mcp/metrics.py
|
||||
metrics = {
|
||||
'tool_calls_total': Counter('MCP tool calls'),
|
||||
'tool_call_duration': Histogram('Tool execution time'),
|
||||
'tool_errors': Counter('Tool errors by type'),
|
||||
'operation_queue_depth': Gauge('Operations waiting'),
|
||||
'database_queries': Counter('Database operations'),
|
||||
'credential_rotations': Counter('Encryption key rotations')
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting Guide
|
||||
|
||||
### Problem: "Tool not found"
|
||||
**Solution:** Verify tool is registered in MCP server
|
||||
|
||||
### Problem: "Credential encryption failed"
|
||||
**Solution:** Check encryption key environment variable
|
||||
|
||||
### Problem: "State transition invalid"
|
||||
**Solution:** Project state doesn't allow this operation
|
||||
|
||||
### Problem: "Operation timeout"
|
||||
**Solution:** Long-running operation in queue, check logs
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [Existing ARCHITECTURE_MCP_FIRST.md](./ARCHITECTURE_MCP_FIRST.md)
|
||||
- [Component Audit](./COMPONENT_AUDIT.md)
|
||||
- [MCP Tools Spec](./MCP_TOOLS_SPEC.md)
|
||||
- [FastMCP Documentation](https://github.com/jlowin/FastMCP)
|
||||
Reference in New Issue
Block a user