Skip to main content
Glama

MCP Memory Server

by keleshteri
rule-engine.ts8.12 kB
/** * @ai-metadata * @class: RuleEngine * @description: File modification rules engine that enforces AI safety protocols, permission checks, and approval workflows for secure AI assistant operations * @last-update: 2024-12-20 * @last-editor: Mohammad Mehdi Shaban Keleshteri * @changelog: ./CHANGELOG.md * @stability: stable * @edit-permissions: method-specific * @method-permissions: { "constructor": "read-only", "checkBeforeModification": "read-only", "getActionsAfterModification": "read-only", "evaluateRule": "read-only", "evaluateCondition": "read-only", "getDefaultRules": "read-only", "addCustomRule": "allow", "removeRule": "allow", "enableRule": "allow", "disableRule": "allow", "listRules": "read-only" } * @dependencies: ["chalk", "./types.js"] * @tests: ["./tests/rule-engine.test.js"] * @breaking-changes-risk: high * @review-required: true * @ai-context: "This is the core safety system that enforces file modification rules and permissions for AI assistants. Changes here directly affect AI safety protocols. Extreme caution required." * * @approvals: * - dev-approved: false * - dev-approved-by: "" * - dev-approved-date: "" * - code-review-approved: false * - code-review-approved-by: "" * - code-review-date: "" * - qa-approved: false * - qa-approved-by: "" * - qa-approved-date: "" * * @approval-rules: * - require-dev-approval-for: ["breaking-changes", "security-related", "rule-modifications", "safety-protocols"] * - require-code-review-for: ["all-changes"] * - require-qa-approval-for: ["production-ready"] */ import chalk from 'chalk'; import { AIMetadata, ApprovalStatus, AIRule } from './types.js'; export class RuleEngine { private rules: AIRule[]; constructor() { this.rules = this.getDefaultRules(); } async checkBeforeModification(filePath: string, metadata: AIMetadata | null, approvals: ApprovalStatus | null): Promise<{ allowed: boolean; reasons: string[]; warnings: string[]; }> { const result = { allowed: true, reasons: [] as string[], warnings: [] as string[] }; if (!metadata) { result.warnings.push('No @ai-metadata found in file'); return result; } // Check edit permissions if (metadata.editPermissions === 'read-only') { result.allowed = false; result.reasons.push('File is marked as read-only'); } // Check if file requires dev approval if (metadata.breakingChangesRisk === 'high' && (!approvals?.devApproved)) { result.allowed = false; result.reasons.push('High-risk file requires dev approval before modification'); } // Check if review is required if (metadata.reviewRequired && (!approvals?.codeReviewApproved)) { result.allowed = false; result.reasons.push('File requires code review approval before modification'); } // Check stability if (metadata.stability === 'deprecated') { result.warnings.push('This file is deprecated - consider if modification is necessary'); } // Apply custom rules for (const rule of this.rules.filter(r => r.enabled)) { const ruleResult = this.evaluateRule(rule, metadata, approvals, filePath); if (!ruleResult.passed) { if (rule.priority > 8) { result.allowed = false; result.reasons.push(ruleResult.message); } else { result.warnings.push(ruleResult.message); } } } return result; } getActionsAfterModification(filePath: string, metadata: AIMetadata | null): string[] { const actions: string[] = [ 'invalidate_approvals', 'update_last_modified', 'add_to_changelog' ]; if (metadata?.breakingChangesRisk === 'high') { actions.push('require_immediate_review'); } if (metadata?.tests && metadata.tests.length > 0) { actions.push('run_tests'); } return actions; } private evaluateRule(rule: AIRule, metadata: AIMetadata, approvals: ApprovalStatus | null, filePath: string): { passed: boolean; message: string; } { try { // Simple rule evaluation - in a real implementation, you might use a proper expression evaluator const context = { metadata, approvals: approvals || {}, filePath, // Helper functions hasApproval: (type: string) => { if (!approvals) return false; return (approvals as any)[`${type}Approved`] === true; }, isHighRisk: () => metadata.breakingChangesRisk === 'high', isReadOnly: () => metadata.editPermissions === 'read-only', isDeprecated: () => metadata.stability === 'deprecated' }; // Basic rule evaluation (you could use a more sophisticated expression evaluator) const passed = this.evaluateCondition(rule.condition, context); return { passed, message: rule.action }; } catch (error) { console.warn(chalk.yellow(`Warning: Could not evaluate rule ${rule.id}: ${error}`)); return { passed: true, message: '' }; } } private evaluateCondition(condition: string, context: any): boolean { // Simple condition evaluator - replace with a proper one for production try { // Create a safe evaluation context const evalContext = { ...context, // Prevent access to dangerous functions eval: undefined, Function: undefined, require: undefined, process: undefined, global: undefined, window: undefined }; // Use Function constructor for safe evaluation (still be careful in production) const func = new Function(...Object.keys(evalContext), `return ${condition}`); return func(...Object.values(evalContext)); } catch (error) { console.warn(chalk.yellow(`Could not evaluate condition: ${condition}`)); return true; // Default to allowing if we can't evaluate } } private getDefaultRules(): AIRule[] { return [ { id: 'no-modify-read-only', name: 'Prevent Read-Only Modifications', condition: 'isReadOnly()', action: 'File is marked as read-only and cannot be modified', priority: 10, enabled: true }, { id: 'high-risk-needs-approval', name: 'High Risk Needs Approval', condition: 'isHighRisk() && !hasApproval("dev")', action: 'High-risk file requires developer approval', priority: 9, enabled: true }, { id: 'deprecated-warning', name: 'Deprecated File Warning', condition: 'isDeprecated()', action: 'Consider if modifying deprecated file is necessary', priority: 5, enabled: true }, { id: 'review-required', name: 'Review Required', condition: 'metadata.reviewRequired && !hasApproval("codeReview")', action: 'File requires code review before modification', priority: 8, enabled: true }, { id: 'stable-code-protection', name: 'Stable Code Protection', condition: 'metadata.stability === "stable" && !hasApproval("dev")', action: 'Stable code should be reviewed before modification', priority: 6, enabled: true } ]; } addCustomRule(rule: AIRule): void { this.rules.push(rule); console.log(chalk.blue(`➕ Added custom rule: ${rule.name}`)); } removeRule(ruleId: string): void { this.rules = this.rules.filter(rule => rule.id !== ruleId); console.log(chalk.blue(`➖ Removed rule: ${ruleId}`)); } enableRule(ruleId: string): void { const rule = this.rules.find(r => r.id === ruleId); if (rule) { rule.enabled = true; console.log(chalk.green(`✅ Enabled rule: ${ruleId}`)); } } disableRule(ruleId: string): void { const rule = this.rules.find(r => r.id === ruleId); if (rule) { rule.enabled = false; console.log(chalk.yellow(`⏸️ Disabled rule: ${ruleId}`)); } } listRules(): AIRule[] { return [...this.rules]; } }

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/keleshteri/mcp-memory'

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