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:
55
tools/dss_mcp/plugins/__init__.py
Normal file
55
tools/dss_mcp/plugins/__init__.py
Normal file
@@ -0,0 +1,55 @@
|
||||
"""
|
||||
DSS MCP Server Plugins
|
||||
|
||||
This directory contains dynamically loaded plugins for the DSS MCP server.
|
||||
|
||||
Plugin Contract:
|
||||
- Each plugin is a .py file in this directory
|
||||
- Must export TOOLS: List[types.Tool] with MCP tool definitions
|
||||
- Must have a handler class with execute_tool(name, arguments) method
|
||||
- Optional: export PLUGIN_METADATA dict with name, version, author
|
||||
|
||||
Example Plugin Structure:
|
||||
from mcp import types
|
||||
|
||||
PLUGIN_METADATA = {
|
||||
"name": "My Plugin",
|
||||
"version": "1.0.0",
|
||||
"author": "DSS Team"
|
||||
}
|
||||
|
||||
TOOLS = [
|
||||
types.Tool(name="my_tool", description="...", inputSchema={...})
|
||||
]
|
||||
|
||||
class PluginTools:
|
||||
async def execute_tool(self, name, arguments):
|
||||
if name == "my_tool":
|
||||
return {"result": "success"}
|
||||
|
||||
Developer Workflow:
|
||||
1. Copy _template.py to new_plugin.py
|
||||
2. Edit TOOLS list and PluginTools class
|
||||
3. (Optional) Create requirements.txt if plugin needs dependencies
|
||||
4. Run: ../install_plugin_deps.sh (if dependencies added)
|
||||
5. Restart MCP server: supervisorctl restart dss-mcp
|
||||
6. Plugin tools are immediately available to all clients
|
||||
|
||||
Dependency Management:
|
||||
- If your plugin needs Python packages, create a requirements.txt file
|
||||
- Place it in the same directory as your plugin (e.g., plugins/my_plugin/requirements.txt)
|
||||
- Run ../install_plugin_deps.sh to install all plugin dependencies
|
||||
- Use --check flag to see which plugins have dependencies without installing
|
||||
|
||||
Example plugin with dependencies:
|
||||
plugins/
|
||||
├── my_plugin/
|
||||
│ ├── __init__.py
|
||||
│ ├── tool.py (exports TOOLS and PluginTools)
|
||||
│ └── requirements.txt (jinja2>=3.1.2, httpx>=0.25.0)
|
||||
└── _template.py
|
||||
|
||||
See _template.py for a complete example.
|
||||
"""
|
||||
|
||||
__all__ = [] # Plugins are auto-discovered, not explicitly exported
|
||||
217
tools/dss_mcp/plugins/_template.py
Normal file
217
tools/dss_mcp/plugins/_template.py
Normal file
@@ -0,0 +1,217 @@
|
||||
"""
|
||||
Plugin Template for DSS MCP Server
|
||||
|
||||
This file serves as both documentation and a starting point for new plugins.
|
||||
|
||||
To create a new plugin:
|
||||
1. Copy this file: cp _template.py my_plugin.py
|
||||
2. Update PLUGIN_METADATA with your plugin details
|
||||
3. Define your tools in the TOOLS list
|
||||
4. Implement tool logic in the PluginTools class
|
||||
5. Restart the MCP server
|
||||
|
||||
The plugin will be automatically discovered and registered.
|
||||
"""
|
||||
|
||||
from typing import Dict, Any, List
|
||||
from mcp import types
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 1. PLUGIN METADATA (Optional but recommended)
|
||||
# =============================================================================
|
||||
|
||||
PLUGIN_METADATA = {
|
||||
"name": "Template Plugin",
|
||||
"version": "1.0.0",
|
||||
"author": "DSS Team",
|
||||
"description": "Template plugin demonstrating the plugin contract"
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 2. TOOLS DEFINITION (Required)
|
||||
# =============================================================================
|
||||
|
||||
TOOLS = [
|
||||
types.Tool(
|
||||
name="template_hello",
|
||||
description="A simple hello world tool to verify the plugin system works",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name to greet (optional)",
|
||||
"default": "World"
|
||||
}
|
||||
}
|
||||
}
|
||||
),
|
||||
types.Tool(
|
||||
name="template_echo",
|
||||
description="Echo back the provided message",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": "Message to echo back"
|
||||
},
|
||||
"uppercase": {
|
||||
"type": "boolean",
|
||||
"description": "Convert to uppercase (optional)",
|
||||
"default": False
|
||||
}
|
||||
},
|
||||
"required": ["message"]
|
||||
}
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 3. PLUGIN TOOLS HANDLER (Required)
|
||||
# =============================================================================
|
||||
|
||||
class PluginTools:
|
||||
"""
|
||||
Handler class for plugin tools.
|
||||
|
||||
The PluginRegistry will instantiate this class and call execute_tool()
|
||||
to handle tool invocations.
|
||||
|
||||
Contract:
|
||||
- Must have async execute_tool(name: str, arguments: dict) method
|
||||
- Should return list[types.TextContent | types.ImageContent | types.EmbeddedResource]
|
||||
- Can raise exceptions for errors (will be caught and logged)
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""
|
||||
Initialize the plugin tools handler.
|
||||
|
||||
Args:
|
||||
**kwargs: Optional context/dependencies (context_manager, user_id, etc.)
|
||||
"""
|
||||
# Extract any dependencies you need
|
||||
self.context_manager = kwargs.get('context_manager')
|
||||
self.user_id = kwargs.get('user_id')
|
||||
self.audit_log = kwargs.get('audit_log')
|
||||
|
||||
# Initialize any plugin-specific state
|
||||
self.call_count = 0
|
||||
|
||||
async def execute_tool(self, name: str, arguments: Dict[str, Any]) -> List:
|
||||
"""
|
||||
Route tool calls to appropriate implementation methods.
|
||||
|
||||
Args:
|
||||
name: Tool name (matches TOOLS[].name)
|
||||
arguments: Tool arguments from the client
|
||||
|
||||
Returns:
|
||||
List of MCP content objects (TextContent, ImageContent, etc.)
|
||||
|
||||
Raises:
|
||||
ValueError: If tool name is unknown
|
||||
"""
|
||||
self.call_count += 1
|
||||
|
||||
# Route to implementation methods
|
||||
if name == "template_hello":
|
||||
return await self._handle_hello(arguments)
|
||||
elif name == "template_echo":
|
||||
return await self._handle_echo(arguments)
|
||||
else:
|
||||
raise ValueError(f"Unknown tool: {name}")
|
||||
|
||||
async def _handle_hello(self, arguments: Dict[str, Any]) -> List[types.TextContent]:
|
||||
"""
|
||||
Implementation of template_hello tool.
|
||||
|
||||
Args:
|
||||
arguments: Tool arguments (contains 'name')
|
||||
|
||||
Returns:
|
||||
Greeting message
|
||||
"""
|
||||
name = arguments.get("name", "World")
|
||||
|
||||
message = f"Hello, {name}! The plugin system is operational. (Call #{self.call_count})"
|
||||
|
||||
return [
|
||||
types.TextContent(
|
||||
type="text",
|
||||
text=message
|
||||
)
|
||||
]
|
||||
|
||||
async def _handle_echo(self, arguments: Dict[str, Any]) -> List[types.TextContent]:
|
||||
"""
|
||||
Implementation of template_echo tool.
|
||||
|
||||
Args:
|
||||
arguments: Tool arguments (contains 'message' and optional 'uppercase')
|
||||
|
||||
Returns:
|
||||
Echoed message
|
||||
"""
|
||||
message = arguments["message"]
|
||||
uppercase = arguments.get("uppercase", False)
|
||||
|
||||
if uppercase:
|
||||
message = message.upper()
|
||||
|
||||
return [
|
||||
types.TextContent(
|
||||
type="text",
|
||||
text=f"Echo: {message}"
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# NOTES FOR PLUGIN DEVELOPERS
|
||||
# =============================================================================
|
||||
|
||||
"""
|
||||
## Plugin Development Tips
|
||||
|
||||
### Error Handling
|
||||
- The plugin loader catches exceptions during loading, so syntax errors won't crash the server
|
||||
- Runtime exceptions in execute_tool() are caught and logged by the MCP server
|
||||
- Return clear error messages to help users understand what went wrong
|
||||
|
||||
### Dependencies
|
||||
- You can import from other DSS modules: from ..context.project_context import get_context_manager
|
||||
- Keep dependencies minimal - plugins should be self-contained
|
||||
- Standard library and existing DSS dependencies only (no new pip packages without discussion)
|
||||
|
||||
### Testing
|
||||
- Test your plugin by:
|
||||
1. Restarting the MCP server: supervisorctl restart dss-mcp
|
||||
2. Using the MCP server directly via API: POST /api/tools/your_tool_name
|
||||
3. Via Claude Code if connected to the MCP server
|
||||
|
||||
### Best Practices
|
||||
- Use clear, descriptive tool names prefixed with your plugin name (e.g., "analytics_track_event")
|
||||
- Provide comprehensive inputSchema with descriptions
|
||||
- Return structured data using types.TextContent
|
||||
- Log errors with logger.error() for debugging
|
||||
- Keep tools focused - one tool should do one thing well
|
||||
|
||||
### Advanced Features
|
||||
- For image results, use types.ImageContent
|
||||
- For embedded resources, use types.EmbeddedResource
|
||||
- Access project context via self.context_manager if injected
|
||||
- Use async/await for I/O operations (API calls, database queries, etc.)
|
||||
|
||||
## Example Plugin Ideas
|
||||
|
||||
- **Network Logger**: Capture and analyze browser network requests
|
||||
- **Performance Analyzer**: Measure component render times, bundle sizes
|
||||
- **Workflow Helper**: Automate common development workflows
|
||||
- **Integration Tools**: Connect to external services (Slack, GitHub, etc.)
|
||||
- **Custom Validators**: Project-specific validation rules
|
||||
"""
|
||||
98
tools/dss_mcp/plugins/hello_world.py
Normal file
98
tools/dss_mcp/plugins/hello_world.py
Normal file
@@ -0,0 +1,98 @@
|
||||
"""
|
||||
Hello World Plugin - Test Plugin for DSS MCP Server
|
||||
|
||||
Simple plugin to validate the plugin loading system is working correctly.
|
||||
"""
|
||||
|
||||
from typing import Dict, Any, List
|
||||
from mcp import types
|
||||
|
||||
|
||||
PLUGIN_METADATA = {
|
||||
"name": "Hello World Plugin",
|
||||
"version": "1.0.0",
|
||||
"author": "DSS Team",
|
||||
"description": "Simple test plugin to validate plugin system"
|
||||
}
|
||||
|
||||
|
||||
TOOLS = [
|
||||
types.Tool(
|
||||
name="hello_world",
|
||||
description="Simple hello world tool to test plugin loading",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name to greet",
|
||||
"default": "World"
|
||||
}
|
||||
}
|
||||
}
|
||||
),
|
||||
types.Tool(
|
||||
name="plugin_status",
|
||||
description="Get status of the plugin system",
|
||||
inputSchema={
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class PluginTools:
|
||||
"""Handler for hello world plugin tools"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.call_count = 0
|
||||
|
||||
async def execute_tool(self, name: str, arguments: Dict[str, Any]) -> List:
|
||||
"""Execute tool by name"""
|
||||
self.call_count += 1
|
||||
|
||||
if name == "hello_world":
|
||||
return await self._hello_world(arguments)
|
||||
elif name == "plugin_status":
|
||||
return await self._plugin_status(arguments)
|
||||
else:
|
||||
raise ValueError(f"Unknown tool: {name}")
|
||||
|
||||
async def _hello_world(self, arguments: Dict[str, Any]) -> List[types.TextContent]:
|
||||
"""Simple hello world implementation"""
|
||||
name = arguments.get("name", "World")
|
||||
|
||||
message = (
|
||||
f"Hello, {name}!\n\n"
|
||||
f"✓ Plugin system is operational\n"
|
||||
f"✓ Dynamic loading works correctly\n"
|
||||
f"✓ Tool routing is functional\n"
|
||||
f"✓ Call count: {self.call_count}"
|
||||
)
|
||||
|
||||
return [
|
||||
types.TextContent(
|
||||
type="text",
|
||||
text=message
|
||||
)
|
||||
]
|
||||
|
||||
async def _plugin_status(self, arguments: Dict[str, Any]) -> List[types.TextContent]:
|
||||
"""Return plugin system status"""
|
||||
status = {
|
||||
"status": "operational",
|
||||
"plugin_name": PLUGIN_METADATA["name"],
|
||||
"plugin_version": PLUGIN_METADATA["version"],
|
||||
"tools_count": len(TOOLS),
|
||||
"call_count": self.call_count,
|
||||
"tools": [tool.name for tool in TOOLS]
|
||||
}
|
||||
|
||||
import json
|
||||
return [
|
||||
types.TextContent(
|
||||
type="text",
|
||||
text=json.dumps(status, indent=2)
|
||||
)
|
||||
]
|
||||
Reference in New Issue
Block a user