mini-todo-list-mcp
Version:
A streamlined Model Context Protocol (MCP) server for todo management with essential CRUD operations, bulk functionality, and workflow support
132 lines (111 loc) • 3.28 kB
text/typescript
/**
* RuleService.ts - Rule management service
*
* Core business logic for managing rules.
* Includes: Add rules from file, get rules, and clear all rules.
*/
import {
Rule,
createRule,
AddRulesSchema,
GetRulesSchema,
} from '../models/Rule.js'
import { z } from 'zod'
import { databaseService } from './DatabaseService.js'
import * as fs from 'fs'
import * as path from 'path'
/**
* RuleService Class
*/
class RuleService {
/**
* Add rules from a file
*/
async addRules(data: { filePath: string; clearAll?: boolean }): Promise<Rule[]> {
const { filePath, clearAll } = data
// Clear all existing rules if requested
if (clearAll) {
const deletedCount = this.clearAllRules()
console.log(`Cleared ${deletedCount} existing rules before adding new ones`)
}
// Check if file exists
if (!fs.existsSync(filePath)) {
throw new Error(`File does not exist: ${filePath}`)
}
if (!fs.statSync(filePath).isFile()) {
throw new Error(`Path is not a file: ${filePath}`)
}
// Read file content
let fileContent: string
try {
fileContent = await fs.promises.readFile(filePath, 'utf-8')
} catch (error) {
throw new Error(`Failed to read file ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`)
}
const trimmedContent = fileContent.trim()
if (!trimmedContent) {
throw new Error(`File is empty: ${filePath}`)
}
// Create rule with file content as description
const ruleData = createRule(trimmedContent, filePath)
const db = databaseService.getDb()
const stmt = db.prepare(`
INSERT INTO rules (description, createdAt, updatedAt, filePath)
VALUES (?, ?, ?, ?)
`)
const result = stmt.run(
ruleData.description,
ruleData.createdAt,
ruleData.updatedAt,
ruleData.filePath,
)
const ruleId = result.lastInsertRowid as number
// Return the created rule with the auto-generated ID
const createdRule: Rule = {
id: ruleId,
...ruleData,
}
return [createdRule]
}
/**
* Get all rules or a specific rule by ID
*/
getRules(data?: { id?: number }): Rule[] {
const db = databaseService.getDb()
if (data?.id) {
// Get specific rule by ID
const stmt = db.prepare('SELECT * FROM rules WHERE id = ?')
const row = stmt.get(data.id) as any
if (!row) return []
return [this.rowToRule(row)]
} else {
// Get all rules
const stmt = db.prepare('SELECT * FROM rules ORDER BY createdAt ASC')
const rows = stmt.all() as any[]
return rows.map(row => this.rowToRule(row))
}
}
/**
* Clear all rules from the database
*/
clearAllRules(): number {
const db = databaseService.getDb()
const stmt = db.prepare('DELETE FROM rules')
const result = stmt.run()
return result.changes
}
/**
* Helper to convert a database row to a Rule object
*/
private rowToRule(row: any): Rule {
return {
id: row.id,
description: row.description,
createdAt: row.createdAt,
updatedAt: row.updatedAt,
filePath: row.filePath,
}
}
}
// Create a singleton instance
export const ruleService = new RuleService()