UNPKG

plaier-mcp-server

Version:

MCP Server for Plaier Football API - Advanced football analytics and predictions

373 lines (370 loc) 18.1 kB
// ==================== TEAM FORMATTING ==================== export function formatTeamPerformance(performance, teamName, tournamentName, reportType) { let result = `**Team Performance Analysis for ${teamName} in ${tournamentName}:**\n\n`; // Determine what metrics to show based on report type let metricName = ''; switch (reportType) { case 'nominal-score': metricName = 'Nominal Score'; break; case 'effective-score': metricName = 'Effective Score'; break; case 'effective-score-vs-nominal-score': metricName = 'Effective vs Nominal Score'; break; case 'goal-difference-per-season': metricName = 'Goal Difference Per Season'; break; default: metricName = 'Performance Score'; } // Create the table header result += `| Team | ${metricName} |\n`; result += `|------|${'-'.repeat(metricName.length + 2)}|\n`; // Add data rows performance.forEach(team => { let metricValue = ''; if (reportType === 'nominal-score' && team.nominal_score !== undefined) { metricValue = team.nominal_score.toFixed(2); } else if (reportType === 'effective-score' && team.effective_score !== undefined) { metricValue = team.effective_score.toFixed(2); } else if (reportType === 'effective-score-vs-nominal-score') { const nominal = team.nominal_score !== undefined ? team.nominal_score.toFixed(2) : 'N/A'; const effective = team.effective_score !== undefined ? team.effective_score.toFixed(2) : 'N/A'; metricValue = `${effective} vs ${nominal}`; } else if (reportType === 'goal-difference-per-season' && team.goal_difference_per_season !== undefined) { metricValue = team.goal_difference_per_season.toFixed(2); } else { metricValue = 'N/A'; } result += `| ${team.team_name} | ${metricValue} |\n`; }); // Add explanation result += `\n**Report Type:** ${reportType}\n`; if (reportType === 'nominal-score') { result += `\n**Explanation:** Nominal Score represents the theoretical team strength based on player quality.`; } else if (reportType === 'effective-score') { result += `\n**Explanation:** Effective Score represents the actual team performance accounting for tactical effectiveness.`; } else if (reportType === 'effective-score-vs-nominal-score') { result += `\n**Explanation:** This comparison shows the difference between theoretical team strength and actual performance.`; result += `\n- Teams with higher effective than nominal scores are overperforming.`; result += `\n- Teams with lower effective than nominal scores are underperforming.`; } else if (reportType === 'goal-difference-per-season') { result += `\n**Explanation:** Goal Difference Per Season shows the average goal difference per match over a season.`; } return result; } export function formatTeamInfo(team) { const scoreInfo = team.team_score ? `\n- Team Score: ${team.team_score}` : ''; const effectiveScore = team.effective_team_score ? `\n- Effective Score: ${team.effective_team_score}` : ''; const nominalScore = team.nominal_team_score ? `\n- Nominal Score: ${team.nominal_team_score}` : ''; return `**${team.team_name}** (ID: ${team.team_id}) - Tournament: ${team.tournament_name} (${team.tournament_country}) - Fair Market Value: €${team.fair_market_value}M${scoreInfo}${effectiveScore}${nominalScore}`; } export function formatTeamsList(teams, title = "Teams", limit) { const teamsToShow = limit ? teams.slice(0, limit) : teams; let result = `**${title}:**\n\n`; teamsToShow.forEach((team, index) => { result += `${index + 1}. **${team.team_name}** (${team.team_id})\n`; result += ` - Score: ${team.nominal_score || 'N/A'} | Value: €${team.fair_market_value}M\n\n`; }); if (limit && teams.length > limit) { result += `... and ${teams.length - limit} more teams.\n`; } return result; } // ==================== PLAYER FORMATTING ==================== export function formatPlayerInfo(player) { const nationalTeam = player.national_team_name ? `\n- National Team: ${player.national_team_name}` : ''; return `**${player.player_name}** (ID: ${player.player_id}) - Team: ${player.team_name} (${player.tournament_name}) - Position: ${player.position} (${player.position_group}) - Nationality: ${player.nationality} - Age: ${player.age} - Score: ${player.score} - Fair Market Value: €${player.fair_market_value}M${nationalTeam}`; } export function formatPlayerRanking(players, limit = 10) { const topPlayers = players.slice(0, limit); let result = `**Top ${topPlayers.length} Players:**\n\n`; topPlayers.forEach(player => { result += `${player.rank}. **${player.player_name}** (${player.team_name})\n`; result += ` - Score: ${player.score} | Value: €${player.fair_market_value}M | Age: ${player.age}\n`; result += ` - Position: ${player.position} | Nationality: ${player.nationality}\n\n`; }); if (players.length > limit) { result += `... and ${players.length - limit} more players in the full ranking.\n`; } return result; } // ==================== TOURNAMENT FORMATTING ==================== export function formatTournamentInfo(tournament) { return `**${tournament.tournament_name}** (ID: ${tournament.tournament_id}) - Country: ${tournament.tournament_country} - Level: ${tournament.tournament_level} - Fair Market Value: €${tournament.fair_market_value}M`; } export function formatTournamentsList(tournaments) { let result = `**Available Tournaments (${tournaments.length} total):**\n\n`; // Group by country for better organization const byCountry = tournaments.reduce((acc, tournament) => { if (!acc[tournament.tournament_country]) { acc[tournament.tournament_country] = []; } acc[tournament.tournament_country].push(tournament); return acc; }, {}); Object.entries(byCountry).forEach(([country, countryTournaments]) => { result += `**${country}:**\n`; countryTournaments.forEach(tournament => { result += `- ${tournament.tournament_name} (ID: ${tournament.tournament_id}, Level: ${tournament.tournament_level})\n`; }); result += '\n'; }); return result; } export function formatTournamentFixtures(fixtures, limit = 20) { const fixturesToShow = fixtures.slice(0, limit); let result = `**Tournament Fixtures (showing ${fixturesToShow.length} of ${fixtures.length}):**\n\n`; fixturesToShow.forEach(fixture => { const matchDay = fixture.match_day ? `MD ${fixture.match_day} | ` : ''; const date = new Date(fixture.match_date).toLocaleDateString(); const time = new Date(fixture.match_date).toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', timeZone: 'UTC' }); if (fixture.team1_goals !== undefined && fixture.team2_goals !== undefined) { // Match has been played result += `${matchDay}${date} ${time} UTC | **${fixture.team1_name} ${fixture.team1_goals}-${fixture.team2_goals} ${fixture.team2_name}**\n`; } else { // Match is scheduled result += `${matchDay}${date} ${time} UTC | ${fixture.team1_name} vs ${fixture.team2_name}\n`; } }); if (fixtures.length > limit) { result += `\n... and ${fixtures.length - limit} more fixtures.\n`; } return result; } export function formatTournamentPrediction(predictions) { let result = `**End-of-Season Tournament Table Prediction:**\n\n`; result += `| Pos | Team | Pts | GD | GF | GA |\n`; result += `|-----|------|-----|----|----|----|\n`; predictions.forEach((team, index) => { const position = team.rank || (index + 1); result += `| ${position} | ${team.team_name} | ${team.points_per_season} | ${team.goal_difference_per_season > 0 ? '+' : ''}${team.goal_difference_per_season} | ${team.goals_per_season} | ${team.goals_against_per_season} |\n`; }); return result; } // ==================== MATCH FORMATTING ==================== export function formatMatchPrediction(prediction, homeTeam, awayTeam) { return `**Match Prediction: ${homeTeam} vs ${awayTeam}** **Predicted Score:** ${prediction.goals.toFixed(2)} - ${prediction.goals_against.toFixed(2)} **Goal Difference:** ${prediction.goal_difference > 0 ? '+' : ''}${prediction.goal_difference.toFixed(2)} (from home perspective) **Probabilities:** - Home Win: ${prediction.home_win_probability.toFixed(1)}% - Draw: ${prediction.draw_probability.toFixed(1)}% - Away Win: ${prediction.away_win_probability.toFixed(1)}% **Fair Betting Odds:** - Home Win: ${prediction.fair_odds_win.toFixed(2)} - Draw: ${prediction.fair_odds_draw.toFixed(2)} - Away Win: ${prediction.fair_odds_away.toFixed(2)}`; } export function formatExactResultPredictions(predictions, homeTeam, awayTeam) { let result = `**Exact Result Predictions: ${homeTeam} vs ${awayTeam}**\n\n`; result += `| Result | Probability | Fair Odds |\n`; result += `|--------|-------------|----------|\n`; predictions.forEach(prediction => { result += `| ${prediction.result} | ${prediction.probability.toFixed(1)}% | ${prediction.odds.toFixed(2)} |\n`; }); return result; } export function formatBestLineups(lineups, teamName, opponentName, limit = 5) { const lineupsToShow = lineups.slice(0, limit); let result = `**Best ${lineupsToShow.length} Lineups for ${teamName} vs ${opponentName}:**\n\n`; lineupsToShow.forEach((lineup, index) => { result += `**${index + 1}. Formation: ${lineup.formation}** (Goal Difference: ${lineup.goal_difference > 0 ? '+' : ''}${lineup.goal_difference.toFixed(2)})\n`; result += `Players: ${lineup.lineup.map(p => p.player_name).join(', ')}\n\n`; }); return result; } export function formatBestFormations(formations, teamName, opponentName) { let result = `**Best Formations for ${teamName} vs ${opponentName}:**\n\n`; result += `| Formation | Goal Difference |\n`; result += `|-----------|----------------|\n`; formations.forEach(formation => { const gdFormatted = formation.best_goal_difference > 0 ? `+${formation.best_goal_difference.toFixed(2)}` : formation.best_goal_difference.toFixed(2); result += `| ${formation.formation} | ${gdFormatted} |\n`; }); return result; } export function formatLineupCount(counts, teamName, limit = 15) { const countsToShow = counts.slice(0, limit); let result = `**Player Appearances in Best Lineups for ${teamName}:**\n\n`; // Check if we have the new API format with position information const hasPositions = counts.some(count => count.position); if (hasPositions) { result += `| Player | Position | Appearances (out of 1000) |\n`; result += `|--------|----------|---------------------------|\n`; } else { result += `| Player | Appearances (out of 1000) |\n`; result += `|--------|---------------------------|\n`; } countsToShow.forEach(count => { // Handle possible API response variations const playerName = count.player_name || `Player ${count.player_id}`; const position = count.position || 'Unknown'; // Support both new (appearances) and old (count) API response formats const appearanceCount = count.appearances !== undefined ? count.appearances : count.count !== undefined ? count.count : 0; // Support both new (appearance_percentage) and calculated percentage const percentage = count.appearance_percentage !== undefined ? count.appearance_percentage.toFixed(1) : (appearanceCount > 0 ? ((appearanceCount / 1000) * 100).toFixed(1) : '0.0'); if (hasPositions) { result += `| ${playerName} | ${position} | ${appearanceCount} (${percentage}%) |\n`; } else { result += `| ${playerName} | ${appearanceCount} (${percentage}%) |\n`; } }); if (counts.length > limit) { result += `\n... and ${counts.length - limit} more players.\n`; } return result; } // ==================== TRANSFER FORMATTING ==================== export function formatTransferImpact(impacts, playerName) { let result = `**Transfer Impact Analysis for ${playerName}:**\n\n`; result += `| Team | Goals | Goals Against | Goal Difference | Points |\n`; result += `|------|-------|---------------|-----------------|--------|\n`; impacts.forEach(impact => { const goalsChange = impact.goals_change > 0 ? `+${impact.goals_change.toFixed(2)}` : impact.goals_change.toFixed(2); const gaChange = impact.goals_against_change > 0 ? `+${impact.goals_against_change.toFixed(2)}` : impact.goals_against_change.toFixed(2); const gdChange = impact.gd_change > 0 ? `+${impact.gd_change.toFixed(2)}` : impact.gd_change.toFixed(2); const pointsChange = impact.points_change > 0 ? `+${impact.points_change.toFixed(2)}` : impact.points_change.toFixed(2); result += `| ${impact.team || impact.team_name} | ${goalsChange} | ${gaChange} | ${gdChange} | ${pointsChange} |\n`; }); return result; } // ==================== ADVANCED ANALYSIS FORMATTING ==================== export function formatMostImportantPlayers(players, teamName, limit = 10) { const playersToShow = players.slice(0, limit); let result = `**Most Important Players for ${teamName}:**\n\n`; result += `| Player | Goals Impact | Goals Against Impact | Goal Diff Impact | Points Impact |\n`; result += `|--------|--------------|---------------------|-------------------|---------------|\n`; playersToShow.forEach(player => { const goalsImpact = player.goals_change.toFixed(2); const gaImpact = player.goals_against_change.toFixed(2); const gdImpact = player.gd_change.toFixed(2); const pointsImpact = player.points_change.toFixed(2); result += `| ${player.player} | ${goalsImpact} | ${gaImpact} | ${gdImpact} | ${pointsImpact} |\n`; }); return result; } export function formatTeamImprovement(improvements, teamName) { let result = `**Team Improvement Matrix for ${teamName}:**\n\n`; // Create header row with score ranges const scoreRanges = [4500, 4750, 5000, 5250, 5500, 5750, 6000]; result += `| Position | ${scoreRanges.map(score => score.toString()).join(' | ')} |\n`; // Create separator row result += `|----------|${scoreRanges.map(() => '------').join('|')}|\n`; // Group improvements by position for cleaner display const positionGroups = {}; improvements.forEach(improvement => { if (!positionGroups[improvement.position_group]) { positionGroups[improvement.position_group] = []; } positionGroups[improvement.position_group].push(improvement); }); // Display improvement values by position and score range Object.entries(positionGroups).forEach(([groupName, positions]) => { // Add position group header result += `| **${groupName}** | | | | | | | |\n`; // Add each position in the group positions.forEach(improvement => { const values = scoreRanges.map(score => { const value = improvement[score]; if (typeof value === 'number') { return value > 0 ? `+${value.toFixed(2)}` : value.toFixed(2); } return 'N/A'; }); result += `| ${improvement.position} | ${values.join(' | ')} |\n`; }); }); // Add explanation result += `\n**Explanation:** This matrix shows the expected goal difference improvement per season if a player with the specified position and score is added to the team. Higher values indicate greater potential improvement.\n`; return result; } // ==================== UTILITY FUNCTIONS ==================== export function formatError(error) { const message = typeof error === 'string' ? error : error.message; return `❌ **Error:** ${message}`; } export function formatWarning(message) { return `⚠️ **Warning:** ${message}`; } export function formatInfo(message) { return `ℹ️ **Info:** ${message}`; } export function formatDataSummary(dataType, count, total) { if (total !== undefined) { return `📊 **${dataType}:** Showing ${count} of ${total} items`; } else { return `📊 **${dataType}:** ${count} items found`; } } export function formatCurrency(amount, currency = '€', suffix = 'M') { return `${currency}${amount}${suffix}`; } export function formatPercentage(value, decimals = 1) { return `${value.toFixed(decimals)}%`; } export function formatDateTime(dateString, includeTime = true) { const date = new Date(dateString); if (includeTime) { return date.toLocaleString('en-GB', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', timeZone: 'UTC' }) + ' UTC'; } else { return date.toLocaleDateString('en-GB'); } } export function truncateText(text, maxLength, suffix = '...') { if (text.length <= maxLength) { return text; } return text.substring(0, maxLength - suffix.length) + suffix; } export function formatListWithLimit(items, formatter, limit = 10, title) { const itemsToShow = items.slice(0, limit); let result = title ? `**${title}:**\n\n` : ''; itemsToShow.forEach((item, index) => { result += formatter(item, index) + '\n'; }); if (items.length > limit) { result += `\n... and ${items.length - limit} more items.\n`; } return result; } //# sourceMappingURL=formatting.js.map