Files
dss/docs/03_reference/MCP_TOOLS_SPEC.md
Digital Production Factory 276ed71f31 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
2025-12-09 18:45:48 -03:00

12 KiB

DSS MCP Tools Specification

New Tools for Project & Figma Management

Instead of REST endpoints, the following MCP tools should be implemented in tools/dss_mcp/tools/project_tools.py:


1. dss_create_project

Purpose: Create a new DSS project with empty Figma manifest

Input Schema:

{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "Project name (required)"
    },
    "root_path": {
      "type": "string",
      "description": "Project root path (default: '.')"
    },
    "description": {
      "type": "string",
      "description": "Optional project description"
    }
  },
  "required": ["name", "root_path"]
}

Implementation:

async def create_project(self, name: str, root_path: str, description: str = "") -> Dict:
    """Create a new project with empty Figma manifest"""
    # 1. Insert into projects table (name, root_path, description, created_at)
    # 2. Create figma.json manifest in project folder:
    #    {
    #      "version": "1.0",
    #      "files": [],
    #      "lastUpdated": "2025-12-05T..."
    #    }
    # 3. Return project metadata (id, name, root_path)
    # 4. Emit project-created event

    project_id = conn.execute(
        "INSERT INTO projects (name, root_path, description, created_at) VALUES (?, ?, ?, ?)",
        (name, root_path, description, datetime.now().isoformat())
    ).lastrowid

    # Create manifest file
    manifest_path = os.path.join(root_path, "figma.json")
    manifest = {
        "version": "1.0",
        "files": [],
        "lastUpdated": datetime.now().isoformat()
    }
    os.makedirs(root_path, exist_ok=True)
    with open(manifest_path, 'w') as f:
        json.dump(manifest, f, indent=2)

    return {
        "project_id": project_id,
        "name": name,
        "root_path": root_path,
        "manifest_path": manifest_path,
        "status": "created"
    }

Returns:

{
  "project_id": "1",
  "name": "my-design-system",
  "root_path": "./packages/design",
  "manifest_path": "./packages/design/figma.json",
  "status": "created"
}

2. dss_setup_figma_credentials

Purpose: Store Figma API token at user level (encrypted)

Input Schema:

{
  "type": "object",
  "properties": {
    "api_token": {
      "type": "string",
      "description": "Figma Personal Access Token"
    }
  },
  "required": ["api_token"]
}

Implementation:

async def setup_figma_credentials(self, api_token: str, user_id: str = None) -> Dict:
    """Store and validate Figma API credentials at user level"""

    # 1. Validate token by testing Figma API
    headers = {"X-Figma-Token": api_token}
    async with httpx.AsyncClient() as client:
        response = await client.get("https://api.figma.com/v1/me", headers=headers)
        if not response.is_success:
            raise ValueError("Invalid Figma API token")
        user_data = response.json()

    # 2. Encrypt and store in project_integrations table (user-scoped)
    # Use project_id=NULL for global user credentials
    cipher = MCPConfig.get_cipher()
    encrypted_config = cipher.encrypt(
        json.dumps({"api_token": api_token}).encode()
    ).decode()

    conn.execute(
        """INSERT OR REPLACE INTO project_integrations
           (project_id, user_id, integration_type, config, enabled, created_at)
           VALUES (NULL, ?, 'figma', ?, 1, ?)""",
        (user_id or "anonymous", encrypted_config, datetime.now().isoformat())
    )

    # 3. Update integration_health
    conn.execute(
        """INSERT OR REPLACE INTO integration_health
           (integration_type, is_healthy, last_success_at)
           VALUES ('figma', 1, ?)""",
        (datetime.now().isoformat(),)
    )

    return {
        "status": "configured",
        "figma_user": user_data.get("name"),
        "workspace": user_data.get("email"),
        "message": "Figma credentials stored securely at user level"
    }

Returns:

{
  "status": "configured",
  "figma_user": "John Designer",
  "workspace": "john@company.com",
  "message": "Figma credentials stored securely at user level"
}

3. dss_get_project_manifest

Purpose: Read project's figma.json manifest

Input Schema:

{
  "type": "object",
  "properties": {
    "project_id": {
      "type": "string",
      "description": "Project ID"
    }
  },
  "required": ["project_id"]
}

Implementation:

async def get_project_manifest(self, project_id: str) -> Dict:
    """Get project's Figma manifest"""

    # 1. Get project path from database
    project = conn.execute(
        "SELECT root_path FROM projects WHERE id = ?", (project_id,)
    ).fetchone()

    if not project:
        raise ValueError(f"Project {project_id} not found")

    # 2. Read figma.json
    manifest_path = os.path.join(project["root_path"], "figma.json")

    if os.path.exists(manifest_path):
        with open(manifest_path, 'r') as f:
            manifest = json.load(f)
    else:
        manifest = {
            "version": "1.0",
            "files": [],
            "lastUpdated": datetime.now().isoformat()
        }

    return manifest

Returns:

{
  "version": "1.0",
  "files": [
    {
      "key": "figd_abc123",
      "name": "Design Tokens",
      "linkedAt": "2025-12-05T15:30:00Z"
    }
  ],
  "lastUpdated": "2025-12-05T16:00:00Z"
}

4. dss_add_figma_file

Purpose: Add Figma file reference to project manifest

Input Schema:

{
  "type": "object",
  "properties": {
    "project_id": {
      "type": "string",
      "description": "Project ID"
    },
    "file_key": {
      "type": "string",
      "description": "Figma file key (e.g., figd_abc123 or full URL)"
    },
    "file_name": {
      "type": "string",
      "description": "Optional display name for the file"
    }
  },
  "required": ["project_id", "file_key"]
}

Implementation:

async def add_figma_file(self, project_id: str, file_key: str, file_name: str = None) -> Dict:
    """Add Figma file to project manifest"""

    # 1. Extract file key from URL if needed
    if "figma.com" in file_key:
        match = re.search(r"file/([a-zA-Z0-9]+)", file_key)
        file_key = match.group(1) if match else file_key

    # 2. Get project and load manifest
    project = conn.execute(
        "SELECT root_path FROM projects WHERE id = ?", (project_id,)
    ).fetchone()

    manifest_path = os.path.join(project["root_path"], "figma.json")
    with open(manifest_path, 'r') as f:
        manifest = json.load(f)

    # 3. Check if file already linked
    existing = next((f for f in manifest["files"] if f["key"] == file_key), None)
    if existing:
        raise ValueError(f"File {file_key} already linked to project")

    # 4. Add file to manifest
    manifest["files"].append({
        "key": file_key,
        "name": file_name or f"Figma File {file_key[:8]}",
        "linkedAt": datetime.now().isoformat()
    })
    manifest["lastUpdated"] = datetime.now().isoformat()

    # 5. Write manifest back
    with open(manifest_path, 'w') as f:
        json.dump(manifest, f, indent=2)

    return {
        "project_id": project_id,
        "file_key": file_key,
        "file_name": file_name,
        "status": "added",
        "files_count": len(manifest["files"])
    }

Returns:

{
  "project_id": "1",
  "file_key": "figd_abc123",
  "file_name": "Design Tokens",
  "status": "added",
  "files_count": 1
}

5. dss_discover_figma_files

Purpose: Discover available Figma files and suggest linking

Input Schema:

{
  "type": "object",
  "properties": {
    "project_id": {
      "type": "string",
      "description": "Project ID"
    }
  },
  "required": ["project_id"]
}

Implementation:

async def discover_figma_files(self, project_id: str, user_id: str = None) -> Dict:
    """Discover available Figma files from user's workspaces"""

    # 1. Get user's Figma credentials from project_integrations
    creds = conn.execute(
        """SELECT config FROM project_integrations
           WHERE integration_type='figma' AND (project_id IS NULL OR project_id=?)
           LIMIT 1""",
        (project_id,)
    ).fetchone()

    if not creds:
        raise ValueError("Figma credentials not configured. Run /setup-figma first.")

    # 2. Decrypt credentials
    cipher = MCPConfig.get_cipher()
    config = json.loads(cipher.decrypt(creds["config"].encode()).decode())
    api_token = config["api_token"]

    # 3. Fetch user's teams from Figma API
    headers = {"X-Figma-Token": api_token}
    available_files = []

    async with httpx.AsyncClient() as client:
        # Get teams
        resp = await client.get("https://api.figma.com/v1/teams", headers=headers)
        teams = resp.json().get("teams", [])

        # Get projects in each team
        for team in teams:
            team_resp = await client.get(
                f"https://api.figma.com/v1/teams/{team['id']}/projects",
                headers=headers
            )
            projects = team_resp.json().get("projects", [])

            for project in projects:
                # Get files in each project
                files_resp = await client.get(
                    f"https://api.figma.com/v1/projects/{project['id']}/files",
                    headers=headers
                )
                files = files_resp.json().get("files", [])

                for file in files:
                    available_files.append({
                        "key": file["key"],
                        "name": file["name"],
                        "team": team["name"],
                        "project": project["name"]
                    })

    # 4. Get currently linked files
    manifest = await self.get_project_manifest(project_id)
    linked_keys = {f["key"] for f in manifest["files"]}

    # 5. Return available files (excluding already linked)
    available = [f for f in available_files if f["key"] not in linked_keys]

    return {
        "project_id": project_id,
        "linked_files": manifest["files"],
        "available_files": available[:10],  # Top 10 suggestions
        "total_available": len(available),
        "message": f"Found {len(available)} available Figma files"
    }

Returns:

{
  "project_id": "1",
  "linked_files": [
    {"key": "figd_abc123", "name": "Design Tokens", "linkedAt": "..."}
  ],
  "available_files": [
    {"key": "figd_xyz789", "name": "Components", "team": "Design", "project": "Main"},
    {"key": "figd_def456", "name": "Icons", "team": "Design", "project": "Main"}
  ],
  "total_available": 2,
  "message": "Found 2 available Figma files"
}

6. dss_list_project_figma_files

Purpose: List all Figma files currently linked to project

Input Schema:

{
  "type": "object",
  "properties": {
    "project_id": {
      "type": "string",
      "description": "Project ID"
    }
  },
  "required": ["project_id"]
}

Implementation:

async def list_project_figma_files(self, project_id: str) -> Dict:
    """List all Figma files in project manifest"""

    manifest = await self.get_project_manifest(project_id)

    return {
        "project_id": project_id,
        "files": manifest["files"],
        "count": len(manifest["files"])
    }

Returns:

{
  "project_id": "1",
  "files": [
    {
      "key": "figd_abc123",
      "name": "Design Tokens",
      "linkedAt": "2025-12-05T15:30:00Z"
    },
    {
      "key": "figd_xyz789",
      "name": "Components",
      "linkedAt": "2025-12-05T16:00:00Z"
    }
  ],
  "count": 2
}

Implementation Checklist

  • Add 6 new tools to project_tools.py
  • Create manifest read/write helper functions
  • Add encryption for Figma tokens in project_integrations table
  • Add to PROJECT_TOOLS list
  • Register in handler.py tool registry
  • Add audit logging to mcp_tool_usage table
  • Update integration_health on success/failure
  • Add circuit breaker for Figma API calls
  • Add input validation for file keys and tokens
  • Test with MCP client

Architecture Benefits

No REST endpoints - All work via MCP tools User-level credentials - Figma tokens stored per-user in database Manifest-driven - figma.json declares project dependencies Versionable - Manifests can be checked into git Discoverable - Claude can list available Figma files Audit trail - All operations logged in mcp_tool_usage Circuit breaker - Protected against cascading API failures Encrypted storage - Credentials encrypted with Fernet

This is the true MCP-first architecture for DSS! 🚀