browse_wwdc_topics
Explore WWDC topic categories with IDs to filter Apple developer videos. Use topic IDs like "swiftui-ui-frameworks" to find relevant sessions and content.
Instructions
List all WWDC topic categories with their IDs. Essential first step before using list_wwdc_videos with topic filtering. Returns topic IDs like "swiftui-ui-frameworks" that can be used in other tools.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| topicId | No | Topic ID to explore. Available IDs: "accessibility-inclusion", "app-services", "app-store-distribution-marketing", "audio-video", "business-education", "design", "developer-tools", "essentials", "graphics-games", "health-fitness", "machine-learning-ai", "maps-location", "photos-camera", "privacy-security", "safari-web", "spatial-computing", "swift", "swiftui-ui-frameworks", "system-services". Leave empty to see all topics with video counts. | |
| includeVideos | No | List videos in the topic. Set false for topic structure only. Default: true | |
| year | No | Filter topic videos by year. Only when browsing specific topic. | |
| limit | No | Max videos per topic (default: 20). |
Implementation Reference
- src/tools/wwdc/wwdc-handlers.ts:711-817 (handler)The main handler function `handleBrowseWWDCTopics` that implements the tool logic. It lists all WWDC topics or explores a specific topic with its videos. Uses helper functions loadGlobalMetadata() and loadTopicIndex() to fetch data, groups videos by year, and formats the output as markdown.
export async function handleBrowseWWDCTopics( topicId?: string, includeVideos: boolean = true, year?: string, limit: number = 20, ): Promise<string> { try { const metadata = await loadGlobalMetadata(); if (!topicId) { // List all available topics let content = '# WWDC Topics\n\n'; content += `Found ${metadata.topics.length} topics:\n\n`; metadata.topics.forEach(topic => { content += `## [${topic.name}](${topic.url})\n`; content += `**Topic ID:** ${topic.id}\n`; // Show video count for this topic const topicStats = metadata.statistics.byTopic[topic.id]; if (topicStats) { content += `**Videos:** ${topicStats}\n`; } content += '\n'; }); return content; } // Browse specific topic const topic = metadata.topics.find(t => t.id === topicId); if (!topic) { return `Topic "${topicId}" not found. Available topics: ${metadata.topics.map(t => t.id).join(', ')}`; } let content = `# ${topic.name}\n\n`; content += `**Topic ID:** ${topic.id}\n`; content += `**URL:** [${topic.url}](${topic.url})\n\n`; if (includeVideos) { try { const topicIndex = await loadTopicIndex(topicId); // Filter by year if specified let videosToShow = topicIndex.videos; if (year && year !== 'all') { videosToShow = videosToShow.filter(v => v.year === year); } // Apply limit videosToShow = videosToShow.slice(0, limit); content += `## Videos (${videosToShow.length}${videosToShow.length === limit ? '+' : ''})\n\n`; if (videosToShow.length === 0) { content += 'No videos found for this topic.\n'; } else { // Group by year const videosByYear = videosToShow.reduce((acc, video) => { if (!acc[video.year]) { acc[video.year] = []; } acc[video.year].push(video); return acc; }, {} as Record<string, typeof videosToShow>); Object.keys(videosByYear) .sort((a, b) => parseInt(b) - parseInt(a)) .forEach(y => { content += `### WWDC${y}\n\n`; videosByYear[y].forEach(video => { content += `- [${video.title}](${video.url})`; const features: string[] = []; if (video.hasTranscript) { features.push('Transcript'); } if (video.hasCode) { features.push('Code'); } if (features.length > 0) { content += ` | ${features.join(' | ')}`; } content += '\n'; }); content += '\n'; }); } } catch (error) { content += `Error loading videos for topic: ${error instanceof Error ? error.message : String(error)}\n`; } } return content; } catch (error) { logger.error('Failed to browse WWDC topics:', error); const errorMessage = error instanceof Error ? error.message : String(error); return `Error: Failed to browse WWDC topics: ${errorMessage}`; } } - src/schemas/wwdc.schemas.ts:52-57 (schema)Zod schema definition `browseWWDCTopicsSchema` that validates input parameters: topicId (optional string), includeVideos (boolean, default true), year (optional string), and limit (number 1-100, default 20).
export const browseWWDCTopicsSchema = z.object({ topicId: z.string().optional().describe('Specific topic ID to browse'), includeVideos: z.boolean().default(true).describe('Include video list'), year: z.string().optional().describe('Filter videos by year'), limit: z.number().min(1).max(100).default(20).describe('Maximum number of videos per topic'), }); - src/tools/handlers.ts:280-289 (registration)Tool registration wrapper that validates arguments using browseWWDCTopicsSchema.parse() and calls handleBrowseWWDCTopics with validated parameters. Returns formatted content as MCP tool response.
browse_wwdc_topics: async (args, _server) => { const validatedArgs = browseWWDCTopicsSchema.parse(args); const result = await handleBrowseWWDCTopics( validatedArgs.topicId, validatedArgs.includeVideos, validatedArgs.year, validatedArgs.limit, ); return { content: [{ type: 'text', text: result }] }; }, - src/tools/definitions.ts:397-421 (registration)MCP tool definition with metadata including tool name 'browse_wwdc_topics', description, and inputSchema specifying parameter types, descriptions, and available topic IDs.
name: 'browse_wwdc_topics', description: 'List all WWDC topic categories with their IDs. Essential first step before using list_wwdc_videos with topic filtering. Returns topic IDs like "swiftui-ui-frameworks" that can be used in other tools.', inputSchema: { type: 'object', properties: { topicId: { type: 'string', description: 'Topic ID to explore. Available IDs: "accessibility-inclusion", "app-services", "app-store-distribution-marketing", "audio-video", "business-education", "design", "developer-tools", "essentials", "graphics-games", "health-fitness", "machine-learning-ai", "maps-location", "photos-camera", "privacy-security", "safari-web", "spatial-computing", "swift", "swiftui-ui-frameworks", "system-services". Leave empty to see all topics with video counts.', }, includeVideos: { type: 'boolean', description: 'List videos in the topic. Set false for topic structure only. Default: true', }, year: { type: 'string', description: 'Filter topic videos by year. Only when browsing specific topic.', }, limit: { type: 'number', description: 'Max videos per topic (default: 20).', }, }, required: [], }, }, - src/tools/wwdc/wwdc-handlers.ts:1-12 (helper)Imports helper utilities used by the handler: loadGlobalMetadata and loadTopicIndex from utils/wwdc-data-source.js, plus logger for error handling and WWDCVideo type.
/** * WWDC Video MCP Tool Handlers */ import type { WWDCVideo } from '../../types/wwdc.js'; import { logger } from '../../utils/logger.js'; import { loadGlobalMetadata, loadTopicIndex, loadYearIndex, loadVideoData, } from '../../utils/wwdc-data-source.js';