plex-mcp 0.0.1 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -25,6 +25,14 @@ class PlexMCPServer {
25
25
  this.setupToolHandlers();
26
26
  }
27
27
 
28
+ getHttpsAgent() {
29
+ const verifySSL = process.env.PLEX_VERIFY_SSL !== 'false';
30
+ return new (require('https').Agent)({
31
+ rejectUnauthorized: verifySSL,
32
+ minVersion: 'TLSv1.2'
33
+ });
34
+ }
35
+
28
36
  setupToolHandlers() {
29
37
  this.server.setRequestHandler(ListToolsRequestSchema, async () => {
30
38
  return {
@@ -380,6 +388,25 @@ class PlexMCPServer {
380
388
  required: [],
381
389
  },
382
390
  },
391
+ {
392
+ name: "browse_playlist",
393
+ description: "Browse and view the contents of a specific playlist with full track metadata",
394
+ inputSchema: {
395
+ type: "object",
396
+ properties: {
397
+ playlist_id: {
398
+ type: "string",
399
+ description: "The ID of the playlist to browse",
400
+ },
401
+ limit: {
402
+ type: "number",
403
+ description: "Maximum number of items to return (default: 50)",
404
+ default: 50,
405
+ },
406
+ },
407
+ required: ["playlist_id"],
408
+ },
409
+ },
383
410
  {
384
411
  name: "create_playlist",
385
412
  description: "Create a new playlist on the Plex server. Note: Non-smart playlists require an initial item (item_key parameter) to be created successfully.",
@@ -611,6 +638,8 @@ class PlexMCPServer {
611
638
  return await this.handleOnDeck(request.params.arguments);
612
639
  case "list_playlists":
613
640
  return await this.handleListPlaylists(request.params.arguments);
641
+ case "browse_playlist":
642
+ return await this.handleBrowsePlaylist(request.params.arguments);
614
643
  case "create_playlist":
615
644
  return await this.handleCreatePlaylist(request.params.arguments);
616
645
  case "add_to_playlist":
@@ -690,10 +719,7 @@ class PlexMCPServer {
690
719
 
691
720
  const response = await axios.get(searchUrl, {
692
721
  params,
693
- httpsAgent: new (require('https').Agent)({
694
- rejectUnauthorized: false,
695
- minVersion: 'TLSv1.2'
696
- })
722
+ httpsAgent: this.getHttpsAgent()
697
723
  });
698
724
 
699
725
  let results = this.parseSearchResults(response.data);
@@ -786,6 +812,12 @@ class PlexMCPServer {
786
812
  contentRating: item.contentRating,
787
813
  Media: item.Media,
788
814
  key: item.key,
815
+ ratingKey: item.ratingKey, // Critical: the unique identifier for playlist operations
816
+ // Additional hierarchical info for music tracks
817
+ parentTitle: item.parentTitle, // Album name
818
+ grandparentTitle: item.grandparentTitle, // Artist name
819
+ parentRatingKey: item.parentRatingKey, // Album ID
820
+ grandparentRatingKey: item.grandparentRatingKey, // Artist ID
789
821
  // Additional metadata for basic filters
790
822
  studio: item.studio,
791
823
  genres: item.Genre ? item.Genre.map(g => g.tag) : [],
@@ -807,8 +839,24 @@ class PlexMCPServer {
807
839
  formatted += ` - ${item.type}`;
808
840
  }
809
841
 
842
+ // Add artist/album info for music tracks
843
+ if (item.grandparentTitle && item.parentTitle) {
844
+ formatted += `\n Artist: ${item.grandparentTitle} | Album: ${item.parentTitle}`;
845
+ } else if (item.parentTitle) {
846
+ formatted += `\n Album/Show: ${item.parentTitle}`;
847
+ }
848
+
810
849
  if (item.rating) {
811
- formatted += ` - Rating: ${item.rating}`;
850
+ formatted += `\n Rating: ${item.rating}`;
851
+ }
852
+
853
+ if (item.duration) {
854
+ formatted += `\n Duration: ${this.formatDuration(item.duration)}`;
855
+ }
856
+
857
+ // CRITICAL: Show the ratingKey for playlist operations
858
+ if (item.ratingKey) {
859
+ formatted += `\n **ID: ${item.ratingKey}** (use this for playlists)`;
812
860
  }
813
861
 
814
862
  if (item.summary) {
@@ -819,6 +867,21 @@ class PlexMCPServer {
819
867
  }).join('\n\n');
820
868
  }
821
869
 
870
+ formatDuration(milliseconds) {
871
+ if (!milliseconds || milliseconds === 0) return 'Unknown';
872
+
873
+ const seconds = Math.floor(milliseconds / 1000);
874
+ const minutes = Math.floor(seconds / 60);
875
+ const hours = Math.floor(minutes / 60);
876
+
877
+ if (hours > 0) {
878
+ const remainingMinutes = minutes % 60;
879
+ return `${hours}h ${remainingMinutes}m`;
880
+ } else {
881
+ return `${minutes}m`;
882
+ }
883
+ }
884
+
822
885
  async handleBrowseLibraries(args) {
823
886
  try {
824
887
  const plexUrl = process.env.PLEX_URL || 'http://localhost:32400';
@@ -835,10 +898,7 @@ class PlexMCPServer {
835
898
 
836
899
  const response = await axios.get(librariesUrl, {
837
900
  params,
838
- httpsAgent: new (require('https').Agent)({
839
- rejectUnauthorized: false,
840
- minVersion: 'TLSv1.2'
841
- })
901
+ httpsAgent: this.getHttpsAgent()
842
902
  });
843
903
 
844
904
  const libraries = this.parseLibraries(response.data);
@@ -965,10 +1025,7 @@ class PlexMCPServer {
965
1025
 
966
1026
  const response = await axios.get(libraryUrl, {
967
1027
  params,
968
- httpsAgent: new (require('https').Agent)({
969
- rejectUnauthorized: false,
970
- minVersion: 'TLSv1.2'
971
- })
1028
+ httpsAgent: this.getHttpsAgent()
972
1029
  });
973
1030
 
974
1031
  let results = this.parseLibraryContent(response.data);
@@ -1090,10 +1147,7 @@ class PlexMCPServer {
1090
1147
 
1091
1148
  const response = await axios.get(recentUrl, {
1092
1149
  params,
1093
- httpsAgent: new (require('https').Agent)({
1094
- rejectUnauthorized: false,
1095
- minVersion: 'TLSv1.2'
1096
- })
1150
+ httpsAgent: this.getHttpsAgent()
1097
1151
  });
1098
1152
 
1099
1153
  const results = this.parseLibraryContent(response.data);
@@ -1191,10 +1245,7 @@ class PlexMCPServer {
1191
1245
 
1192
1246
  const response = await axios.get(historyUrl, {
1193
1247
  params,
1194
- httpsAgent: new (require('https').Agent)({
1195
- rejectUnauthorized: false,
1196
- minVersion: 'TLSv1.2'
1197
- })
1248
+ httpsAgent: this.getHttpsAgent()
1198
1249
  });
1199
1250
 
1200
1251
  const results = this.parseWatchHistory(response.data);
@@ -1314,10 +1365,7 @@ class PlexMCPServer {
1314
1365
 
1315
1366
  const response = await axios.get(onDeckUrl, {
1316
1367
  params,
1317
- httpsAgent: new (require('https').Agent)({
1318
- rejectUnauthorized: false,
1319
- minVersion: 'TLSv1.2'
1320
- })
1368
+ httpsAgent: this.getHttpsAgent()
1321
1369
  });
1322
1370
 
1323
1371
  const results = this.parseOnDeck(response.data);
@@ -1423,10 +1471,7 @@ class PlexMCPServer {
1423
1471
 
1424
1472
  const response = await axios.get(playlistsUrl, {
1425
1473
  params,
1426
- httpsAgent: new (require('https').Agent)({
1427
- rejectUnauthorized: false,
1428
- minVersion: 'TLSv1.2'
1429
- })
1474
+ httpsAgent: this.getHttpsAgent()
1430
1475
  });
1431
1476
 
1432
1477
  const playlists = this.parsePlaylists(response.data);
@@ -1456,6 +1501,120 @@ class PlexMCPServer {
1456
1501
  }
1457
1502
  }
1458
1503
 
1504
+ async handleBrowsePlaylist(args) {
1505
+ const { playlist_id, limit = 50 } = args;
1506
+
1507
+ try {
1508
+ const plexUrl = process.env.PLEX_URL || 'http://localhost:32400';
1509
+ const plexToken = process.env.PLEX_TOKEN;
1510
+
1511
+ if (!plexToken) {
1512
+ throw new Error('PLEX_TOKEN environment variable is required');
1513
+ }
1514
+
1515
+ // First get playlist info
1516
+ const playlistUrl = `${plexUrl}/playlists/${playlist_id}`;
1517
+ const response = await axios.get(playlistUrl, {
1518
+ params: {
1519
+ 'X-Plex-Token': plexToken
1520
+ },
1521
+ httpsAgent: this.getHttpsAgent()
1522
+ });
1523
+
1524
+ const playlistData = response.data.MediaContainer;
1525
+ if (!playlistData || !playlistData.Metadata || playlistData.Metadata.length === 0) {
1526
+ return {
1527
+ content: [
1528
+ {
1529
+ type: "text",
1530
+ text: `Playlist with ID ${playlist_id} not found or is empty`,
1531
+ },
1532
+ ],
1533
+ };
1534
+ }
1535
+
1536
+ const playlist = playlistData.Metadata[0];
1537
+ const items = playlistData.Metadata[0].Metadata || [];
1538
+
1539
+ // Limit results if specified
1540
+ const limitedItems = limit ? items.slice(0, limit) : items;
1541
+
1542
+ let resultText = `**${playlist.title}**`;
1543
+ if (playlist.smart) {
1544
+ resultText += ` (Smart Playlist)`;
1545
+ }
1546
+ resultText += `\n`;
1547
+ if (playlist.summary) {
1548
+ resultText += `${playlist.summary}\n`;
1549
+ }
1550
+ resultText += `Duration: ${this.formatDuration(playlist.duration || 0)}\n`;
1551
+ resultText += `Items: ${items.length}`;
1552
+ if (limit && items.length > limit) {
1553
+ resultText += ` (showing first ${limit})`;
1554
+ }
1555
+ resultText += `\n\n`;
1556
+
1557
+ if (limitedItems.length === 0) {
1558
+ resultText += `This playlist is empty.`;
1559
+ } else {
1560
+ resultText += limitedItems.map((item, index) => {
1561
+ let itemText = `${index + 1}. **${item.title}**`;
1562
+
1563
+ // Add artist/album info for music
1564
+ if (item.grandparentTitle && item.parentTitle) {
1565
+ itemText += `\n Artist: ${item.grandparentTitle}\n Album: ${item.parentTitle}`;
1566
+ } else if (item.parentTitle) {
1567
+ itemText += `\n Album/Show: ${item.parentTitle}`;
1568
+ }
1569
+
1570
+ // Add duration
1571
+ if (item.duration) {
1572
+ itemText += `\n Duration: ${this.formatDuration(item.duration)}`;
1573
+ }
1574
+
1575
+ // Add rating key for identification
1576
+ itemText += `\n ID: ${item.ratingKey}`;
1577
+
1578
+ // Add media type
1579
+ const mediaType = this.getMediaTypeFromItem(item);
1580
+ if (mediaType) {
1581
+ itemText += `\n Type: ${mediaType}`;
1582
+ }
1583
+
1584
+ return itemText;
1585
+ }).join('\n\n');
1586
+ }
1587
+
1588
+ return {
1589
+ content: [
1590
+ {
1591
+ type: "text",
1592
+ text: resultText,
1593
+ },
1594
+ ],
1595
+ };
1596
+ } catch (error) {
1597
+ return {
1598
+ content: [
1599
+ {
1600
+ type: "text",
1601
+ text: `Error browsing playlist: ${error.message}`,
1602
+ },
1603
+ ],
1604
+ isError: true,
1605
+ };
1606
+ }
1607
+ }
1608
+
1609
+ getMediaTypeFromItem(item) {
1610
+ if (item.type === 'track') return 'Music Track';
1611
+ if (item.type === 'episode') return 'TV Episode';
1612
+ if (item.type === 'movie') return 'Movie';
1613
+ if (item.type === 'artist') return 'Artist';
1614
+ if (item.type === 'album') return 'Album';
1615
+ return item.type || 'Unknown';
1616
+ }
1617
+
1459
1618
  parsePlaylists(data) {
1460
1619
  if (!data.MediaContainer || !data.MediaContainer.Metadata) {
1461
1620
  return [];
@@ -1540,10 +1699,7 @@ class PlexMCPServer {
1540
1699
  // First get server info to get machine identifier
1541
1700
  const serverResponse = await axios.get(`${plexUrl}/`, {
1542
1701
  headers: { 'X-Plex-Token': plexToken },
1543
- httpsAgent: new (require('https').Agent)({
1544
- rejectUnauthorized: false,
1545
- minVersion: 'TLSv1.2'
1546
- })
1702
+ httpsAgent: this.getHttpsAgent()
1547
1703
  });
1548
1704
 
1549
1705
  const machineIdentifier = serverResponse.data?.MediaContainer?.machineIdentifier;
@@ -1576,20 +1732,22 @@ class PlexMCPServer {
1576
1732
  headers: {
1577
1733
  'Content-Length': '0'
1578
1734
  },
1579
- httpsAgent: new (require('https').Agent)({
1580
- rejectUnauthorized: false,
1581
- minVersion: 'TLSv1.2'
1582
- })
1735
+ httpsAgent: this.getHttpsAgent()
1583
1736
  });
1584
1737
 
1585
1738
  // Get the created playlist info from the response
1586
1739
  const playlistData = response.data?.MediaContainer?.Metadata?.[0];
1587
1740
 
1588
- let resultText = `Successfully created ${smart ? 'smart ' : ''}playlist: **${title}**`;
1741
+ let resultText = `✅ Successfully created ${smart ? 'smart ' : ''}playlist: **${title}**`;
1589
1742
  if (playlistData) {
1590
- resultText += `\n Playlist ID: ${playlistData.ratingKey}`;
1743
+ resultText += `\n **Playlist ID: ${playlistData.ratingKey}** (use this ID for future operations)`;
1591
1744
  resultText += `\n Type: ${type}`;
1592
1745
  if (smart) resultText += `\n Smart Playlist: Yes`;
1746
+ if (item_key && !smart) {
1747
+ resultText += `\n Initial item added: ${item_key}`;
1748
+ }
1749
+ } else {
1750
+ resultText += `\n ⚠️ Playlist created but details not available - check your playlists`;
1593
1751
  }
1594
1752
 
1595
1753
  return {
@@ -1636,6 +1794,21 @@ You can try creating the playlist manually in Plex and then use other MCP tools
1636
1794
  throw new Error('PLEX_TOKEN environment variable is required');
1637
1795
  }
1638
1796
 
1797
+ // Get playlist info before adding items
1798
+ const playlistUrl = `${plexUrl}/playlists/${playlist_id}`;
1799
+ const beforeResponse = await axios.get(playlistUrl, {
1800
+ params: {
1801
+ 'X-Plex-Token': plexToken
1802
+ },
1803
+ httpsAgent: this.getHttpsAgent()
1804
+ });
1805
+
1806
+ const beforeData = beforeResponse.data.MediaContainer;
1807
+ const beforeCount = (beforeData.Metadata && beforeData.Metadata[0] && beforeData.Metadata[0].Metadata)
1808
+ ? beforeData.Metadata[0].Metadata.length : 0;
1809
+ const playlistTitle = beforeData.Metadata && beforeData.Metadata[0]
1810
+ ? beforeData.Metadata[0].title : `Playlist ${playlist_id}`;
1811
+
1639
1812
  const addUrl = `${plexUrl}/playlists/${playlist_id}/items`;
1640
1813
  const params = {
1641
1814
  'X-Plex-Token': plexToken,
@@ -1644,13 +1817,39 @@ You can try creating the playlist manually in Plex and then use other MCP tools
1644
1817
 
1645
1818
  const response = await axios.put(addUrl, null, {
1646
1819
  params,
1647
- httpsAgent: new (require('https').Agent)({
1648
- rejectUnauthorized: false,
1649
- minVersion: 'TLSv1.2'
1650
- })
1820
+ httpsAgent: this.getHttpsAgent()
1821
+ });
1822
+
1823
+ // Verify the addition by checking the playlist again
1824
+ const afterResponse = await axios.get(playlistUrl, {
1825
+ params: {
1826
+ 'X-Plex-Token': plexToken
1827
+ },
1828
+ httpsAgent: this.getHttpsAgent()
1651
1829
  });
1652
1830
 
1653
- const resultText = `Successfully added ${item_keys.length} item(s) to playlist ${playlist_id}`;
1831
+ const afterData = afterResponse.data.MediaContainer;
1832
+ const afterCount = (afterData.Metadata && afterData.Metadata[0] && afterData.Metadata[0].Metadata)
1833
+ ? afterData.Metadata[0].Metadata.length : 0;
1834
+
1835
+ const actualAdded = afterCount - beforeCount;
1836
+ const attempted = item_keys.length;
1837
+
1838
+ let resultText = `Playlist "${playlistTitle}" update:\n`;
1839
+ resultText += `• Attempted to add: ${attempted} item(s)\n`;
1840
+ resultText += `• Actually added: ${actualAdded} item(s)\n`;
1841
+ resultText += `• Playlist size: ${beforeCount} → ${afterCount} items\n`;
1842
+
1843
+ if (actualAdded === attempted) {
1844
+ resultText += `✅ All items added successfully!`;
1845
+ } else if (actualAdded > 0) {
1846
+ resultText += `⚠️ Partial success: ${attempted - actualAdded} item(s) may have been duplicates or invalid`;
1847
+ } else {
1848
+ resultText += `❌ No items were added. This may indicate:\n`;
1849
+ resultText += ` - Invalid item IDs (use ratingKey from search results)\n`;
1850
+ resultText += ` - Items already exist in playlist\n`;
1851
+ resultText += ` - Permission issues`;
1852
+ }
1654
1853
 
1655
1854
  return {
1656
1855
  content: [
@@ -1684,6 +1883,21 @@ You can try creating the playlist manually in Plex and then use other MCP tools
1684
1883
  throw new Error('PLEX_TOKEN environment variable is required');
1685
1884
  }
1686
1885
 
1886
+ // Get playlist info before removing items
1887
+ const playlistUrl = `${plexUrl}/playlists/${playlist_id}`;
1888
+ const beforeResponse = await axios.get(playlistUrl, {
1889
+ params: {
1890
+ 'X-Plex-Token': plexToken
1891
+ },
1892
+ httpsAgent: this.getHttpsAgent()
1893
+ });
1894
+
1895
+ const beforeData = beforeResponse.data.MediaContainer;
1896
+ const beforeCount = (beforeData.Metadata && beforeData.Metadata[0] && beforeData.Metadata[0].Metadata)
1897
+ ? beforeData.Metadata[0].Metadata.length : 0;
1898
+ const playlistTitle = beforeData.Metadata && beforeData.Metadata[0]
1899
+ ? beforeData.Metadata[0].title : `Playlist ${playlist_id}`;
1900
+
1687
1901
  const removeUrl = `${plexUrl}/playlists/${playlist_id}/items`;
1688
1902
  const params = {
1689
1903
  'X-Plex-Token': plexToken,
@@ -1692,13 +1906,39 @@ You can try creating the playlist manually in Plex and then use other MCP tools
1692
1906
 
1693
1907
  const response = await axios.delete(removeUrl, {
1694
1908
  params,
1695
- httpsAgent: new (require('https').Agent)({
1696
- rejectUnauthorized: false,
1697
- minVersion: 'TLSv1.2'
1698
- })
1909
+ httpsAgent: this.getHttpsAgent()
1910
+ });
1911
+
1912
+ // Verify the removal by checking the playlist again
1913
+ const afterResponse = await axios.get(playlistUrl, {
1914
+ params: {
1915
+ 'X-Plex-Token': plexToken
1916
+ },
1917
+ httpsAgent: this.getHttpsAgent()
1699
1918
  });
1700
1919
 
1701
- const resultText = `Successfully removed ${item_keys.length} item(s) from playlist ${playlist_id}`;
1920
+ const afterData = afterResponse.data.MediaContainer;
1921
+ const afterCount = (afterData.Metadata && afterData.Metadata[0] && afterData.Metadata[0].Metadata)
1922
+ ? afterData.Metadata[0].Metadata.length : 0;
1923
+
1924
+ const actualRemoved = beforeCount - afterCount;
1925
+ const attempted = item_keys.length;
1926
+
1927
+ let resultText = `Playlist "${playlistTitle}" update:\n`;
1928
+ resultText += `• Attempted to remove: ${attempted} item(s)\n`;
1929
+ resultText += `• Actually removed: ${actualRemoved} item(s)\n`;
1930
+ resultText += `• Playlist size: ${beforeCount} → ${afterCount} items\n`;
1931
+
1932
+ if (actualRemoved === attempted) {
1933
+ resultText += `✅ All items removed successfully!`;
1934
+ } else if (actualRemoved > 0) {
1935
+ resultText += `⚠️ Partial success: ${attempted - actualRemoved} item(s) were not found in the playlist`;
1936
+ } else {
1937
+ resultText += `❌ No items were removed. This may indicate:\n`;
1938
+ resultText += ` - Invalid item IDs\n`;
1939
+ resultText += ` - Items not present in playlist\n`;
1940
+ resultText += ` - Permission issues`;
1941
+ }
1702
1942
 
1703
1943
  return {
1704
1944
  content: [
@@ -1739,10 +1979,7 @@ You can try creating the playlist manually in Plex and then use other MCP tools
1739
1979
 
1740
1980
  const response = await axios.delete(deleteUrl, {
1741
1981
  params,
1742
- httpsAgent: new (require('https').Agent)({
1743
- rejectUnauthorized: false,
1744
- minVersion: 'TLSv1.2'
1745
- })
1982
+ httpsAgent: this.getHttpsAgent()
1746
1983
  });
1747
1984
 
1748
1985
  const resultText = `Successfully deleted playlist ${playlist_id}`;
@@ -1795,10 +2032,7 @@ You can try creating the playlist manually in Plex and then use other MCP tools
1795
2032
 
1796
2033
  const response = await axios.get(itemUrl, {
1797
2034
  params,
1798
- httpsAgent: new (require('https').Agent)({
1799
- rejectUnauthorized: false,
1800
- minVersion: 'TLSv1.2'
1801
- })
2035
+ httpsAgent: this.getHttpsAgent()
1802
2036
  });
1803
2037
 
1804
2038
  const item = response.data?.MediaContainer?.Metadata?.[0];
@@ -2242,10 +2476,7 @@ You can try creating the playlist manually in Plex and then use other MCP tools
2242
2476
 
2243
2477
  const response = await axios.get(collectionsUrl, {
2244
2478
  params,
2245
- httpsAgent: new (require('https').Agent)({
2246
- rejectUnauthorized: false,
2247
- minVersion: 'TLSv1.2'
2248
- })
2479
+ httpsAgent: this.getHttpsAgent()
2249
2480
  });
2250
2481
 
2251
2482
  const collections = this.parseCollections(response.data);
@@ -2296,10 +2527,7 @@ You can try creating the playlist manually in Plex and then use other MCP tools
2296
2527
 
2297
2528
  const response = await axios.get(collectionUrl, {
2298
2529
  params,
2299
- httpsAgent: new (require('https').Agent)({
2300
- rejectUnauthorized: false,
2301
- minVersion: 'TLSv1.2'
2302
- })
2530
+ httpsAgent: this.getHttpsAgent()
2303
2531
  });
2304
2532
 
2305
2533
  const results = this.parseLibraryContent(response.data);
@@ -2399,10 +2627,7 @@ You can try creating the playlist manually in Plex and then use other MCP tools
2399
2627
 
2400
2628
  const response = await axios.get(mediaUrl, {
2401
2629
  params,
2402
- httpsAgent: new (require('https').Agent)({
2403
- rejectUnauthorized: false,
2404
- minVersion: 'TLSv1.2'
2405
- })
2630
+ httpsAgent: this.getHttpsAgent()
2406
2631
  });
2407
2632
 
2408
2633
  const item = response.data?.MediaContainer?.Metadata?.[0];
@@ -2691,10 +2916,7 @@ You can try creating the playlist manually in Plex and then use other MCP tools
2691
2916
  // Get library information first
2692
2917
  const librariesResponse = await axios.get(`${plexUrl}/library/sections`, {
2693
2918
  params: { 'X-Plex-Token': plexToken },
2694
- httpsAgent: new (require('https').Agent)({
2695
- rejectUnauthorized: false,
2696
- minVersion: 'TLSv1.2'
2697
- })
2919
+ httpsAgent: this.getHttpsAgent()
2698
2920
  });
2699
2921
 
2700
2922
  const libraries = this.parseLibraries(librariesResponse.data);
@@ -2783,10 +3005,7 @@ You can try creating the playlist manually in Plex and then use other MCP tools
2783
3005
  'X-Plex-Container-Start': offset,
2784
3006
  'X-Plex-Container-Size': batchSize
2785
3007
  },
2786
- httpsAgent: new (require('https').Agent)({
2787
- rejectUnauthorized: false,
2788
- minVersion: 'TLSv1.2'
2789
- })
3008
+ httpsAgent: this.getHttpsAgent()
2790
3009
  });
2791
3010
 
2792
3011
  const content = this.parseLibraryContent(contentResponse.data);
@@ -2827,10 +3046,7 @@ You can try creating the playlist manually in Plex and then use other MCP tools
2827
3046
  try {
2828
3047
  const mediaResponse = await axios.get(`${plexUrl}${item.key}`, {
2829
3048
  params: { 'X-Plex-Token': plexToken },
2830
- httpsAgent: new (require('https').Agent)({
2831
- rejectUnauthorized: false,
2832
- minVersion: 'TLSv1.2'
2833
- })
3049
+ httpsAgent: this.getHttpsAgent()
2834
3050
  });
2835
3051
 
2836
3052
  const detailedItem = mediaResponse.data?.MediaContainer?.Metadata?.[0];
@@ -3059,10 +3275,7 @@ You can try creating the playlist manually in Plex and then use other MCP tools
3059
3275
  } else {
3060
3276
  const librariesResponse = await axios.get(`${plexUrl}/library/sections`, {
3061
3277
  params: { 'X-Plex-Token': plexToken },
3062
- httpsAgent: new (require('https').Agent)({
3063
- rejectUnauthorized: false,
3064
- minVersion: 'TLSv1.2'
3065
- })
3278
+ httpsAgent: this.getHttpsAgent()
3066
3279
  });
3067
3280
 
3068
3281
  const allLibraries = this.parseLibraries(librariesResponse.data);
@@ -3165,10 +3378,7 @@ You can try creating the playlist manually in Plex and then use other MCP tools
3165
3378
  try {
3166
3379
  const historyResponse = await axios.get(`${plexUrl}/status/sessions/history/all`, {
3167
3380
  params: historyParams,
3168
- httpsAgent: new (require('https').Agent)({
3169
- rejectUnauthorized: false,
3170
- minVersion: 'TLSv1.2'
3171
- })
3381
+ httpsAgent: this.getHttpsAgent()
3172
3382
  });
3173
3383
 
3174
3384
  const history = this.parseWatchHistory(historyResponse.data);
@@ -3249,10 +3459,7 @@ You can try creating the playlist manually in Plex and then use other MCP tools
3249
3459
  query: trackName,
3250
3460
  type: 10 // Track type
3251
3461
  },
3252
- httpsAgent: new (require('https').Agent)({
3253
- rejectUnauthorized: false,
3254
- minVersion: 'TLSv1.2'
3255
- })
3462
+ httpsAgent: this.getHttpsAgent()
3256
3463
  });
3257
3464
 
3258
3465
  const tracks = this.parseSearchResults(searchResponse.data);
@@ -3260,10 +3467,7 @@ You can try creating the playlist manually in Plex and then use other MCP tools
3260
3467
  if (track.key) {
3261
3468
  const trackDetailResponse = await axios.get(`${plexUrl}${track.key}`, {
3262
3469
  params: { 'X-Plex-Token': plexToken },
3263
- httpsAgent: new (require('https').Agent)({
3264
- rejectUnauthorized: false,
3265
- minVersion: 'TLSv1.2'
3266
- })
3470
+ httpsAgent: this.getHttpsAgent()
3267
3471
  });
3268
3472
 
3269
3473
  const trackDetail = trackDetailResponse.data?.MediaContainer?.Metadata?.[0];
@@ -3308,10 +3512,7 @@ You can try creating the playlist manually in Plex and then use other MCP tools
3308
3512
  'X-Plex-Container-Size': 10,
3309
3513
  sort: 'addedAt:desc' // Recently added first
3310
3514
  },
3311
- httpsAgent: new (require('https').Agent)({
3312
- rejectUnauthorized: false,
3313
- minVersion: 'TLSv1.2'
3314
- })
3515
+ httpsAgent: this.getHttpsAgent()
3315
3516
  });
3316
3517
 
3317
3518
  const tracks = this.parseLibraryContent(genreSearchResponse.data);
@@ -3342,10 +3543,7 @@ You can try creating the playlist manually in Plex and then use other MCP tools
3342
3543
  query: artist,
3343
3544
  type: 8 // Artist type
3344
3545
  },
3345
- httpsAgent: new (require('https').Agent)({
3346
- rejectUnauthorized: false,
3347
- minVersion: 'TLSv1.2'
3348
- })
3546
+ httpsAgent: this.getHttpsAgent()
3349
3547
  });
3350
3548
 
3351
3549
  const artists = this.parseSearchResults(artistSearchResponse.data);
@@ -3353,10 +3551,7 @@ You can try creating the playlist manually in Plex and then use other MCP tools
3353
3551
  if (foundArtist.key) {
3354
3552
  const artistDetailResponse = await axios.get(`${plexUrl}${foundArtist.key}`, {
3355
3553
  params: { 'X-Plex-Token': plexToken },
3356
- httpsAgent: new (require('https').Agent)({
3357
- rejectUnauthorized: false,
3358
- minVersion: 'TLSv1.2'
3359
- })
3554
+ httpsAgent: this.getHttpsAgent()
3360
3555
  });
3361
3556
 
3362
3557
  const artistTracks = this.parseLibraryContent(artistDetailResponse.data);