sch_search
Search scholarly articles and academic papers using a query, with options to limit results and specify number of top results.
Instructions
Alias of sch.search
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| q | Yes | ||
| top | No | ||
| limit | No |
Implementation Reference
- src/server.ts:167-173 (registration)Registers the 'sch_search' tool as an alias of 'sch.search' on the MCP server, using schSearchShape for input schema and calling schSearch handler.
server.tool('sch_search', 'Alias of sch.search', schSearchShape, OPEN, async ({ q, top, limit }) => { const res = await schSearch(q, top ?? limit ?? 5); return { content: [{ type: 'text', text: JSON.stringify(res) }] }; } ); - src/server.ts:159-159 (schema)Defines the input schema (schSearchShape) for sch_search: q (string, required) and optional top and limit integers.
const schSearchShape = { q: z.string(), top: z.number().int().optional(), limit: z.number().int().optional() }; - src/tools/scholar.ts:70-77 (handler)The schSearch function that executes the search logic: concurrently queries arxivSearch, crossrefSearch, and wikiSearch, then merges and slices results.
export async function schSearch(q: string, top = 5) { const [ax, cr, wiki] = await Promise.all([ arxivSearch(q, top), crossrefSearch(q, top), wikiSearch(q, 'en', top) ]); return [...ax, ...cr, ...wiki].slice(0, top * 2); } - src/tools/scholar.ts:46-68 (helper)Helper function that searches arXiv API and returns scholarly article results.
export async function arxivSearch(q: string, top = 5) { const url = `http://export.arxiv.org/api/query?search_query=all:${encodeURIComponent(q)}&start=0&max_results=${top}`; const res = await fetchWithLimits(url, 10000, 1024*1024); if (!res.body) return []; const xml = res.body.toString('utf-8'); const parser = new XMLParser({ ignoreAttributes: false }); const data: any = parser.parse(xml); const entries = Array.isArray(data.feed?.entry) ? data.feed.entry : (data.feed?.entry ? [data.feed.entry] : []); return entries.map((e: any) => { const id = (e.id || '').split('/abs/')[1] || e.id || ''; const links = Array.isArray(e.link) ? e.link : [e.link]; const pdf = links.find((l:any) => l['@_type'] === 'application/pdf')?.['@_href'] || ''; return { title: (e.title || '').trim(), authors: (Array.isArray(e.author) ? e.author : [e.author]).map((a:any) => a.name), year: (e.published || '').slice(0,4), arxivId: id, pdfUrl: pdf, url: `https://arxiv.org/abs/${id}`, source: 'arxiv' }; }); } - src/tools/scholar.ts:31-44 (helper)Helper function that searches Crossref API and returns scholarly work results.
export async function crossrefSearch(q: string, top = 5) { const url = `https://api.crossref.org/works?query=${encodeURIComponent(q)}&rows=${top}`; const res = await fetchWithLimits(url, 8000, 1024*1024); if (!res.body) return []; const data = JSON.parse(res.body.toString('utf-8')); return (data.message.items || []).map((it: any) => ({ title: (it.title && it.title[0]) || '', authors: (it.author || []).map((a:any) => [a.given, a.family].filter(Boolean).join(' ')), year: (it.created?.['date-parts']?.[0]?.[0]) || (it.issued?.['date-parts']?.[0]?.[0]) || '', doi: it.DOI || '', url: it.URL || '', source: 'crossref' })); }