@cyanheads/pubmed-mcp-server 1.3.0 → 1.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.
package/README.md CHANGED
@@ -1,15 +1,19 @@
1
- # PubMed MCP Server
1
+ <div align="center">
2
2
 
3
- [![TypeScript](https://img.shields.io/badge/TypeScript-^5.8.3-blue.svg)](https://www.typescriptlang.org/)
4
- [![Model Context Protocol](https://img.shields.io/badge/MCP%20SDK-^1.17.0-green.svg)](https://modelcontextprotocol.io/)
5
- [![Version](https://img.shields.io/badge/Version-1.3.0-blue.svg)](./CHANGELOG.md)
6
- [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
7
- [![Status](https://img.shields.io/badge/Status-Stable-green.svg)](https://github.com/cyanheads/pubmed-mcp-server/issues)
8
- [![GitHub](https://img.shields.io/github/stars/cyanheads/pubmed-mcp-server?style=social)](https://github.com/cyanheads/pubmed-mcp-server)
3
+ # pubmed-mcp-server
9
4
 
10
5
  **Empower your AI agents and research tools with seamless PubMed integration!**
11
6
 
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.
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-^5.8.3-blue.svg?style=flat-square)](https://www.typescriptlang.org/)
8
+ [![Model Context Protocol](https://img.shields.io/badge/MCP%20SDK-^1.17.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/)
9
+ [![Version](https://img.shields.io/badge/Version-1.3.2-blue.svg?style=flat-square)](./CHANGELOG.md)
10
+ [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=flat-square)](https://opensource.org/licenses/Apache-2.0)
11
+ [![Status](https://img.shields.io/badge/Status-Stable-green.svg?style=flat-square)](https://github.com/cyanheads/pubmed-mcp-server/issues)
12
+ [![GitHub](https://img.shields.io/github/stars/cyanheads/pubmed-mcp-server?style=social)](https://github.com/cyanheads/pubmed-mcp-server)
13
+
14
+ </div>
15
+
16
+ Model Context Protocol (MCP) 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
17
 
14
18
  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.
15
19
 
@@ -17,23 +21,22 @@ Built on the [`cyanheads/mcp-ts-template`](https://github.com/cyanheads/mcp-ts-t
17
21
 
18
22
  This server equips your AI with specialized tools to interact with PubMed:
19
23
 
20
- | Tool Name | Description | Key Features |
21
- | :-------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
22
- | [`search_pubmed_articles`](./src/mcp-server/tools/searchPubMedArticles/) | Searches PubMed for articles based on your query. (See [Example](./examples/search_pubmed_articles_example.md)) | - Filter by max results, sort order, date range, publication types.<br/>- Uses NCBI ESearch for PMIDs.<br/>- Optionally fetches brief summaries (title, authors, source, dates) via ESummary. |
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. |
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. |
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. |
26
- | [`generate_pubmed_chart`](./src/mcp-server/tools/generatePubMedChart/) | Generates a chart image (PNG) from given input data. (See [Examples](./examples/generate_pubmed_chart)) | - Supports a wide range of chart types: `bar`, `line`, `scatter`, `pie`, `doughnut`, `bubble`, `radar`, and `polarArea`.<br/>- Takes data values and field specifications for axes and series.<br/>- Uses `Chart.js` and `chartjs-node-canvas` for direct server-side rendering. |
24
+ | Tool Name | Description | Example |
25
+ | :-------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------- |
26
+ | [`search_pubmed_articles`](./src/mcp-server/tools/searchPubMedArticles/) | Searches PubMed for articles based on your query. | [View Example](./examples/search_pubmed_articles_example.md) |
27
+ | [`fetch_pubmed_content`](./src/mcp-server/tools/fetchPubMedContent/) | Retrieves detailed information for PubMed articles. | [View Example](./examples/fetch_pubmed_content_example.md) |
28
+ | [`get_pubmed_article_connections`](./src/mcp-server/tools/getPubMedArticleConnections/) | Finds related articles (cited by, similar, references) or formats citations for a PMID. | [Ex. 1](./examples/get_pubmed_article_connections_1.md), [Ex. 2](./examples/get_pubmed_article_connections_2.md) |
29
+ | [`pubmed_research_agent`](./src/mcp-server/tools/pubmedResearchAgent/) | Generates a standardized JSON research plan outline from component details. | [View Example](./examples/pubmed_research_agent_example.md) |
30
+ | [`generate_pubmed_chart`](./src/mcp-server/tools/generatePubMedChart/) | Generates a chart image (PNG) from given input data. | [View Examples](./examples/generate_pubmed_chart/) |
27
31
 
28
32
  ---
29
33
 
30
34
  ## Table of Contents
31
35
 
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) |
36
+ | [Overview](#overview) | [Features](#features) | [Installation](#installation) |
37
+ | :------------------------------ | :--------------------------------------------- | :---------------------------- |
38
+ | [Configuration](#configuration) | [Project Structure](#project-structure) |
39
+ | [Tools](#tools) | [Development & Testing](#development--testing) | [License](#license) |
37
40
 
38
41
  ## Overview
39
42
 
@@ -58,11 +61,11 @@ Leverages the robust utilities provided by the `mcp-ts-template`:
58
61
 
59
62
  - **Logging**: Structured, configurable logging (file rotation, stdout JSON, MCP notifications) with sensitive data redaction.
60
63
  - **Error Handling**: Centralized error processing, standardized error types (`McpError`), and automatic logging.
61
- - **Configuration**: Environment variable loading (`dotenv`) with comprehensive validation.
64
+ - **Configuration**: Environment variable loading (`dotenv`) with comprehensive validation using Zod.
62
65
  - **Input Validation/Sanitization**: Uses `zod` for schema validation and custom sanitization logic.
63
66
  - **Request Context**: Tracking and correlation of operations via unique request IDs using `AsyncLocalStorage`.
64
67
  - **Type Safety**: Strong typing enforced by TypeScript and Zod schemas.
65
- - **HTTP Transport**: High-performance HTTP server using **Hono**, featuring session management with garbage collection and CORS support.
68
+ - **HTTP Transport**: High-performance HTTP server using **Hono**, featuring session management and authentication support.
66
69
  - **Authentication**: Robust authentication layer supporting JWT and OAuth 2.1, with fine-grained scope enforcement.
67
70
  - **Deployment**: Multi-stage `Dockerfile` for creating small, secure production images with native dependency support.
68
71
 
@@ -83,56 +86,8 @@ Leverages the robust utilities provided by the `mcp-ts-template`:
83
86
 
84
87
  - [Node.js (>=18.0.0)](https://nodejs.org/)
85
88
  - [npm](https://www.npmjs.com/) (comes with Node.js)
86
- - [Docker](https://www.docker.com/) (optional, for containerized deployment)
87
89
  - **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/)
88
90
 
89
- ### Install via npm (recommended)
90
-
91
- ```bash
92
- npm install @cyanheads/pubmed-mcp-server
93
- ```
94
-
95
- ### Alternatively Install from Source
96
-
97
- 1. Clone the repository:
98
-
99
- ```bash
100
- git clone https://github.com/cyanheads/pubmed-mcp-server.git
101
- cd pubmed-mcp-server
102
- ```
103
-
104
- 2. Install dependencies:
105
-
106
- ```bash
107
- npm install
108
- ```
109
-
110
- 3. Build the project:
111
- ```bash
112
- npm run build
113
- *or npm run rebuild*
114
- ```
115
-
116
- ## Configuration
117
-
118
- ### Environment Variables
119
-
120
- 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)
121
-
122
- | Variable | Description | Default |
123
- | ---------------------- | ---------------------------------------------------------------------------------------- | ------------------------------ |
124
- | `MCP_TRANSPORT_TYPE` | Transport mechanism: `stdio` or `http`. | `stdio` |
125
- | `MCP_HTTP_PORT` | Port for the HTTP server (if `MCP_TRANSPORT_TYPE=http`). | `3010` |
126
- | `MCP_HTTP_HOST` | Host address for the HTTP server (if `MCP_TRANSPORT_TYPE=http`). | `127.0.0.1` |
127
- | `MCP_ALLOWED_ORIGINS` | Comma-separated list of allowed origins for CORS (if `MCP_TRANSPORT_TYPE=http`). | (none) |
128
- | `MCP_LOG_LEVEL` | Logging level (`debug`, `info`, `notice`, `warning`, `error`, `crit`, `alert`, `emerg`). | `debug` |
129
- | `LOG_OUTPUT_MODE` | Logging output mode: `file` or `stdout`. | `file` |
130
- | `MCP_AUTH_MODE` | Authentication mode for HTTP: `jwt` or `oauth`. | `jwt` |
131
- | `MCP_AUTH_SECRET_KEY` | **Required for `jwt` auth.** Minimum 32-character secret key for JWT authentication. | (none) |
132
- | `NCBI_API_KEY` | **Recommended.** Your NCBI API Key for higher rate limits and reliable access. | (none) |
133
- | `NCBI_TOOL_IDENTIFIER` | Tool identifier for NCBI E-utility requests. | `@cyanheads/pubmed-mcp-server` |
134
- | `LOGS_DIR` | Directory for log file storage (if `LOG_OUTPUT_MODE=file`). | `logs/` |
135
-
136
91
  ### MCP Client Settings
137
92
 
138
93
  Add the following to your MCP client's configuration file (e.g., `cline_mcp_settings.json`). This configuration uses `npx` to run the server, which will automatically install the package if not already present:
@@ -145,14 +100,55 @@ Add the following to your MCP client's configuration file (e.g., `cline_mcp_sett
145
100
  "args": ["@cyanheads/pubmed-mcp-server"],
146
101
  "env": {
147
102
  "NCBI_API_KEY": "your_ncbi_api_key_here"
148
- },
149
- "disabled": false,
150
- "autoApprove": []
103
+ }
151
104
  }
152
105
  }
153
106
  }
154
107
  ```
155
108
 
109
+ ### If running manually (not via MCP client for development or testing)
110
+
111
+ #### Install via npm
112
+
113
+ ```bash
114
+ npm install @cyanheads/pubmed-mcp-server
115
+ ```
116
+
117
+ #### Alternatively Install from Source
118
+
119
+ 1. Clone the repository:
120
+ ```bash
121
+ git clone https://github.com/cyanheads/pubmed-mcp-server.git
122
+ cd pubmed-mcp-server
123
+ ```
124
+ 2. Install dependencies:
125
+ ```bash
126
+ npm install
127
+ ```
128
+ 3. Build the project:
129
+ ```bash
130
+ npm run build
131
+ ```
132
+
133
+ ## Configuration
134
+
135
+ ### Environment Variables
136
+
137
+ Configure the server using environment variables. For local development, these can be set in a `.env` file at the project root or directly in your environment. Otherwise, you can set them in your MCP client configuration as shown above.
138
+
139
+ | Variable | Description | Default |
140
+ | :-------------------- | :--------------------------------------------------------------------------------------- | :------------ |
141
+ | `MCP_TRANSPORT_TYPE` | Transport mechanism: `stdio` or `http`. | `stdio` |
142
+ | `MCP_HTTP_PORT` | Port for the HTTP server (if `MCP_TRANSPORT_TYPE=http`). | `3010` |
143
+ | `MCP_HTTP_HOST` | Host address for the HTTP server (if `MCP_TRANSPORT_TYPE=http`). | `127.0.0.1` |
144
+ | `MCP_ALLOWED_ORIGINS` | Comma-separated list of allowed origins for CORS (if `MCP_TRANSPORT_TYPE=http`). | (none) |
145
+ | `MCP_LOG_LEVEL` | Logging level (`debug`, `info`, `notice`, `warning`, `error`, `crit`, `alert`, `emerg`). | `debug` |
146
+ | `MCP_AUTH_MODE` | Authentication mode for HTTP: `jwt` or `oauth`. | `jwt` |
147
+ | `MCP_AUTH_SECRET_KEY` | **Required for `jwt` auth.** Minimum 32-character secret key for JWT authentication. | (none) |
148
+ | `NCBI_API_KEY` | **Recommended.** Your NCBI API Key for higher rate limits and reliable access. | (none) |
149
+ | `LOGS_DIR` | Directory for log file storage. | `logs/` |
150
+ | `NODE_ENV` | Runtime environment (`development`, `production`). | `development` |
151
+
156
152
  ## Project Structure
157
153
 
158
154
  The codebase follows a modular structure within the `src/` directory:
@@ -165,7 +161,6 @@ src/
165
161
  ├── mcp-server/ # Core MCP server logic and capability registration
166
162
  │ ├── server.ts # Server setup, capability registration
167
163
  │ ├── transports/ # Transport handling (stdio, http)
168
- │ ├── resources/ # MCP Resource implementations
169
164
  │ └── tools/ # MCP Tool implementations (subdirs per tool)
170
165
  ├── services/ # External service integrations
171
166
  │ └── NCBI/ # NCBI E-utilities API client and parsing
@@ -191,40 +186,38 @@ _Note: All tools support comprehensive error handling and return structured JSON
191
186
 
192
187
  ## Examples
193
188
 
194
- Comprehensive usage examples are available in the [`examples/`](examples/) directory:
189
+ Comprehensive usage examples for each tool are available in the [`examples/`](examples/) directory.
195
190
 
196
- - [Search PubMed Articles](examples/search_pubmed_articles_example.md)
197
- - [Fetch Article Content](examples/fetch_pubmed_content_example.md)
198
- - [Article Connections](examples/get_pubmed_article_connections_1.md)
199
- - [Research Planning](examples/pubmed_research_agent_example.md)
200
- - [Chart Generation](./examples/generate_pubmed_chart/) - Generated chart examples (bar, line, scatter, pie, bubble, radar, polarArea) are available in the `examples/generate_pubmed_chart/` directory.
191
+ - **`search_pubmed_articles`**: [View Example](./examples/search_pubmed_articles_example.md)
192
+ - **`fetch_pubmed_content`**: [View Example](./examples/fetch_pubmed_content_example.md)
193
+ - **`get_pubmed_article_connections`**: [Ex. 1](./examples/get_pubmed_article_connections_1.md), [Ex. 2](./examples/get_pubmed_article_connections_2.md)
194
+ - **`pubmed_research_agent`**: [View Example](./examples/pubmed_research_agent_example.md)
195
+ - **`generate_pubmed_chart`**: [View Examples](./examples/generate_pubmed_chart/)
201
196
 
202
- ## Development
197
+ ## Development & Testing
203
198
 
204
- ### Build and Test
199
+ ### Development Scripts
205
200
 
206
201
  ```bash
207
202
  # Build the project (compile TS to JS in dist/ and make executable)
208
203
  npm run build
209
204
 
210
- # Test the server locally using the MCP inspector tool (stdio transport)
211
- npm run inspector
212
-
213
- # Test the server locally using the MCP inspector tool (http transport)
214
- npm run inspector:http
215
-
216
205
  # Clean build artifacts
217
206
  npm run clean
218
207
 
219
- # Generate a file tree representation for documentation
220
- npm run tree
221
-
222
208
  # Clean build artifacts and then rebuild the project
223
209
  npm run rebuild
224
210
 
225
211
  # Format code with Prettier
226
212
  npm run format
227
213
 
214
+ # Generate a file tree representation for documentation
215
+ npm run tree
216
+ ```
217
+
218
+ ### Running the Server
219
+
220
+ ```bash
228
221
  # Start the server using stdio (default)
229
222
  npm start
230
223
  # Or explicitly:
@@ -232,6 +225,12 @@ npm run start:stdio
232
225
 
233
226
  # Start the server using HTTP transport
234
227
  npm run start:http
228
+
229
+ # Test the server locally using the MCP inspector tool (stdio transport)
230
+ npm run inspector
231
+
232
+ # Test the server locally using the MCP inspector tool (http transport)
233
+ npm run inspector:http
235
234
  ```
236
235
 
237
236
  ## License
@@ -63,13 +63,28 @@ const EnvSchema = z
63
63
  MCP_HTTP_PORT: z.coerce.number().int().positive().default(3010),
64
64
  MCP_HTTP_HOST: z.string().default("127.0.0.1"),
65
65
  MCP_HTTP_ENDPOINT_PATH: z.string().default("/mcp"),
66
- MCP_HTTP_MAX_PORT_RETRIES: z.coerce.number().int().nonnegative().default(15),
67
- MCP_HTTP_PORT_RETRY_DELAY_MS: z.coerce.number().int().nonnegative().default(50),
68
- MCP_STATEFUL_SESSION_STALE_TIMEOUT_MS: z.coerce.number().int().positive().default(1800000),
66
+ MCP_HTTP_MAX_PORT_RETRIES: z.coerce
67
+ .number()
68
+ .int()
69
+ .nonnegative()
70
+ .default(15),
71
+ MCP_HTTP_PORT_RETRY_DELAY_MS: z.coerce
72
+ .number()
73
+ .int()
74
+ .nonnegative()
75
+ .default(50),
76
+ MCP_STATEFUL_SESSION_STALE_TIMEOUT_MS: z.coerce
77
+ .number()
78
+ .int()
79
+ .positive()
80
+ .default(1800000),
69
81
  MCP_ALLOWED_ORIGINS: z.string().optional(),
70
82
  // Authentication
71
83
  MCP_AUTH_MODE: z.enum(["jwt", "oauth", "none"]).default("none"),
72
- MCP_AUTH_SECRET_KEY: z.string().min(32, "MCP_AUTH_SECRET_KEY must be at least 32 characters long.").optional(),
84
+ MCP_AUTH_SECRET_KEY: z
85
+ .string()
86
+ .min(32, "MCP_AUTH_SECRET_KEY must be at least 32 characters long.")
87
+ .optional(),
73
88
  OAUTH_ISSUER_URL: z.string().url().optional(),
74
89
  OAUTH_JWKS_URI: z.string().url().optional(),
75
90
  OAUTH_AUDIENCE: z.string().optional(),
@@ -96,10 +111,18 @@ const EnvSchema = z
96
111
  }
97
112
  if (data.MCP_AUTH_MODE === "oauth") {
98
113
  if (!data.OAUTH_ISSUER_URL) {
99
- ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["OAUTH_ISSUER_URL"], message: "OAUTH_ISSUER_URL is required for 'oauth' mode." });
114
+ ctx.addIssue({
115
+ code: z.ZodIssueCode.custom,
116
+ path: ["OAUTH_ISSUER_URL"],
117
+ message: "OAUTH_ISSUER_URL is required for 'oauth' mode.",
118
+ });
100
119
  }
101
120
  if (!data.OAUTH_AUDIENCE) {
102
- ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["OAUTH_AUDIENCE"], message: "OAUTH_AUDIENCE is required for 'oauth' mode." });
121
+ ctx.addIssue({
122
+ code: z.ZodIssueCode.custom,
123
+ path: ["OAUTH_AUDIENCE"],
124
+ message: "OAUTH_AUDIENCE is required for 'oauth' mode.",
125
+ });
103
126
  }
104
127
  }
105
128
  });
@@ -111,7 +134,9 @@ if (!parsedEnv.success) {
111
134
  }
112
135
  const env = parsedEnv.success ? parsedEnv.data : EnvSchema.parse({});
113
136
  const ensureDirectory = (dirPath, rootDir, dirName) => {
114
- const resolvedDirPath = path.isAbsolute(dirPath) ? dirPath : path.resolve(rootDir, dirPath);
137
+ const resolvedDirPath = path.isAbsolute(dirPath)
138
+ ? dirPath
139
+ : path.resolve(rootDir, dirPath);
115
140
  if (!resolvedDirPath.startsWith(rootDir)) {
116
141
  if (process.stdout.isTTY) {
117
142
  console.error(`Error: ${dirName} path "${dirPath}" is outside the project boundary "${rootDir}".`);
@@ -151,7 +176,9 @@ export const config = {
151
176
  mcpHttpMaxPortRetries: env.MCP_HTTP_MAX_PORT_RETRIES,
152
177
  mcpHttpPortRetryDelayMs: env.MCP_HTTP_PORT_RETRY_DELAY_MS,
153
178
  mcpStatefulSessionStaleTimeoutMs: env.MCP_STATEFUL_SESSION_STALE_TIMEOUT_MS,
154
- mcpAllowedOrigins: env.MCP_ALLOWED_ORIGINS?.split(",").map((o) => o.trim()).filter(Boolean),
179
+ mcpAllowedOrigins: env.MCP_ALLOWED_ORIGINS?.split(",")
180
+ .map((o) => o.trim())
181
+ .filter(Boolean),
155
182
  mcpAuthMode: env.MCP_AUTH_MODE,
156
183
  mcpAuthSecretKey: env.MCP_AUTH_SECRET_KEY,
157
184
  oauthIssuerUrl: env.OAUTH_ISSUER_URL,
@@ -160,7 +187,8 @@ export const config = {
160
187
  devMcpClientId: env.DEV_MCP_CLIENT_ID,
161
188
  devMcpScopes: env.DEV_MCP_SCOPES?.split(",").map((s) => s.trim()),
162
189
  ncbiApiKey: env.NCBI_API_KEY,
163
- ncbiToolIdentifier: env.NCBI_TOOL_IDENTIFIER || `${env.MCP_SERVER_NAME || pkg.name}/${env.MCP_SERVER_VERSION || pkg.version}`,
190
+ ncbiToolIdentifier: env.NCBI_TOOL_IDENTIFIER ||
191
+ `${env.MCP_SERVER_NAME || pkg.name}/${env.MCP_SERVER_VERSION || pkg.version}`,
164
192
  ncbiAdminEmail: env.NCBI_ADMIN_EMAIL,
165
193
  ncbiRequestDelayMs: env.NCBI_REQUEST_DELAY_MS ?? (env.NCBI_API_KEY ? 100 : 334),
166
194
  ncbiMaxRetries: env.NCBI_MAX_RETRIES,
package/dist/index.js CHANGED
@@ -51,70 +51,40 @@ const shutdown = async (signal) => {
51
51
  triggerEvent: signal,
52
52
  });
53
53
  logger.info(`Received ${signal}. Initiating graceful shutdown...`, 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);
65
- }
66
- else if (mcpClosed && httpClosed) {
67
- logger.info("Graceful shutdown completed successfully. Exiting.", shutdownContext);
68
- process.exit(0);
54
+ try {
55
+ let closePromise = Promise.resolve();
56
+ const transportType = config.mcpTransportType;
57
+ if (transportType === "stdio" && mcpStdioServer) {
58
+ logger.info("Attempting to close main MCP server (STDIO)...", shutdownContext);
59
+ closePromise = mcpStdioServer.close();
69
60
  }
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,
61
+ else if (transportType === "http" && actualHttpServer) {
62
+ logger.info("Attempting to close HTTP server...", shutdownContext);
63
+ closePromise = new Promise((resolve, reject) => {
64
+ actualHttpServer.close((err) => {
65
+ if (err) {
66
+ logger.error("Error closing HTTP server.", {
67
+ ...shutdownContext,
68
+ error: err.message,
69
+ });
70
+ return reject(err);
71
+ }
72
+ logger.info("HTTP server closed successfully.", shutdownContext);
73
+ resolve();
74
+ });
84
75
  });
85
- mcpClosed = true; // Consider it closed even on error to allow exit
86
- if (!closeError)
87
- closeError = err;
88
- checkAndExit();
89
- });
90
- }
91
- else {
92
- mcpClosed = true; // No STDIO McpServer to close
76
+ }
77
+ await closePromise;
78
+ logger.info("Graceful shutdown completed successfully. Exiting.", shutdownContext);
79
+ process.exit(0);
93
80
  }
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();
81
+ catch (error) {
82
+ logger.error("Critical error during shutdown process.", {
83
+ ...shutdownContext,
84
+ errorMessage: error instanceof Error ? error.message : String(error),
85
+ errorStack: error instanceof Error ? error.stack : undefined,
110
86
  });
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();
87
+ process.exit(1);
118
88
  }
119
89
  };
120
90
  /**
@@ -32,6 +32,11 @@ async function createMcpServerInstance() {
32
32
  operation: "createMcpServerInstance",
33
33
  });
34
34
  logger.info("Initializing MCP server instance", context);
35
+ requestContextService.configure({
36
+ appName: config.mcpServerName,
37
+ appVersion: config.mcpServerVersion,
38
+ environment,
39
+ });
35
40
  const server = new McpServer({ name: config.mcpServerName, version: config.mcpServerVersion }, {
36
41
  capabilities: {
37
42
  logging: {},
@@ -90,11 +95,6 @@ export async function initializeAndStartServer() {
90
95
  operation: "initializeAndStartServer",
91
96
  });
92
97
  logger.info("MCP Server initialization sequence started.", context);
93
- requestContextService.configure({
94
- appName: config.mcpServerName,
95
- appVersion: config.mcpServerVersion,
96
- environment,
97
- });
98
98
  try {
99
99
  const result = await startTransport();
100
100
  logger.info("MCP Server initialization sequence completed successfully.", context);
@@ -19,6 +19,7 @@ export declare class NcbiRequestQueueManager {
19
19
  private requestQueue;
20
20
  private isProcessingQueue;
21
21
  private lastRequestTime;
22
+ private keepAliveInterval;
22
23
  constructor();
23
24
  /**
24
25
  * Processes the request queue, ensuring delays between requests to respect NCBI rate limits.
@@ -9,8 +9,11 @@ export class NcbiRequestQueueManager {
9
9
  this.requestQueue = [];
10
10
  this.isProcessingQueue = false;
11
11
  this.lastRequestTime = 0;
12
- // Constructor should not have side-effects like logging.
13
- // The service that uses this manager can log its creation if needed.
12
+ // This interval's purpose is to keep the Node.js event loop alive
13
+ // so the process doesn't exit prematurely when running in stdio mode.
14
+ this.keepAliveInterval = setInterval(() => {
15
+ // This empty interval keeps the event loop active.
16
+ }, 1000000);
14
17
  }
15
18
  /**
16
19
  * Processes the request queue, ensuring delays between requests to respect NCBI rate limits.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyanheads/pubmed-mcp-server",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "A Model Context Protocol (MCP) server enabling AI agents to intelligently search, retrieve, and analyze biomedical literature from PubMed via NCBI E-utilities. Built on the mcp-ts-template for robust, production-ready performance.",
5
5
  "main": "dist/index.js",
6
6
  "files": [
@@ -49,7 +49,7 @@
49
49
  "jose": "^6.0.12",
50
50
  "jsonwebtoken": "^9.0.2",
51
51
  "node-cron": "^4.2.1",
52
- "openai": "^5.10.2",
52
+ "openai": "^5.11.0",
53
53
  "partial-json": "^0.1.7",
54
54
  "sanitize-html": "^2.17.0",
55
55
  "tiktoken": "^1.0.21",