plaier-mcp-server
Version:
MCP Server for Plaier Football API - Advanced football analytics and predictions
373 lines (370 loc) • 18.1 kB
JavaScript
// ==================== 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