@cyanheads/pubmed-mcp-server 1.2.4 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/README.md +2 -2
  2. package/dist/config/index.d.ts +13 -52
  3. package/dist/config/index.js +51 -222
  4. package/dist/mcp-server/server.d.ts +0 -5
  5. package/dist/mcp-server/server.js +18 -34
  6. package/dist/mcp-server/tools/fetchPubMedContent/logic.js +2 -2
  7. package/dist/mcp-server/tools/getPubMedArticleConnections/logic/citationFormatter.js +2 -2
  8. package/dist/mcp-server/tools/getPubMedArticleConnections/logic/elinkHandler.js +3 -3
  9. package/dist/mcp-server/tools/searchPubMedArticles/logic.js +2 -2
  10. package/dist/mcp-server/transports/auth/authFactory.d.ts +10 -0
  11. package/dist/mcp-server/transports/auth/authFactory.js +41 -0
  12. package/dist/mcp-server/transports/auth/authMiddleware.d.ts +19 -0
  13. package/dist/mcp-server/transports/auth/authMiddleware.js +57 -0
  14. package/dist/mcp-server/transports/auth/index.d.ts +8 -5
  15. package/dist/mcp-server/transports/auth/index.js +6 -4
  16. package/dist/mcp-server/transports/auth/{core → lib}/authTypes.d.ts +0 -5
  17. package/dist/mcp-server/transports/auth/lib/authTypes.js +8 -0
  18. package/dist/mcp-server/transports/auth/{core → lib}/authUtils.js +21 -14
  19. package/dist/mcp-server/transports/auth/strategies/authStrategy.d.ts +17 -0
  20. package/dist/mcp-server/transports/auth/strategies/authStrategy.js +1 -0
  21. package/dist/mcp-server/transports/auth/strategies/jwtStrategy.d.ts +7 -0
  22. package/dist/mcp-server/transports/auth/strategies/jwtStrategy.js +112 -0
  23. package/dist/mcp-server/transports/auth/strategies/oauthStrategy.d.ts +7 -0
  24. package/dist/mcp-server/transports/auth/strategies/oauthStrategy.js +101 -0
  25. package/dist/mcp-server/transports/core/baseTransportManager.d.ts +17 -0
  26. package/dist/mcp-server/transports/core/baseTransportManager.js +18 -0
  27. package/dist/mcp-server/transports/core/honoNodeBridge.d.ts +23 -0
  28. package/dist/mcp-server/transports/core/honoNodeBridge.js +51 -0
  29. package/dist/mcp-server/transports/core/statefulTransportManager.d.ts +31 -0
  30. package/dist/mcp-server/transports/core/statefulTransportManager.js +233 -0
  31. package/dist/mcp-server/transports/core/statelessTransportManager.d.ts +20 -0
  32. package/dist/mcp-server/transports/core/statelessTransportManager.js +92 -0
  33. package/dist/mcp-server/transports/core/transportTypes.d.ts +68 -0
  34. package/dist/mcp-server/transports/core/transportTypes.js +5 -0
  35. package/dist/mcp-server/transports/{httpErrorHandler.d.ts → http/httpErrorHandler.d.ts} +4 -9
  36. package/dist/mcp-server/transports/{httpErrorHandler.js → http/httpErrorHandler.js} +33 -8
  37. package/dist/mcp-server/transports/http/httpTransport.d.ts +22 -0
  38. package/dist/mcp-server/transports/http/httpTransport.js +251 -0
  39. package/dist/mcp-server/transports/http/httpTypes.d.ts +16 -0
  40. package/dist/mcp-server/transports/http/httpTypes.js +5 -0
  41. package/dist/mcp-server/transports/http/index.d.ts +7 -0
  42. package/dist/mcp-server/transports/http/index.js +6 -0
  43. package/dist/mcp-server/transports/http/mcpTransportMiddleware.d.ts +25 -0
  44. package/dist/mcp-server/transports/http/mcpTransportMiddleware.js +63 -0
  45. package/dist/mcp-server/transports/stdio/index.d.ts +5 -0
  46. package/dist/mcp-server/transports/stdio/index.js +5 -0
  47. package/dist/mcp-server/transports/{stdioTransport.d.ts → stdio/stdioTransport.d.ts} +2 -2
  48. package/dist/mcp-server/transports/{stdioTransport.js → stdio/stdioTransport.js} +10 -5
  49. package/dist/services/NCBI/{ncbiConstants.d.ts → core/ncbiConstants.d.ts} +1 -1
  50. package/dist/services/NCBI/{ncbiConstants.js → core/ncbiConstants.js} +1 -1
  51. package/dist/services/NCBI/{ncbiCoreApiClient.d.ts → core/ncbiCoreApiClient.d.ts} +2 -2
  52. package/dist/services/NCBI/{ncbiCoreApiClient.js → core/ncbiCoreApiClient.js} +4 -4
  53. package/dist/services/NCBI/{ncbiRequestQueueManager.d.ts → core/ncbiRequestQueueManager.d.ts} +2 -2
  54. package/dist/services/NCBI/{ncbiRequestQueueManager.js → core/ncbiRequestQueueManager.js} +3 -3
  55. package/dist/services/NCBI/{ncbiResponseHandler.d.ts → core/ncbiResponseHandler.d.ts} +2 -2
  56. package/dist/services/NCBI/{ncbiResponseHandler.js → core/ncbiResponseHandler.js} +3 -3
  57. package/dist/services/NCBI/{ncbiService.d.ts → core/ncbiService.d.ts} +3 -3
  58. package/dist/services/NCBI/{ncbiService.js → core/ncbiService.js} +2 -2
  59. package/dist/{utils/parsing/ncbi-parsing → services/NCBI/parsing}/eSummaryResultParser.d.ts +1 -1
  60. package/dist/{utils/parsing/ncbi-parsing → services/NCBI/parsing}/eSummaryResultParser.js +1 -1
  61. package/dist/{utils/parsing/ncbi-parsing → services/NCBI/parsing}/index.d.ts +1 -1
  62. package/dist/{utils/parsing/ncbi-parsing → services/NCBI/parsing}/index.js +1 -1
  63. package/dist/{utils/parsing/ncbi-parsing → services/NCBI/parsing}/pubmedArticleStructureParser.d.ts +1 -1
  64. package/dist/{utils/parsing/ncbi-parsing → services/NCBI/parsing}/pubmedArticleStructureParser.js +1 -1
  65. package/dist/{utils/parsing/ncbi-parsing → services/NCBI/parsing}/xmlGenericHelpers.d.ts +1 -1
  66. package/dist/{utils/parsing/ncbi-parsing → services/NCBI/parsing}/xmlGenericHelpers.js +1 -1
  67. package/dist/types-global/errors.d.ts +2 -0
  68. package/dist/types-global/errors.js +2 -0
  69. package/dist/utils/internal/errorHandler.js +1 -1
  70. package/dist/utils/internal/logger.d.ts +13 -1
  71. package/dist/utils/internal/logger.js +43 -9
  72. package/dist/utils/network/fetchWithTimeout.d.ts +21 -0
  73. package/dist/utils/network/fetchWithTimeout.js +59 -0
  74. package/dist/utils/network/index.d.ts +6 -0
  75. package/dist/utils/network/index.js +5 -0
  76. package/dist/utils/scheduling/index.d.ts +6 -0
  77. package/dist/utils/scheduling/index.js +6 -0
  78. package/dist/utils/scheduling/scheduler.d.ts +72 -0
  79. package/dist/utils/scheduling/scheduler.js +150 -0
  80. package/dist/utils/security/sanitization.js +35 -18
  81. package/package.json +9 -7
  82. package/dist/mcp-server/transports/auth/core/authTypes.js +0 -5
  83. package/dist/mcp-server/transports/auth/strategies/jwt/jwtMiddleware.d.ts +0 -27
  84. package/dist/mcp-server/transports/auth/strategies/jwt/jwtMiddleware.js +0 -149
  85. package/dist/mcp-server/transports/auth/strategies/oauth/oauthMiddleware.d.ts +0 -20
  86. package/dist/mcp-server/transports/auth/strategies/oauth/oauthMiddleware.js +0 -124
  87. package/dist/mcp-server/transports/httpTransport.d.ts +0 -21
  88. package/dist/mcp-server/transports/httpTransport.js +0 -208
  89. /package/dist/mcp-server/transports/auth/{core → lib}/authContext.d.ts +0 -0
  90. /package/dist/mcp-server/transports/auth/{core → lib}/authContext.js +0 -0
  91. /package/dist/mcp-server/transports/auth/{core → lib}/authUtils.d.ts +0 -0
@@ -0,0 +1,251 @@
1
+ /**
2
+ * @fileoverview Configures and starts the HTTP MCP transport using Hono.
3
+ * This file has been refactored to correctly integrate Hono's streaming
4
+ * capabilities with the Model Context Protocol SDK's transport layer.
5
+ * @module src/mcp-server/transports/http/httpTransport
6
+ */
7
+ import { serve } from "@hono/node-server";
8
+ import { Hono } from "hono";
9
+ import { cors } from "hono/cors";
10
+ import { stream } from "hono/streaming";
11
+ import http from "http";
12
+ import { config } from "../../../config/index.js";
13
+ import { logger, rateLimiter, requestContextService, } from "../../../utils/index.js";
14
+ import { createAuthMiddleware, createAuthStrategy } from "../auth/index.js";
15
+ import { StatefulTransportManager } from "../core/statefulTransportManager.js";
16
+ import { StatelessTransportManager } from "../core/statelessTransportManager.js";
17
+ import { httpErrorHandler } from "./httpErrorHandler.js";
18
+ import { mcpTransportMiddleware } from "./mcpTransportMiddleware.js";
19
+ const HTTP_PORT = config.mcpHttpPort;
20
+ const HTTP_HOST = config.mcpHttpHost;
21
+ const MCP_ENDPOINT_PATH = config.mcpHttpEndpointPath;
22
+ /**
23
+ * Converts a Fetch API Headers object to Node.js IncomingHttpHeaders.
24
+ * Hono uses Fetch API Headers, but the underlying transport managers expect
25
+ * Node's native IncomingHttpHeaders.
26
+ * @param headers - The Headers object to convert.
27
+ * @returns An object compatible with IncomingHttpHeaders.
28
+ */
29
+ async function isPortInUse(port, host, parentContext) {
30
+ const context = { ...parentContext, operation: "isPortInUse", port, host };
31
+ logger.debug(`Checking if port ${port} is in use...`, context);
32
+ return new Promise((resolve) => {
33
+ const tempServer = http.createServer();
34
+ tempServer
35
+ .once("error", (err) => {
36
+ const inUse = err.code === "EADDRINUSE";
37
+ logger.debug(`Port check resulted in error: ${err.code}. Port in use: ${inUse}`, context);
38
+ resolve(inUse);
39
+ })
40
+ .once("listening", () => {
41
+ logger.debug(`Successfully bound to port ${port} temporarily. Port is not in use.`, context);
42
+ tempServer.close(() => resolve(false));
43
+ })
44
+ .listen(port, host);
45
+ });
46
+ }
47
+ function startHttpServerWithRetry(app, initialPort, host, maxRetries, parentContext) {
48
+ const startContext = {
49
+ ...parentContext,
50
+ operation: "startHttpServerWithRetry",
51
+ };
52
+ logger.info(`Attempting to start HTTP server on port ${initialPort} with ${maxRetries} retries.`, startContext);
53
+ return new Promise((resolve, reject) => {
54
+ const tryBind = (port, attempt) => {
55
+ const attemptContext = { ...startContext, port, attempt };
56
+ if (attempt > maxRetries + 1) {
57
+ const error = new Error(`Failed to bind to any port after ${maxRetries} retries.`);
58
+ logger.fatal(error.message, attemptContext);
59
+ return reject(error);
60
+ }
61
+ isPortInUse(port, host, attemptContext)
62
+ .then((inUse) => {
63
+ if (inUse) {
64
+ logger.warning(`Port ${port} is in use, retrying on port ${port + 1}...`, attemptContext);
65
+ setTimeout(() => tryBind(port + 1, attempt + 1), config.mcpHttpPortRetryDelayMs);
66
+ return;
67
+ }
68
+ try {
69
+ const serverInstance = serve({ fetch: app.fetch, port, hostname: host }, (info) => {
70
+ const serverAddress = `http://${info.address}:${info.port}${MCP_ENDPOINT_PATH}`;
71
+ logger.info(`HTTP transport listening at ${serverAddress}`, {
72
+ ...attemptContext,
73
+ address: serverAddress,
74
+ sessionMode: config.mcpSessionMode,
75
+ });
76
+ if (process.stdout.isTTY) {
77
+ console.log(`\n🚀 MCP Server running at: ${serverAddress}`);
78
+ console.log(` Session Mode: ${config.mcpSessionMode}\n`);
79
+ }
80
+ });
81
+ resolve(serverInstance);
82
+ }
83
+ catch (err) {
84
+ if (err &&
85
+ typeof err === "object" &&
86
+ "code" in err &&
87
+ err.code !== "EADDRINUSE") {
88
+ const errorToLog = err instanceof Error ? err : new Error(String(err));
89
+ logger.error("An unexpected error occurred while starting the server.", errorToLog, attemptContext);
90
+ return reject(err);
91
+ }
92
+ logger.warning(`Encountered EADDRINUSE race condition on port ${port}, retrying...`, attemptContext);
93
+ setTimeout(() => tryBind(port + 1, attempt + 1), config.mcpHttpPortRetryDelayMs);
94
+ }
95
+ })
96
+ .catch((err) => {
97
+ logger.fatal("Failed to check if port is in use.", err, attemptContext);
98
+ reject(err);
99
+ });
100
+ };
101
+ tryBind(initialPort, 1);
102
+ });
103
+ }
104
+ function createTransportManager(createServerInstanceFn, sessionMode, context) {
105
+ const opContext = {
106
+ ...context,
107
+ operation: "createTransportManager",
108
+ sessionMode,
109
+ };
110
+ logger.info(`Creating transport manager for session mode: ${sessionMode}`, opContext);
111
+ switch (sessionMode) {
112
+ case "stateless":
113
+ return new StatelessTransportManager(createServerInstanceFn);
114
+ case "stateful":
115
+ return new StatefulTransportManager(createServerInstanceFn);
116
+ case "auto":
117
+ default:
118
+ logger.info("Defaulting to 'auto' mode (stateful with stateless fallback).", opContext);
119
+ return new StatefulTransportManager(createServerInstanceFn);
120
+ }
121
+ }
122
+ export function createHttpApp(transportManager, createServerInstanceFn, parentContext) {
123
+ const app = new Hono();
124
+ const transportContext = {
125
+ ...parentContext,
126
+ component: "HttpTransportSetup",
127
+ };
128
+ logger.info("Creating Hono HTTP application.", transportContext);
129
+ app.use("*", cors({
130
+ origin: config.mcpAllowedOrigins || [],
131
+ allowMethods: ["GET", "POST", "DELETE", "OPTIONS"],
132
+ allowHeaders: [
133
+ "Content-Type",
134
+ "Mcp-Session-Id",
135
+ "Last-Event-ID",
136
+ "Authorization",
137
+ ],
138
+ credentials: true,
139
+ }));
140
+ app.use("*", async (c, next) => {
141
+ c.env.outgoing.setHeader("X-Content-Type-Options", "nosniff");
142
+ await next();
143
+ });
144
+ app.use(MCP_ENDPOINT_PATH, async (c, next) => {
145
+ const clientIp = c.req.header("x-forwarded-for")?.split(",")[0].trim() || "unknown_ip";
146
+ const context = requestContextService.createRequestContext({
147
+ operation: "httpRateLimitCheck",
148
+ ipAddress: clientIp,
149
+ });
150
+ try {
151
+ rateLimiter.check(clientIp, context);
152
+ logger.debug("Rate limit check passed.", context);
153
+ }
154
+ catch (error) {
155
+ logger.warning("Rate limit check failed.", {
156
+ ...context,
157
+ error: error instanceof Error ? error.message : String(error),
158
+ });
159
+ throw error;
160
+ }
161
+ await next();
162
+ });
163
+ const authStrategy = createAuthStrategy();
164
+ if (authStrategy) {
165
+ logger.info("Authentication strategy found, enabling auth middleware.", transportContext);
166
+ app.use(MCP_ENDPOINT_PATH, createAuthMiddleware(authStrategy));
167
+ }
168
+ else {
169
+ logger.info("No authentication strategy found, auth middleware disabled.", transportContext);
170
+ }
171
+ app.onError(httpErrorHandler);
172
+ app.get("/healthz", (c) => {
173
+ return c.json({
174
+ status: "ok",
175
+ timestamp: new Date().toISOString(),
176
+ });
177
+ });
178
+ app.get(MCP_ENDPOINT_PATH, (c) => {
179
+ const sessionId = c.req.header("mcp-session-id");
180
+ if (sessionId) {
181
+ return c.text("GET requests to existing sessions are not supported.", 405);
182
+ }
183
+ return c.json({
184
+ status: "ok",
185
+ mode: "stateless",
186
+ message: "Server is running. Provide a Mcp-Session-Id header to stream from a session.",
187
+ });
188
+ });
189
+ app.post(MCP_ENDPOINT_PATH, mcpTransportMiddleware(transportManager, createServerInstanceFn), (c) => {
190
+ const response = c.get("mcpResponse");
191
+ if (response.sessionId) {
192
+ c.header("Mcp-Session-Id", response.sessionId);
193
+ }
194
+ response.headers.forEach((value, key) => {
195
+ c.header(key, value);
196
+ });
197
+ c.status(response.statusCode);
198
+ if (response.stream) {
199
+ return stream(c, async (s) => {
200
+ if (response.stream) {
201
+ await s.pipe(response.stream);
202
+ }
203
+ });
204
+ }
205
+ else {
206
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
207
+ return c.json(response.body);
208
+ }
209
+ });
210
+ app.delete(MCP_ENDPOINT_PATH, async (c) => {
211
+ const sessionId = c.req.header("mcp-session-id");
212
+ const context = requestContextService.createRequestContext({
213
+ ...transportContext,
214
+ operation: "handleDeleteRequest",
215
+ sessionId,
216
+ });
217
+ if (sessionId) {
218
+ if (transportManager instanceof StatefulTransportManager) {
219
+ const response = await transportManager.handleDeleteRequest(sessionId, context);
220
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
221
+ return c.json(response.body, response.statusCode);
222
+ }
223
+ else {
224
+ return c.json({
225
+ error: "Method Not Allowed",
226
+ message: "DELETE operations are not supported in this mode.",
227
+ }, 405);
228
+ }
229
+ }
230
+ else {
231
+ return c.json({
232
+ status: "stateless_mode",
233
+ message: "No sessions to delete in stateless mode",
234
+ });
235
+ }
236
+ });
237
+ logger.info("Hono application setup complete.", transportContext);
238
+ return app;
239
+ }
240
+ export async function startHttpTransport(createServerInstanceFn, parentContext) {
241
+ const transportContext = {
242
+ ...parentContext,
243
+ component: "HttpTransportStart",
244
+ };
245
+ logger.info("Starting HTTP transport.", transportContext);
246
+ const transportManager = createTransportManager(createServerInstanceFn, config.mcpSessionMode, transportContext);
247
+ const app = createHttpApp(transportManager, createServerInstanceFn, transportContext);
248
+ const server = await startHttpServerWithRetry(app, HTTP_PORT, HTTP_HOST, config.mcpHttpMaxPortRetries, transportContext);
249
+ logger.info("HTTP transport started successfully.", transportContext);
250
+ return { app, server, transportManager };
251
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @fileoverview Defines custom types for the Hono HTTP transport layer.
3
+ * @module src/mcp-server/transports/http/httpTypes
4
+ */
5
+ import type { IncomingMessage, ServerResponse } from "http";
6
+ /**
7
+ * Extends Hono's Bindings to include the raw Node.js request and response objects.
8
+ * This is necessary for integrating with libraries like the MCP SDK that
9
+ * need to write directly to the response stream.
10
+ *
11
+ * As per `@hono/node-server`, the response object is available on `c.env.outgoing`.
12
+ */
13
+ export type HonoNodeBindings = {
14
+ incoming: IncomingMessage;
15
+ outgoing: ServerResponse;
16
+ };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @fileoverview Defines custom types for the Hono HTTP transport layer.
3
+ * @module src/mcp-server/transports/http/httpTypes
4
+ */
5
+ export {};
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @fileoverview Barrel file for the HTTP transport module.
3
+ * @module src/mcp-server/transports/http/index
4
+ */
5
+ export { createHttpApp, startHttpTransport } from "./httpTransport.js";
6
+ export { httpErrorHandler } from "./httpErrorHandler.js";
7
+ export type { HonoNodeBindings } from "./httpTypes.js";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @fileoverview Barrel file for the HTTP transport module.
3
+ * @module src/mcp-server/transports/http/index
4
+ */
5
+ export { createHttpApp, startHttpTransport } from "./httpTransport.js";
6
+ export { httpErrorHandler } from "./httpErrorHandler.js";
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @fileoverview Hono middleware for handling MCP transport logic.
3
+ * This middleware encapsulates the logic for processing MCP requests,
4
+ * delegating to the appropriate transport manager, and preparing the
5
+ * response for Hono to send.
6
+ * @module src/mcp-server/transports/http/mcpTransportMiddleware
7
+ */
8
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9
+ import { TransportManager, TransportResponse } from "../core/transportTypes.js";
10
+ import { HonoNodeBindings } from "./httpTypes.js";
11
+ /**
12
+ * Creates a Hono middleware for handling MCP POST requests.
13
+ * @param transportManager - The main transport manager (usually stateful).
14
+ * @param createServerInstanceFn - Function to create an McpServer instance.
15
+ * @returns A Hono middleware function.
16
+ */
17
+ type McpMiddlewareEnv = {
18
+ Variables: {
19
+ mcpResponse: TransportResponse;
20
+ };
21
+ };
22
+ export declare const mcpTransportMiddleware: (transportManager: TransportManager, createServerInstanceFn: () => Promise<McpServer>) => import("hono").MiddlewareHandler<McpMiddlewareEnv & {
23
+ Bindings: HonoNodeBindings;
24
+ }, string, {}>;
25
+ export {};
@@ -0,0 +1,63 @@
1
+ /**
2
+ * @fileoverview Hono middleware for handling MCP transport logic.
3
+ * This middleware encapsulates the logic for processing MCP requests,
4
+ * delegating to the appropriate transport manager, and preparing the
5
+ * response for Hono to send.
6
+ * @module src/mcp-server/transports/http/mcpTransportMiddleware
7
+ */
8
+ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
9
+ import { createMiddleware } from "hono/factory";
10
+ import { config } from "../../../config/index.js";
11
+ import { requestContextService } from "../../../utils/index.js";
12
+ import { StatelessTransportManager } from "../core/statelessTransportManager.js";
13
+ /**
14
+ * Converts a Fetch API Headers object to Node.js IncomingHttpHeaders.
15
+ * @param headers - The Headers object to convert.
16
+ * @returns An object compatible with IncomingHttpHeaders.
17
+ */
18
+ function toIncomingHttpHeaders(headers) {
19
+ const result = {};
20
+ headers.forEach((value, key) => {
21
+ result[key] = value;
22
+ });
23
+ return result;
24
+ }
25
+ /**
26
+ * Handles a stateless request by creating an ephemeral transport manager.
27
+ * @param createServerInstanceFn - Function to create an McpServer instance.
28
+ * @param headers - The request headers.
29
+ * @param body - The request body.
30
+ * @param context - The request context.
31
+ * @returns A promise resolving with the transport response.
32
+ */
33
+ async function handleStatelessRequest(createServerInstanceFn, headers, body, context) {
34
+ const statelessManager = new StatelessTransportManager(createServerInstanceFn);
35
+ return statelessManager.handleRequest(toIncomingHttpHeaders(headers), body, context);
36
+ }
37
+ export const mcpTransportMiddleware = (transportManager, createServerInstanceFn) => createMiddleware(async (c, next) => {
38
+ const sessionId = c.req.header("mcp-session-id");
39
+ const context = requestContextService.createRequestContext({
40
+ operation: "mcpTransportMiddleware",
41
+ sessionId,
42
+ });
43
+ const body = await c.req.json();
44
+ let response;
45
+ if (isInitializeRequest(body)) {
46
+ if (config.mcpSessionMode === "stateless") {
47
+ response = await handleStatelessRequest(createServerInstanceFn, c.req.raw.headers, body, context);
48
+ }
49
+ else {
50
+ response = await transportManager.initializeAndHandle(toIncomingHttpHeaders(c.req.raw.headers), body, context);
51
+ }
52
+ }
53
+ else {
54
+ if (sessionId) {
55
+ response = await transportManager.handleRequest(toIncomingHttpHeaders(c.req.raw.headers), body, context, sessionId);
56
+ }
57
+ else {
58
+ response = await handleStatelessRequest(createServerInstanceFn, c.req.raw.headers, body, context);
59
+ }
60
+ }
61
+ c.set("mcpResponse", response);
62
+ await next();
63
+ });
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @fileoverview Barrel file for the Stdio transport module.
3
+ * @module src/mcp-server/transports/stdio/index
4
+ */
5
+ export { startStdioTransport } from "./stdioTransport.js";
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @fileoverview Barrel file for the Stdio transport module.
3
+ * @module src/mcp-server/transports/stdio/index
4
+ */
5
+ export { startStdioTransport } from "./stdioTransport.js";
@@ -18,7 +18,7 @@
18
18
  * @module src/mcp-server/transports/stdioTransport
19
19
  */
20
20
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
21
- import { RequestContext } from "../../utils/index.js";
21
+ import { RequestContext } from "../../../utils/index.js";
22
22
  /**
23
23
  * Connects a given `McpServer` instance to the Stdio transport.
24
24
  * This function initializes the SDK's `StdioServerTransport`, which manages
@@ -39,4 +39,4 @@ import { RequestContext } from "../../utils/index.js";
39
39
  * @returns A promise that resolves when the Stdio transport is successfully connected.
40
40
  * @throws {Error} If the connection fails during setup.
41
41
  */
42
- export declare function connectStdioTransport(server: McpServer, parentContext: RequestContext): Promise<void>;
42
+ export declare function startStdioTransport(server: McpServer, parentContext: RequestContext): Promise<void>;
@@ -18,7 +18,7 @@
18
18
  * @module src/mcp-server/transports/stdioTransport
19
19
  */
20
20
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
21
- import { ErrorHandler, logger } from "../../utils/index.js";
21
+ import { ErrorHandler, logger } from "../../../utils/index.js";
22
22
  /**
23
23
  * Connects a given `McpServer` instance to the Stdio transport.
24
24
  * This function initializes the SDK's `StdioServerTransport`, which manages
@@ -39,13 +39,13 @@ import { ErrorHandler, logger } from "../../utils/index.js";
39
39
  * @returns A promise that resolves when the Stdio transport is successfully connected.
40
40
  * @throws {Error} If the connection fails during setup.
41
41
  */
42
- export async function connectStdioTransport(server, parentContext) {
42
+ export async function startStdioTransport(server, parentContext) {
43
43
  const operationContext = {
44
44
  ...parentContext,
45
45
  operation: "connectStdioTransport",
46
46
  transportType: "Stdio",
47
47
  };
48
- logger.debug("Attempting to connect stdio transport...", operationContext);
48
+ logger.info("Attempting to connect stdio transport...", operationContext);
49
49
  try {
50
50
  logger.debug("Creating StdioServerTransport instance...", operationContext);
51
51
  const transport = new StdioServerTransport();
@@ -57,7 +57,12 @@ export async function connectStdioTransport(server, parentContext) {
57
57
  }
58
58
  }
59
59
  catch (err) {
60
- ErrorHandler.handleError(err, { ...operationContext, critical: true });
61
- throw err; // Re-throw after handling to allow caller to react if necessary
60
+ // Let the ErrorHandler log the error with all context, then rethrow.
61
+ throw ErrorHandler.handleError(err, {
62
+ operation: "connectStdioTransport",
63
+ context: operationContext,
64
+ critical: true,
65
+ rethrow: true,
66
+ });
62
67
  }
63
68
  }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @fileoverview Constants and shared type definitions for NCBI E-utility interactions.
3
- * @module src/services/NCBI/ncbiConstants
3
+ * @module src/services/NCBI/core/ncbiConstants
4
4
  */
5
5
  export declare const NCBI_EUTILS_BASE_URL = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils";
6
6
  /**
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * @fileoverview Constants and shared type definitions for NCBI E-utility interactions.
3
- * @module src/services/NCBI/ncbiConstants
3
+ * @module src/services/NCBI/core/ncbiConstants
4
4
  */
5
5
  export const NCBI_EUTILS_BASE_URL = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils";
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * @fileoverview Core client for making HTTP requests to NCBI E-utilities.
3
3
  * Handles request construction, API key injection, retries, and basic error handling.
4
- * @module src/services/NCBI/ncbiCoreApiClient
4
+ * @module src/services/NCBI/core/ncbiCoreApiClient
5
5
  */
6
6
  import { AxiosResponse } from "axios";
7
- import { RequestContext } from "../../utils/index.js";
7
+ import { RequestContext } from "../../../utils/index.js";
8
8
  import { NcbiRequestParams, NcbiRequestOptions } from "./ncbiConstants.js";
9
9
  export declare class NcbiCoreApiClient {
10
10
  private axiosInstance;
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * @fileoverview Core client for making HTTP requests to NCBI E-utilities.
3
3
  * Handles request construction, API key injection, retries, and basic error handling.
4
- * @module src/services/NCBI/ncbiCoreApiClient
4
+ * @module src/services/NCBI/core/ncbiCoreApiClient
5
5
  */
6
6
  import axios from "axios";
7
- import { config } from "../../config/index.js";
8
- import { BaseErrorCode, McpError } from "../../types-global/errors.js";
9
- import { logger, requestContextService, sanitizeInputForLogging, } from "../../utils/index.js";
7
+ import { config } from "../../../config/index.js";
8
+ import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
9
+ import { logger, requestContextService, sanitizeInputForLogging, } from "../../../utils/index.js";
10
10
  import { NCBI_EUTILS_BASE_URL, } from "./ncbiConstants.js";
11
11
  export class NcbiCoreApiClient {
12
12
  constructor() {
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * @fileoverview Manages a queue for NCBI E-utility requests to ensure compliance with rate limits.
3
- * @module src/services/NCBI/ncbiRequestQueueManager
3
+ * @module src/services/NCBI/core/ncbiRequestQueueManager
4
4
  */
5
- import { RequestContext } from "../../utils/index.js";
5
+ import { RequestContext } from "../../../utils/index.js";
6
6
  import { NcbiRequestParams } from "./ncbiConstants.js";
7
7
  /**
8
8
  * Interface for a queued NCBI request.
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * @fileoverview Manages a queue for NCBI E-utility requests to ensure compliance with rate limits.
3
- * @module src/services/NCBI/ncbiRequestQueueManager
3
+ * @module src/services/NCBI/core/ncbiRequestQueueManager
4
4
  */
5
- import { config } from "../../config/index.js";
6
- import { logger, requestContextService, sanitizeInputForLogging, } from "../../utils/index.js";
5
+ import { config } from "../../../config/index.js";
6
+ import { logger, requestContextService, sanitizeInputForLogging, } from "../../../utils/index.js";
7
7
  export class NcbiRequestQueueManager {
8
8
  constructor() {
9
9
  this.requestQueue = [];
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * @fileoverview Handles parsing of NCBI E-utility responses and NCBI-specific error extraction.
3
- * @module src/services/NCBI/ncbiResponseHandler
3
+ * @module src/services/NCBI/core/ncbiResponseHandler
4
4
  */
5
5
  import { AxiosResponse } from "axios";
6
- import { RequestContext } from "../../utils/index.js";
6
+ import { RequestContext } from "../../../utils/index.js";
7
7
  import { NcbiRequestOptions } from "./ncbiConstants.js";
8
8
  export declare class NcbiResponseHandler {
9
9
  private xmlParser;
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * @fileoverview Handles parsing of NCBI E-utility responses and NCBI-specific error extraction.
3
- * @module src/services/NCBI/ncbiResponseHandler
3
+ * @module src/services/NCBI/core/ncbiResponseHandler
4
4
  */
5
5
  import { XMLParser, XMLValidator } from "fast-xml-parser";
6
- import { BaseErrorCode, McpError } from "../../types-global/errors.js";
7
- import { logger, requestContextService, sanitizeInputForLogging, } from "../../utils/index.js";
6
+ import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
7
+ import { logger, requestContextService, sanitizeInputForLogging, } from "../../../utils/index.js";
8
8
  export class NcbiResponseHandler {
9
9
  constructor() {
10
10
  this.xmlParser = new XMLParser({
@@ -5,10 +5,10 @@
5
5
  * retries, and parsing of XML/JSON responses. It aims to provide a robust
6
6
  * and compliant interface for other parts of the pubmed-mcp-server to
7
7
  * access PubMed data.
8
- * @module src/services/NCBI/ncbiService
8
+ * @module src/services/NCBI/core/ncbiService
9
9
  */
10
- import { ESearchResult, EFetchArticleSet } from "../../types-global/pubmedXml.js";
11
- import { RequestContext } from "../../utils/index.js";
10
+ import { ESearchResult, EFetchArticleSet } from "../../../types-global/pubmedXml.js";
11
+ import { RequestContext } from "../../../utils/index.js";
12
12
  import { NcbiRequestParams, NcbiRequestOptions } from "./ncbiConstants.js";
13
13
  export declare class NcbiService {
14
14
  private queueManager;
@@ -5,9 +5,9 @@
5
5
  * retries, and parsing of XML/JSON responses. It aims to provide a robust
6
6
  * and compliant interface for other parts of the pubmed-mcp-server to
7
7
  * access PubMed data.
8
- * @module src/services/NCBI/ncbiService
8
+ * @module src/services/NCBI/core/ncbiService
9
9
  */
10
- import { logger, requestContextService, } from "../../utils/index.js";
10
+ import { logger, requestContextService, } from "../../../utils/index.js";
11
11
  import { NcbiCoreApiClient } from "./ncbiCoreApiClient.js";
12
12
  import { NcbiRequestQueueManager } from "./ncbiRequestQueueManager.js";
13
13
  import { NcbiResponseHandler } from "./ncbiResponseHandler.js";
@@ -2,7 +2,7 @@
2
2
  * @fileoverview Helper functions for parsing ESummary results from NCBI.
3
3
  * Handles different ESummary XML structures and formats the data into
4
4
  * consistent ParsedBriefSummary objects.
5
- * @module src/utils/parsing/ncbi-parsing/eSummaryResultParser
5
+ * @module src/services/NCBI/parsing/eSummaryResultParser
6
6
  */
7
7
  import { ESummaryResult, ParsedBriefSummary, ESummaryAuthor as XmlESummaryAuthor } from "../../../types-global/pubmedXml.js";
8
8
  import { RequestContext } from "../../../utils/index.js";
@@ -2,7 +2,7 @@
2
2
  * @fileoverview Helper functions for parsing ESummary results from NCBI.
3
3
  * Handles different ESummary XML structures and formats the data into
4
4
  * consistent ParsedBriefSummary objects.
5
- * @module src/utils/parsing/ncbi-parsing/eSummaryResultParser
5
+ * @module src/services/NCBI/parsing/eSummaryResultParser
6
6
  */
7
7
  import { dateParser, logger, requestContextService, } from "../../../utils/index.js"; // Note: utils/index.js is the barrel file
8
8
  import { ensureArray, getAttribute, getText } from "./xmlGenericHelpers.js";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @fileoverview Barrel file for NCBI XML parsing helper utilities.
3
3
  * Re-exports functions from more specific parser modules.
4
- * @module src/utils/parsing/ncbi-parsing/index
4
+ * @module src/services/NCBI/parsing/index
5
5
  */
6
6
  export * from "./xmlGenericHelpers.js";
7
7
  export * from "./pubmedArticleStructureParser.js";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @fileoverview Barrel file for NCBI XML parsing helper utilities.
3
3
  * Re-exports functions from more specific parser modules.
4
- * @module src/utils/parsing/ncbi-parsing/index
4
+ * @module src/services/NCBI/parsing/index
5
5
  */
6
6
  export * from "./xmlGenericHelpers.js";
7
7
  export * from "./pubmedArticleStructureParser.js";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @fileoverview Helper functions for parsing detailed PubMed Article XML structures,
3
3
  * typically from EFetch results.
4
- * @module src/utils/parsing/ncbi-parsing/pubmedArticleStructureParser
4
+ * @module src/services/NCBI/parsing/pubmedArticleStructureParser
5
5
  */
6
6
  import { XmlArticle, XmlAuthorList, XmlGrantList, XmlJournal, XmlKeywordList, XmlMedlineCitation, XmlMeshHeadingList, XmlPublicationTypeList, ParsedArticleAuthor, ParsedJournalInfo, ParsedMeshTerm, ParsedGrant, ParsedArticleDate } from "../../../types-global/pubmedXml.js";
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @fileoverview Helper functions for parsing detailed PubMed Article XML structures,
3
3
  * typically from EFetch results.
4
- * @module src/utils/parsing/ncbi-parsing/pubmedArticleStructureParser
4
+ * @module src/services/NCBI/parsing/pubmedArticleStructureParser
5
5
  */
6
6
  import { ensureArray, getText, getAttribute } from "./xmlGenericHelpers.js";
7
7
  /**