Skip to main content
Glama

find_related_wwdc_videos

Find related WWDC sessions to build learning paths. Discover prerequisites, follow-up content, and thematically similar talks for comprehensive developer education.

Instructions

Discover WWDC sessions related to a specific video. Finds prerequisite sessions, follow-up content, and thematically similar talks. Essential for creating learning paths.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
videoIdYesSource video ID. Example: "10101" for keynote.
yearYesSource video year. Example: "2025"
includeExplicitRelatedNoInclude Apple's recommended related videos. Usually prerequisites or follow-ups. Default: true
includeTopicRelatedNoInclude videos from same topic categories. Good for comprehensive learning. Default: true
includeYearRelatedNoInclude other videos from same WWDC. Default: false
limitNoMax related videos (default: 15).

Implementation Reference

  • The main handler function `handleFindRelatedWWDCVideos` that implements the tool logic. It loads the source video, finds related videos through three strategies: (1) explicit related videos from video metadata, (2) topic-related videos by matching topics, and (3) year-related videos from the same WWDC year. Results are scored, sorted, and formatted as markdown output.
    export async function handleFindRelatedWWDCVideos(
      videoId: string,
      year: string,
      includeExplicitRelated: boolean = true,
      includeTopicRelated: boolean = true,
      includeYearRelated: boolean = false,
      limit: number = 15,
    ): Promise<string> {
      try {
        // Load the source video
        const sourceVideo = await loadVideoData(year, videoId);
    
        let content = `# Related Videos for "${sourceVideo.title}"\n\n`;
        content += `**Source:** [${sourceVideo.title}](${sourceVideo.url}) (WWDC${year})\n\n`;
    
        const relatedVideos: Array<{
          video: WWDCVideo & { year: string };
          relationship: string;
          score: number;
        }> = [];
    
        // 1. Explicit related videos from video metadata
        if (includeExplicitRelated && sourceVideo.relatedVideos) {
          for (const related of sourceVideo.relatedVideos) {
            try {
              const relatedVideo = await loadVideoData(related.year, related.id);
    
              relatedVideos.push({
                video: { ...relatedVideo, year: related.year },
                relationship: 'Explicitly related',
                score: 10,
              });
            } catch (error) {
              logger.warn(`Failed to load related video ${related.year}-${related.id}:`, error);
            }
          }
        }
    
        // 2. Topic-related videos
        if (includeTopicRelated && sourceVideo.topics && sourceVideo.topics.length > 0) {
          for (const topic of sourceVideo.topics) {
            try {
              // Try to find topic by name mapping
              const metadata = await loadGlobalMetadata();
              const topicEntry = metadata.topics.find(t =>
                t.name.toLowerCase() === topic.toLowerCase() ||
                t.id.toLowerCase().includes(topic.toLowerCase().replace(/\s+/g, '-')),
              );
    
              if (topicEntry) {
                const topicIndex = await loadTopicIndex(topicEntry.id);
    
                // Get videos from same topic (excluding source video)
                const topicVideos = topicIndex.videos.filter(v => v.id !== videoId);
    
                // Load video data for scoring
                const videoFiles = topicVideos.slice(0, 10).map((v: any) => v.dataFile); // Limit to avoid too many requests
                const videos = await loadVideosData(videoFiles);
    
                for (const video of videos) {
                  // Skip if already added
                  if (relatedVideos.find(r => r.video.id === video.id && r.video.year === video.year)) {
                    continue;
                  }
    
                  // Calculate similarity score based on shared topics
                  const sharedTopics = (sourceVideo.topics || []).filter(t =>
                    video.topics?.some(vt => vt.toLowerCase() === t.toLowerCase()) || false,
                  );
                  const score = sharedTopics.length * 2;
    
                  relatedVideos.push({
                    video: { ...video, year: video.year },
                    relationship: `Same topic: ${topicEntry.name}`,
                    score,
                  });
                }
              }
            } catch (error) {
              logger.warn(`Failed to find topic-related videos for topic ${topic}:`, error);
            }
          }
        }
    
        // 3. Year-related videos (same year, similar topics)
        if (includeYearRelated) {
          try {
            const yearIndex = await loadYearIndex(year);
    
            // Get videos from same year with overlapping topics
            const yearVideos = yearIndex.videos.filter(v =>
              v.id !== videoId &&
              v.topics.some(t => sourceVideo.topics?.some(st => st.toLowerCase() === t.toLowerCase()) || false),
            );
    
            // Load a sample of videos
            const videoFiles = yearVideos.slice(0, 10).map((v: any) => v.dataFile);
            const videos = await loadVideosData(videoFiles);
    
            for (const video of videos) {
              // Skip if already added
              if (relatedVideos.find(r => r.video.id === video.id && r.video.year === video.year)) {
                continue;
              }
    
              const sharedTopics = (sourceVideo.topics || []).filter(t =>
                video.topics?.some(vt => vt.toLowerCase() === t.toLowerCase()) || false,
              );
              const score = sharedTopics.length;
    
              relatedVideos.push({
                video: { ...video, year: video.year },
                relationship: `Same year, shared topics: ${sharedTopics.join(', ')}`,
                score,
              });
            }
          } catch (error) {
            logger.warn('Failed to find year-related videos:', error);
          }
        }
    
        // Sort by score (descending) and apply limit
        relatedVideos.sort((a, b) => b.score - a.score);
        const limitedResults = relatedVideos.slice(0, limit);
    
        if (limitedResults.length === 0) {
          content += 'No related videos found.\n';
        } else {
          content += `## Related Videos (${limitedResults.length})\n\n`;
    
          limitedResults.forEach(result => {
            content += `### [${result.video.title}](${result.video.url})\n`;
            content += `*WWDC${result.video.year} | ${result.relationship}*\n\n`;
    
            const features: string[] = [];
            if (result.video.duration) {
              features.push(`Duration: ${result.video.duration}`);
            }
            if (result.video.hasTranscript) {
              features.push('Transcript');
            }
            if (result.video.hasCode) {
              features.push('Code');
            }
    
            if (features.length > 0) {
              content += `${features.join(' | ')}\n`;
            }
    
            if (result.video.topics.length > 0) {
              content += `**Topics:** ${result.video.topics.join(', ')}\n`;
            }
    
            content += '\n';
          });
        }
    
        return content;
    
      } catch (error) {
        logger.error('Failed to find related WWDC videos:', error);
        const errorMessage = error instanceof Error ? error.message : String(error);
        return `Error: Failed to find related WWDC videos: ${errorMessage}`;
      }
    }
  • Zod schema `findRelatedWWDCVideosSchema` that validates input parameters: videoId (required string), year (required string), includeExplicitRelated (boolean, default true), includeTopicRelated (boolean, default true), includeYearRelated (boolean, default false), and limit (number 1-50, default 15).
     * Schema for find_related_wwdc_videos
     */
    export const findRelatedWWDCVideosSchema = z.object({
      videoId: z.string().describe('Video ID to find related videos for'),
      year: z.string().describe('Year of the source video'),
      includeExplicitRelated: z.boolean().default(true).describe('Include explicitly related videos'),
      includeTopicRelated: z.boolean().default(true).describe('Include videos from same topics'),
      includeYearRelated: z.boolean().default(false).describe('Include videos from same year'),
      limit: z.number().min(1).max(50).default(15).describe('Maximum number of related videos'),
    });
  • Tool registration definition for 'find_related_wwdc_videos' with description and JSON Schema input schema. Defines the tool's metadata, required parameters (videoId, year), and optional parameters with their descriptions.
    {
      name: 'find_related_wwdc_videos',
      description: 'Discover WWDC sessions related to a specific video. Finds prerequisite sessions, follow-up content, and thematically similar talks. Essential for creating learning paths.',
      inputSchema: {
        type: 'object',
        properties: {
          videoId: {
            type: 'string',
            description: 'Source video ID. Example: "10101" for keynote.',
          },
          year: {
            type: 'string',
            description: 'Source video year. Example: "2025"',
          },
          includeExplicitRelated: {
            type: 'boolean',
            description: 'Include Apple\'s recommended related videos. Usually prerequisites or follow-ups. Default: true',
          },
          includeTopicRelated: {
            type: 'boolean',
            description: 'Include videos from same topic categories. Good for comprehensive learning. Default: true',
          },
          includeYearRelated: {
            type: 'boolean',
            description: 'Include other videos from same WWDC. Default: false',
          },
          limit: {
            type: 'number',
            description: 'Max related videos (default: 15).',
          },
        },
        required: ['videoId', 'year'],
      },
    },
  • Handler wrapper in the toolHandlers map that validates incoming arguments using the Zod schema, then delegates to handleFindRelatedWWDCVideos with the parsed parameters, returning the result as MCP content.
    find_related_wwdc_videos: async (args, _server) => {
      const validatedArgs = findRelatedWWDCVideosSchema.parse(args);
      const result = await handleFindRelatedWWDCVideos(
        validatedArgs.videoId,
        validatedArgs.year,
        validatedArgs.includeExplicitRelated,
        validatedArgs.includeTopicRelated,
        validatedArgs.includeYearRelated,
        validatedArgs.limit,
      );
      return { content: [{ type: 'text', text: result }] };
    },

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/kimsungwhee/apple-docs-mcp'

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