import express from 'express';
import open from 'open';
import dotenv from 'dotenv';
import axios from 'axios';
import { tokenManager } from './token-manager.js';
dotenv.config();
const app = express();
const port = 8888;
// Comprehensive scopes for full Spotify control
const SCOPES = [
'user-read-playback-state',
'user-modify-playback-state',
'user-read-currently-playing',
'user-read-playback-position',
'user-top-read',
'user-read-recently-played',
'user-library-read',
'user-library-modify',
'playlist-read-private',
'playlist-read-collaborative',
'playlist-modify-public',
'playlist-modify-private',
].join(' ');
app.get('/login', (req, res) => {
const params = new URLSearchParams({
response_type: 'code',
client_id: process.env.SPOTIFY_CLIENT_ID!,
scope: SCOPES,
redirect_uri: process.env.SPOTIFY_REDIRECT_URI!,
});
res.redirect(`https://accounts.spotify.com/authorize?${params.toString()}`);
});
app.get('/callback', async (req, res) => {
const code = req.query.code as string;
if (!code) {
res.send('❌ No code provided - authorization failed.');
return;
}
try {
const params = new URLSearchParams();
params.append('code', code);
params.append('redirect_uri', process.env.SPOTIFY_REDIRECT_URI!);
params.append('grant_type', 'authorization_code');
const authHeader = Buffer.from(
`${process.env.SPOTIFY_CLIENT_ID}:${process.env.SPOTIFY_CLIENT_SECRET}`
).toString('base64');
const response = await axios.post('https://accounts.spotify.com/api/token', params, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `Basic ${authHeader}`
}
});
await tokenManager.saveTokens(response.data);
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>Spotify MCP - Authentication Successful</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background: linear-gradient(135deg, #1DB954 0%, #191414 100%);
}
.container {
background: white;
padding: 3rem;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
text-align: center;
max-width: 500px;
}
h1 { color: #1DB954; margin-top: 0; }
p { color: #333; line-height: 1.6; }
.checkmark { font-size: 4rem; color: #1DB954; }
</style>
</head>
<body>
<div class="container">
<div class="checkmark">✓</div>
<h1>Authentication Successful!</h1>
<p>Your Spotify account has been connected to the MCP server.</p>
<p>You can now close this window and start using the server.</p>
</div>
</body>
</html>
`);
// Give the response time to render before exiting
setTimeout(() => {
console.info('\n✓ Authentication successful! Tokens saved to spotify-tokens.json');
console.info('You can now start the MCP server with: npm start');
process.exit(0);
}, 1000);
} catch (error) {
console.error('Authentication error:', error);
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>Spotify MCP - Authentication Failed</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background: linear-gradient(135deg, #e74c3c 0%, #191414 100%);
}
.container {
background: white;
padding: 3rem;
border-radius: 12px;
box-shadow: 0 10px 40px rgba(0,0,0,0.3);
text-align: center;
max-width: 500px;
}
h1 { color: #e74c3c; margin-top: 0; }
p { color: #333; line-height: 1.6; }
.errormark { font-size: 4rem; color: #e74c3c; }
</style>
</head>
<body>
<div class="container">
<div class="errormark">✗</div>
<h1>Authentication Failed</h1>
<p>There was an error connecting to Spotify. Please check the console for details.</p>
</div>
</body>
</html>
`);
}
});
app.listen(port, async () => {
console.info(`
╔════════════════════════════════════════════════════════════╗
║ Spotify MCP Server - Authentication Setup ║
╚════════════════════════════════════════════════════════════╝
🌐 Auth server running on http://localhost:${port}
🔐 Opening browser for Spotify authentication...
Follow the prompts in your browser to authorize the application.
`);
try {
await open(`http://localhost:${port}/login`);
} catch (error) {
console.error('Could not open browser automatically. Please navigate to:');
console.error(`http://localhost:${port}/login`);
}
});