@cyanheads/pubmed-mcp-server 1.3.1 → 1.3.3

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
@@ -6,7 +6,7 @@
6
6
 
7
7
  [![TypeScript](https://img.shields.io/badge/TypeScript-^5.8.3-blue.svg?style=flat-square)](https://www.typescriptlang.org/)
8
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.1-blue.svg?style=flat-square)](./CHANGELOG.md)
9
+ [![Version](https://img.shields.io/badge/Version-1.3.2-blue.svg?style=flat-square)](./CHANGELOG.md)
10
10
  [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=flat-square)](https://opensource.org/licenses/Apache-2.0)
11
11
  [![Status](https://img.shields.io/badge/Status-Stable-green.svg?style=flat-square)](https://github.com/cyanheads/pubmed-mcp-server/issues)
12
12
  [![GitHub](https://img.shields.io/github/stars/cyanheads/pubmed-mcp-server?style=social)](https://github.com/cyanheads/pubmed-mcp-server)
@@ -137,30 +137,63 @@ const ensureDirectory = (dirPath, rootDir, dirName) => {
137
137
  const resolvedDirPath = path.isAbsolute(dirPath)
138
138
  ? dirPath
139
139
  : path.resolve(rootDir, dirPath);
140
- if (!resolvedDirPath.startsWith(rootDir)) {
140
+ if (!resolvedDirPath.startsWith(rootDir + path.sep) &&
141
+ resolvedDirPath !== rootDir) {
141
142
  if (process.stdout.isTTY) {
142
- console.error(`Error: ${dirName} path "${dirPath}" is outside the project boundary "${rootDir}".`);
143
+ console.error(`Error: ${dirName} path "${dirPath}" resolves to "${resolvedDirPath}", which is outside the project boundary "${rootDir}".`);
143
144
  }
144
145
  return null;
145
146
  }
146
- try {
147
- if (!existsSync(resolvedDirPath)) {
147
+ if (!existsSync(resolvedDirPath)) {
148
+ try {
148
149
  mkdirSync(resolvedDirPath, { recursive: true });
150
+ if (process.stdout.isTTY) {
151
+ console.log(`Created ${dirName} directory: ${resolvedDirPath}`);
152
+ }
149
153
  }
150
- else {
151
- if (!statSync(resolvedDirPath).isDirectory()) {
152
- console.error(`Error: ${dirName} path ${resolvedDirPath} exists but is not a directory.`);
153
- return null;
154
+ catch (err) {
155
+ const errorMessage = err instanceof Error ? err.message : String(err);
156
+ if (process.stdout.isTTY) {
157
+ console.error(`Error creating ${dirName} directory at ${resolvedDirPath}: ${errorMessage}`);
154
158
  }
159
+ return null;
155
160
  }
156
- return resolvedDirPath;
157
161
  }
158
- catch (error) {
159
- console.error(`Error ensuring ${dirName} directory at ${resolvedDirPath}: ${error.message}`);
160
- return null;
162
+ else {
163
+ try {
164
+ const stats = statSync(resolvedDirPath);
165
+ if (!stats.isDirectory()) {
166
+ if (process.stdout.isTTY) {
167
+ console.error(`Error: ${dirName} path ${resolvedDirPath} exists but is not a directory.`);
168
+ }
169
+ return null;
170
+ }
171
+ }
172
+ catch (statError) {
173
+ const errorMessage = statError instanceof Error
174
+ ? statError.message
175
+ : "An unknown error occurred";
176
+ if (process.stdout.isTTY) {
177
+ console.error(`Error accessing ${dirName} path ${resolvedDirPath}: ${errorMessage}`);
178
+ }
179
+ return null;
180
+ }
161
181
  }
182
+ return resolvedDirPath;
162
183
  };
163
- const validatedLogsPath = ensureDirectory(env.LOGS_DIR, projectRoot, "logs");
184
+ let validatedLogsPath = ensureDirectory(env.LOGS_DIR, projectRoot, "logs");
185
+ if (!validatedLogsPath) {
186
+ if (process.stdout.isTTY) {
187
+ console.warn(`Warning: Custom logs directory ('${env.LOGS_DIR}') is invalid or outside the project boundary. Falling back to default.`);
188
+ }
189
+ const defaultLogsDir = path.join(projectRoot, "logs");
190
+ validatedLogsPath = ensureDirectory(defaultLogsDir, projectRoot, "logs");
191
+ if (!validatedLogsPath) {
192
+ if (process.stdout.isTTY) {
193
+ console.warn("Warning: Default logs directory could not be created. File logging will be disabled.");
194
+ }
195
+ }
196
+ }
164
197
  export const config = {
165
198
  pkg,
166
199
  mcpServerName: env.MCP_SERVER_NAME || pkg.name,
@@ -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.1",
3
+ "version": "1.3.3",
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": [