Skip to main content
Glama
justfeltlikerunning

Sleeper Fantasy MCP

get_player_news

Retrieve player news, injury updates, and recent status changes for fantasy football rosters, trending players, or league-wide monitoring.

Instructions

Get player news, injury status, and recent updates for your roster or specific players

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
leagueNoLeague name (ROAD_TO_GLORY or DYNASTY), defaults to configured default
scopeNoScope of players to check: 'roster' (your team), 'trending' (add/drop activity), or 'all' (league-wide)roster
hoursBackNoHours back to check for news updates (default: 48)
includeHealthyNoInclude healthy players without injury status (default: false)
playerIdsNoSpecific player IDs to check (optional)

Implementation Reference

  • Main handler function `execute` in PlayerNewsTool class that implements the core logic for fetching player news, injury statuses, trending activity, and generating summaries based on input parameters.
    async execute(args: any) {
      const leagueConfig = getLeagueConfig(args.league);
      
      if (!leagueConfig) {
        throw new Error(`League configuration not found for: ${args.league}`);
      }
    
      const scope = args.scope || 'roster';
      const hoursBack = args.hoursBack || 48;
      const includeHealthy = args.includeHealthy || false;
      const specificPlayerIds = args.playerIds;
      const cutoffTime = Date.now() - (hoursBack * 60 * 60 * 1000);
    
      try {
        // Fetch required data
        const fetchPromises = [
          fetch(`${config.api.baseUrl}/players/nfl`),
          scope === 'roster' || scope === 'all' ? fetch(`${config.api.baseUrl}/league/${leagueConfig.id}/rosters`) : Promise.resolve(null),
          scope === 'trending' || scope === 'all' ? fetch(`${config.api.baseUrl}/players/nfl/trending/add?lookback_hours=${Math.min(hoursBack, 72)}&limit=50`) : Promise.resolve(null),
          scope === 'trending' || scope === 'all' ? fetch(`${config.api.baseUrl}/players/nfl/trending/drop?lookback_hours=${Math.min(hoursBack, 72)}&limit=50`) : Promise.resolve(null)
        ];
    
        const responses = await Promise.all(fetchPromises);
        
        if (!responses[0]?.ok) {
          throw new Error('Failed to fetch player data');
        }
    
        const players = await responses[0].json();
        const rosters: SleeperRoster[] = responses[1] ? await responses[1].json() : [];
        const trendingAdd = responses[2] ? await responses[2].json() : [];
        const trendingDrop = responses[3] ? await responses[3].json() : [];
    
        // Get relevant player IDs based on scope
        let relevantPlayerIds: Set<string>;
        
        if (specificPlayerIds && specificPlayerIds.length > 0) {
          relevantPlayerIds = new Set(specificPlayerIds);
        } else if (scope === 'roster') {
          // Find user's roster by matching owner username or team name
          const users = await fetch(`${config.api.baseUrl}/league/${leagueConfig.id}/users`).then(r => r.json());
          const myUser = users?.find((user: any) => user.display_name === config.username);
          const myRoster = rosters.find(roster => 
            (myUser && roster.owner_id === myUser.user_id)
          );
          relevantPlayerIds = new Set(myRoster?.players || []);
        } else if (scope === 'trending') {
          const trendingPlayerIds = [
            ...trendingAdd.map((p: any) => p.player_id),
            ...trendingDrop.map((p: any) => p.player_id)
          ];
          relevantPlayerIds = new Set(trendingPlayerIds);
        } else {
          // scope === 'all' - check all players, but limit to fantasy-relevant positions
          relevantPlayerIds = new Set();
        }
    
        // Process players and filter for news-worthy updates
        const newsUpdates = Object.entries(players)
          .filter(([playerId, player]: [string, any]) => {
            // Skip if not in relevant player set (unless scope is 'all')
            if (scope !== 'all' && !relevantPlayerIds.has(playerId)) return false;
            
            // Skip non-fantasy positions for 'all' scope
            if (scope === 'all' && (!player.fantasy_positions || 
                !player.fantasy_positions.some((pos: string) => ['QB', 'RB', 'WR', 'TE', 'K', 'DEF'].includes(pos)))) {
              return false;
            }
    
            // Check if player has recent news update
            const hasRecentUpdate = player.news_updated && player.news_updated > cutoffTime;
            
            // Check if player has injury/status information
            const hasInjuryInfo = player.injury_status || 
                                  player.injury_body_part || 
                                  player.practice_participation ||
                                  player.status === 'Injured Reserve' ||
                                  player.status === 'Out' ||
                                  player.status === 'Questionable' ||
                                  player.status === 'Doubtful';
    
            // Include if has recent update OR injury info (unless only wanting healthy players)
            if (!includeHealthy && !hasInjuryInfo && !hasRecentUpdate) return false;
            if (includeHealthy && !hasRecentUpdate && !hasInjuryInfo) return false;
    
            return true;
          })
          .map(([playerId, player]: [string, any]) => {
            const isTrendingAdd = trendingAdd.some((trend: any) => trend.player_id === playerId);
            const isTrendingDrop = trendingDrop.some((trend: any) => trend.player_id === playerId);
            
            // Calculate time since last update
            let timeSinceUpdate = "No recent updates";
            if (player.news_updated) {
              const hoursSince = Math.floor((Date.now() - player.news_updated) / (1000 * 60 * 60));
              if (hoursSince < 1) {
                timeSinceUpdate = "Less than 1 hour ago";
              } else if (hoursSince < 24) {
                timeSinceUpdate = `${hoursSince} hours ago`;
              } else {
                const daysSince = Math.floor(hoursSince / 24);
                timeSinceUpdate = `${daysSince} day${daysSince > 1 ? 's' : ''} ago`;
              }
            }
    
            return {
              playerId,
              name: `${player.first_name || ''} ${player.last_name || ''}`.trim(),
              position: player.position,
              team: player.team || 'FA',
              status: player.status || 'Active',
              injuryStatus: player.injury_status || null,
              injuryBodyPart: player.injury_body_part || null,
              injuryStartDate: player.injury_start_date,
              injuryNotes: player.injury_notes || null,
              practiceParticipation: player.practice_participation || null,
              practiceDescription: player.practice_description || null,
              newsUpdated: player.news_updated,
              timeSinceUpdate,
              trending: {
                isAdding: isTrendingAdd,
                isDropping: isTrendingDrop,
                activity: isTrendingAdd ? 'Being added' : isTrendingDrop ? 'Being dropped' : 'Stable'
              },
              fantasyPositions: player.fantasy_positions || []
            };
          })
          .sort((a, b) => {
            // Sort by most recent news first, then by injury severity
            if (a.newsUpdated && b.newsUpdated) {
              return b.newsUpdated - a.newsUpdated;
            }
            if (a.newsUpdated && !b.newsUpdated) return -1;
            if (!a.newsUpdated && b.newsUpdated) return 1;
            
            // Secondary sort by injury severity
            const getSeverity = (status: string | null) => {
              if (!status) return 0;
              if (status === 'Out' || status === 'Injured Reserve') return 5;
              if (status === 'Doubtful') return 4;
              if (status === 'Questionable') return 3;
              if (status === 'Probable') return 2;
              return 1;
            };
            
            return getSeverity(b.injuryStatus) - getSeverity(a.injuryStatus);
          });
    
        const result = {
          league: args.league || config.defaultLeague,
          scope: scope,
          hoursBack: hoursBack,
          cutoffTime: new Date(cutoffTime).toISOString(),
          totalUpdates: newsUpdates.length,
          filters: {
            includeHealthy: includeHealthy,
            specificPlayers: specificPlayerIds ? specificPlayerIds.length : 0
          },
          updates: newsUpdates,
          summary: {
            withInjuries: newsUpdates.filter(p => p.injuryStatus).length,
            withRecentNews: newsUpdates.filter(p => p.newsUpdated && p.newsUpdated > cutoffTime).length,
            trending: {
              adding: newsUpdates.filter(p => p.trending.isAdding).length,
              dropping: newsUpdates.filter(p => p.trending.isDropping).length
            },
            byStatus: this.getStatusSummary(newsUpdates)
          }
        };
    
        return {
          content: [
            {
              type: "text",
              text: JSON.stringify(result, null, 2),
            },
          ],
        };
      } catch (error) {
        throw new Error(`Failed to get player news: ${error instanceof Error ? error.message : String(error)}`);
      }
    }
  • Input schema defining parameters for the get_player_news tool including league, scope, time range, filters, and optional player IDs.
    inputSchema = {
      type: "object",
      properties: {
        league: {
          type: "string",
          description: "League name (ROAD_TO_GLORY or DYNASTY), defaults to configured default",
          enum: ["ROAD_TO_GLORY", "DYNASTY"]
        },
        scope: {
          type: "string",
          description: "Scope of players to check: 'roster' (your team), 'trending' (add/drop activity), or 'all' (league-wide)",
          enum: ["roster", "trending", "all"],
          default: "roster"
        },
        hoursBack: {
          type: "number",
          description: "Hours back to check for news updates (default: 48)",
          minimum: 1,
          maximum: 168,
          default: 48
        },
        includeHealthy: {
          type: "boolean",
          description: "Include healthy players without injury status (default: false)",
          default: false
        },
        playerIds: {
          type: "array",
          description: "Specific player IDs to check (optional)",
          items: {
            type: "string"
          }
        }
      }
    };
  • src/index.ts:88-89 (registration)
    Registration in the CallToolRequestSchema handler switch statement, dispatching calls to get_player_news to the playerNewsTool instance's execute method.
    case "get_player_news":
      return await playerNewsTool.execute(args);
  • src/index.ts:48-63 (registration)
    Registration in the ListToolsRequestSchema handler, including playerNewsTool instance in the list of available tools.
    server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        leagueTool,
        rosterTool,
        matchupTool,
        playerTool,
        projectionsTool,
        matchupProjectionsTool,
        lineupOptimizerTool,
        trendingTool,
        historicalScoresTool,
        playerNewsTool,
        transactionsTool,
        stateScheduleTool,
      ],
    }));
  • src/index.ts:19-32 (registration)
    Import and instantiation of the PlayerNewsTool class for use in tool registration and dispatch.
    import { PlayerNewsTool } from "./tools/PlayerNewsTool.js";
    import { TransactionsTool } from "./tools/TransactionsTool.js";
    import { StateScheduleTool } from "./tools/StateScheduleTool.js";
    
    const leagueTool = new LeagueTool();
    const rosterTool = new RosterTool();
    const matchupTool = new MatchupTool();
    const playerTool = new PlayerTool();
    const projectionsTool = new ProjectionsTool();
    const matchupProjectionsTool = new MatchupProjectionsTool();
    const lineupOptimizerTool = new LineupOptimizerTool();
    const trendingTool = new TrendingTool();
    const historicalScoresTool = new HistoricalScoresTool();
    const playerNewsTool = new PlayerNewsTool();

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