#
tokens: 48781/50000 81/111 files (page 1/4)
lines: off (toggle) GitHub
raw markdown copy
This is page 1 of 4. Use http://codebase.md/cyanheads/pubmed-mcp-server?page={x} to view the full context.

# Directory Structure

```
├── .clinerules
│   └── clinerules.md
├── .dockerignore
├── .github
│   ├── FUNDING.yml
│   └── workflows
│       └── publish.yml
├── .gitignore
├── .ncurc.json
├── CHANGELOG.md
├── Dockerfile
├── docs
│   ├── project-spec.md
│   ├── publishing-mcp-server-registry.md
│   └── tree.md
├── eslint.config.js
├── examples
│   ├── generate_pubmed_chart
│   │   ├── bar_chart.png
│   │   ├── doughnut_chart.png
│   │   ├── line_chart.png
│   │   ├── pie_chart.png
│   │   ├── polar_chart.png
│   │   ├── radar_chart.png
│   │   └── scatter_plot.png
│   ├── pubmed_article_connections_1.md
│   ├── pubmed_article_connections_2.md
│   ├── pubmed_fetch_contents_example.md
│   ├── pubmed_research_agent_example.md
│   └── pubmed_search_articles_example.md
├── LICENSE
├── mcp.json
├── package-lock.json
├── package.json
├── README.md
├── repomix.config.json
├── scripts
│   ├── clean.ts
│   ├── fetch-openapi-spec.ts
│   ├── make-executable.ts
│   ├── tree.ts
│   └── validate-mcp-publish-schema.ts
├── server.json
├── smithery.yaml
├── src
│   ├── config
│   │   └── index.ts
│   ├── index.ts
│   ├── mcp-server
│   │   ├── server.ts
│   │   ├── tools
│   │   │   ├── pubmedArticleConnections
│   │   │   │   ├── index.ts
│   │   │   │   ├── logic
│   │   │   │   │   ├── citationFormatter.ts
│   │   │   │   │   ├── elinkHandler.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── types.ts
│   │   │   │   └── registration.ts
│   │   │   ├── pubmedFetchContents
│   │   │   │   ├── index.ts
│   │   │   │   ├── logic.ts
│   │   │   │   └── registration.ts
│   │   │   ├── pubmedGenerateChart
│   │   │   │   ├── index.ts
│   │   │   │   ├── logic.ts
│   │   │   │   └── registration.ts
│   │   │   ├── pubmedResearchAgent
│   │   │   │   ├── index.ts
│   │   │   │   ├── logic
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── inputSchema.ts
│   │   │   │   │   ├── outputTypes.ts
│   │   │   │   │   └── planOrchestrator.ts
│   │   │   │   ├── logic.ts
│   │   │   │   └── registration.ts
│   │   │   └── pubmedSearchArticles
│   │   │       ├── index.ts
│   │   │       ├── logic.ts
│   │   │       └── registration.ts
│   │   └── transports
│   │       ├── auth
│   │       │   ├── authFactory.ts
│   │       │   ├── authMiddleware.ts
│   │       │   ├── index.ts
│   │       │   ├── lib
│   │       │   │   ├── authContext.ts
│   │       │   │   ├── authTypes.ts
│   │       │   │   └── authUtils.ts
│   │       │   └── strategies
│   │       │       ├── authStrategy.ts
│   │       │       ├── jwtStrategy.ts
│   │       │       └── oauthStrategy.ts
│   │       ├── core
│   │       │   ├── baseTransportManager.ts
│   │       │   ├── headerUtils.ts
│   │       │   ├── honoNodeBridge.ts
│   │       │   ├── statefulTransportManager.ts
│   │       │   ├── statelessTransportManager.ts
│   │       │   └── transportTypes.ts
│   │       ├── http
│   │       │   ├── httpErrorHandler.ts
│   │       │   ├── httpTransport.ts
│   │       │   ├── httpTypes.ts
│   │       │   ├── index.ts
│   │       │   └── mcpTransportMiddleware.ts
│   │       └── stdio
│   │           ├── index.ts
│   │           └── stdioTransport.ts
│   ├── services
│   │   └── NCBI
│   │       ├── core
│   │       │   ├── ncbiConstants.ts
│   │       │   ├── ncbiCoreApiClient.ts
│   │       │   ├── ncbiRequestQueueManager.ts
│   │       │   ├── ncbiResponseHandler.ts
│   │       │   └── ncbiService.ts
│   │       └── parsing
│   │           ├── eSummaryResultParser.ts
│   │           ├── index.ts
│   │           ├── pubmedArticleStructureParser.ts
│   │           └── xmlGenericHelpers.ts
│   ├── types-global
│   │   ├── declarations.d.ts
│   │   ├── errors.ts
│   │   └── pubmedXml.ts
│   └── utils
│       ├── index.ts
│       ├── internal
│       │   ├── errorHandler.ts
│       │   ├── index.ts
│       │   ├── logger.ts
│       │   ├── performance.ts
│       │   └── requestContext.ts
│       ├── metrics
│       │   ├── index.ts
│       │   └── tokenCounter.ts
│       ├── network
│       │   ├── fetchWithTimeout.ts
│       │   └── index.ts
│       ├── parsing
│       │   ├── dateParser.ts
│       │   ├── index.ts
│       │   └── jsonParser.ts
│       ├── scheduling
│       │   ├── index.ts
│       │   └── scheduler.ts
│       ├── security
│       │   ├── idGenerator.ts
│       │   ├── index.ts
│       │   ├── rateLimiter.ts
│       │   └── sanitization.ts
│       └── telemetry
│           ├── instrumentation.ts
│           └── semconv.ts
├── tsconfig.json
├── tsconfig.typedoc.json
├── tsdoc.json
└── typedoc.json
```

# Files

--------------------------------------------------------------------------------
/.ncurc.json:
--------------------------------------------------------------------------------

```json
{
  "reject": ["chrono-node", "dotenv", "zod", "@hono/node-server", "hono"]
}

```

--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------

```
node_modules
npm-debug.log
.npm
.nyc_output
coverage
.git
.gitignore
README.md
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
dist
*.log
.DS_Store
Thumbs.db
```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
# =============================================================================
# OPERATING SYSTEM FILES
# =============================================================================
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

# =============================================================================
# IDE AND EDITOR FILES
# =============================================================================
.idea/
*.swp
*.swo
*~
*.sublime-workspace
*.sublime-project
.history/

# =============================================================================
# NODE.JS & PACKAGE MANAGERS
# =============================================================================
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
.npm
.pnp.js
.pnp.cjs
.pnp.mjs
.pnp.json
.pnp.ts

# =============================================================================
# TYPESCRIPT & JAVASCRIPT
# =============================================================================
*.tsbuildinfo
.tscache/
*.js.map
*.mjs.map
*.cjs.map
*.d.ts.map
*.d.ts
!*.d.ts.template
*.tgz
.eslintcache
.rollup.cache

# =============================================================================
# PYTHON
# =============================================================================
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
develop-eggs/
eggs/
.eggs/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
.pytest_cache/
.coverage
htmlcov/
.tox/
.venv
venv/
ENV/

# =============================================================================
# JAVA
# =============================================================================
*.class
*.jar
*.war
*.nar
*.ear
hs_err_pid*
target/
.gradle/

# =============================================================================
# RUBY
# =============================================================================
*.gem
*.rbc
/.config
/coverage/
/InstalledFiles
/pkg/
/spec/reports/
/test/tmp/
/test/version_tmp/
/tmp/
.byebug_history

# =============================================================================
# BUILD & DISTRIBUTION
# =============================================================================
build/
dist/
out/

# =============================================================================
# COMPILED FILES
# =============================================================================
*.com
*.dll
*.exe
*.o

# =============================================================================
# PACKAGE & ARCHIVE FILES
# =============================================================================
*.7z
*.dmg
*.gz
*.iso
*.rar
*.tar
*.tar.gz
*.zip

# =============================================================================
# LOGS & DATABASES
# =============================================================================
*.log
*.sql
*.sqlite
*.sqlite3
logs/

# =============================================================================
# TESTING & COVERAGE
# =============================================================================
coverage/
.nyc_output/

# =============================================================================
# CACHE & TEMPORARY FILES
# =============================================================================
.cache/
.parcel-cache/
*.bak

# =============================================================================
# ENVIRONMENT & CONFIGURATION
# =============================================================================
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
.sample-env
!sample.template.*
mcp-servers.json
mcp-config.json
.wrangler
worker-configuration.d.ts

# =============================================================================
# DEMO & EXAMPLE DIRECTORIES
# =============================================================================

# =============================================================================
# GENERATED DOCUMENTATION
# =============================================================================
docs/api/

# =============================================================================
# APPLICATION SPECIFIC
# =============================================================================
.storage/
repomix-output*
duckdata/
.claude
data/
docs/devdocs.md

# =============================================================================
# MCP REGISTRY
# =============================================================================
.mcpregistry_github_token
.mcpregistry_registry_token

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
<div align="center">

# pubmed-mcp-server

**Empower your AI agents and research tools with seamless PubMed integration!**

[![TypeScript](https://img.shields.io/badge/TypeScript-^5.8.3-blue.svg?style=flat-square)](https://www.typescriptlang.org/)
[![Model Context Protocol](https://img.shields.io/badge/MCP%20SDK-^1.18.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/)
[![Version](https://img.shields.io/badge/Version-1.4.4-blue.svg?style=flat-square)](./CHANGELOG.md)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=flat-square)](https://opensource.org/licenses/Apache-2.0)
[![Status](https://img.shields.io/badge/Status-Stable-green.svg?style=flat-square)](https://github.com/cyanheads/pubmed-mcp-server/issues)
[![GitHub](https://img.shields.io/github/stars/cyanheads/pubmed-mcp-server?style=social)](https://github.com/cyanheads/pubmed-mcp-server)

</div>

A production-grade Model Context Protocol (MCP) server that empowers AI agents and research tools with comprehensive access to PubMed. Enables advanced, automated workflows for searching, retrieving, analyzing, and visualizing biomedical and scientific literature via NCBI E-utilities.

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.

## 🚀 Core Capabilities: PubMed Tools 🛠️

This server equips your AI with specialized tools to interact with PubMed:

| Tool Name                                                                        | Description                                                                             | Example                                                                                                  |
| :------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------- |
| [`pubmed_search_articles`](./src/mcp-server/tools/pubmedSearchArticles/)         | Searches PubMed for articles based on your query.                                       | [View Example](./examples/pubmed_search_articles_example.md)                                             |
| [`pubmed_fetch_contents`](./src/mcp-server/tools/pubmedFetchContents/)           | Retrieves detailed information for PubMed articles.                                     | [View Example](./examples/pubmed_fetch_contents_example.md)                                              |
| [`pubmed_article_connections`](./src/mcp-server/tools/pubmedArticleConnections/) | Finds related articles (cited by, similar, references) or formats citations for a PMID. | [Ex. 1](./examples/pubmed_article_connections_1.md), [Ex. 2](./examples/pubmed_article_connections_2.md) |
| [`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)                                              |
| [`pubmed_generate_chart`](./src/mcp-server/tools/pubmedGenerateChart/)           | Generates a chart image (PNG) from given input data.                                    | [View Examples](./examples/generate_pubmed_chart/)                                                       |

---

## Table of Contents

| [Overview](#overview)           | [Features](#features)                          | [Installation](#installation) |
| :------------------------------ | :--------------------------------------------- | :---------------------------- |
| [Configuration](#configuration) | [Project Structure](#project-structure)        |
| [Tools](#tools)                 | [Development & Testing](#development--testing) | [License](#license)           |

## Overview

The PubMed MCP Server acts as a bridge, allowing applications (MCP Clients) that understand the Model Context Protocol (MCP) – like advanced AI assistants (LLMs), IDE extensions, or custom research tools – to interact directly and efficiently with PubMed's vast biomedical literature database.

Instead of complex API integration or manual searches, your tools can leverage this server to:

- **Automate research workflows**: Search literature, fetch full article metadata, track citations, and generate research plans programmatically.
- **Gain research insights**: Access detailed publication data, author information, journal details, MeSH terms, and citation networks without leaving the host application.
- **Integrate PubMed into AI-driven research**: Enable LLMs to conduct literature reviews, analyze research trends, and support evidence-based decision making.
- **Visualize research data**: Generate charts and visualizations from publication metadata and search results.

Built on the robust `mcp-ts-template`, this server provides a standardized, secure, and efficient way to expose PubMed functionality via the MCP standard. It achieves this by integrating with NCBI's E-utilities API, ensuring compliance with rate limits and providing comprehensive error handling.

> **Developer Note**: This repository includes a [.clinerules](.clinerules) file that serves as a developer cheat sheet for your LLM coding agent with quick reference for the codebase patterns, file locations, and code snippets.

## Features

### Core Utilities

Leverages the robust utilities provided by the `mcp-ts-template`:

- **Logging**: Structured, configurable logging (file rotation, stdout JSON, MCP notifications) with sensitive data redaction.
- **Error Handling**: Centralized error processing, standardized error types (`McpError`), and automatic logging.
- **Configuration**: Environment variable loading (`dotenv`) with comprehensive validation using Zod.
- **Input Validation/Sanitization**: Uses `zod` for schema validation and custom sanitization logic.
- **Request Context**: Tracking and correlation of operations via unique request IDs using `AsyncLocalStorage`.
- **Type Safety**: Strong typing enforced by TypeScript and Zod schemas.
- **HTTP Transport**: High-performance HTTP server using **Hono**, featuring session management and authentication support.
- **Authentication**: Robust authentication layer supporting JWT and OAuth 2.1, with fine-grained scope enforcement.
- **Deployment**: Multi-stage `Dockerfile` for creating small, secure production images with native dependency support.

### PubMed Integration

- **NCBI E-utilities Integration**: Comprehensive access to ESearch, EFetch, ELink, and ESummary APIs with automatic XML parsing.
- **Advanced Search Capabilities**: Complex query construction with date ranges, publication types, author filters, and MeSH term support.
- **Full Article Metadata**: Retrieve complete publication data including abstracts, authors, affiliations, journal information, DOIs, and citation data.
- **Citation Network Analysis**: Find related articles, citing articles, and reference lists through ELink integration.
- **Research Planning**: Generate structured research plans with automated literature search strategies.
- **Data Visualization**: Create PNG charts from publication metadata (bar, line, scatter, pie, bubble, radar, polarArea).
- **Multiple Output Formats**: Support for JSON, MEDLINE text, full XML, and formatted citations (RIS, BibTeX, APA, MLA).
- **Batch Processing**: Efficient handling of multiple PMIDs with pagination support.

## Installation

### Prerequisites

- [Node.js (>=20.0.0)](https://nodejs.org/)
- [npm](https://www.npmjs.com/) (comes with Node.js)
- **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/)

### MCP Client Settings

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.
All environment variables are optional, but recommended for production use. NCBI API key is recommended to avoid rate limiting issues.

```json
{
  "mcpServers": {
    "pubmed-mcp-server": {
      "command": "npx",
      "args": ["@cyanheads/pubmed-mcp-server"],
      "env": {
        "MCP_LOG_LEVEL": "debug",
        "MCP_TRANSPORT_TYPE": "http",
        "MCP_HTTP_PORT": "3017",
        "NCBI_API_KEY": "YOUR_NCBI_API_KEY_HERE"
      }
    }
  }
}
```

### If running manually (not via MCP client for development or testing)

#### Install via npm

```bash
npm install @cyanheads/pubmed-mcp-server
```

#### Alternatively Install from Source

1.  Clone the repository:
    ```bash
    git clone https://github.com/cyanheads/pubmed-mcp-server.git
    cd pubmed-mcp-server
    ```
2.  Install dependencies:
    ```bash
    npm install
    ```
3.  Build the project:
    ```bash
    npm run build
    ```

## Configuration

### Environment Variables

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.

| Variable              | Description                                                                              | Default       |
| :-------------------- | :--------------------------------------------------------------------------------------- | :------------ |
| `MCP_TRANSPORT_TYPE`  | Transport mechanism: `stdio` or `http`.                                                  | `stdio`       |
| `MCP_HTTP_PORT`       | Port for the HTTP server (if `MCP_TRANSPORT_TYPE=http`).                                 | `3017`        |
| `MCP_HTTP_HOST`       | Host address for the HTTP server (if `MCP_TRANSPORT_TYPE=http`).                         | `127.0.0.1`   |
| `MCP_ALLOWED_ORIGINS` | Comma-separated list of allowed origins for CORS (if `MCP_TRANSPORT_TYPE=http`).         | (none)        |
| `MCP_LOG_LEVEL`       | Logging level (`debug`, `info`, `notice`, `warning`, `error`, `crit`, `alert`, `emerg`). | `debug`       |
| `MCP_AUTH_MODE`       | Authentication mode for HTTP: `jwt` or `oauth`.                                          | `jwt`         |
| `MCP_AUTH_SECRET_KEY` | **Required for `jwt` auth.** Minimum 32-character secret key for JWT authentication.     | (none)        |
| `NCBI_API_KEY`        | **Recommended.** Your NCBI API Key for higher rate limits and reliable access.           | (none)        |
| `LOGS_DIR`            | Directory for log file storage.                                                          | `logs/`       |
| `NODE_ENV`            | Runtime environment (`development`, `production`).                                       | `development` |

## Project Structure

The codebase follows a modular structure within the `src/` directory:

```
src/
├── index.ts              # Entry point: Initializes and starts the server
├── config/               # Configuration loading (env vars, package info)
│   └── index.ts
├── mcp-server/           # Core MCP server logic and capability registration
│   ├── server.ts         # Server setup, capability registration
│   ├── transports/       # Transport handling (stdio, http)
│   └── tools/            # MCP Tool implementations (subdirs per tool)
├── services/             # External service integrations
│   └── NCBI/             # NCBI E-utilities API client and parsing
├── types-global/         # Shared TypeScript type definitions
└── utils/                # Common utility functions (logger, error handler, etc.)
```

For a detailed file tree, run `npm run tree` or see [docs/tree.md](docs/tree.md).

## Tools

The PubMed MCP Server provides a comprehensive suite of tools for biomedical literature research, callable via the Model Context Protocol.

| Tool Name                    | Description                                                            | Key Arguments                                                                                             |
| :--------------------------- | :--------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------- |
| `pubmed_search_articles`     | Searches PubMed for articles using queries, filters, and date ranges.  | `queryTerm`, `maxResults?`, `sortBy?`, `dateRange?`, `filterByPublicationTypes?`, `fetchBriefSummaries?`  |
| `pubmed_fetch_contents`      | Fetches detailed article information using PMIDs or search history.    | `pmids?`, `queryKey?`, `webEnv?`, `detailLevel?`, `includeMeshTerms?`, `includeGrantInfo?`                |
| `pubmed_article_connections` | Finds related articles, citations, and references for a given PMID.    | `sourcePmid`, `relationshipType?`, `maxRelatedResults?`, `citationStyles?`                                |
| `pubmed_research_agent`      | Generates structured research plans with literature search strategies. | `project_title_suggestion`, `primary_research_goal`, `research_keywords`, `organism_focus?`, `p1_*`, etc. |
| `pubmed_generate_chart`      | Creates customizable PNG charts from structured publication data.      | `chartType`, `dataValues`, `xField`, `yField`, `title?`, `seriesField?`, `sizeField?`                     |

_Note: All tools support comprehensive error handling and return structured JSON responses._

## Examples

Comprehensive usage examples for each tool are available in the [`examples/`](examples/) directory.

- **`pubmed_search_articles`**: [View Example](./examples/pubmed_search_articles_example.md)
- **`pubmed_fetch_contents`**: [View Example](./examples/pubmed_fetch_contents_example.md)
- **`pubmed_article_connections`**: [Ex. 1](./examples/pubmed_article_connections_1.md), [Ex. 2](./examples/pubmed_article_connections_2.md)
- **`pubmed_research_agent`**: [View Example](./examples/pubmed_research_agent_example.md)
- **`pubmed_generate_chart`**: [View Examples](./examples/generate_pubmed_chart/)

## Development & Testing

### Development Scripts

```bash
# Build the project (compile TS to JS in dist/ and make executable)
npm run build

# Clean build artifacts
npm run clean

# Clean build artifacts and then rebuild the project
npm run rebuild

# Lint the codebase
npm run lint

# Format code with Prettier
npm run format

# Generate a file tree representation for documentation
npm run tree
```

### Running the Server

```bash
# Start the server using stdio (default)
npm start
# Or explicitly:
npm run start:stdio

# Start the server using HTTP transport
npm run start:http

# Test the server locally using the MCP inspector tool (stdio transport)
npm run inspector

# Test the server locally using the MCP inspector tool (http transport)
npm run inspector:http
```

## License

This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.

---

<div align="center">
Built with the <a href="https://modelcontextprotocol.io/">Model Context Protocol</a>
</div>

```

--------------------------------------------------------------------------------
/src/types-global/declarations.d.ts:
--------------------------------------------------------------------------------

```typescript
declare module "citation-js";

```

--------------------------------------------------------------------------------
/tsconfig.typedoc.json:
--------------------------------------------------------------------------------

```json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "rootDir": "."
  },
  "include": ["src/**/*", "scripts/**/*.ts"]
  // The 'exclude' is also inherited.
}

```

--------------------------------------------------------------------------------
/src/mcp-server/transports/stdio/index.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Barrel file for the Stdio transport module.
 * @module src/mcp-server/transports/stdio/index
 */

export { startStdioTransport } from "./stdioTransport.js";

```

--------------------------------------------------------------------------------
/src/utils/scheduling/index.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Barrel file for the scheduling module.
 * Exports the singleton schedulerService for application-wide use.
 * @module src/utils/scheduling
 */

export * from "./scheduler.js";

```

--------------------------------------------------------------------------------
/src/utils/network/index.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Barrel file for network utilities.
 * @module src/utils/network/index
 */

export * from "./fetchWithTimeout.js";
export type { FetchWithTimeoutOptions } from "./fetchWithTimeout.js"; // Explicitly re-exporting type

```

--------------------------------------------------------------------------------
/src/utils/metrics/index.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Barrel file for metrics-related utility modules.
 * This file re-exports utilities for collecting and processing metrics,
 * such as token counting.
 * @module src/utils/metrics
 */

export * from "./tokenCounter.js";

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/pubmedFetchContents/index.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Barrel file for the pubmed_fetch_contents tool.
 * Exports the tool's registration function.
 * @module src/mcp-server/tools/pubmedFetchContents/index
 */

export { registerPubMedFetchContentsTool } from "./registration.js";

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/pubmedResearchAgent/index.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Barrel file for the pubmed_research_agent tool.
 * Exports the tool's registration function.
 * @module src/mcp-server/tools/pubmedResearchAgent/index
 */

export { registerPubMedResearchAgentTool } from "./registration.js";

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/pubmedSearchArticles/index.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Barrel file for the pubmedSearchArticles tool.
 * Exports the tool's registration function.
 * @module src/mcp-server/tools/pubmedSearchArticles/index
 */

export { registerPubMedSearchArticlesTool } from "./registration.js";

```

--------------------------------------------------------------------------------
/src/utils/parsing/index.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Barrel file for parsing utility modules.
 * This file re-exports utilities related to parsing various data formats,
 * such as JSON and dates.
 * @module src/utils/parsing
 */

export * from "./dateParser.js";
export * from "./jsonParser.js";

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/pubmedArticleConnections/index.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Barrel file for the pubmedArticleConnections tool.
 * Exports the registration function for this tool.
 * @module src/mcp-server/tools/pubmedArticleConnections/index
 */

export { registerPubMedArticleConnectionsTool } from "./registration.js";

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/pubmedGenerateChart/index.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Barrel export for the 'pubmed_generate_chart' tool.
 * This file re-exports the registration function for the tool,
 * making it easier to import and register with the MCP server.
 * @module src/mcp-server/tools/pubmedGenerateChart/index
 */
export * from "./registration.js";

```

--------------------------------------------------------------------------------
/src/mcp-server/transports/http/index.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Barrel file for the HTTP transport module.
 * @module src/mcp-server/transports/http/index
 */

export { createHttpApp, startHttpTransport } from "./httpTransport.js";
export { httpErrorHandler } from "./httpErrorHandler.js";
export type { HonoNodeBindings } from "./httpTypes.js";

```

--------------------------------------------------------------------------------
/src/utils/security/index.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Barrel file for security-related utility modules.
 * This file re-exports utilities for input sanitization, rate limiting,
 * and ID generation.
 * @module src/utils/security
 */

export * from "./idGenerator.js";
export * from "./rateLimiter.js";
export * from "./sanitization.js";

```

--------------------------------------------------------------------------------
/src/services/NCBI/parsing/index.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Barrel file for NCBI XML parsing helper utilities.
 * Re-exports functions from more specific parser modules.
 * @module src/services/NCBI/parsing/index
 */

export * from "./xmlGenericHelpers.js";
export * from "./pubmedArticleStructureParser.js";
export * from "./eSummaryResultParser.js";

```

--------------------------------------------------------------------------------
/typedoc.json:
--------------------------------------------------------------------------------

```json
{
  "$schema": "https://typedoc.org/schema.json",
  "entryPoints": ["src", "scripts"],
  "entryPointStrategy": "expand",
  "out": "docs/api",
  "readme": "README.md",
  "name": "mcp-ts-template API Documentation",
  "includeVersion": true,
  "excludePrivate": true,
  "excludeProtected": true,
  "excludeInternal": true,
  "theme": "default"
}

```

--------------------------------------------------------------------------------
/src/utils/internal/index.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Barrel file for internal utility modules.
 * This file re-exports core internal utilities related to error handling,
 * logging, and request context management.
 * @module src/utils/internal
 */

export * from "./errorHandler.js";
export * from "./logger.js";
export * from "./performance.js";
export * from "./requestContext.js";

```

--------------------------------------------------------------------------------
/mcp.json:
--------------------------------------------------------------------------------

```json
{
  "mcpServers": {
    "pubmed-mcp-server": {
      "command": "npx",
      "args": ["@cyanheads/pubmed-mcp-server"],
      "env": {
        "MCP_LOG_LEVEL": "debug",
        "MCP_TRANSPORT_TYPE": "http",
        "MCP_HTTP_PORT": "3017",
        "NCBI_API_KEY": "YOUR_NCBI_API_KEY_HERE",
        "MCP_HTTP_HOST": "0.0.0.0",
        "MCP_SESSION_MODE": "stateless"
      }
    }
  }
}

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/pubmedResearchAgent/logic/index.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Barrel export file for the pubmed_research_agent tool's core logic.
 * @module pubmedResearchAgent/logic/index
 */

export * from "./inputSchema.js";
export * from "./outputTypes.js";
export * from "./planOrchestrator.js";
// Individual section prompt generators are not typically exported directly from here,
// as they are used internally by the planOrchestrator.

```

--------------------------------------------------------------------------------
/repomix.config.json:
--------------------------------------------------------------------------------

```json
{
  "output": {
    "filePath": "repomix-output.xml",
    "style": "xml",
    "removeComments": false,
    "removeEmptyLines": false,
    "topFilesLength": 5,
    "showLineNumbers": false,
    "copyToClipboard": false
  },
  "include": [],
  "ignore": {
    "useGitignore": true,
    "useDefaultPatterns": true,
    "customPatterns": [".clinerules"]
  },
  "security": {
    "enableSecurityCheck": true
  }
}

```

--------------------------------------------------------------------------------
/src/utils/telemetry/semconv.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Defines local OpenTelemetry semantic convention constants to ensure
 * stability and avoid dependency conflicts with different versions of
 * `@opentelemetry/semantic-conventions`.
 * @module src/utils/telemetry/semconv
 */

/**
 * The method or function name, or equivalent (usually rightmost part of the code unit's name).
 */
export const ATTR_CODE_FUNCTION = "code.function";

/**
 * The "namespace" within which `code.function` is defined.
 * Usually the qualified class or module name, etc.
 */
export const ATTR_CODE_NAMESPACE = "code.namespace";

```

--------------------------------------------------------------------------------
/src/mcp-server/transports/http/httpTypes.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Defines custom types for the Hono HTTP transport layer.
 * @module src/mcp-server/transports/http/httpTypes
 */

import type { IncomingMessage, ServerResponse } from "http";

/**
 * Extends Hono's Bindings to include the raw Node.js request and response objects.
 * This is necessary for integrating with libraries like the MCP SDK that
 * need to write directly to the response stream.
 *
 * As per `@hono/node-server`, the response object is available on `c.env.outgoing`.
 */
export type HonoNodeBindings = {
  incoming: IncomingMessage;
  outgoing: ServerResponse;
};

```

--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------

```yaml
name: Publish Package to npm
on:
  push:
    tags:
      - "v*"

jobs:
  build-and-publish:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20.x"
          registry-url: "https://registry.npmjs.org"
          cache: "npm"

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build

      - name: Publish to npm
        run: npm publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

```

--------------------------------------------------------------------------------
/src/mcp-server/transports/auth/lib/authTypes.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Shared types for authentication middleware.
 * @module src/mcp-server/transports/auth/core/auth.types
 */

import type { AuthInfo as SdkAuthInfo } from "@modelcontextprotocol/sdk/server/auth/types.js";

/**
 * Defines the structure for authentication information derived from a token.
 * It extends the base SDK type to include common optional claims.
 */
export type AuthInfo = SdkAuthInfo & {
  subject?: string;
};

// The declaration for `http.IncomingMessage` is no longer needed here,
// as the new architecture avoids direct mutation where possible and handles
// the attachment within the Hono context.

```

--------------------------------------------------------------------------------
/src/mcp-server/transports/auth/index.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Barrel file for the auth module.
 * Exports core utilities and middleware strategies for easier imports.
 * @module src/mcp-server/transports/auth/index
 */

export { authContext } from "./lib/authContext.js";
export { withRequiredScopes } from "./lib/authUtils.js";
export type { AuthInfo } from "./lib/authTypes.js";

export { createAuthStrategy } from "./authFactory.js";
export { createAuthMiddleware } from "./authMiddleware.js";
export type { AuthStrategy } from "./strategies/authStrategy.js";
export { JwtStrategy } from "./strategies/jwtStrategy.js";
export { OauthStrategy } from "./strategies/oauthStrategy.js";

```

--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------

```javascript
import pluginJs from "@eslint/js";
import globals from "globals";
import tseslint from "typescript-eslint";

const combinedGlobals = { ...globals.browser, ...globals.node };
const trimmedGlobals = Object.fromEntries(
  Object.entries(combinedGlobals).map(([key, value]) => [key.trim(), value]),
);

export default [
  {
    ignores: ["coverage/", "tests/", "dist/", "build/", "node_modules/"],
  },
  { languageOptions: { globals: trimmedGlobals } },
  pluginJs.configs.recommended,
  ...tseslint.configs.recommended,
  {
    rules: {
      "@typescript-eslint/no-unused-vars": [
        "error",
        {
          argsIgnorePattern: "^_",
          varsIgnorePattern: "^_",
          caughtErrorsIgnorePattern: "^_",
        },
      ],
    },
  },
];

```

--------------------------------------------------------------------------------
/src/mcp-server/transports/auth/strategies/authStrategy.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Defines the interface for all authentication strategies.
 * This interface establishes a contract for verifying authentication tokens,
 * ensuring that any authentication method (JWT, OAuth, etc.) can be used
 * interchangeably by the core authentication middleware.
 * @module src/mcp-server/transports/auth/strategies/AuthStrategy
 */
import type { AuthInfo } from "../lib/authTypes.js";

export interface AuthStrategy {
  /**
   * Verifies an authentication token.
   * @param token The raw token string extracted from the request.
   * @returns A promise that resolves with the AuthInfo on successful verification.
   * @throws {McpError} if the token is invalid, expired, or fails verification for any reason.
   */
  verify(token: string): Promise<AuthInfo>;
}

```

--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------

```yaml
# These are supported funding model platforms

github: cyanheads
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: cyanheads
thanks_dev: # Replace with a single thanks.dev username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

```

--------------------------------------------------------------------------------
/src/utils/index.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Barrel file for the utils module.
 * This file re-exports all utilities from their categorized subdirectories,
 * providing a single entry point for accessing utility functions.
 * @module src/utils
 */

// Re-export all utilities from their categorized subdirectories
export * from "./internal/index.js";
export * from "./metrics/index.js";
export * from "./parsing/index.js";
export * from "./security/index.js";
export * from "./network/index.js";
export * from "./scheduling/index.js";

// It's good practice to have index.ts files in each subdirectory
// that export the contents of that directory.
// Assuming those will be created or already exist.
// If not, this might need adjustment to export specific files, e.g.:
// export * from './internal/errorHandler.js';
// export * from './internal/logger.js';
// ... etc.

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/pubmedArticleConnections/logic/types.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Shared type definitions for the pubmedArticleConnections tool logic.
 * @module src/mcp-server/tools/pubmedArticleConnections/logic/types
 */

import type { PubMedArticleConnectionsInput } from "./index.js";

// Helper type for enriched related articles
export interface RelatedArticle {
  pmid: string;
  title?: string;
  authors?: string; // e.g., "Smith J, Doe A"
  score?: number; // From ELink, if available
  linkUrl: string;
}

export interface CitationOutput {
  ris?: string;
  bibtex?: string;
  apa_string?: string;
  mla_string?: string;
}

export interface ToolOutputData {
  sourcePmid: string;
  relationshipType: PubMedArticleConnectionsInput["relationshipType"];
  relatedArticles: RelatedArticle[];
  citations: CitationOutput;
  retrievedCount: number;
  eUtilityUrl?: string; // ELink or EFetch URL
  message?: string; // For errors or additional info
}

```

--------------------------------------------------------------------------------
/tsdoc.json:
--------------------------------------------------------------------------------

```json
{
  "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
  "tagDefinitions": [
    {
      "tagName": "@fileoverview",
      "syntaxKind": "modifier"
    },
    {
      "tagName": "@module",
      "syntaxKind": "modifier"
    },
    {
      "tagName": "@type",
      "syntaxKind": "modifier"
    },
    {
      "tagName": "@typedef",
      "syntaxKind": "block"
    },
    {
      "tagName": "@function",
      "syntaxKind": "block"
    },
    {
      "tagName": "@template",
      "syntaxKind": "modifier"
    },
    {
      "tagName": "@property",
      "syntaxKind": "block"
    },
    {
      "tagName": "@class",
      "syntaxKind": "block"
    },
    {
      "tagName": "@static",
      "syntaxKind": "modifier"
    },
    {
      "tagName": "@private",
      "syntaxKind": "modifier"
    },
    {
      "tagName": "@constant",
      "syntaxKind": "block"
    }
  ]
}

```

--------------------------------------------------------------------------------
/src/services/NCBI/core/ncbiConstants.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Constants and shared type definitions for NCBI E-utility interactions.
 * @module src/services/NCBI/core/ncbiConstants
 */

export const NCBI_EUTILS_BASE_URL =
  "https://eutils.ncbi.nlm.nih.gov/entrez/eutils";

/**
 * Interface for common NCBI E-utility request parameters.
 * Specific E-utilities will have additional parameters.
 */
export interface NcbiRequestParams {
  db?: string; // Target database (e.g., "pubmed", "pmc"). Optional for EInfo to list all databases.
  [key: string]: string | number | undefined; // Allows for other E-utility specific parameters
}

/**
 * Interface for options controlling how NCBI requests are made and responses are handled.
 */
export interface NcbiRequestOptions {
  retmode?: "xml" | "json" | "text"; // Desired response format
  rettype?: string; // Specific type of data to return (e.g., "abstract", "medline")
  usePost?: boolean; // Hint to use HTTP POST for large payloads (e.g., many IDs)
  returnRawXml?: boolean; // If true and retmode is 'xml', returns the raw XML string instead of parsed object (after error checking)
}

```

--------------------------------------------------------------------------------
/src/mcp-server/transports/auth/lib/authContext.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Defines the AsyncLocalStorage context for authentication information.
 * This module provides a mechanism to store and retrieve authentication details
 * (like scopes and client ID) across asynchronous operations, making it available
 * from the middleware layer down to the tool and resource handlers without
 * drilling props.
 *
 * @module src/mcp-server/transports/auth/core/authContext
 */

import { AsyncLocalStorage } from "async_hooks";
import type { AuthInfo } from "./authTypes.js";

/**
 * Defines the structure of the store used within the AsyncLocalStorage.
 * It holds the authentication information for the current request context.
 */
interface AuthStore {
  authInfo: AuthInfo;
}

/**
 * An instance of AsyncLocalStorage to hold the authentication context (`AuthStore`).
 * This allows `authInfo` to be accessible throughout the async call chain of a request
 * after being set in the authentication middleware.
 *
 * @example
 * // In middleware:
 * await authContext.run({ authInfo }, next);
 *
 * // In a deeper handler:
 * const store = authContext.getStore();
 * const scopes = store?.authInfo.scopes;
 */
export const authContext = new AsyncLocalStorage<AuthStore>();

```

--------------------------------------------------------------------------------
/src/mcp-server/transports/core/baseTransportManager.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Abstract base class for transport managers.
 * @module src/mcp-server/transports/core/baseTransportManager
 */

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import type { IncomingHttpHeaders } from "http";
import {
  logger,
  RequestContext,
  requestContextService,
} from "../../../utils/index.js";
import { TransportManager, TransportResponse } from "./transportTypes.js";

/**
 * Abstract base class for transport managers, providing common functionality.
 */
export abstract class BaseTransportManager implements TransportManager {
  protected readonly createServerInstanceFn: () => Promise<McpServer>;

  constructor(createServerInstanceFn: () => Promise<McpServer>) {
    const context = requestContextService.createRequestContext({
      operation: "BaseTransportManager.constructor",
      managerType: this.constructor.name,
    });
    logger.debug("Initializing transport manager.", context);
    this.createServerInstanceFn = createServerInstanceFn;
  }

  abstract handleRequest(
    headers: IncomingHttpHeaders,
    body: unknown,
    context: RequestContext,
    sessionId?: string,
  ): Promise<TransportResponse>;

  abstract shutdown(): Promise<void>;
}

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/pubmedResearchAgent/logic.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Core logic invocation for the pubmed_research_agent tool.
 * This tool generates a structured research plan outline with instructive placeholders,
 * designed to be completed by a calling LLM (the MCP Client).
 * @module pubmedResearchAgent/logic
 */

import {
  logger,
  RequestContext,
  requestContextService,
  sanitizeInputForLogging,
} from "../../../utils/index.js";
import {
  generateFullResearchPlanOutline,
  PubMedResearchAgentInput,
  PubMedResearchPlanGeneratedOutput,
} from "./logic/index.js";

export async function pubmedResearchAgentLogic(
  input: PubMedResearchAgentInput,
  parentRequestContext: RequestContext,
): Promise<PubMedResearchPlanGeneratedOutput> {
  const operationContext = requestContextService.createRequestContext({
    parentRequestId: parentRequestContext.requestId,
    operation: "pubmedResearchAgentLogicExecution",
    input: sanitizeInputForLogging(input),
  });

  logger.info(
    `Executing 'pubmed_research_agent' to generate research plan outline. Keywords: ${input.research_keywords.join(
      ", ",
    )}`,
    operationContext,
  );

  const researchPlanOutline = generateFullResearchPlanOutline(
    input,
    operationContext,
  );

  logger.notice("Successfully generated research plan outline.", {
    ...operationContext,
    projectTitle: input.project_title_suggestion,
  });

  return researchPlanOutline;
}

```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    // Target modern JavaScript
    "target": "ES2022",

    // Use modern Node.js module system
    "module": "NodeNext",
    "moduleResolution": "NodeNext",

    // Enable all strict type checking
    "strict": true,
    "noUncheckedIndexedAccess": true,

    // Module interop for CommonJS compatibility
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,

    // Output configuration
    "outDir": "./dist",
    "rootDir": "./src",
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,

    // Import helpers to reduce bundle size
    "importHelpers": true,

    // Skip type checking of declaration files
    "skipLibCheck": true,

    // Ensure consistent file naming
    "forceConsistentCasingInFileNames": true,

    // Enable experimental decorators if needed
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,

    // Node.js specific
    "lib": ["ES2022"],
    "types": ["node"],

    // Error on unused locals and parameters
    "noUnusedLocals": true,
    "noUnusedParameters": true,

    // Ensure void returns are handled
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,

    // Modern resolution features
    "resolveJsonModule": true,
    "allowJs": false
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"],
  "ts-node": {
    "esm": true,
    "experimentalSpecifierResolution": "node"
  }
}

```

--------------------------------------------------------------------------------
/src/mcp-server/transports/core/headerUtils.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Provides a utility for converting HTTP headers between Node.js
 * and Web Standards formats, ensuring compliance and correctness.
 * @module src/mcp-server/transports/core/headerUtils
 */

import type { OutgoingHttpHeaders } from "http";

/**
 * Converts Node.js-style OutgoingHttpHeaders to a Web-standard Headers object.
 *
 * This function is critical for interoperability between Node.js's `http` module
 * and Web APIs like Fetch and Hono. It correctly handles multi-value headers
 * (e.g., `Set-Cookie`), which Node.js represents as an array of strings, by
 * using the `Headers.append()` method. Standard single-value headers are set
 * using `Headers.set()`.
 *
 * @param nodeHeaders - The Node.js-style headers object to convert.
 * @returns A Web-standard Headers object.
 */
export function convertNodeHeadersToWebHeaders(
  nodeHeaders: OutgoingHttpHeaders,
): Headers {
  const webHeaders = new Headers();
  for (const [key, value] of Object.entries(nodeHeaders)) {
    // Skip undefined headers, which are valid in Node.js but not in Web Headers.
    if (value === undefined) {
      continue;
    }

    if (Array.isArray(value)) {
      // For arrays, append each value to support multi-value headers.
      for (const v of value) {
        webHeaders.append(key, String(v));
      }
    } else {
      // For single values, set the header, overwriting any existing value.
      webHeaders.set(key, String(value));
    }
  }
  return webHeaders;
}

```

--------------------------------------------------------------------------------
/examples/pubmed_article_connections_1.md:
--------------------------------------------------------------------------------

```markdown
Tool Call Arguments:

```json
{
  "sourcePmid": "39704040",
  "relationshipType": "pubmed_similar_articles",
  "maxRelatedResults": 3
}
```

Tool Response:

```json
{
  "sourcePmid": "39704040",
  "relationshipType": "pubmed_similar_articles",
  "relatedArticles": [
    {
      "pmid": "38728204",
      "title": "Ciita Regulates Local and Systemic Immune Responses in a Combined rAAV-&#x3b1;-synuclein and Preformed Fibril-Induced Rat Model for Parkinson's Disease.",
      "authors": "Fredlund F, Jimenez-Ferrer I, Grabert K, et al.",
      "score": 34156797,
      "linkUrl": "https://pubmed.ncbi.nlm.nih.gov/38728204/"
    },
    {
      "pmid": "27147665",
      "title": "Inhibition of the JAK/STAT Pathway Protects Against &#x3b1;-Synuclein-Induced Neuroinflammation and Dopaminergic Neurodegeneration.",
      "authors": "Qin H, Buckley JA, Li X, et al.",
      "score": 33315411,
      "linkUrl": "https://pubmed.ncbi.nlm.nih.gov/27147665/"
    },
    {
      "pmid": "39652643",
      "title": "Transmission of peripheral blood &#x3b1;-synuclein fibrils exacerbates synucleinopathy and neurodegeneration in Parkinson's disease by endothelial Lag3 endocytosis.",
      "authors": "Duan Q, Zhang Q, Jiang S, et al.",
      "score": 33247981,
      "linkUrl": "https://pubmed.ncbi.nlm.nih.gov/39652643/"
    }
  ],
  "citations": {},
  "retrievedCount": 3,
  "eUtilityUrl": "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/elink.fcgi?dbfrom=pubmed&db=pubmed&id=39704040&retmode=xml&cmd=neighbor_score"
}
```

```

--------------------------------------------------------------------------------
/src/mcp-server/transports/auth/authFactory.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Factory for creating an authentication strategy based on configuration.
 * This module centralizes the logic for selecting and instantiating the correct
 * authentication strategy, promoting loose coupling and easy extensibility.
 * @module src/mcp-server/transports/auth/authFactory
 */
import { config } from "../../../config/index.js";
import { logger, requestContextService } from "../../../utils/index.js";
import { AuthStrategy } from "./strategies/authStrategy.js";
import { JwtStrategy } from "./strategies/jwtStrategy.js";
import { OauthStrategy } from "./strategies/oauthStrategy.js";

/**
 * Creates and returns an authentication strategy instance based on the
 * application's configuration (`config.mcpAuthMode`).
 *
 * @returns An instance of a class that implements the `AuthStrategy` interface,
 *          or `null` if authentication is disabled (`none`).
 * @throws {Error} If the auth mode is unknown or misconfigured.
 */
export function createAuthStrategy(): AuthStrategy | null {
  const context = requestContextService.createRequestContext({
    operation: "createAuthStrategy",
    authMode: config.mcpAuthMode,
  });
  logger.info("Creating authentication strategy...", context);

  switch (config.mcpAuthMode) {
    case "jwt":
      logger.debug("Instantiating JWT authentication strategy.", context);
      return new JwtStrategy();
    case "oauth":
      logger.debug("Instantiating OAuth authentication strategy.", context);
      return new OauthStrategy();
    case "none":
      logger.info("Authentication is disabled ('none' mode).", context);
      return null; // No authentication
    default:
      // This ensures that if a new auth mode is added to the config type
      // but not to this factory, we get a compile-time or runtime error.
      logger.error(
        `Unknown authentication mode: ${config.mcpAuthMode}`,
        context,
      );
      throw new Error(`Unknown authentication mode: ${config.mcpAuthMode}`);
  }
}

```

--------------------------------------------------------------------------------
/examples/pubmed_article_connections_2.md:
--------------------------------------------------------------------------------

```markdown
Tool Call Arguments:

```json
{
  "sourcePmid": "39704040",
  "relationshipType": "citation_formats",
  "citationStyles": ["ris", "bibtex", "apa_string", "mla_string"]
}
```

Tool Response:

```json
{
  "sourcePmid": "39704040",
  "relationshipType": "citation_formats",
  "relatedArticles": [],
  "citations": {
    "ris": "TY  - JOUR\nAU  - Bellini, Gabriele\nAU  - D'Antongiovanni, Vanessa\nAU  - Palermo, Giovanni\nAU  - Antonioli, Luca\nAU  - Fornai, Matteo\nAU  - Ceravolo, Roberto\nAU  - Bernardini, Nunzia\nAU  - Derkinderen, Pascal\nAU  - Pellegrini, Carolina\nTI  - &#x3b1;-Synuclein in Parkinson's Disease: From Bench to Bedside.\nJO  - Medicinal research reviews\nVL  - 45\nIS  - 3\nSP  - 909\nEP  - 946\nPY  - 2025\nDO  - 10.1002/med.22091\nUR  - https://pubmed.ncbi.nlm.nih.gov/39704040\nER  - \n",
    "bibtex": "@article{Bellini2025,\n  author    = {Bellini, Gabriele and D'Antongiovanni, Vanessa and Palermo, Giovanni and Antonioli, Luca and Fornai, Matteo and Ceravolo, Roberto and Bernardini, Nunzia and Derkinderen, Pascal and Pellegrini, Carolina},\n  title     = {&#x3b1;-Synuclein in Parkinson's Disease: From Bench to Bedside.},\n  journal   = {Medicinal research reviews},\n  year      = {2025},\n  volume    = {45},\n  number    = {3},\n  pages     = {909--946},\n  month     = {may},\n  doi       = {10.1002/med.22091},\n  pmid      = {39704040}\n}\n",
    "apa_string": "Bellini, G., D'Antongiovanni, V., Palermo, G., Antonioli, L., Fornai, M., Ceravolo, R., Bernardini, N., Derkinderen, P., Pellegrini & C.. (2025). &#x3b1;-Synuclein in Parkinson's Disease: From Bench to Bedside.. <em>Medicinal research reviews</em>, <em>45</em>(3), 909-946. https://doi.org/10.1002/med.22091",
    "mla_string": "Bellini, Gabriele, et al. \"&#x3b1;-Synuclein in Parkinson's Disease: From Bench to Bedside..\" <em>Medicinal research reviews</em>, vol. 45, no. 3, May. 2025, pp. 909–946. PubMed Central, doi:10.1002/med.22091."
  },
  "retrievedCount": 1,
  "eUtilityUrl": "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=pubmed&id=39704040&retmode=xml"
}
```

```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
# ---- Build Stage ----
# Use a modern, secure Node.js Alpine image.
# Alpine is lightweight, which reduces the attack surface.
FROM node:23-alpine AS build

# Set the working directory inside the container.
WORKDIR /usr/src/app

# Install build-time dependencies for native modules, especially node-canvas.
# This includes python, make, and g++ for compilation, and dev libraries for canvas.
RUN apk add --no-cache \
    python3 \
    make \
    g++ \
    cairo-dev \
    jpeg-dev \
    pango-dev \
    giflib-dev

# Copy package definitions to leverage Docker layer caching.
COPY package.json package-lock.json* ./

# Install all npm dependencies. `npm ci` is used for reproducible builds.
RUN npm ci

# Copy the rest of the application source code.
COPY . .

# Compile TypeScript to JavaScript.
RUN npm run build

# ---- Production Stage ----
# Start from a fresh, minimal Node.js Alpine image for the final image.
FROM node:23-alpine AS production

WORKDIR /usr/src/app

# Set the environment to production for optimized performance.
ENV NODE_ENV=production

# Install only the runtime dependencies for node-canvas.
# This keeps the final image smaller than including the -dev packages.
RUN apk add --no-cache \
    cairo \
    jpeg \
    pango \
    giflib

# Create a non-root user and group for enhanced security.
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

# Create and set permissions for the log directory.
RUN mkdir -p /var/log/pubmed-mcp-server && chown -R appuser:appgroup /var/log/pubmed-mcp-server

# Copy build artifacts from the build stage.
# This includes the compiled code and production node_modules.
COPY --from=build /usr/src/app/dist ./dist
COPY --from=build /usr/src/app/node_modules ./node_modules
COPY --from=build /usr/src/app/package.json ./

# Switch to the non-root user.
USER appuser

# Expose the port the server will listen on.
# The PORT variable is typically provided by the deployment environment (e.g., Smithery).
ENV MCP_HTTP_PORT=${PORT:-3017}
EXPOSE ${MCP_HTTP_PORT}

# Set runtime environment variables.
ENV MCP_HTTP_HOST=0.0.0.0
ENV MCP_TRANSPORT_TYPE=http
ENV MCP_SESSION_MODE=stateless
ENV MCP_LOG_LEVEL=info
ENV LOGS_DIR=/var/log/pubmed-mcp-server
ENV MCP_AUTH_MODE=none
ENV MCP_FORCE_CONSOLE_LOGGING=true

# The command to start the server.
CMD ["node", "dist/index.js"]

```

--------------------------------------------------------------------------------
/src/mcp-server/transports/auth/lib/authUtils.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Provides utility functions for authorization, specifically for
 * checking token scopes against required permissions for a given operation.
 * @module src/mcp-server/transports/auth/core/authUtils
 */

import { BaseErrorCode, McpError } from "../../../../types-global/errors.js";
import { logger, requestContextService } from "../../../../utils/index.js";
import { authContext } from "./authContext.js";

/**
 * Checks if the current authentication context contains all the specified scopes.
 * This function is designed to be called within tool or resource handlers to
 * enforce scope-based access control. It retrieves the authentication information
 * from `authContext` (AsyncLocalStorage).
 *
 * @param requiredScopes - An array of scope strings that are mandatory for the operation.
 * @throws {McpError} Throws an error with `BaseErrorCode.INTERNAL_ERROR` if the
 *   authentication context is missing, which indicates a server configuration issue.
 * @throws {McpError} Throws an error with `BaseErrorCode.FORBIDDEN` if one or
 *   more required scopes are not present in the validated token.
 */
export function withRequiredScopes(requiredScopes: string[]): void {
  const operationName = "withRequiredScopesCheck";
  const initialContext = requestContextService.createRequestContext({
    operation: operationName,
    requiredScopes,
  });

  logger.debug("Performing scope authorization check.", initialContext);

  const store = authContext.getStore();

  if (!store || !store.authInfo) {
    logger.crit(
      "Authentication context is missing in withRequiredScopes. This is a server configuration error.",
      initialContext,
    );
    // This is a server-side logic error; the auth middleware should always populate this.
    throw new McpError(
      BaseErrorCode.INTERNAL_ERROR,
      "Authentication context is missing. This indicates a server configuration error.",
      {
        ...initialContext,
        error: "AuthStore not found in AsyncLocalStorage.",
      },
    );
  }

  const { scopes: grantedScopes, clientId, subject } = store.authInfo;
  const grantedScopeSet = new Set(grantedScopes);

  const missingScopes = requiredScopes.filter(
    (scope) => !grantedScopeSet.has(scope),
  );

  const finalContext = {
    ...initialContext,
    grantedScopes,
    clientId,
    subject,
  };

  if (missingScopes.length > 0) {
    const errorContext = { ...finalContext, missingScopes };
    logger.warning(
      "Authorization failed: Missing required scopes.",
      errorContext,
    );
    throw new McpError(
      BaseErrorCode.FORBIDDEN,
      `Insufficient permissions. Missing required scopes: ${missingScopes.join(", ")}`,
      errorContext,
    );
  }

  logger.debug("Scope authorization successful.", finalContext);
}

```

--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------

```yaml
# Smithery configuration for a custom Docker container deployment.

# Defines how to start the server once the container is built.
startCommand:
  # Specifies that the server communicates over HTTP.
  type: "http"
  # Defines the configuration variables the server expects.
  # These are passed as environment variables or query parameters.
  configSchema:
    type: "object"
    properties:
      NCBI_API_KEY:
        type: "string"
        description: "API key for the NCBI service."
      NCBI_TOOL_IDENTIFIER:
        type: "string"
        description: "Tool identifier for the NCBI service."
      NCBI_ADMIN_EMAIL:
        type: "string"
        description: "Admin email for the NCBI service."
      MCP_LOG_LEVEL:
        type: "string"
        default: "info"
        description: "Minimum logging level."
      MCP_TRANSPORT_TYPE:
        type: "string"
        enum: ["stdio", "http"]
        default: "http"
        description: "MCP communication transport ('stdio' or 'http')."
      MCP_HTTP_PORT:
        type: "integer"
        default: 3017
        description: "HTTP server port (if MCP_TRANSPORT_TYPE is 'http')."
      MCP_HTTP_HOST:
        type: "string"
        default: "0.0.0.0"
        description: "HTTP server host to bind to."
      MCP_SESSION_MODE:
        type: "string"
        enum: ["stateless", "stateful", "auto"]
        default: "stateless"
        description: "Server session management mode."
      MCP_AUTH_MODE:
        type: "string"
        enum: ["none", "jwt", "oauth"]
        default: "none"
        description: "Server authentication mode."
      MCP_FORCE_CONSOLE_LOGGING:
        type: "boolean"
        default: false
        description: "Force console logging, even in non-TTY environments like Docker."
  # Specifies the build configuration for the Smithery Docker container.
  commandFunction: |-
    (config) => ({
      command: 'node',
      args: ['build/index.js'],
      env: {
        NCBI_API_KEY: config.NCBI_API_KEY,
        NCBI_TOOL_IDENTIFIER: config.NCBI_TOOL_IDENTIFIER,
        NCBI_ADMIN_EMAIL: config.NCBI_ADMIN_EMAIL,
        MCP_LOG_LEVEL: config.MCP_LOG_LEVEL,
        MCP_TRANSPORT_TYPE: config.MCP_TRANSPORT_TYPE,
        MCP_HTTP_PORT: config.MCP_HTTP_PORT,
        MCP_HTTP_HOST: config.MCP_HTTP_HOST,
        MCP_SESSION_MODE: config.MCP_SESSION_MODE,
        MCP_AUTH_MODE: config.MCP_AUTH_MODE,
        MCP_FORCE_CONSOLE_LOGGING: config.MCP_FORCE_CONSOLE_LOGGING
      }
    })
  # Provides an example configuration for users.
  exampleConfig:
    NCBI_API_KEY: "your_ncbi_api_key"
    NCBI_TOOL_IDENTIFIER: "@cyanheads/pubmed-mcp-server"
    NCBI_ADMIN_EMAIL: "[email protected]"
    MCP_LOG_LEVEL: "debug"
    MCP_TRANSPORT_TYPE: "http"
    MCP_HTTP_PORT: 3017
    MCP_HTTP_HOST: "0.0.0.0"
    MCP_SESSION_MODE: "stateless"
    MCP_AUTH_MODE: "none"
    MCP_FORCE_CONSOLE_LOGGING: true

```

--------------------------------------------------------------------------------
/src/services/NCBI/parsing/xmlGenericHelpers.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Generic helper functions for parsing XML data, particularly
 * structures from fast-xml-parser.
 * @module src/services/NCBI/parsing/xmlGenericHelpers
 */

/**
 * Ensures that the input is an array. If it's not an array, it wraps it in one.
 * Handles undefined or null by returning an empty array.
 * @param item - The item to ensure is an array.
 * @returns An array containing the item, or an empty array if item is null/undefined.
 * @template T - The type of the items in the array.
 */
export function ensureArray<T>(item: T | T[] | undefined | null): T[] {
  if (item === undefined || item === null) {
    return [];
  }
  return Array.isArray(item) ? item : [item];
}

/**
 * Safely extracts text content from an XML element, which might be a string or an object with a "#text" property.
 * Handles cases where #text might be a number or boolean by converting to string.
 * @param element - The XML element (string, object with #text, or undefined).
 * @param defaultValue - The value to return if text cannot be extracted. Defaults to an empty string.
 * @returns The text content or the default value.
 */
export function getText(element: unknown, defaultValue = ""): string {
  if (element === undefined || element === null) {
    return defaultValue;
  }
  if (typeof element === "string") {
    return element;
  }
  if (typeof element === "number" || typeof element === "boolean") {
    return String(element); // Handle direct number/boolean elements
  }
  if (typeof element === "object") {
    const obj = element as Record<string, unknown>;
    if (obj["#text"] !== undefined) {
      const val = obj["#text"];
      if (typeof val === "string") return val;
      if (typeof val === "number" || typeof val === "boolean")
        return String(val);
    }
  }
  return defaultValue;
}

/**
 * Safely extracts an attribute value from an XML element.
 * Assumes attributes are prefixed with "@_" by fast-xml-parser.
 * @param element - The XML element object.
 * @param attributeName - The name of the attribute (e.g., "_UI", "_MajorTopicYN", without the "@_" prefix).
 * @param defaultValue - The value to return if the attribute is not found. Defaults to an empty string.
 * @returns The attribute value or the default value.
 */
export function getAttribute(
  element: unknown,
  attributeName: string, // e.g., "UI", "MajorTopicYN"
  defaultValue = "",
): string {
  const fullAttributeName = `@_${attributeName}`; // As per fast-xml-parser config
  if (element && typeof element === "object") {
    const obj = element as Record<string, unknown>;
    const val = obj[fullAttributeName];
    if (typeof val === "string") return val;
    if (typeof val === "boolean") return String(val); // Convert boolean attributes to string
    if (typeof val === "number") return String(val); // Convert number attributes to string
  }
  return defaultValue;
}

```

--------------------------------------------------------------------------------
/scripts/clean.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node

/**
 * @fileoverview Utility script to clean build artifacts and temporary directories.
 * @module scripts/clean
 *   By default, it removes the 'dist' and 'logs' directories.
 *   Custom directories can be specified as command-line arguments.
 *   Works on all platforms using Node.js path normalization.
 *
 * @example
 * // Add to package.json:
 * // "scripts": {
 * //   "clean": "ts-node --esm scripts/clean.ts",
 * //   "rebuild": "npm run clean && npm run build"
 * // }
 *
 * // Run with default directories:
 * // npm run clean
 *
 * // Run with custom directories:
 * // ts-node --esm scripts/clean.ts temp coverage
 */

import { rm, access } from "fs/promises";
import { join } from "path";

/**
 * Represents the result of a clean operation for a single directory.
 * @property dir - The name of the directory targeted for cleaning.
 * @property status - Indicates if the cleaning was successful or skipped.
 * @property reason - If skipped, the reason why.
 */
interface CleanResult {
  dir: string;
  status: "success" | "skipped";
  reason?: string;
}

/**
 * Asynchronously checks if a directory exists at the given path.
 * @param dirPath - The absolute or relative path to the directory.
 * @returns A promise that resolves to `true` if the directory exists, `false` otherwise.
 */
async function directoryExists(dirPath: string): Promise<boolean> {
  try {
    await access(dirPath);
    return true;
  } catch {
    return false;
  }
}

/**
 * Main function to perform the cleaning operation.
 * It reads command line arguments for target directories or uses defaults ('dist', 'logs').
 * Reports the status of each cleaning attempt.
 */
const clean = async (): Promise<void> => {
  try {
    let dirsToClean: string[] = ["dist", "logs"];
    const args = process.argv.slice(2);

    if (args.length > 0) {
      dirsToClean = args;
    }

    console.log(`Attempting to clean directories: ${dirsToClean.join(", ")}`);

    const results = await Promise.allSettled(
      dirsToClean.map(async (dir): Promise<CleanResult> => {
        const dirPath = join(process.cwd(), dir);

        const exists = await directoryExists(dirPath);

        if (!exists) {
          return { dir, status: "skipped", reason: "does not exist" };
        }

        await rm(dirPath, { recursive: true, force: true });
        return { dir, status: "success" };
      }),
    );

    results.forEach((result) => {
      if (result.status === "fulfilled") {
        const { dir, status, reason } = result.value;
        if (status === "success") {
          console.log(`Successfully cleaned directory: ${dir}`);
        } else {
          console.log(`Skipped cleaning directory ${dir}: ${reason}.`);
        }
      } else {
        // The error here is the actual error object from the rejected promise
        console.error(
          `Error cleaning a directory (details below):\n`,
          result.reason,
        );
      }
    });
  } catch (error) {
    console.error(
      "An unexpected error occurred during the clean script execution:",
      error instanceof Error ? error.message : error,
    );
    process.exit(1);
  }
};

clean();

```

--------------------------------------------------------------------------------
/src/mcp-server/transports/auth/authMiddleware.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Defines a unified Hono middleware for authentication.
 * This middleware is strategy-agnostic. It extracts a Bearer token,
 * delegates verification to the provided authentication strategy, and
 * populates the async-local storage context with the resulting auth info.
 * @module src/mcp-server/transports/auth/authMiddleware
 */
import type { HttpBindings } from "@hono/node-server";
import type { Context, Next } from "hono";
import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
import {
  ErrorHandler,
  logger,
  requestContextService,
} from "../../../utils/index.js";
import { authContext } from "./lib/authContext.js";
import type { AuthStrategy } from "./strategies/authStrategy.js";

/**
 * Creates a Hono middleware function that enforces authentication using a given strategy.
 *
 * @param strategy - An instance of a class that implements the `AuthStrategy` interface.
 * @returns A Hono middleware function.
 */
export function createAuthMiddleware(strategy: AuthStrategy) {
  return async function authMiddleware(
    c: Context<{ Bindings: HttpBindings }>,
    next: Next,
  ) {
    const context = requestContextService.createRequestContext({
      operation: "authMiddleware",
      method: c.req.method,
      path: c.req.path,
    });

    logger.debug("Initiating authentication check.", context);

    const authHeader = c.req.header("Authorization");
    if (!authHeader || !authHeader.startsWith("Bearer ")) {
      logger.warning("Authorization header missing or invalid.", context);
      throw new McpError(
        BaseErrorCode.UNAUTHORIZED,
        "Missing or invalid Authorization header. Bearer scheme required.",
        context,
      );
    }

    const token = authHeader.substring(7);
    if (!token) {
      logger.warning(
        "Bearer token is missing from Authorization header.",
        context,
      );
      throw new McpError(
        BaseErrorCode.UNAUTHORIZED,
        "Authentication token is missing.",
        context,
      );
    }

    logger.debug(
      "Extracted Bearer token, proceeding to verification.",
      context,
    );

    try {
      const authInfo = await strategy.verify(token);

      const authLogContext = {
        ...context,
        clientId: authInfo.clientId,
        subject: authInfo.subject,
        scopes: authInfo.scopes,
      };
      logger.info(
        "Authentication successful. Auth context populated.",
        authLogContext,
      );

      // Run the next middleware in the chain within the populated auth context.
      await authContext.run({ authInfo }, next);
    } catch (error) {
      // The strategy is expected to throw an McpError.
      // We re-throw it here to be caught by the global httpErrorHandler.
      logger.warning("Authentication verification failed.", {
        ...context,
        error: error instanceof Error ? error.message : String(error),
      });

      // Ensure consistent error handling
      throw ErrorHandler.handleError(error, {
        operation: "authMiddlewareVerification",
        context,
        rethrow: true, // Rethrow to be caught by Hono's global error handler
        errorCode: BaseErrorCode.UNAUTHORIZED, // Default to unauthorized if not more specific
      });
    }
  };
}

```

--------------------------------------------------------------------------------
/examples/pubmed_search_articles_example.md:
--------------------------------------------------------------------------------

```markdown
Tool Call Arguments:

```json
{
  "queryTerm": "neuroinflammation AND (Alzheimer's OR Parkinson's) AND microglia",
  "maxResults": 15,
  "sortBy": "pub_date",
  "dateRange": {
    "minDate": "2023/01/01",
    "maxDate": "2024/12/31",
    "dateType": "pdat"
  },
  "filterByPublicationTypes": ["Review", "Journal Article"],
  "fetchBriefSummaries": 5
}
```

Tool Response:

```json
{
  "searchParameters": {
    "queryTerm": "neuroinflammation AND (Alzheimer's OR Parkinson's) AND microglia",
    "maxResults": 15,
    "sortBy": "pub_date",
    "dateRange": {
      "minDate": "2023/01/01",
      "maxDate": "2024/12/31",
      "dateType": "pdat"
    },
    "filterByPublicationTypes": ["Review", "Journal Article"],
    "fetchBriefSummaries": 5
  },
  "effectiveESearchTerm": "neuroinflammation AND (Alzheimer's OR Parkinson's) AND microglia AND (2023/01/01[pdat] : 2024/12/31[pdat]) AND (\"Review\"[Publication Type] OR \"Journal Article\"[Publication Type])",
  "totalFound": 1290,
  "retrievedPmidCount": 15,
  "pmids": [
    39715098, 39359093, 39704040, 39653749, 39648189, 39075895, 40256246,
    39761611, 39726135, 39719687, 39718073, 39514171, 39433702, 39400857,
    39029776
  ],
  "briefSummaries": [
    {
      "pmid": "39715098",
      "title": "The compound (E)-2-(3,4-dihydroxystyryl)-3-hydroxy-4H-pyran-4-one alleviates neuroinflammation and cognitive impairment in a mouse model of Alzheimer's disease.",
      "authors": "Liu X, Wu W, Li X, et al.",
      "source": "Neural Regen Res",
      "doi": "",
      "pubDate": "2025-11-01",
      "epubDate": "2024-07-10"
    },
    {
      "pmid": "39359093",
      "title": "The cGAS-STING-interferon regulatory factor 7 pathway regulates neuroinflammation in Parkinson's disease.",
      "authors": "Zhou S, Li T, Zhang W, et al.",
      "source": "Neural Regen Res",
      "doi": "",
      "pubDate": "2025-08-01",
      "epubDate": "2024-06-03"
    },
    {
      "pmid": "39704040",
      "title": "&#x3b1;-Synuclein in Parkinson's Disease: From Bench to Bedside.",
      "authors": "Bellini G, D'Antongiovanni V, Palermo G, et al.",
      "source": "Med Res Rev",
      "doi": "",
      "pubDate": "2026-05-20",
      "epubDate": "2024-12-20"
    },
    {
      "pmid": "39653749",
      "title": "Neuroinflammation in Alzheimer disease.",
      "authors": "Heneka MT, van der Flier WM, Jessen F, et al.",
      "source": "Nat Rev Immunol",
      "doi": "",
      "pubDate": "2026-05-20",
      "epubDate": "2024-12-09"
    },
    {
      "pmid": "39648189",
      "title": "Unveiling the Involvement of Herpes Simplex Virus-1 in Alzheimer's Disease: Possible Mechanisms and Therapeutic Implications.",
      "authors": "Chauhan P, Begum MY, Narapureddy BR, et al.",
      "source": "Mol Neurobiol",
      "doi": "",
      "pubDate": "2026-05-20",
      "epubDate": "2024-12-09"
    }
  ],
  "eSearchUrl": "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=pubmed&term=neuroinflammation+AND+%28Alzheimer%27s+OR+Parkinson%27s%29+AND+microglia+AND+%282023%2F01%2F01%5Bpdat%5D+%3A+2024%2F12%2F31%5Bpdat%5D%29+AND+%28%22Review%22%5BPublication+Type%5D+OR+%22Journal+Article%22%5BPublication+Type%5D%29&retmax=15&sort=pub_date&usehistory=y",
  "eSummaryUrl": "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esummary.fcgi?db=pubmed&version=2.0&retmode=xml&WebEnv=MCID_6832175795dfc79c7001d173&query_key=1&retmax=5"
}
```

```

--------------------------------------------------------------------------------
/src/mcp-server/transports/http/httpErrorHandler.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Centralized error handler for the Hono HTTP transport.
 * This middleware intercepts errors that occur during request processing,
 * standardizes them using the application's ErrorHandler utility, and
 * formats them into a consistent JSON-RPC error response.
 * @module src/mcp-server/transports/httpErrorHandler
 */

import { Context } from "hono";
import { StatusCode } from "hono/utils/http-status";
import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
import {
  ErrorHandler,
  logger,
  requestContextService,
} from "../../../utils/index.js";
import { HonoNodeBindings } from "./httpTypes.js";

/**
 * A centralized error handling middleware for Hono.
 * This function is registered with `app.onError()` and will catch any errors
 * thrown from preceding middleware or route handlers.
 *
 * @param err - The error that was thrown.
 * @param c - The Hono context object for the request.
 * @returns A Response object containing the formatted JSON-RPC error.
 */
export const httpErrorHandler = async (
  err: Error,
  c: Context<{ Bindings: HonoNodeBindings }>,
): Promise<Response> => {
  const context = requestContextService.createRequestContext({
    operation: "httpErrorHandler",
    path: c.req.path,
    method: c.req.method,
  });
  logger.debug("HTTP error handler invoked.", context);

  const handledError = ErrorHandler.handleError(err, {
    operation: "httpTransport",
    context,
  });

  let status: StatusCode = 500;
  if (handledError instanceof McpError) {
    switch (handledError.code) {
      case BaseErrorCode.NOT_FOUND:
        status = 404;
        break;
      case BaseErrorCode.UNAUTHORIZED:
        status = 401;
        break;
      case BaseErrorCode.FORBIDDEN:
        status = 403;
        break;
      case BaseErrorCode.VALIDATION_ERROR:
      case BaseErrorCode.INVALID_INPUT:
        status = 400;
        break;
      case BaseErrorCode.CONFLICT:
        status = 409;
        break;
      case BaseErrorCode.RATE_LIMITED:
        status = 429;
        break;
      default:
        status = 500;
    }
  }
  logger.debug(`Mapping error to HTTP status ${status}.`, {
    ...context,
    status,
    errorCode: (handledError as McpError).code,
  });

  // Attempt to get the request ID from the body, but don't fail if it's not there or unreadable.
  let requestId: string | number | null = null;
  // Only attempt to read the body if it hasn't been consumed already.
  if (c.req.raw.bodyUsed === false) {
    try {
      const body = await c.req.json();
      requestId = body?.id || null;
      logger.debug("Extracted JSON-RPC request ID from body.", {
        ...context,
        jsonRpcId: requestId,
      });
    } catch {
      logger.warning(
        "Could not parse request body to extract JSON-RPC ID.",
        context,
      );
      // Ignore parsing errors, requestId will remain null
    }
  } else {
    logger.debug(
      "Request body already consumed, cannot extract JSON-RPC ID.",
      context,
    );
  }

  const errorCode =
    handledError instanceof McpError ? handledError.code : -32603;

  c.status(status);
  const errorResponse = {
    jsonrpc: "2.0",
    error: {
      code: errorCode,
      message: handledError.message,
    },
    id: requestId,
  };
  logger.info(`Sending formatted error response for request.`, {
    ...context,
    status,
    errorCode,
    jsonRpcId: requestId,
  });
  return c.json(errorResponse);
};

```

--------------------------------------------------------------------------------
/src/mcp-server/transports/stdio/stdioTransport.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Handles the setup and connection for the Stdio MCP transport.
 * Implements the MCP Specification 2025-03-26 for stdio transport.
 * This transport communicates directly over standard input (stdin) and
 * standard output (stdout), typically used when the MCP server is launched
 * as a child process by a host application.
 *
 * Specification Reference:
 * https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/transports.mdx#stdio
 *
 * --- Authentication Note ---
 * As per the MCP Authorization Specification (2025-03-26, Section 1.2),
 * STDIO transports SHOULD NOT implement HTTP-based authentication flows.
 * Authorization is typically handled implicitly by the host application
 * controlling the server process. This implementation follows that guideline.
 *
 * @see {@link https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/authorization.mdx | MCP Authorization Specification}
 * @module src/mcp-server/transports/stdioTransport
 */

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { ErrorHandler, logger, RequestContext } from "../../../utils/index.js";

/**
 * Connects a given `McpServer` instance to the Stdio transport.
 * This function initializes the SDK's `StdioServerTransport`, which manages
 * communication over `process.stdin` and `process.stdout` according to the
 * MCP stdio transport specification.
 *
 * MCP Spec Points Covered by SDK's `StdioServerTransport`:
 * - Reads JSON-RPC messages (requests, notifications, responses, batches) from stdin.
 * - Writes JSON-RPC messages to stdout.
 * - Handles newline delimiters and ensures no embedded newlines in output messages.
 * - Ensures only valid MCP messages are written to stdout.
 *
 * Logging via the `logger` utility MAY result in output to stderr, which is
 * permitted by the spec for logging purposes.
 *
 * @param server - The `McpServer` instance.
 * @param parentContext - The logging and tracing context from the calling function.
 * @returns A promise that resolves when the Stdio transport is successfully connected.
 * @throws {Error} If the connection fails during setup.
 */
export async function startStdioTransport(
  server: McpServer,
  parentContext: RequestContext,
): Promise<void> {
  const operationContext = {
    ...parentContext,
    operation: "connectStdioTransport",
    transportType: "Stdio",
  };
  logger.info("Attempting to connect stdio transport...", operationContext);

  try {
    logger.debug("Creating StdioServerTransport instance...", operationContext);
    const transport = new StdioServerTransport();

    logger.debug(
      "Connecting McpServer instance to StdioServerTransport...",
      operationContext,
    );
    await server.connect(transport);

    logger.info(
      "MCP Server connected and listening via stdio transport.",
      operationContext,
    );
    if (process.stdout.isTTY) {
      console.log(
        `\n🚀 MCP Server running in STDIO mode.\n   (MCP Spec: 2025-03-26 Stdio Transport)\n`,
      );
    }
  } catch (err) {
    // Let the ErrorHandler log the error with all context, then rethrow.
    throw ErrorHandler.handleError(err, {
      operation: "connectStdioTransport",
      context: operationContext,
      critical: true,
      rethrow: true,
    });
  }
}

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/pubmedGenerateChart/registration.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Registers the 'pubmed_generate_chart' tool with the MCP server.
 * This tool now accepts parameterized input for generating charts.
 * @module src/mcp-server/tools/pubmedGenerateChart/registration
 */
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
import {
  ErrorHandler,
  logger,
  RequestContext,
  requestContextService,
} from "../../../utils/index.js";
import {
  PubMedGenerateChartInput,
  PubMedGenerateChartInputSchema,
  pubmedGenerateChartLogic,
} from "./logic.js";

export async function registerPubMedGenerateChartTool(
  server: McpServer,
): Promise<void> {
  const operation = "registerPubMedGenerateChartTool";
  const toolName = "pubmed_generate_chart";
  const toolDescription =
    "Generates a customizable chart (PNG) from structured data. Supports various plot types and requires data values and field mappings for axes. Returns a Base64-encoded PNG image.";
  const context = requestContextService.createRequestContext({ operation });

  await ErrorHandler.tryCatch(
    async () => {
      server.tool(
        toolName,
        toolDescription,
        PubMedGenerateChartInputSchema.shape,
        async (
          input: PubMedGenerateChartInput,
          mcpProvidedContext: unknown,
        ): Promise<CallToolResult> => {
          const richContext: RequestContext =
            requestContextService.createRequestContext({
              parentRequestId: context.requestId,
              operation: "pubmedGenerateChartToolHandler",
              mcpToolContext: mcpProvidedContext,
              input,
            });

          try {
            const result = await pubmedGenerateChartLogic(input, richContext);
            return {
              content: [
                {
                  type: "image",
                  data: result.base64Data,
                  mimeType: "image/png",
                },
              ],
              isError: false,
            };
          } catch (error) {
            const handledError = ErrorHandler.handleError(error, {
              operation: "pubmedGenerateChartToolHandler",
              context: richContext,
              input,
              rethrow: false,
            });

            const mcpError =
              handledError instanceof McpError
                ? handledError
                : new McpError(
                    BaseErrorCode.INTERNAL_ERROR,
                    "An unexpected error occurred while generating the chart.",
                    {
                      originalErrorName: handledError.name,
                      originalErrorMessage: handledError.message,
                    },
                  );

            return {
              content: [
                {
                  type: "text",
                  text: JSON.stringify({
                    error: {
                      code: mcpError.code,
                      message: mcpError.message,
                      details: mcpError.details,
                    },
                  }),
                },
              ],
              isError: true,
            };
          }
        },
      );
      logger.notice(`Tool '${toolName}' registered.`, context);
    },
    {
      operation,
      context,
      errorCode: BaseErrorCode.INITIALIZATION_FAILED,
      critical: true,
    },
  );
}

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/pubmedSearchArticles/registration.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Registration for the pubmed_search_articles MCP tool.
 * @module src/mcp-server/tools/pubmedSearchArticles/registration
 */

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
import {
  ErrorHandler,
  logger,
  RequestContext,
  requestContextService,
} from "../../../utils/index.js";
import {
  PubMedSearchArticlesInput,
  PubMedSearchArticlesInputSchema,
  pubmedSearchArticlesLogic,
} from "./logic.js";

/**
 * Registers the pubmed_search_articles tool with the MCP server.
 * @param server - The McpServer instance.
 */
export async function registerPubMedSearchArticlesTool(
  server: McpServer,
): Promise<void> {
  const operation = "registerPubMedSearchArticlesTool";
  const toolName = "pubmed_search_articles";
  const toolDescription =
    "Searches PubMed for articles using a query term and optional filters (max results, sort, date range, publication types). Uses NCBI ESearch to find PMIDs and ESummary (optional) for brief summaries. Returns a JSON object with search parameters, ESearch term, result counts, PMIDs, optional summaries, and E-utility URLs.";
  const context = requestContextService.createRequestContext({ operation });

  await ErrorHandler.tryCatch(
    async () => {
      server.tool(
        toolName,
        toolDescription,
        PubMedSearchArticlesInputSchema.shape,
        async (
          input: PubMedSearchArticlesInput,
          mcpProvidedContext: unknown,
        ): Promise<CallToolResult> => {
          const richContext: RequestContext =
            requestContextService.createRequestContext({
              parentRequestId: context.requestId,
              operation: "pubmedSearchArticlesToolHandler",
              mcpToolContext: mcpProvidedContext,
              input,
            });

          try {
            const result = await pubmedSearchArticlesLogic(input, richContext);
            return {
              content: [
                { type: "text", text: JSON.stringify(result, null, 2) },
              ],
              isError: false,
            };
          } catch (error) {
            const handledError = ErrorHandler.handleError(error, {
              operation: "pubmedSearchArticlesToolHandler",
              context: richContext,
              input,
              rethrow: false,
            });

            const mcpError =
              handledError instanceof McpError
                ? handledError
                : new McpError(
                    BaseErrorCode.INTERNAL_ERROR,
                    "An unexpected error occurred while searching PubMed articles.",
                    {
                      originalErrorName: handledError.name,
                      originalErrorMessage: handledError.message,
                    },
                  );

            return {
              content: [
                {
                  type: "text",
                  text: JSON.stringify({
                    error: {
                      code: mcpError.code,
                      message: mcpError.message,
                      details: mcpError.details,
                    },
                  }),
                },
              ],
              isError: true,
            };
          }
        },
      );
      logger.notice(`Tool '${toolName}' registered.`, context);
    },
    {
      operation,
      context,
      errorCode: BaseErrorCode.INITIALIZATION_FAILED,
      critical: true,
    },
  );
}

```

--------------------------------------------------------------------------------
/server.json:
--------------------------------------------------------------------------------

```json
{
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
  "name": "io.github.cyanheads/pubmed-mcp-server",
  "description": "Comprehensive PubMed MCP Server to search, retrieve, and analyze biomedical literature from NCBI.",
  "status": "active",
  "repository": {
    "url": "https://github.com/cyanheads/pubmed-mcp-server",
    "source": "github"
  },
  "website_url": "https://github.com/cyanheads/pubmed-mcp-server#readme",
  "version": "1.4.4",
  "packages": [
    {
      "registry_type": "npm",
      "registry_base_url": "https://registry.npmjs.org",
      "identifier": "@cyanheads/pubmed-mcp-server",
      "version": "1.4.4",
      "runtime_hint": "node",
      "package_arguments": [
        {
          "type": "positional",
          "value": "dist/index.js"
        }
      ],
      "environment_variables": [
        {
          "name": "MCP_TRANSPORT_TYPE",
          "description": "Specifies the transport mechanism for the server.",
          "format": "string",
          "is_required": true,
          "default": "stdio"
        },
        {
          "name": "MCP_LOG_LEVEL",
          "description": "Sets the minimum log level for output (e.g., 'debug', 'info', 'warn').",
          "format": "string",
          "is_required": false,
          "default": "info"
        },
        {
          "name": "NCBI_API_KEY",
          "description": "Your NCBI API key for higher rate limits.",
          "format": "string",
          "is_required": false
        }
      ],
      "transport": {
        "type": "stdio"
      }
    },
    {
      "registry_type": "npm",
      "registry_base_url": "https://registry.npmjs.org",
      "identifier": "@cyanheads/pubmed-mcp-server",
      "version": "1.4.4",
      "runtime_hint": "node",
      "package_arguments": [
        {
          "type": "positional",
          "value": "dist/index.js"
        }
      ],
      "environment_variables": [
        {
          "name": "MCP_TRANSPORT_TYPE",
          "description": "Specifies the transport mechanism for the server.",
          "format": "string",
          "is_required": true,
          "default": "http"
        },
        {
          "name": "MCP_HTTP_HOST",
          "description": "The host for the HTTP server.",
          "format": "string",
          "is_required": false,
          "default": "localhost"
        },
        {
          "name": "MCP_HTTP_PORT",
          "description": "The port for the HTTP server.",
          "format": "string",
          "is_required": false,
          "default": "3017"
        },
        {
          "name": "MCP_HTTP_ENDPOINT_PATH",
          "description": "The endpoint path for MCP requests.",
          "format": "string",
          "is_required": false,
          "default": "/mcp"
        },
        {
          "name": "MCP_AUTH_MODE",
          "description": "Authentication mode: 'none', 'jwt', or 'oauth'.",
          "format": "string",
          "is_required": false,
          "default": "none"
        },
        {
          "name": "MCP_LOG_LEVEL",
          "description": "Sets the minimum log level (e.g., 'debug', 'info', 'warn').",
          "format": "string",
          "is_required": false,
          "default": "info"
        },
        {
          "name": "NCBI_API_KEY",
          "description": "Your NCBI API key for higher rate limits.",
          "format": "string",
          "is_required": false
        }
      ],
      "transport": {
        "type": "streamable-http",
        "url": "http://localhost:3017/mcp"
      }
    }
  ],
  "mcpName": "io.github.cyanheads/pubmed-mcp-server"
}
```

--------------------------------------------------------------------------------
/src/mcp-server/tools/pubmedFetchContents/registration.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Registration for the pubmed_fetch_contents MCP tool.
 * @module src/mcp-server/tools/pubmedFetchContents/registration
 */

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
import {
  ErrorHandler,
  logger,
  RequestContext,
  requestContextService,
} from "../../../utils/index.js";
import {
  PubMedFetchContentsInput,
  PubMedFetchContentsInputSchema,
  pubMedFetchContentsLogic,
} from "./logic.js";

/**
 * Registers the pubmed_fetch_contents tool with the MCP server.
 * @param server - The McpServer instance.
 */
export async function registerPubMedFetchContentsTool(
  server: McpServer,
): Promise<void> {
  const operation = "registerPubMedFetchContentsTool";
  const toolName = "pubmed_fetch_contents";
  const toolDescription =
    "Fetches detailed information from PubMed using NCBI EFetch. Can be used with a direct list of PMIDs or with queryKey/webEnv from an ESearch history entry. Supports pagination (retstart, retmax) when using history. Available 'detailLevel' options: 'abstract_plus' (parsed details), 'full_xml' (raw PubMedArticle XML), 'medline_text' (MEDLINE format), or 'citation_data' (minimal citation data). Returns a JSON object containing results, any PMIDs not found (if applicable), and EFetch details.";

  const context = requestContextService.createRequestContext({ operation });

  await ErrorHandler.tryCatch(
    async () => {
      server.tool(
        toolName,
        toolDescription,
        PubMedFetchContentsInputSchema._def.schema.shape,
        async (
          input: PubMedFetchContentsInput,
          toolContext: unknown,
        ): Promise<CallToolResult> => {
          const richContext: RequestContext =
            requestContextService.createRequestContext({
              parentRequestId: context.requestId,
              operation: "pubMedFetchContentsToolHandler",
              mcpToolContext: toolContext,
              input,
            });

          try {
            const result = await pubMedFetchContentsLogic(input, richContext);
            return {
              content: [{ type: "text", text: result.content }],
              isError: false,
            };
          } catch (error) {
            const handledError = ErrorHandler.handleError(error, {
              operation: "pubMedFetchContentsToolHandler",
              context: richContext,
              input,
              rethrow: false,
            });

            const mcpError =
              handledError instanceof McpError
                ? handledError
                : new McpError(
                    BaseErrorCode.INTERNAL_ERROR,
                    "An unexpected error occurred while fetching PubMed content.",
                    {
                      originalErrorName: handledError.name,
                      originalErrorMessage: handledError.message,
                    },
                  );

            return {
              content: [
                {
                  type: "text",
                  text: JSON.stringify({
                    error: {
                      code: mcpError.code,
                      message: mcpError.message,
                      details: mcpError.details,
                    },
                  }),
                },
              ],
              isError: true,
            };
          }
        },
      );

      logger.notice(`Tool '${toolName}' registered.`, context);
    },
    {
      operation,
      context,
      errorCode: BaseErrorCode.INITIALIZATION_FAILED,
      critical: true,
    },
  );
}

```

--------------------------------------------------------------------------------
/src/utils/internal/performance.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Provides a utility for performance monitoring of tool execution.
 * This module introduces a higher-order function to wrap tool logic, measure its
 * execution time, and log a structured metrics event.
 * @module src/utils/internal/performance
 */

import { SpanStatusCode, trace } from "@opentelemetry/api";
import {
  ATTR_CODE_FUNCTION,
  ATTR_CODE_NAMESPACE,
} from "../telemetry/semconv.js";
import { config } from "../../config/index.js";
import { McpError } from "../../types-global/errors.js";
import { logger } from "./logger.js";
import { RequestContext } from "./requestContext.js";

/**
 * Calculates the size of a payload in bytes.
 * @param payload - The payload to measure.
 * @returns The size in bytes.
 * @private
 */
function getPayloadSize(payload: unknown): number {
  if (!payload) return 0;
  try {
    const stringified = JSON.stringify(payload);
    return Buffer.byteLength(stringified, "utf8");
  } catch {
    return 0; // Could not stringify
  }
}

/**
 * A higher-order function that wraps a tool's core logic to measure its performance
 * and log a structured metrics event upon completion.
 *
 * @template T The expected return type of the tool's logic function.
 * @param toolLogicFn - The asynchronous tool logic function to be executed and measured.
 * @param context - The request context for the operation, used for logging and tracing.
 * @param inputPayload - The input payload to the tool for size calculation.
 * @returns A promise that resolves with the result of the tool logic function.
 * @throws Re-throws any error caught from the tool logic function after logging the failure.
 */
export async function measureToolExecution<T>(
  toolLogicFn: () => Promise<T>,
  context: RequestContext & { toolName: string },
  inputPayload: unknown,
): Promise<T> {
  const tracer = trace.getTracer(
    config.openTelemetry.serviceName,
    config.openTelemetry.serviceVersion,
  );
  const { toolName } = context;

  return tracer.startActiveSpan(`tool_execution:${toolName}`, async (span) => {
    span.setAttributes({
      [ATTR_CODE_FUNCTION]: toolName,
      [ATTR_CODE_NAMESPACE]: "mcp-tools",
      "mcp.tool.input_bytes": getPayloadSize(inputPayload),
    });

    const startTime = process.hrtime.bigint();
    let isSuccess = false;
    let errorCode: string | undefined;
    let outputPayload: T | undefined;

    try {
      const result = await toolLogicFn();
      isSuccess = true;
      outputPayload = result;
      span.setStatus({ code: SpanStatusCode.OK });
      span.setAttribute("mcp.tool.output_bytes", getPayloadSize(outputPayload));
      return result;
    } catch (error) {
      if (error instanceof McpError) {
        errorCode = error.code;
      } else if (error instanceof Error) {
        errorCode = "UNHANDLED_ERROR";
      } else {
        errorCode = "UNKNOWN_ERROR";
      }

      if (error instanceof Error) {
        span.recordException(error);
      }
      span.setStatus({
        code: SpanStatusCode.ERROR,
        message: error instanceof Error ? error.message : String(error),
      });

      throw error;
    } finally {
      const endTime = process.hrtime.bigint();
      const durationMs = Number(endTime - startTime) / 1_000_000;

      span.setAttributes({
        "mcp.tool.duration_ms": parseFloat(durationMs.toFixed(2)),
        "mcp.tool.success": isSuccess,
      });
      if (errorCode) {
        span.setAttribute("mcp.tool.error_code", errorCode);
      }

      span.end();

      logger.info("Tool execution finished.", {
        ...context,
        metrics: {
          durationMs: parseFloat(durationMs.toFixed(2)),
          isSuccess,
          errorCode,
          inputBytes: getPayloadSize(inputPayload),
          outputBytes: getPayloadSize(outputPayload),
        },
      });
    }
  });
}

```

--------------------------------------------------------------------------------
/src/utils/network/fetchWithTimeout.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Provides a utility function to make fetch requests with a specified timeout.
 * @module src/utils/network/fetchWithTimeout
 */

import { logger } from "../internal/logger.js"; // Adjusted import path
import type { RequestContext } from "../internal/requestContext.js"; // Adjusted import path
import { McpError, BaseErrorCode } from "../../types-global/errors.js";

/**
 * Options for the fetchWithTimeout utility.
 * Extends standard RequestInit but omits 'signal' as it's handled internally.
 */
export type FetchWithTimeoutOptions = Omit<RequestInit, "signal">;

/**
 * Fetches a resource with a specified timeout.
 *
 * @param url - The URL to fetch.
 * @param timeoutMs - The timeout duration in milliseconds.
 * @param context - The request context for logging.
 * @param options - Optional fetch options (RequestInit), excluding 'signal'.
 * @returns A promise that resolves to the Response object.
 * @throws {McpError} If the request times out or another fetch-related error occurs.
 */
export async function fetchWithTimeout(
  url: string | URL,
  timeoutMs: number,
  context: RequestContext,
  options?: FetchWithTimeoutOptions,
): Promise<Response> {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);

  const urlString = url.toString();
  const operationDescription = `fetch ${options?.method || "GET"} ${urlString}`;

  logger.debug(
    `Attempting ${operationDescription} with ${timeoutMs}ms timeout.`,
    context,
  );

  try {
    const response = await fetch(url, {
      ...options,
      signal: controller.signal,
    });
    clearTimeout(timeoutId);

    if (!response.ok) {
      const errorBody = await response
        .text()
        .catch(() => "Could not read response body");
      logger.error(
        `Fetch failed for ${urlString} with status ${response.status}.`,
        {
          ...context,
          statusCode: response.status,
          statusText: response.statusText,
          responseBody: errorBody,
          errorSource: "FetchHttpError",
        },
      );
      throw new McpError(
        BaseErrorCode.SERVICE_UNAVAILABLE,
        `Fetch failed for ${urlString}. Status: ${response.status}`,
        {
          ...context,
          statusCode: response.status,
          statusText: response.statusText,
          responseBody: errorBody,
        },
      );
    }

    logger.debug(
      `Successfully fetched ${urlString}. Status: ${response.status}`,
      context,
    );
    return response;
  } catch (error) {
    clearTimeout(timeoutId);
    if (error instanceof Error && error.name === "AbortError") {
      logger.error(`${operationDescription} timed out after ${timeoutMs}ms.`, {
        ...context,
        errorSource: "FetchTimeout",
      });
      throw new McpError(
        BaseErrorCode.TIMEOUT,
        `${operationDescription} timed out.`,
        { ...context, errorSource: "FetchTimeout" },
      );
    }

    // Log and re-throw other errors as McpError
    const errorMessage = error instanceof Error ? error.message : String(error);
    logger.error(
      `Network error during ${operationDescription}: ${errorMessage}`,
      {
        ...context,
        originalErrorName: error instanceof Error ? error.name : "UnknownError",
        errorSource: "FetchNetworkError",
      },
    );

    if (error instanceof McpError) {
      // If it's already an McpError, re-throw it
      throw error;
    }

    throw new McpError(
      BaseErrorCode.SERVICE_UNAVAILABLE, // Generic error for network/service issues
      `Network error during ${operationDescription}: ${errorMessage}`,
      {
        ...context,
        originalErrorName: error instanceof Error ? error.name : "UnknownError",
        errorSource: "FetchNetworkErrorWrapper",
      },
    );
  }
}

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/pubmedArticleConnections/registration.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Registers the 'pubmed_article_connections' tool with the MCP server.
 * This tool finds articles related to a source PMID or retrieves citation formats.
 * @module src/mcp-server/tools/pubmedArticleConnections/registration
 */

import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
import {
  ErrorHandler,
  logger,
  RequestContext,
  requestContextService,
} from "../../../utils/index.js";
import {
  PubMedArticleConnectionsInput,
  PubMedArticleConnectionsInputSchema,
  handlePubMedArticleConnections,
} from "./logic/index.js";

/**
 * Registers the 'pubmed_article_connections' tool with the given MCP server instance.
 * @param {McpServer} server - The MCP server instance.
 */
export async function registerPubMedArticleConnectionsTool(
  server: McpServer,
): Promise<void> {
  const operation = "registerPubMedArticleConnectionsTool";
  const toolName = "pubmed_article_connections";
  const toolDescription =
    "Finds articles related to a source PubMed ID (PMID) or retrieves formatted citations for it. Supports finding similar articles, articles that cite the source, articles referenced by the source (via NCBI ELink), or fetching data to generate citations in various styles (RIS, BibTeX, APA, MLA via NCBI EFetch and server-side formatting). Returns a JSON object detailing the connections or formatted citations.";
  const context = requestContextService.createRequestContext({ operation });

  await ErrorHandler.tryCatch(
    async () => {
      server.tool(
        toolName,
        toolDescription,
        PubMedArticleConnectionsInputSchema.shape,
        async (
          input: PubMedArticleConnectionsInput,
          mcpProvidedContext: unknown,
        ): Promise<CallToolResult> => {
          const richContext: RequestContext =
            requestContextService.createRequestContext({
              parentRequestId: context.requestId,
              operation: "pubMedArticleConnectionsToolHandler",
              mcpToolContext: mcpProvidedContext,
              input,
            });

          try {
            const result = await handlePubMedArticleConnections(
              input,
              richContext,
            );
            return {
              content: [
                { type: "text", text: JSON.stringify(result, null, 2) },
              ],
              isError: false,
            };
          } catch (error) {
            const handledError = ErrorHandler.handleError(error, {
              operation: "pubMedArticleConnectionsToolHandler",
              context: richContext,
              input,
              rethrow: false,
            });

            const mcpError =
              handledError instanceof McpError
                ? handledError
                : new McpError(
                    BaseErrorCode.INTERNAL_ERROR,
                    "An unexpected error occurred while getting PubMed article connections.",
                    {
                      originalErrorName: handledError.name,
                      originalErrorMessage: handledError.message,
                    },
                  );

            return {
              content: [
                {
                  type: "text",
                  text: JSON.stringify({
                    error: {
                      code: mcpError.code,
                      message: mcpError.message,
                      details: mcpError.details,
                    },
                  }),
                },
              ],
              isError: true,
            };
          }
        },
      );
      logger.notice(`Tool '${toolName}' registered.`, context);
    },
    {
      operation,
      context,
      errorCode: BaseErrorCode.INITIALIZATION_FAILED,
      critical: true,
    },
  );
}

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/pubmedResearchAgent/registration.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Registration for the pubmed_research_agent tool.
 * @module pubmedResearchAgent/registration
 */

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
import {
  ErrorHandler,
  logger,
  RequestContext,
  requestContextService,
} from "../../../utils/index.js";
import { pubmedResearchAgentLogic } from "./logic.js";
import {
  PubMedResearchAgentInput,
  PubMedResearchAgentInputSchema,
} from "./logic/index.js";

/**
 * Registers the pubmed_research_agent tool with the MCP server.
 * @param server - The McpServer instance.
 */
export async function registerPubMedResearchAgentTool(
  server: McpServer,
): Promise<void> {
  const operation = "registerPubMedResearchAgentTool";
  const toolName = "pubmed_research_agent";
  const toolDescription =
    "Generates a standardized JSON research plan outline from component details you provide. It accepts granular inputs for all research phases (conception, data collection, analysis, dissemination, cross-cutting concerns). If `include_detailed_prompts_for_agent` is true, the output plan will embed instructive prompts and detailed guidance notes to aid the research agent. The tool's primary function is to organize and structure your rough ideas into a formal, machine-readable plan. This plan is intended for further processing; as the research agent, you should then utilize your full suite of tools (e.g., file manipulation, `get_pubmed_article_connections` for literature/data search via PMID) to execute the outlined research, tailored to the user's request.";
  const context = requestContextService.createRequestContext({ operation });

  await ErrorHandler.tryCatch(
    async () => {
      server.tool(
        toolName,
        toolDescription,
        PubMedResearchAgentInputSchema.shape,
        async (
          input: PubMedResearchAgentInput,
          mcpProvidedContext: unknown,
        ): Promise<CallToolResult> => {
          const richContext: RequestContext =
            requestContextService.createRequestContext({
              parentRequestId: context.requestId,
              operation: "pubmedResearchAgentToolHandler",
              mcpToolContext: mcpProvidedContext,
              input,
            });

          try {
            const result = await pubmedResearchAgentLogic(input, richContext);
            return {
              content: [
                { type: "text", text: JSON.stringify(result, null, 2) },
              ],
              isError: false,
            };
          } catch (error) {
            const handledError = ErrorHandler.handleError(error, {
              operation: "pubmedResearchAgentToolHandler",
              context: richContext,
              input,
              rethrow: false,
            });

            const mcpError =
              handledError instanceof McpError
                ? handledError
                : new McpError(
                    BaseErrorCode.INTERNAL_ERROR,
                    "An unexpected error occurred while generating the research plan.",
                    {
                      originalErrorName: handledError.name,
                      originalErrorMessage: handledError.message,
                    },
                  );

            return {
              content: [
                {
                  type: "text",
                  text: JSON.stringify({
                    error: {
                      code: mcpError.code,
                      message: mcpError.message,
                      details: mcpError.details,
                    },
                  }),
                },
              ],
              isError: true,
            };
          }
        },
      );
      logger.notice(`Tool '${toolName}' registered.`, context);
    },
    {
      operation,
      context,
      errorCode: BaseErrorCode.INITIALIZATION_FAILED,
      critical: true,
    },
  );
}

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/pubmedResearchAgent/logic/outputTypes.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Defines TypeScript types for the structured research plan outline
 * generated by the pubmed_research_agent tool. The tool primarily structures
 * client-provided inputs.
 * @module pubmedResearchAgent/logic/outputTypes
 */

// All string fields are optional as they depend on client input.
// If include_detailed_prompts_for_agent is true, these strings might be prefixed
// with a generic instruction by the planOrchestrator.

export interface Phase1Step1_1_Content {
  primary_research_question?: string;
  knowledge_gap_statement?: string;
  primary_hypothesis?: string;
  pubmed_search_strategy?: string;
  guidance_notes?: string | string[];
}
export interface Phase1Step1_2_Content {
  literature_review_scope?: string;
  key_databases_and_search_approach?: string;
  guidance_notes?: string | string[];
}
export interface Phase1Step1_3_Content {
  experimental_paradigm?: string;
  data_acquisition_plan_existing_data?: string;
  data_acquisition_plan_new_data?: string;
  blast_utilization_plan?: string;
  controls_and_rigor_measures?: string;
  methodological_challenges_and_mitigation?: string;
  guidance_notes?: string | string[];
}
export interface Phase1Output {
  title: "Phase 1: Conception and Planning";
  step_1_1_research_question_and_hypothesis: Phase1Step1_1_Content;
  step_1_2_literature_review_strategy: Phase1Step1_2_Content;
  step_1_3_experimental_design_and_data_acquisition: Phase1Step1_3_Content;
}

export interface Phase2Step2_1_Content {
  data_collection_methods_wet_lab?: string;
  data_collection_methods_dry_lab?: string;
  guidance_notes?: string | string[];
}
export interface Phase2Step2_2_Content {
  data_preprocessing_and_qc_plan?: string;
  guidance_notes?: string | string[];
}
export interface Phase2Output {
  title: "Phase 2: Data Collection and Processing";
  step_2_1_data_collection_retrieval: Phase2Step2_1_Content;
  step_2_2_data_preprocessing_and_qc: Phase2Step2_2_Content;
}

export interface Phase3Step3_1_Content {
  data_analysis_strategy?: string;
  bioinformatics_pipeline_summary?: string;
  guidance_notes?: string | string[];
}
export interface Phase3Step3_2_Content {
  results_interpretation_framework?: string;
  comparison_with_literature_plan?: string;
  guidance_notes?: string | string[];
}
export interface Phase3Output {
  title: "Phase 3: Analysis and Interpretation";
  step_3_1_data_analysis_plan: Phase3Step3_1_Content;
  step_3_2_results_interpretation: Phase3Step3_2_Content;
}

export interface Phase4Step4_1_Content {
  dissemination_manuscript_plan?: string;
  dissemination_data_deposition_plan?: string;
  guidance_notes?: string | string[];
}
export interface Phase4Step4_2_Content {
  peer_review_and_publication_approach?: string;
  guidance_notes?: string | string[];
}
export interface Phase4Step4_3_Content {
  future_research_directions?: string;
  guidance_notes?: string | string[];
}
export interface Phase4Output {
  title: "Phase 4: Dissemination and Iteration";
  step_4_1_dissemination_strategy: Phase4Step4_1_Content;
  step_4_2_peer_review_and_publication: Phase4Step4_2_Content;
  step_4_3_further_research_and_iteration: Phase4Step4_3_Content;
}

export interface CrossCuttingContent {
  record_keeping_and_data_management?: string;
  collaboration_strategy?: string;
  ethical_considerations?: string;
  guidance_notes?: string | string[];
}
export interface CrossCuttingOutput {
  title: "Cross-Cutting Considerations";
  content: CrossCuttingContent;
}

export interface PubMedResearchPlanGeneratedOutput {
  plan_title: string;
  overall_instructions_for_research_agent?: string;
  input_summary: {
    keywords_received: string[];
    primary_goal_stated_or_inferred: string;
    organism_focus?: string; // Ensured this is present
    included_detailed_prompts_for_agent: boolean; // Renamed from included_challenges_consideration for clarity
  };
  phase_1_conception_and_planning: Phase1Output;
  phase_2_data_collection_and_processing: Phase2Output;
  phase_3_analysis_and_interpretation: Phase3Output;
  phase_4_dissemination_and_iteration: Phase4Output;
  cross_cutting_considerations: CrossCuttingOutput;
}

```

--------------------------------------------------------------------------------
/src/mcp-server/transports/http/mcpTransportMiddleware.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Hono middleware for handling MCP transport logic.
 * This middleware encapsulates the logic for processing MCP requests,
 * delegating to the appropriate transport manager, and preparing the
 * response for Hono to send.
 * @module src/mcp-server/transports/http/mcpTransportMiddleware
 */

import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
import { MiddlewareHandler } from "hono";
import { createMiddleware } from "hono/factory";
import { IncomingHttpHeaders } from "http";
import { config } from "../../../config/index.js";
import { RequestContext, requestContextService } from "../../../utils/index.js";
import { ServerInstanceInfo } from "../../server.js";
import { StatefulTransportManager } from "../core/statefulTransportManager.js";
import { StatelessTransportManager } from "../core/statelessTransportManager.js";
import { TransportManager, TransportResponse } from "../core/transportTypes.js";
import { HonoNodeBindings } from "./httpTypes.js";

/**
 * Converts a Fetch API Headers object to Node.js IncomingHttpHeaders.
 * @param headers - The Headers object to convert.
 * @returns An object compatible with IncomingHttpHeaders.
 */
function toIncomingHttpHeaders(headers: Headers): IncomingHttpHeaders {
  const result: IncomingHttpHeaders = {};
  headers.forEach((value, key) => {
    result[key] = value;
  });
  return result;
}

/**
 * Handles a stateless request by creating an ephemeral transport manager.
 * @param createServerInstanceFn - Function to create an McpServer instance.
 * @param headers - The request headers.
 * @param body - The request body.
 * @param context - The request context.
 * @returns A promise resolving with the transport response.
 */
async function handleStatelessRequest(
  createServerInstanceFn: () => Promise<ServerInstanceInfo>,
  headers: Headers,
  body: unknown,
  context: RequestContext,
): Promise<TransportResponse> {
  const getMcpServer = async () => (await createServerInstanceFn()).server;
  const statelessManager = new StatelessTransportManager(getMcpServer);
  return statelessManager.handleRequest(
    toIncomingHttpHeaders(headers),
    body,
    context,
  );
}

/**
 * Creates a Hono middleware for handling MCP POST requests.
 * @param transportManager - The main transport manager (usually stateful).
 * @param createServerInstanceFn - Function to create an McpServer instance.
 * @returns A Hono middleware function.
 */

type McpMiddlewareEnv = {
  Variables: {
    mcpResponse: TransportResponse;
  };
};

export const mcpTransportMiddleware = (
  transportManager: TransportManager,
  createServerInstanceFn: () => Promise<ServerInstanceInfo>,
): MiddlewareHandler<McpMiddlewareEnv & { Bindings: HonoNodeBindings }> => {
  return createMiddleware<McpMiddlewareEnv & { Bindings: HonoNodeBindings }>(
    async (c, next) => {
      const sessionId = c.req.header("mcp-session-id");
      const context = requestContextService.createRequestContext({
        operation: "mcpTransportMiddleware",
        sessionId,
      });

      const body = await c.req.json();
      let response: TransportResponse;

      if (isInitializeRequest(body)) {
        if (config.mcpSessionMode === "stateless") {
          response = await handleStatelessRequest(
            createServerInstanceFn,
            c.req.raw.headers,
            body,
            context,
          );
        } else {
          response = await (
            transportManager as StatefulTransportManager
          ).initializeAndHandle(
            toIncomingHttpHeaders(c.req.raw.headers),
            body,
            context,
          );
        }
      } else {
        if (sessionId) {
          response = await transportManager.handleRequest(
            toIncomingHttpHeaders(c.req.raw.headers),
            body,
            context,
            sessionId,
          );
        } else {
          response = await handleStatelessRequest(
            createServerInstanceFn,
            c.req.raw.headers,
            body,
            context,
          );
        }
      }

      c.set("mcpResponse", response);
      await next();
    },
  );
};

```

--------------------------------------------------------------------------------
/src/services/NCBI/core/ncbiRequestQueueManager.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Manages a queue for NCBI E-utility requests to ensure compliance with rate limits.
 * @module src/services/NCBI/core/ncbiRequestQueueManager
 */

import { config } from "../../../config/index.js";
import {
  logger,
  RequestContext,
  requestContextService,
  sanitizeInputForLogging,
} from "../../../utils/index.js";
import { NcbiRequestParams } from "./ncbiConstants.js";

/**
 * Interface for a queued NCBI request.
 */
export interface QueuedRequest<T = unknown> {
  resolve: (value: T | PromiseLike<T>) => void;
  reject: (reason?: unknown) => void;
  task: () => Promise<T>; // The actual function that makes the API call
  context: RequestContext;
  endpoint: string; // For logging purposes
  params: NcbiRequestParams; // For logging purposes
}

export class NcbiRequestQueueManager {
  private requestQueue: QueuedRequest[] = [];
  private isProcessingQueue = false;
  private lastRequestTime = 0;

  constructor() {
    // The constructor is kept for future initializations, if any.
  }

  /**
   * Processes the request queue, ensuring delays between requests to respect NCBI rate limits.
   */
  private async processQueue(): Promise<void> {
    if (this.isProcessingQueue || this.requestQueue.length === 0) {
      return;
    }
    this.isProcessingQueue = true;

    const requestItem = this.requestQueue.shift();
    if (!requestItem) {
      this.isProcessingQueue = false;
      return;
    }

    const { resolve, reject, task, context, endpoint, params } = requestItem;

    try {
      const now = Date.now();
      const timeSinceLastRequest = now - this.lastRequestTime;
      const delayNeeded = config.ncbiRequestDelayMs - timeSinceLastRequest;

      if (delayNeeded > 0) {
        logger.debug(
          `Delaying NCBI request by ${delayNeeded}ms to respect rate limit.`,
          requestContextService.createRequestContext({
            ...context,
            operation: "NCBI_RateLimitDelay",
            delayNeeded,
            endpoint,
          }),
        );
        await new Promise((r) => setTimeout(r, delayNeeded));
      }

      this.lastRequestTime = Date.now();
      logger.info(
        `Executing NCBI request via queue: ${endpoint}`,
        requestContextService.createRequestContext({
          ...context,
          operation: "NCBI_ExecuteFromQueue",
          endpoint,
          params: sanitizeInputForLogging(params),
        }),
      );
      const result = await task();
      resolve(result);
    } catch (error: unknown) {
      const err = error as Error;
      logger.error(
        "Error processing NCBI request from queue",
        err,
        requestContextService.createRequestContext({
          ...context,
          operation: "NCBI_QueueError",
          endpoint,
          params: sanitizeInputForLogging(params),
          errorMessage: err?.message,
        }),
      );
      reject(err);
    } finally {
      this.isProcessingQueue = false;
      if (this.requestQueue.length > 0) {
        // Ensure processQueue is called without awaiting it here to prevent deep stacks
        Promise.resolve().then(() => this.processQueue());
      }
    }
  }

  /**
   * Enqueues a task (an NCBI API call) to be processed.
   * @param task A function that returns a Promise resolving to the API call result.
   * @param context The request context for logging and correlation.
   * @param endpoint The NCBI endpoint being called (e.g., "esearch", "efetch").
   * @param params The parameters for the NCBI request.
   * @returns A Promise that resolves or rejects with the result of the task.
   */
  public enqueueRequest<T>(
    task: () => Promise<T>,
    context: RequestContext,
    endpoint: string,
    params: NcbiRequestParams,
  ): Promise<T> {
    return new Promise<T>((resolve, reject) => {
      this.requestQueue.push({
        resolve: (value: unknown) => resolve(value as T),
        reject: (reason?: unknown) => reject(reason),
        task,
        context,
        endpoint,
        params,
      });
      if (!this.isProcessingQueue) {
        // Ensure processQueue is called without awaiting it here
        Promise.resolve().then(() => this.processQueue());
      }
    });
  }
}

```

--------------------------------------------------------------------------------
/src/services/NCBI/core/ncbiService.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Service for interacting with NCBI E-utilities.
 * This module centralizes all communication with NCBI's E-utility APIs,
 * handling request construction, API key management, rate limiting,
 * retries, and parsing of XML/JSON responses. It aims to provide a robust
 * and compliant interface for other parts of the pubmed-mcp-server to
 * access PubMed data.
 * @module src/services/NCBI/core/ncbiService
 */

import {
  ESearchResult,
  EFetchArticleSet,
  ESearchResponseContainer,
} from "../../../types-global/pubmedXml.js";
import {
  logger,
  RequestContext,
  requestContextService,
} from "../../../utils/index.js";
import { NcbiRequestParams, NcbiRequestOptions } from "./ncbiConstants.js";
import { NcbiCoreApiClient } from "./ncbiCoreApiClient.js";
import { NcbiRequestQueueManager } from "./ncbiRequestQueueManager.js";
import { NcbiResponseHandler } from "./ncbiResponseHandler.js";

export class NcbiService {
  private queueManager: NcbiRequestQueueManager;
  private apiClient: NcbiCoreApiClient;
  private responseHandler: NcbiResponseHandler;

  constructor() {
    this.queueManager = new NcbiRequestQueueManager();
    this.apiClient = new NcbiCoreApiClient();
    this.responseHandler = new NcbiResponseHandler();
  }

  private async performNcbiRequest<T>(
    endpoint: string,
    params: NcbiRequestParams,
    context: RequestContext,
    options: NcbiRequestOptions = {},
  ): Promise<T> {
    const task = async () => {
      const rawResponse = await this.apiClient.makeRequest(
        endpoint,
        params,
        context,
        options,
      );
      return this.responseHandler.parseAndHandleResponse<T>(
        rawResponse,
        endpoint,
        context,
        options,
      );
    };

    return this.queueManager.enqueueRequest<T>(task, context, endpoint, params);
  }

  public async eSearch(
    params: NcbiRequestParams,
    context: RequestContext,
  ): Promise<ESearchResult> {
    const response = await this.performNcbiRequest<ESearchResponseContainer>(
      "esearch",
      params,
      context,
      {
        retmode: "xml",
      },
    );

    const esResult = response.eSearchResult;
    return {
      count: parseInt(esResult.Count, 10) || 0,
      retmax: parseInt(esResult.RetMax, 10) || 0,
      retstart: parseInt(esResult.RetStart, 10) || 0,
      queryKey: esResult.QueryKey,
      webEnv: esResult.WebEnv,
      idList: esResult.IdList?.Id || [],
      queryTranslation: esResult.QueryTranslation,
      errorList: esResult.ErrorList,
      warningList: esResult.WarningList,
    };
  }

  public async eSummary(
    params: NcbiRequestParams,
    context: RequestContext,
  ): Promise<unknown> {
    // Determine retmode based on params, default to xml
    const retmode =
      params.version === "2.0" && params.retmode === "json" ? "json" : "xml";
    return this.performNcbiRequest("esummary", params, context, { retmode });
  }

  public async eFetch(
    params: NcbiRequestParams,
    context: RequestContext,
    options: NcbiRequestOptions = { retmode: "xml" }, // Default retmode for eFetch
  ): Promise<EFetchArticleSet> {
    // Determine if POST should be used based on number of IDs
    const usePost =
      typeof params.id === "string" && params.id.split(",").length > 200;
    const fetchOptions = { ...options, usePost };

    return this.performNcbiRequest<EFetchArticleSet>(
      "efetch",
      params,
      context,
      fetchOptions,
    );
  }

  public async eLink(
    params: NcbiRequestParams,
    context: RequestContext,
  ): Promise<unknown> {
    return this.performNcbiRequest("elink", params, context, {
      retmode: "xml",
    });
  }

  public async eInfo(
    params: NcbiRequestParams,
    context: RequestContext,
  ): Promise<unknown> {
    return this.performNcbiRequest("einfo", params, context, {
      retmode: "xml",
    });
  }
}

let ncbiServiceInstance: NcbiService;

export function getNcbiService(): NcbiService {
  if (!ncbiServiceInstance) {
    ncbiServiceInstance = new NcbiService();
    logger.debug(
      "NcbiService lazily initialized.",
      requestContextService.createRequestContext({
        service: "NcbiService",
        operation: "getNcbiServiceInstance",
      }),
    );
  }
  return ncbiServiceInstance;
}

```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "@cyanheads/pubmed-mcp-server",
  "version": "1.4.4",
  "description": "Production-ready PubMed Model Context Protocol (MCP) server that empowers AI agents and research tools with comprehensive access to PubMed's article database. Enables advanced, automated LLM workflows for searching, retrieving, analyzing, and visualizing biomedical and scientific literature via NCBI E-utilities.",
  "mcpName": "io.github.cyanheads/pubmed-mcp-server",
  "main": "dist/index.js",
  "files": [
    "dist"
  ],
  "bin": {
    "pubmed-mcp-server": "dist/index.js"
  },
  "exports": "./dist/index.js",
  "type": "module",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/cyanheads/pubmed-mcp-server.git"
  },
  "bugs": {
    "url": "https://github.com/cyanheads/pubmed-mcp-server/issues"
  },
  "homepage": "https://github.com/cyanheads/pubmed-mcp-server#readme",
  "scripts": {
    "build": "tsc && node --loader ts-node/esm scripts/make-executable.ts dist/index.js",
    "start": "node dist/index.js",
    "start:stdio": "MCP_LOG_LEVEL=debug MCP_TRANSPORT_TYPE=stdio node dist/index.js",
    "start:http": "MCP_LOG_LEVEL=debug MCP_TRANSPORT_TYPE=http node dist/index.js",
    "rebuild": "ts-node --esm scripts/clean.ts && npm run build",
    "docs:generate": "typedoc --tsconfig ./tsconfig.typedoc.json",
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    "tree": "ts-node --esm scripts/tree.ts",
    "fetch-spec": "ts-node --esm scripts/fetch-openapi-spec.ts",
    "format": "prettier --write \"**/*.{ts,js,json,md,html,css}\"",
    "inspector": "mcp-inspector --config mcp.json --server pubmed-mcp-server",
    "test": "vitest run",
    "test:watch": "vitest",
    "test:coverage": "vitest run --coverage"
  },
  "dependencies": {
    "@hono/node-server": "^1.17.1",
    "@modelcontextprotocol/sdk": "^1.18.0",
    "@opentelemetry/api": "^1.9.0",
    "@opentelemetry/auto-instrumentations-node": "^0.64.1",
    "@opentelemetry/exporter-metrics-otlp-http": "^0.205.0",
    "@opentelemetry/exporter-trace-otlp-http": "^0.205.0",
    "@opentelemetry/instrumentation-winston": "^0.50.0",
    "@opentelemetry/resources": "^2.1.0",
    "@opentelemetry/sdk-metrics": "^2.1.0",
    "@opentelemetry/sdk-node": "^0.205.0",
    "@opentelemetry/sdk-trace-node": "^2.1.0",
    "@opentelemetry/semantic-conventions": "^1.37.0",
    "@types/node": "^24.4.0",
    "@types/sanitize-html": "^2.16.0",
    "@types/validator": "13.15.3",
    "axios": "^1.12.2",
    "chart.js": "^4.5.0",
    "chartjs-node-canvas": "^5.0.0",
    "chrono-node": "^2.8.3",
    "citation-js": "^0.7.20",
    "dotenv": "^16.6.1",
    "fast-xml-parser": "^5.2.5",
    "hono": "^4.8.4",
    "jose": "^6.1.0",
    "js-yaml": "^4.1.0",
    "node-cron": "^4.2.1",
    "openai": "^5.20.2",
    "partial-json": "^0.1.7",
    "patch-package": "^8.0.0",
    "sanitize-html": "^2.17.0",
    "tiktoken": "^1.0.22",
    "ts-node": "^10.9.2",
    "typescript": "^5.9.2",
    "typescript-eslint": "^8.43.0",
    "validator": "13.15.15",
    "winston": "^3.17.0",
    "winston-transport": "^4.9.0",
    "zod": "^3.25.74"
  },
  "keywords": [
    "mcp",
    "model-context-protocol",
    "ai-agent",
    "llm",
    "llm-integration",
    "pubmed",
    "pubmed-api",
    "ncbi",
    "e-utilities",
    "biomedical-research",
    "scientific-literature",
    "computational-biology",
    "typescript",
    "ai-tools",
    "bioinformatics",
    "health-tech",
    "research-automation",
    "literature-search",
    "citation-analysis",
    "data-visualization",
    "api-server",
    "typescript",
    "nodejs",
    "ai-tools",
    "research-agent",
    "generative-ai"
  ],
  "author": "Casey Hand @cyanheads",
  "email": "[email protected]",
  "license": "Apache-2.0",
  "funding": [
    {
      "type": "github",
      "url": "https://github.com/sponsors/cyanheads"
    },
    {
      "type": "buy_me_a_coffee",
      "url": "https://www.buymeacoffee.com/cyanheads"
    }
  ],
  "engines": {
    "node": ">=20.0.0"
  },
  "devDependencies": {
    "@types/js-yaml": "^4.0.9",
    "@types/node-cron": "^3.0.11",
    "ajv": "^8.17.1",
    "ajv-formats": "^3.0.1",
    "eslint": "^9.35.0",
    "prettier": "^3.6.2",
    "typedoc": "^0.28.13",
    "vite-tsconfig-paths": "^5.1.4",
    "vitest": "^3.2.4"
  },
  "overrides": {
    "patch-package": {
      "tmp": "0.2.5"
    }
  },
  "publishConfig": {
    "access": "public"
  }
}

```

--------------------------------------------------------------------------------
/scripts/make-executable.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node

/**
 * @fileoverview Utility script to make files executable (chmod +x) on Unix-like systems.
 * @module scripts/make-executable
 *   On Windows, this script does nothing but exits successfully.
 *   Useful for CLI applications where built output needs executable permissions.
 *   Default target (if no args): dist/index.js.
 *   Ensures output paths are within the project directory for security.
 *
 * @example
 * // Add to package.json build script:
 * // "build": "tsc && ts-node --esm scripts/make-executable.ts dist/index.js"
 *
 * @example
 * // Run directly with custom files:
 * // ts-node --esm scripts/make-executable.ts path/to/script1 path/to/script2
 */

import fs from "fs/promises";
import os from "os";
import path from "path";

const isUnix = os.platform() !== "win32";
const projectRoot = process.cwd();
const EXECUTABLE_MODE = 0o755; // rwxr-xr-x

/**
 * Represents the result of an attempt to make a file executable.
 * @property file - The relative path of the file targeted.
 * @property status - The outcome of the operation ('success', 'error', or 'skipped').
 * @property reason - If status is 'error' or 'skipped', an explanation.
 */
interface ExecutableResult {
  file: string;
  status: "success" | "error" | "skipped";
  reason?: string;
}

/**
 * Main function to make specified files executable.
 * Skips operation on Windows. Processes command-line arguments for target files
 * or defaults to 'dist/index.js'. Reports status for each file.
 */
const makeExecutable = async (): Promise<void> => {
  try {
    const targetFiles: string[] =
      process.argv.slice(2).length > 0
        ? process.argv.slice(2)
        : ["dist/index.js"];

    if (!isUnix) {
      console.log(
        "Skipping chmod operation: Script is running on Windows (not applicable).",
      );
      return;
    }

    console.log(
      `Attempting to make files executable: ${targetFiles.join(", ")}`,
    );

    const results = await Promise.allSettled(
      targetFiles.map(async (targetFile): Promise<ExecutableResult> => {
        const normalizedPath = path.resolve(projectRoot, targetFile);

        if (
          !normalizedPath.startsWith(projectRoot + path.sep) &&
          normalizedPath !== projectRoot
        ) {
          return {
            file: targetFile,
            status: "error",
            reason: `Path resolves outside project boundary: ${normalizedPath}`,
          };
        }

        try {
          await fs.access(normalizedPath); // Check if file exists
          await fs.chmod(normalizedPath, EXECUTABLE_MODE);
          return { file: targetFile, status: "success" };
        } catch (error) {
          const err = error as NodeJS.ErrnoException;
          if (err.code === "ENOENT") {
            return {
              file: targetFile,
              status: "error",
              reason: "File not found",
            };
          }
          console.error(
            `Error setting executable permission for ${targetFile}: ${err.message}`,
          );
          return { file: targetFile, status: "error", reason: err.message };
        }
      }),
    );

    let hasErrors = false;
    results.forEach((result) => {
      if (result.status === "fulfilled") {
        const { file, status, reason } = result.value;
        if (status === "success") {
          console.log(`Successfully made executable: ${file}`);
        } else if (status === "error") {
          console.error(`Error for ${file}: ${reason}`);
          hasErrors = true;
        } else if (status === "skipped") {
          // This status is not currently generated by the mapAsync logic but kept for future flexibility
          console.warn(`Skipped ${file}: ${reason}`);
        }
      } else {
        console.error(
          `Unexpected failure for one of the files: ${result.reason}`,
        );
        hasErrors = true;
      }
    });

    if (hasErrors) {
      console.error(
        "One or more files could not be made executable. Please check the errors above.",
      );
      // process.exit(1); // Uncomment to exit with error if any file fails
    } else {
      console.log("All targeted files processed successfully.");
    }
  } catch (error) {
    console.error(
      "A fatal error occurred during the make-executable script:",
      error instanceof Error ? error.message : error,
    );
    process.exit(1);
  }
};

makeExecutable();

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/pubmedArticleConnections/logic/index.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Main logic handler for the 'pubmed_article_connections' MCP tool.
 * Orchestrates calls to ELink or citation formatting handlers.
 * @module src/mcp-server/tools/pubmedArticleConnections/logic/index
 */

import { z } from "zod";
import { BaseErrorCode, McpError } from "../../../../types-global/errors.js";
import {
  logger,
  RequestContext,
  requestContextService,
  sanitizeInputForLogging,
} from "../../../../utils/index.js";
import { handleCitationFormats } from "./citationFormatter.js";
import { handleELinkRelationships } from "./elinkHandler.js";
import type { ToolOutputData } from "./types.js";

/**
 * Zod schema for the input parameters of the 'pubmed_article_connections' tool.
 */
export const PubMedArticleConnectionsInputSchema = z.object({
  sourcePmid: z
    .string()
    .regex(/^\d+$/)
    .describe(
      "The PubMed Unique Identifier (PMID) of the source article for which to find connections or format citations. This PMID must be a valid number string.",
    ),
  relationshipType: z
    .enum([
      "pubmed_similar_articles",
      "pubmed_citedin",
      "pubmed_references",
      "citation_formats",
    ])
    .default("pubmed_similar_articles")
    .describe(
      "Specifies the type of connection or action: 'pubmed_similar_articles' (finds similar articles), 'pubmed_citedin' (finds citing articles), 'pubmed_references' (finds referenced articles), or 'citation_formats' (retrieves formatted citations).",
    ),
  maxRelatedResults: z
    .number()
    .int()
    .positive()
    .max(50, "Maximum 50 related results can be requested.")
    .optional()
    .default(5)
    .describe(
      "Maximum number of related articles to retrieve for relationship-based searches. Default is 5, max is 50.",
    ),
  citationStyles: z
    .array(z.enum(["ris", "bibtex", "apa_string", "mla_string"]))
    .optional()
    .default(["ris"])
    .describe(
      "An array of citation styles to format the source article into when 'relationshipType' is 'citation_formats'. Supported styles: 'ris', 'bibtex', 'apa_string', 'mla_string'. Default is ['ris'].",
    ),
});

/**
 * Type alias for the validated input of the 'pubmed_article_connections' tool.
 */
export type PubMedArticleConnectionsInput = z.infer<
  typeof PubMedArticleConnectionsInputSchema
>;

/**
 * Main handler for the 'pubmed_article_connections' tool.
 * @param {PubMedArticleConnectionsInput} input - Validated input parameters.
 * @param {RequestContext} context - The request context for this tool invocation.
 * @returns {Promise<ToolOutputData>} The result of the tool call.
 */
export async function handlePubMedArticleConnections(
  input: PubMedArticleConnectionsInput,
  context: RequestContext,
): Promise<ToolOutputData> {
  const toolLogicContext = requestContextService.createRequestContext({
    parentRequestId: context.requestId,
    operation: "handlePubMedArticleConnections",
    toolName: "pubmed_article_connections",
    input: sanitizeInputForLogging(input),
  });

  logger.info("Executing pubmed_article_connections tool", toolLogicContext);

  const outputData: ToolOutputData = {
    sourcePmid: input.sourcePmid,
    relationshipType: input.relationshipType,
    relatedArticles: [],
    citations: {},
    retrievedCount: 0,
    eUtilityUrl: undefined,
    message: undefined,
  };

  switch (input.relationshipType) {
    case "pubmed_similar_articles":
    case "pubmed_citedin":
    case "pubmed_references":
      await handleELinkRelationships(input, outputData, toolLogicContext);
      break;
    case "citation_formats":
      await handleCitationFormats(input, outputData, toolLogicContext);
      break;
    default:
      throw new McpError(
        BaseErrorCode.VALIDATION_ERROR,
        `Unsupported relationshipType: ${input.relationshipType}`,
        { ...toolLogicContext, receivedType: input.relationshipType },
      );
  }

  if (
    outputData.retrievedCount === 0 &&
    !outputData.message &&
    (input.relationshipType !== "citation_formats" ||
      Object.keys(outputData.citations).length === 0)
  ) {
    outputData.message = "No results found for the given parameters.";
  }

  logger.notice("Successfully executed pubmed_article_connections tool.", {
    ...toolLogicContext,
    relationshipType: input.relationshipType,
    retrievedCount: outputData.retrievedCount,
    citationsGenerated: Object.keys(outputData.citations).length,
  });

  return outputData;
}

```

--------------------------------------------------------------------------------
/src/utils/internal/requestContext.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Utilities for creating and managing request contexts.
 * A request context is an object carrying a unique ID, timestamp, and other
 * relevant data for logging, tracing, and processing. It also defines
 * configuration and operational context structures.
 * @module src/utils/internal/requestContext
 */

import { trace } from "@opentelemetry/api";
import { generateUUID } from "../index.js";
import { logger } from "./logger.js";

/**
 * Defines the core structure for context information associated with a request or operation.
 * This is fundamental for logging, tracing, and passing operational data.
 */
export interface RequestContext {
  /**
   * Unique ID for the context instance.
   * Used for log correlation and request tracing.
   */
  requestId: string;

  /**
   * ISO 8601 timestamp indicating when the context was created.
   */
  timestamp: string;

  /**
   * Allows arbitrary key-value pairs for specific context needs.
   * Using `unknown` promotes type-safe access.
   * Consumers must type-check/assert when accessing extended properties.
   */
  [key: string]: unknown;
}

/**
 * Configuration for the {@link requestContextService}.
 * Allows for future extensibility of service-wide settings.
 */
export interface ContextConfig {
  /** Custom configuration properties. Allows for arbitrary key-value pairs. */
  [key: string]: unknown;
}

/**
 * Represents a broader context for a specific operation or task.
 * It can optionally include a base {@link RequestContext} and other custom properties
 * relevant to the operation.
 */
export interface OperationContext {
  /** Optional base request context data, adhering to the `RequestContext` structure. */
  requestContext?: RequestContext;

  /** Allows for additional, custom properties specific to the operation. */
  [key: string]: unknown;
}

/**
 * Singleton-like service object for managing request context operations.
 * @private
 */
const requestContextServiceInstance = {
  /**
   * Internal configuration store for the service.
   */
  config: {} as ContextConfig,

  /**
   * Configures the request context service with new settings.
   * Merges the provided partial configuration with existing settings.
   *
   * @param config - A partial `ContextConfig` object containing settings to update or add.
   * @returns A shallow copy of the newly updated configuration.
   */
  configure(config: Partial<ContextConfig>): ContextConfig {
    this.config = {
      ...this.config,
      ...config,
    };
    const logContext = this.createRequestContext({
      operation: "RequestContextService.configure",
      newConfigState: { ...this.config },
    });
    logger.debug("RequestContextService configuration updated", logContext);
    return { ...this.config };
  },

  /**
   * Retrieves a shallow copy of the current service configuration.
   * This prevents direct mutation of the internal configuration state.
   *
   * @returns A shallow copy of the current `ContextConfig`.
   */
  getConfig(): ContextConfig {
    return { ...this.config };
  },

  /**
   * Creates a new {@link RequestContext} instance.
   * Each context is assigned a unique `requestId` (UUID) and a current `timestamp` (ISO 8601).
   * Additional custom properties can be merged into the context.
   *
   * @param additionalContext - An optional record of key-value pairs to be
   *   included in the created request context.
   * @returns A new `RequestContext` object.
   */
  createRequestContext(
    additionalContext: Record<string, unknown> = {},
  ): RequestContext {
    const requestId = generateUUID();
    const timestamp = new Date().toISOString();

    const context: RequestContext = {
      requestId,
      timestamp,
      ...additionalContext,
    };

    // --- OpenTelemetry Integration ---
    // Automatically inject active trace and span IDs into the context for correlation.
    const activeSpan = trace.getActiveSpan();
    if (activeSpan) {
      const spanContext = activeSpan.spanContext();
      context.traceId = spanContext.traceId;
      context.spanId = spanContext.spanId;
    }
    // --- End OpenTelemetry Integration ---

    return context;
  },
};

/**
 * Primary export for request context functionalities.
 * This service provides methods to create and manage {@link RequestContext} instances,
 * which are essential for logging, tracing, and correlating operations.
 */
export const requestContextService = requestContextServiceInstance;

```

--------------------------------------------------------------------------------
/src/mcp-server/transports/core/transportTypes.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Defines the core types and interfaces for the transport layer abstraction.
 * This module establishes the data contracts and abstract interfaces that decouple
 * the MCP server's core logic from specific transport implementations like HTTP or stdio.
 * @module src/mcp-server/transports/core/transportTypes
 */

import type { IncomingHttpHeaders } from "http";
import { RequestContext } from "../../../utils/index.js";

/**
 * Defines the set of valid HTTP status codes that the transport layer can return.
 * This ensures type safety and consistency in response handling.
 */
export type HttpStatusCode =
  | 200 // OK
  | 201 // Created
  | 400 // Bad Request
  | 401 // Unauthorized
  | 403 // Forbidden
  | 404 // Not Found
  | 409 // Conflict
  | 429 // Too Many Requests
  | 500 // Internal Server Error
  | 502 // Bad Gateway
  | 503; // Service Unavailable

/**
 * A base interface for all transport responses, containing common properties.
 */
interface BaseTransportResponse {
  sessionId?: string;
  headers: Headers;
  statusCode: HttpStatusCode;
}

/**
 * Represents a transport response where the entire body is buffered in memory.
 * Suitable for small, non-streamed responses.
 */
export interface BufferedTransportResponse extends BaseTransportResponse {
  type: "buffered";
  body: unknown;
}

/**
 * Represents a transport response that streams its body.
 * Essential for handling large or chunked responses efficiently without high memory usage.
 */
export interface StreamingTransportResponse extends BaseTransportResponse {
  type: "stream";
  stream: ReadableStream<Uint8Array>;
}

/**
 * A discriminated union representing the possible types of a transport response.
 * Using a discriminated union on the `type` property allows for type-safe handling
 * of different response formats (buffered vs. streamed).
 */
export type TransportResponse =
  | BufferedTransportResponse
  | StreamingTransportResponse;

/**
 * Represents the state of an active, persistent transport session.
 */
export interface TransportSession {
  id: string;
  createdAt: Date;
  lastAccessedAt: Date;
  /**
   * A counter for requests currently being processed for this session.
   * This is a critical mechanism to prevent race conditions where a session
   * might be garbage-collected while a long-running request is still in flight.
   * It is incremented when a request begins and decremented when it finishes.
   */
  activeRequests: number;
}

/**
 * Defines the abstract interface for a transport manager.
 * This contract ensures that any transport manager, regardless of its statefulness,
 * provides a consistent way to handle requests and manage its lifecycle.
 */
export interface TransportManager {
  /**
   * Handles an incoming request.
   * @param headers The incoming request headers.
   * @param body The parsed body of the request.
   * @param context The request context for logging, tracing, and metadata.
   * @param sessionId An optional session identifier for stateful operations.
   * @returns A promise that resolves to a TransportResponse object.
   */
  handleRequest(
    headers: IncomingHttpHeaders,
    body: unknown,
    context: RequestContext,
    sessionId?: string,
  ): Promise<TransportResponse>;

  /**
   * Gracefully shuts down the transport manager, cleaning up any resources.
   */
  shutdown(): Promise<void>;
}

/**
 * Extends the base TransportManager with operations specific to stateful sessions.
 */
export interface StatefulTransportManager extends TransportManager {
  /**
   * Initializes a new stateful session and handles the first request.
   * @param headers The incoming request headers.
   * @param body The parsed body of the request.
   * @param context The request context.
   * @returns A promise resolving to a TransportResponse, which will include a session ID.
   */
  initializeAndHandle(
    headers: IncomingHttpHeaders,
    body: unknown,
    context: RequestContext,
  ): Promise<TransportResponse>;

  /**
   * Handles a request to explicitly delete a session.
   * @param sessionId The ID of the session to delete.
   * @param context The request context.
   * @returns A promise resolving to a TransportResponse confirming closure.
   */
  handleDeleteRequest(
    sessionId: string,
    context: RequestContext,
  ): Promise<TransportResponse>;

  /**
   * Retrieves information about a specific session.
   * @param sessionId The ID of the session to retrieve.
   * @returns A TransportSession object if the session exists, otherwise undefined.
   */
  getSession(sessionId: string): TransportSession | undefined;
}

```

--------------------------------------------------------------------------------
/src/utils/parsing/dateParser.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Provides utility functions for parsing natural language date strings
 * into Date objects or detailed parsing results using the `chrono-node` library.
 * @module src/utils/parsing/dateParser
 */
import * as chrono from "chrono-node";
import { BaseErrorCode } from "../../types-global/errors.js";
import { ErrorHandler, logger, RequestContext } from "../index.js";

/**
 * Parses a natural language date string into a JavaScript Date object.
 * Uses `chrono.parseDate` for lenient parsing of various date formats.
 *
 * @param text - The natural language date string to parse.
 * @param context - The request context for logging and error tracking.
 * @param refDate - Optional reference date for parsing relative dates. Defaults to current date/time.
 * @returns A promise resolving with a Date object or `null` if parsing fails.
 * @throws {McpError} If an unexpected error occurs during parsing.
 * @private
 */
export async function parseDateString(
  text: string,
  context: RequestContext,
  refDate?: Date,
): Promise<Date | null> {
  const operation = "parseDateString";
  const logContext = { ...context, operation, inputText: text, refDate };
  logger.debug(`Attempting to parse date string: "${text}"`, logContext);

  return await ErrorHandler.tryCatch(
    async () => {
      const parsedDate = chrono.parseDate(text, refDate, { forwardDate: true });
      if (parsedDate) {
        logger.debug(
          `Successfully parsed "${text}" to ${parsedDate.toISOString()}`,
          logContext,
        );
        return parsedDate;
      } else {
        logger.warning(`Failed to parse date string: "${text}"`, logContext);
        return null;
      }
    },
    {
      operation,
      context: logContext,
      input: { text, refDate },
      errorCode: BaseErrorCode.PARSING_ERROR,
    },
  );
}

/**
 * Parses a natural language date string and returns detailed parsing results.
 * Provides more information than just the Date object, including matched text and components.
 *
 * @param text - The natural language date string to parse.
 * @param context - The request context for logging and error tracking.
 * @param refDate - Optional reference date for parsing relative dates. Defaults to current date/time.
 * @returns A promise resolving with an array of `chrono.ParsedResult` objects. Empty if no dates found.
 * @throws {McpError} If an unexpected error occurs during parsing.
 * @private
 */
export async function parseDateStringDetailed(
  text: string,
  context: RequestContext,
  refDate?: Date,
): Promise<chrono.ParsedResult[]> {
  const operation = "parseDateStringDetailed";
  const logContext = { ...context, operation, inputText: text, refDate };
  logger.debug(
    `Attempting detailed parse of date string: "${text}"`,
    logContext,
  );

  return await ErrorHandler.tryCatch(
    async () => {
      const results = chrono.parse(text, refDate, { forwardDate: true });
      logger.debug(
        `Detailed parse of "${text}" resulted in ${results.length} result(s)`,
        logContext,
      );
      return results;
    },
    {
      operation,
      context: logContext,
      input: { text, refDate },
      errorCode: BaseErrorCode.PARSING_ERROR,
    },
  );
}

/**
 * An object providing date parsing functionalities.
 *
 * @example
 * ```typescript
 * import { dateParser, requestContextService } from './utils'; // Assuming utils/index.js exports these
 * const context = requestContextService.createRequestContext({ operation: 'TestDateParsing' });
 *
 * async function testParsing() {
 *   const dateObj = await dateParser.parseDate("next Friday at 3pm", context);
 *   if (dateObj) {
 *     console.log("Parsed Date:", dateObj.toISOString());
 *   }
 *
 *   const detailedResults = await dateParser.parse("Meeting on 2024-12-25 and another one tomorrow", context);
 *   detailedResults.forEach(result => {
 *     console.log("Detailed Result:", result.text, result.start.date());
 *   });
 * }
 * testParsing();
 * ```
 */
export const dateParser = {
  /**
   * Parses a natural language date string and returns detailed parsing results
   * from `chrono-node`.
   * @param text - The natural language date string to parse.
   * @param context - The request context for logging and error tracking.
   * @param refDate - Optional reference date for parsing relative dates.
   * @returns A promise resolving with an array of `chrono.ParsedResult` objects.
   */
  parse: parseDateStringDetailed,
  /**
   * Parses a natural language date string into a single JavaScript Date object.
   * @param text - The natural language date string to parse.
   * @param context - The request context for logging and error tracking.
   * @param refDate - Optional reference date for parsing relative dates.
   * @returns A promise resolving with a Date object or `null`.
   */
  parseDate: parseDateString,
};

```

--------------------------------------------------------------------------------
/src/utils/metrics/tokenCounter.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Provides utility functions for counting tokens in text and chat messages
 * using the `tiktoken` library, specifically configured for 'gpt-4o' tokenization.
 * These functions are essential for managing token limits and estimating costs
 * when interacting with language models.
 * @module src/utils/metrics/tokenCounter
 */
import { ChatCompletionMessageParam } from "openai/resources/chat/completions";
import { encoding_for_model, Tiktoken, TiktokenModel } from "tiktoken";
import { BaseErrorCode } from "../../types-global/errors.js";
import { ErrorHandler, logger, RequestContext } from "../index.js";

/**
 * The specific Tiktoken model used for all tokenization operations in this module.
 * This ensures consistent token counting.
 * @private
 */
const TOKENIZATION_MODEL: TiktokenModel = "gpt-4o";

/**
 * Calculates the number of tokens for a given text string using the
 * tokenizer specified by `TOKENIZATION_MODEL`.
 * Wraps tokenization in `ErrorHandler.tryCatch` for robust error management.
 *
 * @param text - The input text to tokenize.
 * @param context - Optional request context for logging and error handling.
 * @returns A promise that resolves with the number of tokens in the text.
 * @throws {McpError} If tokenization fails.
 */
export async function countTokens(
  text: string,
  context?: RequestContext,
): Promise<number> {
  return ErrorHandler.tryCatch(
    () => {
      let encoding: Tiktoken | null = null;
      try {
        encoding = encoding_for_model(TOKENIZATION_MODEL);
        const tokens = encoding.encode(text);
        return tokens.length;
      } finally {
        encoding?.free();
      }
    },
    {
      operation: "countTokens",
      context: context,
      input: { textSample: text.substring(0, 50) + "..." },
      errorCode: BaseErrorCode.INTERNAL_ERROR,
    },
  );
}

/**
 * Calculates the estimated number of tokens for an array of chat messages.
 * Uses the tokenizer specified by `TOKENIZATION_MODEL` and accounts for
 * special tokens and message overhead according to OpenAI's guidelines.
 *
 * For multi-part content, only text parts are currently tokenized.
 *
 * Reference: {@link https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb}
 *
 * @param messages - An array of chat messages.
 * @param context - Optional request context for logging and error handling.
 * @returns A promise that resolves with the estimated total number of tokens.
 * @throws {McpError} If tokenization fails.
 */
export async function countChatTokens(
  messages: ReadonlyArray<ChatCompletionMessageParam>,
  context?: RequestContext,
): Promise<number> {
  return ErrorHandler.tryCatch(
    () => {
      let encoding: Tiktoken | null = null;
      let num_tokens = 0;
      try {
        encoding = encoding_for_model(TOKENIZATION_MODEL);

        const tokens_per_message = 3; // For gpt-4o, gpt-4, gpt-3.5-turbo
        const tokens_per_name = 1; // For gpt-4o, gpt-4, gpt-3.5-turbo

        for (const message of messages) {
          num_tokens += tokens_per_message;
          num_tokens += encoding.encode(message.role).length;

          if (typeof message.content === "string") {
            num_tokens += encoding.encode(message.content).length;
          } else if (Array.isArray(message.content)) {
            for (const part of message.content) {
              if (part.type === "text") {
                num_tokens += encoding.encode(part.text).length;
              } else {
                logger.warning(
                  `Non-text content part found (type: ${part.type}), token count contribution ignored.`,
                  context,
                );
              }
            }
          }

          if ("name" in message && message.name) {
            num_tokens += tokens_per_name;
            num_tokens += encoding.encode(message.name).length;
          }

          if (
            message.role === "assistant" &&
            "tool_calls" in message &&
            message.tool_calls
          ) {
            for (const tool_call of message.tool_calls) {
              if (tool_call.type === "function" && tool_call.function.name) {
                num_tokens += encoding.encode(tool_call.function.name).length;
                if (tool_call.function.arguments) {
                  num_tokens += encoding.encode(
                    tool_call.function.arguments,
                  ).length;
                }
              }
            }
          }

          if (
            message.role === "tool" &&
            "tool_call_id" in message &&
            message.tool_call_id
          ) {
            num_tokens += encoding.encode(message.tool_call_id).length;
          }
        }
        num_tokens += 3; // Every reply is primed with <|start|>assistant<|message|>
        return num_tokens;
      } finally {
        encoding?.free();
      }
    },
    {
      operation: "countChatTokens",
      context: context,
      input: { messageCount: messages.length },
      errorCode: BaseErrorCode.INTERNAL_ERROR,
    },
  );
}

```

--------------------------------------------------------------------------------
/src/mcp-server/transports/auth/strategies/oauthStrategy.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Implements the OAuth 2.1 authentication strategy.
 * This module provides a concrete implementation of the AuthStrategy for validating
 * JWTs against a remote JSON Web Key Set (JWKS), as is common in OAuth 2.1 flows.
 * @module src/mcp-server/transports/auth/strategies/OauthStrategy
 */
import { createRemoteJWKSet, jwtVerify, JWTVerifyResult } from "jose";
import { config } from "../../../../config/index.js";
import { BaseErrorCode, McpError } from "../../../../types-global/errors.js";
import {
  ErrorHandler,
  logger,
  requestContextService,
} from "../../../../utils/index.js";
import type { AuthInfo } from "../lib/authTypes.js";
import type { AuthStrategy } from "./authStrategy.js";

export class OauthStrategy implements AuthStrategy {
  private readonly jwks: ReturnType<typeof createRemoteJWKSet>;

  constructor() {
    const context = requestContextService.createRequestContext({
      operation: "OauthStrategy.constructor",
    });
    logger.debug("Initializing OauthStrategy...", context);

    if (config.mcpAuthMode !== "oauth") {
      // This check is for internal consistency, so a standard Error is acceptable here.
      throw new Error("OauthStrategy instantiated for non-oauth auth mode.");
    }
    if (!config.oauthIssuerUrl || !config.oauthAudience) {
      logger.fatal(
        "CRITICAL: OAUTH_ISSUER_URL and OAUTH_AUDIENCE must be set for OAuth mode.",
        context,
      );
      // This is a user-facing configuration error, so McpError is appropriate.
      throw new McpError(
        BaseErrorCode.CONFIGURATION_ERROR,
        "OAUTH_ISSUER_URL and OAUTH_AUDIENCE must be set for OAuth mode.",
        context,
      );
    }

    try {
      const jwksUrl = new URL(
        config.oauthJwksUri ||
          `${config.oauthIssuerUrl.replace(/\/$/, "")}/.well-known/jwks.json`,
      );
      this.jwks = createRemoteJWKSet(jwksUrl, {
        cooldownDuration: 300000, // 5 minutes
        timeoutDuration: 5000, // 5 seconds
      });
      logger.info(`JWKS client initialized for URL: ${jwksUrl.href}`, context);
    } catch (error) {
      logger.fatal("Failed to initialize JWKS client.", {
        ...context,
        error: error instanceof Error ? error.message : String(error),
      });
      // This is a critical startup failure, so a specific McpError is warranted.
      throw new McpError(
        BaseErrorCode.SERVICE_UNAVAILABLE,
        "Could not initialize JWKS client for OAuth strategy.",
        {
          ...context,
          originalError: error instanceof Error ? error.message : "Unknown",
        },
      );
    }
  }

  async verify(token: string): Promise<AuthInfo> {
    const context = requestContextService.createRequestContext({
      operation: "OauthStrategy.verify",
    });
    logger.debug("Attempting to verify OAuth token via JWKS.", context);

    try {
      const { payload }: JWTVerifyResult = await jwtVerify(token, this.jwks, {
        issuer: config.oauthIssuerUrl!,
        audience: config.oauthAudience!,
      });
      logger.debug("OAuth token signature verified successfully.", {
        ...context,
        claims: payload,
      });

      const scopes =
        typeof payload.scope === "string" ? payload.scope.split(" ") : [];
      if (scopes.length === 0) {
        logger.warning(
          "Invalid token: missing or empty 'scope' claim.",
          context,
        );
        throw new McpError(
          BaseErrorCode.UNAUTHORIZED,
          "Token must contain valid, non-empty scopes.",
          context,
        );
      }

      const clientId =
        typeof payload.client_id === "string" ? payload.client_id : undefined;
      if (!clientId) {
        logger.warning("Invalid token: missing 'client_id' claim.", context);
        throw new McpError(
          BaseErrorCode.UNAUTHORIZED,
          "Token must contain a 'client_id' claim.",
          context,
        );
      }

      const authInfo: AuthInfo = {
        token,
        clientId,
        scopes,
        subject: typeof payload.sub === "string" ? payload.sub : undefined,
      };
      logger.info("OAuth token verification successful.", {
        ...context,
        clientId,
        scopes,
      });
      return authInfo;
    } catch (error) {
      // If the error is already a structured McpError, re-throw it directly.
      if (error instanceof McpError) {
        throw error;
      }

      const message =
        error instanceof Error && error.name === "JWTExpired"
          ? "Token has expired."
          : "OAuth token verification failed.";

      logger.warning(`OAuth token verification failed: ${message}`, {
        ...context,
        errorName: error instanceof Error ? error.name : "Unknown",
      });

      // For all other errors, use the ErrorHandler to wrap them.
      throw ErrorHandler.handleError(error, {
        operation: "OauthStrategy.verify",
        context,
        rethrow: true,
        errorCode: BaseErrorCode.UNAUTHORIZED,
        errorMapper: () =>
          new McpError(BaseErrorCode.UNAUTHORIZED, message, context),
      });
    }
  }
}

```

--------------------------------------------------------------------------------
/src/utils/scheduling/scheduler.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Provides a singleton service for scheduling and managing cron jobs.
 * This service wraps the 'node-cron' library to offer a unified interface for
 * defining, starting, stopping, and listing recurring tasks within the application.
 * @module src/utils/scheduling/scheduler
 */

import cron, { ScheduledTask, createTask } from "node-cron";
import { logger, RequestContext } from "../internal/index.js";
import { requestContextService } from "../internal/requestContext.js";

/**
 * Represents a scheduled job managed by the SchedulerService.
 */
export interface Job {
  /** A unique identifier for the job. */
  id: string;
  /** The cron pattern defining the job's schedule. */
  schedule: string;
  /** A description of what the job does. */
  description: string;
  /** The underlying 'node-cron' task instance. */
  task: ScheduledTask;
  /** Indicates whether the job is currently running. */
  isRunning: boolean;
}

/**
 * A singleton service for scheduling and managing cron jobs.
 */
export class SchedulerService {
  private static instance: SchedulerService;
  private jobs: Map<string, Job> = new Map();

  /** @private */
  private constructor() {
    logger.info("SchedulerService initialized.", {
      requestId: "scheduler-init",
      timestamp: new Date().toISOString(),
    });
  }

  /**
   * Gets the singleton instance of the SchedulerService.
   * @returns The singleton SchedulerService instance.
   */
  public static getInstance(): SchedulerService {
    if (!SchedulerService.instance) {
      SchedulerService.instance = new SchedulerService();
    }
    return SchedulerService.instance;
  }

  /**
   * Schedules a new job.
   *
   * @param id - A unique identifier for the job.
   * @param schedule - The cron pattern for the schedule (e.g., '* * * * *').
   * @param taskFunction - The function to execute on schedule. It receives a RequestContext.
   * @param description - A description of the job.
   * @returns The newly created Job object.
   */
  public schedule(
    id: string,
    schedule: string,
    taskFunction: (context: RequestContext) => void | Promise<void>,
    description: string,
  ): Job {
    if (this.jobs.has(id)) {
      throw new Error(`Job with ID '${id}' already exists.`);
    }

    if (!cron.validate(schedule)) {
      throw new Error(`Invalid cron schedule: ${schedule}`);
    }

    const task = createTask(schedule, async () => {
      const job = this.jobs.get(id);
      if (job && job.isRunning) {
        logger.warning(
          `Job '${id}' is already running. Skipping this execution.`,
          {
            requestId: `job-skip-${id}`,
            timestamp: new Date().toISOString(),
          },
        );
        return;
      }

      if (job) {
        job.isRunning = true;
      }

      const context = requestContextService.createRequestContext({
        jobId: id,
        schedule,
      });

      logger.info(`Starting job '${id}'...`, context);
      try {
        await Promise.resolve(taskFunction(context));
        logger.info(`Job '${id}' completed successfully.`, context);
      } catch (error) {
        logger.error(`Job '${id}' failed.`, error as Error, context);
      } finally {
        if (job) {
          job.isRunning = false;
        }
      }
    });

    const newJob: Job = {
      id,
      schedule,
      description,
      task,
      isRunning: false,
    };

    this.jobs.set(id, newJob);
    logger.info(`Job '${id}' scheduled: ${description}`, {
      requestId: `job-schedule-${id}`,
      timestamp: new Date().toISOString(),
    });
    return newJob;
  }

  /**
   * Starts a scheduled job.
   * @param id - The ID of the job to start.
   */
  public start(id: string): void {
    const job = this.jobs.get(id);
    if (!job) {
      throw new Error(`Job with ID '${id}' not found.`);
    }
    job.task.start();
    logger.info(`Job '${id}' started.`, {
      requestId: `job-start-${id}`,
      timestamp: new Date().toISOString(),
    });
  }

  /**
   * Stops a scheduled job.
   * @param id - The ID of the job to stop.
   */
  public stop(id: string): void {
    const job = this.jobs.get(id);
    if (!job) {
      throw new Error(`Job with ID '${id}' not found.`);
    }
    job.task.stop();
    logger.info(`Job '${id}' stopped.`, {
      requestId: `job-stop-${id}`,
      timestamp: new Date().toISOString(),
    });
  }

  /**
   * Removes a job from the scheduler. The job is stopped before being removed.
   * @param id - The ID of the job to remove.
   */
  public remove(id: string): void {
    const job = this.jobs.get(id);
    if (!job) {
      throw new Error(`Job with ID '${id}' not found.`);
    }
    job.task.stop();
    this.jobs.delete(id);
    logger.info(`Job '${id}' removed.`, {
      requestId: `job-remove-${id}`,
      timestamp: new Date().toISOString(),
    });
  }

  /**
   * Gets a list of all scheduled jobs.
   * @returns An array of all Job objects.
   */
  public listJobs(): Job[] {
    return Array.from(this.jobs.values());
  }
}

/**
 * The singleton instance of the SchedulerService.
 * Use this instance for all job scheduling operations.
 */
export const schedulerService = SchedulerService.getInstance();

```

--------------------------------------------------------------------------------
/src/mcp-server/transports/auth/strategies/jwtStrategy.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Implements the JWT authentication strategy.
 * This module provides a concrete implementation of the AuthStrategy for validating
 * JSON Web Tokens (JWTs). It encapsulates all logic related to JWT verification,
 * including secret key management and payload validation.
 * @module src/mcp-server/transports/auth/strategies/JwtStrategy
 */
import { jwtVerify } from "jose";
import { config, environment } from "../../../../config/index.js";
import { BaseErrorCode, McpError } from "../../../../types-global/errors.js";
import {
  ErrorHandler,
  logger,
  requestContextService,
} from "../../../../utils/index.js";
import type { AuthInfo } from "../lib/authTypes.js";
import type { AuthStrategy } from "./authStrategy.js";

export class JwtStrategy implements AuthStrategy {
  private readonly secretKey: Uint8Array | null;

  constructor() {
    const context = requestContextService.createRequestContext({
      operation: "JwtStrategy.constructor",
    });
    logger.debug("Initializing JwtStrategy...", context);

    if (config.mcpAuthMode === "jwt") {
      if (environment === "production" && !config.mcpAuthSecretKey) {
        logger.fatal(
          "CRITICAL: MCP_AUTH_SECRET_KEY is not set in production for JWT auth.",
          context,
        );
        throw new McpError(
          BaseErrorCode.CONFIGURATION_ERROR,
          "MCP_AUTH_SECRET_KEY must be set for JWT auth in production.",
          context,
        );
      } else if (!config.mcpAuthSecretKey) {
        logger.warning(
          "MCP_AUTH_SECRET_KEY is not set. JWT auth will be bypassed (DEV ONLY).",
          context,
        );
        this.secretKey = null;
      } else {
        logger.info("JWT secret key loaded successfully.", context);
        this.secretKey = new TextEncoder().encode(config.mcpAuthSecretKey);
      }
    } else {
      this.secretKey = null;
    }
  }

  async verify(token: string): Promise<AuthInfo> {
    const context = requestContextService.createRequestContext({
      operation: "JwtStrategy.verify",
    });
    logger.debug("Attempting to verify JWT.", context);

    // Handle development mode bypass
    if (!this.secretKey) {
      if (environment !== "production") {
        logger.warning(
          "Bypassing JWT verification: No secret key (DEV ONLY).",
          context,
        );
        return {
          token: "dev-mode-placeholder-token",
          clientId: config.devMcpClientId || "dev-client-id",
          scopes: config.devMcpScopes || ["dev-scope"],
        };
      }
      // This path is defensive. The constructor should prevent this state in production.
      logger.crit("Auth secret key is missing in production.", context);
      throw new McpError(
        BaseErrorCode.CONFIGURATION_ERROR,
        "Auth secret key is missing in production. This indicates a server configuration error.",
        context,
      );
    }

    try {
      const { payload: decoded } = await jwtVerify(token, this.secretKey);
      logger.debug("JWT signature verified successfully.", {
        ...context,
        claims: decoded,
      });

      const clientId =
        typeof decoded.cid === "string"
          ? decoded.cid
          : typeof decoded.client_id === "string"
            ? decoded.client_id
            : undefined;

      if (!clientId) {
        logger.warning(
          "Invalid token: missing 'cid' or 'client_id' claim.",
          context,
        );
        throw new McpError(
          BaseErrorCode.UNAUTHORIZED,
          "Invalid token: missing 'cid' or 'client_id' claim.",
          context,
        );
      }

      let scopes: string[] = [];
      if (
        Array.isArray(decoded.scp) &&
        decoded.scp.every((s) => typeof s === "string")
      ) {
        scopes = decoded.scp as string[];
      } else if (typeof decoded.scope === "string" && decoded.scope.trim()) {
        scopes = decoded.scope.split(" ").filter(Boolean);
      }

      if (scopes.length === 0) {
        logger.warning(
          "Invalid token: missing or empty 'scp' or 'scope' claim.",
          context,
        );
        throw new McpError(
          BaseErrorCode.UNAUTHORIZED,
          "Token must contain valid, non-empty scopes.",
          context,
        );
      }

      const authInfo: AuthInfo = {
        token,
        clientId,
        scopes,
        subject: decoded.sub,
      };
      logger.info("JWT verification successful.", {
        ...context,
        clientId,
        scopes,
      });
      return authInfo;
    } catch (error) {
      // If the error is already a structured McpError, re-throw it directly.
      if (error instanceof McpError) {
        throw error;
      }

      const message =
        error instanceof Error && error.name === "JWTExpired"
          ? "Token has expired."
          : "Token verification failed.";

      logger.warning(`JWT verification failed: ${message}`, {
        ...context,
        errorName: error instanceof Error ? error.name : "Unknown",
      });

      throw ErrorHandler.handleError(error, {
        operation: "JwtStrategy.verify",
        context,
        rethrow: true,
        errorCode: BaseErrorCode.UNAUTHORIZED,
        errorMapper: () =>
          new McpError(BaseErrorCode.UNAUTHORIZED, message, context),
      });
    }
  }
}

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/pubmedArticleConnections/logic/citationFormatter.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Handles citation formatting for the pubmedArticleConnections tool.
 * Fetches article details using EFetch and formats them into various citation styles.
 * @module src/mcp-server/tools/pubmedArticleConnections/logic/citationFormatter
 */

import Cite from "citation-js";
import { getNcbiService } from "../../../../services/NCBI/core/ncbiService.js";
import type {
  XmlPubmedArticle,
  XmlPubmedArticleSet,
} from "../../../../types-global/pubmedXml.js";
import {
  logger,
  RequestContext,
  requestContextService,
} from "../../../../utils/index.js";
import {
  extractAuthors,
  extractDoi,
  extractJournalInfo,
  extractPmid,
  getText,
} from "../../../../services/NCBI/parsing/index.js";
import { ensureArray } from "../../../../services/NCBI/parsing/index.js";
import type { PubMedArticleConnectionsInput } from "./index.js";
import type { ToolOutputData } from "./types.js";

// Main handler for citation formats
export async function handleCitationFormats(
  input: PubMedArticleConnectionsInput,
  outputData: ToolOutputData,
  context: RequestContext,
): Promise<void> {
  const eFetchParams = {
    db: "pubmed",
    id: input.sourcePmid,
    retmode: "xml" as const,
  };

  const eFetchBaseUrl =
    "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi";
  const searchParamsString = new URLSearchParams(
    eFetchParams as Record<string, string>,
  ).toString();
  outputData.eUtilityUrl = `${eFetchBaseUrl}?${searchParamsString}`;

  const ncbiService = getNcbiService();
  const eFetchResult: { PubmedArticleSet?: XmlPubmedArticleSet } =
    (await ncbiService.eFetch(eFetchParams, context)) as {
      PubmedArticleSet?: XmlPubmedArticleSet;
    };

  const pubmedArticles = ensureArray<XmlPubmedArticle>(
    eFetchResult?.PubmedArticleSet?.PubmedArticle as
      | XmlPubmedArticle
      | XmlPubmedArticle[]
      | undefined,
  );

  if (pubmedArticles.length === 0) {
    outputData.message =
      "Could not retrieve article details for citation formatting.";
    logger.warning(outputData.message, context);
    return;
  }

  const article: XmlPubmedArticle = pubmedArticles[0]!;
  const csl = pubmedArticleToCsl(article, context);
  const cite = new Cite(csl);

  if (input.citationStyles?.includes("ris")) {
    outputData.citations.ris = cite.format("ris");
  }
  if (input.citationStyles?.includes("bibtex")) {
    outputData.citations.bibtex = cite.format("bibtex");
  }
  if (input.citationStyles?.includes("apa_string")) {
    outputData.citations.apa_string = cite.format("bibliography", {
      format: "text",
      template: "apa",
    });
  }
  if (input.citationStyles?.includes("mla_string")) {
    outputData.citations.mla_string = cite.format("bibliography", {
      format: "text",
      template: "mla",
    });
  }
  outputData.retrievedCount = 1;
}

/**
 * Converts an XML PubMed Article object to a CSL-JSON object.
 * @param article The PubMed article in XML format.
 * @param context The request context for logging.
 * @returns A CSL-JSON object compatible with citation-js.
 */
function pubmedArticleToCsl(
  article: XmlPubmedArticle,
  context: RequestContext,
): Record<string, unknown> {
  const medlineCitation = article.MedlineCitation;
  const articleDetails = medlineCitation?.Article;
  const pmid = extractPmid(medlineCitation);

  const cslContext = requestContextService.createRequestContext({
    ...context,
    pmid,
  });
  logger.debug("Converting PubMed XML to CSL-JSON", cslContext);

  if (!articleDetails) {
    logger.warning("Article details not found for CSL conversion", cslContext);
    return { id: pmid || "unknown", title: "Article details not found" };
  }

  const authors = extractAuthors(articleDetails.AuthorList);
  const journalInfo = extractJournalInfo(
    articleDetails.Journal,
    medlineCitation,
  );
  const title = getText(articleDetails.ArticleTitle);
  const doi = extractDoi(articleDetails);

  const cslAuthors = authors.map((author) =>
    author.collectiveName
      ? { literal: author.collectiveName }
      : { family: author.lastName, given: author.firstName },
  );

  const dateParts: (number | string)[] = [];
  if (journalInfo?.publicationDate?.year) {
    dateParts.push(parseInt(journalInfo.publicationDate.year, 10));
    if (journalInfo.publicationDate.month) {
      // Convert month name/number to number
      const monthNumber = new Date(
        `${journalInfo.publicationDate.month} 1, 2000`,
      ).getMonth();
      if (!isNaN(monthNumber)) {
        dateParts.push(monthNumber + 1);
        if (journalInfo.publicationDate.day) {
          dateParts.push(parseInt(journalInfo.publicationDate.day, 10));
        }
      }
    }
  }

  const cslData: Record<string, unknown> = {
    id: pmid,
    type: "article-journal",
    title: title,
    author: cslAuthors,
    issued: {
      "date-parts": [dateParts],
    },
    "container-title": journalInfo?.title,
    volume: journalInfo?.volume,
    issue: journalInfo?.issue,
    page: journalInfo?.pages,
    DOI: doi,
    PMID: pmid,
    URL: pmid ? `https://pubmed.ncbi.nlm.nih.gov/${pmid}` : undefined,
  };

  // Clean up any undefined/null properties
  for (const [key, value] of Object.entries(cslData)) {
    if (value === undefined || value === null) {
      delete (cslData as Record<string, unknown>)[key];
    }
  }

  return cslData;
}

```

--------------------------------------------------------------------------------
/src/mcp-server/transports/core/honoNodeBridge.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Provides a high-fidelity bridge between the MCP SDK's Node.js-style
 * streamable HTTP transport and Hono's Web Standards-based streaming response.
 * This class is essential for adapting the Node.js `http.ServerResponse` API
 * to a format consumable by modern web frameworks.
 * @module src/mcp-server/transports/core/honoNodeBridge
 */

import { PassThrough } from "stream";
import type { OutgoingHttpHeaders } from "http";

/**
 * A mock `http.ServerResponse` that pipes all written data to a `PassThrough` stream.
 *
 * This class serves as a critical compatibility layer, emulating the behavior of a
 * Node.js `ServerResponse` to capture status codes, headers, and the response body.
 * The captured data can then be used to construct a Web-standard `Response` object,
 * for instance in a Hono application. It pays close attention to the timing of when
 * headers are considered "sent" to mimic Node.js behavior accurately.
 */
export class HonoStreamResponse extends PassThrough {
  public statusCode = 200;
  public headers: OutgoingHttpHeaders = {};
  private _headersSent = false;

  constructor() {
    super();
  }

  /**
   * A getter that reports whether the headers have been sent.
   * In this emulation, headers are considered sent the first time `write()` or `end()` is called.
   */
  get headersSent(): boolean {
    return this._headersSent;
  }

  /**
   * Sets the status code and headers for the response, mimicking `http.ServerResponse.writeHead`.
   *
   * @param statusCode - The HTTP status code.
   * @param statusMessageOrHeaders - An optional status message (string) or headers object.
   * @param headers - An optional headers object, used if the second argument is a status message.
   * @returns The instance of the class for chaining.
   */
  writeHead(
    statusCode: number,
    statusMessageOrHeaders?: string | OutgoingHttpHeaders,
    headers?: OutgoingHttpHeaders,
  ): this {
    if (this._headersSent) {
      // Per Node.js spec, do nothing if headers are already sent.
      return this;
    }
    this.statusCode = statusCode;

    const headersArg =
      typeof statusMessageOrHeaders === "string"
        ? headers
        : statusMessageOrHeaders;

    if (headersArg) {
      for (const [key, value] of Object.entries(headersArg)) {
        if (value !== undefined) {
          this.setHeader(key, value);
        }
      }
    }
    return this;
  }

  /**
   * Sets a single header value.
   *
   * @param name - The name of the header.
   * @param value - The value of the header.
   * @returns The instance of the class for chaining.
   */
  setHeader(name: string, value: string | number | string[]): this {
    if (this._headersSent) {
      // This is a deviation from Node.js (which would throw), but provides a
      // more graceful warning for this emulation layer.
      console.warn(
        `[HonoBridge] Warning: Cannot set header "${name}" after headers are sent.`,
      );
      return this;
    }
    this.headers[name.toLowerCase()] = value;
    return this;
  }

  /**
   * Gets a header that has been queued for the response.
   * @param name - The name of the header.
   * @returns The value of the header, or undefined if not set.
   */
  getHeader(name: string): string | number | string[] | undefined {
    return this.headers[name.toLowerCase()];
  }

  /**
   * Returns a copy of the current outgoing headers.
   */
  getHeaders(): OutgoingHttpHeaders {
    return { ...this.headers };
  }

  /**
   * Removes a header that has been queued for the response.
   * @param name - The name of the header to remove.
   */
  removeHeader(name: string): void {
    if (this._headersSent) {
      console.warn(
        `[HonoBridge] Warning: Cannot remove header "${name}" after headers are sent.`,
      );
      return;
    }
    delete this.headers[name.toLowerCase()];
  }

  /**
   * A private helper to mark headers as sent. This is called implicitly
   * before any part of the body is written.
   */
  private ensureHeadersSent(): void {
    if (!this._headersSent) {
      this._headersSent = true;
    }
  }

  /**
   * Writes a chunk of the response body, mimicking `http.ServerResponse.write`.
   * This is the first point where headers are implicitly flushed.
   */
  write(
    chunk: unknown,
    encodingOrCallback?:
      | BufferEncoding
      | ((error: Error | null | undefined) => void),
    callback?: (error: Error | null | undefined) => void,
  ): boolean {
    this.ensureHeadersSent();

    const encoding =
      typeof encodingOrCallback === "string" ? encodingOrCallback : undefined;
    const cb =
      typeof encodingOrCallback === "function" ? encodingOrCallback : callback;

    if (encoding) {
      return super.write(chunk, encoding, cb);
    }
    return super.write(chunk, cb);
  }

  /**
   * Finishes sending the response, mimicking `http.ServerResponse.end`.
   * This also implicitly flushes headers if they haven't been sent yet.
   */
  end(
    chunk?: unknown,
    encodingOrCallback?: BufferEncoding | (() => void),
    callback?: () => void,
  ): this {
    this.ensureHeadersSent();

    const encoding =
      typeof encodingOrCallback === "string" ? encodingOrCallback : undefined;
    const cb =
      typeof encodingOrCallback === "function" ? encodingOrCallback : callback;

    if (encoding) {
      super.end(chunk, encoding, cb);
    } else {
      super.end(chunk, cb);
    }
    return this;
  }
}

```

--------------------------------------------------------------------------------
/src/utils/parsing/jsonParser.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Provides a utility class for parsing potentially partial JSON strings.
 * It wraps the 'partial-json' npm library and includes functionality to handle
 * optional <think>...</think> blocks often found at the beginning of LLM outputs.
 * @module src/utils/parsing/jsonParser
 */
import {
  parse as parsePartialJson,
  Allow as PartialJsonAllow,
} from "partial-json";
import { BaseErrorCode, McpError } from "../../types-global/errors.js";
import { logger, RequestContext, requestContextService } from "../index.js";

/**
 * Enum mirroring `partial-json`'s `Allow` constants. These specify
 * what types of partial JSON structures are permissible during parsing.
 * They can be combined using bitwise OR (e.g., `Allow.STR | Allow.OBJ`).
 *
 * The available properties are:
 * - `STR`: Allow partial string.
 * - `NUM`: Allow partial number.
 * - `ARR`: Allow partial array.
 * - `OBJ`: Allow partial object.
 * - `NULL`: Allow partial null.
 * - `BOOL`: Allow partial boolean.
 * - `NAN`: Allow partial NaN. (Note: Standard JSON does not support NaN)
 * - `INFINITY`: Allow partial Infinity. (Note: Standard JSON does not support Infinity)
 * - `_INFINITY`: Allow partial -Infinity. (Note: Standard JSON does not support -Infinity)
 * - `INF`: Allow both partial Infinity and -Infinity.
 * - `SPECIAL`: Allow all special values (NaN, Infinity, -Infinity).
 * - `ATOM`: Allow all atomic values (strings, numbers, booleans, null, special values).
 * - `COLLECTION`: Allow all collection values (objects, arrays).
 * - `ALL`: Allow all value types to be partial (default for `partial-json`'s parse).
 * @see {@link https://github.com/promplate/partial-json-parser-js} for more details.
 */
export const Allow = PartialJsonAllow;

/**
 * Regular expression to find a <think> block at the start of a string.
 * Captures content within <think>...</think> (Group 1) and the rest of the string (Group 2).
 * @private
 */
const thinkBlockRegex = /^<think>([\s\S]*?)<\/think>\s*([\s\S]*)$/;

/**
 * Utility class for parsing potentially partial JSON strings.
 * Wraps the 'partial-json' library for robust JSON parsing, handling
 * incomplete structures and optional <think> blocks from LLMs.
 */
export class JsonParser {
  /**
   * Parses a JSON string, which may be partial or prefixed with a <think> block.
   * If a <think> block is present, its content is logged, and parsing proceeds on the
   * remainder. Uses 'partial-json' to handle incomplete JSON.
   *
   * @template T The expected type of the parsed JSON object. Defaults to `any`.
   * @param jsonString - The JSON string to parse.
   * @param allowPartial - Bitwise OR combination of `Allow` constants specifying permissible
   *   partial JSON types. Defaults to `Allow.ALL`.
   * @param context - Optional `RequestContext` for logging and error correlation.
   * @returns The parsed JavaScript value.
   * @throws {McpError} If the string is empty after processing or if `partial-json` fails.
   */
  parse<T = unknown>(
    jsonString: string,
    allowPartial: number = Allow.ALL,
    context?: RequestContext,
  ): T {
    let stringToParse = jsonString;
    const match = jsonString.match(thinkBlockRegex);

    if (match) {
      const thinkContent = match[1]?.trim() ?? "";
      const restOfString = match[2] ?? "";

      const logContext =
        context ||
        requestContextService.createRequestContext({
          operation: "JsonParser.thinkBlock",
        });
      if (thinkContent) {
        logger.debug("LLM <think> block detected and logged.", {
          ...logContext,
          thinkContent,
        });
      } else {
        logger.debug("Empty LLM <think> block detected.", logContext);
      }
      stringToParse = restOfString;
    }

    stringToParse = stringToParse.trim();

    if (!stringToParse) {
      throw new McpError(
        BaseErrorCode.VALIDATION_ERROR,
        "JSON string is empty after removing <think> block and trimming.",
        context,
      );
    }

    try {
      return parsePartialJson(stringToParse, allowPartial) as T;
    } catch (e: unknown) {
      const error = e as Error;
      const errorLogContext =
        context ||
        requestContextService.createRequestContext({
          operation: "JsonParser.parseError",
        });
      logger.error("Failed to parse JSON content.", {
        ...errorLogContext,
        errorDetails: error.message,
        contentAttempted: stringToParse.substring(0, 200),
      });

      throw new McpError(
        BaseErrorCode.VALIDATION_ERROR,
        `Failed to parse JSON: ${error.message}`,
        {
          ...context,
          originalContentSample:
            stringToParse.substring(0, 200) +
            (stringToParse.length > 200 ? "..." : ""),
          rawError: error instanceof Error ? error.stack : String(error),
        },
      );
    }
  }
}

/**
 * Singleton instance of the `JsonParser`.
 * Use this instance to parse JSON strings, with support for partial JSON and <think> blocks.
 * @example
 * ```typescript
 * import { jsonParser, Allow, requestContextService } from './utils';
 * const context = requestContextService.createRequestContext({ operation: 'TestJsonParsing' });
 *
 * const fullJson = '{"key": "value"}';
 * const parsedFull = jsonParser.parse(fullJson, Allow.ALL, context);
 * console.log(parsedFull); // Output: { key: 'value' }
 *
 * const partialObject = '<think>This is a thought.</think>{"key": "value", "arr": [1,';
 * try {
 *   const parsedPartial = jsonParser.parse(partialObject, undefined, context);
 *   console.log(parsedPartial);
 * } catch (e) {
 *   console.error("Parsing partial object failed:", e);
 * }
 * ```
 */
export const jsonParser = new JsonParser();

```

--------------------------------------------------------------------------------
/src/services/NCBI/core/ncbiCoreApiClient.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Core client for making HTTP requests to NCBI E-utilities.
 * Handles request construction, API key injection, retries, and basic error handling.
 * @module src/services/NCBI/core/ncbiCoreApiClient
 */

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
import { config } from "../../../config/index.js";
import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
import {
  logger,
  RequestContext,
  requestContextService,
  sanitizeInputForLogging,
} from "../../../utils/index.js";
import {
  NCBI_EUTILS_BASE_URL,
  NcbiRequestParams,
  NcbiRequestOptions,
} from "./ncbiConstants.js";

export class NcbiCoreApiClient {
  private axiosInstance: AxiosInstance;

  constructor() {
    this.axiosInstance = axios.create({
      timeout: 30000, // 30 seconds timeout for NCBI requests
    });
  }

  /**
   * Makes an HTTP request to the specified NCBI E-utility endpoint.
   * Handles parameter assembly, API key injection, GET/POST selection, and retries.
   * @param endpoint The E-utility endpoint (e.g., "esearch", "efetch").
   * @param params The parameters for the E-utility.
   * @param context The request context for logging.
   * @param options Options for the request, like retmode and whether to use POST.
   * @param retries The current retry attempt number.
   * @returns A Promise resolving to the raw AxiosResponse.
   * @throws {McpError} If the request fails after all retries or an unexpected error occurs.
   */
  public async makeRequest(
    endpoint: string,
    params: NcbiRequestParams,
    context: RequestContext,
    options: NcbiRequestOptions = {},
  ): Promise<AxiosResponse> {
    const rawParams: Record<string, string | number | undefined> = {
      tool: config.ncbiToolIdentifier,
      email: config.ncbiAdminEmail,
      api_key: config.ncbiApiKey,
      ...params,
    };

    const finalParams: Record<string, string> = {};
    for (const key in rawParams) {
      if (Object.prototype.hasOwnProperty.call(rawParams, key)) {
        const value = rawParams[key];
        if (value !== undefined && value !== null) {
          finalParams[key] = String(value);
        }
      }
    }

    const requestConfig: AxiosRequestConfig = {
      method: options.usePost ? "POST" : "GET",
      url: `${NCBI_EUTILS_BASE_URL}/${endpoint}.fcgi`,
    };

    if (options.usePost) {
      requestConfig.data = new URLSearchParams(finalParams).toString();
      requestConfig.headers = {
        "Content-Type": "application/x-www-form-urlencoded",
      };
    } else {
      requestConfig.params = finalParams;
    }

    for (let attempt = 0; attempt <= config.ncbiMaxRetries; attempt++) {
      try {
        logger.debug(
          `Making NCBI HTTP request: ${requestConfig.method} ${requestConfig.url}`,
          requestContextService.createRequestContext({
            ...context,
            operation: "NCBI_HttpRequest",
            endpoint,
            method: requestConfig.method,
            requestParams: sanitizeInputForLogging(finalParams),
            attempt: attempt + 1,
          }),
        );
        const response: AxiosResponse = await this.axiosInstance(requestConfig);
        return response;
      } catch (error: unknown) {
        const err = error as Error;
        if (attempt < config.ncbiMaxRetries) {
          const retryDelay = Math.pow(2, attempt) * 200;
          logger.warning(
            `NCBI request to ${endpoint} failed. Retrying (${attempt + 1}/${config.ncbiMaxRetries}) in ${retryDelay}ms...`,
            requestContextService.createRequestContext({
              ...context,
              operation: "NCBI_HttpRequestRetry",
              endpoint,
              error: err.message,
              retryCount: attempt + 1,
              maxRetries: config.ncbiMaxRetries,
              delay: retryDelay,
            }),
          );
          await new Promise((r) => setTimeout(r, retryDelay));
          continue; // Continue to the next iteration of the loop
        }

        // If all retries are exhausted, handle the final error
        if (axios.isAxiosError(error)) {
          logger.error(
            `Axios error during NCBI request to ${endpoint} after ${attempt} retries`,
            error,
            requestContextService.createRequestContext({
              ...context,
              operation: "NCBI_AxiosError",
              endpoint,
              status: error.response?.status,
              responseData: sanitizeInputForLogging(error.response?.data),
            }),
          );
          throw new McpError(
            BaseErrorCode.NCBI_SERVICE_UNAVAILABLE,
            `NCBI request failed: ${error.message}`,
            {
              endpoint,
              status: error.response?.status,
              details: error.response?.data
                ? String(error.response.data).substring(0, 500)
                : undefined,
            },
          );
        }
        if (error instanceof McpError) throw error;

        logger.error(
          `Unexpected error during NCBI request to ${endpoint} after ${attempt} retries`,
          err,
          requestContextService.createRequestContext({
            ...context,
            operation: "NCBI_UnexpectedError",
            endpoint,
            errorMessage: err.message,
          }),
        );
        throw new McpError(
          BaseErrorCode.INTERNAL_ERROR,
          `Unexpected error communicating with NCBI: ${err.message}`,
          { endpoint },
        );
      }
    }
    // This line should theoretically be unreachable, but it satisfies TypeScript's need
    // for a return path if the loop completes without returning or throwing.
    throw new McpError(
      BaseErrorCode.INTERNAL_ERROR,
      "Request failed after all retries.",
      { endpoint },
    );
  }
}

```

--------------------------------------------------------------------------------
/src/mcp-server/server.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Main entry point for the MCP (Model Context Protocol) server.
 * This file orchestrates the server's lifecycle:
 * 1. Initializes the core `McpServer` instance (from `@modelcontextprotocol/sdk`) with its identity and capabilities.
 * 2. Registers available resources and tools, making them discoverable and usable by clients.
 * 3. Selects and starts the appropriate communication transport (stdio or Streamable HTTP)
 *    based on configuration.
 * 4. Handles top-level error management during startup.
 *
 * @module src/mcp-server/server
 */

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import http from "http";
import { config, environment } from "../config/index.js";
import { ErrorHandler, logger, requestContextService } from "../utils/index.js";
import { registerPubMedArticleConnectionsTool } from "./tools/pubmedArticleConnections/index.js";
import { registerPubMedFetchContentsTool } from "./tools/pubmedFetchContents/index.js";
import { registerPubMedGenerateChartTool } from "./tools/pubmedGenerateChart/index.js";
import { registerPubMedResearchAgentTool } from "./tools/pubmedResearchAgent/index.js";
import { registerPubMedSearchArticlesTool } from "./tools/pubmedSearchArticles/index.js";
import { startHttpTransport } from "./transports/http/index.js";
import { startStdioTransport } from "./transports/stdio/index.js";

type SdkToolSpec = Parameters<McpServer["registerTool"]>[1];
type ServerIdentity = ConstructorParameters<typeof McpServer>[0];
type McpServerOptions = NonNullable<ConstructorParameters<typeof McpServer>[1]>;

export interface DescribedTool extends SdkToolSpec {
  title: string;
}

export interface ServerInstanceInfo {
  server: McpServer;
  tools: DescribedTool[];
  identity: ServerIdentity;
  options: McpServerOptions;
}

/**
 * Creates and configures a new instance of the `McpServer`.
 *
 * @returns A promise resolving with the configured `McpServer` instance and its tool metadata.
 * @throws {McpError} If any resource or tool registration fails.
 * @private
 */
async function createMcpServerInstance(): Promise<ServerInstanceInfo> {
  const context = requestContextService.createRequestContext({
    operation: "createMcpServerInstance",
  });
  logger.info("Initializing MCP server instance", context);

  requestContextService.configure({
    appName: config.mcpServerName,
    appVersion: config.mcpServerVersion,
    environment,
  });

  const identity: ServerIdentity = {
    name: config.mcpServerName,
    version: config.mcpServerVersion,
    description: config.mcpServerDescription,
  };

  const options: McpServerOptions = {
    capabilities: {
      logging: {},
      resources: { listChanged: true },
      tools: { listChanged: true },
    },
  };

  const server = new McpServer(identity, options);

  const registeredTools: DescribedTool[] = [];
  const originalRegisterTool = server.registerTool.bind(server);
  server.registerTool = (name, spec, implementation) => {
    registeredTools.push({
      title: name,
      description: spec.description,
      inputSchema: spec.inputSchema,
      outputSchema: spec.outputSchema,
      annotations: spec.annotations,
    });
    return originalRegisterTool(name, spec, implementation);
  };

  try {
    logger.debug("Registering resources and tools...", context);
    // IMPORTANT: Keep tool registrations in alphabetical order.
    await registerPubMedArticleConnectionsTool(server);
    await registerPubMedFetchContentsTool(server);
    await registerPubMedGenerateChartTool(server);
    await registerPubMedResearchAgentTool(server);
    await registerPubMedSearchArticlesTool(server);
    logger.info("Resources and tools registered successfully", context);
  } catch (err) {
    logger.error("Failed to register resources/tools", {
      ...context,
      error: err instanceof Error ? err.message : String(err),
      stack: err instanceof Error ? err.stack : undefined,
    });
    throw err;
  }

  return { server, tools: registeredTools, identity, options };
}

/**
 * Selects, sets up, and starts the appropriate MCP transport layer based on configuration.
 *
 * @returns Resolves with `McpServer` for 'stdio' or `http.Server` for 'http'.
 * @throws {Error} If transport type is unsupported or setup fails.
 * @private
 */
async function startTransport(): Promise<McpServer | http.Server> {
  const transportType = config.mcpTransportType;
  const context = requestContextService.createRequestContext({
    operation: "startTransport",
    transport: transportType,
  });
  logger.info(`Starting transport: ${transportType}`, context);

  if (transportType === "http") {
    const { server } = await startHttpTransport(
      createMcpServerInstance,
      context,
    );
    return server as http.Server;
  }

  if (transportType === "stdio") {
    const { server } = await createMcpServerInstance();
    await startStdioTransport(server, context);
    return server;
  }

  logger.fatal(
    `Unsupported transport type configured: ${transportType}`,
    context,
  );
  throw new Error(
    `Unsupported transport type: ${transportType}. Must be 'stdio' or 'http'.`,
  );
}

/**
 * Main application entry point. Initializes and starts the MCP server.
 */
export async function initializeAndStartServer(): Promise<
  McpServer | http.Server
> {
  const context = requestContextService.createRequestContext({
    operation: "initializeAndStartServer",
  });
  logger.info("MCP Server initialization sequence started.", context);
  try {
    const result = await startTransport();
    logger.info(
      "MCP Server initialization sequence completed successfully.",
      context,
    );
    return result;
  } catch (err) {
    logger.fatal("Critical error during MCP server initialization.", {
      ...context,
      error: err instanceof Error ? err.message : String(err),
      stack: err instanceof Error ? err.stack : undefined,
    });
    ErrorHandler.handleError(err, {
      ...context,
      operation: "initializeAndStartServer_Catch",
      critical: true,
    });
    logger.info(
      "Exiting process due to critical initialization error.",
      context,
    );
    process.exit(1);
  }
}

```

--------------------------------------------------------------------------------
/src/types-global/errors.ts:
--------------------------------------------------------------------------------

```typescript
/**
 * @fileoverview Defines standardized error codes, a custom error class, and related schemas
 * for handling errors within the Model Context Protocol (MCP) server and its components.
 * This module provides a structured way to represent and communicate errors, ensuring
 * consistency and clarity for both server-side operations and client-side error handling.
 * @module src/types-global/errors
 */

import { z } from "zod";

/**
 * Defines a comprehensive set of standardized error codes for common issues encountered
 * within MCP servers, tools, or related operations. These codes are designed to help
 * clients and developers programmatically understand the nature of an error, facilitating
 * more precise error handling and debugging.
 */
export enum BaseErrorCode {
  /** Access denied due to invalid credentials or lack of authentication. */
  UNAUTHORIZED = "UNAUTHORIZED",
  /** Access denied despite valid authentication, due to insufficient permissions. */
  FORBIDDEN = "FORBIDDEN",
  /** The requested resource or entity could not be found. */
  NOT_FOUND = "NOT_FOUND",
  /** The request could not be completed due to a conflict with the current state of the resource. */
  CONFLICT = "CONFLICT",
  /** The request failed due to invalid input parameters or data. */
  VALIDATION_ERROR = "VALIDATION_ERROR",
  /** The provided input is invalid, but not necessarily a schema validation failure. */
  INVALID_INPUT = "INVALID_INPUT",
  /** An error occurred while parsing input data (e.g., date string, JSON). */
  PARSING_ERROR = "PARSING_ERROR",
  /** The request was rejected because the client has exceeded rate limits. */
  RATE_LIMITED = "RATE_LIMITED",
  /** The request timed out before a response could be generated. */
  TIMEOUT = "TIMEOUT",
  /** The service is temporarily unavailable, possibly due to maintenance or overload. */
  SERVICE_UNAVAILABLE = "SERVICE_UNAVAILABLE",
  /** An unexpected error occurred on the server side. */
  INTERNAL_ERROR = "INTERNAL_ERROR",
  /** An error occurred, but the specific cause is unknown or cannot be categorized. */
  UNKNOWN_ERROR = "UNKNOWN_ERROR",
  /** An error occurred during the loading or validation of configuration data. */
  CONFIGURATION_ERROR = "CONFIGURATION_ERROR",
  /** An error occurred during the initialization phase of a service or module. */
  INITIALIZATION_FAILED = "INITIALIZATION_FAILED",

  // NCBI Specific Errors
  /** An error was returned by the NCBI E-utilities API. */
  NCBI_API_ERROR = "NCBI_API_ERROR",
  /** An error occurred while parsing a response from NCBI (e.g., XML, JSON). */
  NCBI_PARSING_ERROR = "NCBI_PARSING_ERROR",
  /** A warning or notice related to NCBI rate limits. */
  NCBI_RATE_LIMIT_WARNING = "NCBI_RATE_LIMIT_WARNING",
  /** An error related to the construction or validity of an NCBI E-utility query. */
  NCBI_QUERY_ERROR = "NCBI_QUERY_ERROR",
  /** NCBI service temporarily unavailable or returned a server-side error. */
  NCBI_SERVICE_UNAVAILABLE = "NCBI_SERVICE_UNAVAILABLE",
}

/**
 * Custom error class for MCP-specific errors, extending the built-in `Error` class.
 * It standardizes error reporting by encapsulating a `BaseErrorCode`, a descriptive
 * human-readable message, and optional structured details for more context.
 *
 * This class is central to error handling within the MCP framework, allowing for
 * consistent error creation and propagation.
 */
export class McpError extends Error {
  /**
   * The standardized error code from {@link BaseErrorCode}.
   */
  public readonly code: BaseErrorCode;

  /**
   * Optional additional details or context about the error.
   * This can be any structured data that helps in understanding or debugging the error.
   */
  public readonly details?: Record<string, unknown>;

  /**
   * Creates an instance of McpError.
   *
   * @param code - The standardized error code that categorizes the error.
   * @param message - A human-readable description of the error.
   * @param details - Optional. A record containing additional structured details about the error.
   */
  constructor(
    code: BaseErrorCode,
    message: string,
    details?: Record<string, unknown>,
  ) {
    super(message);

    this.code = code;
    this.details = details;
    this.name = "McpError";

    // Maintain a proper prototype chain.
    Object.setPrototypeOf(this, McpError.prototype);

    // Capture the stack trace, excluding the constructor call from it, if available.
    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, McpError);
    }
  }
}

/**
 * Zod schema for validating error objects. This schema can be used for:
 * - Validating error structures when parsing error responses from external services.
 * - Ensuring consistency when creating or handling error objects internally.
 * - Generating TypeScript types for error objects.
 *
 * The schema enforces the presence of a `code` (from {@link BaseErrorCode}) and a `message`,
 * and allows for optional `details`.
 */
export const ErrorSchema = z
  .object({
    /**
     * The error code, corresponding to one of the {@link BaseErrorCode} enum values.
     * This field is required and helps in programmatically identifying the error type.
     */
    code: z
      .nativeEnum(BaseErrorCode)
      .describe("Standardized error code from BaseErrorCode enum"),
    /**
     * A human-readable, descriptive message explaining the error.
     * This field is required and provides context to developers or users.
     */
    message: z
      .string()
      .min(1, "Error message cannot be empty.")
      .describe("Detailed human-readable error message"),
    /**
     * Optional. A record containing additional structured details or context about the error.
     * This can include things like invalid field names, specific values that caused issues, or other relevant data.
     */
    details: z
      .record(z.unknown())
      .optional()
      .describe(
        "Optional structured details providing more context about the error",
      ),
  })
  .describe(
    "Schema for validating structured error objects, ensuring consistency in error reporting.",
  );

/**
 * TypeScript type inferred from the {@link ErrorSchema}.
 * This type represents the structure of a validated error object, commonly used
 * for error responses or when passing error information within the application.
 */
export type ErrorResponse = z.infer<typeof ErrorSchema>;

```
Page 1/4FirstPrevNextLast