UNPKG

apple-developer-docs-mcp

Version:

An MCP server that fetches the right data from Apple's developer documentation site

159 lines (151 loc) 6.92 kB
/** * Handles content summarization using MCP SDK sampling */ export class DocumentSummarizer { server; config; constructor(server, config = {}) { this.server = server; this.config = { maxDocuments: 5, maxContentLength: 15000, temperature: 0.3, maxTokens: 1000, ...config }; } /** * Prepare content for summarization by chunking and selecting most relevant parts */ prepareContent(searchResults, documentContents, userQuestion) { // Combine search results with their content const combinedContent = searchResults.slice(0, this.config.maxDocuments).map((result, index) => { const content = documentContents[index] || ''; return `## ${result.title}\n**URL:** ${result.url}\n**Type:** ${result.type}\n\n${content}`; }).join('\n\n---\n\n'); // Truncate if content is too long if (combinedContent.length > this.config.maxContentLength) { const truncated = combinedContent.substring(0, this.config.maxContentLength); const lastCompleteSection = truncated.lastIndexOf('\n\n---\n\n'); if (lastCompleteSection > 0) { return truncated.substring(0, lastCompleteSection) + '\n\n[Content truncated for length]'; } return truncated + '\n\n[Content truncated for length]'; } return combinedContent; } /** * Summarize content using MCP SDK sampling */ async summarizeContent(searchResults, documentContents, userQuestion, docsQuery) { try { // Prepare content for summarization const preparedContent = this.prepareContent(searchResults, documentContents, userQuestion); // Use MCP SDK sampling to get LLM summary with multiple messages const samplingResult = await this.server.server.createMessage({ messages: [ { role: 'user', content: { type: 'text', text: `You are an expert Apple developer documentation analyst. When answering questions, focus on practical implementation details. Guidelines: - Prioritize code examples, API signatures, and method details - Include specific class names, protocols, and property types - Mention OS version requirements and framework dependencies - Highlight any best practices, performance tips, or common pitfalls - Reference related APIs or alternative approaches when relevant - Use markdown code blocks for any code snippets or API signatures` } }, { role: 'user', content: { type: 'text', text: `I need help with the following question about Apple development: ${userQuestion} (Search context: ${docsQuery})` } }, { role: 'user', content: { type: 'text', text: `Here is the relevant Apple Developer documentation I found: ${preparedContent}` } }, { role: 'user', content: { type: 'text', text: `Please provide a clear, concise, technically detailed response that helps me implement a solution effectively. Just the markdown-formatted text response, no intro or conclusion. My question: ${userQuestion}` } } ], maxTokens: this.config.maxTokens, temperature: this.config.temperature }); // Extract summary from sampling result const summaryText = samplingResult.content.type === 'text' ? samplingResult.content.text : 'Unable to generate summary from the provided content.'; // Create relevant docs list const relevantDocs = searchResults.slice(0, this.config.maxDocuments).map(result => ({ title: result.title, url: result.url, relevance: result.description || 'Related documentation' })); return { insights: summaryText, relevantDocs, sourceCount: Math.min(searchResults.length, this.config.maxDocuments) }; } catch (error) { console.error('Error during summarization:', error); // Fallback summary if sampling fails const fallbackSummary = this.createFallbackSummary(searchResults, userQuestion, docsQuery); return { insights: fallbackSummary, relevantDocs: searchResults.slice(0, this.config.maxDocuments).map(result => ({ title: result.title, url: result.url, relevance: result.description || 'Related documentation' })), sourceCount: Math.min(searchResults.length, this.config.maxDocuments) }; } } /** * Create a fallback summary when sampling fails */ createFallbackSummary(searchResults, userQuestion, docsQuery) { const docCount = searchResults.length; const topDocs = searchResults.slice(0, 3); return `## Search Results Summary Found ${docCount} documentation resources related to "${docsQuery}" that may help answer your question: "${userQuestion}" **Top Resources:** ${topDocs.map(doc => `- **${doc.title}** (${doc.type}): ${doc.description}`).join('\n')} **Note:** Automated summarization is temporarily unavailable. Please review the full documentation at the provided links for detailed information. **Recommendation:** Focus on the documentation marked as "API" or "Guide" types for the most relevant technical information.`; } /** * Format the final result for display */ formatResult(result, docsQuery, userQuestion) { let formatted = result.insights.trim(); if (!formatted) { formatted = `No insights available for the query "${docsQuery}". Please try a different question.`; } formatted += `---\n\n`; formatted += `This summary was generated by analyzing ${result.sourceCount} Apple Developer documentation sources.\n`; result.relevantDocs.forEach((doc, index) => { formatted += `${index + 1}. [${doc.title}](${doc.url})\n`; }); formatted += `*For the complete information, please refer to the original documentation.*`; return formatted; } } //# sourceMappingURL=summarizer.js.map