Skip to main content
Glama
aminer-client.ts5.61 kB
/** * AMiner API Client */ import fetch from 'node-fetch'; import { AminerConfig, AminerSearchResponse, SearchParams, SearchResult, AminerPaper, Paper, SearchResultFormatted, ErrorResult } from './types.js'; export class AminerClient { private config: AminerConfig; constructor(config: AminerConfig) { this.config = config; } /** * Search papers */ async searchPapers(params: SearchParams): Promise<SearchResult> { // Validate required parameters if (!params.keyword && !params.venue && !params.author) { throw new Error('At least one of keyword, venue, or author must be provided'); } if (params.size > 10) { throw new Error('Size parameter cannot exceed 10'); } // Build query parameters const searchParams = new URLSearchParams(); if (params.keyword) searchParams.append('keyword', params.keyword); if (params.venue) searchParams.append('venue', params.venue); if (params.author) searchParams.append('author', params.author); searchParams.append('page', params.page.toString()); searchParams.append('size', params.size.toString()); if (params.order) searchParams.append('order', params.order); const url = `${this.config.baseUrl}?${searchParams.toString()}`; try { const response = await fetch(url, { method: 'GET', headers: { 'Authorization': this.config.apiKey, 'Content-Type': 'application/json', }, }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json() as AminerSearchResponse; // Add detailed response data check if (!data) { throw new Error('API returned empty response'); } if (!data.success) { throw new Error(`API Error (${data.code}): ${data.msg}`); } // Check the completeness of the response data if (typeof data.total !== 'number') { console.warn('API response missing or invalid total field, defaulting to 0'); } // Ensure data.data is not null, if it is null, use an empty array const papers = data.data || []; const total = data.total || 0; return { papers, total, page: params.page, size: params.size, hasMore: (params.page + 1) * params.size < total, }; } catch (error) { if (error instanceof Error) { throw new Error(`Failed to search papers: ${error.message}`); } throw new Error('Unknown error occurred while searching papers'); } } /** * Search papers by keyword */ async searchByKeyword(keyword: string, page = 0, size = 10, order?: 'year' | 'n_citation'): Promise<SearchResult> { return this.searchPapers({ keyword, page, size, order }); } /** * Search papers by venue */ async searchByVenue(venue: string, page = 0, size = 10, order?: 'year' | 'n_citation'): Promise<SearchResult> { return this.searchPapers({ venue, page, size, order }); } /** * Search papers by author */ async searchByAuthor(author: string, page = 0, size = 10, order?: 'year' | 'n_citation'): Promise<SearchResult> { return this.searchPapers({ author, page, size, order }); } /** * Format paper information as JSON */ formatPaper(paper: AminerPaper | null | undefined): Paper | ErrorResult { // Add empty value check if (!paper) { return { error: "Invalid Paper Data", message: "No paper information available." }; } const title = paper.title_zh || paper.title || 'N/A'; const authors = paper.authors && Array.isArray(paper.authors) ? paper.authors.map((author) => ({ name: author?.name_zh || author?.name || 'Unknown', org: author?.org || null })) : []; const venue = paper.venue ? { name_zh: paper.venue.name_zh || null, name_en: paper.venue.name_en || null, alias: paper.venue.alias || null } : null; const year = paper.year || null; const citations = paper.n_citation || 0; const abstract = paper.abstract_zh || paper.abstract || null; const doi = paper.doi || null; const url = paper.url || null; const keywords = paper.keywords_zh || paper.keywords || []; return { title, authors, venue, year, citations, abstract, doi, url, keywords, language: paper.language || null }; } /** * Format search results as JSON */ formatSearchResults(result: SearchResult): SearchResultFormatted { const { papers, total, page, size, hasMore } = result; // Ensure papers is not null or undefined const formattedPapers = papers && Array.isArray(papers) ? papers.map((paper, index) => { const formattedPaper = this.formatPaper(paper); // Only process successfully formatted papers, skip error results if ('error' in formattedPaper) { return null; } return { index: page * size + index + 1, ...formattedPaper }; }).filter((paper): paper is NonNullable<typeof paper> => paper !== null) : []; return { summary: { total, page: page + 1, size, hasMore, currentPageResults: formattedPapers.length }, papers: formattedPapers, pagination: { currentPage: page + 1, nextPage: hasMore ? page + 2 : null, previousPage: page > 0 ? page : null } }; } }

Implementation Reference

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/scipenai/aminer-mcp-server'

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