@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
|
-
|
|
1
|
+
<div align="center">
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[](https://modelcontextprotocol.io/)
|
|
5
|
-
[](./CHANGELOG.md)
|
|
6
|
-
[](https://opensource.org/licenses/Apache-2.0)
|
|
7
|
-
[](https://github.com/cyanheads/pubmed-mcp-server/issues)
|
|
8
|
-
[](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
|
-
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
[](https://modelcontextprotocol.io/)
|
|
9
|
+
[](./CHANGELOG.md)
|
|
10
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
11
|
+
[](https://github.com/cyanheads/pubmed-mcp-server/issues)
|
|
12
|
+
[](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
|
|
21
|
-
| :-------------------------------------------------------------------------------------- |
|
|
22
|
-
| [`search_pubmed_articles`](./src/mcp-server/tools/searchPubMedArticles/) | Searches PubMed for articles based on your query.
|
|
23
|
-
| [`fetch_pubmed_content`](./src/mcp-server/tools/fetchPubMedContent/) | Retrieves detailed information for PubMed articles.
|
|
24
|
-
| [`get_pubmed_article_connections`](./src/mcp-server/tools/getPubMedArticleConnections/) | Finds related articles (cited by, similar, references) or formats citations for a PMID.
|
|
25
|
-
| [`pubmed_research_agent`](./src/mcp-server/tools/pubmedResearchAgent/) | Generates a standardized JSON research plan outline from component details.
|
|
26
|
-
| [`generate_pubmed_chart`](./src/mcp-server/tools/generatePubMedChart/) | Generates a chart image (PNG) from given input data.
|
|
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)
|
|
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
|
|
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
|
-
- [
|
|
197
|
-
- [
|
|
198
|
-
- [
|
|
199
|
-
- [
|
|
200
|
-
- [
|
|
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
|
-
###
|
|
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
|
package/dist/config/index.js
CHANGED
|
@@ -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
|
|
67
|
-
|
|
68
|
-
|
|
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
|
|
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({
|
|
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({
|
|
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)
|
|
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(",")
|
|
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 ||
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
95
|
-
logger.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
//
|
|
13
|
-
//
|
|
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.
|
|
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.
|
|
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",
|