Skip to main content
Glama

DollhouseMCP

by DollhouseMCP
TRIGGER_EXTRACTION_IMPLEMENTATION_GUIDE.md12 kB
# Trigger Extraction Implementation Guide ## Overview This guide documents the pattern established in PR #1133 for implementing trigger extraction for different element types. Memory triggers have been successfully implemented and this pattern should be followed for Skills (#1121), Templates (#1122), and Agents (#1123). ## Implementation Checklist ### 1. Update Element Type Interface **File**: `src/types/elements/I[ElementType].ts` ```typescript export interface I[ElementType]Metadata extends IElementMetadata { // ... existing fields ... triggers?: string[]; // Add this field } ``` ### 2. Add Validation Constants **File**: `src/elements/[elementType]/[ElementType].ts` ```typescript /** * Maximum length for individual trigger words used in Enhanced Index * @constant {number} */ const MAX_TRIGGER_LENGTH = 50; /** * Validation pattern for trigger words - allows alphanumeric characters, hyphens, and underscores * @constant {RegExp} */ const TRIGGER_VALIDATION_REGEX = /^[a-zA-Z0-9\-_]+$/; ``` ### 3. Update Element Constructor **File**: `src/elements/[elementType]/[ElementType].ts` Add trigger validation in the constructor: ```typescript constructor(yamlContent: string, name: string, metadata?: Partial<I[ElementType]Metadata>) { super(yamlContent, name, 'elementType'); // Validate and sanitize triggers if present if (metadata?.triggers && Array.isArray(metadata.triggers)) { this.metadata.triggers = metadata.triggers .filter(trigger => typeof trigger === 'string') .map(trigger => trigger.trim().toLowerCase()) .filter(trigger => trigger.length > 0 && trigger.length <= MAX_TRIGGER_LENGTH && TRIGGER_VALIDATION_REGEX.test(trigger) ) .slice(0, 20); // Limit to reasonable number } } ``` ### 4. Update Manager to Extract Triggers **File**: `src/elements/[elementType]/[ElementType]Manager.ts` In the method that parses YAML metadata: ```typescript private parseMetadata(yamlContent: string): I[ElementType]Metadata { // ... existing parsing ... // Extract and validate triggers triggers: Array.isArray(metadataSource.triggers) ? metadataSource.triggers .map((trigger: string) => sanitizeInput(trigger, MAX_TRIGGER_LENGTH)) .filter((trigger: string) => trigger && TRIGGER_VALIDATION_REGEX.test(trigger)) .slice(0, 20) : // Limit number of triggers [], // ... rest of metadata ... } ``` ### 5. Update BaseElement for Metadata Preservation **File**: `src/elements/BaseElement.ts` Add selective preservation for triggers: ```typescript // In the metadata processing section if ('triggers' in metadata && Array.isArray((metadata as any).triggers)) { baseMetadata.triggers = (metadata as any).triggers; } ``` ### 6. Update EnhancedIndexManager **File**: `src/portfolio/EnhancedIndexManager.ts` In the `extractActionTriggers` method, add extraction for your element type: ```typescript // Extract [ElementType] triggers case 'elementTypes': { const elementManager = [ElementType]Manager.getInstance(); const elements = await elementManager.listElements(); for (const elementName of elements) { try { const element = await elementManager.getElement(elementName); const metadata = element.getMetadata(); if (metadata.triggers && Array.isArray(metadata.triggers)) { for (const trigger of metadata.triggers) { const normalized = this.normalizeTrigger(trigger); if (normalized) { this.addTriggerMapping(normalized, elementName, actionTriggers); } } } } catch (error) { logger.warn(`Failed to extract triggers from ${elementType} ${elementName}`, { error }); } } break; } ``` ### 7. Create Unit Tests **File**: `test/unit/[ElementType]Manager.triggers.test.ts` Test scenarios to cover: - Basic trigger extraction - Invalid trigger filtering - Long trigger truncation - Special character handling - Empty/null trigger arrays - Maximum trigger count limits ```typescript describe('[ElementType]Manager - Trigger Extraction', () => { it('should extract valid triggers from metadata', async () => { const yamlContent = ` metadata: triggers: [create, generate, build] content: test `; const element = await manager.createElement('test-element', yamlContent); const metadata = element.getMetadata(); expect(metadata.triggers).toEqual(['create', 'generate', 'build']); }); // Add more test cases... }); ``` ### 8. Create Performance Tests **File**: `test/unit/[ElementType]Manager.triggers.performance.test.ts` ```typescript it('should handle large numbers of triggers efficiently', async () => { const triggers = Array.from({ length: 200 }, (_, i) => `trigger${i}`); // Test performance with many triggers }); ``` ### 9. Create Integration Tests **File**: `test/integration/[elementType]-enhanced-index.test.ts` ```typescript describe('[ElementType] Enhanced Index Integration', () => { it('should find elements by trigger verb', async () => { // Create element with triggers // Rebuild index // Search by trigger // Verify element found }); }); ``` ## Element-Specific Considerations ### Skills (#1121) - Skills often have action-oriented names already - Consider extracting verbs from skill names in addition to triggers - Example triggers: "analyze", "optimize", "validate", "debug" ### Templates (#1122) - Templates might use triggers for template selection - Consider context-specific triggers - Example triggers: "email", "report", "proposal", "invoice" ### Agents (#1123) - Agents are goal-oriented, triggers should reflect goals - May need multiple trigger categories (action vs. goal) - Example triggers: "investigate", "research", "solve", "coordinate" ## Validation Rules ### Trigger Format Requirements - **Length**: 1-50 characters - **Characters**: Alphanumeric, hyphens, underscores only - **Case**: Convert to lowercase for consistency - **Count**: Maximum 20 triggers per element (configurable) ### Security Considerations - Always sanitize input with `sanitizeInput()` function - Validate against regex pattern - Limit total number of triggers - Trim whitespace - Filter empty strings ## Testing Requirements ### Unit Tests - [ ] Valid trigger extraction - [ ] Invalid character filtering - [ ] Length validation - [ ] Count limits - [ ] Empty/null handling - [ ] Type checking ### Integration Tests - [ ] Enhanced Index integration - [ ] Search by trigger verb - [ ] Multiple elements with same trigger - [ ] No triggers scenario ### Performance Tests - [ ] Handle 200+ triggers - [ ] Large YAML files - [ ] Concurrent access ## Migration Considerations ### Backward Compatibility - Elements without triggers should work normally - Existing elements don't require triggers - Triggers are optional metadata ### Index Rebuild After implementing triggers for a new element type: 1. Clear existing index: `rm ~/.dollhouse/portfolio/capability-index.yaml` 2. Rebuild: The index will rebuild automatically on next access ## Common Pitfalls to Avoid 1. **Don't forget type annotations** - TypeScript compilation will fail 2. **Don't skip validation** - Security risk with unsanitized input 3. **Don't allow unlimited triggers** - Performance and storage impact 4. **Don't modify BaseElement carelessly** - Can break YAML serialization 5. **Don't forget ESM test compatibility** - Add to ignore list if needed ## Verification Steps After implementation: 1. Run build: `npm run build` 2. Run tests: `npm test` 3. Test manually: ```bash # Create element with triggers # Restart server # Search using trigger verb ``` 4. Check Enhanced Index: ```bash cat ~/.dollhouse/portfolio/capability-index.yaml | grep action_triggers -A 20 ``` ## Example PR Description Template ```markdown ## Summary Implements trigger extraction for [ElementType] elements as part of Enhanced Index improvements. ## Changes - Added `triggers` field to I[ElementType]Metadata interface - Implemented trigger validation and sanitization - Updated [ElementType]Manager to extract triggers from YAML - Added BaseElement metadata preservation for triggers - Integrated with EnhancedIndexManager action triggers - Added comprehensive unit and integration tests ## Testing - Unit tests for trigger extraction and validation - Integration tests with Enhanced Index - Performance tests with 200+ triggers - Manual testing with various trigger patterns ## Related Issues Fixes #[issue-number] ## Checklist - [ ] Tests pass locally - [ ] Build successful - [ ] Documentation updated - [ ] Backward compatible ``` ## Performance Expectations The trigger extraction system is designed to be efficient: - **Trigger extraction**: Should complete in <100ms for files up to 1MB - **Index rebuild**: With 1000+ elements should complete in <5 seconds - **Memory usage**: Should not exceed 50MB during trigger processing - **Search performance**: O(1) lookups with hash-based action_triggers map - **Validation overhead**: Negligible (<5ms) due to pre-compiled regex patterns Performance considerations: - Triggers are extracted once during element load, not on every access - Maximum 20 triggers per element prevents unbounded processing - 50-character limit per trigger keeps memory usage predictable - Index uses Maps for O(1) average-case lookups ## Troubleshooting Common Issues ### TypeScript Compilation Errors **Problem**: TypeScript errors about missing trigger field **Solution**: Ensure triggers field is added to the interface with proper typing: ```typescript triggers?: string[]; // Note the optional ? operator ``` ### Tests Failing in ESM Mode **Problem**: Jest tests fail with ESM import errors **Solution**: Add test file to ignore list in `test/jest.config.cjs`: ```javascript testPathIgnorePatterns: [ // ... other patterns 'your-element-enhanced-index\\.test\\.ts$' ] ``` ### Index Not Updating **Problem**: Triggers not appearing in searches after adding **Solution**: 1. Clear the capability index: `rm ~/.dollhouse/portfolio/capability-index.yaml` 2. Restart the MCP server 3. The index will rebuild automatically on next access ### Invalid Characters in Triggers **Problem**: Triggers with spaces or special characters not working **Solution**: Only alphanumeric, hyphens, and underscores allowed. The validation regex is: ```javascript /^[a-zA-Z0-9\-_]+$/ ``` ### Triggers Not Being Preserved **Problem**: Triggers disappear after saving element **Solution**: Check that the Manager's save method preserves the triggers array, even when empty: ```typescript if (key === 'triggers' && Array.isArray(value)) { acc[key] = value; // Preserve even empty arrays } ``` ## Version Compatibility > **Note**: This guide applies to **mcp-server v1.9.6+**. > > For earlier versions: > - v1.9.5 and below: Triggers not supported > - v1.9.6: Memory triggers added (PR #1133) > - v1.9.7+: Skills triggers added (PR #1136) > - Future: Templates and Agents triggers planned The trigger extraction system is backward compatible: - Elements without triggers continue to work normally - Existing elements can add triggers without breaking changes - Enhanced Index gracefully handles missing trigger fields ## Success Metrics Implementation is complete when: 1. ✅ Elements can define triggers in metadata 2. ✅ Triggers are validated and sanitized 3. ✅ Enhanced Index includes triggers in action_triggers 4. ✅ Search by trigger verb returns correct elements 5. ✅ Tests provide >95% coverage 6. ✅ Performance acceptable with 200+ triggers 7. ✅ Backward compatibility maintained ## Support For questions or issues: - Review PR #1133 for Memory implementation reference - Check SESSION_NOTES_2025-09-26-memory-triggers.md - Create issue with `trigger-extraction` label --- *Last updated: September 2025 (v1.9.7) - Based on successful Memory trigger implementation in PR #1133*

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/DollhouseMCP/DollhouseMCP'

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