Skip to main content
Glama

get_watch_history

Retrieve detailed watch history and session data from Plex Media Server, enabling filtering by user, media type, and session count for precise insights.

Instructions

Get detailed watch history with session information

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
limitNoNumber of sessions to return (default: 50)
mediaTypeNoFilter by media typeall
userIdNoFilter by specific user ID (optional)

Implementation Reference

  • The primary handler function implementing the get_watch_history tool logic. Fetches session history from Plex API (/status/sessions/history/all), applies filters for user/mediaType, computes progress/completion, returns formatted JSON response. Includes fallback using library metadata if direct history unavailable.
    private async getWatchHistory(limit: number, userId?: string, mediaType: string = "all") {
      try {
        // Try primary watch history endpoint
        const params: Record<string, any> = {
          "X-Plex-Container-Size": limit,
          sort: "date:desc",
        };
        
        if (userId) {
          params.accountID = userId;
        }
        
        const data = await this.makeRequest("/status/sessions/history/all", params);
        let sessions = data.MediaContainer?.Metadata || [];
        
        // Filter by media type if specified
        if (mediaType !== "all") {
          const typeMap: Record<string, string> = {
            movie: "movie",
            show: "show", 
            episode: "episode"
          };
          sessions = sessions.filter((session: any) => session.type === typeMap[mediaType]);
        }
        
        return {
          content: [
            {
              type: "text",
              text: JSON.stringify({
                watchHistory: sessions.map((session: any) => ({
                  sessionKey: session.sessionKey,
                  ratingKey: session.ratingKey,
                  title: session.title,
                  type: session.type,
                  year: session.year,
                  viewedAt: session.viewedAt,
                  duration: session.duration,
                  viewOffset: session.viewOffset,
                  user: session.User?.title,
                  player: session.Player?.title,
                  platform: session.Player?.platform,
                  progress: session.viewOffset && session.duration ? 
                    Math.round((session.viewOffset / session.duration) * 100) : 0,
                  completed: session.viewOffset && session.duration ? 
                    session.viewOffset >= (session.duration * 0.9) : false,
                })),
                totalSessions: sessions.length,
              }, null, 2),
            },
          ],
        };
      } catch (error) {
        // Fallback: Create pseudo-history from library metadata
        try {
          const librariesData = await this.makeRequest("/library/sections");
          const libraries = librariesData.MediaContainer?.Directory || [];
          
          let allViewedItems: any[] = [];
          
          for (const library of libraries) {
            try {
              const params: Record<string, any> = {
                "X-Plex-Container-Size": Math.ceil(limit / libraries.length) + 5,
                sort: "lastViewedAt:desc",
              };
              
              if (mediaType !== "all") {
                params.type = this.getPlexTypeId(mediaType);
              }
              
              const contentData = await this.makeRequest(`/library/sections/${library.key}/all`, params);
              const content = contentData.MediaContainer?.Metadata || [];
              
              // Filter for items that have been viewed
              const viewedContent = content.filter((item: any) => 
                item.lastViewedAt && item.viewCount > 0
              );
              
              // Convert to history-like format
              const historyItems = viewedContent.map((item: any) => ({
                ratingKey: item.ratingKey,
                title: item.title,
                type: item.type,
                year: item.year,
                lastViewedAt: item.lastViewedAt,
                viewCount: item.viewCount,
                duration: item.duration,
                viewOffset: item.viewOffset || 0,
                library: library.title,
              }));
              
              allViewedItems.push(...historyItems);
            } catch (libError) {
              continue;
            }
          }
          
          // Sort by last viewed date and take requested number
          allViewedItems.sort((a: any, b: any) => (b.lastViewedAt || 0) - (a.lastViewedAt || 0));
          allViewedItems = allViewedItems.slice(0, limit);
          
          return {
            content: [
              {
                type: "text",
                text: JSON.stringify({
                  watchHistory: allViewedItems.map((item: any) => ({
                    ratingKey: item.ratingKey,
                    title: item.title,
                    type: item.type,
                    year: item.year,
                    lastViewedAt: item.lastViewedAt,
                    viewCount: item.viewCount,
                    duration: item.duration,
                    viewOffset: item.viewOffset,
                    library: item.library,
                    progress: item.viewOffset && item.duration ? 
                      Math.round((item.viewOffset / item.duration) * 100) : 0,
                    completed: item.viewOffset && item.duration ? 
                      item.viewOffset >= (item.duration * 0.85) : item.viewCount > 0,
                  })),
                  totalSessions: allViewedItems.length,
                  note: "Generated from library metadata (fallback method)",
                }, null, 2),
              },
            ],
          };
        } catch (fallbackError) {
          return {
            content: [
              {
                type: "text",
                text: JSON.stringify({
                  error: "Watch history not available",
                  message: "Unable to retrieve watch history from this Plex server",
                  suggestion: "This feature may require Plex Pass or detailed logging to be enabled",
                  watchHistory: [],
                }, null, 2),
              },
            ],
          };
        }
      }
    }
  • Input schema definition for the get_watch_history tool, specifying parameters: limit (number, default 50), userId (optional string), mediaType (enum: movie/show/episode/all, default 'all').
    {
      name: "get_watch_history",
      description: "Get detailed watch history with session information",
      inputSchema: {
        type: "object",
        properties: {
          limit: {
            type: "number",
            description: "Number of sessions to return (default: 50)",
            default: 50,
          },
          userId: {
            type: "string",
            description: "Filter by specific user ID (optional)",
          },
          mediaType: {
            type: "string",
            description: "Filter by media type",
            enum: ["movie", "show", "episode", "all"],
            default: "all",
          },
        },
      },
    },
  • src/index.ts:305-310 (registration)
    Tool registration in the central CallToolRequestSchema switch dispatcher: maps 'get_watch_history' calls to the getWatchHistory handler with parsed arguments.
    case "get_watch_history":
      return await this.getWatchHistory(
        ((args as any)?.limit as number) || 50,
        (args as any)?.userId as string,
        (args as any)?.mediaType as string || "all"
      );
  • src/index.ts:44-257 (registration)
    Tool registration in ListToolsRequestSchema handler: includes get_watch_history in the advertised tools list with name, description, and inputSchema.
    this.server.setRequestHandler(ListToolsRequestSchema, async () => {
      return {
        tools: [
          {
            name: "get_libraries",
            description: "Get all Plex libraries",
            inputSchema: {
              type: "object",
              properties: {},
            },
          },
          {
            name: "search_media",
            description: "Search for media in Plex libraries",
            inputSchema: {
              type: "object",
              properties: {
                query: {
                  type: "string",
                  description: "Search query",
                },
                type: {
                  type: "string",
                  description: "Media type (movie, show, episode, artist, album, track)",
                  enum: ["movie", "show", "episode", "artist", "album", "track"],
                },
              },
              required: ["query"],
            },
          },
          {
            name: "get_recently_added",
            description: "Get recently added media",
            inputSchema: {
              type: "object",
              properties: {
                limit: {
                  type: "number",
                  description: "Number of items to return (default: 10)",
                  default: 10,
                },
              },
            },
          },
          {
            name: "get_on_deck",
            description: "Get on deck (continue watching) items",
            inputSchema: {
              type: "object",
              properties: {},
            },
          },
          {
            name: "get_media_details",
            description: "Get detailed information about a specific media item",
            inputSchema: {
              type: "object",
              properties: {
                ratingKey: {
                  type: "string",
                  description: "The rating key of the media item",
                },
              },
              required: ["ratingKey"],
            },
          },
          {
            name: "get_recently_watched",
            description: "Get recently watched movies and shows",
            inputSchema: {
              type: "object",
              properties: {
                limit: {
                  type: "number",
                  description: "Number of items to return (default: 25)",
                  default: 25,
                },
                mediaType: {
                  type: "string",
                  description: "Filter by media type (movie, show, episode, all)",
                  enum: ["movie", "show", "episode", "all"],
                  default: "all",
                },
              },
            },
          },
          {
            name: "get_fully_watched",
            description: "Get all fully watched movies and shows from a library",
            inputSchema: {
              type: "object",
              properties: {
                libraryKey: {
                  type: "string",
                  description: "Library section key (optional, searches all if not provided)",
                },
                mediaType: {
                  type: "string",
                  description: "Filter by media type (movie, show, all)",
                  enum: ["movie", "show", "all"],
                  default: "all",
                },
                limit: {
                  type: "number",
                  description: "Number of items to return (default: 100)",
                  default: 100,
                },
              },
            },
          },
          {
            name: "get_watch_stats",
            description: "Get comprehensive watch statistics (Tautulli-style analytics)",
            inputSchema: {
              type: "object",
              properties: {
                timeRange: {
                  type: "number",
                  description: "Time range in days (default: 30)",
                  default: 30,
                },
                statType: {
                  type: "string",
                  description: "Type of statistics to retrieve",
                  enum: ["plays", "duration", "users", "libraries", "platforms"],
                  default: "plays",
                },
              },
            },
          },
          {
            name: "get_user_stats",
            description: "Get user-specific watch statistics",
            inputSchema: {
              type: "object",
              properties: {
                timeRange: {
                  type: "number",
                  description: "Time range in days (default: 30)",
                  default: 30,
                },
              },
            },
          },
          {
            name: "get_library_stats",
            description: "Get library-specific statistics",
            inputSchema: {
              type: "object",
              properties: {
                libraryKey: {
                  type: "string",
                  description: "Library section key (optional)",
                },
              },
            },
          },
          {
            name: "get_popular_content",
            description: "Get most popular content by plays or duration",
            inputSchema: {
              type: "object",
              properties: {
                timeRange: {
                  type: "number",
                  description: "Time range in days (default: 30)",
                  default: 30,
                },
                metric: {
                  type: "string",
                  description: "Sort by plays or total duration",
                  enum: ["plays", "duration"],
                  default: "plays",
                },
                mediaType: {
                  type: "string",
                  description: "Filter by media type",
                  enum: ["movie", "show", "episode", "all"],
                  default: "all",
                },
                limit: {
                  type: "number",
                  description: "Number of items to return (default: 10)",
                  default: 10,
                },
              },
            },
          },
          {
            name: "get_watch_history",
            description: "Get detailed watch history with session information",
            inputSchema: {
              type: "object",
              properties: {
                limit: {
                  type: "number",
                  description: "Number of sessions to return (default: 50)",
                  default: 50,
                },
                userId: {
                  type: "string",
                  description: "Filter by specific user ID (optional)",
                },
                mediaType: {
                  type: "string",
                  description: "Filter by media type",
                  enum: ["movie", "show", "episode", "all"],
                  default: "all",
                },
              },
            },
          },
        ],
      };
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. While 'Get' implies a read-only operation, the description doesn't address important behavioral aspects such as authentication requirements, rate limits, pagination behavior (beyond the 'limit' parameter), or what format the 'detailed watch history' and 'session information' will be returned in. This leaves significant gaps for an agent trying to understand how to properly invoke this tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise - a single sentence that efficiently communicates the core purpose. There's no wasted language or unnecessary elaboration. The description is appropriately sized for what it attempts to convey, though it could benefit from additional context.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the absence of annotations and output schema, the description is insufficiently complete. For a tool that returns 'detailed watch history with session information', the description should provide more context about what constitutes 'detailed' information, what 'session information' includes, and the general structure of the response. The lack of output schema means the description needs to compensate by explaining return values, which it doesn't do.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has 100% description coverage, providing clear documentation for all three parameters (limit, mediaType, userId). The description doesn't add any meaningful parameter semantics beyond what's already in the schema - it mentions 'detailed watch history with session information' but doesn't explain how the parameters affect what's returned. With complete schema coverage, the baseline score of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Get detailed watch history with session information' - it specifies the verb ('Get') and resource ('watch history') with the additional detail of including 'session information'. However, it doesn't explicitly differentiate this from sibling tools like 'get_recently_watched' or 'get_watch_stats', which likely provide related but different data.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. With multiple sibling tools related to watch history and media consumption (e.g., get_recently_watched, get_watch_stats, get_user_stats), there's no indication of what distinguishes this tool's scope or when it's the appropriate choice over those alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Related Tools

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/niavasha/plex-mcp-server'

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