Skip to main content
Glama

Token Saver MCP

by jerry426
generate-instructions.ts9.37 kB
#!/usr/bin/env tsx /** * Template-based documentation generator * Maintains static content in template, only generates dynamic tool sections */ import * as fs from 'node:fs/promises' import { glob } from 'glob' interface ParsedMetadata { name: string title: string category: string description: string docs: any } /** * Parse tool metadata from file (simplified version) */ async function parseToolFile(filePath: string): Promise<ParsedMetadata | null> { try { const content = await fs.readFile(filePath, 'utf-8') const metadataMatch = content.match(/export\s+const\s+metadata(?::\s*ToolMetadata)?\s*=\s*({[\s\S]*?^})\s*$/m) if (!metadataMatch) return null // Use Function constructor for simple parsing // eslint-disable-next-line no-new-func const parseFunc = new Function(`return ${metadataMatch[1].replace(/as\s+const/g, '')}`) return parseFunc() as ParsedMetadata } catch (error) { console.error(`Error parsing ${filePath}:`, error) return null } } /** * Generate tool documentation section */ function generateToolDocs(tool: ParsedMetadata): string { const sections: string[] = [] sections.push(`#### \`${tool.name}\``) if (tool.docs?.brief) { sections.push(`**${tool.docs.brief}**\n`) } sections.push(tool.description) // Add parameters if (tool.docs?.parameters && Object.keys(tool.docs.parameters).length > 0) { sections.push('\n**Parameters:**') Object.entries(tool.docs.parameters).forEach(([param, desc]) => { sections.push(`- \`${param}\`: ${desc}`) }) } // Add example if (tool.docs?.examples?.[0]) { sections.push('\n**Example:**') sections.push('```javascript') sections.push(tool.docs.examples[0].code) sections.push('```') } // Add tips if (tool.docs?.tips?.length > 0) { sections.push('\n**Tips:**') tool.docs.tips.forEach((tip: string) => { sections.push(`- ${tip}`) }) } // Add performance info if (tool.docs?.performance) { const perf = tool.docs.performance if (perf.speed || perf.tokenSavings) { sections.push('\n**Performance:**') if (perf.speed) sections.push(`- Speed: ${perf.speed}`) if (perf.tokenSavings) sections.push(`- Token Savings: ${perf.tokenSavings}`) } } // For helper tools if (tool.docs?.testsAutomatically) { sections.push('\n**Tests Automatically:**') tool.docs.testsAutomatically.forEach((item: string) => { sections.push(`- ${item}`) }) } if (tool.docs?.savesTimeVsManual) { sections.push('\n**Saves Time By:**') tool.docs.savesTimeVsManual.forEach((item: string) => { sections.push(`- ${item}`) }) } // Add a prominent separator after each tool sections.push('\n---\n') return sections.join('\n') } /** * Generate category section */ function generateCategorySection(category: string, tools: ParsedMetadata[]): string { const sections: string[] = [] const categoryNames: Record<string, string> = { lsp: 'Code Navigation (LSP)', cdp: 'Browser Control (CDP)', helper: 'Browser Testing Helpers', system: 'System Tools', memory: 'Memory System', } const categoryDescriptions: Record<string, string> = { lsp: 'Tools for navigating and understanding code using Language Server Protocol:', cdp: 'Tools for browser automation using Chrome DevTools Protocol:', helper: 'High-level testing utilities that combine multiple operations:', system: '', memory: 'Persistent context management replacing /resume with 90% fewer tokens:', } sections.push(`### ${categoryNames[category]} - ${tools.length} Tools`) if (categoryDescriptions[category]) { sections.push(`\n${categoryDescriptions[category]}`) } // Add separator before first tool sections.push('\n---\n') tools.forEach((tool) => { const toolDoc = generateToolDocs(tool) sections.push(toolDoc) }) return sections.join('\n') } /** * Generate decision tree based on available tools */ function generateDecisionTree(tools: ParsedMetadata[]): string { const hasLsp = tools.some(t => t.category === 'lsp') const hasCdp = tools.some(t => t.category === 'cdp') const hasHelper = tools.some(t => t.category === 'helper') const hasMemory = tools.some(t => t.category === 'memory') const lines: string[] = [] if (hasMemory) { lines.push('Starting new session? → smart_resume (instead of /resume)') lines.push('Saving context? → write_memory with importance/verbosity') lines.push('Finding past info? → read_memory or list_memories') } if (hasLsp) { lines.push('Finding code? → search_text → get_definition → get_references') lines.push('Understanding code? → get_hover → get_document_symbols') lines.push('Refactoring? → rename_symbol (after get_references)') lines.push('Finding errors? → get_diagnostics') } if (hasHelper) { lines.push('Testing UI? → test_react_component, test_form_validation') lines.push('Testing API? → test_api_endpoint') lines.push('Debugging browser? → debug_javascript_error → get_browser_console') } if (hasCdp) { lines.push('Need interaction? → click_element, type_in_browser') lines.push('Performance issues? → check_page_performance') } return lines.join('\n') } /** * Process template with replacements */ async function processTemplate(templatePath: string, replacements: Record<string, string>): Promise<string> { let content = await fs.readFile(templatePath, 'utf-8') // Replace all placeholders for (const [placeholder, value] of Object.entries(replacements)) { const regex = new RegExp(`{{${placeholder}}}`, 'g') content = content.replace(regex, value) } return content } /** * Main function */ async function main() { console.log('📄 Template-Based Documentation Generator') console.log('🎯 Preserving static content, generating dynamic sections\n') try { // Load package.json for version const packageJson = JSON.parse(await fs.readFile('package.json', 'utf-8')) const version = packageJson.version // Find and parse all tool files const toolFiles = await glob('mcp-server/src/tools/**/*.ts', { ignore: ['**/*.test.ts', '**/index.ts', '**/types.ts', '**/tool-manager.ts', '**/lsp-implementations.ts'], }) const tools: ParsedMetadata[] = [] for (const file of toolFiles) { const metadata = await parseToolFile(file) if (metadata) { tools.push(metadata) console.log(` ✓ Parsed: ${metadata.name} (${metadata.category})`) } } // Categorize tools const toolsByCategory: Record<string, ParsedMetadata[]> = {} tools.forEach((tool) => { if (!toolsByCategory[tool.category]) { toolsByCategory[tool.category] = [] } toolsByCategory[tool.category].push(tool) }) // Generate category summaries const categorySummary: string[] = [] const categoryOrder = ['memory', 'lsp', 'cdp', 'helper', 'system'] categoryOrder.forEach((cat) => { if (toolsByCategory[cat]) { const count = toolsByCategory[cat].length const name = { memory: 'Memory tools', lsp: 'LSP tools', cdp: 'CDP tools', helper: 'Helper tools', system: 'System tools', }[cat] const desc = { memory: 'Persistent context management', lsp: 'Code navigation and intelligence', cdp: 'Browser automation and control', helper: 'High-level testing utilities', system: 'Utilities and meta functions', }[cat] categorySummary.push(`- **${count} ${name}** - ${desc}`) } }) // Generate tool sections const toolSections: string[] = [] categoryOrder.forEach((cat) => { if (toolsByCategory[cat]) { toolSections.push(generateCategorySection(cat, toolsByCategory[cat])) } }) // Prepare replacements const replacements: Record<string, string> = { GENERATION_DATE: new Date().toISOString(), VERSION: version, TOTAL_TOOLS: tools.length.toString(), TOOL_CATEGORIES_SUMMARY: categorySummary.join('\n'), TOOL_SECTIONS: toolSections.join('\n\n'), DECISION_TREE: generateDecisionTree(tools), CATEGORY_COUNT: Object.keys(toolsByCategory).length.toString(), } // Process template const output = await processTemplate('README_USAGE_GUIDE.template.md', replacements) // Write output await fs.writeFile('README_USAGE_GUIDE.md', output) console.log(`\n✅ Generated documentation from template`) console.log(`📈 Processed ${tools.length} tools in ${Object.keys(toolsByCategory).length} categories`) console.log(`📝 Static content preserved, dynamic sections updated`) console.log('\n💡 Benefits of template approach:') console.log(' - Developers maintain human-written content') console.log(' - Tool documentation stays auto-generated') console.log(' - Best of both worlds: quality + accuracy') } catch (error) { console.error('❌ Error:', error) process.exit(1) } } if (require.main === module) { main().catch(console.error) } export { generateToolDocs, processTemplate }

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/jerry426/token-saver-mcp'

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