@cyanheads/pubmed-mcp-server 1.0.14 → 1.1.1

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 (44) hide show
  1. package/README.md +183 -116
  2. package/dist/config/index.d.ts +8 -20
  3. package/dist/config/index.js +16 -45
  4. package/dist/index.js +77 -29
  5. package/dist/mcp-server/server.d.ts +3 -2
  6. package/dist/mcp-server/server.js +16 -14
  7. package/dist/mcp-server/tools/fetchPubMedContent/logic.js +2 -1
  8. package/dist/mcp-server/tools/generatePubMedChart/logic.d.ts +3 -3
  9. package/dist/mcp-server/tools/generatePubMedChart/logic.js +21 -10
  10. package/dist/mcp-server/tools/generatePubMedChart/registration.js +2 -2
  11. package/dist/mcp-server/tools/getPubMedArticleConnections/logic/citationFormatter.js +2 -1
  12. package/dist/mcp-server/tools/getPubMedArticleConnections/logic/elinkHandler.js +2 -1
  13. package/dist/mcp-server/tools/pubmedResearchAgent/logic/inputSchema.js +1 -1
  14. package/dist/mcp-server/tools/pubmedResearchAgent/logic/planOrchestrator.js +21 -10
  15. package/dist/mcp-server/tools/searchPubMedArticles/logic.js +2 -1
  16. package/dist/mcp-server/transports/authentication/authContext.d.ts +33 -0
  17. package/dist/mcp-server/transports/authentication/authContext.js +24 -0
  18. package/dist/mcp-server/transports/authentication/authMiddleware.d.ts +21 -15
  19. package/dist/mcp-server/transports/authentication/authMiddleware.js +51 -69
  20. package/dist/mcp-server/transports/authentication/authUtils.d.ts +18 -0
  21. package/dist/mcp-server/transports/authentication/authUtils.js +45 -0
  22. package/dist/mcp-server/transports/authentication/oauthMiddleware.d.ts +24 -0
  23. package/dist/mcp-server/transports/authentication/oauthMiddleware.js +109 -0
  24. package/dist/mcp-server/transports/authentication/types.d.ts +17 -0
  25. package/dist/mcp-server/transports/authentication/types.js +5 -0
  26. package/dist/mcp-server/transports/httpTransport.d.ts +5 -4
  27. package/dist/mcp-server/transports/httpTransport.js +177 -143
  28. package/dist/services/NCBI/ncbiCoreApiClient.js +0 -5
  29. package/dist/services/NCBI/ncbiRequestQueueManager.js +2 -4
  30. package/dist/services/NCBI/ncbiResponseHandler.js +0 -3
  31. package/dist/services/NCBI/ncbiService.d.ts +1 -1
  32. package/dist/services/NCBI/ncbiService.js +11 -4
  33. package/dist/utils/internal/logger.js +10 -30
  34. package/package.json +27 -13
  35. package/dist/services/index.d.ts +0 -7
  36. package/dist/services/index.js +0 -7
  37. package/dist/services/llm-providers/index.d.ts +0 -7
  38. package/dist/services/llm-providers/index.js +0 -7
  39. package/dist/services/llm-providers/llmFactory.d.ts +0 -69
  40. package/dist/services/llm-providers/llmFactory.js +0 -132
  41. package/dist/services/llm-providers/openRouter/index.d.ts +0 -6
  42. package/dist/services/llm-providers/openRouter/index.js +0 -7
  43. package/dist/services/llm-providers/openRouter/openRouterProvider.d.ts +0 -99
  44. package/dist/services/llm-providers/openRouter/openRouterProvider.js +0 -329
package/README.md CHANGED
@@ -1,16 +1,17 @@
1
- # PubMed MCP Server Pubmed
1
+ # PubMed MCP Server
2
2
 
3
3
  [![TypeScript](https://img.shields.io/badge/TypeScript-^5.8.3-blue.svg)](https://www.typescriptlang.org/)
4
- [![Model Context Protocol SDK](https://img.shields.io/badge/MCP%20SDK-1.12.0-green.svg)](https://github.com/modelcontextprotocol/typescript-sdk)
5
- [![MCP Spec Version](https://img.shields.io/badge/MCP%20Spec-2025--03--26-lightgrey.svg)](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/changelog.mdx)
6
- [![Version](https://img.shields.io/badge/Version-1.0.14-blue.svg)](./CHANGELOG.md)
4
+ [![Model Context Protocol](https://img.shields.io/badge/MCP%20SDK-^1.12.1-green.svg)](https://modelcontextprotocol.io/)
5
+ [![Version](https://img.shields.io/badge/Version-1.0.16-blue.svg)](./CHANGELOG.md)
7
6
  [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
8
- [![Status](https://img.shields.io/badge/Status-Beta-orange.svg)](https://github.com/cyanheads/pubmed-mcp-server/issues)
7
+ [![Status](https://img.shields.io/badge/Status-Stable-green.svg)](https://github.com/cyanheads/pubmed-mcp-server/issues)
9
8
  [![GitHub](https://img.shields.io/github/stars/cyanheads/pubmed-mcp-server?style=social)](https://github.com/cyanheads/pubmed-mcp-server)
10
9
 
11
- **Unlock the power of biomedical literature for your AI agents with the PubMed MCP Server!**
10
+ **Empower your AI agents and research tools with seamless PubMed integration!**
12
11
 
13
- This server acts as a bridge, connecting your AI to NCBI's PubMed and E-utilities through the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/). It empowers language models to seamlessly search, retrieve, and analyze biomedical articles and data. Built with TypeScript and adhering to the **MCP 2025-03-26 specification**, it's designed for robustness and includes production-grade utilities.
12
+ An MCP (Model Context Protocol) server providing comprehensive access to PubMed's biomedical literature database. Enables LLMs and AI agents to search, retrieve, analyze, and visualize scientific publications through NCBI's E-utilities API with advanced research workflow capabilities.
13
+
14
+ Built on the [`cyanheads/mcp-ts-template`](https://github.com/cyanheads/mcp-ts-template), this server follows a modular architecture with robust error handling, logging, and security features.
14
15
 
15
16
  ## 🚀 Core Capabilities: PubMed Tools 🛠️
16
17
 
@@ -22,156 +23,222 @@ This server equips your AI with specialized tools to interact with PubMed:
22
23
  | [`fetch_pubmed_content`](./src/mcp-server/tools/fetchPubMedContent/) | Retrieves detailed information for PubMed articles. Can use a list of PMIDs or ESearch history (queryKey/webEnv) with pagination. (See [Example](./examples/fetch_pubmed_content_example.md)) | - Flexible `detailLevel`: `abstract_plus` (parsed details, optional MeSH/grant), `full_xml` (JSON representation of the PubMedArticle XML structure), `medline_text` (MEDLINE format), `citation_data` (minimal for citations).<br/>- Supports direct PMID list or `queryKey`/`webEnv` from ESearch history.<br/>- Supports `retstart`/`retmax` for pagination with history.<br/>- Uses NCBI EFetch. |
23
24
  | [`get_pubmed_article_connections`](./src/mcp-server/tools/getPubMedArticleConnections/) | Finds related articles (cited by, similar, references) or formats citations for a PMID. (See [Ex.1](./examples/get_pubmed_article_connections_1.md), [Ex.2](./examples/get_pubmed_article_connections_2.md)) | - Uses NCBI ELink for relationships.<br/>- Uses NCBI EFetch for citation data (RIS, BibTeX, APA, MLA).<br/>- Filter by max related results. |
24
25
  | [`pubmed_research_agent`](./src/mcp-server/tools/pubmedResearchAgent/) | Generates a standardized JSON research plan outline from component details. (See [Example](./examples/pubmed_research_agent_example.md)) | - Accepts granular inputs for all research phases.<br/>- Optionally embeds instructive prompts for agent execution.<br/>- Structures rough ideas into a formal, machine-readable plan for further processing. |
25
- | [`generate_pubmed_chart`](./src/mcp-server/tools/generatePubMedChart/) | Generates a chart image (SVG) from given input data. (See [Bar](./examples/generate_pubmed_chart_example_bar.svg), [Line](./examples/generate_pubmed_chart_example_line.svg), [Scatter](./examples/generate_pubmed_chart_example_scatter.svg)) | - Supports 'bar', 'line', and 'scatter' chart types.<br/>- Takes data values and field specifications for axes and encoding.<br/>- Constructs a Vega-Lite specification internally and renders it as an SVG. |
26
+ | [`generate_pubmed_chart`](./src/mcp-server/tools/generatePubMedChart/) | Generates a chart image (PNG) from given input data. (See [Bar](./examples/generate_pubmed_chart_example_bar.svg), [Line](./examples/generate_pubmed_chart_example_line.svg), [Scatter](./examples/generate_pubmed_chart_example_scatter.svg)) | - Supports 'bar', 'line', and 'scatter' chart types.<br/>- Takes data values and field specifications for axes and encoding.<br/>- Constructs a Vega-Lite specification internally and renders it as a PNG. |
26
27
 
27
28
  ---
28
29
 
29
- ## Key Features Beyond Tools
30
+ ## Table of Contents
31
+
32
+ | [Overview](#overview) | [Features](#features) | [Installation](#installation) |
33
+
34
+ | [Configuration](#configuration) | [Project Structure](#project-structure) |
35
+
36
+ | [Tools](#tools) | [Resources](#resources) | [Development](#development) | [License](#license) |
37
+
38
+ ## Overview
39
+
40
+ The PubMed MCP Server acts as a bridge, allowing applications (MCP Clients) that understand the Model Context Protocol (MCP) – like advanced AI assistants (LLMs), IDE extensions, or custom research tools – to interact directly and efficiently with PubMed's vast biomedical literature database.
41
+
42
+ Instead of complex API integration or manual searches, your tools can leverage this server to:
43
+
44
+ - **Automate research workflows**: Search literature, fetch full article metadata, track citations, and generate research plans programmatically.
45
+ - **Gain research insights**: Access detailed publication data, author information, journal details, MeSH terms, and citation networks without leaving the host application.
46
+ - **Integrate PubMed into AI-driven research**: Enable LLMs to conduct literature reviews, analyze research trends, and support evidence-based decision making.
47
+ - **Visualize research data**: Generate charts and visualizations from publication metadata and search results.
48
+
49
+ Built on the robust `mcp-ts-template`, this server provides a standardized, secure, and efficient way to expose PubMed functionality via the MCP standard. It achieves this by integrating with NCBI's E-utilities API, ensuring compliance with rate limits and providing comprehensive error handling.
50
+
51
+ > **Developer Note**: This repository includes a [.clinerules](.clinerules) file that serves as a developer cheat sheet for your LLM coding agent with quick reference for the codebase patterns, file locations, and code snippets.
52
+
53
+ ## Features
54
+
55
+ ### Core Utilities
56
+
57
+ Leverages the robust utilities provided by the `mcp-ts-template`:
58
+
59
+ - **Logging**: Structured, configurable logging (file rotation, console, MCP notifications) with sensitive data redaction.
60
+ - **Error Handling**: Centralized error processing, standardized error types (`McpError`), and automatic logging.
61
+ - **Configuration**: Environment variable loading (`dotenv`) with comprehensive validation.
62
+ - **Input Validation/Sanitization**: Uses `zod` for schema validation and custom sanitization logic.
63
+ - **Request Context**: Tracking and correlation of operations via unique request IDs.
64
+ - **Type Safety**: Strong typing enforced by TypeScript and Zod schemas.
65
+ - **HTTP Transport Option**: Built-in Express server with SSE, session management, CORS support, and JWT authentication.
66
+ - **Rate Limiting**: Built-in request queuing and delay management for NCBI API compliance.
67
+
68
+ ### PubMed Integration
69
+
70
+ - **NCBI E-utilities Integration**: Comprehensive access to ESearch, EFetch, ELink, and ESummary APIs with automatic XML parsing.
71
+ - **Advanced Search Capabilities**: Complex query construction with date ranges, publication types, author filters, and MeSH term support.
72
+ - **Full Article Metadata**: Retrieve complete publication data including abstracts, authors, affiliations, journal information, DOIs, and citation data.
73
+ - **Citation Network Analysis**: Find related articles, citing articles, and reference lists through ELink integration.
74
+ - **Research Planning**: Generate structured research plans with automated literature search strategies.
75
+ - **Data Visualization**: Create PNG charts from publication metadata (bar charts, line graphs, scatter plots).
76
+ - **Multiple Output Formats**: Support for JSON, MEDLINE text, full XML, and formatted citations (RIS, BibTeX, APA, MLA).
77
+ - **Batch Processing**: Efficient handling of multiple PMIDs with pagination support.
78
+
79
+ ## Installation
80
+
81
+ ### Prerequisites
82
+
83
+ - [Node.js (>=18.0.0)](https://nodejs.org/)
84
+ - [npm](https://www.npmjs.com/) (comes with Node.js)
85
+ - **NCBI API Key** (recommended for higher rate limits) - [Get one here](https://ncbiinsights.ncbi.nlm.nih.gov/2017/11/02/new-api-keys-for-the-e-utilities/)
86
+
87
+ ### Install via npm (recommended)
88
+
89
+ ```bash
90
+ npm install @cyanheads/pubmed-mcp-server
91
+ ```
92
+
93
+ ### Alternatively Install from Source
30
94
 
31
- | Feature Category | Description |
32
- | :----------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
33
- | **🔌 MCP Compliant** | Fully functional server supporting `stdio` and `http` (SSE) transports. |
34
- | **🚀 Production-Ready Utils** | Includes robust logging, error handling, ID generation, rate limiting, request context tracking, and input sanitization. |
35
- | **🔒 Secure & Type-Safe** | Built with TypeScript & Zod for strong type checking and input validation. Manages NCBI API keys, implements rate limiting, and features JWT-based auth middleware for HTTP. |
36
- | **⚙️ Advanced Error Handling** | Consistent error categorization, detailed logging, and centralized handling, including specific error types for NCBI interactions. |
37
- | **📚 Well-Documented** | Comprehensive JSDoc comments, API references, and project specifications. |
38
- | **🤖 Agent-Friendly** | Includes a `.clinerules` developer cheatsheet tailored for LLM coding agents using this server. |
39
- | **🛠️ Developer Utilities** | Scripts for cleaning builds, setting executable permissions, generating directory trees, and fetching OpenAPI specifications. |
95
+ 1. Clone the repository:
40
96
 
41
- ## 🚀 Quick Start
97
+ ```bash
98
+ git clone https://github.com/cyanheads/pubmed-mcp-server.git
99
+ cd pubmed-mcp-server
100
+ ```
42
101
 
43
- Get the PubMed MCP server running in minutes:
102
+ 2. Install dependencies:
44
103
 
45
- 1. **Clone the repository:**
104
+ ```bash
105
+ npm install
106
+ ```
46
107
 
47
- ```bash
48
- git clone https://github.com/cyanheads/pubmed-mcp-server.git
49
- cd pubmed-mcp-server
50
- ```
108
+ 3. Build the project:
109
+ ```bash
110
+ npm run build
111
+ *or npm run rebuild*
112
+ ```
51
113
 
52
- 2. **Install dependencies:**
114
+ ## Configuration
53
115
 
54
- ```bash
55
- npm install
56
- ```
116
+ ### Environment Variables
57
117
 
58
- 3. **Configure Environment Variables (`.env` file):**
59
- Create a `.env` file in the project root. Key variables:
118
+ Configure the server using environment variables. These environmental variables are set within your MCP client config/settings (e.g. `claude_desktop_config.json` for Claude Desktop)
60
119
 
61
- ```env
62
- # REQUIRED FOR HTTP TRANSPORT
63
- MCP_AUTH_SECRET_KEY=generate_a_strong_random_32_plus_char_secret_key
120
+ | Variable | Description | Default |
121
+ | ---------------------- | ---------------------------------------------------------------------------------------- | ------------------------------ |
122
+ | `MCP_TRANSPORT_TYPE` | Transport mechanism: `stdio` or `http`. | `stdio` |
123
+ | `MCP_HTTP_PORT` | Port for the HTTP server (if `MCP_TRANSPORT_TYPE=http`). | `3010` |
124
+ | `MCP_HTTP_HOST` | Host address for the HTTP server (if `MCP_TRANSPORT_TYPE=http`). | `127.0.0.1` |
125
+ | `MCP_ALLOWED_ORIGINS` | Comma-separated list of allowed origins for CORS (if `MCP_TRANSPORT_TYPE=http`). | (none) |
126
+ | `MCP_LOG_LEVEL` | Logging level (`debug`, `info`, `notice`, `warning`, `error`, `crit`, `alert`, `emerg`). | `debug` |
127
+ | `MCP_AUTH_SECRET_KEY` | **Required for HTTP transport.** Minimum 32-character secret key for JWT authentication. | (none) |
128
+ | `NCBI_API_KEY` | **Recommended.** Your NCBI API Key for higher rate limits and reliable access. | (none) |
129
+ | `NCBI_TOOL_IDENTIFIER` | Tool identifier for NCBI E-utility requests. | `@cyanheads/pubmed-mcp-server` |
130
+ | `LOGS_DIR` | Directory for log file storage. | `logs/` |
64
131
 
65
- # RECOMMENDED FOR NCBI E-UTILITIES (for higher rate limits)
66
- # NCBI_API_KEY=your_ncbi_api_key_here
67
- # NCBI_ADMIN_EMAIL=your_email@example.com # Recommended if using an API key
68
- # NCBI_TOOL_IDENTIFIER=@cyanheads/pubmed-mcp-server/1.0.13 # Optional: Tool identifier for NCBI (defaults to current version)
69
- ```
132
+ ### MCP Client Settings
70
133
 
71
- For all options, see the [Configuration](#⚙️-configuration) section below or the [Developer Cheatsheet (.clinerules)](./.clinerules).
134
+ Add to your MCP client settings (e.g., `cline_mcp_settings.json`):
72
135
 
73
- New example files for each tool are available in the `examples/` directory (e.g., `examples/search_pubmed_articles_example.md`, `examples/generate_pubmed_chart_example_bar.svg`).
136
+ ```json
137
+ {
138
+ "mcpServers": {
139
+ "pubmed-mcp-server": {
140
+ "command": "node",
141
+ "args": ["/path/to/your/pubmed-mcp-server/dist/index.js"],
142
+ "env": {
143
+ "NCBI_API_KEY": "your_ncbi_api_key_here"
144
+ },
145
+ "disabled": false,
146
+ "autoApprove": []
147
+ }
148
+ }
149
+ }
150
+ ```
74
151
 
75
- 4. **Build the project:**
152
+ **Note**: You can see [mcp.json](mcp.json) for an example MCP client configuration file that includes the PubMed MCP Server.
76
153
 
77
- ```bash
78
- npm run build
79
- # Or use 'npm run rebuild' for a clean install (deletes node_modules, logs, dist)
80
- ```
154
+ ## Project Structure
81
155
 
82
- 5. **Format the code (Optional but Recommended):**
156
+ The codebase follows a modular structure within the `src/` directory:
83
157
 
84
- ```bash
85
- npm run format
86
- ```
158
+ ```
159
+ src/
160
+ ├── index.ts # Entry point: Initializes and starts the server
161
+ ├── config/ # Configuration loading (env vars, package info)
162
+ │ └── index.ts
163
+ ├── mcp-server/ # Core MCP server logic and capability registration
164
+ │ ├── server.ts # Server setup, capability registration
165
+ │ ├── transports/ # Transport handling (stdio, http)
166
+ │ ├── resources/ # MCP Resource implementations
167
+ │ └── tools/ # MCP Tool implementations (subdirs per tool)
168
+ ├── services/ # External service integrations
169
+ │ ├── NCBI/ # NCBI E-utilities API client and parsing
170
+ │ └── llm-providers/ # LLM provider integrations (optional)
171
+ ├── types-global/ # Shared TypeScript type definitions
172
+ └── utils/ # Common utility functions (logger, error handler, etc.)
173
+ ```
87
174
 
88
- 6. **Run the Server:**
175
+ For a detailed file tree, run `npm run tree` or see [docs/tree.md](docs/tree.md).
89
176
 
90
- - **Via Stdio (Default):** Many MCP host applications will run this automatically using `stdio`. To run manually for testing:
91
- ```bash
92
- npm start
93
- # or 'npm run start:stdio'
94
- ```
95
- - **Via HTTP (SSE):** (Ensure `MCP_TRANSPORT_TYPE=http` and `MCP_AUTH_SECRET_KEY` are set in your `.env`)
96
- ```bash
97
- npm run start:http
98
- ```
99
- This starts an HTTP server (default: `http://127.0.0.1:3010`) using Server-Sent Events.
177
+ ## Tools
100
178
 
101
- ## ⚙️ Configuration
179
+ The PubMed MCP Server provides a comprehensive suite of tools for biomedical literature research, callable via the Model Context Protocol.
102
180
 
103
- ### Server Configuration (Environment Variables)
181
+ | Tool Name | Description | Key Arguments |
182
+ | :------------------------------- | :--------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------- |
183
+ | `search_pubmed_articles` | Searches PubMed for articles using queries, filters, and date ranges. | `queryTerm`, `maxResults?`, `sortBy?`, `dateRange?`, `filterByPublicationTypes?`, `fetchBriefSummaries?` |
184
+ | `fetch_pubmed_content` | Fetches detailed article information using PMIDs or search history. | `pmids?`, `queryKey?`, `webEnv?`, `detailLevel?`, `includeMeshTerms?`, `includeGrantInfo?` |
185
+ | `get_pubmed_article_connections` | Finds related articles, citations, and references for a given PMID. | `sourcePmid`, `relationshipType?`, `maxRelatedResults?`, `citationStyles?` |
186
+ | `pubmed_research_agent` | Generates structured research plans with literature search strategies. | `project_title_suggestion`, `primary_research_goal`, `research_keywords`, `organism_focus?`, `p1_*`, etc. |
187
+ | `generate_pubmed_chart` | Creates customizable PNG charts from structured publication data. | `chartType`, `dataValues`, `xField`, `yField`, `title?`, `colorField?`, `seriesField?`, `sizeField?` |
104
188
 
105
- Configure the PubMed MCP server's behavior using environment variables (typically in a `.env` file).
189
+ _Note: All tools support comprehensive error handling and return structured JSON responses._
106
190
 
107
- | Variable | Description | Default |
108
- | :---------------------- | :----------------------------------------------------------------------------------------------------- | :--------------------------------------- |
109
- | `MCP_TRANSPORT_TYPE` | Server transport: `stdio` or `http`. | `stdio` |
110
- | `MCP_HTTP_PORT` | Port for the HTTP server (if `MCP_TRANSPORT_TYPE=http`). | `3010` |
111
- | `MCP_HTTP_HOST` | Host address for the HTTP server (if `MCP_TRANSPORT_TYPE=http`). | `127.0.0.1` |
112
- | `MCP_ALLOWED_ORIGINS` | Comma-separated allowed origins for CORS (if `MCP_TRANSPORT_TYPE=http`). | (none) |
113
- | `MCP_LOG_LEVEL` | Server logging level (`debug`, `info`, `warning`, `error`, etc.). | `debug` |
114
- | `LOGS_DIR` | Directory for log files. | `logs/` (in project root) |
115
- | `NODE_ENV` | Runtime environment (`development`, `production`). | `development` |
116
- | `MCP_AUTH_SECRET_KEY` | **Required for HTTP transport.** Secret key (min 32 chars) for signing/verifying auth tokens (JWT). | (none - **MUST be set in production**) |
117
- | `NCBI_API_KEY` | **Optional, but highly recommended.** Your NCBI API Key for higher rate limits (10/sec vs 3/sec). | (none) |
118
- | `NCBI_ADMIN_EMAIL` | **Optional, but recommended if using an API key.** Your email for NCBI contact. | (none) |
119
- | `NCBI_TOOL_IDENTIFIER` | Optional. Tool identifier sent to NCBI. | `@cyanheads/pubmed-mcp-server/<version>` |
120
- | `NCBI_REQUEST_DELAY_MS` | Milliseconds to wait between NCBI requests. Dynamically set (e.g., 100ms with API key, 334ms without). | (see `src/config/index.ts`) |
121
- | `NCBI_MAX_RETRIES` | Maximum number of retries for failed NCBI requests. | `3` |
191
+ ## Examples
122
192
 
123
- **Note on HTTP Port Retries:** If the `MCP_HTTP_PORT` is busy, the server automatically tries the next port (up to 15 times).
193
+ Comprehensive usage examples are available in the [`examples/`](examples/) directory:
124
194
 
125
- **Security Note for HTTP Transport:** When using `MCP_TRANSPORT_TYPE=http`, authentication is **mandatory** as per the MCP specification. This server includes JWT-based authentication middleware. You **MUST** set a strong, unique `MCP_AUTH_SECRET_KEY` in your production environment.
195
+ - [Search PubMed Articles](examples/search_pubmed_articles_example.md)
196
+ - [Fetch Article Content](examples/fetch_pubmed_content_example.md)
197
+ - [Article Connections](examples/get_pubmed_article_connections_1.md)
198
+ - [Research Planning](examples/pubmed_research_agent_example.md)
199
+ - [Chart Generation](examples/) - Generated chart examples (bar, line, scatter) are available in the `examples/` directory.
126
200
 
127
- For a **comprehensive list of all available environment variables**, their descriptions, and default values, please review the configuration loader at `src/config/index.ts`.
201
+ ## Development
128
202
 
129
- ## 🏗️ Project Structure Overview
203
+ ### Build and Test
130
204
 
131
- The `src/` directory contains the core logic:
205
+ ```bash
206
+ # Build the project (compile TS to JS in dist/ and make executable)
207
+ npm run build
132
208
 
133
- - `config/`: Environment variable loading and package information.
134
- - `mcp-server/`: The heart of the PubMed MCP server.
135
- - `server.ts`: Initializes the server instance and registers all tools and resources.
136
- - `resources/`: Implementations for MCP resources (e.g., server status, PubMed statistics).
137
- - `tools/`: Implementations for MCP tools (like `searchPubMedArticles`, `fetchPubMedContent`, `getPubMedArticleConnections`).
138
- - `transports/`: Handles `stdio` and `http` (SSE) communication, including authentication for HTTP.
139
- - `services/`: Integrations with external services.
140
- - `NCBI/ncbiService.ts`: Manages all interactions with NCBI E-utilities, including API calls, rate limiting, and response parsing.
141
- - `llm-providers/`: (Optional) For integrating LLM capabilities directly within the server.
142
- - `types-global/`: Shared TypeScript definitions, especially for errors and MCP types.
143
- - `utils/`: A comprehensive suite of reusable utilities for logging, error handling, security, parsing, metrics, and more.
209
+ # Test the server locally using the MCP inspector tool (stdio transport)
210
+ npm run inspector
144
211
 
145
- For a detailed file tree, run: `npm run tree` or see [Directory Tree](./docs/tree.md).
212
+ # Test the server locally using the MCP inspector tool (http transport)
213
+ npm run inspector:http
146
214
 
147
- ## 🧩 Extending with More PubMed Capabilities
215
+ # Clean build artifacts
216
+ npm run clean
148
217
 
149
- Adding new tools or resources is straightforward:
218
+ # Generate a file tree representation for documentation
219
+ npm run tree
220
+
221
+ # Clean build artifacts and then rebuild the project
222
+ npm run rebuild
150
223
 
151
- 1. **Directory Setup**: Create a new directory under `src/mcp-server/tools/yourNewTool/` or `src/mcp-server/resources/yourNewResource/`.
152
- 2. **Implement Core Logic (`logic.ts`)**:
153
- - Define Zod schemas for input validation.
154
- - Write the function that interacts with `src/services/NCBI/ncbiService.ts` (e.g., `ncbiService.eLink(...)`).
155
- - Parse the NCBI response and format it according to MCP specifications (`CallToolResult` or `ReadResourceResult`).
156
- 3. **Register the Capability (`registration.ts`)**:
157
- - For tools: `server.tool(name, description, zodSchemaShape, handlerFunction)`
158
- - For resources: `server.resource(registrationName, template, metadata, handlerFunction)`
159
- - Always wrap your logic in `ErrorHandler.tryCatch` for robust error management.
160
- 4. **Export and Integrate**: Export the registration function from your new directory's `index.ts` and call it within `src/mcp-server/server.ts`.
224
+ # Format code with Prettier
225
+ npm run format
161
226
 
162
- ## 🌍 Learn More
227
+ # Start the server using stdio (default)
228
+ npm start
229
+ # Or explicitly:
230
+ npm run start:stdio
163
231
 
164
- - **[Model Context Protocol Official Site](https://modelcontextprotocol.io/)**
165
- - **[MCP Specification (2025-03-26)](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/changelog.mdx)**
166
- - **[TypeScript SDK for MCP](https://github.com/modelcontextprotocol/typescript-sdk)**
167
- - **[NCBI E-utilities Documentation](https://www.ncbi.nlm.nih.gov/books/NBK25501/)**
232
+ # Start the server using HTTP transport
233
+ npm run start:http
234
+ ```
168
235
 
169
- ## 📜 License
236
+ ## License
170
237
 
171
- This project is licensed under the Apache License 2.0. See the [LICENSE](LICENSE) file for details.
238
+ This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.
172
239
 
173
240
  ---
174
241
 
175
242
  <div align="center">
176
- Empowering AI with PubMed | Built on the <a href="https://modelcontextprotocol.io/">Model Context Protocol</a>
243
+ Built with the <a href="https://modelcontextprotocol.io/">Model Context Protocol</a>
177
244
  </div>
@@ -39,26 +39,14 @@ export declare const config: {
39
39
  mcpAllowedOrigins: string[] | undefined;
40
40
  /** Auth secret key (JWTs, http transport). From `MCP_AUTH_SECRET_KEY`. CRITICAL. */
41
41
  mcpAuthSecretKey: string | undefined;
42
- /** OpenRouter App URL. From `OPENROUTER_APP_URL`. Default: "http://localhost:3000". */
43
- openrouterAppUrl: string;
44
- /** OpenRouter App Name. From `OPENROUTER_APP_NAME`. Defaults to `mcpServerName`. */
45
- openrouterAppName: string;
46
- /** OpenRouter API Key. From `OPENROUTER_API_KEY`. */
47
- openrouterApiKey: string | undefined;
48
- /** Default LLM model. From `LLM_DEFAULT_MODEL`. */
49
- llmDefaultModel: string;
50
- /** Default LLM temperature. From `LLM_DEFAULT_TEMPERATURE`. */
51
- llmDefaultTemperature: number | undefined;
52
- /** Default LLM top_p. From `LLM_DEFAULT_TOP_P`. */
53
- llmDefaultTopP: number | undefined;
54
- /** Default LLM max tokens. From `LLM_DEFAULT_MAX_TOKENS`. */
55
- llmDefaultMaxTokens: number | undefined;
56
- /** Default LLM top_k. From `LLM_DEFAULT_TOP_K`. */
57
- llmDefaultTopK: number | undefined;
58
- /** Default LLM min_p. From `LLM_DEFAULT_MIN_P`. */
59
- llmDefaultMinP: number | undefined;
60
- /** Gemini API Key. From `GEMINI_API_KEY`. */
61
- geminiApiKey: string | undefined;
42
+ /** Auth mode ('jwt' or 'oauth'). From `MCP_AUTH_MODE`. */
43
+ mcpAuthMode: "jwt" | "oauth";
44
+ /** OAuth Issuer URL. From `OAUTH_ISSUER_URL`. */
45
+ oauthIssuerUrl: string | undefined;
46
+ /** OAuth Audience. From `OAUTH_AUDIENCE`. */
47
+ oauthAudience: string | undefined;
48
+ /** OAuth JWKS URI. From `OAUTH_JWKS_URI`. */
49
+ oauthJwksUri: string | undefined;
62
50
  /** NCBI API Key. From `NCBI_API_KEY`. */
63
51
  ncbiApiKey: string | undefined;
64
52
  /** NCBI Tool Identifier. From `NCBI_TOOL_IDENTIFIER`. Defaults to server name/version. */
@@ -95,31 +95,14 @@ const EnvSchema = z.object({
95
95
  .string()
96
96
  .min(32, "MCP_AUTH_SECRET_KEY must be at least 32 characters long for security reasons.")
97
97
  .optional(),
98
- /** Optional. Application URL for OpenRouter integration. */
99
- OPENROUTER_APP_URL: z
100
- .string()
101
- .url("OPENROUTER_APP_URL must be a valid URL (e.g., http://localhost:3000)")
102
- .optional(),
103
- /** Optional. Application name for OpenRouter. Defaults to MCP_SERVER_NAME or package name. */
104
- OPENROUTER_APP_NAME: z.string().optional(),
105
- /** Optional. API key for OpenRouter services. */
106
- OPENROUTER_API_KEY: z.string().optional(),
107
- /** Default LLM model. Default: "google/gemini-2.5-flash-preview:thinking". */
108
- LLM_DEFAULT_MODEL: z
109
- .string()
110
- .default("google/gemini-2.5-flash-preview-05-20"),
111
- /** Optional. Default LLM temperature (0.0-2.0). */
112
- LLM_DEFAULT_TEMPERATURE: z.coerce.number().min(0).max(2).optional(),
113
- /** Optional. Default LLM top_p (0.0-1.0). */
114
- LLM_DEFAULT_TOP_P: z.coerce.number().min(0).max(1).optional(),
115
- /** Optional. Default LLM max tokens (positive integer). */
116
- LLM_DEFAULT_MAX_TOKENS: z.coerce.number().int().positive().optional(),
117
- /** Optional. Default LLM top_k (non-negative integer). */
118
- LLM_DEFAULT_TOP_K: z.coerce.number().int().nonnegative().optional(),
119
- /** Optional. Default LLM min_p (0.0-1.0). */
120
- LLM_DEFAULT_MIN_P: z.coerce.number().min(0).max(1).optional(),
121
- /** Optional. API key for Google Gemini services. */
122
- GEMINI_API_KEY: z.string().optional(),
98
+ /** Authentication mode ('jwt' or 'oauth'). Default: 'jwt'. */
99
+ MCP_AUTH_MODE: z.enum(["jwt", "oauth"]).default("jwt"),
100
+ /** OAuth: The expected issuer of the JWT. */
101
+ OAUTH_ISSUER_URL: z.string().url().optional(),
102
+ /** OAuth: The expected audience of the JWT. */
103
+ OAUTH_AUDIENCE: z.string().optional(),
104
+ /** OAuth: The URI of the JWKS endpoint. */
105
+ OAUTH_JWKS_URI: z.string().url().optional(),
123
106
  /** Optional. OAuth provider authorization endpoint URL. */
124
107
  OAUTH_PROXY_AUTHORIZATION_URL: z
125
108
  .string()
@@ -261,26 +244,14 @@ export const config = {
261
244
  .filter(Boolean),
262
245
  /** Auth secret key (JWTs, http transport). From `MCP_AUTH_SECRET_KEY`. CRITICAL. */
263
246
  mcpAuthSecretKey: env.MCP_AUTH_SECRET_KEY,
264
- /** OpenRouter App URL. From `OPENROUTER_APP_URL`. Default: "http://localhost:3000". */
265
- openrouterAppUrl: env.OPENROUTER_APP_URL || "http://localhost:3000",
266
- /** OpenRouter App Name. From `OPENROUTER_APP_NAME`. Defaults to `mcpServerName`. */
267
- openrouterAppName: env.OPENROUTER_APP_NAME || pkg.name || "MCP TS App",
268
- /** OpenRouter API Key. From `OPENROUTER_API_KEY`. */
269
- openrouterApiKey: env.OPENROUTER_API_KEY,
270
- /** Default LLM model. From `LLM_DEFAULT_MODEL`. */
271
- llmDefaultModel: env.LLM_DEFAULT_MODEL,
272
- /** Default LLM temperature. From `LLM_DEFAULT_TEMPERATURE`. */
273
- llmDefaultTemperature: env.LLM_DEFAULT_TEMPERATURE,
274
- /** Default LLM top_p. From `LLM_DEFAULT_TOP_P`. */
275
- llmDefaultTopP: env.LLM_DEFAULT_TOP_P,
276
- /** Default LLM max tokens. From `LLM_DEFAULT_MAX_TOKENS`. */
277
- llmDefaultMaxTokens: env.LLM_DEFAULT_MAX_TOKENS,
278
- /** Default LLM top_k. From `LLM_DEFAULT_TOP_K`. */
279
- llmDefaultTopK: env.LLM_DEFAULT_TOP_K,
280
- /** Default LLM min_p. From `LLM_DEFAULT_MIN_P`. */
281
- llmDefaultMinP: env.LLM_DEFAULT_MIN_P,
282
- /** Gemini API Key. From `GEMINI_API_KEY`. */
283
- geminiApiKey: env.GEMINI_API_KEY,
247
+ /** Auth mode ('jwt' or 'oauth'). From `MCP_AUTH_MODE`. */
248
+ mcpAuthMode: env.MCP_AUTH_MODE,
249
+ /** OAuth Issuer URL. From `OAUTH_ISSUER_URL`. */
250
+ oauthIssuerUrl: env.OAUTH_ISSUER_URL,
251
+ /** OAuth Audience. From `OAUTH_AUDIENCE`. */
252
+ oauthAudience: env.OAUTH_AUDIENCE,
253
+ /** OAuth JWKS URI. From `OAUTH_JWKS_URI`. */
254
+ oauthJwksUri: env.OAUTH_JWKS_URI,
284
255
  // NCBI Configuration
285
256
  /** NCBI API Key. From `NCBI_API_KEY`. */
286
257
  ncbiApiKey: env.NCBI_API_KEY,
package/dist/index.js CHANGED
@@ -22,16 +22,21 @@
22
22
  * @module src/index
23
23
  */
24
24
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
25
+ import http from "http"; // Import http module
25
26
  import { config, environment } from "./config/index.js";
26
27
  import { initializeAndStartServer } from "./mcp-server/server.js";
27
28
  import { requestContextService } from "./utils/index.js";
28
29
  import { logger } from "./utils/internal/logger.js";
29
30
  /**
30
31
  * Holds the main MCP server instance, primarily for STDIO transport.
31
- * For HTTP transport, server instances are typically managed per session.
32
32
  * @private
33
33
  */
34
- let server;
34
+ let mcpStdioServer;
35
+ /**
36
+ * Holds the Node.js HTTP server instance if HTTP transport is used.
37
+ * @private
38
+ */
39
+ let actualHttpServer;
35
40
  /**
36
41
  * Gracefully shuts down the main MCP server and associated resources.
37
42
  * Called on process termination signals or critical unhandled errors.
@@ -46,25 +51,70 @@ const shutdown = async (signal) => {
46
51
  triggerEvent: signal,
47
52
  });
48
53
  logger.info(`Received ${signal}. Initiating graceful shutdown...`, shutdownContext);
49
- try {
50
- if (server) {
51
- logger.info("Attempting to close main MCP server...", shutdownContext);
52
- await server.close();
53
- logger.info("Main MCP server closed successfully.", shutdownContext);
54
+ let mcpClosed = false;
55
+ let httpClosed = false;
56
+ let closeError = null;
57
+ const checkAndExit = () => {
58
+ if (closeError) {
59
+ logger.error("Critical error encountered during shutdown process.", {
60
+ ...shutdownContext,
61
+ errorMessage: closeError.message,
62
+ errorStack: closeError.stack,
63
+ });
64
+ process.exit(1);
54
65
  }
55
- else {
56
- logger.notice("No global server instance found to close during shutdown (this may be normal for HTTP transport).", shutdownContext);
66
+ else if (mcpClosed && httpClosed) {
67
+ logger.info("Graceful shutdown completed successfully. Exiting.", shutdownContext);
68
+ process.exit(0);
57
69
  }
58
- logger.info("Graceful shutdown completed successfully. Exiting.", shutdownContext);
59
- process.exit(0);
70
+ };
71
+ if (mcpStdioServer) {
72
+ logger.info("Attempting to close main MCP server (STDIO)...", shutdownContext);
73
+ mcpStdioServer
74
+ .close()
75
+ .then(() => {
76
+ logger.info("Main MCP server (STDIO) closed successfully.", shutdownContext);
77
+ mcpClosed = true;
78
+ checkAndExit();
79
+ })
80
+ .catch((err) => {
81
+ logger.error("Error closing MCP server (STDIO).", {
82
+ ...shutdownContext,
83
+ error: err,
84
+ });
85
+ mcpClosed = true; // Consider it closed even on error to allow exit
86
+ if (!closeError)
87
+ closeError = err;
88
+ checkAndExit();
89
+ });
60
90
  }
61
- catch (error) {
62
- logger.error("Critical error encountered during shutdown process.", {
63
- ...shutdownContext,
64
- errorMessage: error instanceof Error ? error.message : String(error),
65
- errorStack: error instanceof Error ? error.stack : undefined,
91
+ else {
92
+ mcpClosed = true; // No STDIO McpServer to close
93
+ }
94
+ if (actualHttpServer) {
95
+ logger.info("Attempting to close HTTP server...", shutdownContext);
96
+ actualHttpServer.close((err) => {
97
+ if (err) {
98
+ logger.error("Error closing HTTP server.", {
99
+ ...shutdownContext,
100
+ error: err,
101
+ });
102
+ if (!closeError)
103
+ closeError = err;
104
+ }
105
+ else {
106
+ logger.info("HTTP server closed successfully.", shutdownContext);
107
+ }
108
+ httpClosed = true;
109
+ checkAndExit();
66
110
  });
67
- process.exit(1);
111
+ }
112
+ else {
113
+ httpClosed = true; // No HTTP server to close
114
+ }
115
+ // Initial check in case no servers needed closing
116
+ if (mcpClosed && httpClosed) {
117
+ checkAndExit();
68
118
  }
69
119
  };
70
120
  /**
@@ -100,13 +150,6 @@ const start = async () => {
100
150
  }
101
151
  await logger.initialize(validatedMcpLogLevel);
102
152
  logger.info(`Logger has been initialized by start(). Effective MCP logging level set to: ${validatedMcpLogLevel}.`);
103
- // Configure RequestContextService once globally
104
- requestContextService.configure({
105
- appName: config.mcpServerName,
106
- appVersion: config.mcpServerVersion,
107
- environment,
108
- });
109
- logger.debug("RequestContextService configured with app name, version, and environment.");
110
153
  const transportType = config.mcpTransportType;
111
154
  const startupContext = requestContextService.createRequestContext({
112
155
  operation: `ServerStartupSequence_${transportType}`,
@@ -129,14 +172,19 @@ const start = async () => {
129
172
  logger.info(`Starting ${config.mcpServerName} (Version: ${config.mcpServerVersion}, Transport: ${transportType}, Env: ${environment})...`, startupContext);
130
173
  try {
131
174
  logger.debug("Calling initializeAndStartServer to set up MCP transport...", startupContext);
132
- const potentialServerInstance = await initializeAndStartServer();
133
- if (transportType === "stdio" &&
134
- potentialServerInstance instanceof McpServer) {
135
- server = potentialServerInstance;
175
+ const serverInstance = await initializeAndStartServer();
176
+ if (transportType === "stdio" && serverInstance instanceof McpServer) {
177
+ mcpStdioServer = serverInstance;
136
178
  logger.info("STDIO McpServer instance stored globally for shutdown.", startupContext);
137
179
  }
180
+ else if (transportType === "http" &&
181
+ serverInstance instanceof http.Server) {
182
+ actualHttpServer = serverInstance;
183
+ logger.info("HTTP transport initialized, http.Server instance stored globally for shutdown.", startupContext);
184
+ }
138
185
  else if (transportType === "http") {
139
- logger.info("HTTP transport initialized. Server lifecycle managed by HTTP listener and session handlers.", startupContext);
186
+ // This case should ideally not be reached if initializeAndStartServer correctly returns http.Server
187
+ logger.warning("HTTP transport initialized, but no http.Server instance was returned to index.ts. Shutdown might be incomplete.", startupContext);
140
188
  }
141
189
  logger.info(`${config.mcpServerName} is now running and ready to accept connections via ${transportType} transport.`, {
142
190
  ...startupContext,