Skip to main content
Glama

optimize_lineup

Generate optimal fantasy football lineups using Sleeper projections to maximize weekly scoring potential based on league settings and player matchups.

Instructions

Suggest optimal lineup based on projections

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
leagueNoLeague name (ROAD_TO_GLORY or DYNASTY), defaults to configured default
weekNoWeek number (defaults to current week)

Implementation Reference

  • Defines the tool's name, description, and input schema for league and week parameters.
    name = "optimize_lineup"; description = "Suggest optimal lineup based on projections"; inputSchema = { type: "object", properties: { league: { type: "string", description: "League name (ROAD_TO_GLORY or DYNASTY), defaults to configured default", enum: ["ROAD_TO_GLORY", "DYNASTY"] }, week: { type: "number", description: "Week number (defaults to current week)", minimum: 1, maximum: 18 } } };
  • The execute method that handles the tool invocation, fetches necessary data from Sleeper API, computes player projections, optimizes the lineup using helper methods, and returns structured results with current vs optimized lineup analysis.
    async execute(args: any) { const leagueConfig = getLeagueConfig(args.league); if (!leagueConfig) { throw new Error(`League configuration not found for: ${args.league}`); } const week = args.week || this.getCurrentWeek(); const season = new Date().getFullYear().toString(); try { // Fetch league settings, roster, and player data const [leagueResponse, rostersResponse, usersResponse, playersResponse] = await Promise.all([ fetch(`${config.api.baseUrl}/league/${leagueConfig.id}`), fetch(`${config.api.baseUrl}/league/${leagueConfig.id}/rosters`), fetch(`${config.api.baseUrl}/league/${leagueConfig.id}/users`), fetch(`${config.api.baseUrl}/players/nfl`) ]); if (!leagueResponse.ok || !rostersResponse.ok || !usersResponse.ok || !playersResponse.ok) { throw new Error('Failed to fetch lineup optimization data'); } const league = await leagueResponse.json(); const rosters = await rostersResponse.json(); const users = await usersResponse.json(); const players = await playersResponse.json(); const userMap = new Map(users.map((user: any) => [user.user_id, user])); // Find user's roster const myRoster = rosters.find((roster: any) => { const user: any = userMap.get(roster.owner_id); return user?.display_name === config.username || user?.username === config.username || user?.display_name === leagueConfig.teamName || user?.username === leagueConfig.teamName; }); if (!myRoster) { throw new Error(`Could not find roster for user: ${config.username}`); } // Get league roster positions (lineup requirements) const rosterPositions = league.roster_positions; // Fetch individual projections for roster players const projectionPromises = myRoster.players.map(async (playerId: string) => { try { const projectionResponse = await fetch( `https://api.sleeper.app/projections/nfl/player/${playerId}?season=${season}&season_type=regular&week=${week}` ); if (projectionResponse.ok) { const data = await projectionResponse.json(); return { playerId, projectedPoints: data.stats?.pts_ppr || 0 }; } return { playerId, projectedPoints: 0 }; } catch (error) { console.warn(`Failed to fetch projection for player ${playerId}:`, error); return { playerId, projectedPoints: 0 }; } }); const projectionResults = await Promise.all(projectionPromises); const playerProjections = new Map( projectionResults.map(r => [r.playerId, r.projectedPoints]) ); // Create player pool with projections const playerPool = myRoster.players.map((playerId: string) => { const player = players[playerId]; if (!player) return null; const projectedPoints = playerProjections.get(playerId) || 0; return { playerId, name: `${player.first_name} ${player.last_name}`, position: player.position, team: player.team, status: player.status, projectedPoints: Number(projectedPoints.toFixed(2)), eligiblePositions: player.fantasy_positions || [player.position] }; }).filter(Boolean).filter((p: any) => p.status === 'Active'); // Sort players by projected points within each position const playersByPosition = this.groupPlayersByPosition(playerPool); // Optimize lineup based on roster positions const optimizedLineup = this.optimizeLineup(playersByPosition, rosterPositions); const currentStarters = myRoster.starters; // Calculate current vs optimal projections const currentProjection = currentStarters.reduce((sum: number, playerId: string) => { return sum + (playerProjections.get(playerId) || 0); }, 0); const optimalProjection = optimizedLineup.reduce((sum: number, player: any) => sum + player.projectedPoints, 0); // Find suggested changes const changes = this.findLineupChanges(currentStarters, optimizedLineup, players); const result = { week, season, league: args.league || config.defaultLeague, currentLineup: { starters: currentStarters.map((playerId: string) => { const player = players[playerId]; const projectedPoints = playerProjections.get(playerId) || 0; return { playerId, name: player ? `${player.first_name} ${player.last_name}` : 'Unknown', position: player?.position || 'UNK', team: player?.team || 'UNK', projectedPoints: Number(projectedPoints.toFixed(2)) }; }), totalProjected: currentProjection.toFixed(1) }, optimizedLineup: { starters: optimizedLineup, totalProjected: optimalProjection.toFixed(1) }, analysis: { projectedImprovement: (optimalProjection - currentProjection).toFixed(1), isOptimal: Math.abs(optimalProjection - currentProjection) < 0.1, suggestedChanges: changes, rosterPositions: rosterPositions } }; return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { throw new Error(`Failed to optimize lineup: ${error instanceof Error ? error.message : String(error)}`); } }
  • src/index.ts:82-83 (registration)
    Registers the tool handler in the MCP server's CallToolRequestSchema switch statement, routing calls to the LineupOptimizerTool's execute method.
    case "optimize_lineup": return await lineupOptimizerTool.execute(args);
  • src/index.ts:49-63 (registration)
    Registers the LineupOptimizerTool instance in the server's list of available tools for ListToolsRequestSchema.
    tools: [ leagueTool, rosterTool, matchupTool, playerTool, projectionsTool, matchupProjectionsTool, lineupOptimizerTool, trendingTool, historicalScoresTool, playerNewsTool, transactionsTool, stateScheduleTool, ], }));
  • Helper method implementing the lineup optimization logic: selects highest-projected available players for each roster position, handling FLEX and SUPER_FLEX specially, ensuring no duplicates.
    private optimizeLineup(playersByPosition: { [key: string]: any[] }, rosterPositions: string[]) { const lineup: any[] = []; const usedPlayers = new Set<string>(); // Fill specific positions first rosterPositions.forEach(position => { if (position === 'BN') return; // Skip bench slots let bestPlayer: any = null; if (position === 'FLEX') { // For FLEX, consider RB, WR, TE const flexEligible = ['RB', 'WR', 'TE']; flexEligible.forEach(pos => { if (playersByPosition[pos]) { playersByPosition[pos].forEach(player => { if (!usedPlayers.has(player.playerId) && (!bestPlayer || player.projectedPoints > bestPlayer.projectedPoints)) { bestPlayer = player; } }); } }); } else if (position === 'SUPER_FLEX') { // For SUPER_FLEX, consider all positions Object.keys(playersByPosition).forEach(pos => { playersByPosition[pos].forEach(player => { if (!usedPlayers.has(player.playerId) && (!bestPlayer || player.projectedPoints > bestPlayer.projectedPoints)) { bestPlayer = player; } }); }); } else { // Regular position if (playersByPosition[position]) { bestPlayer = playersByPosition[position].find(player => !usedPlayers.has(player.playerId) ); } } if (bestPlayer) { lineup.push({ ...bestPlayer, lineupPosition: position }); usedPlayers.add(bestPlayer.playerId); } }); return lineup; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/justfeltlikerunning/sleeper-fantasy-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server