Skip to main content
Glama

Claude Slack

bundle-api.jsโ€ข7.78 kB
#!/usr/bin/env node /** * Bundle API Script for Claude-Slack * * This script copies the API package into the template directory * so it gets included when users run `npx claude-slack`. * * Run with: npm run bundle-api */ const fs = require('fs-extra'); const path = require('path'); const { exec } = require('child_process'); const util = require('util'); const execAsync = util.promisify(exec); // Colors for console output const colors = { reset: '\x1b[0m', bright: '\x1b[1m', green: '\x1b[32m', yellow: '\x1b[33m', blue: '\x1b[34m', red: '\x1b[31m', cyan: '\x1b[36m' }; function log(message, color = 'reset') { console.log(`${colors[color]}${message}${colors.reset}`); } async function bundleAPI() { log('\n๐Ÿ“ฆ Claude-Slack API Bundling Script', 'bright'); log('='.repeat(50), 'blue'); // Define paths const rootDir = path.join(__dirname, '..'); const apiSource = path.join(rootDir, 'api'); const templateDest = path.join(rootDir, 'template', 'global', 'mcp', 'claude-slack', 'api'); // Step 1: Verify API source exists log('\n1๏ธโƒฃ Checking API source directory...', 'cyan'); if (!fs.existsSync(apiSource)) { log(`โŒ API source not found at: ${apiSource}`, 'red'); process.exit(1); } log(` โœ“ Found API at: ${apiSource}`, 'green'); // Step 2: Clean destination log('\n2๏ธโƒฃ Cleaning destination directory...', 'cyan'); if (fs.existsSync(templateDest)) { await fs.remove(templateDest); log(' โœ“ Removed old API bundle', 'green'); } // Step 3: Copy API with filtering log('\n3๏ธโƒฃ Bundling API package...', 'cyan'); let fileCount = 0; let skippedCount = 0; const skippedPatterns = []; await fs.copy(apiSource, templateDest, { filter: (src, dest) => { const relativePath = path.relative(apiSource, src); // Always include directories (to traverse into them) if (fs.statSync(src).isDirectory()) { // But skip certain directories entirely if (src.includes('__pycache__') || src.includes('.pytest_cache') || src.includes('.git') || src.includes('node_modules')) { skippedPatterns.push(relativePath || path.basename(src)); return false; } return true; } // Skip unwanted files if (src.includes('.pyc') || // Compiled Python src.includes('.pyo') || // Optimized Python src.includes('.egg-info') || // Package info src.includes('.DS_Store') || // macOS files src.endsWith('~') || // Backup files path.basename(src).startsWith('.')) { // Hidden files skippedCount++; return false; } // Skip test files if not needed in production if (src.includes('/tests/') || src.includes('/test_') || src.endsWith('_test.py')) { skippedCount++; return false; } // Include everything else fileCount++; return true; } }); log(` โœ“ Copied ${fileCount} files`, 'green'); if (skippedCount > 0) { log(` โœ“ Skipped ${skippedCount} unnecessary files`, 'yellow'); } if (skippedPatterns.length > 0) { log(` โœ“ Excluded patterns: ${[...new Set(skippedPatterns)].join(', ')}`, 'yellow'); } // Step 4: Create __init__.py if missing log('\n4๏ธโƒฃ Ensuring package structure...', 'cyan'); const initPath = path.join(templateDest, '__init__.py'); if (!fs.existsSync(initPath)) { await fs.writeFile(initPath, '"""Claude-Slack API Package"""\n'); log(' โœ“ Created __init__.py', 'green'); } // Step 5: Generate requirements.txt for the bundled API log('\n5๏ธโƒฃ Generating requirements.txt...', 'cyan'); const requirementsSource = path.join(apiSource, 'requirements.txt'); const requirementsDest = path.join(rootDir, 'template', 'global', 'mcp', 'claude-slack', 'requirements.txt'); // Start with base requirements let requirements = `# Claude-Slack API Requirements # Generated by bundle-api.js - DO NOT EDIT MANUALLY # Core API dependencies aiosqlite>=0.19.0 qdrant-client>=1.7.0 sentence-transformers>=2.2.0 numpy>=1.24.0 # MCP Server dependencies mcp>=0.1.0 python-dotenv>=1.0.0 # Optional dependencies # Uncomment based on your deployment: # psycopg2-binary>=2.9.0 # For PostgreSQL # redis>=4.5.0 # For Redis caching `; // If there's a source requirements.txt, merge it if (fs.existsSync(requirementsSource)) { const sourceReqs = await fs.readFile(requirementsSource, 'utf8'); const customReqs = sourceReqs .split('\n') .filter(line => line && !line.startsWith('#') && !requirements.includes(line)) .join('\n'); if (customReqs) { requirements += '\n# Additional requirements from API\n' + customReqs + '\n'; } } await fs.writeFile(requirementsDest, requirements); log(' โœ“ Generated requirements.txt', 'green'); // Step 8: Verify the bundle log('\n8๏ธโƒฃ Verifying bundle integrity...', 'cyan'); const criticalFiles = [ 'api/__init__.py', 'api/unified_api.py', 'api/db/sqlite_store.py', 'api/db/qdrant_store.py', 'api/db/message_store.py', 'api/models.py', 'requirements.txt', ]; let allGood = true; for (const file of criticalFiles) { const fullPath = path.join(rootDir, 'template', 'global', 'mcp', 'claude-slack', file); if (fs.existsSync(fullPath)) { log(` โœ“ ${file}`, 'green'); } else { log(` โœ— Missing: ${file}`, 'red'); allGood = false; } } // Step 9: Generate bundle info log('\n9๏ธโƒฃ Generating bundle info...', 'cyan'); const bundleInfo = { bundled_at: new Date().toISOString(), api_version: '4.1.0', files_bundled: fileCount, files_skipped: skippedCount, bundle_size: await calculateDirSize(templateDest), requirements: requirements.split('\n').filter(l => l && !l.startsWith('#')).length }; const bundleInfoPath = path.join(templateDest, 'bundle_info.json'); await fs.writeJson(bundleInfoPath, bundleInfo, { spaces: 2 }); log(' โœ“ Created bundle_info.json', 'green'); // Final summary log('\n' + '='.repeat(50), 'blue'); if (allGood) { log('โœ… API bundling completed successfully!', 'bright'); log(`\n๐Ÿ“Š Bundle Summary:`, 'cyan'); log(` โ€ข Files bundled: ${fileCount}`, 'green'); log(` โ€ข Files skipped: ${skippedCount}`, 'yellow'); log(` โ€ข Bundle size: ${formatBytes(bundleInfo.bundle_size)}`, 'green'); log(` โ€ข Requirements: ${bundleInfo.requirements} packages`, 'green'); log(`\n๐Ÿ“ Next steps:`, 'cyan'); log(` 1. Test locally: npm run test-install`, 'blue'); log(` 2. Publish: npm publish`, 'blue'); log(` 3. Users install: npx claude-slack`, 'blue'); } else { log('โš ๏ธ Bundle completed with warnings - review missing files', 'yellow'); } } async function calculateDirSize(dir) { let size = 0; async function walkDir(currentPath) { const files = await fs.readdir(currentPath); for (const file of files) { const filePath = path.join(currentPath, file); const stats = await fs.stat(filePath); if (stats.isDirectory()) { await walkDir(filePath); } else { size += stats.size; } } } await walkDir(dir); return size; } function formatBytes(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } // Run the bundling bundleAPI().catch(error => { log(`\nโŒ Bundling failed: ${error.message}`, 'red'); console.error(error); process.exit(1); });

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/theo-nash/claude-slack'

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