Skip to main content
Glama

Keyboard Shortcuts MCP Server

by jenova-marie
convert-md-to-json.js4.97 kB
#!/usr/bin/env node import { readFileSync, writeFileSync, readdirSync, statSync, mkdirSync } from 'fs'; import { join, dirname, basename, relative } from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const dataDir = join(__dirname, '..', 'data'); const docsDir = join(__dirname, '..', 'docs'); function parseMdToJson(mdContent, filePath, metadata = null) { const lines = mdContent.split('\n'); const categories = []; let currentCategory = null; // Use provided metadata or extract from file path let os, desktop, application, fileName; if (metadata) { os = metadata.os; desktop = metadata.desktop || null; application = metadata.application || null; fileName = metadata.file; } else { // Extract from file path const relativePath = relative(dataDir, filePath); const parts = relativePath.split('/'); os = parts[0]; // ubuntu const type = parts[1]; // desktops, apps, tools desktop = null; application = null; if (type === 'desktops') { desktop = parts[2]; // gnome } else if (type === 'apps') { application = basename(filePath, '.md'); } else if (type === 'tools') { application = basename(filePath, '.md'); } fileName = basename(filePath, '.md'); } for (const line of lines) { // Skip title lines and TOC if (line.startsWith('# ') || line.startsWith('- [')) continue; if (line.trim() === '---') continue; // Skip dividers // Main category header (##) if (line.startsWith('## ')) { if (currentCategory && currentCategory.shortcuts.length > 0) { categories.push(currentCategory); } currentCategory = { name: line.replace('## ', '').trim(), shortcuts: [] }; continue; } // Sub-category header (###) - append to current category name if (line.startsWith('### ')) { if (currentCategory && currentCategory.shortcuts.length > 0) { categories.push(currentCategory); } const subName = line.replace('### ', '').trim(); // Keep parent category context if exists const parentName = currentCategory ? currentCategory.name.split(' - ')[0] : ''; currentCategory = { name: parentName ? `${parentName} - ${subName}` : subName, shortcuts: [] }; continue; } // Shortcut patterns: // - `keys` - description // - **keys** - description (bold format) const match1 = line.match(/^-\s+`([^`]+)`\s+-\s+(.+)$/); const match2 = line.match(/^-\s+\*\*([^*]+)\*\*\s+-\s+(.+)$/); const match = match1 || match2; if (match && currentCategory) { currentCategory.shortcuts.push({ keys: match[1].trim(), description: match[2].trim() }); } } // Add last category if (currentCategory && currentCategory.shortcuts.length > 0) { categories.push(currentCategory); } return { os, desktop, application, file: fileName, categories }; } function convertFile(mdPath) { try { const mdContent = readFileSync(mdPath, 'utf-8'); const jsonData = parseMdToJson(mdContent, mdPath); const jsonPath = mdPath.replace('.md', '.json'); writeFileSync(jsonPath, JSON.stringify(jsonData, null, 2)); console.log(`✓ Converted: ${relative(dataDir, mdPath)}`); } catch (error) { console.error(`✗ Failed: ${relative(dataDir, mdPath)}`, error.message); } } function findMdFiles(dir) { const files = []; const entries = readdirSync(dir); for (const entry of entries) { const fullPath = join(dir, entry); const stat = statSync(fullPath); if (stat.isDirectory()) { files.push(...findMdFiles(fullPath)); } else if (entry.endsWith('.md')) { files.push(fullPath); } } return files; } // Convert docs/ files with manual metadata function convertDocsFile(mdFileName, metadata) { const mdPath = join(docsDir, mdFileName); const outputDir = join(dataDir, metadata.os, 'desktops', metadata.desktop); try { mkdirSync(outputDir, { recursive: true }); const mdContent = readFileSync(mdPath, 'utf-8'); const jsonData = parseMdToJson(mdContent, mdPath, metadata); const jsonPath = join(outputDir, `${metadata.file}.json`); writeFileSync(jsonPath, JSON.stringify(jsonData, null, 2)); console.log(`✓ Converted: docs/${mdFileName} → ${relative(dataDir, jsonPath)}`); } catch (error) { console.error(`✗ Failed: docs/${mdFileName}`, error.message); } } // Main execution const mdFiles = findMdFiles(dataDir); console.log(`Found ${mdFiles.length} markdown files in data/ to convert\n`); for (const mdFile of mdFiles) { convertFile(mdFile); } // Convert KDE shortcuts for Ubuntu console.log('\nConverting docs/ files...\n'); convertDocsFile('kde.md', { os: 'ubuntu', desktop: 'kde', file: 'kde-shortcuts' }); console.log(`\n✓ Conversion complete!`);

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/jenova-marie/keyboard-shortcuts-mcp'

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