paper-search-cli 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/README.md +1 -1
  2. package/README.zh.md +1 -1
  3. package/dist/capabilities/body-snippet-search/handler.d.ts +9 -0
  4. package/dist/capabilities/body-snippet-search/handler.d.ts.map +1 -0
  5. package/dist/capabilities/body-snippet-search/handler.js +17 -0
  6. package/dist/capabilities/body-snippet-search/handler.js.map +1 -0
  7. package/dist/capabilities/body-snippet-search/index.d.ts +7 -0
  8. package/dist/capabilities/body-snippet-search/index.d.ts.map +1 -0
  9. package/dist/capabilities/body-snippet-search/index.js +6 -0
  10. package/dist/capabilities/body-snippet-search/index.js.map +1 -0
  11. package/dist/capabilities/body-snippet-search/schemas.d.ts +36 -0
  12. package/dist/capabilities/body-snippet-search/schemas.d.ts.map +1 -0
  13. package/dist/capabilities/body-snippet-search/schemas.js +16 -0
  14. package/dist/capabilities/body-snippet-search/schemas.js.map +1 -0
  15. package/dist/capabilities/body-snippet-search/service.d.ts +4 -0
  16. package/dist/capabilities/body-snippet-search/service.d.ts.map +1 -0
  17. package/dist/capabilities/body-snippet-search/service.js +4 -0
  18. package/dist/capabilities/body-snippet-search/service.js.map +1 -0
  19. package/dist/capabilities/body-snippet-search/tools.d.ts +187 -0
  20. package/dist/capabilities/body-snippet-search/tools.d.ts.map +1 -0
  21. package/dist/capabilities/body-snippet-search/tools.js +49 -0
  22. package/dist/capabilities/body-snippet-search/tools.js.map +1 -0
  23. package/dist/capabilities/body-snippet-search/types.d.ts +14 -0
  24. package/dist/capabilities/body-snippet-search/types.d.ts.map +1 -0
  25. package/dist/capabilities/body-snippet-search/types.js +2 -0
  26. package/dist/capabilities/body-snippet-search/types.js.map +1 -0
  27. package/dist/capabilities/citation-expansion/CitationService.d.ts +47 -0
  28. package/dist/capabilities/citation-expansion/CitationService.d.ts.map +1 -0
  29. package/dist/capabilities/citation-expansion/CitationService.js +243 -0
  30. package/dist/capabilities/citation-expansion/CitationService.js.map +1 -0
  31. package/dist/capabilities/citation-expansion/handler.d.ts +36 -0
  32. package/dist/capabilities/citation-expansion/handler.d.ts.map +1 -0
  33. package/dist/capabilities/citation-expansion/handler.js +44 -0
  34. package/dist/capabilities/citation-expansion/handler.js.map +1 -0
  35. package/dist/capabilities/citation-expansion/index.d.ts +6 -0
  36. package/dist/capabilities/citation-expansion/index.d.ts.map +1 -0
  37. package/dist/capabilities/citation-expansion/index.js +5 -0
  38. package/dist/capabilities/citation-expansion/index.js.map +1 -0
  39. package/dist/capabilities/citation-expansion/schemas.d.ts +28 -0
  40. package/dist/capabilities/citation-expansion/schemas.d.ts.map +1 -0
  41. package/dist/capabilities/citation-expansion/schemas.js +13 -0
  42. package/dist/capabilities/citation-expansion/schemas.js.map +1 -0
  43. package/dist/capabilities/citation-expansion/tools.d.ts +82 -0
  44. package/dist/capabilities/citation-expansion/tools.d.ts.map +1 -0
  45. package/dist/capabilities/citation-expansion/tools.js +48 -0
  46. package/dist/capabilities/citation-expansion/tools.js.map +1 -0
  47. package/dist/capabilities/citation-expansion/types.d.ts +22 -0
  48. package/dist/capabilities/citation-expansion/types.d.ts.map +1 -0
  49. package/dist/capabilities/citation-expansion/types.js +2 -0
  50. package/dist/capabilities/citation-expansion/types.js.map +1 -0
  51. package/dist/capabilities/journal-metrics/JournalMetricsService.d.ts +3 -0
  52. package/dist/capabilities/journal-metrics/JournalMetricsService.d.ts.map +1 -0
  53. package/dist/capabilities/journal-metrics/JournalMetricsService.js +142 -0
  54. package/dist/capabilities/journal-metrics/JournalMetricsService.js.map +1 -0
  55. package/dist/capabilities/journal-metrics/handler.d.ts +12 -0
  56. package/dist/capabilities/journal-metrics/handler.d.ts.map +1 -0
  57. package/dist/capabilities/journal-metrics/handler.js +38 -0
  58. package/dist/capabilities/journal-metrics/handler.js.map +1 -0
  59. package/dist/capabilities/journal-metrics/index.d.ts +6 -0
  60. package/dist/capabilities/journal-metrics/index.d.ts.map +1 -0
  61. package/dist/capabilities/journal-metrics/index.js +5 -0
  62. package/dist/capabilities/journal-metrics/index.js.map +1 -0
  63. package/dist/capabilities/journal-metrics/schemas.d.ts +28 -0
  64. package/dist/capabilities/journal-metrics/schemas.d.ts.map +1 -0
  65. package/dist/capabilities/journal-metrics/schemas.js +13 -0
  66. package/dist/capabilities/journal-metrics/schemas.js.map +1 -0
  67. package/dist/capabilities/journal-metrics/tools.d.ts +67 -0
  68. package/dist/capabilities/journal-metrics/tools.d.ts.map +1 -0
  69. package/dist/capabilities/journal-metrics/tools.js +27 -0
  70. package/dist/capabilities/journal-metrics/tools.js.map +1 -0
  71. package/dist/capabilities/journal-metrics/types.d.ts +36 -0
  72. package/dist/capabilities/journal-metrics/types.d.ts.map +1 -0
  73. package/dist/capabilities/journal-metrics/types.js +2 -0
  74. package/dist/capabilities/journal-metrics/types.js.map +1 -0
  75. package/dist/capabilities/metadata-search/MultiSourceSearchService.d.ts +9 -0
  76. package/dist/capabilities/metadata-search/MultiSourceSearchService.d.ts.map +1 -0
  77. package/dist/capabilities/metadata-search/MultiSourceSearchService.js +78 -0
  78. package/dist/capabilities/metadata-search/MultiSourceSearchService.js.map +1 -0
  79. package/dist/capabilities/metadata-search/handler.d.ts +23 -0
  80. package/dist/capabilities/metadata-search/handler.d.ts.map +1 -0
  81. package/dist/capabilities/metadata-search/handler.js +163 -0
  82. package/dist/capabilities/metadata-search/handler.js.map +1 -0
  83. package/dist/capabilities/metadata-search/index.d.ts +6 -0
  84. package/dist/capabilities/metadata-search/index.d.ts.map +1 -0
  85. package/dist/capabilities/metadata-search/index.js +5 -0
  86. package/dist/capabilities/metadata-search/index.js.map +1 -0
  87. package/dist/capabilities/metadata-search/schemas.d.ts +83 -0
  88. package/dist/capabilities/metadata-search/schemas.d.ts.map +1 -0
  89. package/dist/capabilities/metadata-search/schemas.js +81 -0
  90. package/dist/capabilities/metadata-search/schemas.js.map +1 -0
  91. package/dist/capabilities/metadata-search/tools.d.ts +178 -0
  92. package/dist/capabilities/metadata-search/tools.d.ts.map +1 -0
  93. package/dist/capabilities/metadata-search/tools.js +75 -0
  94. package/dist/capabilities/metadata-search/tools.js.map +1 -0
  95. package/dist/capabilities/metadata-search/types.d.ts +13 -0
  96. package/dist/capabilities/metadata-search/types.d.ts.map +1 -0
  97. package/dist/capabilities/metadata-search/types.js +2 -0
  98. package/dist/capabilities/metadata-search/types.js.map +1 -0
  99. package/dist/capabilities/pdf-discovery/DownloadTier.d.ts +22 -0
  100. package/dist/capabilities/pdf-discovery/DownloadTier.d.ts.map +1 -0
  101. package/dist/capabilities/pdf-discovery/DownloadTier.js +2 -0
  102. package/dist/capabilities/pdf-discovery/DownloadTier.js.map +1 -0
  103. package/dist/capabilities/pdf-discovery/OpenAccessFallbackService.d.ts +10 -0
  104. package/dist/capabilities/pdf-discovery/OpenAccessFallbackService.d.ts.map +1 -0
  105. package/dist/capabilities/pdf-discovery/OpenAccessFallbackService.js +53 -0
  106. package/dist/capabilities/pdf-discovery/OpenAccessFallbackService.js.map +1 -0
  107. package/dist/capabilities/pdf-discovery/handler.d.ts +19 -0
  108. package/dist/capabilities/pdf-discovery/handler.d.ts.map +1 -0
  109. package/dist/capabilities/pdf-discovery/handler.js +56 -0
  110. package/dist/capabilities/pdf-discovery/handler.js.map +1 -0
  111. package/dist/capabilities/pdf-discovery/index.d.ts +7 -0
  112. package/dist/capabilities/pdf-discovery/index.d.ts.map +1 -0
  113. package/dist/capabilities/pdf-discovery/index.js +5 -0
  114. package/dist/capabilities/pdf-discovery/index.js.map +1 -0
  115. package/dist/capabilities/pdf-discovery/schemas.d.ts +37 -0
  116. package/dist/capabilities/pdf-discovery/schemas.d.ts.map +1 -0
  117. package/dist/capabilities/pdf-discovery/schemas.js +22 -0
  118. package/dist/capabilities/pdf-discovery/schemas.js.map +1 -0
  119. package/dist/capabilities/pdf-discovery/tiers/directPdfUrl.d.ts +3 -0
  120. package/dist/capabilities/pdf-discovery/tiers/directPdfUrl.d.ts.map +1 -0
  121. package/dist/capabilities/pdf-discovery/tiers/directPdfUrl.js +27 -0
  122. package/dist/capabilities/pdf-discovery/tiers/directPdfUrl.js.map +1 -0
  123. package/dist/capabilities/pdf-discovery/tiers/primary.d.ts +3 -0
  124. package/dist/capabilities/pdf-discovery/tiers/primary.d.ts.map +1 -0
  125. package/dist/capabilities/pdf-discovery/tiers/primary.js +21 -0
  126. package/dist/capabilities/pdf-discovery/tiers/primary.js.map +1 -0
  127. package/dist/capabilities/pdf-discovery/tiers/repositories.d.ts +3 -0
  128. package/dist/capabilities/pdf-discovery/tiers/repositories.d.ts.map +1 -0
  129. package/dist/capabilities/pdf-discovery/tiers/repositories.js +35 -0
  130. package/dist/capabilities/pdf-discovery/tiers/repositories.js.map +1 -0
  131. package/dist/capabilities/pdf-discovery/tiers/scihub.d.ts +3 -0
  132. package/dist/capabilities/pdf-discovery/tiers/scihub.d.ts.map +1 -0
  133. package/dist/capabilities/pdf-discovery/tiers/scihub.js +21 -0
  134. package/dist/capabilities/pdf-discovery/tiers/scihub.js.map +1 -0
  135. package/dist/capabilities/pdf-discovery/tiers/unpaywall.d.ts +3 -0
  136. package/dist/capabilities/pdf-discovery/tiers/unpaywall.d.ts.map +1 -0
  137. package/dist/capabilities/pdf-discovery/tiers/unpaywall.js +29 -0
  138. package/dist/capabilities/pdf-discovery/tiers/unpaywall.js.map +1 -0
  139. package/dist/capabilities/pdf-discovery/tools.d.ts +114 -0
  140. package/dist/capabilities/pdf-discovery/tools.d.ts.map +1 -0
  141. package/dist/capabilities/pdf-discovery/tools.js +42 -0
  142. package/dist/capabilities/pdf-discovery/tools.js.map +1 -0
  143. package/dist/capabilities/pdf-discovery/types.d.ts +19 -0
  144. package/dist/capabilities/pdf-discovery/types.d.ts.map +1 -0
  145. package/dist/capabilities/pdf-discovery/types.js +2 -0
  146. package/dist/capabilities/pdf-discovery/types.js.map +1 -0
  147. package/dist/cli.js +6 -6
  148. package/dist/cli.js.map +1 -1
  149. package/dist/core/capabilityProfile.d.ts +1 -17
  150. package/dist/core/capabilityProfile.d.ts.map +1 -1
  151. package/dist/core/capabilityProfile.js +1 -166
  152. package/dist/core/capabilityProfile.js.map +1 -1
  153. package/dist/core/diagnostics.d.ts +1 -42
  154. package/dist/core/diagnostics.d.ts.map +1 -1
  155. package/dist/core/diagnostics.js +1 -585
  156. package/dist/core/diagnostics.js.map +1 -1
  157. package/dist/core/handleToolCall.d.ts +1 -0
  158. package/dist/core/handleToolCall.d.ts.map +1 -1
  159. package/dist/core/handleToolCall.js +265 -509
  160. package/dist/core/handleToolCall.js.map +1 -1
  161. package/dist/core/httpPolicies.d.ts +2 -0
  162. package/dist/core/httpPolicies.d.ts.map +1 -0
  163. package/dist/core/httpPolicies.js +2 -0
  164. package/dist/core/httpPolicies.js.map +1 -0
  165. package/dist/core/liveSmoke.d.ts +1 -41
  166. package/dist/core/liveSmoke.d.ts.map +1 -1
  167. package/dist/core/liveSmoke.js +1 -225
  168. package/dist/core/liveSmoke.js.map +1 -1
  169. package/dist/core/platformFactories.d.ts +2 -0
  170. package/dist/core/platformFactories.d.ts.map +1 -0
  171. package/dist/core/platformFactories.js +2 -0
  172. package/dist/core/platformFactories.js.map +1 -0
  173. package/dist/core/platformMetadata.d.ts +1 -27
  174. package/dist/core/platformMetadata.d.ts.map +1 -1
  175. package/dist/core/platformMetadata.js +1 -257
  176. package/dist/core/platformMetadata.js.map +1 -1
  177. package/dist/core/schemas.d.ts +10 -202
  178. package/dist/core/schemas.d.ts.map +1 -1
  179. package/dist/core/schemas.js +11 -118
  180. package/dist/core/schemas.js.map +1 -1
  181. package/dist/core/searchers.d.ts +24 -24
  182. package/dist/core/searchers.d.ts.map +1 -1
  183. package/dist/core/searchers.js +14 -79
  184. package/dist/core/searchers.js.map +1 -1
  185. package/dist/core/textReports.d.ts +1 -20
  186. package/dist/core/textReports.d.ts.map +1 -1
  187. package/dist/core/textReports.js +1 -84
  188. package/dist/core/textReports.js.map +1 -1
  189. package/dist/core/tools.d.ts.map +1 -1
  190. package/dist/core/tools.js +15 -237
  191. package/dist/core/tools.js.map +1 -1
  192. package/dist/infrastructure/cache/RequestCache.d.ts +26 -0
  193. package/dist/infrastructure/cache/RequestCache.d.ts.map +1 -0
  194. package/dist/infrastructure/cache/RequestCache.js +66 -0
  195. package/dist/infrastructure/cache/RequestCache.js.map +1 -0
  196. package/dist/infrastructure/http/ErrorHandler.d.ts +99 -0
  197. package/dist/infrastructure/http/ErrorHandler.d.ts.map +1 -0
  198. package/dist/infrastructure/http/ErrorHandler.js +266 -0
  199. package/dist/infrastructure/http/ErrorHandler.js.map +1 -0
  200. package/dist/infrastructure/http/HttpClient.d.ts +31 -0
  201. package/dist/infrastructure/http/HttpClient.d.ts.map +1 -0
  202. package/dist/infrastructure/http/HttpClient.js +50 -0
  203. package/dist/infrastructure/http/HttpClient.js.map +1 -0
  204. package/dist/infrastructure/pdf/PDFExtractor.d.ts +34 -0
  205. package/dist/infrastructure/pdf/PDFExtractor.d.ts.map +1 -0
  206. package/dist/infrastructure/pdf/PDFExtractor.js +130 -0
  207. package/dist/infrastructure/pdf/PDFExtractor.js.map +1 -0
  208. package/dist/infrastructure/pdf/PdfDownload.d.ts +7 -0
  209. package/dist/infrastructure/pdf/PdfDownload.d.ts.map +1 -0
  210. package/dist/infrastructure/pdf/PdfDownload.js +175 -0
  211. package/dist/infrastructure/pdf/PdfDownload.js.map +1 -0
  212. package/dist/infrastructure/rate-limit/RateLimiter.d.ts +50 -0
  213. package/dist/infrastructure/rate-limit/RateLimiter.d.ts.map +1 -0
  214. package/dist/infrastructure/rate-limit/RateLimiter.js +128 -0
  215. package/dist/infrastructure/rate-limit/RateLimiter.js.map +1 -0
  216. package/dist/infrastructure/security/SecurityUtils.d.ts +80 -0
  217. package/dist/infrastructure/security/SecurityUtils.d.ts.map +1 -0
  218. package/dist/infrastructure/security/SecurityUtils.js +357 -0
  219. package/dist/infrastructure/security/SecurityUtils.js.map +1 -0
  220. package/dist/management/capability-profile/index.d.ts +18 -0
  221. package/dist/management/capability-profile/index.d.ts.map +1 -0
  222. package/dist/management/capability-profile/index.js +167 -0
  223. package/dist/management/capability-profile/index.js.map +1 -0
  224. package/dist/management/config/index.d.ts +2 -0
  225. package/dist/management/config/index.d.ts.map +1 -0
  226. package/dist/management/config/index.js +2 -0
  227. package/dist/management/config/index.js.map +1 -0
  228. package/dist/management/diagnostics/index.d.ts +43 -0
  229. package/dist/management/diagnostics/index.d.ts.map +1 -0
  230. package/dist/management/diagnostics/index.js +586 -0
  231. package/dist/management/diagnostics/index.js.map +1 -0
  232. package/dist/management/reports/index.d.ts +21 -0
  233. package/dist/management/reports/index.d.ts.map +1 -0
  234. package/dist/management/reports/index.js +85 -0
  235. package/dist/management/reports/index.js.map +1 -0
  236. package/dist/management/skills/index.d.ts +2 -0
  237. package/dist/management/skills/index.d.ts.map +1 -0
  238. package/dist/management/skills/index.js +2 -0
  239. package/dist/management/skills/index.js.map +1 -0
  240. package/dist/management/smoke/index.d.ts +42 -0
  241. package/dist/management/smoke/index.d.ts.map +1 -0
  242. package/dist/management/smoke/index.js +226 -0
  243. package/dist/management/smoke/index.js.map +1 -0
  244. package/dist/registry/aliases.d.ts +2 -0
  245. package/dist/registry/aliases.d.ts.map +1 -0
  246. package/dist/registry/aliases.js +2 -0
  247. package/dist/registry/aliases.js.map +1 -0
  248. package/dist/registry/httpPolicies.d.ts +3 -0
  249. package/dist/registry/httpPolicies.d.ts.map +1 -0
  250. package/dist/registry/httpPolicies.js +17 -0
  251. package/dist/registry/httpPolicies.js.map +1 -0
  252. package/dist/registry/platformFactories.d.ts +8 -0
  253. package/dist/registry/platformFactories.d.ts.map +1 -0
  254. package/dist/registry/platformFactories.js +55 -0
  255. package/dist/registry/platformFactories.js.map +1 -0
  256. package/dist/registry/platformMetadata.d.ts +39 -0
  257. package/dist/registry/platformMetadata.d.ts.map +1 -0
  258. package/dist/registry/platformMetadata.js +321 -0
  259. package/dist/registry/platformMetadata.js.map +1 -0
  260. package/dist/services/CitationService.d.ts +2 -65
  261. package/dist/services/CitationService.d.ts.map +1 -1
  262. package/dist/services/CitationService.js +1 -242
  263. package/dist/services/CitationService.js.map +1 -1
  264. package/dist/services/JournalMetricsService.d.ts +2 -37
  265. package/dist/services/JournalMetricsService.d.ts.map +1 -1
  266. package/dist/services/JournalMetricsService.js +1 -141
  267. package/dist/services/JournalMetricsService.js.map +1 -1
  268. package/dist/services/MultiSourceSearchService.d.ts +2 -18
  269. package/dist/services/MultiSourceSearchService.d.ts.map +1 -1
  270. package/dist/services/MultiSourceSearchService.js +1 -77
  271. package/dist/services/MultiSourceSearchService.js.map +1 -1
  272. package/dist/services/OpenAccessFallbackService.d.ts +3 -40
  273. package/dist/services/OpenAccessFallbackService.d.ts.map +1 -1
  274. package/dist/services/OpenAccessFallbackService.js +1 -146
  275. package/dist/services/OpenAccessFallbackService.js.map +1 -1
  276. package/dist/utils/ErrorHandler.d.ts +2 -98
  277. package/dist/utils/ErrorHandler.d.ts.map +1 -1
  278. package/dist/utils/ErrorHandler.js +2 -265
  279. package/dist/utils/ErrorHandler.js.map +1 -1
  280. package/dist/utils/HttpClient.d.ts +1 -5
  281. package/dist/utils/HttpClient.d.ts.map +1 -1
  282. package/dist/utils/HttpClient.js +1 -29
  283. package/dist/utils/HttpClient.js.map +1 -1
  284. package/dist/utils/PDFExtractor.d.ts +2 -33
  285. package/dist/utils/PDFExtractor.d.ts.map +1 -1
  286. package/dist/utils/PDFExtractor.js +2 -129
  287. package/dist/utils/PDFExtractor.js.map +1 -1
  288. package/dist/utils/PdfDownload.d.ts +1 -6
  289. package/dist/utils/PdfDownload.d.ts.map +1 -1
  290. package/dist/utils/PdfDownload.js +1 -141
  291. package/dist/utils/PdfDownload.js.map +1 -1
  292. package/dist/utils/RateLimiter.d.ts +1 -49
  293. package/dist/utils/RateLimiter.d.ts.map +1 -1
  294. package/dist/utils/RateLimiter.js +1 -127
  295. package/dist/utils/RateLimiter.js.map +1 -1
  296. package/dist/utils/RequestCache.d.ts +2 -25
  297. package/dist/utils/RequestCache.d.ts.map +1 -1
  298. package/dist/utils/RequestCache.js +2 -65
  299. package/dist/utils/RequestCache.js.map +1 -1
  300. package/dist/utils/SecurityUtils.d.ts +2 -79
  301. package/dist/utils/SecurityUtils.d.ts.map +1 -1
  302. package/dist/utils/SecurityUtils.js +2 -356
  303. package/dist/utils/SecurityUtils.js.map +1 -1
  304. package/package.json +1 -1
@@ -1,13 +1,11 @@
1
1
  import { parseToolArgs } from './schemas.js';
2
2
  import { PaperFactory } from '../models/Paper.js';
3
- import { TIMEOUTS } from '../config/constants.js';
4
- import { logDebug } from '../utils/Logger.js';
5
- import { searchMultipleSources } from '../services/MultiSourceSearchService.js';
6
- import { downloadWithFallback } from '../services/OpenAccessFallbackService.js';
7
- import { queryJournalMetrics } from '../services/JournalMetricsService.js';
8
- import CitationService from '../services/CitationService.js';
9
- import { withTimeout } from '../utils/SecurityUtils.js';
10
- import { getGenericSearchToolPlatform, getPlatformMetadata, isPlatformAlias, resolvePlatformId } from './platformMetadata.js';
3
+ import { handleSemanticSnippets } from '../capabilities/body-snippet-search/handler.js';
4
+ import { handleGetPaperCitations, handleGetPaperReferences } from '../capabilities/citation-expansion/handler.js';
5
+ import { handleGenericSearch, handleGetPaperByDoi, handleSearchPapers } from '../capabilities/metadata-search/handler.js';
6
+ import { handleJournalMetrics } from '../capabilities/journal-metrics/handler.js';
7
+ import { handleDownloadPaper, handleDownloadWithFallback } from '../capabilities/pdf-discovery/handler.js';
8
+ import { getGenericSearchToolPlatform, isPlatformAlias } from './platformMetadata.js';
11
9
  function jsonTextResponse(text) {
12
10
  return {
13
11
  content: [
@@ -18,526 +16,284 @@ function jsonTextResponse(text) {
18
16
  ]
19
17
  };
20
18
  }
21
- const DOI_LOOKUP_SOURCES = [
22
- 'crossref',
23
- 'openalex',
24
- 'unpaywall',
25
- 'pubmed',
26
- 'pmc',
27
- 'europepmc',
28
- 'core',
29
- 'webofscience',
30
- 'arxiv'
31
- ];
32
- function parseJournalList(value) {
33
- if (!value)
34
- return [];
35
- if (Array.isArray(value))
36
- return value.map(item => String(item));
37
- return String(value)
38
- .split(/\r?\n|[,;;]/)
39
- .map(item => item.trim())
40
- .filter(Boolean);
41
- }
42
- function normalizeDoi(value) {
43
- return value
44
- .trim()
45
- .replace(/^https?:\/\/(?:dx\.)?doi\.org\//i, '')
46
- .toLowerCase();
47
- }
48
- function paperMatchesDoi(paper, doi) {
49
- return normalizeDoi(paper.doi || '') === normalizeDoi(doi);
50
- }
51
- function resolveCitationTarget(args) {
52
- if (args.paperId)
53
- return args.paperId;
54
- if (args.doi)
55
- return `DOI:${args.doi}`;
56
- if (args.arxivId)
57
- return `ARXIV:${args.arxivId}`;
58
- throw new Error('Provide paperId, doi, or arxivId');
59
- }
60
- function citationResponse(target, relation, papers) {
61
- return {
62
- target,
63
- relation,
64
- provider: 'semantic_scholar',
65
- total: papers.length,
66
- papers
67
- };
68
- }
69
- async function handleGenericSearch(platform, args, searchers) {
70
- const resolvedPlatform = resolvePlatformId(platform);
71
- const searcher = searchers[resolvedPlatform];
72
- if (!searcher) {
73
- throw new Error(`Unsupported platform: ${platform}`);
74
- }
75
- const { query, ...searchOptions } = args;
76
- const results = await searcher.search(query, searchOptions);
77
- const displayName = getPlatformMetadata(platform)?.displayName || platform;
78
- return jsonTextResponse(`Found ${results.length} ${displayName} papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
79
- }
80
- export async function handleToolCall(toolNameRaw, rawArgs, searchers) {
81
- const toolName = toolNameRaw;
82
- const args = parseToolArgs(toolName, rawArgs);
83
- const genericSearchPlatform = getGenericSearchToolPlatform(toolNameRaw);
84
- if (genericSearchPlatform) {
85
- return handleGenericSearch(genericSearchPlatform, args, searchers);
86
- }
87
- switch (toolName) {
88
- case 'search_papers': {
89
- const { query, platform, sources, maxResults, year, author, journal, category, days, fetchDetails, fieldsOfStudy, sortBy, sortOrder } = args;
90
- const results = [];
91
- const searchOptions = {
92
- maxResults,
93
- year,
94
- author,
95
- journal,
96
- category,
97
- days,
98
- fetchDetails,
99
- fieldsOfStudy,
100
- sortBy,
101
- sortOrder
102
- };
103
- if (platform === 'all') {
104
- const result = await searchMultipleSources(searchers, query, sources || 'all', searchOptions);
105
- return jsonTextResponse(`Found ${result.total} papers across ${result.sources_used.length} source(s).\n\n${JSON.stringify(result, null, 2)}`);
106
- }
107
- else if (sources) {
108
- const result = await searchMultipleSources(searchers, query, sources, searchOptions);
109
- return jsonTextResponse(`Found ${result.total} papers across ${result.sources_used.length} source(s).\n\n${JSON.stringify(result, null, 2)}`);
110
- }
111
- else {
112
- const resolvedPlatform = resolvePlatformId(platform);
113
- const searcher = searchers[resolvedPlatform];
114
- if (!searcher) {
115
- throw new Error(`Unsupported platform: ${platform}`);
116
- }
117
- const platformResults = await searcher.search(query, searchOptions);
118
- results.push(...platformResults.map((paper) => PaperFactory.toDict(paper)));
119
- }
120
- return jsonTextResponse(`Found ${results.length} papers.\n\n${JSON.stringify(results, null, 2)}`);
19
+ const TOOL_HANDLERS = {
20
+ search_papers: async (args, searchers) => handleSearchPapers(args, searchers),
21
+ search_arxiv: async (args, searchers) => {
22
+ const { query, maxResults, category, author, year, sortBy, sortOrder } = args;
23
+ const results = await searchers.arxiv.search(query, {
24
+ maxResults,
25
+ category,
26
+ author,
27
+ year,
28
+ sortBy,
29
+ sortOrder
30
+ });
31
+ return jsonTextResponse(`Found ${results.length} arXiv papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
32
+ },
33
+ search_webofscience: async (args, searchers) => {
34
+ const { query, maxResults, year, author, journal, sortBy, sortOrder } = args;
35
+ if (!process.env.WOS_API_KEY) {
36
+ throw new Error('Web of Science API key not configured. Please set WOS_API_KEY environment variable.');
121
37
  }
122
- case 'search_arxiv': {
123
- const { query, maxResults, category, author, year, sortBy, sortOrder } = args;
124
- const results = await searchers.arxiv.search(query, {
125
- maxResults,
126
- category,
127
- author,
128
- year,
129
- sortBy,
130
- sortOrder
131
- });
132
- return jsonTextResponse(`Found ${results.length} arXiv papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
38
+ const results = await searchers.webofscience.search(query, {
39
+ maxResults,
40
+ year,
41
+ author,
42
+ journal,
43
+ sortBy,
44
+ sortOrder
45
+ });
46
+ return jsonTextResponse(`Found ${results.length} Web of Science papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
47
+ },
48
+ search_pubmed: async (args, searchers) => {
49
+ const { query, maxResults, year, author, journal, publicationType, sortBy } = args;
50
+ const results = await searchers.pubmed.search(query, {
51
+ maxResults,
52
+ year,
53
+ author,
54
+ journal,
55
+ publicationType,
56
+ sortBy
57
+ });
58
+ const rateStatus = searchers.pubmed.getRateLimiterStatus();
59
+ const apiKeyStatus = searchers.pubmed.hasApiKey() ? 'configured' : 'not configured';
60
+ const rateLimit = searchers.pubmed.hasApiKey() ? '10 requests/second' : '3 requests/second';
61
+ return jsonTextResponse(`Found ${results.length} PubMed papers.\n\nAPI Status: ${apiKeyStatus} (${rateLimit})\nRate Limiter: ${rateStatus.availableTokens}/${rateStatus.maxTokens} tokens available\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
62
+ },
63
+ search_biorxiv: async (args, searchers) => {
64
+ const { query, maxResults, days, category } = args;
65
+ const results = await searchers.biorxiv.search(query, {
66
+ maxResults,
67
+ days,
68
+ category
69
+ });
70
+ return jsonTextResponse(`Found ${results.length} bioRxiv papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
71
+ },
72
+ search_medrxiv: async (args, searchers) => {
73
+ const { query, maxResults, days, category } = args;
74
+ const results = await searchers.medrxiv.search(query, {
75
+ maxResults,
76
+ days,
77
+ category
78
+ });
79
+ return jsonTextResponse(`Found ${results.length} medRxiv papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
80
+ },
81
+ search_semantic_scholar: async (args, searchers) => {
82
+ const { query, maxResults, year, fieldsOfStudy } = args;
83
+ const results = await searchers.semantic.search(query, {
84
+ maxResults,
85
+ year,
86
+ fieldsOfStudy
87
+ });
88
+ const rateStatus = searchers.semantic.getRateLimiterStatus();
89
+ const apiKeyStatus = searchers.semantic.hasApiKey()
90
+ ? 'configured'
91
+ : 'not configured (using free tier)';
92
+ const rateLimit = searchers.semantic.hasApiKey() ? '200 requests/minute' : '20 requests/minute';
93
+ return jsonTextResponse(`Found ${results.length} Semantic Scholar papers.\n\nAPI Status: ${apiKeyStatus} (${rateLimit})\nRate Limiter: ${rateStatus.availableTokens}/${rateStatus.maxTokens} tokens available\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
94
+ },
95
+ search_semantic_snippets: async (args, searchers) => handleSemanticSnippets(args, searchers),
96
+ get_paper_citations: async (args) => handleGetPaperCitations(args),
97
+ get_paper_references: async (args) => handleGetPaperReferences(args),
98
+ search_iacr: async (args, searchers) => {
99
+ const { query, maxResults, fetchDetails } = args;
100
+ const results = await searchers.iacr.search(query, { maxResults, fetchDetails });
101
+ return jsonTextResponse(`Found ${results.length} IACR ePrint papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
102
+ },
103
+ download_paper: async (args, searchers) => handleDownloadPaper(args, searchers),
104
+ download_with_fallback: async (args, searchers) => handleDownloadWithFallback(args, searchers),
105
+ query_journal_metrics: async (args) => handleJournalMetrics(args),
106
+ search_google_scholar: async (args, searchers) => {
107
+ const { query, maxResults, yearLow, yearHigh, author } = args;
108
+ const results = await searchers.googlescholar.search(query, {
109
+ maxResults,
110
+ yearLow,
111
+ yearHigh,
112
+ author
113
+ });
114
+ return jsonTextResponse(`Found ${results.length} Google Scholar papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
115
+ },
116
+ get_paper_by_doi: async (args, searchers) => handleGetPaperByDoi(args, searchers),
117
+ search_scihub: async (args, searchers) => {
118
+ const { doiOrUrl, downloadPdf, savePath } = args;
119
+ const resolvedSavePath = savePath || './downloads';
120
+ const results = await searchers.scihub.search(doiOrUrl);
121
+ if (results.length === 0) {
122
+ return jsonTextResponse(`No paper found on Sci-Hub for: ${doiOrUrl}`);
133
123
  }
134
- case 'search_webofscience': {
135
- const { query, maxResults, year, author, journal, sortBy, sortOrder } = args;
136
- if (!process.env.WOS_API_KEY) {
137
- throw new Error('Web of Science API key not configured. Please set WOS_API_KEY environment variable.');
138
- }
139
- const results = await searchers.webofscience.search(query, {
140
- maxResults,
141
- year,
142
- author,
143
- journal,
144
- sortBy,
145
- sortOrder
146
- });
147
- return jsonTextResponse(`Found ${results.length} Web of Science papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
148
- }
149
- case 'search_pubmed': {
150
- const { query, maxResults, year, author, journal, publicationType, sortBy } = args;
151
- const results = await searchers.pubmed.search(query, {
152
- maxResults,
153
- year,
154
- author,
155
- journal,
156
- publicationType,
157
- sortBy
158
- });
159
- const rateStatus = searchers.pubmed.getRateLimiterStatus();
160
- const apiKeyStatus = searchers.pubmed.hasApiKey() ? 'configured' : 'not configured';
161
- const rateLimit = searchers.pubmed.hasApiKey() ? '10 requests/second' : '3 requests/second';
162
- return jsonTextResponse(`Found ${results.length} PubMed papers.\n\nAPI Status: ${apiKeyStatus} (${rateLimit})\nRate Limiter: ${rateStatus.availableTokens}/${rateStatus.maxTokens} tokens available\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
163
- }
164
- case 'search_biorxiv': {
165
- const { query, maxResults, days, category } = args;
166
- const results = await searchers.biorxiv.search(query, {
167
- maxResults,
168
- days,
169
- category
170
- });
171
- return jsonTextResponse(`Found ${results.length} bioRxiv papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
172
- }
173
- case 'search_medrxiv': {
174
- const { query, maxResults, days, category } = args;
175
- const results = await searchers.medrxiv.search(query, {
176
- maxResults,
177
- days,
178
- category
179
- });
180
- return jsonTextResponse(`Found ${results.length} medRxiv papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
181
- }
182
- case 'search_semantic_scholar': {
183
- const { query, maxResults, year, fieldsOfStudy } = args;
184
- const results = await searchers.semantic.search(query, {
185
- maxResults,
186
- year,
187
- fieldsOfStudy
188
- });
189
- const rateStatus = searchers.semantic.getRateLimiterStatus();
190
- const apiKeyStatus = searchers.semantic.hasApiKey()
191
- ? 'configured'
192
- : 'not configured (using free tier)';
193
- const rateLimit = searchers.semantic.hasApiKey() ? '200 requests/minute' : '20 requests/minute';
194
- return jsonTextResponse(`Found ${results.length} Semantic Scholar papers.\n\nAPI Status: ${apiKeyStatus} (${rateLimit})\nRate Limiter: ${rateStatus.availableTokens}/${rateStatus.maxTokens} tokens available\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
195
- }
196
- case 'search_semantic_snippets': {
197
- const results = await searchers.semantic.searchSnippets(args);
198
- const bodyCount = results.filter(result => result.snippet.snippetKind === 'body').length;
199
- return jsonTextResponse(`Found ${results.length} Semantic Scholar snippet(s), including ${bodyCount} body snippet(s).\n\n${JSON.stringify(results, null, 2)}`);
200
- }
201
- case 'get_paper_citations': {
202
- const target = resolveCitationTarget(args);
203
- const service = new CitationService();
204
- const papers = await service.getCitations(target, args.limit);
205
- const result = citationResponse(target, 'citations', papers);
206
- return jsonTextResponse(`Found ${papers.length} citing paper(s).\n\n${JSON.stringify(result, null, 2)}`);
207
- }
208
- case 'get_paper_references': {
209
- const target = resolveCitationTarget(args);
210
- const service = new CitationService();
211
- const papers = await service.getReferences(target, args.limit);
212
- const result = citationResponse(target, 'references', papers);
213
- return jsonTextResponse(`Found ${papers.length} cited reference(s).\n\n${JSON.stringify(result, null, 2)}`);
214
- }
215
- case 'search_iacr': {
216
- const { query, maxResults, fetchDetails } = args;
217
- const results = await searchers.iacr.search(query, { maxResults, fetchDetails });
218
- return jsonTextResponse(`Found ${results.length} IACR ePrint papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
219
- }
220
- case 'download_paper': {
221
- const { paperId, platform, savePath } = args;
222
- const resolvedSavePath = savePath || './downloads';
223
- const resolvedPlatform = resolvePlatformId(platform);
224
- const searcher = searchers[resolvedPlatform];
225
- if (!searcher) {
226
- throw new Error(`Unsupported platform for download: ${platform}`);
227
- }
228
- if (!searcher.getCapabilities().download) {
229
- const result = await downloadWithFallback(searchers, {
230
- source: resolvedPlatform,
231
- paperId,
232
- doi: paperId,
233
- savePath: resolvedSavePath,
234
- useSciHub: true
235
- });
236
- if (result.status === 'ok') {
237
- return jsonTextResponse(`PDF downloaded successfully via fallback to: ${result.path}\n\n${JSON.stringify(result, null, 2)}`);
238
- }
239
- return jsonTextResponse(`PDF download failed via fallback.\n\n${JSON.stringify(result, null, 2)}`);
240
- }
124
+ const paper = results[0];
125
+ let responseText = `Found paper on Sci-Hub:\n\n${JSON.stringify(PaperFactory.toDict(paper), null, 2)}`;
126
+ if (downloadPdf && paper.pdfUrl) {
241
127
  try {
242
- const filePath = await searcher.downloadPdf(paperId, { savePath: resolvedSavePath });
243
- return jsonTextResponse(`PDF downloaded successfully to: ${filePath}`);
128
+ const filePath = await searchers.scihub.downloadPdf(doiOrUrl, { savePath: resolvedSavePath });
129
+ responseText += `\n\nPDF downloaded successfully to: ${filePath}`;
244
130
  }
245
- catch (error) {
246
- const result = await downloadWithFallback(searchers, {
247
- source: resolvedPlatform,
248
- paperId,
249
- doi: paperId,
250
- savePath: resolvedSavePath,
251
- useSciHub: true
252
- });
253
- if (result.status === 'ok') {
254
- return jsonTextResponse(`Primary download failed; PDF downloaded successfully via fallback to: ${result.path}\n\n${JSON.stringify(result, null, 2)}`);
255
- }
256
- throw error;
131
+ catch (downloadError) {
132
+ responseText += `\n\nFailed to download PDF: ${downloadError.message}`;
257
133
  }
258
134
  }
259
- case 'download_with_fallback': {
260
- const result = await downloadWithFallback(searchers, args);
261
- return jsonTextResponse(`Download with fallback ${result.status}.\n\n${JSON.stringify(result, null, 2)}`);
135
+ return jsonTextResponse(responseText);
136
+ },
137
+ check_scihub_mirrors: async (args, searchers) => {
138
+ const { forceCheck } = args;
139
+ if (forceCheck) {
140
+ await searchers.scihub.forceHealthCheck();
262
141
  }
263
- case 'query_journal_metrics': {
264
- const { readFileSync } = await import('fs');
265
- const journals = [
266
- ...parseJournalList(args.journal),
267
- ...parseJournalList(args.journals),
268
- ...(args.file
269
- ? readFileSync(String(args.file), 'utf8')
270
- .split(/\r?\n/)
271
- .map(line => line.trim())
272
- .filter(line => line && !line.startsWith('#'))
273
- : [])
274
- ];
275
- const rows = await queryJournalMetrics({ journals, includeRaw: args.includeRaw });
276
- const found = rows.filter(row => row.status === 'found').length;
277
- return jsonTextResponse(`Found journal metrics for ${found}/${rows.length} journal(s).\n\n${JSON.stringify(rows, null, 2)}`);
142
+ const mirrorStatus = searchers.scihub.getMirrorStatus();
143
+ return jsonTextResponse(`Sci-Hub Mirror Status:\n\n${JSON.stringify(mirrorStatus, null, 2)}`);
144
+ },
145
+ search_sciencedirect: async (args, searchers) => {
146
+ const { query, maxResults, year, author, journal, openAccess } = args;
147
+ if (!process.env.ELSEVIER_API_KEY) {
148
+ throw new Error('Elsevier API key not configured. Please set ELSEVIER_API_KEY environment variable.');
278
149
  }
279
- case 'search_google_scholar': {
280
- const { query, maxResults, yearLow, yearHigh, author } = args;
281
- const results = await searchers.googlescholar.search(query, {
282
- maxResults,
283
- yearLow,
284
- yearHigh,
285
- author
286
- });
287
- return jsonTextResponse(`Found ${results.length} Google Scholar papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
150
+ const results = await searchers.sciencedirect.search(query, {
151
+ maxResults,
152
+ year,
153
+ author,
154
+ journal,
155
+ openAccess
156
+ });
157
+ return jsonTextResponse(`Found ${results.length} ScienceDirect papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
158
+ },
159
+ search_springer: async (args, searchers) => {
160
+ const { query, maxResults, year, author, journal, subject, openAccess, type } = args;
161
+ if (!process.env.SPRINGER_API_KEY) {
162
+ throw new Error('Springer API key not configured. Please set SPRINGER_API_KEY environment variable.');
288
163
  }
289
- case 'get_paper_by_doi': {
290
- const { doi, platform } = args;
291
- const results = [];
292
- const sourceResults = {};
293
- const errors = {};
294
- const failedSources = [];
295
- if (platform === 'all') {
296
- const selected = DOI_LOOKUP_SOURCES.filter(source => source in searchers);
297
- const settled = await Promise.allSettled(selected.map(async (source) => {
298
- const searcher = searchers[source];
299
- const paper = await withTimeout(searcher.getPaperByDoi(doi), TIMEOUTS.SOURCE_TASK, `${source} DOI lookup timed out after ${TIMEOUTS.SOURCE_TASK}ms`);
300
- return { source, paper };
301
- }));
302
- for (let i = 0; i < settled.length; i += 1) {
303
- const source = selected[i];
304
- const result = settled[i];
305
- if (result.status === 'rejected') {
306
- sourceResults[source] = 0;
307
- errors[source] = result.reason?.message || String(result.reason);
308
- failedSources.push(source);
309
- logDebug(`Error getting paper by DOI from ${source}:`, result.reason);
310
- continue;
311
- }
312
- const paper = result.value.paper;
313
- if (!paper) {
314
- sourceResults[source] = 0;
315
- continue;
316
- }
317
- if (!paperMatchesDoi(paper, doi)) {
318
- sourceResults[source] = 0;
319
- errors[source] = `Returned DOI ${paper.doi || '(missing)'} did not match requested DOI ${doi}`;
320
- failedSources.push(source);
321
- continue;
322
- }
323
- sourceResults[source] = 1;
324
- results.push(PaperFactory.toDict(paper));
325
- }
326
- }
327
- else {
328
- const searcher = searchers[resolvePlatformId(platform)];
329
- if (!searcher) {
330
- throw new Error(`Unsupported platform: ${platform}`);
331
- }
332
- const paper = await searcher.getPaperByDoi(doi);
333
- if (paper) {
334
- results.push(PaperFactory.toDict(paper));
335
- }
336
- }
337
- if (results.length === 0) {
338
- if (platform === 'all') {
339
- const result = {
340
- doi,
341
- sources_requested: 'all',
342
- sources_used: DOI_LOOKUP_SOURCES.filter(source => source in searchers),
343
- source_results: sourceResults,
344
- errors,
345
- failed_sources: failedSources,
346
- warnings: failedSources.map(source => `${source}: ${errors[source]}`),
347
- total: 0,
348
- papers: []
349
- };
350
- return jsonTextResponse(`No paper found with DOI: ${doi}\n\n${JSON.stringify(result, null, 2)}`);
351
- }
352
- return jsonTextResponse(`No paper found with DOI: ${doi}`);
353
- }
354
- if (platform === 'all') {
355
- const result = {
356
- doi,
357
- sources_requested: 'all',
358
- sources_used: DOI_LOOKUP_SOURCES.filter(source => source in searchers),
359
- source_results: sourceResults,
360
- errors,
361
- failed_sources: failedSources,
362
- warnings: failedSources.map(source => `${source}: ${errors[source]}`),
363
- total: results.length,
364
- papers: results
365
- };
366
- return jsonTextResponse(`Found ${results.length} paper(s) with DOI ${doi} across ${result.sources_used.length} source(s).\n\n${JSON.stringify(result, null, 2)}`);
367
- }
368
- return jsonTextResponse(`Found ${results.length} paper(s) with DOI ${doi}:\n\n${JSON.stringify(results, null, 2)}`);
369
- }
370
- case 'search_scihub': {
371
- const { doiOrUrl, downloadPdf, savePath } = args;
372
- const resolvedSavePath = savePath || './downloads';
373
- const results = await searchers.scihub.search(doiOrUrl);
374
- if (results.length === 0) {
375
- return jsonTextResponse(`No paper found on Sci-Hub for: ${doiOrUrl}`);
376
- }
377
- const paper = results[0];
378
- let responseText = `Found paper on Sci-Hub:\n\n${JSON.stringify(PaperFactory.toDict(paper), null, 2)}`;
379
- if (downloadPdf && paper.pdfUrl) {
380
- try {
381
- const filePath = await searchers.scihub.downloadPdf(doiOrUrl, { savePath: resolvedSavePath });
382
- responseText += `\n\nPDF downloaded successfully to: ${filePath}`;
383
- }
384
- catch (downloadError) {
385
- responseText += `\n\nFailed to download PDF: ${downloadError.message}`;
386
- }
387
- }
388
- return jsonTextResponse(responseText);
389
- }
390
- case 'check_scihub_mirrors': {
391
- const { forceCheck } = args;
392
- if (forceCheck) {
393
- await searchers.scihub.forceHealthCheck();
394
- }
395
- const mirrorStatus = searchers.scihub.getMirrorStatus();
396
- return jsonTextResponse(`Sci-Hub Mirror Status:\n\n${JSON.stringify(mirrorStatus, null, 2)}`);
397
- }
398
- case 'search_sciencedirect': {
399
- const { query, maxResults, year, author, journal, openAccess } = args;
400
- if (!process.env.ELSEVIER_API_KEY) {
401
- throw new Error('Elsevier API key not configured. Please set ELSEVIER_API_KEY environment variable.');
402
- }
403
- const results = await searchers.sciencedirect.search(query, {
404
- maxResults,
405
- year,
406
- author,
407
- journal,
408
- openAccess
409
- });
410
- return jsonTextResponse(`Found ${results.length} ScienceDirect papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
411
- }
412
- case 'search_springer': {
413
- const { query, maxResults, year, author, journal, subject, openAccess, type } = args;
414
- if (!process.env.SPRINGER_API_KEY) {
415
- throw new Error('Springer API key not configured. Please set SPRINGER_API_KEY environment variable.');
416
- }
417
- const results = await searchers.springer.search(query, {
418
- maxResults,
419
- year,
420
- author,
421
- journal,
422
- subject,
423
- openAccess,
424
- type
425
- });
426
- return jsonTextResponse(`Found ${results.length} Springer papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
427
- }
428
- case 'search_wiley': {
429
- return jsonTextResponse(`DEPRECATED: Wiley TDM API does not support keyword search.\n\n` +
430
- `To access Wiley content:\n` +
431
- `1. Use search_crossref to find Wiley articles (filter by publisher if needed)\n` +
432
- `2. Use download_paper with platform="wiley" and the DOI to download the PDF\n\n` +
433
- `Example: download_paper(paperId="10.1111/jtsb.12390", platform="wiley")`);
164
+ const results = await searchers.springer.search(query, {
165
+ maxResults,
166
+ year,
167
+ author,
168
+ journal,
169
+ subject,
170
+ openAccess,
171
+ type
172
+ });
173
+ return jsonTextResponse(`Found ${results.length} Springer papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
174
+ },
175
+ search_wiley: async () => jsonTextResponse(`DEPRECATED: Wiley TDM API does not support keyword search.\n\n` +
176
+ `To access Wiley content:\n` +
177
+ `1. Use search_crossref to find Wiley articles (filter by publisher if needed)\n` +
178
+ `2. Use download_paper with platform="wiley" and the DOI to download the PDF\n\n` +
179
+ `Example: download_paper(paperId="10.1111/jtsb.12390", platform="wiley")`),
180
+ search_scopus: async (args, searchers) => {
181
+ const { query, maxResults, year, author, journal, affiliation, subject, openAccess, documentType } = args;
182
+ if (!process.env.ELSEVIER_API_KEY) {
183
+ throw new Error('Elsevier API key not configured. Please set ELSEVIER_API_KEY environment variable.');
434
184
  }
435
- case 'search_scopus': {
436
- const { query, maxResults, year, author, journal, affiliation, subject, openAccess, documentType } = args;
437
- if (!process.env.ELSEVIER_API_KEY) {
438
- throw new Error('Elsevier API key not configured. Please set ELSEVIER_API_KEY environment variable.');
439
- }
440
- const results = await searchers.scopus.search(query, {
441
- maxResults,
442
- year,
443
- author,
444
- journal,
445
- affiliation,
446
- subject,
447
- openAccess,
448
- documentType
449
- });
450
- return jsonTextResponse(`Found ${results.length} Scopus papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
451
- }
452
- case 'search_crossref': {
453
- const { query, maxResults, year, author, sortBy, sortOrder } = args;
454
- const results = await searchers.crossref.search(query, {
455
- maxResults,
456
- year,
457
- author,
458
- sortBy,
459
- sortOrder
460
- });
461
- return jsonTextResponse(`Found ${results.length} Crossref papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
462
- }
463
- case 'search_openalex': {
464
- const { query, maxResults, year } = args;
465
- const results = await searchers.openalex.search(query, { maxResults, year });
466
- return jsonTextResponse(`Found ${results.length} OpenAlex papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
467
- }
468
- case 'search_unpaywall': {
469
- const { query, maxResults } = args;
470
- const results = await searchers.unpaywall.search(query, { maxResults });
471
- return jsonTextResponse(`Found ${results.length} Unpaywall record(s).\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
472
- }
473
- case 'search_pmc': {
474
- const { query, maxResults, year } = args;
475
- const results = await searchers.pmc.search(query, { maxResults, year });
476
- return jsonTextResponse(`Found ${results.length} PMC papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
477
- }
478
- case 'search_europepmc': {
479
- const { query, maxResults, year } = args;
480
- const results = await searchers.europepmc.search(query, { maxResults, year });
481
- return jsonTextResponse(`Found ${results.length} Europe PMC papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
482
- }
483
- case 'search_core': {
484
- const { query, maxResults, year } = args;
485
- const results = await searchers.core.search(query, { maxResults, year });
486
- return jsonTextResponse(`Found ${results.length} CORE papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
487
- }
488
- case 'search_openaire': {
489
- const { query, maxResults, year } = args;
490
- const results = await searchers.openaire.search(query, { maxResults, year });
491
- return jsonTextResponse(`Found ${results.length} OpenAIRE papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
492
- }
493
- case 'get_platform_status': {
494
- const { validate } = args;
495
- const statusInfo = [];
496
- for (const [platformName, searcher] of Object.entries(searchers)) {
497
- if (isPlatformAlias(platformName))
498
- continue;
499
- const capabilities = searcher.getCapabilities();
500
- const hasApiKey = searcher.hasApiKey();
501
- let apiKeyStatus = 'not_required';
502
- if (capabilities.requiresApiKey) {
503
- if (hasApiKey) {
504
- if (validate) {
505
- try {
506
- const isValid = await searcher.validateApiKey();
507
- apiKeyStatus = isValid ? 'valid' : 'invalid';
508
- }
509
- catch {
510
- apiKeyStatus = 'unknown';
511
- }
185
+ const results = await searchers.scopus.search(query, {
186
+ maxResults,
187
+ year,
188
+ author,
189
+ journal,
190
+ affiliation,
191
+ subject,
192
+ openAccess,
193
+ documentType
194
+ });
195
+ return jsonTextResponse(`Found ${results.length} Scopus papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
196
+ },
197
+ search_crossref: async (args, searchers) => {
198
+ const { query, maxResults, year, author, sortBy, sortOrder } = args;
199
+ const results = await searchers.crossref.search(query, {
200
+ maxResults,
201
+ year,
202
+ author,
203
+ sortBy,
204
+ sortOrder
205
+ });
206
+ return jsonTextResponse(`Found ${results.length} Crossref papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
207
+ },
208
+ search_openalex: async (args, searchers) => {
209
+ const { query, maxResults, year } = args;
210
+ const results = await searchers.openalex.search(query, { maxResults, year });
211
+ return jsonTextResponse(`Found ${results.length} OpenAlex papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
212
+ },
213
+ search_unpaywall: async (args, searchers) => {
214
+ const { query, maxResults } = args;
215
+ const results = await searchers.unpaywall.search(query, { maxResults });
216
+ return jsonTextResponse(`Found ${results.length} Unpaywall record(s).\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
217
+ },
218
+ search_pmc: async (args, searchers) => {
219
+ const { query, maxResults, year } = args;
220
+ const results = await searchers.pmc.search(query, { maxResults, year });
221
+ return jsonTextResponse(`Found ${results.length} PMC papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
222
+ },
223
+ search_europepmc: async (args, searchers) => {
224
+ const { query, maxResults, year } = args;
225
+ const results = await searchers.europepmc.search(query, { maxResults, year });
226
+ return jsonTextResponse(`Found ${results.length} Europe PMC papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
227
+ },
228
+ search_core: async (args, searchers) => {
229
+ const { query, maxResults, year } = args;
230
+ const results = await searchers.core.search(query, { maxResults, year });
231
+ return jsonTextResponse(`Found ${results.length} CORE papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
232
+ },
233
+ search_openaire: async (args, searchers) => {
234
+ const { query, maxResults, year } = args;
235
+ const results = await searchers.openaire.search(query, { maxResults, year });
236
+ return jsonTextResponse(`Found ${results.length} OpenAIRE papers.\n\n${JSON.stringify(results.map((paper) => PaperFactory.toDict(paper)), null, 2)}`);
237
+ },
238
+ get_platform_status: async (args, searchers) => {
239
+ const { validate } = args;
240
+ const statusInfo = [];
241
+ for (const [platformName, searcher] of Object.entries(searchers)) {
242
+ if (isPlatformAlias(platformName))
243
+ continue;
244
+ const capabilities = searcher.getCapabilities();
245
+ const hasApiKey = searcher.hasApiKey();
246
+ let apiKeyStatus = 'not_required';
247
+ if (capabilities.requiresApiKey) {
248
+ if (hasApiKey) {
249
+ if (validate) {
250
+ try {
251
+ const isValid = await searcher.validateApiKey();
252
+ apiKeyStatus = isValid ? 'valid' : 'invalid';
512
253
  }
513
- else {
514
- apiKeyStatus = 'configured';
254
+ catch {
255
+ apiKeyStatus = 'unknown';
515
256
  }
516
257
  }
517
258
  else {
518
- apiKeyStatus = 'missing';
259
+ apiKeyStatus = 'configured';
519
260
  }
520
261
  }
521
- let additionalInfo = {};
522
- if (platformName === 'scihub') {
523
- const mirrorStatus = searchers.scihub.getMirrorStatus();
524
- additionalInfo = {
525
- mirrorCount: mirrorStatus.length,
526
- workingMirrors: mirrorStatus.filter(m => m.status === 'Working').length
527
- };
262
+ else {
263
+ apiKeyStatus = 'missing';
528
264
  }
529
- statusInfo.push({
530
- platform: platformName,
531
- baseUrl: searcher.getBaseUrl(),
532
- capabilities,
533
- apiKeyStatus,
534
- ...additionalInfo
535
- });
536
265
  }
537
- return jsonTextResponse(`Platform Status:\n\n${JSON.stringify(statusInfo, null, 2)}`);
266
+ let additionalInfo = {};
267
+ if (platformName === 'scihub') {
268
+ const mirrorStatus = searchers.scihub.getMirrorStatus();
269
+ additionalInfo = {
270
+ mirrorCount: mirrorStatus.length,
271
+ workingMirrors: mirrorStatus.filter(m => m.status === 'Working').length
272
+ };
273
+ }
274
+ statusInfo.push({
275
+ platform: platformName,
276
+ baseUrl: searcher.getBaseUrl(),
277
+ capabilities,
278
+ apiKeyStatus,
279
+ ...additionalInfo
280
+ });
538
281
  }
539
- default:
540
- throw new Error(`Unknown tool: ${toolNameRaw}`);
282
+ return jsonTextResponse(`Platform Status:\n\n${JSON.stringify(statusInfo, null, 2)}`);
283
+ }
284
+ };
285
+ export const TOOL_HANDLER_NAMES = Object.keys(TOOL_HANDLERS).sort();
286
+ export async function handleToolCall(toolNameRaw, rawArgs, searchers) {
287
+ const toolName = toolNameRaw;
288
+ const args = parseToolArgs(toolName, rawArgs);
289
+ const genericSearchPlatform = getGenericSearchToolPlatform(toolNameRaw);
290
+ if (genericSearchPlatform) {
291
+ return handleGenericSearch(genericSearchPlatform, args, searchers);
292
+ }
293
+ const handler = TOOL_HANDLERS[toolNameRaw];
294
+ if (!handler) {
295
+ throw new Error(`Unknown tool: ${toolNameRaw}`);
541
296
  }
297
+ return handler(args, searchers);
542
298
  }
543
299
  //# sourceMappingURL=handleToolCall.js.map