# Internal Plugin Marketplace Architecture

**Date**: 2025-12-06
**Status**: ✅ Analysis Complete - Server-Side Plugin Loading Recommended
**Use Case**: Internal team use only (no external distribution)
**Confidence**: Very High (95%+)
**Validated By**: Gemini 3 Pro Preview (6-step thinkdeep analysis + expert validation)

---

## Executive Summary

**Question**: How should we distribute plugins for internal DSS team use?

**Answer**: **Server-Side Plugin Directory** with auto-loading via `plugin_loader.py`

**Previous Analysis Corrected**: The GitHub-based marketplace analysis was for PUBLIC distribution. For **internal-only use**, we need a much simpler approach.

---

## Key Requirements (Clarified)

1. ✅ **Internal use only** - DSS is for company team members
2. ✅ **Own plugins only** - No external plugin installation needed
3. ✅ **Simple distribution** - Hosted on DSS server itself
4. ✅ **Works with REMOTE + LOCAL modes** - Same plugins for both scenarios
5. ✅ **Zero client installation** - Plugins auto-available when connected

---

## Recommended Architecture: Server-Side Plugin Loading

### Core Concept

Instead of "installing" plugins to developer machines, **all plugins run on the DSS MCP server** and are auto-loaded on server startup.

```
┌─────────────────────────────────────────────────┐
│    DSS Server (dss.overbits.luz.uy)             │
│                                                  │
│  ┌────────────────────────────────────────┐    │
│  │  /plugins/ (Server-Side Directory)     │    │
│  │  ├── network-logger/                   │    │
│  │  ├── performance-analyzer/             │    │
│  │  └── custom-workflow/                  │    │
│  └────────────────┬───────────────────────┘    │
│                   │                              │
│  ┌────────────────▼───────────────────────┐    │
│  │  plugin_loader.py                      │    │
│  │  - Scans /plugins/ on server startup   │    │
│  │  - Imports TOOLS from each plugin      │    │
│  │  - Registers with FastMCP dynamically  │    │
│  └────────────────┬───────────────────────┘    │
│                   │                              │
│  ┌────────────────▼───────────────────────┐    │
│  │  DSS MCP Server                        │    │
│  │  - Built-in tools (project, debug)     │    │
│  │  - Plugin tools (auto-discovered)      │    │
│  │  - Strategy Pattern (REMOTE/LOCAL)     │    │
│  └────────────────┬───────────────────────┘    │
└───────────────────┼──────────────────────────────┘
                    │
                    │ MCP Protocol
                    │
         ┌──────────▼──────────┐
         │  Developer Machine  │
         │  - Claude Code CLI  │
         │  - Connects to MCP  │
         │  - All tools ready  │
         └─────────────────────┘
```

### Why Server-Side?

**REMOTE Mode**:
- ✅ MCP server on dss.overbits.luz.uy
- ✅ Plugins execute server-side
- ✅ Access to Shadow State API
- ✅ No localhost access needed

**LOCAL Mode**:
- ✅ MCP server still on dss.overbits.luz.uy
- ✅ Plugins use Strategy Pattern
- ✅ LocalStrategy handles browser automation
- ⚠️ May need reverse tunnel for localhost access (future enhancement)

---

## Implementation Design

### 1. Directory Structure

```
/home/overbits/dss/
├── tools/dss_mcp/
│   ├── server.py (main MCP server)
│   ├── plugin_loader.py (NEW - auto-discovery)
│   ├── tools/
│   │   ├── project_tools.py (built-in)
│   │   └── debug_tools.py (built-in)
│   └── plugins/ (NEW - plugin directory)
│       ├── __init__.py
│       ├── README.md (plugin development guide)
│       ├── _template/ (copy-paste template)
│       │   ├── __init__.py
│       │   ├── tools.py
│       │   └── README.md
│       ├── network-logger/
│       │   ├── __init__.py (exports TOOLS)
│       │   ├── tools.py (implementation)
│       │   └── README.md
│       └── performance-analyzer/
│           ├── __init__.py
│           ├── tools.py
│           └── README.md
```

### 2. Plugin Contract

Every plugin MUST have an `__init__.py` that exports a `TOOLS` list.

**Example**: `tools/dss_mcp/plugins/network-logger/__init__.py`

```python
"""Network Logger Plugin - Captures browser network requests."""

from .tools import get_network_requests, analyze_network_waterfall

# Plugin Contract:
# - TOOLS: List of callables to register as MCP tools
# - RESOURCES: (Optional) List of resources
# - PROMPTS: (Optional) List of prompts

TOOLS = [
    get_network_requests,
    analyze_network_waterfall
]

# Optional metadata
__version__ = "1.0.0"
__author__ = "DSS Team"
__description__ = "Captures and analyzes browser network traffic"
```

**Example**: `tools/dss_mcp/plugins/network-logger/tools.py`

```python
"""Network Logger tool implementations."""

import logging
from typing import Dict, Any, List, Optional

logger = logging.getLogger(__name__)

async def get_network_requests(
    session_id: str,
    filter_type: str = "all"
) -> List[Dict[str, Any]]:
    """
    Get browser network requests from Shadow State.

    Args:
        session_id: Browser session ID
        filter_type: Filter by type (xhr, fetch, all)

    Returns:
        List of network request objects
    """
    # Implementation using RemoteStrategy or LocalStrategy
    # Can access Shadow State API via /api/browser-logs/{session_id}
    logger.info(f"Fetching network requests for session {session_id}")

    # TODO: Implement actual network request retrieval
    return []

async def analyze_network_waterfall(
    session_id: str
) -> Dict[str, Any]:
    """
    Analyze network request waterfall.

    Returns:
        Performance analysis with waterfall data
    """
    logger.info(f"Analyzing network waterfall for session {session_id}")

    # TODO: Implement waterfall analysis
    return {"status": "not_implemented"}
```

### 3. Plugin Loader with Deferred Registration Pattern

**File**: `tools/dss_mcp/plugin_loader.py`

```python
"""
Dynamic plugin loader for DSS MCP server.

Implements the "Deferred Registration" pattern for FastMCP:
- Plugins define callable functions (not decorated)
- Loader scans /plugins/ directory
- Loader imports and collects TOOLS from each plugin
- Server applies @mcp.tool() decorator at runtime
"""

import importlib
import pkgutil
import os
import logging
from pathlib import Path
from typing import List, Callable

logger = logging.getLogger(__name__)


class PluginLoader:
    """
    Loads plugins from a directory and collects their tools.

    Resiliency: If one plugin fails to load, it logs the error
    and continues with other plugins (doesn't crash the server).
    """

    def __init__(self, plugin_dir: str):
        """
        Initialize plugin loader.

        Args:
            plugin_dir: Absolute path to plugins directory
        """
        self.plugin_dir = plugin_dir
        self.loaded_tools: List[Callable] = []
        self.failed_plugins: List[str] = []

    def load_plugins(self) -> List[Callable]:
        """
        Scan plugin directory, import modules, and aggregate tools.

        Returns:
            List of callables ready to be decorated by FastMCP.
        """
        if not os.path.exists(self.plugin_dir):
            logger.warning(f"Plugin directory not found: {self.plugin_dir}")
            return []

        logger.info(f"Scanning for plugins in {self.plugin_dir}")

        # Iterate over subdirectories in plugins/
        for module_info in pkgutil.iter_modules([self.plugin_dir]):
            if module_info.ispkg:
                self._load_single_plugin(module_info.name)

        logger.info(
            f"Plugin loading complete: "
            f"{len(self.loaded_tools)} tools from "
            f"{len(self.loaded_tools) - len(self.failed_plugins)} plugins"
        )

        if self.failed_plugins:
            logger.warning(f"Failed to load plugins: {', '.join(self.failed_plugins)}")

        return self.loaded_tools

    def _load_single_plugin(self, plugin_name: str):
        """
        Load a single plugin by name.

        Args:
            plugin_name: Name of the plugin subdirectory
        """
        try:
            # Dynamic import: tools.dss_mcp.plugins.<plugin_name>
            # This assumes the server runs from project root
            module_path = f"tools.dss_mcp.plugins.{plugin_name}"
            module = importlib.import_module(module_path)

            # Check plugin contract
            if hasattr(module, "TOOLS") and isinstance(module.TOOLS, list):
                self.loaded_tools.extend(module.TOOLS)

                # Log plugin metadata if available
                version = getattr(module, "__version__", "unknown")
                description = getattr(module, "__description__", "")

                logger.info(
                    f"✓ Loaded plugin '{plugin_name}' v{version}: "
                    f"{len(module.TOOLS)} tools"
                )
                if description:
                    logger.info(f"  └─ {description}")
            else:
                logger.warning(
                    f"✗ Plugin '{plugin_name}' skipped: "
                    f"No 'TOOLS' list found in __init__.py"
                )
                self.failed_plugins.append(plugin_name)

        except Exception as e:
            # CRITICAL: Do not crash the server for a bad plugin
            logger.error(
                f"✗ Failed to load plugin '{plugin_name}': {str(e)}",
                exc_info=True
            )
            self.failed_plugins.append(plugin_name)
```

### 4. Server Integration

**File**: `tools/dss_mcp/server.py` (update)

```python
"""DSS MCP Server with dynamic plugin loading."""

import os
from mcp.server.fastmcp import FastMCP

# Import built-in tools
from .tools.project_tools import PROJECT_TOOLS
from .tools.debug_tools import DEBUG_TOOLS

# Import plugin loader
from .plugin_loader import PluginLoader

# Initialize FastMCP
mcp = FastMCP("DSS Core")

# Initialize Plugin Loader
plugin_path = os.path.join(os.path.dirname(__file__), "plugins")
loader = PluginLoader(plugin_path)

# Load plugins
discovered_tools = loader.load_plugins()

# Register all tools dynamically
# FastMCP requires us to apply the decorator manually
for tool_func in discovered_tools:
    # Apply @mcp.tool() decorator to each discovered function
    mcp.tool()(tool_func)

# Also register built-in tools
for tool_func in PROJECT_TOOLS + DEBUG_TOOLS:
    mcp.tool()(tool_func)

# ... rest of server startup ...
```

---

## Implementation Plan

### Phase 1: Plugin Loader (1 day) - 🔴 CRITICAL

**Files to create**:
1. `tools/dss_mcp/plugin_loader.py` (complete implementation above)
2. `tools/dss_mcp/plugins/__init__.py` (empty)
3. `tools/dss_mcp/plugins/README.md` (plugin development guide)

**Files to update**:
1. `tools/dss_mcp/server.py` (integrate plugin loader)

**Testing**:
```bash
# Create test plugin
mkdir -p tools/dss_mcp/plugins/test_plugin
cat > tools/dss_mcp/plugins/test_plugin/__init__.py << 'EOF'
async def hello_dss():
    """Test plugin tool."""
    return "Hello from test plugin!"

TOOLS = [hello_dss]
EOF

# Restart MCP server
sudo supervisorctl restart dss-mcp

# Verify plugin loaded (check logs)
tail -f /var/log/supervisor/dss-mcp.log
```

### Phase 2: Plugin Directory Structure (0.5 day)

**Create plugin template**:

`tools/dss_mcp/plugins/_template/__init__.py`:
```python
"""
Template Plugin - Copy this directory to create new plugins.

Steps:
1. Copy _template/ to your-plugin-name/
2. Update __init__.py with your plugin metadata
3. Implement your tools in tools.py
4. Export TOOLS list with your tool functions
5. Restart MCP server: sudo supervisorctl restart dss-mcp
"""

from .tools import example_tool

TOOLS = [
    example_tool
]

__version__ = "1.0.0"
__author__ = "Your Name"
__description__ = "Description of what your plugin does"
```

`tools/dss_mcp/plugins/_template/tools.py`:
```python
"""Plugin tool implementations."""

import logging

logger = logging.getLogger(__name__)

async def example_tool(input_param: str) -> dict:
    """
    Example tool function.

    Args:
        input_param: Description of parameter

    Returns:
        Result dictionary
    """
    logger.info(f"Example tool called with: {input_param}")

    # Your implementation here
    return {
        "status": "success",
        "result": f"Processed: {input_param}"
    }
```

### Phase 3: Example Plugins (1 day)

Create 2-3 real-world plugins:

1. **network-logger** - Captures network requests via Shadow State
2. **performance-analyzer** - Analyzes performance metrics
3. **workflow-helper** - Common workflow shortcuts

### Phase 4: Optional Discovery API (0.5 day)

Add to `tools/api/server.py`:

```python
@app.get("/api/plugins/list")
async def list_plugins():
    """List all available server-side plugins."""
    plugins_dir = Path("/home/overbits/dss/tools/dss_mcp/plugins")
    plugins = []

    for plugin_dir in plugins_dir.iterdir():
        if plugin_dir.is_dir() and not plugin_dir.name.startswith('_'):
            try:
                # Import to get metadata
                module_name = f"tools.dss_mcp.plugins.{plugin_dir.name}"
                module = importlib.import_module(module_name)

                plugins.append({
                    "id": plugin_dir.name,
                    "name": plugin_dir.name.replace('-', ' ').title(),
                    "version": getattr(module, '__version__', 'unknown'),
                    "author": getattr(module, '__author__', 'unknown'),
                    "description": getattr(module, '__description__', ''),
                    "tools_count": len(getattr(module, 'TOOLS', []))
                })
            except Exception as e:
                logger.error(f"Error reading plugin {plugin_dir.name}: {e}")

    return {"plugins": plugins}
```

---

## Developer Workflow

### Adding a New Plugin

```bash
# 1. Copy template
cd /home/overbits/dss/tools/dss_mcp/plugins
cp -r _template my-new-plugin

# 2. Edit plugin files
vim my-new-plugin/__init__.py
# Update metadata (__version__, __author__, __description__)
# Update TOOLS list

vim my-new-plugin/tools.py
# Implement your tool functions

# 3. Restart MCP server
sudo supervisorctl restart dss-mcp

# 4. Verify plugin loaded
tail -20 /var/log/supervisor/dss-mcp.log | grep "my-new-plugin"

# 5. Test from Claude Code
# All tools should now be available automatically!
```

### Using Plugins (Developer Perspective)

```python
# Developer connects to DSS MCP server
# All plugins auto-available, no installation needed!

# Example: Use network logger plugin
result = await get_network_requests(
    session_id="abc123",
    filter_type="xhr"
)

# Example: Analyze performance
analysis = await analyze_network_waterfall(session_id="abc123")
```

---

## Key Advantages

1. ✅ **Zero Client Setup** - No installation on developer machines
2. ✅ **Central Management** - Update plugins server-side, all devs get new version
3. ✅ **Instant Availability** - Connect to MCP → all plugins ready
4. ✅ **Team Consistency** - Everyone uses exact same toolset
5. ✅ **Simple Development** - Copy template, edit, restart server
6. ✅ **Works with REMOTE/LOCAL** - Plugins use Strategy Pattern
7. ✅ **Resilient** - Bad plugin doesn't crash server
8. ✅ **No External Dependencies** - Everything internal

---

## Security & Access Control

### For Internal Use:

- ✅ **No authentication** needed for plugin loading (trusted code)
- ✅ **File permissions** on `/plugins/` directory (dev team only)
- ✅ **Access control** at MCP server level (who can connect)
- ✅ **Code review** before adding to `/plugins/` (team process)

### Security Note:

Since this is **internal-only**:
- All code in `/plugins/` is trusted (written by team)
- No sandboxing needed (not running untrusted code)
- Simple file permissions sufficient (Unix permissions)

---

## Comparison: Previous vs Current Approach

| Aspect | GitHub Marketplace (Previous) | Server-Side Loading (Current) |
|--------|-------------------------------|-------------------------------|
| **Use Case** | Public distribution | Internal team use |
| **Installation** | Client downloads | Zero installation |
| **Updates** | Each dev updates | Server-side only |
| **Complexity** | High (GitHub integration) | Low (file-based) |
| **Implementation** | 5-7 days | 2-3 days |
| **Maintenance** | Ongoing (registry API) | Minimal (add files) |
| **Developer UX** | Multi-step install | Instant availability |
| **Consistency** | Version mismatches | Always same version |

---

## Future Enhancements

### Possible Additions (Not Required Initially):

1. **Hot Reloading** - Reload plugins without server restart
   - Use `importlib.reload()` with file watching
   - WATCH OUT: Python caching issues

2. **Plugin Dependencies** - Handle inter-plugin dependencies
   - Add `REQUIRES = ['other-plugin']` to contract
   - Load in dependency order

3. **Plugin Versioning** - Semantic versioning and compatibility
   - Check `__min_server_version__` before loading
   - Warn on incompatible plugins

4. **Local Proxy Agent** - For LOCAL mode localhost access
   - Lightweight agent on developer machine
   - Reverse tunnel or SSH forwarding
   - Enables LOCAL browser automation from server-side plugins

---

## Expert Validation Summary

From Gemini 3 Pro Preview analysis:

> "This is a robust architectural pivot. Moving from a hypothetical 'GitHub Marketplace' to a concrete **Server-Side Plugin Loader** significantly reduces complexity while solving the immediate requirement: extending DSS capabilities without managing client-side installations."

**Key Validation Points**:

1. ✅ **Deferred Registration Pattern** correctly identified for FastMCP
2. ✅ **Resiliency Core** with try/except to prevent bad plugins crashing server
3. ✅ **Plugin Contract** with TOOLS list export pattern
4. ✅ **Naming Collision Risk** noted (mitigated by team discipline)
5. ✅ **Dependency Risk** handled via try/except and logging
6. ✅ **Hot Reloading** correctly identified as risky (use supervisord restart)

---

## Next Steps

**Priority Order**:

1. **🔴 Phase 1** - Create `plugin_loader.py` (1 day)
2. **🟡 Phase 2** - Create plugin directory structure (0.5 day)
3. **🟢 Phase 3** - Create example plugins (1 day)
4. **🔵 Phase 4** - Optional discovery API (0.5 day)

**Total Effort**: 2-3 days for complete implementation

**Start With**: Phase 1 - implement and test plugin_loader.py with a single test plugin.

---

**Status**: ✅ Ready for Implementation
**Confidence**: Very High (95%+)
**Last Updated**: 2025-12-06
