@pharmatools/pubcrawl 2.0.0

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 (80) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +111 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +45 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/lib/cache.d.ts +19 -0
  8. package/dist/lib/cache.d.ts.map +1 -0
  9. package/dist/lib/cache.js +52 -0
  10. package/dist/lib/cache.js.map +1 -0
  11. package/dist/lib/dailymed.d.ts +9 -0
  12. package/dist/lib/dailymed.d.ts.map +1 -0
  13. package/dist/lib/dailymed.js +58 -0
  14. package/dist/lib/dailymed.js.map +1 -0
  15. package/dist/lib/emc.d.ts +13 -0
  16. package/dist/lib/emc.d.ts.map +1 -0
  17. package/dist/lib/emc.js +215 -0
  18. package/dist/lib/emc.js.map +1 -0
  19. package/dist/lib/label-mapping.d.ts +8 -0
  20. package/dist/lib/label-mapping.d.ts.map +1 -0
  21. package/dist/lib/label-mapping.js +23 -0
  22. package/dist/lib/label-mapping.js.map +1 -0
  23. package/dist/lib/ncbi.d.ts +37 -0
  24. package/dist/lib/ncbi.d.ts.map +1 -0
  25. package/dist/lib/ncbi.js +175 -0
  26. package/dist/lib/ncbi.js.map +1 -0
  27. package/dist/lib/openfda.d.ts +8 -0
  28. package/dist/lib/openfda.d.ts.map +1 -0
  29. package/dist/lib/openfda.js +83 -0
  30. package/dist/lib/openfda.js.map +1 -0
  31. package/dist/lib/xml-parser.d.ts +23 -0
  32. package/dist/lib/xml-parser.d.ts.map +1 -0
  33. package/dist/lib/xml-parser.js +273 -0
  34. package/dist/lib/xml-parser.js.map +1 -0
  35. package/dist/tools/abstract.d.ts +3 -0
  36. package/dist/tools/abstract.d.ts.map +1 -0
  37. package/dist/tools/abstract.js +107 -0
  38. package/dist/tools/abstract.js.map +1 -0
  39. package/dist/tools/cite.d.ts +3 -0
  40. package/dist/tools/cite.d.ts.map +1 -0
  41. package/dist/tools/cite.js +188 -0
  42. package/dist/tools/cite.js.map +1 -0
  43. package/dist/tools/compare-labels.d.ts +3 -0
  44. package/dist/tools/compare-labels.d.ts.map +1 -0
  45. package/dist/tools/compare-labels.js +126 -0
  46. package/dist/tools/compare-labels.js.map +1 -0
  47. package/dist/tools/fulltext.d.ts +3 -0
  48. package/dist/tools/fulltext.d.ts.map +1 -0
  49. package/dist/tools/fulltext.js +88 -0
  50. package/dist/tools/fulltext.js.map +1 -0
  51. package/dist/tools/related.d.ts +3 -0
  52. package/dist/tools/related.d.ts.map +1 -0
  53. package/dist/tools/related.js +63 -0
  54. package/dist/tools/related.js.map +1 -0
  55. package/dist/tools/search-indication.d.ts +3 -0
  56. package/dist/tools/search-indication.d.ts.map +1 -0
  57. package/dist/tools/search-indication.js +73 -0
  58. package/dist/tools/search-indication.js.map +1 -0
  59. package/dist/tools/search.d.ts +3 -0
  60. package/dist/tools/search.d.ts.map +1 -0
  61. package/dist/tools/search.js +69 -0
  62. package/dist/tools/search.js.map +1 -0
  63. package/dist/tools/smpc.d.ts +3 -0
  64. package/dist/tools/smpc.d.ts.map +1 -0
  65. package/dist/tools/smpc.js +52 -0
  66. package/dist/tools/smpc.js.map +1 -0
  67. package/dist/tools/trending.d.ts +3 -0
  68. package/dist/tools/trending.d.ts.map +1 -0
  69. package/dist/tools/trending.js +106 -0
  70. package/dist/tools/trending.js.map +1 -0
  71. package/dist/tools/uspi.d.ts +3 -0
  72. package/dist/tools/uspi.d.ts.map +1 -0
  73. package/dist/tools/uspi.js +117 -0
  74. package/dist/tools/uspi.js.map +1 -0
  75. package/dist/types.d.ts +93 -0
  76. package/dist/types.d.ts.map +1 -0
  77. package/dist/types.js +2 -0
  78. package/dist/types.js.map +1 -0
  79. package/package.json +49 -0
  80. package/server.json +29 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 PharmaTools.AI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # PubCrawl
2
+
3
+ An [MCP server](https://modelcontextprotocol.io) that gives LLM clients access to PubMed, FDA drug labelling, and UK medicines data. A peer-reviewed pub crawl through the literature — and the label.
4
+
5
+ Built by [PharmaTools.AI](https://pharmatools.ai).
6
+
7
+ ## Tools
8
+
9
+ ### Literature
10
+
11
+ | Tool | What it does |
12
+ |------|-------------|
13
+ | `search_pubmed` | Search PubMed with filters for date range, article type, and sort order. Returns PMIDs, titles, authors, journals, and DOIs. |
14
+ | `get_abstract` | Get the full structured abstract for an article — broken into labeled sections (background, methods, results, conclusions) with keywords and MeSH terms. |
15
+ | `get_full_text` | Retrieve the full text of open-access articles from PubMed Central, with parsed sections, figure/table captions, and reference counts. |
16
+ | `find_related` | Find similar articles using PubMed's neighbor algorithm, ranked by relevance score. |
17
+ | `format_citation` | Generate a formatted citation in APA, Vancouver, Harvard, or BibTeX style. |
18
+ | `trending_papers` | Find recent papers on a topic, with optional filtering to high-impact journals (Nature, Science, Cell, NEJM, Lancet, JAMA, etc.). |
19
+
20
+ ### Drug Labelling
21
+
22
+ | Tool | What it does |
23
+ |------|-------------|
24
+ | `get_uspi` | Pull US Prescribing Information sections from DailyMed — indications, dosing, warnings, contraindications, and more. Parsed from FDA structured product labels. |
25
+ | `get_smpc` | Retrieve UK Summary of Product Characteristics from the eMC — the UK equivalent of US prescribing information, with numbered SmPC sections. |
26
+ | `compare_labels` | Side-by-side comparison of US (USPI) and UK (SmPC) labelling for the same drug. Spot regulatory differences in indications, warnings, and dosing. |
27
+ | `search_by_indication` | Find drugs approved for a medical condition. Searches FDA labelling via OpenFDA, then cross-references UK availability on the eMC. |
28
+
29
+ ## Setup
30
+
31
+ ### Prerequisites
32
+
33
+ - Node.js 18+
34
+ - An MCP-compatible client (Claude Desktop, Cursor, etc.)
35
+
36
+ ### Install and build
37
+
38
+ ```bash
39
+ git clone https://github.com/nickjlamb/pubcrawl.git
40
+ cd pubcrawl
41
+ npm install
42
+ npm run build
43
+ ```
44
+
45
+ ### Configure Claude Desktop
46
+
47
+ Add to your `claude_desktop_config.json`:
48
+
49
+ **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
50
+ **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
51
+
52
+ ```json
53
+ {
54
+ "mcpServers": {
55
+ "pubcrawl": {
56
+ "command": "node",
57
+ "args": ["/path/to/pubcrawl/dist/index.js"]
58
+ }
59
+ }
60
+ }
61
+ ```
62
+
63
+ Restart Claude Desktop. PubCrawl will appear under **+ → Connectors**.
64
+
65
+ ### NCBI API key (optional)
66
+
67
+ Without an API key, requests are rate-limited to 3/second. With one, you get 10/second.
68
+
69
+ 1. Create a free NCBI account at https://www.ncbi.nlm.nih.gov/account/
70
+ 2. Go to Account Settings → API Key Management
71
+ 3. Add the key to your config:
72
+
73
+ ```json
74
+ {
75
+ "mcpServers": {
76
+ "pubcrawl": {
77
+ "command": "node",
78
+ "args": ["/path/to/pubcrawl/dist/index.js"],
79
+ "env": {
80
+ "NCBI_API_KEY": "your_key_here"
81
+ }
82
+ }
83
+ }
84
+ }
85
+ ```
86
+
87
+ ## Example prompts
88
+
89
+ Once connected, just ask naturally:
90
+
91
+ - "Search PubMed for recent clinical trials on semaglutide"
92
+ - "Get the abstract for PMID 38127654"
93
+ - "Find papers related to this one and format citations in APA"
94
+ - "What are the trending papers on CRISPR gene therapy this month?"
95
+ - "Get the full text of that paper from PMC"
96
+ - "Get the FDA prescribing information for metformin — just the indications and warnings"
97
+ - "Pull the UK SmPC for atorvastatin"
98
+ - "Compare US and UK labelling for lisinopril"
99
+ - "What drugs are approved for type 2 diabetes?"
100
+
101
+ ## Development
102
+
103
+ ```bash
104
+ npm run dev # TypeScript watch mode
105
+ npm run build # Compile to dist/
106
+ npm start # Run the server
107
+ ```
108
+
109
+ ## License
110
+
111
+ MIT
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import "dotenv/config";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env node
2
+ import "dotenv/config";
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import { setApiKey } from "./lib/ncbi.js";
6
+ import { registerSearchTool } from "./tools/search.js";
7
+ import { registerAbstractTool } from "./tools/abstract.js";
8
+ import { registerFullTextTool } from "./tools/fulltext.js";
9
+ import { registerRelatedTool } from "./tools/related.js";
10
+ import { registerCiteTool } from "./tools/cite.js";
11
+ import { registerTrendingTool } from "./tools/trending.js";
12
+ import { registerUspiTool } from "./tools/uspi.js";
13
+ import { registerSmpcTool } from "./tools/smpc.js";
14
+ import { registerCompareLabelsTool } from "./tools/compare-labels.js";
15
+ import { registerSearchIndicationTool } from "./tools/search-indication.js";
16
+ const server = new McpServer({
17
+ name: "pubcrawl",
18
+ version: "2.0.0",
19
+ });
20
+ // Configure NCBI API key if available (10 req/s vs 3 req/s)
21
+ if (process.env.NCBI_API_KEY) {
22
+ setApiKey(process.env.NCBI_API_KEY);
23
+ }
24
+ // Register all tools
25
+ registerSearchTool(server);
26
+ registerAbstractTool(server);
27
+ registerFullTextTool(server);
28
+ registerRelatedTool(server);
29
+ registerCiteTool(server);
30
+ registerTrendingTool(server);
31
+ registerUspiTool(server);
32
+ registerSmpcTool(server);
33
+ registerCompareLabelsTool(server);
34
+ registerSearchIndicationTool(server);
35
+ // Start server
36
+ async function main() {
37
+ const transport = new StdioServerTransport();
38
+ await server.connect(transport);
39
+ console.error("PubCrawl MCP server running on stdio");
40
+ }
41
+ main().catch((error) => {
42
+ console.error("Fatal error:", error);
43
+ process.exit(1);
44
+ });
45
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AAE5E,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,4DAA4D;AAC5D,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC7B,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;AACtC,CAAC;AAED,qBAAqB;AACrB,kBAAkB,CAAC,MAAM,CAAC,CAAC;AAC3B,oBAAoB,CAAC,MAAM,CAAC,CAAC;AAC7B,oBAAoB,CAAC,MAAM,CAAC,CAAC;AAC7B,mBAAmB,CAAC,MAAM,CAAC,CAAC;AAC5B,gBAAgB,CAAC,MAAM,CAAC,CAAC;AACzB,oBAAoB,CAAC,MAAM,CAAC,CAAC;AAC7B,gBAAgB,CAAC,MAAM,CAAC,CAAC;AACzB,gBAAgB,CAAC,MAAM,CAAC,CAAC;AACzB,yBAAyB,CAAC,MAAM,CAAC,CAAC;AAClC,4BAA4B,CAAC,MAAM,CAAC,CAAC;AAErC,eAAe;AACf,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;AACxD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,19 @@
1
+ export declare const TTL: {
2
+ readonly SEARCH: number;
3
+ readonly ABSTRACT: number;
4
+ readonly FULLTEXT: number;
5
+ readonly RELATED: number;
6
+ readonly SUMMARY: number;
7
+ readonly LABEL: number;
8
+ };
9
+ export declare class LRUCache {
10
+ private cache;
11
+ private maxEntries;
12
+ constructor(maxEntries?: number);
13
+ get<T>(key: string): T | undefined;
14
+ set<T>(key: string, value: T, ttl: number): void;
15
+ clear(): void;
16
+ get size(): number;
17
+ }
18
+ export declare const cache: LRUCache;
19
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/lib/cache.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,GAAG;;;;;;;CAON,CAAC;AAEX,qBAAa,QAAQ;IACnB,OAAO,CAAC,KAAK,CAA0C;IACvD,OAAO,CAAC,UAAU,CAAS;gBAEf,UAAU,SAAsB;IAI5C,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAelC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAgBhD,KAAK,IAAI,IAAI;IAIb,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF;AAED,eAAO,MAAM,KAAK,UAAiB,CAAC"}
@@ -0,0 +1,52 @@
1
+ const DEFAULT_MAX_ENTRIES = 500;
2
+ const ONE_HOUR_MS = 60 * 60 * 1000;
3
+ const ONE_DAY_MS = 24 * 60 * 60 * 1000;
4
+ export const TTL = {
5
+ SEARCH: ONE_HOUR_MS,
6
+ ABSTRACT: ONE_DAY_MS,
7
+ FULLTEXT: ONE_DAY_MS,
8
+ RELATED: ONE_HOUR_MS,
9
+ SUMMARY: ONE_HOUR_MS,
10
+ LABEL: ONE_DAY_MS,
11
+ };
12
+ export class LRUCache {
13
+ cache = new Map();
14
+ maxEntries;
15
+ constructor(maxEntries = DEFAULT_MAX_ENTRIES) {
16
+ this.maxEntries = maxEntries;
17
+ }
18
+ get(key) {
19
+ const entry = this.cache.get(key);
20
+ if (!entry)
21
+ return undefined;
22
+ if (Date.now() - entry.timestamp > entry.ttl) {
23
+ this.cache.delete(key);
24
+ return undefined;
25
+ }
26
+ // Move to end (most recently used)
27
+ this.cache.delete(key);
28
+ this.cache.set(key, entry);
29
+ return entry.value;
30
+ }
31
+ set(key, value, ttl) {
32
+ if (this.cache.has(key)) {
33
+ this.cache.delete(key);
34
+ }
35
+ if (this.cache.size >= this.maxEntries) {
36
+ // Evict least recently used (first entry)
37
+ const firstKey = this.cache.keys().next().value;
38
+ if (firstKey !== undefined) {
39
+ this.cache.delete(firstKey);
40
+ }
41
+ }
42
+ this.cache.set(key, { value, timestamp: Date.now(), ttl });
43
+ }
44
+ clear() {
45
+ this.cache.clear();
46
+ }
47
+ get size() {
48
+ return this.cache.size;
49
+ }
50
+ }
51
+ export const cache = new LRUCache();
52
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/lib/cache.ts"],"names":[],"mappings":"AAEA,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAChC,MAAM,WAAW,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AACnC,MAAM,UAAU,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEvC,MAAM,CAAC,MAAM,GAAG,GAAG;IACjB,MAAM,EAAE,WAAW;IACnB,QAAQ,EAAE,UAAU;IACpB,QAAQ,EAAE,UAAU;IACpB,OAAO,EAAE,WAAW;IACpB,OAAO,EAAE,WAAW;IACpB,KAAK,EAAE,UAAU;CACT,CAAC;AAEX,MAAM,OAAO,QAAQ;IACX,KAAK,GAAG,IAAI,GAAG,EAA+B,CAAC;IAC/C,UAAU,CAAS;IAE3B,YAAY,UAAU,GAAG,mBAAmB;QAC1C,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;IAED,GAAG,CAAI,GAAW;QAChB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAE7B,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;YAC7C,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,mCAAmC;QACnC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACvB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC3B,OAAO,KAAK,CAAC,KAAU,CAAC;IAC1B,CAAC;IAED,GAAG,CAAI,GAAW,EAAE,KAAQ,EAAE,GAAW;QACvC,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACxB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;QAED,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACvC,0CAA0C;YAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;YAChD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;CACF;AAED,MAAM,CAAC,MAAM,KAAK,GAAG,IAAI,QAAQ,EAAE,CAAC"}
@@ -0,0 +1,9 @@
1
+ export interface DailyMedSearchResult {
2
+ setid: string;
3
+ title: string;
4
+ published_date: string;
5
+ spl_version: string;
6
+ }
7
+ export declare function searchDrugLabels(drugName: string): Promise<DailyMedSearchResult[]>;
8
+ export declare function fetchSplXml(setid: string): Promise<string>;
9
+ //# sourceMappingURL=dailymed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dailymed.d.ts","sourceRoot":"","sources":["../../src/lib/dailymed.ts"],"names":[],"mappings":"AAgCA,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAyBxF;AAED,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAWhE"}
@@ -0,0 +1,58 @@
1
+ import { cache, TTL } from "./cache.js";
2
+ const BASE_URL = "https://dailymed.nlm.nih.gov/dailymed/services/v2";
3
+ const REQUEST_DELAY_MS = 200;
4
+ const TIMEOUT_MS = 20000;
5
+ let lastRequestTime = 0;
6
+ async function rateLimitedFetch(url) {
7
+ const now = Date.now();
8
+ const elapsed = now - lastRequestTime;
9
+ if (elapsed < REQUEST_DELAY_MS) {
10
+ await new Promise((resolve) => setTimeout(resolve, REQUEST_DELAY_MS - elapsed));
11
+ }
12
+ lastRequestTime = Date.now();
13
+ const controller = new AbortController();
14
+ const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
15
+ try {
16
+ const response = await fetch(url, { signal: controller.signal });
17
+ if (!response.ok) {
18
+ throw new Error(`DailyMed API error: ${response.status} ${response.statusText}`);
19
+ }
20
+ return response;
21
+ }
22
+ finally {
23
+ clearTimeout(timeout);
24
+ }
25
+ }
26
+ export async function searchDrugLabels(drugName) {
27
+ const cacheKey = `dailymed:search:${drugName.toLowerCase()}`;
28
+ const cached = cache.get(cacheKey);
29
+ if (cached)
30
+ return cached;
31
+ const params = new URLSearchParams({
32
+ drug_name: drugName,
33
+ pagesize: "10",
34
+ });
35
+ const url = `${BASE_URL}/spls.json?${params}`;
36
+ const response = await rateLimitedFetch(url);
37
+ const data = await response.json();
38
+ const results = (data.data ?? []).map((item) => ({
39
+ setid: String(item.setid ?? ""),
40
+ title: String(item.title ?? ""),
41
+ published_date: String(item.published_date ?? ""),
42
+ spl_version: String(item.spl_version ?? ""),
43
+ }));
44
+ cache.set(cacheKey, results, TTL.SEARCH);
45
+ return results;
46
+ }
47
+ export async function fetchSplXml(setid) {
48
+ const cacheKey = `dailymed:spl:${setid}`;
49
+ const cached = cache.get(cacheKey);
50
+ if (cached)
51
+ return cached;
52
+ const url = `${BASE_URL}/spls/${setid}.xml`;
53
+ const response = await rateLimitedFetch(url);
54
+ const text = await response.text();
55
+ cache.set(cacheKey, text, TTL.LABEL);
56
+ return text;
57
+ }
58
+ //# sourceMappingURL=dailymed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dailymed.js","sourceRoot":"","sources":["../../src/lib/dailymed.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export interface EmcSearchResult {
2
+ product_id: string;
3
+ name: string;
4
+ company: string;
5
+ }
6
+ export declare function searchEmc(drugName: string): Promise<EmcSearchResult[]>;
7
+ export declare function fetchSmpcHtml(productId: string): Promise<string>;
8
+ export declare function parseSmpcSections(html: string, requestedSections?: string[]): Array<{
9
+ code: string;
10
+ title: string;
11
+ content: string;
12
+ }>;
13
+ //# sourceMappingURL=emc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emc.d.ts","sourceRoot":"","sources":["../../src/lib/emc.ts"],"names":[],"mappings":"AAqCA,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAqC5E;AAED,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAWtE;AA0CD,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,iBAAiB,CAAC,EAAE,MAAM,EAAE,GAC3B,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAsGzD"}
@@ -0,0 +1,215 @@
1
+ import * as cheerio from "cheerio";
2
+ import { cache, TTL } from "./cache.js";
3
+ const BASE_URL = "https://www.medicines.org.uk";
4
+ const REQUEST_DELAY_MS = 1000;
5
+ const TIMEOUT_MS = 20000;
6
+ const USER_AGENT = "PubCrawl/2.0 (pharmatools.ai; nick@pharmatools.ai)";
7
+ let lastRequestTime = 0;
8
+ async function rateLimitedFetch(url) {
9
+ const now = Date.now();
10
+ const elapsed = now - lastRequestTime;
11
+ if (elapsed < REQUEST_DELAY_MS) {
12
+ await new Promise((resolve) => setTimeout(resolve, REQUEST_DELAY_MS - elapsed));
13
+ }
14
+ lastRequestTime = Date.now();
15
+ const controller = new AbortController();
16
+ const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
17
+ try {
18
+ const response = await fetch(url, {
19
+ signal: controller.signal,
20
+ headers: { "User-Agent": USER_AGENT },
21
+ });
22
+ if (!response.ok) {
23
+ throw new Error(`eMC error: ${response.status} ${response.statusText}`);
24
+ }
25
+ return response;
26
+ }
27
+ finally {
28
+ clearTimeout(timeout);
29
+ }
30
+ }
31
+ export async function searchEmc(drugName) {
32
+ const cacheKey = `emc:search:${drugName.toLowerCase()}`;
33
+ const cached = cache.get(cacheKey);
34
+ if (cached)
35
+ return cached;
36
+ const params = new URLSearchParams({ q: drugName });
37
+ const url = `${BASE_URL}/emc/search?${params}`;
38
+ const response = await rateLimitedFetch(url);
39
+ const html = await response.text();
40
+ const $ = cheerio.load(html);
41
+ const results = [];
42
+ // Parse search results — look for SmPC links (href contains /smpc)
43
+ $("a[href*='/emc/product/']").each((_, el) => {
44
+ const href = $(el).attr("href") ?? "";
45
+ const smpcMatch = href.match(/\/emc\/product\/(\d+)\/smpc/);
46
+ if (!smpcMatch)
47
+ return;
48
+ const product_id = smpcMatch[1];
49
+ const name = $(el).text().trim();
50
+ // Skip generic link text like "Health Professionals (SmPC)"
51
+ if (!name || name.toLowerCase().includes("health professional"))
52
+ return;
53
+ // Avoid duplicates
54
+ if (results.some((r) => r.product_id === product_id))
55
+ return;
56
+ // Try to find company from nearby text
57
+ const parent = $(el).closest("li, tr, div, article");
58
+ const company = parent.find(".company, .manufacturer").text().trim();
59
+ results.push({ product_id, name, company });
60
+ });
61
+ cache.set(cacheKey, results, TTL.SEARCH);
62
+ return results;
63
+ }
64
+ export async function fetchSmpcHtml(productId) {
65
+ const cacheKey = `emc:smpc:${productId}`;
66
+ const cached = cache.get(cacheKey);
67
+ if (cached)
68
+ return cached;
69
+ const url = `${BASE_URL}/emc/product/${productId}/smpc`;
70
+ const response = await rateLimitedFetch(url);
71
+ const html = await response.text();
72
+ cache.set(cacheKey, html, TTL.LABEL);
73
+ return html;
74
+ }
75
+ // SmPC standard section numbering
76
+ const SMPC_SECTION_NAMES = {
77
+ "4.1": "Therapeutic indications",
78
+ "4.2": "Posology and method of administration",
79
+ "4.3": "Contraindications",
80
+ "4.4": "Special warnings and precautions for use",
81
+ "4.5": "Interaction with other medicinal products",
82
+ "4.6": "Fertility, pregnancy and lactation",
83
+ "4.7": "Effects on ability to drive and use machines",
84
+ "4.8": "Undesirable effects",
85
+ "4.9": "Overdose",
86
+ "5.1": "Pharmacodynamic properties",
87
+ "5.2": "Pharmacokinetic properties",
88
+ "5.3": "Preclinical safety data",
89
+ "6.1": "List of excipients",
90
+ "6.2": "Incompatibilities",
91
+ "6.3": "Shelf life",
92
+ "6.4": "Special precautions for storage",
93
+ "6.5": "Nature and contents of container",
94
+ "6.6": "Special precautions for disposal",
95
+ };
96
+ function htmlToText(html) {
97
+ const $ = cheerio.load(html);
98
+ // Replace <br> with newlines
99
+ $("br").replaceWith("\n");
100
+ // Replace <li> with bullet points
101
+ $("li").each((_, el) => {
102
+ $(el).prepend("- ");
103
+ $(el).append("\n");
104
+ });
105
+ // Add newlines after block elements
106
+ $("p, div, tr, h1, h2, h3, h4, h5, h6").each((_, el) => {
107
+ $(el).append("\n");
108
+ });
109
+ return $.text().replace(/\n{3,}/g, "\n\n").trim();
110
+ }
111
+ export function parseSmpcSections(html, requestedSections) {
112
+ const $ = cheerio.load(html);
113
+ const results = [];
114
+ const sectionElements = [];
115
+ // Strategy 1: Look for elements with IDs or classes containing section numbers
116
+ $("[id*='SECTION'], [id*='section'], .sectionHeading, h2, h3, h4").each((_, el) => {
117
+ const text = $(el).text().trim();
118
+ const match = text.match(/^(\d+\.?\d*)\s+(.+)/);
119
+ if (match) {
120
+ sectionElements.push({
121
+ code: match[1],
122
+ title: match[2].trim(),
123
+ element: $(el),
124
+ });
125
+ }
126
+ });
127
+ // Strategy 2: If strategy 1 found nothing, scan all text nodes for section headers
128
+ if (sectionElements.length === 0) {
129
+ $("*").each((_, el) => {
130
+ const $el = $(el);
131
+ if ($el.children().length > 0 && !$el.is("a, span, strong, em, b, i"))
132
+ return;
133
+ const text = $el.text().trim();
134
+ const match = text.match(/^(\d+\.?\d*)\s+(.+)/);
135
+ if (match && SMPC_SECTION_NAMES[match[1]]) {
136
+ sectionElements.push({
137
+ code: match[1],
138
+ title: match[2].trim(),
139
+ element: $el,
140
+ });
141
+ }
142
+ });
143
+ }
144
+ // Extract content between consecutive headings
145
+ for (let i = 0; i < sectionElements.length; i++) {
146
+ const current = sectionElements[i];
147
+ const next = sectionElements[i + 1];
148
+ // Collect all sibling content between this heading and the next
149
+ let content = "";
150
+ let node = current.element.next();
151
+ while (node.length > 0) {
152
+ if (next && node.is(next.element))
153
+ break;
154
+ const nodeHtml = $.html(node);
155
+ if (nodeHtml) {
156
+ content += htmlToText(nodeHtml) + "\n";
157
+ }
158
+ node = node.next();
159
+ }
160
+ // If no content from sibling traversal, try parent's content
161
+ if (!content.trim()) {
162
+ const parent = current.element.parent();
163
+ const parentHtml = $.html(parent);
164
+ if (parentHtml) {
165
+ const parentText = htmlToText(parentHtml);
166
+ // Remove the heading text itself
167
+ const headingText = current.element.text().trim();
168
+ content = parentText.replace(headingText, "").trim();
169
+ }
170
+ }
171
+ results.push({
172
+ code: current.code,
173
+ title: current.title,
174
+ content: content.trim(),
175
+ });
176
+ }
177
+ // Filter by requested sections if specified
178
+ if (requestedSections && requestedSections.length > 0) {
179
+ const filtered = results.filter((s) => {
180
+ return requestedSections.some((req) => {
181
+ const lower = req.toLowerCase().trim();
182
+ // Match by section number (e.g., "4.1")
183
+ if (s.code === lower || s.code === req)
184
+ return true;
185
+ // Match by name substring
186
+ if (s.title.toLowerCase().includes(lower))
187
+ return true;
188
+ if (lower.includes("indication"))
189
+ return s.code === "4.1";
190
+ if (lower.includes("dosage") || lower.includes("dosing") || lower.includes("posology"))
191
+ return s.code === "4.2";
192
+ if (lower.includes("contraindication"))
193
+ return s.code === "4.3";
194
+ if (lower.includes("warning") || lower.includes("precaution"))
195
+ return s.code === "4.4";
196
+ if (lower.includes("interaction"))
197
+ return s.code === "4.5";
198
+ if (lower.includes("pregnancy") || lower.includes("fertility"))
199
+ return s.code === "4.6";
200
+ if (lower.includes("adverse") || lower.includes("undesirable") || lower.includes("side effect"))
201
+ return s.code === "4.8";
202
+ if (lower.includes("overdose") || lower.includes("overdosage"))
203
+ return s.code === "4.9";
204
+ if (lower.includes("pharmacodynamic"))
205
+ return s.code === "5.1";
206
+ if (lower.includes("pharmacokinetic"))
207
+ return s.code === "5.2";
208
+ return false;
209
+ });
210
+ });
211
+ return filtered;
212
+ }
213
+ return results;
214
+ }
215
+ //# sourceMappingURL=emc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emc.js","sourceRoot":"","sources":["../../src/lib/emc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface SectionMapping {
2
+ topic: string;
3
+ us_loinc: string;
4
+ uk_code: string;
5
+ }
6
+ export declare const SECTION_MAP: SectionMapping[];
7
+ export declare function filterSectionMap(requestedTopics?: string[]): SectionMapping[];
8
+ //# sourceMappingURL=label-mapping.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"label-mapping.d.ts","sourceRoot":"","sources":["../../src/lib/label-mapping.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,WAAW,EAAE,cAAc,EAUvC,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,cAAc,EAAE,CAc7E"}
@@ -0,0 +1,23 @@
1
+ export const SECTION_MAP = [
2
+ { topic: "Indications", us_loinc: "34067-9", uk_code: "4.1" },
3
+ { topic: "Dosing", us_loinc: "34068-7", uk_code: "4.2" },
4
+ { topic: "Contraindications", us_loinc: "34070-3", uk_code: "4.3" },
5
+ { topic: "Warnings", us_loinc: "43685-7", uk_code: "4.4" },
6
+ { topic: "Drug Interactions", us_loinc: "34073-7", uk_code: "4.5" },
7
+ { topic: "Pregnancy", us_loinc: "42228-7", uk_code: "4.6" },
8
+ { topic: "Adverse Reactions", us_loinc: "34084-4", uk_code: "4.8" },
9
+ { topic: "Overdosage", us_loinc: "34088-5", uk_code: "4.9" },
10
+ { topic: "Clinical Pharmacology", us_loinc: "34090-1", uk_code: "5.1" },
11
+ ];
12
+ export function filterSectionMap(requestedTopics) {
13
+ if (!requestedTopics || requestedTopics.length === 0)
14
+ return SECTION_MAP;
15
+ return SECTION_MAP.filter((mapping) => requestedTopics.some((req) => {
16
+ const lower = req.toLowerCase().trim();
17
+ return (mapping.topic.toLowerCase().includes(lower) ||
18
+ lower.includes(mapping.topic.toLowerCase()) ||
19
+ mapping.us_loinc === req ||
20
+ mapping.uk_code === req);
21
+ }));
22
+ }
23
+ //# sourceMappingURL=label-mapping.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"label-mapping.js","sourceRoot":"","sources":["../../src/lib/label-mapping.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,MAAM,WAAW,GAAqB;IAC3C,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;IAC7D,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;IACxD,EAAE,KAAK,EAAE,mBAAmB,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;IACnE,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;IAC1D,EAAE,KAAK,EAAE,mBAAmB,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;IACnE,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;IAC3D,EAAE,KAAK,EAAE,mBAAmB,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;IACnE,EAAE,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;IAC5D,EAAE,KAAK,EAAE,uBAAuB,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;CACxE,CAAC;AAEF,MAAM,UAAU,gBAAgB,CAAC,eAA0B;IACzD,IAAI,CAAC,eAAe,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,WAAW,CAAC;IAEzE,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CACpC,eAAe,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;QAC3B,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;QACvC,OAAO,CACL,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC;YAC3C,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;YAC3C,OAAO,CAAC,QAAQ,KAAK,GAAG;YACxB,OAAO,CAAC,OAAO,KAAK,GAAG,CACxB,CAAC;IACJ,CAAC,CAAC,CACH,CAAC;AACJ,CAAC"}