Skip to main content
Glama
migrate.ts6.67 kB
import { z } from 'zod'; import * as fs from 'fs'; import * as path from 'path'; import { migrateBoard } from 'knbn-core/utils/migrations'; import { saveBoard } from 'knbn-core/utils/board-files'; import { Brands } from 'knbn-core/utils/ts'; import * as yaml from 'js-yaml'; import { registerStructuredTool } from '../../patch'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; export interface MigrateResult { migratedCount: number; skippedCount: number; errorCount: number; results: Array<{ filename: string; status: 'migrated' | 'skipped' | 'error'; fromVersion?: string; toVersion?: string; message: string; backupCreated?: boolean; }>; summary: string; } export const registerMigrateTool = (server: McpServer) => registerStructuredTool(server, 'migrate', { title: 'Migrate Board Files', description: 'Migrate board files to the latest version with options for dry-run, backup, and batch processing', inputSchema: { files: z.array(z.string()).optional().describe('Board files to migrate (e.g., ["board1.knbn", "board2.knbn"])'), all: z.boolean().optional().describe('Migrate all .knbn files in the current directory'), dryRun: z.boolean().optional().describe('Show what would be migrated without making changes'), backup: z.boolean().optional().describe('Create backup files before migration (adds .bak extension)') }, outputSchema: { migratedCount: z.number(), skippedCount: z.number(), errorCount: z.number(), results: z.array(z.object({ filename: z.string(), status: z.enum(['migrated', 'skipped', 'error']), fromVersion: z.string().optional(), toVersion: z.string().optional(), message: z.string(), backupCreated: z.boolean().optional() })), summary: z.string() } }, async (args) => { // Validate input if (!args.files && !args.all) { throw new Error('Either files array or all flag must be specified'); } const { files, all, dryRun = false, backup = false } = args; let filesToMigrate: string[] = []; if (all) { // Find all .knbn files in current directory const currentDir = process.cwd(); const allFiles = fs.readdirSync(currentDir); filesToMigrate = allFiles.filter(file => file.endsWith('.knbn')); if (filesToMigrate.length === 0) { return { structuredContent: { migratedCount: 0, skippedCount: 0, errorCount: 0, results: [], summary: 'No .knbn files found in current directory' } }; } } else if (files && files.length > 0) { filesToMigrate = files; } let migratedCount = 0; let skippedCount = 0; let errorCount = 0; const results: MigrateResult['results'] = []; for (const filename of filesToMigrate) { const filepath = path.resolve(filename); if (!fs.existsSync(filepath)) { results.push({ filename, status: 'error', message: `File not found: ${filename}` }); errorCount++; continue; } try { // Load raw file content to check version const fileContent = fs.readFileSync(filepath, 'utf8'); let rawData: any; try { rawData = yaml.load(fileContent) as any; } catch (yamlError) { results.push({ filename, status: 'error', message: `Invalid board file format: ${filename}` }); errorCount++; continue; } if (!rawData || !rawData.metadata || !rawData.metadata.version) { results.push({ filename, status: 'error', message: `Invalid board file format: ${filename}` }); errorCount++; continue; } const currentVersion = rawData.metadata.version; // Try to migrate const migratedBoard = migrateBoard(rawData); const newVersion = migratedBoard.metadata.version; if (currentVersion === newVersion) { results.push({ filename, status: 'skipped', fromVersion: currentVersion, toVersion: newVersion, message: `Already at latest version (${currentVersion})` }); skippedCount++; continue; } if (dryRun) { results.push({ filename, status: 'migrated', fromVersion: currentVersion, toVersion: newVersion, message: `Would migrate from ${currentVersion} to ${newVersion}` }); migratedCount++; continue; } // Create backup if requested let backupCreated = false; if (backup) { const backupPath = `${filepath}.bak`; fs.copyFileSync(filepath, backupPath); backupCreated = true; } // Save migrated board const filepathBrand = Brands.Filepath(filepath); saveBoard(filepathBrand, migratedBoard); results.push({ filename, status: 'migrated', fromVersion: currentVersion, toVersion: newVersion, message: `Migrated from ${currentVersion} to ${newVersion}`, backupCreated }); migratedCount++; } catch (error: any) { results.push({ filename, status: 'error', message: `Migration failed - ${error?.message}` }); errorCount++; } } // Generate summary let summary = 'Migration Summary:\n'; if (dryRun) { summary += ` Would migrate: ${migratedCount} files\n`; } else { summary += ` Migrated: ${migratedCount} files\n`; } summary += ` Already current: ${skippedCount} files`; if (errorCount > 0) { summary += `\n Errors: ${errorCount} files`; } if (dryRun && migratedCount > 0) { summary += '\n\nRun without dryRun to perform the migration.'; } return { structuredContent: { migratedCount, skippedCount, errorCount, results, summary } }; } );

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/mattbalmer/knbn-mcp'

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