Skip to main content
Glama
PLUGINS.md13.8 kB
# Plugin Development Guide This guide explains how to create plugins for the DevStandards MCP server to add support for new programming languages, frameworks, or coding standards. ## Overview The plugin system allows you to extend the DevStandards server with new standards without modifying the core codebase. Each plugin: - Loads standards from CSV files in its data directory - Provides category definitions - Validates standard data - Integrates automatically with the MCP server ## Plugin Architecture ``` devstandards-mcp/ ├── src/ │ └── plugins/ │ ├── base.py # Base plugin interface │ ├── manager.py # Plugin discovery and management │ └── drupal.py # Example plugin implementation └── data/ └── drupal/ # Plugin data directory ├── drupal_standards.csv └── test_standards.csv # Additional CSV files loaded automatically ``` ## Creating a New Plugin ### Step 1: Create Plugin Class Create a new Python file in `src/plugins/` (e.g., `javascript.py`): ```python import csv import json from pathlib import Path from typing import List, Dict from .base import StandardsPlugin, Standard class JavaScriptStandardsPlugin(StandardsPlugin): """Plugin for JavaScript/TypeScript coding standards""" @property def name(self) -> str: """Unique plugin identifier""" return "javascript" @property def version(self) -> str: """Plugin version""" return "1.0.0" @property def description(self) -> str: """Human-readable description""" return "JavaScript and TypeScript best practices and coding standards" def __init__(self, data_path: Path): self.data_path = data_path self._standards: List[Standard] = [] self._categories = { "js_security": "Security best practices for JavaScript", "js_performance": "Performance optimization techniques", "js_es6": "Modern ES6+ patterns and features", "js_react": "React best practices", "js_typescript": "TypeScript specific guidelines" } def load_standards(self) -> List[Standard]: """Load standards from all CSV files in the plugin's data directory""" if self._standards: # Return cached standards return self._standards # Load all CSV files in the data directory csv_files = list(self.data_path.glob("*.csv")) if not csv_files: print(f"No CSV files found in {self.data_path}") return [] for csv_file in csv_files: print(f"Loading standards from {csv_file.name}") with open(csv_file, 'r', encoding='utf-8') as f: reader = csv.DictReader(f) for row in reader: try: # Parse JSON fields examples = json.loads(row.get('examples', '{}')) references = json.loads(row.get('references', '[]')) tags = [tag.strip() for tag in row.get('tags', '').split('|')] standard = Standard( id=row['id'], category=row['category'], subcategory=row['subcategory'], title=row['title'], description=row['description'], severity=row['severity'], examples=examples, references=references, tags=tags, rationale=row.get('rationale'), fix_guidance=row.get('fix_guidance') ) # Validate before adding errors = self.validate_standard(standard) if errors: print(f"Validation errors for {row['id']}: {errors}") continue self._standards.append(standard) except Exception as e: print(f"Error loading standard {row.get('id', 'unknown')} from {csv_file.name}: {e}") print(f"Loaded {len(self._standards)} standards from {len(csv_files)} CSV files") return self._standards def get_categories(self) -> List[Dict[str, str]]: """Get all categories with descriptions""" return [ {"name": name, "description": desc} for name, desc in self._categories.items() ] ``` ### Step 2: Create Data Directory Create a directory for your plugin's data: ```bash mkdir -p data/javascript ``` ### Step 3: Add CSV Data Create CSV files in your plugin's data directory. All CSV files will be loaded automatically. Example `data/javascript/security_standards.csv`: ```csv id,category,subcategory,title,description,severity,examples,references,tags,rationale,fix_guidance JS001,js_security,xss,Sanitize User Input,Always sanitize user input before displaying in DOM,critical,"{""good"": ""element.textContent = userInput;"", ""bad"": ""element.innerHTML = userInput;""}","[""https://owasp.org/www-community/attacks/xss/""]",security|xss|dom,Unsanitized input can lead to XSS attacks,Use textContent or proper sanitization libraries JS002,js_security,validation,Validate on Server Side,Never trust client-side validation alone,high,"{""good"": ""// Server validates all inputs"", ""bad"": ""// Only client-side validation""}","[""https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html""]",security|validation,Client-side validation can be bypassed,Implement server-side validation for all inputs ``` ### Step 4: Enable Plugin Add your plugin to the `PLUGINS_ENABLED` list in `.env`: ```bash PLUGINS_ENABLED=drupal,javascript ``` ## Plugin Interface (StandardsPlugin) All plugins must inherit from `StandardsPlugin` and implement these properties and methods: ### Required Properties ```python @property def name(self) -> str: """Unique identifier for the plugin""" @property def version(self) -> str: """Plugin version (semantic versioning recommended)""" @property def description(self) -> str: """Human-readable description of the plugin""" ``` ### Required Methods ```python def load_standards(self) -> List[Standard]: """Load and return all standards from the plugin's data sources""" def get_categories(self) -> List[Dict[str, str]]: """Return list of categories with name and description""" ``` ### Inherited Methods ```python def validate_standard(self, standard: Standard) -> List[str]: """Validate a standard object (inherited from base class)""" ``` ## Standard Data Model Each standard must conform to this structure: ```python @dataclass class Standard: id: str # Unique identifier (e.g., "JS001") category: str # Main category (e.g., "js_security") subcategory: str # Subcategory (e.g., "xss") title: str # Brief title description: str # Detailed description severity: str # critical|high|medium|low|info examples: dict = field(default_factory=dict) # Code examples references: list = field(default_factory=list) # Reference URLs tags: list = field(default_factory=list) # Searchable tags rationale: Optional[str] = None # Why it matters fix_guidance: Optional[str] = None # How to fix created_at: Optional[str] = None updated_at: Optional[str] = None ``` ## CSV Format CSV files must include these columns: ```csv id,category,subcategory,title,description,severity,examples,references,tags,rationale,fix_guidance ``` ### Field Formats - **id**: Unique string identifier - **category**: Must match a category defined in the plugin - **subcategory**: Logical grouping within category - **title**: Brief, descriptive title - **description**: Detailed explanation - **severity**: One of: critical, high, medium, low, info - **examples**: JSON object with "good" and "bad" code examples - **references**: JSON array of reference URLs - **tags**: Pipe-separated list (e.g., "security|validation|input") - **rationale**: Why this standard is important - **fix_guidance**: How to implement or fix ### Example CSV Entry ```csv JS003,js_performance,optimization,Use const/let over var,Prefer const and let for better scoping and performance,medium,"{""good"": ""const value = 42;"", ""bad"": ""var value = 42;""}","[""https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const""]",es6|scoping|performance,Block scoping prevents bugs and enables optimizations,Replace var with const for immutable values and let for mutable ones ``` ## Best Practices ### 1. Dynamic CSV Loading The plugin automatically loads all `*.csv` files from its data directory: ```python csv_files = list(self.data_path.glob("*.csv")) ``` This allows you to organize standards into multiple files: - `security_standards.csv` - `performance_standards.csv` - `react_standards.csv` - etc. ### 2. Category Management Define all categories in the plugin's `__init__` method: ```python self._categories = { "js_security": "Security best practices", "js_performance": "Performance guidelines", # Add all categories your standards will use } ``` ### 3. Error Handling Always handle parsing errors gracefully: ```python try: # Parse standard except Exception as e: print(f"Error loading standard {row.get('id', 'unknown')}: {e}") continue # Skip problematic entries ``` ### 4. Validation Use the inherited `validate_standard` method to ensure data quality: ```python errors = self.validate_standard(standard) if errors: print(f"Validation errors for {row['id']}: {errors}") continue ``` ### 5. Caching Cache loaded standards to avoid repeated file I/O: ```python if self._standards: # Return cached standards return self._standards ``` ## Testing Your Plugin ### 1. Unit Test Create a test file `test_plugin.py`: ```python import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parent)) from src.plugins.javascript import JavaScriptStandardsPlugin # Test plugin loading plugin = JavaScriptStandardsPlugin(Path("data/javascript")) standards = plugin.load_standards() print(f"Loaded {len(standards)} standards") # Test categories categories = plugin.get_categories() print(f"Categories: {[c['name'] for c in categories]}") ``` ### 2. Integration Test Use the validation script: ```bash python test_mcp_validation.py ``` ### 3. MCP Test Test through MCP tools: ```python import asyncio from src.server import get_standards async def test(): result = await get_standards(category="js_security") print(result) asyncio.run(test()) ``` ## Plugin Discovery The `PluginManager` automatically discovers plugins by: 1. Scanning `src/plugins/*.py` files 2. Finding classes that inherit from `StandardsPlugin` 3. Instantiating each plugin with its data directory 4. Loading standards when first queried ## Troubleshooting ### Plugin Not Loading 1. Check the plugin file is in `src/plugins/` 2. Verify the class inherits from `StandardsPlugin` 3. Ensure all required properties/methods are implemented 4. Check for syntax errors in the plugin file ### Standards Not Found 1. Verify CSV files exist in `data/{plugin_name}/` 2. Check CSV format matches the expected columns 3. Look for parsing errors in console output 4. Ensure categories in CSV match plugin's `_categories` ### Validation Errors Common validation issues: - Invalid severity level (must be: critical/high/medium/low/info) - Missing required fields (id, category, title, etc.) - Malformed JSON in examples or references fields - Category not defined in plugin's `_categories` ## Example Plugins ### Minimal Plugin ```python from .base import StandardsPlugin, Standard class MinimalPlugin(StandardsPlugin): @property def name(self) -> str: return "minimal" @property def version(self) -> str: return "1.0.0" @property def description(self) -> str: return "Minimal example plugin" def load_standards(self) -> List[Standard]: # Load from CSV files return [] def get_categories(self) -> List[Dict[str, str]]: return [{"name": "example", "description": "Example category"}] ``` ### Advanced Plugin Features You can extend plugins with additional features: - Custom validation rules - Multiple data sources (CSV, JSON, API) - Dynamic category generation - Standard generation from code analysis - Integration with external tools ## Contributing When contributing a new plugin: 1. Follow the naming convention: `{language}_standards.py` 2. Include comprehensive CSV data 3. Add tests for your plugin 4. Update documentation 5. Submit a pull request ## Summary Creating a plugin involves: 1. **Create plugin class** inheriting from `StandardsPlugin` 2. **Implement required properties** (name, version, description) 3. **Implement load_standards()** to load CSV files 4. **Implement get_categories()** to define categories 5. **Create data directory** and add CSV files 6. **Enable plugin** in configuration 7. **Test** the plugin works correctly The plugin system makes it easy to extend DevStandards with new languages and frameworks while maintaining a consistent interface for AI assistants.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ivangrynenko/devstandards_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server