@goparrot/franchise-mcp-server
Version:
MCP Server for Franchise API
221 lines (220 loc) ⢠9.21 kB
JavaScript
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);
});