Skip to main content
Glama

Sleeper Fantasy MCP

TrendingTool.ts•7.8 kB
import { config, getLeagueConfig } from "../config.js"; export class TrendingTool { name = "get_trending_players"; description = "Get trending players with add/drop activity and analysis"; inputSchema = { type: "object", properties: { league: { type: "string", description: "League name (ROAD_TO_GLORY or DYNASTY), defaults to configured default", enum: ["ROAD_TO_GLORY", "DYNASTY"] }, type: { type: "string", description: "Type of trending activity (add, drop, both)", enum: ["add", "drop", "both"], default: "both" }, lookbackHours: { type: "number", description: "Hours to look back for trending activity (default: 24)", minimum: 1, maximum: 168, default: 24 }, limit: { type: "number", description: "Maximum number of players to return (default: 20)", minimum: 1, maximum: 50, default: 20 }, position: { type: "string", description: "Filter by position (QB, RB, WR, TE, K, DEF)", enum: ["QB", "RB", "WR", "TE", "K", "DEF"] }, availableOnly: { type: "boolean", description: "Only show players available in your league (default: true)", default: true } } }; async execute(args: any) { const leagueConfig = getLeagueConfig(args.league); if (!leagueConfig) { throw new Error(`League configuration not found for: ${args.league}`); } const type = args.type || 'both'; const lookbackHours = args.lookbackHours || 24; const limit = args.limit || 20; const position = args.position; const availableOnly = args.availableOnly !== false; try { const fetchPromises = [ fetch(`${config.api.baseUrl}/players/nfl`) ]; // Fetch trending data based on type if (type === 'add' || type === 'both') { fetchPromises.push( fetch(`${config.api.baseUrl}/players/nfl/trending/add?lookback_hours=${lookbackHours}&limit=100`) ); } if (type === 'drop' || type === 'both') { fetchPromises.push( fetch(`${config.api.baseUrl}/players/nfl/trending/drop?lookback_hours=${lookbackHours}&limit=100`) ); } // If availableOnly is true, fetch rosters to filter out owned players if (availableOnly) { fetchPromises.push( fetch(`${config.api.baseUrl}/league/${leagueConfig.id}/rosters`) ); } const responses = await Promise.all(fetchPromises); if (responses.some(r => !r.ok)) { throw new Error('Failed to fetch trending player data'); } const players = await responses[0].json(); let trendingAdd: any[] = []; let trendingDrop: any[] = []; let rosters: any[] = []; let responseIndex = 1; if (type === 'add' || type === 'both') { trendingAdd = await responses[responseIndex].json(); responseIndex++; } if (type === 'drop' || type === 'both') { trendingDrop = await responses[responseIndex].json(); responseIndex++; } if (availableOnly) { rosters = await responses[responseIndex].json(); } // Create set of owned players if filtering by availability const ownedPlayerIds = availableOnly ? new Set(rosters.flatMap((roster: any) => roster.players || [])) : new Set(); // Combine trending data const trendingPlayerMap = new Map(); trendingAdd.forEach((trend: any) => { trendingPlayerMap.set(trend.player_id, { playerId: trend.player_id, addCount: trend.count, dropCount: 0, netActivity: trend.count, trend: 'Adding' }); }); trendingDrop.forEach((trend: any) => { const existing = trendingPlayerMap.get(trend.player_id); if (existing) { existing.dropCount = trend.count; existing.netActivity = existing.addCount - trend.count; existing.trend = existing.addCount > trend.count ? 'Net Adding' : 'Net Dropping'; } else { trendingPlayerMap.set(trend.player_id, { playerId: trend.player_id, addCount: 0, dropCount: trend.count, netActivity: -trend.count, trend: 'Dropping' }); } }); // Filter and enhance with player details const trendingPlayers = Array.from(trendingPlayerMap.values()) .map((trending: any) => { const player = players[trending.playerId]; if (!player) return null; // Apply filters if (position && player.position !== position) return null; if (availableOnly && ownedPlayerIds.has(trending.playerId)) return null; if (player.status === 'Inactive' || player.status === 'PUP') return null; return { playerId: trending.playerId, name: `${player.first_name} ${player.last_name}`, position: player.position, team: player.team, status: player.status, age: player.age, addCount: trending.addCount, dropCount: trending.dropCount, netActivity: trending.netActivity, trend: trending.trend, activityScore: Math.abs(trending.netActivity), isAvailable: !ownedPlayerIds.has(trending.playerId) }; }) .filter(Boolean) .sort((a: any, b: any) => { // Sort by activity score (absolute value of net activity) descending if (b.activityScore !== a.activityScore) { return b.activityScore - a.activityScore; } // Then by net activity (positive is better) return b.netActivity - a.netActivity; }) .slice(0, limit); const result = { league: args.league || config.defaultLeague, filters: { type, lookbackHours, position: position || 'ALL', availableOnly }, timeframe: `Last ${lookbackHours} hours`, totalTrending: trendingPlayers.length, players: trendingPlayers, summary: { byTrend: { adding: trendingPlayers.filter((p: any) => p?.trend?.includes('Adding')).length, dropping: trendingPlayers.filter((p: any) => p?.trend?.includes('Dropping')).length }, byPosition: this.getPositionSummary(trendingPlayers), topAdds: trendingPlayers .filter((p: any) => p?.addCount > 0) .slice(0, 5) .map((p: any) => ({ name: p?.name, position: p?.position, adds: p?.addCount })), topDrops: trendingPlayers .filter((p: any) => p?.dropCount > 0) .slice(0, 5) .map((p: any) => ({ name: p?.name, position: p?.position, drops: p?.dropCount })) } }; return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { throw new Error(`Failed to get trending players: ${error instanceof Error ? error.message : String(error)}`); } } private getPositionSummary(players: any[]): Record<string, number> { return players.reduce((summary, player) => { summary[player.position] = (summary[player.position] || 0) + 1; return summary; }, {}); } }

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