plaier-mcp-server
Version:
MCP Server for Plaier Football API - Advanced football analytics and predictions
254 lines ⢠14.3 kB
JavaScript
import { validateInput, TournamentSearchSchema, TournamentFixturesSchema, TournamentPredictionSchema } from '../utils/validation.js';
import { formatTournamentsList, formatTournamentFixtures, formatTournamentPrediction, formatError, formatWarning, formatDataSummary } from '../utils/formatting.js';
export function createTournamentTools(client) {
return [
{
name: "get_tournaments",
description: "Get a list of all tournaments available in the Plaier API with their details and fair market values.",
inputSchema: {
type: "object",
properties: {
country_filter: {
type: "string",
description: "Optional filter to show only tournaments from a specific country"
}
}
}
},
{
name: "search_tournaments",
description: "Search for tournaments by name using fuzzy search. Returns the tournament ID that can be used with other tournament tools.",
inputSchema: {
type: "object",
properties: {
tournament_name: {
type: "string",
description: "Tournament name to search for (minimum 3 characters). Supports partial matches."
}
},
required: ["tournament_name"]
}
},
{
name: "get_tournament_fixtures",
description: "Get all matches and results for a specific tournament, including scheduled fixtures and completed matches with scores.",
inputSchema: {
type: "object",
properties: {
tournament_id: {
type: "number",
description: "The unique ID of the tournament"
},
limit: {
type: "number",
description: "Maximum number of fixtures to display (default 30)",
default: 30
},
show_only_completed: {
type: "boolean",
description: "Show only completed matches with results",
default: false
}
},
required: ["tournament_id"]
}
},
{
name: "predict_tournament",
description: "Get end-of-season table prediction for a tournament showing expected final standings with points, goals, and goal difference.",
inputSchema: {
type: "object",
properties: {
tournament_id: {
type: "number",
description: "The unique ID of the tournament"
}
},
required: ["tournament_id"]
}
},
{
name: "get_tournament_probabilities",
description: "Get probability matrix showing the likelihood of each team finishing in each position at the end of the season.",
inputSchema: {
type: "object",
properties: {
tournament_id: {
type: "number",
description: "The unique ID of the tournament"
}
},
required: ["tournament_id"]
}
}
];
}
export async function handleTournamentTool(toolName, args, client) {
try {
switch (toolName) {
case "get_tournaments": {
try {
const tournaments = await client.getTournaments();
if (tournaments.length === 0) {
return formatWarning('No tournaments found in the API.');
}
// Filter by country if specified
let filteredTournaments = tournaments;
if (args.country_filter) {
const countryFilter = args.country_filter.toLowerCase();
filteredTournaments = tournaments.filter(t => t.tournament_country.toLowerCase().includes(countryFilter));
if (filteredTournaments.length === 0) {
return formatError(`No tournaments found for country filter "${args.country_filter}".`);
}
}
// Sort by fair market value (highest first) and then by tournament level
const sortedTournaments = filteredTournaments.sort((a, b) => {
if (b.fair_market_value !== a.fair_market_value) {
return b.fair_market_value - a.fair_market_value;
}
return a.tournament_level - b.tournament_level;
});
let result = formatTournamentsList(sortedTournaments);
if (args.country_filter) {
result = result.replace('Available Tournaments', `Tournaments in "${args.country_filter}"`);
}
result += '\nš” **Tip:** Use search_tournaments to find a specific tournament, or use the tournament ID with other tournament tools.';
return result;
}
catch (error) {
return formatError(`Error retrieving tournaments: ${error}`);
}
}
case "search_tournaments": {
const params = validateInput(TournamentSearchSchema, args);
try {
const tournamentId = await client.searchTournament(params.tournament_name);
if (tournamentId === "0") {
return formatError(`No tournament found matching "${params.tournament_name}". Try using broader search terms.`);
}
return `Found tournament ID: ${tournamentId}\n\nš” **Tip:** Use this ID with other tournament tools like get_tournament_fixtures or predict_tournament.`;
}
catch (error) {
return formatError(`No tournament found matching "${params.tournament_name}". The search requires at least 3 characters and uses fuzzy matching.`);
}
}
case "get_tournament_fixtures": {
const params = validateInput(TournamentFixturesSchema, args);
try {
const fixtures = await client.getTournamentFixtures(params.tournament_id);
if (fixtures.length === 0) {
return formatWarning(`No fixtures found for tournament ID ${params.tournament_id}. This might be a tournament without fixture data.`);
}
// Filter fixtures if requested
let filteredFixtures = fixtures;
if (args.show_only_completed) {
filteredFixtures = fixtures.filter(f => f.team1_goals !== undefined && f.team2_goals !== undefined);
if (filteredFixtures.length === 0) {
return formatWarning(`No completed matches found for tournament ID ${params.tournament_id}.`);
}
}
// Sort by date (most recent first for completed, upcoming first for scheduled)
const sortedFixtures = filteredFixtures.sort((a, b) => {
const dateA = new Date(a.match_date).getTime();
const dateB = new Date(b.match_date).getTime();
return dateB - dateA;
});
const displayLimit = args.limit || 30;
let result = formatTournamentFixtures(sortedFixtures, displayLimit);
// Add summary information
const completedMatches = fixtures.filter(f => f.team1_goals !== undefined && f.team2_goals !== undefined).length;
const upcomingMatches = fixtures.length - completedMatches;
result += `\n${formatDataSummary('Tournament Fixtures', displayLimit, filteredFixtures.length)}`;
result += `\n**Match Status:** ${completedMatches} completed, ${upcomingMatches} scheduled`;
if (args.show_only_completed) {
result += '\n**Filter:** Showing only completed matches';
}
result += '\n\nš” **Tip:** Use predict_tournament to see expected end-of-season standings.';
return result;
}
catch (error) {
return formatError(`Error retrieving fixtures for tournament ID ${params.tournament_id}: ${error}`);
}
}
case "predict_tournament": {
const params = validateInput(TournamentPredictionSchema, args);
try {
const predictions = await client.getTournamentPrediction(params.tournament_id);
if (predictions.length === 0) {
return formatWarning(`No tournament prediction available for tournament ID ${params.tournament_id}. This might be a tournament type that doesn't support predictions.`);
}
// Sort by points (descending), then by goal difference
const sortedPredictions = predictions.sort((a, b) => {
if (b.points_per_season !== a.points_per_season) {
return b.points_per_season - a.points_per_season;
}
return b.goal_difference_per_season - a.goal_difference_per_season;
});
let result = formatTournamentPrediction(sortedPredictions);
// Add analysis
const topTeams = sortedPredictions.slice(0, 3);
const bottomTeams = sortedPredictions.slice(-3).reverse();
result += `\n\n**Key Insights:**`;
result += `\nš **Title Contenders:** ${topTeams.map(t => t.team_name).join(', ')}`;
result += `\nā ļø **Relegation Battle:** ${bottomTeams.map(t => t.team_name).join(', ')}`;
// Calculate points gaps
if (sortedPredictions.length > 1) {
const titleGap = sortedPredictions[0].points_per_season - sortedPredictions[1].points_per_season;
result += `\nš **Title Race:** ${titleGap.toFixed(1)} point gap predicted`;
}
result += `\n\nš” **Note:** Prediction combines actual results with simulated outcomes for remaining matches.`;
result += '\nš” **Tip:** Use get_tournament_probabilities for detailed position probability analysis.';
return result;
}
catch (error) {
return formatError(`Error retrieving tournament prediction for ID ${params.tournament_id}: ${error}`);
}
}
case "get_tournament_probabilities": {
const params = validateInput(TournamentPredictionSchema, args);
try {
const probabilities = await client.getTournamentProbabilities(params.tournament_id);
if (!probabilities) {
return formatWarning(`No probability data available for tournament ID ${params.tournament_id}. This feature may not be supported for this tournament type.`);
}
// Note: The exact structure of tournament probabilities response may vary
// This is a simplified implementation that handles the response gracefully
let result = `**Tournament Position Probabilities (Tournament ID: ${params.tournament_id}):**\n\n`;
if (Array.isArray(probabilities)) {
// If it's an array format
result += `š **Probability Matrix:** Data shows likelihood of each finishing position\n`;
result += `Total teams analyzed: ${probabilities.length}\n\n`;
probabilities.slice(0, 10).forEach((team, index) => {
result += `${index + 1}. **${team.team_name || `Team ${team.team_id}`}**\n`;
if (team.probabilities) {
const topPositions = Object.entries(team.probabilities)
.sort((a, b) => b[1] - a[1])
.slice(0, 3);
result += ` Most likely positions: ${topPositions.map(([pos, prob]) => `${pos}th (${(prob * 100).toFixed(1)}%)`).join(', ')}\n`;
}
result += '\n';
});
if (probabilities.length > 10) {
result += `... and ${probabilities.length - 10} more teams.\n`;
}
}
else {
// If it's an object or different format
result += `š **Probability Data:** ${JSON.stringify(probabilities, null, 2)}\n`;
}
result += '\nš” **Analysis:** Higher percentages indicate more likely final positions based on current form and remaining fixtures.';
return result;
}
catch (error) {
return formatError(`Error retrieving tournament probabilities for ID ${params.tournament_id}: ${error}`);
}
}
default:
throw new Error(`Unknown tournament tool: ${toolName}`);
}
}
catch (error) {
return formatError(`Tool execution failed: ${error}`);
}
}
//# sourceMappingURL=tournament-tools.js.map