multi-source-query.tsā¢6.98 kB
/**
* Example: Multi-Source Research Query
* Demonstrates how to combine results from multiple research sources
*/
import type { ArxivPaper, SemanticScholarPaper, PubMedPaper } from '../types.js';
import { deduplicatePapers } from '../utils.js';
/**
* Combined paper type for unified results
*/
interface UnifiedPaper {
title: string;
authors: string[];
abstract: string;
year: number;
source: 'arxiv' | 'semantic-scholar' | 'pubmed';
url: string;
citationCount?: number;
venue?: string;
}
/**
* Convert arXiv paper to unified format
*/
function normalizeArxivPaper(paper: ArxivPaper): UnifiedPaper {
return {
title: paper.title,
authors: paper.authors,
abstract: paper.abstract,
year: new Date(paper.published).getFullYear(),
source: 'arxiv',
url: paper.url,
};
}
/**
* Convert Semantic Scholar paper to unified format
*/
function normalizeSemanticScholarPaper(paper: SemanticScholarPaper): UnifiedPaper {
return {
title: paper.title,
authors: paper.authors.map(a => a.name),
abstract: paper.abstract || '',
year: paper.year || 0,
source: 'semantic-scholar',
url: paper.url,
citationCount: paper.citationCount,
venue: paper.venue || undefined,
};
}
/**
* Convert PubMed paper to unified format
*/
function normalizePubMedPaper(paper: PubMedPaper): UnifiedPaper {
return {
title: paper.title,
authors: paper.authors,
abstract: paper.abstract,
year: parseInt(paper.year) || 0,
source: 'pubmed',
url: paper.url,
venue: paper.journal,
};
}
/**
* Combine and deduplicate papers from multiple sources
*/
export function combineResults(
arxivPapers: ArxivPaper[],
semanticScholarPapers: SemanticScholarPaper[],
pubmedPapers: PubMedPaper[]
): UnifiedPaper[] {
const allPapers: UnifiedPaper[] = [
...arxivPapers.map(normalizeArxivPaper),
...semanticScholarPapers.map(normalizeSemanticScholarPaper),
...pubmedPapers.map(normalizePubMedPaper),
];
return deduplicatePapers(allPapers);
}
/**
* Sort papers by relevance (citation count if available, then year)
*/
export function sortByRelevance(papers: UnifiedPaper[]): UnifiedPaper[] {
return papers.sort((a, b) => {
// Prioritize papers with citation counts
if (a.citationCount && b.citationCount) {
return b.citationCount - a.citationCount;
}
if (a.citationCount) return -1;
if (b.citationCount) return 1;
// Otherwise sort by year (most recent first)
return b.year - a.year;
});
}
/**
* Generate summary of papers
*/
export function generateSummary(papers: UnifiedPaper[], maxResults: number = 5): string {
const topPapers = papers.slice(0, maxResults);
let summary = `# Research Summary\n\n`;
summary += `Found ${papers.length} papers across multiple sources.\n\n`;
summary += `## Top ${topPapers.length} Papers\n\n`;
topPapers.forEach((paper, index) => {
summary += `### ${index + 1}. ${paper.title}\n\n`;
summary += `**Authors:** ${paper.authors.slice(0, 5).join(', ')}${paper.authors.length > 5 ? ', et al.' : ''}\n\n`;
summary += `**Year:** ${paper.year}\n\n`;
summary += `**Source:** ${paper.source}\n\n`;
if (paper.citationCount) {
summary += `**Citations:** ${paper.citationCount}\n\n`;
}
if (paper.venue) {
summary += `**Venue:** ${paper.venue}\n\n`;
}
summary += `**Abstract:** ${paper.abstract.slice(0, 300)}${paper.abstract.length > 300 ? '...' : ''}\n\n`;
summary += `**URL:** ${paper.url}\n\n`;
summary += `---\n\n`;
});
return summary;
}
/**
* Extract key trends from papers
*/
export function extractTrends(papers: UnifiedPaper[]): {
yearDistribution: Record<number, number>;
topVenues: Array<{ venue: string; count: number }>;
sourceDistribution: Record<string, number>;
} {
const yearDistribution: Record<number, number> = {};
const venueCount: Record<string, number> = {};
const sourceDistribution: Record<string, number> = {};
papers.forEach(paper => {
// Year distribution
yearDistribution[paper.year] = (yearDistribution[paper.year] || 0) + 1;
// Venue distribution
if (paper.venue) {
venueCount[paper.venue] = (venueCount[paper.venue] || 0) + 1;
}
// Source distribution
sourceDistribution[paper.source] = (sourceDistribution[paper.source] || 0) + 1;
});
const topVenues = Object.entries(venueCount)
.map(([venue, count]) => ({ venue, count }))
.sort((a, b) => b.count - a.count)
.slice(0, 10);
return {
yearDistribution,
topVenues,
sourceDistribution,
};
}
/**
* Example usage demonstrating the complete workflow
* This would be printed to stderr in a real implementation
*/
export function exampleUsage(): string {
return `
=================================================================
Example: MCP Research Assistant Query Workflow
=================================================================
1. Query: "Find recent papers on federated learning in healthcare"
Step 1: Call search_arxiv tool
{
"query": "federated learning healthcare",
"maxResults": 10,
"startYear": 2023
}
Step 2: Call search_semantic_scholar tool
{
"query": "federated learning healthcare",
"maxResults": 10,
"startYear": 2023
}
Step 3: Call search_pubmed tool
{
"query": "federated learning healthcare",
"maxResults": 10,
"startYear": 2023
}
Step 4: Combine and deduplicate results
- Merge papers from all three sources
- Remove duplicates based on title similarity
- Sort by relevance (citation count, then year)
Step 5: Generate summary
- Extract top 5 papers
- Format with titles, authors, abstracts, URLs
- Include citation counts where available
=================================================================
2. Query: "What's the most cited 2023 paper on graph neural networks?"
Step 1: Call search_semantic_scholar tool
{
"query": "graph neural networks",
"maxResults": 50,
"startYear": 2023,
"endYear": 2023
}
Step 2: Sort by citation count
- Filter results to find highest cited paper
Step 3: Get detailed information
Call get_semantic_scholar_paper with paper ID
Step 4: Generate BibTeX citation
Call semantic_scholar_to_bibtex with paper ID
=================================================================
3. Query: "Compare transformer architectures for NLP after 2023"
Step 1: Search across sources
- search_arxiv: "transformer architecture NLP"
- search_semantic_scholar: "transformer NLP"
Step 2: Filter by year >= 2023
Step 3: Analyze trends
- Extract common methods from abstracts
- Identify key innovations
- Compare citation patterns
Step 4: Generate comparative summary
- Group by approach
- Highlight differences
- Provide recommendations
=================================================================
`;
}