index.ts•9.3 kB
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { SpotifyAuth } from './auth/spotify-auth.js';
import { SpotifyTools } from './tools/spotify-tools.js';
import { SpotifyConfig } from './types/index.js';
import dotenv from 'dotenv';
import open from 'open';
// Carregar variáveis de ambiente
dotenv.config();
const config: SpotifyConfig = {
clientId: process.env.SPOTIFY_CLIENT_ID || '',
clientSecret: process.env.SPOTIFY_CLIENT_SECRET || '',
redirectUri: process.env.SPOTIFY_REDIRECT_URI || 'http://localhost:3000/callback',
};
if (!config.clientId || !config.clientSecret) {
console.error('❌ SPOTIFY_CLIENT_ID e SPOTIFY_CLIENT_SECRET são obrigatórios');
console.error('Crie um arquivo .env baseado no env.example');
process.exit(1);
}
const spotifyAuth = new SpotifyAuth(config);
const spotifyTools = new SpotifyTools(spotifyAuth);
// Criar servidor MCP (SDK recente usa um único objeto de config)
const server = new Server({
name: 'spotify-mcp-server',
version: '1.0.0',
capabilities: {
tools: {},
},
});
// Listar ferramentas disponíveis
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'spotify_auth',
description: 'Inicia o processo de autenticação com o Spotify',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'spotify_set_tokens',
description: 'Conclui a autenticação com o código recebido do Spotify',
inputSchema: {
type: 'object',
properties: {
code: {
type: 'string',
description: 'Código de autorização retornado pelo Spotify após login',
},
},
required: ['code'],
},
},
{
name: 'spotify_search',
description: 'Busca por músicas, artistas, álbuns ou playlists no Spotify',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'Termo de busca (nome da música, artista, etc.)',
},
type: {
type: 'string',
enum: ['track', 'artist', 'album', 'playlist'],
description: 'Tipo de conteúdo para buscar',
},
limit: {
type: 'number',
description: 'Número máximo de resultados (padrão: 20)',
},
},
required: ['query', 'type'],
},
},
{
name: 'spotify_play',
description: 'Toca uma música específica no Spotify',
inputSchema: {
type: 'object',
properties: {
track_id: {
type: 'string',
description: 'ID da música no Spotify',
},
device_id: {
type: 'string',
description: 'ID do dispositivo (opcional)',
},
},
required: ['track_id'],
},
},
{
name: 'spotify_pause',
description: 'Pausa a reprodução atual',
inputSchema: {
type: 'object',
properties: {
device_id: {
type: 'string',
description: 'ID do dispositivo (opcional)',
},
},
},
},
{
name: 'spotify_resume',
description: 'Retoma a reprodução pausada',
inputSchema: {
type: 'object',
properties: {
device_id: {
type: 'string',
description: 'ID do dispositivo (opcional)',
},
},
},
},
{
name: 'spotify_next',
description: 'Pula para a próxima música',
inputSchema: {
type: 'object',
properties: {
device_id: {
type: 'string',
description: 'ID do dispositivo (opcional)',
},
},
},
},
{
name: 'spotify_previous',
description: 'Volta para a música anterior',
inputSchema: {
type: 'object',
properties: {
device_id: {
type: 'string',
description: 'ID do dispositivo (opcional)',
},
},
},
},
{
name: 'spotify_current_playing',
description: 'Obtém informações sobre a música que está tocando',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'spotify_devices',
description: 'Lista dispositivos disponíveis para reprodução',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'spotify_playlists',
description: 'Lista playlists do usuário',
inputSchema: {
type: 'object',
properties: {
limit: {
type: 'number',
description: 'Número máximo de playlists (padrão: 20)',
},
},
},
},
{
name: 'spotify_play_playlist',
description: 'Toca uma playlist específica',
inputSchema: {
type: 'object',
properties: {
playlist_id: {
type: 'string',
description: 'ID da playlist no Spotify',
},
device_id: {
type: 'string',
description: 'ID do dispositivo (opcional)',
},
},
required: ['playlist_id'],
},
},
{
name: 'spotify_create_playlist',
description: 'Cria uma nova playlist no Spotify',
inputSchema: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Nome da playlist',
},
description: {
type: 'string',
description: 'Descrição da playlist (opcional)',
},
public: {
type: 'boolean',
description: 'Se a playlist deve ser pública (padrão: false)',
},
},
required: ['name'],
},
},
{
name: 'spotify_add_tracks_to_playlist',
description: 'Adiciona músicas a uma playlist existente',
inputSchema: {
type: 'object',
properties: {
playlist_id: {
type: 'string',
description: 'ID da playlist no Spotify',
},
track_ids: {
type: 'array',
items: {
type: 'string',
},
description: 'Array com os IDs das músicas para adicionar',
},
},
required: ['playlist_id', 'track_ids'],
},
},
],
};
});
// Processar chamadas de ferramentas
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name } = request.params;
const args: any = (request.params.arguments ?? {});
try {
switch (name) {
case 'spotify_auth':
return await spotifyTools.authenticate();
case 'spotify_set_tokens':
return await spotifyTools.setTokens(args.code);
case 'spotify_search':
return await spotifyTools.search(args.query, args.type, args.limit);
case 'spotify_play':
return await spotifyTools.play(args.track_id, args.device_id);
case 'spotify_pause':
return await spotifyTools.pause(args.device_id);
case 'spotify_resume':
return await spotifyTools.resume(args.device_id);
case 'spotify_next':
return await spotifyTools.next(args.device_id);
case 'spotify_previous':
return await spotifyTools.previous(args.device_id);
case 'spotify_current_playing':
return await spotifyTools.getCurrentPlaying();
case 'spotify_devices':
return await spotifyTools.getDevices();
case 'spotify_playlists':
return await spotifyTools.getPlaylists(args.limit);
case 'spotify_play_playlist':
return await spotifyTools.playPlaylist(args.playlist_id, args.device_id);
case 'spotify_create_playlist':
return await spotifyTools.createPlaylist(args.name, args.description, args.public);
case 'spotify_add_tracks_to_playlist':
return await spotifyTools.addTracksToPlaylist(args.playlist_id, args.track_ids);
default:
throw new Error(`Ferramenta desconhecida: ${name}`);
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `Erro ao executar ${name}: ${error instanceof Error ? error.message : String(error)}`,
},
],
};
}
});
// Iniciar servidor
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('🎵 Servidor MCP Spotify iniciado!');
}
main().catch((error) => {
console.error('Erro fatal:', error);
process.exit(1);
});