Skip to main content
Glama

AI Code Toolkit

by AgiFlow
AddRuleTool.ts8.52 kB
/** * AddRuleTool * * DESIGN PATTERNS: * - Tool pattern with getDefinition() and execute() methods * - YAML manipulation for RULES.yaml updates * - JSON Schema validation for inputs * * CODING STANDARDS: * - Implement Tool interface from ../types * - Use TOOL_NAME constant with snake_case * - Return CallToolResult with content array * - Handle errors with isError flag * - Validate all inputs thoroughly * * AVOID: * - Complex business logic in execute method * - Unhandled promise rejections * - Missing input validation */ import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; import type { Tool, ToolDefinition, RulesYamlConfig, RuleSection, RuleItem, } from '../types/index.js'; import { TemplatesManagerService } from '@agiflowai/aicode-utils'; import * as fs from 'node:fs/promises'; import * as yaml from 'js-yaml'; import * as path from 'node:path'; interface AddRuleToolInput { template_name?: string; pattern: string; description: string; inherits?: string[]; must_do?: RuleItem[]; should_do?: RuleItem[]; must_not_do?: RuleItem[]; is_global?: boolean; } export class AddRuleTool implements Tool<AddRuleToolInput> { static readonly TOOL_NAME = 'add_rule'; getDefinition(): ToolDefinition { return { name: AddRuleTool.TOOL_NAME, description: "Add a new design pattern rule to a template's RULES.yaml or global RULES.yaml. Rules define specific coding standards, must-do/must-not-do items, and code examples.", inputSchema: { type: 'object', properties: { template_name: { type: 'string', description: 'Name of the template (e.g., "nextjs-15", "typescript-mcp-package"). Omit for global rules.', }, pattern: { type: 'string', description: 'Pattern identifier (e.g., "src/index.ts", "export-standards")', }, description: { type: 'string', description: 'Description of the rule pattern', }, inherits: { type: 'array', items: { type: 'string' }, description: 'Optional array of inherited rule patterns (e.g., ["barrel-exports", "documentation-standards"])', }, must_do: { type: 'array', items: { type: 'object', properties: { rule: { type: 'string' }, example: { type: 'string' }, codeExample: { type: 'string' }, }, required: ['rule'], }, description: 'Array of must-do rules with optional examples and code examples', }, should_do: { type: 'array', items: { type: 'object', properties: { rule: { type: 'string' }, example: { type: 'string' }, codeExample: { type: 'string' }, }, required: ['rule'], }, description: 'Array of should-do rules (best practices)', }, must_not_do: { type: 'array', items: { type: 'object', properties: { rule: { type: 'string' }, example: { type: 'string' }, codeExample: { type: 'string' }, }, required: ['rule'], }, description: 'Array of must-not-do rules (anti-patterns)', }, is_global: { type: 'boolean', description: 'If true, adds to global RULES.yaml (templates/RULES.yaml). If false or omitted with template_name, adds to template-specific RULES.yaml', }, }, required: ['pattern', 'description'], additionalProperties: false, }, }; } async execute(input: AddRuleToolInput): Promise<CallToolResult> { try { // Determine if this is global or template-specific const isGlobal = input.is_global || !input.template_name; // Get templates root const templatesRoot = await TemplatesManagerService.findTemplatesPath(); let rulesPath: string; let templateRef: string; if (isGlobal) { rulesPath = path.join(templatesRoot, 'RULES.yaml'); templateRef = 'shared'; } else { const templatePath = path.join(templatesRoot, input.template_name!); // Check if template exists try { await fs.access(templatePath); } catch { return { content: [ { type: 'text', text: JSON.stringify( { error: `Template "${input.template_name}" not found at ${templatePath}`, available_hint: 'Check templates directory for available templates', }, null, 2, ), }, ], isError: true, }; } rulesPath = path.join(templatePath, 'RULES.yaml'); templateRef = input.template_name!; } // Read existing RULES.yaml or create new structure let rulesConfig: RulesYamlConfig; try { const content = await fs.readFile(rulesPath, 'utf-8'); const parsed = yaml.load(content) as RulesYamlConfig; rulesConfig = parsed || this.createDefaultConfig(templateRef, isGlobal); // Ensure rules array exists if (!rulesConfig.rules) { rulesConfig.rules = []; } } catch { // File doesn't exist, create new one rulesConfig = this.createDefaultConfig(templateRef, isGlobal); } // Check if rule pattern already exists const existingRule = rulesConfig.rules.find((r) => r.pattern === input.pattern); if (existingRule) { return { content: [ { type: 'text', text: JSON.stringify( { error: `Rule pattern "${input.pattern}" already exists in ${isGlobal ? 'global' : input.template_name} RULES.yaml`, existing_rule: existingRule, }, null, 2, ), }, ], isError: true, }; } // Create new rule section const newRule: RuleSection = { pattern: input.pattern, description: input.description, }; if (input.inherits && input.inherits.length > 0) { newRule.inherits = input.inherits; } if (input.must_do && input.must_do.length > 0) { newRule.must_do = input.must_do; } if (input.should_do && input.should_do.length > 0) { newRule.should_do = input.should_do; } if (input.must_not_do && input.must_not_do.length > 0) { newRule.must_not_do = input.must_not_do; } rulesConfig.rules.push(newRule); // Write back to file const yamlContent = yaml.dump(rulesConfig, { indent: 2, lineWidth: -1, // Disable line wrapping noRefs: true, }); await fs.writeFile(rulesPath, yamlContent, 'utf-8'); return { content: [ { type: 'text', text: JSON.stringify( { success: true, message: `Added rule pattern "${input.pattern}" to ${isGlobal ? 'global' : input.template_name} RULES.yaml`, file: rulesPath, rule: newRule, }, null, 2, ), }, ], }; } catch (error) { return { content: [ { type: 'text', text: JSON.stringify( { error: error instanceof Error ? error.message : 'Unknown error', template_name: input.template_name, pattern: input.pattern, }, null, 2, ), }, ], isError: true, }; } } private createDefaultConfig(templateRef: string, isGlobal: boolean): RulesYamlConfig { return { version: '1.0', template: templateRef, description: isGlobal ? 'Shared rules and patterns for all templates' : `Rules and patterns for ${templateRef} template`, 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/AgiFlow/aicode-toolkit'

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