UNPKG

@goparrot/franchise-mcp-server

Version:

MCP Server for Franchise API

221 lines (220 loc) • 9.21 kB
#!/usr/bin/env node import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; import * as fs from 'fs'; import * as fsPromises from 'fs/promises'; import * as path from 'path'; import * as os from 'os'; import YAML from 'yaml'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; import { startServer } from './src/server.js'; import readline from 'node:readline'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Function to prompt for ACCESS_TOKEN async function promptForAccessToken() { const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); console.log('\nšŸ“‹ To get your access token:'); console.log('1. Go to https://dashboardv2.goparrot.ai'); console.log('2. Open Developer Tools (F12 or right-click -> Inspect)'); console.log('3. Go to Network tab'); console.log('4. Refresh the page'); console.log('5. Look for any request and find "x-access-token" in the request headers'); console.log('6. Copy the token value\n'); return new Promise((resolve) => { rl.question('šŸ”‘ Paste your x-access-token from GoParrot Dashboard: ', (token) => { rl.close(); resolve(token.trim()); }); }); } async function promptForDevMode() { const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); return new Promise((resolve) => { rl.question('\nšŸļø Use dev environment? (y/N): ', (answer) => { rl.close(); resolve(answer.trim().toLowerCase() === 'y'); }); }); } async function updateGooseConfig(accessToken, useSandbox) { try { console.log('\nUpdating Goose configuration...'); const homedir = os.homedir(); const gooseConfigPath = path.join(homedir, '.config', 'goose', 'config.yaml'); // Check if Goose config directory exists const gooseConfigDir = path.dirname(gooseConfigPath); if (!fs.existsSync(gooseConfigDir)) { console.warn('Warning: Goose directory not found at', gooseConfigDir); console.warn('Goose does not appear to be installed on this system.'); console.warn('Skipping Goose configuration update.'); return; } // Read existing config or create new one let config = { extensions: {} }; if (fs.existsSync(gooseConfigPath)) { const configContent = await fsPromises.readFile(gooseConfigPath, 'utf-8'); try { config = YAML.parse(configContent) || { extensions: {} }; } catch (_) { console.log('Error parsing Goose config, creating new one'); } } else { console.log('Goose config file not found, creating new one'); } // Add or update Franchise MCP Server extension if (!config.extensions) { config.extensions = {}; } config.extensions['franchise_mcp_server'] = { name: 'Franchise MCP Server', cmd: 'npx', args: ['franchise-mcp-server', 'start'], enabled: true, type: 'stdio', env: { DASHBOARD_ACCESS_TOKEN: accessToken, SANDBOX: useSandbox ? 'true' : 'false', }, }; // Write updated config await fsPromises.writeFile(gooseConfigPath, YAML.stringify(config, { collectionStyle: 'block', })); console.log('Updated Goose configuration at', gooseConfigPath); } catch (error) { console.error('Failed to update Goose config:', error); } } async function updateClaudeConfig(accessToken, useSandbox) { try { console.log('\nUpdating Claude configuration...'); const homedir = os.homedir(); const platform = os.platform(); let claudeConfigPath; // Set config path based on operating system if (platform === 'win32') { claudeConfigPath = path.join(homedir, 'AppData', 'Roaming', 'Claude', 'claude_desktop_config.json'); } else if (platform === 'darwin') { claudeConfigPath = path.join(homedir, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'); } else { claudeConfigPath = path.join(homedir, '.config', 'claude', 'config.json'); } // Check if Claude config directory exists const claudeConfigDir = path.dirname(claudeConfigPath); const claudeAppDir = path.dirname(claudeConfigDir); if (!fs.existsSync(claudeAppDir)) { console.warn('Warning: Claude app directory not found at', claudeAppDir); console.warn('Claude does not appear to be installed on this system.'); console.warn('Skipping Claude configuration update.'); return; } if (!fs.existsSync(claudeConfigDir)) { console.log('Claude config directory not found, creating it...'); await fsPromises.mkdir(claudeConfigDir, { recursive: true }); } // Read existing config or create new one let config = {}; if (fs.existsSync(claudeConfigPath)) { const configContent = await fsPromises.readFile(claudeConfigPath, 'utf-8'); try { config = JSON.parse(configContent); } catch (e) { console.log('Error parsing Claude config, creating new one'); } } else { console.log('Claude config file not found, creating new one'); } // Add or update MCP Server configuration if (!config.mcpServers) { config.mcpServers = {}; } config.mcpServers['mcp_franchise_api'] = { command: 'npx', args: ['franchise-mcp-server', 'start'], env: { DASHBOARD_ACCESS_TOKEN: accessToken, SANDBOX: useSandbox ? 'true' : 'false', }, }; // Write updated config await fsPromises.writeFile(claudeConfigPath, JSON.stringify(config, null, 2)); console.log('Updated Claude configuration at', claudeConfigPath); } catch (error) { console.error('Failed to update Claude config:', error); } } async function generateGooseUrl() { try { const packageName = 'franchise-mcp-server'; const gooseUrl = `goose://extension?cmd=npx&arg=${encodeURIComponent(packageName)}&arg=start&id=mcp_franchise_api&name=Franchise%20MCP%20Server&description=Franchise%20API%20MCP%20Server`; return gooseUrl; } catch (error) { console.error('Failed to generate Goose URL:', error); throw error; } } async function main() { const startCommand = async () => { try { await startServer(); } catch (error) { console.error(' Failed to start server:', error); process.exit(1); } }; const argv = await yargs(hideBin(process.argv)) .scriptName('mcp_franchise_api') .usage('$0 [cmd] [args]') .command('install', 'Install Franchise MCP Server in Goose and Claude', {}, async () => { console.log('\nšŸš€ Starting Franchise MCP Server installation...\n'); console.log('This will configure the Franchise MCP Server for use with Goose and Claude.'); // Prompt for access token const accessToken = await promptForAccessToken(); if (!accessToken) { console.warn('\nNo access token provided. Server will not be able to access API.'); console.warn('You can set DASHBOARD_ACCESS_TOKEN environment variable when starting the server manually.'); } // Prompt for sandbox mode const useSandbox = await promptForDevMode(); console.log(`\n${useSandbox ? 'šŸļø Using sandbox environment' : 'šŸŒŽ Using production environment'}`); // Update both Goose and Claude configurations await updateGooseConfig(accessToken, useSandbox); await updateClaudeConfig(accessToken, useSandbox); const gooseUrl = await generateGooseUrl(); console.log('\n✨ Installation complete! ✨'); console.log('\n🦢 To add this extension to Goose, copy and paste the following URL into your browser:'); console.log(`\n\x1b[1m${gooseUrl}\x1b[0m\n`); console.log('šŸ¤– The extension has been automatically configured for Claude if installed.'); }) .command('start', 'Start the Franchise MCP Server', {}, startCommand) .command('get-goose-url', 'Get Goose URL for the server', {}, async () => { console.log('\nšŸ” Getting Goose URL for Franchise MCP Server...'); const gooseUrl = await generateGooseUrl(); console.log('\n🦢 Goose URL for Franchise MCP Server:'); console.log(`\n\x1b[1m${gooseUrl}\x1b[0m\n`); }) .command('$0', 'Default command - starts the server', {}, startCommand) .help().argv; } main().catch((error) => { console.error('Error:', error); process.exit(1); });