#
tokens: 47221/50000 61/67 files (page 1/2)
lines: off (toggle) GitHub
raw markdown copy
This is page 1 of 2. Use http://codebase.md/cyanheads/filesystem-mcp-server?lines=false&page={x} to view the full context.

# Directory Structure

```
├── .clinerules
├── .dockerignore
├── .github
│   └── workflows
│       └── publish.yml
├── .gitignore
├── CHANGELOG.md
├── Dockerfile
├── docs
│   └── tree.md
├── LICENSE
├── mcp.json
├── package-lock.json
├── package.json
├── README.md
├── repomix.config.json
├── scripts
│   ├── clean.ts
│   └── tree.ts
├── smithery.yaml
├── src
│   ├── config
│   │   └── index.ts
│   ├── index.ts
│   ├── mcp-server
│   │   ├── server.ts
│   │   ├── state.ts
│   │   ├── tools
│   │   │   ├── copyPath
│   │   │   │   ├── copyPathLogic.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── registration.ts
│   │   │   ├── createDirectory
│   │   │   │   ├── createDirectoryLogic.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── registration.ts
│   │   │   ├── deleteDirectory
│   │   │   │   ├── deleteDirectoryLogic.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── registration.ts
│   │   │   ├── deleteFile
│   │   │   │   ├── deleteFileLogic.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── registration.ts
│   │   │   ├── listFiles
│   │   │   │   ├── index.ts
│   │   │   │   ├── listFilesLogic.ts
│   │   │   │   └── registration.ts
│   │   │   ├── movePath
│   │   │   │   ├── index.ts
│   │   │   │   ├── movePathLogic.ts
│   │   │   │   └── registration.ts
│   │   │   ├── readFile
│   │   │   │   ├── index.ts
│   │   │   │   ├── readFileLogic.ts
│   │   │   │   └── registration.ts
│   │   │   ├── setFilesystemDefault
│   │   │   │   ├── index.ts
│   │   │   │   ├── registration.ts
│   │   │   │   └── setFilesystemDefaultLogic.ts
│   │   │   ├── updateFile
│   │   │   │   ├── index.ts
│   │   │   │   ├── registration.ts
│   │   │   │   └── updateFileLogic.ts
│   │   │   └── writeFile
│   │   │       ├── index.ts
│   │   │       ├── registration.ts
│   │   │       └── writeFileLogic.ts
│   │   └── transports
│   │       ├── authentication
│   │       │   └── authMiddleware.ts
│   │       ├── httpTransport.ts
│   │       └── stdioTransport.ts
│   ├── types-global
│   │   ├── errors.ts
│   │   ├── mcp.ts
│   │   └── tool.ts
│   └── utils
│       ├── index.ts
│       ├── internal
│       │   ├── errorHandler.ts
│       │   ├── index.ts
│       │   ├── logger.ts
│       │   └── requestContext.ts
│       ├── metrics
│       │   ├── index.ts
│       │   └── tokenCounter.ts
│       ├── parsing
│       │   ├── dateParser.ts
│       │   ├── index.ts
│       │   └── jsonParser.ts
│       └── security
│           ├── idGenerator.ts
│           ├── index.ts
│           ├── rateLimiter.ts
│           └── sanitization.ts
└── tsconfig.json
```

# Files

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

```
# Git files
.git
.gitignore

# Node modules (installed within Docker)
node_modules

# Build output (created within Docker)
dist

# Logs
logs
*.log

# OS generated files
.DS_Store
Thumbs.db

# Environment files (should be injected, not built-in)
.env*
!/.env.example

# Editor/IDE config
.vscode/
.idea/

# Backups
backups/

```

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

```
# Operating System Files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

# IDE and Editor Files
.idea/
.vscode/
*.swp
*.swo
*~
*.sublime-workspace
*.sublime-project

# TypeScript
*.tsbuildinfo
.tscache/
*.js.map
*.tgz
.npm
.eslintcache
.rollup.cache
*.mjs.map
*.cjs.map
*.d.ts.map
*.d.ts
!*.d.ts.template
.pnp.js
.pnp.cjs
.pnp.mjs
.pnp.json
.pnp.ts

# Demo and Example Directories
demo/
demos/
example/
examples/
samples/
.sample-env
sample.*
!sample.template.*

# Node.js
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
.env
.env.local
.env.development.local
.env.test.local
.env.production.local

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

# Java
*.class
*.log
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
hs_err_pid*
target/
.gradle/
build/

# Ruby
*.gem
*.rbc
/.config
/coverage/
/InstalledFiles
/pkg/
/spec/reports/
/spec/examples.txt
/test/tmp/
/test/version_tmp/
/tmp/
.byebug_history

# Compiled Files
*.com
*.class
*.dll
*.exe
*.o
*.so

# Package Files
*.7z
*.dmg
*.gz
*.iso
*.rar
*.tar
*.zip

# Logs and Databases
*.log
*.sql
*.sqlite
*.sqlite3

# Build and Distribution
dist/
build/
out/

# Testing
coverage/
.nyc_output/

# Cache
.cache/
.parcel-cache/

# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
*.bak
*.swp
*.swo
*~
.history/
repomix-output*
mcp-servers.json
logs/

```

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

```markdown
# Filesystem MCP Server

[![TypeScript](https://img.shields.io/badge/TypeScript-^5.8.3-blue.svg)](https://www.typescriptlang.org/)
[![Model Context Protocol](https://img.shields.io/badge/MCP-^1.12.0-green.svg)](https://modelcontextprotocol.io/)
[![Version](https://img.shields.io/badge/Version-1.0.4-blue.svg)]()
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![Status](https://img.shields.io/badge/Status-Beta-orange.svg)]()
[![GitHub](https://img.shields.io/github/stars/cyanheads/filesystem-mcp-server?style=social)](https://github.com/cyanheads/filesystem-mcp-server)

**Empower your AI agents with robust, platform-agnostic file system capabilities, now with STDIO & Streamable HTTP transport options.**

This [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server provides a secure and reliable interface for AI agents to interact with the local filesystem. It enables reading, writing, updating, and managing files and directories, backed by a production-ready TypeScript foundation featuring comprehensive logging, error handling, security measures, and now supporting both **STDIO and HTTP transports**.

## Table of Contents

- [Overview](#overview)
- [Features](#features)
- [Installation](#installation)
- [Configuration](#configuration)
- [Usage with MCP Clients](#usage-with-mcp-clients)
- [Available Tools](#available-tools)
- [Project Structure](#project-structure)
- [Development](#development)
- [License](#license)

## Overview

The Model Context Protocol (MCP) is a standard framework allowing AI models to securely interact with external tools and data sources (resources). This server implements the MCP standard to expose essential filesystem operations as tools, enabling AI agents to:

- Read and analyze file contents.
- Create, modify, or overwrite files.
- Manage directories and file paths.
- Perform targeted updates within files.

Built with TypeScript, the server emphasizes type safety, modularity, and robust error handling, making it suitable for reliable integration into AI workflows. It now supports both STDIO for direct process communication and HTTP for network-based interactions.

### Architecture

The server employs a layered architecture for clarity and maintainability:

```mermaid
flowchart TB
    subgraph TransportLayer["Transport Layer"]
        direction LR
        STDIO["STDIO Transport"]
        HTTP["HTTP Transport (Express, JWT Auth)"]
    end

    subgraph APILayer["API Layer"]
        direction LR
        MCP["MCP Protocol Interface"]
        Val["Input Validation (Zod)"]
        PathSan["Path Sanitization"]

        MCP --> Val --> PathSan
    end

    subgraph CoreServices["Core Services"]
        direction LR
        Config["Configuration (Zod-validated Env Vars)"]
        Logger["Logging (Winston, Context-aware)"]
        ErrorH["Error Handling (McpError, ErrorHandler)"]
        ServerLogic["MCP Server Logic"]
        State["Session State (Default Path)"]

        Config --> ServerLogic
        Logger --> ServerLogic & ErrorH
        ErrorH --> ServerLogic
        State --> ServerLogic
    end

    subgraph ToolImpl["Tool Implementation"]
        direction LR
        FSTools["Filesystem Tools"]
        Utils["Core Utilities (Internal, Security, Metrics, Parsing)"]

        FSTools --> ServerLogic
        Utils -- Used by --> FSTools
        Utils -- Used by --> CoreServices
        Utils -- Used by --> APILayer
    end

    TransportLayer --> MCP
    PathSan --> FSTools

    classDef layer fill:#2d3748,stroke:#4299e1,stroke-width:3px,rx:5,color:#fff
    classDef component fill:#1a202c,stroke:#a0aec0,stroke-width:2px,rx:3,color:#fff
    class TransportLayer,APILayer,CoreServices,ToolImpl layer
    class STDIO,HTTP,MCP,Val,PathSan,Config,Logger,ErrorH,ServerLogic,State,FSTools,Utils component
```

- **Transport Layer**: Handles communication via STDIO or HTTP (with Express.js and JWT authentication).
- **API Layer**: Manages MCP communication, validates inputs using Zod, and sanitizes paths.
- **Core Services**: Oversees configuration (Zod-validated environment variables), context-aware logging, standardized error reporting, session state (like the default working directory), and the main MCP server instance.
- **Tool Implementation**: Contains the specific logic for each filesystem tool, leveraging a refactored set of shared utilities categorized into internal, security, metrics, and parsing modules.

## Features

- **Comprehensive File Operations**: Tools for reading, writing, listing, deleting, moving, and copying files and directories.
- **Targeted Updates**: `update_file` tool allows precise search-and-replace operations within files, supporting plain text and regex.
- **Session-Aware Path Management**: `set_filesystem_default` tool establishes a default working directory for resolving relative paths during a session.
- **Dual Transport Support**:
  - **STDIO**: For direct, efficient communication when run as a child process.
  - **HTTP**: For network-based interaction, featuring RESTful endpoints, Server-Sent Events (SSE) for streaming, and JWT-based authentication.
- **Security First**:
  - Built-in path sanitization prevents directory traversal attacks.
  - JWT authentication for HTTP transport.
  - Input validation with Zod.
- **Robust Foundation**: Includes production-grade utilities, now reorganized for better modularity:
  - **Internal Utilities**: Context-aware logging (Winston), standardized error handling (`McpError`, `ErrorHandler`), request context management.
  - **Security Utilities**: Input sanitization, rate limiting, UUID and prefixed ID generation.
  - **Metrics Utilities**: Token counting.
  - **Parsing Utilities**: Natural language date parsing, partial JSON parsing.
- **Enhanced Configuration**: Zod-validated environment variables for type-safe and reliable setup.
- **Type Safety**: Fully implemented in TypeScript for improved reliability and maintainability.

## Installation

### Steps

1.  **Clone the repository:**
    ```bash
    git clone https://github.com/cyanheads/filesystem-mcp-server.git
    cd filesystem-mcp-server
    ```
2.  **Install dependencies:**
    ```bash
    npm install
    ```
3.  **Build the project:**
    ```bash
    npm run build
    ```
    This compiles the TypeScript code to JavaScript in the `dist/` directory and makes the main script executable. The executable will be located at `dist/index.js`.

## Configuration

Configure the server using environment variables (a `.env` file is supported):

**Core Server Settings:**

- **`MCP_LOG_LEVEL`** (Optional): Minimum logging level (e.g., `debug`, `info`, `warn`, `error`). Defaults to `debug`.
- **`LOGS_DIR`** (Optional): Directory for log files. Defaults to `./logs` in the project root.
- **`NODE_ENV`** (Optional): Runtime environment (e.g., `development`, `production`). Defaults to `development`.

**Transport Settings:**

- **`MCP_TRANSPORT_TYPE`** (Optional): Communication transport (`stdio` or `http`). Defaults to `stdio`.
  - **If `http` is selected:**
    - **`MCP_HTTP_PORT`** (Optional): Port for the HTTP server. Defaults to `3010`.
    - **`MCP_HTTP_HOST`** (Optional): Host for the HTTP server. Defaults to `127.0.0.1`.
    - **`MCP_ALLOWED_ORIGINS`** (Optional): Comma-separated list of allowed CORS origins (e.g., `http://localhost:3000,https://example.com`).
    - **`MCP_AUTH_SECRET_KEY`** (Required for HTTP Auth): A secure secret key (at least 32 characters long) for JWT authentication. **CRITICAL for production.**

**Filesystem Security:**

- **`FS_BASE_DIRECTORY`** (Optional): Defines the root directory for all filesystem operations. This can be an **absolute path** or a **path relative to the project root** (e.g., `./data_sandbox`). If set, the server's tools will be restricted to accessing files and directories only within this specified (and resolved absolute) path and its subdirectories. This is a crucial security feature to prevent unintended access to other parts of the filesystem. If not set (which is not recommended for production environments), a warning will be logged, and operations will not be restricted.

**LLM & API Integration (Optional):**

- **`OPENROUTER_APP_URL`**: Your application's URL for OpenRouter.
- **`OPENROUTER_APP_NAME`**: Your application's name for OpenRouter. Defaults to `MCP_SERVER_NAME`.
- **`OPENROUTER_API_KEY`**: API key for OpenRouter services.
- **`LLM_DEFAULT_MODEL`**: Default LLM model to use (e.g., `google/gemini-2.5-flash-preview-05-20`).
- **`LLM_DEFAULT_TEMPERATURE`**, **`LLM_DEFAULT_TOP_P`**, **`LLM_DEFAULT_MAX_TOKENS`**, **`LLM_DEFAULT_TOP_K`**, **`LLM_DEFAULT_MIN_P`**: Default parameters for LLM calls.
- **`GEMINI_API_KEY`**: API key for Google Gemini services.

**OAuth Proxy Integration (Optional, for advanced scenarios):**

- **`OAUTH_PROXY_AUTHORIZATION_URL`**, **`OAUTH_PROXY_TOKEN_URL`**, **`OAUTH_PROXY_REVOCATION_URL`**, **`OAUTH_PROXY_ISSUER_URL`**, **`OAUTH_PROXY_SERVICE_DOCUMENTATION_URL`**, **`OAUTH_PROXY_DEFAULT_CLIENT_REDIRECT_URIS`**: Configuration for an OAuth proxy.

Refer to `src/config/index.ts` and the `.clinerules` file for the complete list and Zod schema definitions.

## Usage with MCP Clients

To allow an MCP client (like an AI assistant) to use this server:

1.  **Run the Server:** Start the server from your terminal:
    ```bash
    node dist/index.js
    # Or if you are in the project root:
    # npm start
    ```
2.  **Configure the Client:** Add the server to your MCP client's configuration. The exact method depends on the client.

    **For STDIO Transport (Default):**
    Typically involves specifying:

    - **Command:** `node`
    - **Arguments:** The absolute path to the built server executable (e.g., `/path/to/filesystem-mcp-server/dist/index.js`).
    - **Environment Variables (Optional):** Set any required environment variables from the [Configuration](#configuration) section.

    **Example MCP Settings for STDIO (Conceptual):**

    ```json
    {
      "mcpServers": {
        "filesystem_stdio": {
          "command": "node",
          "args": ["/path/to/filesystem-mcp-server/dist/index.js"],
          "env": {
            "MCP_LOG_LEVEL": "debug"
            // Other relevant env vars
          },
          "disabled": false,
          "autoApprove": []
        }
      }
    }
    ```

    **For HTTP Transport:**
    The client will need to know the server's URL (e.g., `http://localhost:3010`) and how to authenticate (e.g., providing a JWT Bearer token if `MCP_AUTH_SECRET_KEY` is set). Refer to your MCP client's documentation for HTTP server configuration.

Once configured and running, the client will detect the server and its available tools.

## Available Tools

The server exposes the following tools for filesystem interaction:

| Tool                         | Description                                                                                                                                                                                                                                                                                                        |
| :--------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`set_filesystem_default`** | Sets a default absolute path for the current session. Relative paths used in subsequent tool calls will be resolved against this default. Resets on server restart.                                                                                                                                                |
| **`read_file`**              | Reads the entire content of a specified file as UTF-8 text. Accepts relative (resolved against default) or absolute paths.                                                                                                                                                                                         |
| **`write_file`**             | Writes content to a specified file. Creates the file (and necessary parent directories) if it doesn't exist, or overwrites it if it does. Accepts relative or absolute paths.                                                                                                                                      |
| **`update_file`**            | Performs targeted search-and-replace operations within an existing file using an array of `{search, replace}` blocks. Ideal for localized changes. Supports plain text or regex search (`useRegex: true`) and replacing all occurrences (`replaceAll: true`). Accepts relative or absolute paths. File must exist. |
| **`list_files`**             | Lists files and directories within a specified path. Options include recursive listing (`includeNested: true`) and limiting the number of entries (`maxEntries`). Returns a formatted tree structure. Accepts relative or absolute paths.                                                                          |
| **`delete_file`**            | Permanently removes a specific file. Accepts relative or absolute paths.                                                                                                                                                                                                                                           |
| **`delete_directory`**       | Permanently removes a directory. Use `recursive: true` to remove non-empty directories and their contents (use with caution!). Accepts relative or absolute paths.                                                                                                                                                 |
| **`create_directory`**       | Creates a new directory at the specified path. By default (`create_parents: true`), it also creates any necessary parent directories. Accepts relative or absolute paths.                                                                                                                                          |
| **`move_path`**              | Moves or renames a file or directory from a source path to a destination path. Accepts relative or absolute paths for both.                                                                                                                                                                                        |
| **`copy_path`**              | Copies a file or directory from a source path to a destination path. For directories, it copies recursively by default (`recursive: true`). Accepts relative or absolute paths.                                                                                                                                    |

_Refer to the tool registration files (`src/mcp-server/tools/*/registration.ts`) for detailed input/output schemas (Zod/JSON Schema)._

## Project Structure

The codebase is organized for clarity and maintainability:

```
filesystem-mcp-server/
├── dist/                 # Compiled JavaScript output (after npm run build)
├── logs/                 # Log files (created at runtime)
├── node_modules/         # Project dependencies
├── src/                  # TypeScript source code
│   ├── config/           # Configuration loading (index.ts)
│   ├── mcp-server/       # Core MCP server logic
│   │   ├── server.ts     # Server initialization, tool registration, transport handling
│   │   ├── state.ts      # Session state management (e.g., default path)
│   │   ├── tools/        # Individual tool implementations (one subdir per tool)
│   │   │   ├── readFile/
│   │   │   │   ├── index.ts
│   │   │   │   ├── readFileLogic.ts
│   │   │   │   └── registration.ts
│   │   │   └── ...       # Other tools (writeFile, updateFile, etc.)
│   │   └── transports/   # Communication transport implementations
│   │       ├── authentication/ # Auth middleware for HTTP
│   │       │   └── authMiddleware.ts
│   │       ├── httpTransport.ts
│   │       └── stdioTransport.ts
│   ├── types-global/     # Shared TypeScript types and interfaces
│   │   ├── errors.ts     # Custom error classes and codes (McpError, BaseErrorCode)
│   │   ├── mcp.ts        # MCP related types
│   │   └── tool.ts       # Tool definition types
│   ├── utils/            # Reusable utility modules, categorized
│   │   ├── internal/     # Core internal utilities (errorHandler, logger, requestContext)
│   │   ├── metrics/      # Metrics-related utilities (tokenCounter)
│   │   ├── parsing/      # Parsing utilities (dateParser, jsonParser)
│   │   ├── security/     # Security-related utilities (idGenerator, rateLimiter, sanitization)
│   │   └── index.ts      # Barrel export for all utilities
│   └── index.ts          # Main application entry point
├── .clinerules           # Cheatsheet for LLM assistants
├── .dockerignore
├── Dockerfile
├── LICENSE
├── mcp.json              # MCP server manifest (generated by SDK or manually)
├── package.json
├── package-lock.json
├── README.md             # This file
├── repomix.config.json
├── smithery.yaml         # Smithery configuration (if used)
└── tsconfig.json         # TypeScript compiler options
```

For a live, detailed view of the current structure, run: `npm run tree` (This script might need to be updated if `src/scripts/tree.ts` was part of the changes).

> **Developer Note:** This repository includes a [.clinerules](.clinerules) file. This cheat sheet provides your LLM coding assistant with essential context about codebase patterns, file locations, and usage examples. Keep it updated as the server evolves!

## License

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

---

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

```

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

```typescript
export { registerCopyPathTool } from './registration.js';

```

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

```typescript
export { registerMovePathTool } from './registration.js';

```

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

```typescript
export { registerListFilesTool } from './registration.js';

```

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

```typescript
export { registerWriteFileTool } from './registration.js';

```

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

```typescript
export { registerDeleteFileTool } from './registration.js';

```

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

```typescript
export { registerUpdateFileTool } from './registration.js';

```

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

```typescript
export { registerCreateDirectoryTool } from './registration.js';

```

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

```typescript
export { registerDeleteDirectoryTool } from './registration.js';

```

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

```typescript
export { registerSetFilesystemDefaultTool } from './registration.js';

```

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

```typescript
/**
 * Barrel file for the 'read_file' tool.
 * Exports the registration function for easy import into the main server setup.
 */
export { registerReadFileTool } from './registration.js';

```

--------------------------------------------------------------------------------
/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/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/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/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 "./requestContext.js";

```

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

```json
{
  "mcpServers": {
    "@cyanheads/filesystem-mcp-server": {
      "command": "node",
      "args": ["dist/index.js"],
      "env": {
        "MCP_LOG_LEVEL": "debug",
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_HTTP_PORT": "3010",
        "LOGS_DIR": "./logs",
        "FS_BASE_DIRECTORY": ""
      }
    }
  }
}

```

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

```json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "strict": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "declaration": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}
```

--------------------------------------------------------------------------------
/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": []
  },
  "security": {
    "enableSecurityCheck": true
  }
}
```

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

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

jobs:
  build-and-publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18.x'
          registry-url: 'https://registry.npmjs.org'
      
      - 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/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";

// 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/types-global/tool.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';
import { RequestContext } from "../utils/internal/requestContext.js";
import { McpToolResponse } from './mcp.js';

/**
 * Base interface for tool input parameters
 */
export interface BaseToolInput {
  [key: string]: unknown;
}

/**
 * Base interface for tool response content
 */
export interface BaseToolResponse {
  [key: string]: unknown;
}

/**
 * Interface for tool registration options
 */
export interface ToolRegistrationOptions<TInput extends BaseToolInput> {
  /** Zod schema for input validation */
  inputSchema: z.ZodType<TInput>;
  /** Description of the tool */
  description: string;
  /** Example usage scenarios */
  examples?: { name: string; input: TInput; description?: string }[];
}

/**
 * Interface for a tool handler function
 */
export type ToolHandler<TInput extends BaseToolInput, TResponse extends McpToolResponse> = (
  input: TInput,
  context: RequestContext
) => Promise<TResponse>;

```

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

```typescript
// Type definitions for the MCP (Message Control Protocol) protocol

// Common response types
export interface McpContent {
  type: "text";
  text: string;
}

export interface McpToolResponse {
  content: McpContent[];
  isError?: boolean;
}

// Resource response types
export interface ResourceContent {
  uri: string;
  text: string;
  mimeType?: string;
}

export interface ResourceResponse {
  contents: ResourceContent[];
}

// Prompt response types
export interface PromptMessageContent {
  type: "text";
  text: string;
}

export interface PromptMessage {
  role: "user" | "assistant";
  content: PromptMessageContent;
}

export interface PromptResponse {
  messages: PromptMessage[];
}

// Helper functions
export const createToolResponse = (text: string, isError?: boolean): McpToolResponse => ({
  content: [{
    type: "text",
    text
  }],
  isError
});

export const createResourceResponse = (uri: string, text: string, mimeType?: string): ResourceResponse => ({
  contents: [{
    uri,
    text,
    mimeType
  }]
});

export const createPromptResponse = (text: string, role: "user" | "assistant" = "assistant"): PromptResponse => ({
  messages: [{
    role,
    content: {
      type: "text",
      text
    }
  }]
});
```

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

```yaml
startCommand:
  type: stdio
  configSchema:
    type: object
    properties:
      MCP_TRANSPORT_TYPE:
        type: string
        enum: ["stdio", "http"]
        default: "stdio"
        description: "MCP communication transport ('stdio' or 'http')."
      MCP_HTTP_PORT:
        type: integer
        default: 3010
        description: "HTTP server port (if MCP_TRANSPORT_TYPE is 'http')."
      MCP_LOG_LEVEL:
        type: string
        default: "debug" # Default from src/config/index.ts
        description: "Minimum logging level (e.g., 'debug', 'info', 'warn', 'error')."
      LOGS_DIR:
        type: string
        default: "./logs"
        description: "Directory for log files. Relative to project root."
      FS_BASE_DIRECTORY:
        type: string
        default: ""
        description: "Optional base directory for all filesystem operations. If set, tools cannot access paths outside this directory. Must be an absolute path if provided."
      # Add other relevant env vars from src/config/index.ts if they should be configurable via Smithery
      # For example, MCP_HTTP_HOST, MCP_ALLOWED_ORIGINS, MCP_AUTH_SECRET_KEY could be added here
      # if they need to be set dynamically when the server is started by Smithery.
      # For now, keeping it simple with the core ones.
  commandFunction: |
    (config) => ({
      "command": "node",
      "args": ["dist/index.js"],
      "env": {
        "MCP_TRANSPORT_TYPE": config.MCP_TRANSPORT_TYPE,
        "MCP_HTTP_PORT": String(config.MCP_HTTP_PORT), // Ensure port is a string for env
        "MCP_LOG_LEVEL": config.MCP_LOG_LEVEL,
        "LOGS_DIR": config.LOGS_DIR,
        "FS_BASE_DIRECTORY": config.FS_BASE_DIRECTORY
        // Map other configured env vars here if added to configSchema
      }
    })

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/setFilesystemDefault/setFilesystemDefaultLogic.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from 'zod';
import { McpError } from '../../../types-global/errors.js';
import { RequestContext } from '../../../utils/internal/requestContext.js';
import { serverState } from '../../state.js'; // Import the server state

// Define the input schema using Zod for validation
export const SetFilesystemDefaultInputSchema = z.object({
  path: z.string().min(1, 'Path cannot be empty')
    .describe('The absolute path to set as the default for resolving relative paths during this session.'),
});

// Define the TypeScript type for the input
export type SetFilesystemDefaultInput = z.infer<typeof SetFilesystemDefaultInputSchema>;

// Define the TypeScript type for the output (simple success message)
export interface SetFilesystemDefaultOutput {
  message: string;
  currentDefaultPath: string | null;
}

/**
 * Sets the default filesystem path for the current session.
 *
 * @param {SetFilesystemDefaultInput} input - The input object containing the absolute path.
 * @param {RequestContext} context - The request context for logging and error handling.
 * @returns {Promise<SetFilesystemDefaultOutput>} A promise that resolves with a success message and the new default path.
 * @throws {McpError} Throws McpError if the path is invalid or not absolute.
 */
export const setFilesystemDefaultLogic = async (input: SetFilesystemDefaultInput, context: RequestContext): Promise<SetFilesystemDefaultOutput> => {
  const { path: newPath } = input;

  // The validation (absolute check, sanitization) happens within serverState.setDefaultFilesystemPath
  serverState.setDefaultFilesystemPath(newPath, context);

  const currentPath = serverState.getDefaultFilesystemPath();
  return {
    message: `Default filesystem path successfully set to: ${currentPath}`,
    currentDefaultPath: currentPath,
  };
};

```

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

```dockerfile
# ---- Builder Stage ----
FROM node:22-slim AS builder

# Set working directory
WORKDIR /app

# Install dependencies
# Copy package files first for better caching
COPY package.json package-lock.json* ./
# Install all dependencies (including devDependencies needed for build)
RUN npm install --production=false --ignore-scripts

# Copy source code (respecting .dockerignore)
COPY . .

# Build the application using the 'tsc' command specified in package.json
RUN npm run build

# Remove devDependencies after build
RUN npm prune --production


# ---- Final Stage ----
FROM node:22-slim

ARG FS_BASE_DIRECTORY=""
ENV NODE_ENV=production \
    PATH="/home/service-user/.local/bin:${PATH}" \
    FS_BASE_DIRECTORY=${FS_BASE_DIRECTORY}

# Install mcp-proxy globally for runtime use
# Combine update, install, and clean in one layer
RUN apt-get update && \
    apt-get install -y --no-install-recommends curl && \
    npm install -g [email protected] && \
    npm cache clean --force && \
    apt-get purge -y --auto-remove curl && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

# Create non-root user and group
# Create app directory and set permissions
RUN groupadd --system --gid 1987 service-user && \
    useradd --system --uid 1987 --gid service-user -m service-user && \
    mkdir -p /app && \
    chown -R service-user:service-user /app

# Set working directory
WORKDIR /app

# Copy necessary artifacts from builder stage
# Ensure package.json is copied for runtime metadata if needed
COPY --from=builder --chown=service-user:service-user /app/package.json ./package.json
# Copy production node_modules
COPY --from=builder --chown=service-user:service-user /app/node_modules ./node_modules
# Copy the build output from the correct directory ('dist')
COPY --from=builder --chown=service-user:service-user /app/dist ./dist

# Switch to non-root user
USER service-user

# Expose port if necessary (Update port number if your app uses a different one)
# EXPOSE 3000

# Define the command to run the application using the correct build output path ('dist')
CMD ["mcp-proxy", "node", "dist/index.js"]

```

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

```json
{
  "name": "@cyanheads/filesystem-mcp-server",
  "version": "1.0.4",
  "description": "A Model Context Protocol (MCP) server for platform-agnostic file capabilities, including advanced search and replace, and directory tree traversal",
  "main": "dist/index.js",
  "files": [
    "dist"
  ],
  "type": "module",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/cyanheads/filesystem-mcp-server.git"
  },
  "bugs": {
    "url": "https://github.com/cyanheads/filesystem-mcp-server/issues"
  },
  "homepage": "https://github.com/cyanheads/filesystem-mcp-server#readme",
  "scripts": {
    "build": "tsc",
    "clean": "ts-node scripts/clean.ts",
    "rebuild": "ts-node --esm scripts/clean.ts && npm run build",
    "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",
    "tree": "ts-node --esm scripts/tree.ts",
    "format": "prettier --write \"**/*.{ts,js,json,md,html,css}\"",
    "inspector": "mcp-inspector --config mcp.json --server @cyanheads/filesystem-mcp-server"
  },
  "dependencies": {
    "@google/genai": "^1.0.1",
    "@modelcontextprotocol/sdk": "^1.12.0",
    "@types/jsonwebtoken": "^9.0.9",
    "@types/node": "^22.15.21",
    "@types/sanitize-html": "^2.16.0",
    "@types/validator": "13.15.1",
    "chalk": "^5.4.1",
    "chrono-node": "^2.8.0",
    "cli-table3": "^0.6.5",
    "dotenv": "^16.5.0",
    "express": "^5.1.0",
    "ignore": "^7.0.4",
    "jsonwebtoken": "^9.0.2",
    "openai": "^4.103.0",
    "partial-json": "^0.1.7",
    "sanitize-html": "^2.17.0",
    "tiktoken": "^1.0.21",
    "ts-node": "^10.9.2",
    "typescript": "^5.8.3",
    "validator": "13.15.0",
    "winston": "^3.17.0",
    "winston-daily-rotate-file": "^5.0.0",
    "yargs": "^17.7.2",
    "zod": "^3.25.23"
  },
  "devDependencies": {
    "@types/express": "^5.0.2",
    "@types/js-yaml": "^4.0.9",
    "axios": "^1.9.0",
    "js-yaml": "^4.1.0",
    "prettier": "^3.5.3",
    "typedoc": "^0.28.4"
  },
  "keywords": [
    "AI-integration",
    "MCP",
    "authentication",
    "client",
    "file-operations",
    "file-system",
    "filesystem",
    "http",
    "jwt",
    "LLM",
    "model-context-protocol",
    "sdk",
    "search-replace",
    "server",
    "sse",
    "template",
    "typescript"
  ],
  "author": "Casey Hand <[email protected]> (https://github.com/cyanheads/filesystem-mcp-server#readme)",
  "license": "Apache-2.0",
  "engines": {
    "node": ">=16.0.0"
  },
  "publishConfig": {
    "access": "public"
  }
}

```

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

```typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { BaseErrorCode, McpError } from '../../../types-global/errors.js';
import { ErrorHandler } from '../../../utils/internal/errorHandler.js';
import { logger } from '../../../utils/internal/logger.js';
import { requestContextService } from '../../../utils/internal/requestContext.js';
import {
  WriteFileInput,
  WriteFileInputSchema,
  writeFileLogic,
} from './writeFileLogic.js';

/**
 * Registers the 'write_file' tool with the MCP server.
 *
 * @param {McpServer} server - The McpServer instance to register the tool with.
 * @returns {Promise<void>} A promise that resolves when the tool is registered.
 * @throws {McpError} Throws an error if registration fails.
 */
export const registerWriteFileTool = async (server: McpServer): Promise<void> => {
  const registrationContext = requestContextService.createRequestContext({ operation: 'RegisterWriteFileTool' });
  logger.info("Attempting to register 'write_file' tool", registrationContext);

  await ErrorHandler.tryCatch(
    async () => {
      server.tool(
        'write_file', // Tool name
        'Writes content to a specified file. Creates the file (and necessary directories) if it doesn\'t exist, or overwrites it if it does. Accepts relative or absolute paths (resolved like readFile).', // Description
        WriteFileInputSchema.shape, // Pass the schema shape
        async (params, extra) => {
          const typedParams = params as WriteFileInput;
          const callContext = requestContextService.createRequestContext({ operation: 'WriteFileToolExecution', parentId: registrationContext.requestId });
          logger.info(`Executing 'write_file' tool for path: ${typedParams.path}`, callContext);

          // ErrorHandler will catch McpErrors thrown by the logic
          const result = await ErrorHandler.tryCatch(
            () => writeFileLogic(typedParams, callContext),
            {
              operation: 'writeFileLogic',
              context: callContext,
              input: { path: typedParams.path, content: '[CONTENT REDACTED]' }, // Redact content for logging
              errorCode: BaseErrorCode.INTERNAL_ERROR
            }
          );

          logger.info(`Successfully executed 'write_file' for path: ${result.writtenPath}`, callContext);

          // Format the successful response
          return {
            content: [{ type: 'text', text: result.message }],
          };
        }
      );
      logger.info("'write_file' tool registered successfully", registrationContext);
    },
    {
      operation: 'registerWriteFileTool',
      context: registrationContext,
      errorCode: BaseErrorCode.CONFIGURATION_ERROR,
      critical: true
    }
  );
};

```

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

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

/**
 * Clean Script
 * ============
 * 
 * Description:
 *   A utility script to clean build artifacts and temporary directories from your project.
 *   By default, it removes the 'dist' and 'logs' directories if they exist.
 * 
 * Usage:
 *   - Add to package.json: "clean": "node dist/scripts/clean.js"
 *   - Can be run directly: npm run clean
 *   - Often used in rebuild scripts: "rebuild": "npm run clean && npm run build"
 *   - Can be used with arguments to specify custom directories: node dist/scripts/clean.js temp coverage
 * 
 * Platform compatibility:
 *   - Works on all platforms (Windows, macOS, Linux) using Node.js path normalization
 */

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

/**
 * Interface for clean operation result
 */
interface CleanResult {
  dir: string;
  status: 'success' | 'skipped';
  reason?: string;
}

/**
 * Check if a directory exists without using fs.Stats
 */
async function directoryExists(dirPath: string): Promise<boolean> {
  try {
    await access(dirPath);
    return true;
  } catch {
    return false;
  }
}

/**
 * Main clean function
 */
const clean = async (): Promise<void> => {
  try {
    // Default directories to clean
    let dirsToClean: string[] = ['dist', 'logs'];
    
    // If directories are specified as command line arguments, use those instead
    const args = process.argv.slice(2);
    if (args.length > 0) {
      dirsToClean = args;
    }
    
    console.log(`Cleaning directories: ${dirsToClean.join(', ')}`);

    // Process each directory
    const results = await Promise.allSettled(
      dirsToClean.map(async (dir): Promise<CleanResult> => {  
        const dirPath = join(process.cwd(), dir);
        
        try {
          // Check if directory exists before attempting to remove it
          const exists = await directoryExists(dirPath);
          
          if (!exists) {
            return { dir, status: 'skipped', reason: 'does not exist' };
          }
          
          // Remove directory if it exists
          await rm(dirPath, { recursive: true, force: true });
          return { dir, status: 'success' };
        } catch (error) {
          throw error;
        }
      })
    );

    // Report results
    for (const result of results) {
      if (result.status === 'fulfilled') {
        const { dir, status, reason } = result.value;
        if (status === 'success') {
          console.log(`✓ Successfully cleaned ${dir} directory`);
        } else {
          console.log(`- ${dir} directory ${reason}, skipping cleanup`);
        }
      } else {
        console.error(`× Error cleaning directory: ${result.reason}`);
      }
    }
  } catch (error) {
    console.error('× Error during cleanup:', error instanceof Error ? error.message : error);
    process.exit(1);
  }
};

// Execute the clean function
clean();
```

--------------------------------------------------------------------------------
/src/mcp-server/tools/readFile/readFileLogic.ts:
--------------------------------------------------------------------------------

```typescript
import fs from 'fs/promises'; // Ensure fs is imported
import { z } from 'zod';
// No longer need config for base directory here
import { BaseErrorCode, McpError } from '../../../types-global/errors.js';
import { RequestContext } from '../../../utils/internal/requestContext.js';
import { serverState } from '../../state.js'; // Import serverState for path resolution
// No longer need sanitization directly here for path resolution

// Define the input schema using Zod for validation - Updated description
export const ReadFileInputSchema = z.object({
  path: z.string().min(1, 'Path cannot be empty')
    .describe('The path to the file to read. Can be relative or absolute. If relative, it resolves against the path set by `set_filesystem_default`. If absolute, it is used directly. If relative and no default is set, an error occurs.'),
});

// Define the TypeScript type for the input
export type ReadFileInput = z.infer<typeof ReadFileInputSchema>;

// Define the TypeScript type for the output
export interface ReadFileOutput {
  content: string;
}

/**
 * Reads the content of a specified file.
 *
 * @param {ReadFileInput} input - The input object containing the file path.
 * @param {RequestContext} context - The request context for logging and error handling.
 * @returns {Promise<ReadFileOutput>} A promise that resolves with the file content.
 * @throws {McpError} Throws McpError for path resolution errors, file not found, or I/O errors.
 */
export const readFileLogic = async (input: ReadFileInput, context: RequestContext): Promise<ReadFileOutput> => {
  const { path: requestedPath } = input;

  // Resolve the path using serverState (handles relative/absolute logic and sanitization)
  // This will throw McpError if a relative path is given without a default set.
  const absolutePath = serverState.resolvePath(requestedPath, context);

  try {
    // Read the file content using the resolved absolute path
    const content = await fs.readFile(absolutePath, 'utf8');
    return { content };
  } catch (error: any) {
    // Handle specific file system errors
    // Handle specific file system errors using the resolved absolutePath in messages
    if (error.code === 'ENOENT') {
      // Use NOT_FOUND error code
      throw new McpError(BaseErrorCode.NOT_FOUND, `File not found at resolved path: ${absolutePath}`, { ...context, requestedPath, resolvedPath: absolutePath, originalError: error });
    }
    if (error.code === 'EISDIR') {
       // Use VALIDATION_ERROR
       throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Resolved path is a directory, not a file: ${absolutePath}`, { ...context, requestedPath, resolvedPath: absolutePath, originalError: error });
    }
    // Handle other potential I/O errors using INTERNAL_ERROR
    throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to read file: ${error.message || 'Unknown I/O error'}`, { ...context, originalError: error });
  }
};

```

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

```typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { BaseErrorCode, McpError } from '../../../types-global/errors.js';
import { ErrorHandler } from '../../../utils/internal/errorHandler.js';
import { logger } from '../../../utils/internal/logger.js';
import { requestContextService } from '../../../utils/internal/requestContext.js';
import {
  SetFilesystemDefaultInput,
  SetFilesystemDefaultInputSchema,
  setFilesystemDefaultLogic,
} from './setFilesystemDefaultLogic.js';

/**
 * Registers the 'set_filesystem_default' tool with the MCP server.
 *
 * @param {McpServer} server - The McpServer instance to register the tool with.
 * @returns {Promise<void>} A promise that resolves when the tool is registered.
 * @throws {McpError} Throws an error if registration fails.
 */
export const registerSetFilesystemDefaultTool = async (server: McpServer): Promise<void> => {
  const registrationContext = requestContextService.createRequestContext({ operation: 'RegisterSetFilesystemDefaultTool' });
  logger.info("Attempting to register 'set_filesystem_default' tool", registrationContext);

  await ErrorHandler.tryCatch(
    async () => {
      server.tool(
        'set_filesystem_default', // Tool name
        'Sets a default absolute path for the current session. Relative paths used in other filesystem tools (like readFile) will be resolved against this default. The default is cleared on server restart.', // Description
        SetFilesystemDefaultInputSchema.shape, // Pass the schema shape
        async (params, extra) => {
          const typedParams = params as SetFilesystemDefaultInput;
          const callContext = requestContextService.createRequestContext({ operation: 'SetFilesystemDefaultToolExecution', parentId: registrationContext.requestId });
          logger.info(`Executing 'set_filesystem_default' tool with path: ${typedParams.path}`, callContext);

          // ErrorHandler will catch McpErrors thrown by the logic (e.g., non-absolute path)
          const result = await ErrorHandler.tryCatch(
            () => setFilesystemDefaultLogic(typedParams, callContext),
            {
              operation: 'setFilesystemDefaultLogic',
              context: callContext,
              input: typedParams, // Input is automatically sanitized by ErrorHandler
              errorCode: BaseErrorCode.INTERNAL_ERROR // Default error if unexpected failure
            }
          );

          logger.info(`Successfully executed 'set_filesystem_default'. Current default: ${result.currentDefaultPath}`, callContext);

          // Format the successful response
          return {
            content: [{ type: 'text', text: result.message }],
          };
        }
      );
      logger.info("'set_filesystem_default' tool registered successfully", registrationContext);
    },
    {
      operation: 'registerSetFilesystemDefaultTool',
      context: registrationContext,
      errorCode: BaseErrorCode.CONFIGURATION_ERROR,
      critical: true
    }
  );
};

```

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

```typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { BaseErrorCode, McpError } from '../../../types-global/errors.js';
import { ErrorHandler } from '../../../utils/internal/errorHandler.js';
import { logger } from '../../../utils/internal/logger.js';
import { requestContextService } from '../../../utils/internal/requestContext.js';
import { ReadFileInput, ReadFileInputSchema, readFileLogic } from './readFileLogic.js';

/**
 * Registers the 'read_file' tool with the MCP server.
 *
 * @param {McpServer} server - The McpServer instance to register the tool with.
 * @returns {Promise<void>} A promise that resolves when the tool is registered.
 * @throws {McpError} Throws an error if registration fails.
 */
export const registerReadFileTool = async (server: McpServer): Promise<void> => {
  const registrationContext = requestContextService.createRequestContext({ operation: 'RegisterReadFileTool' });
  logger.info("Attempting to register 'read_file' tool", registrationContext);

  await ErrorHandler.tryCatch(
    async () => {
      // Removed explicit generic <ReadFileInput>, let it be inferred from the schema
      server.tool(
        'read_file', // Tool name
        'Reads the entire content of a specified file as UTF-8 text. Accepts relative or absolute paths. Relative paths are resolved against the session default set by `set_filesystem_default`.', // Updated Description
        ReadFileInputSchema.shape, // Pass the schema shape, not the object instance
        async (params, extra) => { // Correct handler signature: params and extra
          // Cast params to the correct type within the handler for type safety
          const typedParams = params as ReadFileInput;
          // Create a new context for this specific tool execution
          // We might potentially use `extra.requestId` if available and needed for tracing, but let's keep it simple for now.
          const callContext = requestContextService.createRequestContext({ operation: 'ReadFileToolExecution', parentId: registrationContext.requestId });
          logger.info(`Executing 'read_file' tool for path: ${typedParams.path}`, callContext);

          // ErrorHandler will catch McpErrors thrown by readFileLogic and format them
          const result = await ErrorHandler.tryCatch(
            () => readFileLogic(typedParams, callContext), // Use typedParams
            {
              operation: 'readFileLogic',
              context: callContext,
              input: typedParams, // Input is automatically sanitized by ErrorHandler for logging
              errorCode: BaseErrorCode.INTERNAL_ERROR // Default error if unexpected failure
            }
          );

          logger.info(`Successfully read file: ${typedParams.path}`, callContext); // Use typedParams

          // Format the successful response
          return {
            content: [{ type: 'text', text: result.content }],
          };
        }
      );
      logger.info("'read_file' tool registered successfully", registrationContext);
    },
    {
      operation: 'registerReadFileTool',
      context: registrationContext,
      errorCode: BaseErrorCode.CONFIGURATION_ERROR, // Error code if registration itself fails
      critical: true // Failure to register is critical
    }
  );
};

```

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

```typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { BaseErrorCode, McpError } from '../../../types-global/errors.js';
import { ErrorHandler } from '../../../utils/internal/errorHandler.js';
import { logger } from '../../../utils/internal/logger.js';
import { requestContextService } from '../../../utils/internal/requestContext.js';
import { sanitization } from '../../../utils/security/sanitization.js';
import {
  DeleteFileInputSchema,
  deleteFileLogic
} from './deleteFileLogic.js';

/**
 * Registers the 'delete_file' tool with the MCP server.
 *
 * @param {McpServer} server - The McpServer instance to register the tool with.
 * @returns {Promise<void>} A promise that resolves when the tool is registered.
 * @throws {McpError} Throws an error if registration fails.
 */
export const registerDeleteFileTool = async (server: McpServer): Promise<void> => {
  const registrationContext = requestContextService.createRequestContext({ operation: 'RegisterDeleteFileTool' });
  logger.info("Attempting to register 'delete_file' tool", registrationContext);

  await ErrorHandler.tryCatch(
    async () => {
      server.tool(
        'delete_file', // Tool name
        'Removes a specific file. Accepts relative or absolute paths.', // Description
        DeleteFileInputSchema.shape, // Pass the schema shape
        async (params, extra) => {
          // Validate input using the Zod schema
          const validationResult = DeleteFileInputSchema.safeParse(params);
          if (!validationResult.success) {
            const errorContext = requestContextService.createRequestContext({ operation: 'DeleteFileToolValidation' });
            logger.error('Invalid input parameters for delete_file tool', { ...errorContext, errors: validationResult.error.errors });
            throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid parameters: ${validationResult.error.errors.map(e => `${e.path.join('.')} - ${e.message}`).join(', ')}`, errorContext);
          }
          const typedParams = validationResult.data; // Use validated data

          // Create context for this execution
          const callContext = requestContextService.createRequestContext({ operation: 'DeleteFileToolExecution' });
          logger.info(`Executing 'delete_file' tool for path: ${typedParams.path}`, callContext);

          // ErrorHandler will catch McpErrors thrown by the logic
          const result = await ErrorHandler.tryCatch(
            () => deleteFileLogic(typedParams, callContext),
            {
              operation: 'deleteFileLogic',
              context: callContext,
              input: sanitization.sanitizeForLogging(typedParams), // Sanitize path if needed
              errorCode: BaseErrorCode.INTERNAL_ERROR
            }
          );

          logger.info(`Successfully executed 'delete_file' for path: ${result.deletedPath}`, callContext);

          // Format the successful response
          return {
            content: [{ type: 'text', text: result.message }],
          };
        }
      );
      logger.info("'delete_file' tool registered successfully", registrationContext);
    },
    {
      operation: 'registerDeleteFileTool',
      context: registrationContext,
      errorCode: BaseErrorCode.CONFIGURATION_ERROR,
      critical: true
    }
  );
};

```

--------------------------------------------------------------------------------
/src/mcp-server/transports/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 connectStdioTransport(
  server: McpServer,
  parentContext: RequestContext,
): Promise<void> {
  const operationContext = {
    ...parentContext,
    operation: "connectStdioTransport",
    transportType: "Stdio",
  };
  logger.debug("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) {
    ErrorHandler.handleError(err, { ...operationContext, critical: true });
    throw err; // Re-throw after handling to allow caller to react if necessary
  }
}

```

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

```typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { BaseErrorCode, McpError } from '../../../types-global/errors.js';
import { ErrorHandler } from '../../../utils/internal/errorHandler.js';
import { logger } from '../../../utils/internal/logger.js';
import { requestContextService } from '../../../utils/internal/requestContext.js';
import { sanitization } from '../../../utils/security/sanitization.js';
import {
  MovePathInputSchema,
  movePathLogic
} from './movePathLogic.js';

/**
 * Registers the 'move_path' tool with the MCP server.
 *
 * @param {McpServer} server - The McpServer instance to register the tool with.
 * @returns {Promise<void>} A promise that resolves when the tool is registered.
 * @throws {McpError} Throws an error if registration fails.
 */
export const registerMovePathTool = async (server: McpServer): Promise<void> => {
  const registrationContext = requestContextService.createRequestContext({ operation: 'RegisterMovePathTool' });
  logger.info("Attempting to register 'move_path' tool", registrationContext);

  await ErrorHandler.tryCatch(
    async () => {
      server.tool(
        'move_path', // Tool name
        'Moves or renames a file or directory. Accepts relative or absolute paths for source and destination.', // Description
        MovePathInputSchema.shape, // Pass the schema shape
        async (params, extra) => {
          // Validate input using the Zod schema
          const validationResult = MovePathInputSchema.safeParse(params);
          if (!validationResult.success) {
            const errorContext = requestContextService.createRequestContext({ operation: 'MovePathToolValidation' });
            logger.error('Invalid input parameters for move_path tool', { ...errorContext, errors: validationResult.error.errors });
            throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid parameters: ${validationResult.error.errors.map(e => `${e.path.join('.')} - ${e.message}`).join(', ')}`, errorContext);
          }
          const typedParams = validationResult.data; // Use validated data

          // Create context for this execution
          const callContext = requestContextService.createRequestContext({ operation: 'MovePathToolExecution' });
          logger.info(`Executing 'move_path' tool from "${typedParams.source_path}" to "${typedParams.destination_path}"`, callContext);

          // ErrorHandler will catch McpErrors thrown by the logic
          const result = await ErrorHandler.tryCatch(
            () => movePathLogic(typedParams, callContext),
            {
              operation: 'movePathLogic',
              context: callContext,
              input: sanitization.sanitizeForLogging(typedParams), // Sanitize paths
              errorCode: BaseErrorCode.INTERNAL_ERROR
            }
          );

          logger.info(`Successfully executed 'move_path' from "${result.sourcePath}" to "${result.destinationPath}"`, callContext);

          // Format the successful response
          return {
            content: [{ type: 'text', text: result.message }],
          };
        }
      );
      logger.info("'move_path' tool registered successfully", registrationContext);
    },
    {
      operation: 'registerMovePathTool',
      context: registrationContext,
      errorCode: BaseErrorCode.CONFIGURATION_ERROR,
      critical: true
    }
  );
};

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/deleteFile/deleteFileLogic.ts:
--------------------------------------------------------------------------------

```typescript
import fs from 'fs/promises';
import { z } from 'zod';
import { BaseErrorCode, McpError } from '../../../types-global/errors.js';
import { logger } from '../../../utils/internal/logger.js';
import { RequestContext } from '../../../utils/internal/requestContext.js';
import { serverState } from '../../state.js';

// Define the input schema using Zod for validation
export const DeleteFileInputSchema = z.object({
  path: z.string().min(1, 'Path cannot be empty')
    .describe('The path to the file to delete. Can be relative or absolute (resolved like readFile).'),
});

// Define the TypeScript type for the input
export type DeleteFileInput = z.infer<typeof DeleteFileInputSchema>;

// Define the TypeScript type for the output
export interface DeleteFileOutput {
  message: string;
  deletedPath: string;
}

/**
 * Deletes a specified file.
 *
 * @param {DeleteFileInput} input - The input object containing the path to the file.
 * @param {RequestContext} context - The request context.
 * @returns {Promise<DeleteFileOutput>} A promise resolving with the deletion status.
 * @throws {McpError} For path errors, file not found, or I/O errors.
 */
export const deleteFileLogic = async (input: DeleteFileInput, context: RequestContext): Promise<DeleteFileOutput> => {
  const { path: requestedPath } = input;
  const logicContext = { ...context, tool: 'deleteFileLogic' };
  logger.debug(`deleteFileLogic: Received request to delete path "${requestedPath}"`, logicContext);

  // Resolve the path
  const absolutePath = serverState.resolvePath(requestedPath, context);
  logger.debug(`deleteFileLogic: Resolved path to "${absolutePath}"`, { ...logicContext, requestedPath });

  try {
    // Check if the path exists and is a file before attempting deletion
    const stats = await fs.stat(absolutePath);
    if (!stats.isFile()) {
      logger.warning(`deleteFileLogic: Path is not a file "${absolutePath}"`, { ...logicContext, requestedPath });
      throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Path is not a file: ${absolutePath}`, { ...logicContext, requestedPath, resolvedPath: absolutePath });
    }

    // Attempt to delete the file
    await fs.unlink(absolutePath);
    logger.info(`deleteFileLogic: Successfully deleted file "${absolutePath}"`, { ...logicContext, requestedPath });

    return {
      message: `Successfully deleted file: ${absolutePath}`,
      deletedPath: absolutePath,
    };

  } catch (error: any) {
    logger.error(`deleteFileLogic: Error deleting file "${absolutePath}"`, { ...logicContext, requestedPath, error: error.message, code: error.code });

    if (error instanceof McpError) {
      throw error; // Re-throw known McpErrors
    }

    if (error.code === 'ENOENT') {
      logger.warning(`deleteFileLogic: File not found at "${absolutePath}"`, { ...logicContext, requestedPath });
      // Even though we checked with stat, there's a small race condition possibility,
      // or the error came from stat itself. Treat ENOENT as file not found.
      throw new McpError(BaseErrorCode.NOT_FOUND, `File not found at path: ${absolutePath}`, { ...logicContext, requestedPath, resolvedPath: absolutePath, originalError: error });
    }

    // Handle other potential I/O errors
    throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to delete file: ${error.message || 'Unknown I/O error'}`, { ...logicContext, requestedPath, resolvedPath: absolutePath, originalError: error });
  }
};

```

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

```typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { BaseErrorCode, McpError } from '../../../types-global/errors.js';
import { ErrorHandler } from '../../../utils/internal/errorHandler.js';
import { logger } from '../../../utils/internal/logger.js';
import { requestContextService } from '../../../utils/internal/requestContext.js';
import { sanitization } from '../../../utils/security/sanitization.js';
import {
  CopyPathInputSchema,
  copyPathLogic
} from './copyPathLogic.js';

/**
 * Registers the 'copy_path' tool with the MCP server.
 *
 * @param {McpServer} server - The McpServer instance to register the tool with.
 * @returns {Promise<void>} A promise that resolves when the tool is registered.
 * @throws {McpError} Throws an error if registration fails.
 */
export const registerCopyPathTool = async (server: McpServer): Promise<void> => {
  const registrationContext = requestContextService.createRequestContext({ operation: 'RegisterCopyPathTool' });
  logger.info("Attempting to register 'copy_path' tool", registrationContext);

  await ErrorHandler.tryCatch(
    async () => {
      server.tool(
        'copy_path', // Tool name
        'Copies a file or directory to a new location. Accepts relative or absolute paths. Defaults to recursive copy for directories.', // Description
        CopyPathInputSchema.shape, // Pass the schema shape
        async (params, extra) => {
          // Validate input using the Zod schema
          const validationResult = CopyPathInputSchema.safeParse(params);
          if (!validationResult.success) {
            const errorContext = requestContextService.createRequestContext({ operation: 'CopyPathToolValidation' });
            logger.error('Invalid input parameters for copy_path tool', { ...errorContext, errors: validationResult.error.errors });
            throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid parameters: ${validationResult.error.errors.map(e => `${e.path.join('.')} - ${e.message}`).join(', ')}`, errorContext);
          }
          const typedParams = validationResult.data; // Use validated data

          // Create context for this execution
          const callContext = requestContextService.createRequestContext({ operation: 'CopyPathToolExecution' });
          logger.info(`Executing 'copy_path' tool from "${typedParams.source_path}" to "${typedParams.destination_path}", recursive: ${typedParams.recursive}`, callContext);

          // ErrorHandler will catch McpErrors thrown by the logic
          const result = await ErrorHandler.tryCatch(
            () => copyPathLogic(typedParams, callContext),
            {
              operation: 'copyPathLogic',
              context: callContext,
              input: sanitization.sanitizeForLogging(typedParams), // Sanitize paths
              errorCode: BaseErrorCode.INTERNAL_ERROR
            }
          );

          logger.info(`Successfully executed 'copy_path' from "${result.sourcePath}" to "${result.destinationPath}", recursive: ${result.wasRecursive}`, callContext);

          // Format the successful response
          return {
            content: [{ type: 'text', text: result.message }],
          };
        }
      );
      logger.info("'copy_path' tool registered successfully", registrationContext);
    },
    {
      operation: 'registerCopyPathTool',
      context: registrationContext,
      errorCode: BaseErrorCode.CONFIGURATION_ERROR,
      critical: true
    }
  );
};

```

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

```typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { BaseErrorCode, McpError } from '../../../types-global/errors.js';
import { ErrorHandler } from '../../../utils/internal/errorHandler.js';
import { logger } from '../../../utils/internal/logger.js';
import { requestContextService } from '../../../utils/internal/requestContext.js';
import { sanitization } from '../../../utils/security/sanitization.js';
import {
  DeleteDirectoryInputSchema,
  deleteDirectoryLogic
} from './deleteDirectoryLogic.js';

/**
 * Registers the 'delete_directory' tool with the MCP server.
 *
 * @param {McpServer} server - The McpServer instance to register the tool with.
 * @returns {Promise<void>} A promise that resolves when the tool is registered.
 * @throws {McpError} Throws an error if registration fails.
 */
export const registerDeleteDirectoryTool = async (server: McpServer): Promise<void> => {
  const registrationContext = requestContextService.createRequestContext({ operation: 'RegisterDeleteDirectoryTool' });
  logger.info("Attempting to register 'delete_directory' tool", registrationContext);

  await ErrorHandler.tryCatch(
    async () => {
      server.tool(
        'delete_directory', // Tool name
        'Removes a directory. Optionally removes recursively. Accepts relative or absolute paths.', // Description
        DeleteDirectoryInputSchema.shape, // Pass the schema shape
        async (params, extra) => {
          // Validate input using the Zod schema
          const validationResult = DeleteDirectoryInputSchema.safeParse(params);
          if (!validationResult.success) {
            const errorContext = requestContextService.createRequestContext({ operation: 'DeleteDirectoryToolValidation' });
            logger.error('Invalid input parameters for delete_directory tool', { ...errorContext, errors: validationResult.error.errors });
            throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid parameters: ${validationResult.error.errors.map(e => `${e.path.join('.')} - ${e.message}`).join(', ')}`, errorContext);
          }
          const typedParams = validationResult.data; // Use validated data

          // Create context for this execution
          const callContext = requestContextService.createRequestContext({ operation: 'DeleteDirectoryToolExecution' });
          logger.info(`Executing 'delete_directory' tool for path: ${typedParams.path}, recursive: ${typedParams.recursive}`, callContext);

          // ErrorHandler will catch McpErrors thrown by the logic
          const result = await ErrorHandler.tryCatch(
            () => deleteDirectoryLogic(typedParams, callContext),
            {
              operation: 'deleteDirectoryLogic',
              context: callContext,
              input: sanitization.sanitizeForLogging(typedParams), // Sanitize path
              errorCode: BaseErrorCode.INTERNAL_ERROR
            }
          );

          logger.info(`Successfully executed 'delete_directory' for path: ${result.deletedPath}, recursive: ${result.wasRecursive}`, callContext);

          // Format the successful response
          return {
            content: [{ type: 'text', text: result.message }],
          };
        }
      );
      logger.info("'delete_directory' tool registered successfully", registrationContext);
    },
    {
      operation: 'registerDeleteDirectoryTool',
      context: registrationContext,
      errorCode: BaseErrorCode.CONFIGURATION_ERROR,
      critical: true
    }
  );
};

```

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

```typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { BaseErrorCode, McpError } from '../../../types-global/errors.js';
import { ErrorHandler } from '../../../utils/internal/errorHandler.js';
import { logger } from '../../../utils/internal/logger.js';
import { requestContextService } from '../../../utils/internal/requestContext.js';
import { sanitization } from '../../../utils/security/sanitization.js';
import {
  CreateDirectoryInputSchema,
  createDirectoryLogic
} from './createDirectoryLogic.js';

/**
 * Registers the 'create_directory' tool with the MCP server.
 *
 * @param {McpServer} server - The McpServer instance to register the tool with.
 * @returns {Promise<void>} A promise that resolves when the tool is registered.
 * @throws {McpError} Throws an error if registration fails.
 */
export const registerCreateDirectoryTool = async (server: McpServer): Promise<void> => {
  const registrationContext = requestContextService.createRequestContext({ operation: 'RegisterCreateDirectoryTool' });
  logger.info("Attempting to register 'create_directory' tool", registrationContext);

  await ErrorHandler.tryCatch(
    async () => {
      server.tool(
        'create_directory', // Tool name
        'Creates a directory. Optionally creates parent directories. Accepts relative or absolute paths.', // Description
        CreateDirectoryInputSchema.shape, // Pass the schema shape
        async (params, extra) => {
          // Validate input using the Zod schema
          const validationResult = CreateDirectoryInputSchema.safeParse(params);
          if (!validationResult.success) {
            const errorContext = requestContextService.createRequestContext({ operation: 'CreateDirectoryToolValidation' });
            logger.error('Invalid input parameters for create_directory tool', { ...errorContext, errors: validationResult.error.errors });
            throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid parameters: ${validationResult.error.errors.map(e => `${e.path.join('.')} - ${e.message}`).join(', ')}`, errorContext);
          }
          const typedParams = validationResult.data; // Use validated data

          // Create context for this execution
          const callContext = requestContextService.createRequestContext({ operation: 'CreateDirectoryToolExecution' });
          logger.info(`Executing 'create_directory' tool for path: ${typedParams.path}, create_parents: ${typedParams.create_parents}`, callContext);

          // ErrorHandler will catch McpErrors thrown by the logic
          const result = await ErrorHandler.tryCatch(
            () => createDirectoryLogic(typedParams, callContext),
            {
              operation: 'createDirectoryLogic',
              context: callContext,
              input: sanitization.sanitizeForLogging(typedParams), // Sanitize path
              errorCode: BaseErrorCode.INTERNAL_ERROR
            }
          );

          logger.info(`Successfully executed 'create_directory' for path: ${result.createdPath}, parentsCreated: ${result.parentsCreated}`, callContext);

          // Format the successful response
          return {
            content: [{ type: 'text', text: result.message }],
          };
        }
      );
      logger.info("'create_directory' tool registered successfully", registrationContext);
    },
    {
      operation: 'registerCreateDirectoryTool',
      context: registrationContext,
      errorCode: BaseErrorCode.CONFIGURATION_ERROR,
      critical: true
    }
  );
};

```

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

```typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { BaseErrorCode, McpError } from '../../../types-global/errors.js';
import { ErrorHandler } from '../../../utils/internal/errorHandler.js';
import { logger } from '../../../utils/internal/logger.js';
import { requestContextService } from '../../../utils/internal/requestContext.js';
import { sanitization } from '../../../utils/security/sanitization.js';
import {
  ListFilesInputSchema,
  listFilesLogic
} from './listFilesLogic.js';

/**
 * Registers the 'list_files' tool with the MCP server.
 * This tool lists files and directories at a specified path.
 *
 * @param {McpServer} server - The McpServer instance to register the tool with.
 * @returns {Promise<void>} A promise that resolves when the tool is registered.
 * @throws {McpError} Throws an error if registration fails.
 */
export const registerListFilesTool = async (server: McpServer): Promise<void> => {
  const registrationContext = requestContextService.createRequestContext({ operation: 'RegisterListFilesTool' });
  logger.info("Attempting to register 'list_files' tool", registrationContext);

  await ErrorHandler.tryCatch(
    async () => {
      server.tool(
        'list_files', // Tool name
        'Lists files and directories within the specified directory. Optionally lists recursively and returns a tree-like structure. Includes an optional `maxEntries` parameter (default 50) to limit the number of items returned.', // Updated Description
        ListFilesInputSchema.shape, // Pass the schema shape (already updated in logic file)
        async (params, extra) => {
          // Validate input using the Zod schema
          const validationResult = ListFilesInputSchema.safeParse(params);
          if (!validationResult.success) {
            // Create context without explicit parentRequestId
            const errorContext = requestContextService.createRequestContext({ operation: 'ListFilesToolValidation' });
            logger.error('Invalid input parameters for list_files tool', { ...errorContext, errors: validationResult.error.errors });
            throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid parameters: ${validationResult.error.errors.map(e => `${e.path.join('.')} - ${e.message}`).join(', ')}`, errorContext);
          }
          const typedParams = validationResult.data; // Use validated data

          // Create context for this execution without explicit parentRequestId
          const callContext = requestContextService.createRequestContext({ operation: 'ListFilesToolExecution' });
          logger.info(`Executing 'list_files' tool for path: ${typedParams.path}, nested: ${typedParams.includeNested}`, callContext);

          // Call the logic function
          const result = await ErrorHandler.tryCatch(
            () => listFilesLogic(typedParams, callContext),
            {
              operation: 'listFilesLogic',
              context: callContext,
              input: sanitization.sanitizeForLogging(typedParams), // Sanitize input for logging
              errorCode: BaseErrorCode.INTERNAL_ERROR
            }
          );

          logger.info(`Successfully executed 'list_files' for path: ${result.resolvedPath}. Items: ${result.itemCount}`, callContext);

          // Format the successful response - return the tree structure
          return {
            content: [{ type: 'text', text: result.tree }],
          };
        }
      );
      logger.info("'list_files' tool registered successfully", registrationContext);
    },
    {
      operation: 'registerListFilesTool',
      context: registrationContext,
      errorCode: BaseErrorCode.CONFIGURATION_ERROR,
      critical: true // Critical for server startup
    }
  );
};

```

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

```typescript
import { z } from "zod";
import { McpContent, McpToolResponse } from "./mcp.js";

/**
 * Defines a set of standardized error codes for common issues within MCP servers or tools.
 * These codes help clients understand the nature of an error programmatically.
 */
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 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'
}

/**
 * Custom error class for MCP-specific errors.
 * Encapsulates a `BaseErrorCode`, a descriptive message, and optional details.
 * Provides a method to format the error into a standard MCP tool response.
 */
export class McpError extends Error {
  /**
   * Creates an instance of McpError.
   * @param {BaseErrorCode} code - The standardized error code.
   * @param {string} message - A human-readable description of the error.
   * @param {Record<string, unknown>} [details] - Optional additional details about the error.
   */
  constructor(
    public code: BaseErrorCode,
    message: string,
    public details?: Record<string, unknown>
  ) {
    super(message);
    // Set the error name for identification
    this.name = 'McpError';
    // Ensure the prototype chain is correct
    Object.setPrototypeOf(this, McpError.prototype);
  }

  /**
   * Converts the McpError instance into a standard MCP tool response format.
   * This is useful for returning structured errors from tool handlers.
   * @returns {McpToolResponse} An object representing the error, suitable for an MCP tool response.
   */
  toResponse(): McpToolResponse {
    // Construct the text content for the error response
    const errorText = `Error [${this.code}]: ${this.message}${
      this.details ? `\nDetails: ${JSON.stringify(this.details, null, 2)}` : ''
    }`;

    const content: McpContent = {
      type: "text",
      text: errorText
    };

    // Return the structured error response
    return {
      content: [content],
      isError: true // Mark this response as an error
    };
  }
}

/**
 * Zod schema for validating error objects, potentially used for parsing
 * error responses or validating error structures internally.
 */
export const ErrorSchema = z.object({
  /** The error code, corresponding to BaseErrorCode enum values. */
  code: z.nativeEnum(BaseErrorCode).describe("Standardized error code"),
  /** A human-readable description of the error. */
  message: z.string().describe("Detailed error message"),
  /** Optional additional details or context about the error. */
  details: z.record(z.unknown()).optional().describe("Optional structured error details")
}).describe(
  "Schema for validating structured error objects."
);

/**
 * TypeScript type inferred from `ErrorSchema`.
 * Represents a validated error object structure.
 * @typedef {z.infer<typeof ErrorSchema>} ErrorResponse
 */
export type ErrorResponse = z.infer<typeof ErrorSchema>;

```

--------------------------------------------------------------------------------
/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 { 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,
    };
    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/tools/updateFile/registration.ts:
--------------------------------------------------------------------------------

```typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { BaseErrorCode, McpError } from '../../../types-global/errors.js';
import { ErrorHandler } from '../../../utils/internal/errorHandler.js';
import { logger } from '../../../utils/internal/logger.js';
import { requestContextService } from '../../../utils/internal/requestContext.js';
import { sanitization } from '../../../utils/security/sanitization.js';
import {
  UpdateFileInputSchema,
  updateFileLogic
} from './updateFileLogic.js';

/**
 * Registers the 'update_file' tool with the MCP server.
 * This tool accepts a JSON object with 'path', 'blocks' (array of {search, replace}),
 * and optional 'useRegex' and 'replaceAll' flags.
 *
 * @param {McpServer} server - The McpServer instance to register the tool with.
 * @returns {Promise<void>} A promise that resolves when the tool is registered.
 * @throws {McpError} Throws an error if registration fails.
 */
export const registerUpdateFileTool = async (server: McpServer): Promise<void> => {
  const registrationContext = requestContextService.createRequestContext({ operation: 'RegisterUpdateFileTool' });
  logger.info("Attempting to register 'update_file' tool with JSON input format", registrationContext);

  await ErrorHandler.tryCatch(
    async () => {
      server.tool(
        'update_file', // Tool name
        'Performs targeted search-and-replace operations within an existing file using an array of {search, replace} blocks. Preferred for smaller, localized changes. For large-scale updates or overwrites, consider using `write_file`. Accepts relative or absolute paths. File must exist. Supports optional `useRegex` (boolean, default false) and `replaceAll` (boolean, default false).', // Emphasized usage guidance
        UpdateFileInputSchema.shape, // Pass the updated schema shape
        async (params, extra) => {
          // Validate input using the Zod schema before proceeding
          const validationResult = UpdateFileInputSchema.safeParse(params);
          if (!validationResult.success) {
            // Create a new context for validation error
            const errorContext = requestContextService.createRequestContext({ operation: 'UpdateFileToolValidation' });
            logger.error('Invalid input parameters for update_file tool', { ...errorContext, errors: validationResult.error.errors });
            // Throw McpError for invalid parameters
            throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid parameters: ${validationResult.error.errors.map(e => `${e.path.join('.')} - ${e.message}`).join(', ')}`, errorContext);
          }
          const typedParams = validationResult.data; // Use validated data

          // Create a new context for this specific tool execution
          const callContext = requestContextService.createRequestContext({ operation: 'UpdateFileToolExecution' });
          logger.info(`Executing 'update_file' tool for path: ${typedParams.path} with ${typedParams.blocks.length} blocks`, callContext);

          // ErrorHandler will catch McpErrors thrown by the logic
          const result = await ErrorHandler.tryCatch(
            () => updateFileLogic(typedParams, callContext),
            {
              operation: 'updateFileLogic',
              context: callContext,
              // Sanitize input for logging: keep path, redact block content
              input: sanitization.sanitizeForLogging({
                  path: typedParams.path,
                  blocks: typedParams.blocks.map((_, index) => `[Block ${index + 1} REDACTED]`), // Redact block details
                  useRegex: typedParams.useRegex,
                  replaceAll: typedParams.replaceAll,
              }),
              errorCode: BaseErrorCode.INTERNAL_ERROR
            }
          );

          logger.info(`Successfully executed 'update_file' for path: ${result.updatedPath}. Blocks Applied: ${result.blocksApplied}, Failed: ${result.blocksFailed}`, callContext);

          // Format the successful response
          return {
            content: [{ type: 'text', text: result.message }],
          };
        }
      );
      logger.info("'update_file' tool registered successfully with JSON input format", registrationContext);
    },
    {
      operation: 'registerUpdateFileTool',
      context: registrationContext,
      errorCode: BaseErrorCode.CONFIGURATION_ERROR,
      critical: true
    }
  );
};

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/createDirectory/createDirectoryLogic.ts:
--------------------------------------------------------------------------------

```typescript
import fs from 'fs/promises';
import { z } from 'zod';
import { BaseErrorCode, McpError } from '../../../types-global/errors.js';
import { logger } from '../../../utils/internal/logger.js';
import { RequestContext } from '../../../utils/internal/requestContext.js';
import { serverState } from '../../state.js';

// Define the input schema using Zod for validation
export const CreateDirectoryInputSchema = z.object({
  path: z.string().min(1, 'Path cannot be empty')
    .describe('The path to the directory to create. Can be relative or absolute.'),
  create_parents: z.boolean().default(true)
    .describe('If true, create any necessary parent directories that don\'t exist. If false, fail if a parent directory is missing.'),
});

// Define the TypeScript type for the input
export type CreateDirectoryInput = z.infer<typeof CreateDirectoryInputSchema>;

// Define the TypeScript type for the output
export interface CreateDirectoryOutput {
  message: string;
  createdPath: string;
  parentsCreated: boolean; // Indicate if parent directories were also created
}

/**
 * Creates a specified directory, optionally creating parent directories.
 *
 * @param {CreateDirectoryInput} input - The input object containing path and create_parents flag.
 * @param {RequestContext} context - The request context.
 * @returns {Promise<CreateDirectoryOutput>} A promise resolving with the creation status.
 * @throws {McpError} For path errors, if the path already exists and is not a directory, or I/O errors.
 */
export const createDirectoryLogic = async (input: CreateDirectoryInput, context: RequestContext): Promise<CreateDirectoryOutput> => {
  const { path: requestedPath, create_parents } = input;
  const logicContext = { ...context, tool: 'createDirectoryLogic', create_parents };
  logger.debug(`createDirectoryLogic: Received request to create directory "${requestedPath}"`, logicContext);

  // Resolve the path
  const absolutePath = serverState.resolvePath(requestedPath, context);
  logger.debug(`createDirectoryLogic: Resolved path to "${absolutePath}"`, { ...logicContext, requestedPath });

  try {
    // Check if path already exists
    try {
      const stats = await fs.stat(absolutePath);
      if (stats.isDirectory()) {
        logger.info(`createDirectoryLogic: Directory already exists at "${absolutePath}"`, { ...logicContext, requestedPath });
        // Directory already exists, consider this a success (idempotent)
        return {
          message: `Directory already exists: ${absolutePath}`,
          createdPath: absolutePath,
          parentsCreated: false, // No parents needed to be created now
        };
      } else {
        // Path exists but is not a directory (e.g., a file)
        logger.error(`createDirectoryLogic: Path exists but is not a directory "${absolutePath}"`, { ...logicContext, requestedPath });
        throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Path already exists but is not a directory: ${absolutePath}`, { ...logicContext, requestedPath, resolvedPath: absolutePath });
      }
    } catch (statError: any) {
      if (statError.code !== 'ENOENT') {
        // If error is something other than "Not Found", re-throw it
        throw statError;
      }
      // Path does not exist, proceed with creation
      logger.debug(`createDirectoryLogic: Path does not exist, proceeding with creation "${absolutePath}"`, { ...logicContext, requestedPath });
    }

    // Attempt to create the directory
    await fs.mkdir(absolutePath, { recursive: create_parents });
    logger.info(`createDirectoryLogic: Successfully created directory "${absolutePath}" (parents: ${create_parents})`, { ...logicContext, requestedPath });

    return {
      message: `Successfully created directory: ${absolutePath}${create_parents ? ' (including parents if needed)' : ''}`,
      createdPath: absolutePath,
      parentsCreated: create_parents, // Reflects the *option* enabled, not necessarily if they *were* created
    };

  } catch (error: any) {
    logger.error(`createDirectoryLogic: Error creating directory "${absolutePath}"`, { ...logicContext, requestedPath, error: error.message, code: error.code });

    if (error instanceof McpError) {
      throw error; // Re-throw known McpErrors
    }

    // Handle potential I/O errors (e.g., permissions, invalid path components)
    throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to create directory: ${error.message || 'Unknown I/O error'}`, { ...logicContext, requestedPath, resolvedPath: absolutePath, originalError: error });
  }
};

```

--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------

```markdown
# Changelog

All notable changes to this project will be documented in this file.

## [1.0.4] - 2025-05-23

### Changed
- **Configuration**: `FS_BASE_DIRECTORY` can now be set as a path relative to the project root (e.g., `./data_sandbox`) in addition to an absolute path. The server will resolve relative paths to an absolute path. If the directory doesn't exist, it will be created. This feature remains optional, with a warning logged if `FS_BASE_DIRECTORY` is not set.
  - Updated `src/config/index.ts` to handle relative path resolution for `FS_BASE_DIRECTORY`.
  - Updated `README.md` to reflect this new flexibility.

## [1.0.3] - 2025-05-23

### Added

- **Filesystem Access Control**: Introduced `FS_BASE_DIRECTORY` environment variable. If set to an absolute path, all filesystem tool operations are restricted to this directory and its subdirectories, enhancing security by preventing unintended access to other parts of the filesystem.
  - Configuration (`src/config/index.ts`) updated to include `FS_BASE_DIRECTORY`, with validation ensuring it's an absolute path if provided.
  - Server state (`src/mcp-server/state.ts`) now initializes and enforces this base directory. The `resolvePath` method checks if the resolved path is within the `FS_BASE_DIRECTORY` and throws a `FORBIDDEN` error if it's outside.
  - Dockerfile updated to include `FS_BASE_DIRECTORY` as a build argument and environment variable.
  - `mcp.json` and `smithery.yaml` updated to include `FS_BASE_DIRECTORY`.

### Changed

- **Version Bump**: Project version updated to `1.0.3` in `package.json`, `README.md`.
- **Documentation**:
  - `README.md` updated to document the new `FS_BASE_DIRECTORY` feature.
  - `docs/tree.md` updated to reflect the inclusion of `CHANGELOG.md` (though this was part of the diff, it's a documentation update).

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.2] - 2025-05-23

### Added

- **HTTP Transport Support**: Introduced an HTTP transport layer (`src/mcp-server/transports/httpTransport.ts`) alongside the existing STDIO transport. This allows the server to be accessed over the network.
  - Includes JWT-based authentication (`src/mcp-server/transports/authentication/authMiddleware.ts`) for secure HTTP communication.
  - Supports RESTful endpoints and Server-Sent Events (SSE) for streaming.
- **Enhanced Configuration System**: Major overhaul of `src/config/index.ts` using Zod for robust validation of environment variables.
  - Added new configuration options for HTTP transport (port, host, allowed origins, auth secret key).
  - Added new configuration options for LLM integrations (OpenRouter, Gemini API keys, default model parameters).
  - Added new configuration options for OAuth Proxy integration.
- **New Dependencies**: Added `express`, `jsonwebtoken`, `chrono-node`, `openai`, `partial-json`, `tiktoken` to support new features.
- **Untracked Files Added**: `mcp.json` and `smithery.yaml` are now part of the project.

### Changed

- **Utils Refactoring**: Major refactoring of the `src/utils/` directory. Utilities are now organized into subdirectories:
  - `src/utils/internal/` (errorHandler, logger, requestContext)
  - `src/utils/security/` (idGenerator, rateLimiter, sanitization)
  - `src/utils/metrics/` (tokenCounter)
  - `src/utils/parsing/` (dateParser, jsonParser)
- **Project Version**: Bumped version in `package.json` and `package-lock.json` to `1.0.1`.
- **Documentation**:
  - Updated `README.md` to reflect new features, architecture changes (transports, utils structure), and new configuration options.
  - Updated `.clinerules` (developer cheatsheet) with the new project structure and utility usage.
  - Updated `docs/tree.md` to reflect the new directory structure.
- **Tool Registration**: Minor updates in tool registration files to align with refactored utility paths and error handling.
- **Server Initialization**: Modified `src/index.ts` and `src/mcp-server/server.ts` to accommodate the new transport layer and configuration system.

### Removed

- Old top-level utility files from `src/utils/` (e.g., `errorHandler.ts`, `logger.ts`) have been moved into the new categorized subdirectories.

## [1.0.0] - Initial Release Date

- Initial release of the Filesystem MCP Server.
- Core filesystem tools: `set_filesystem_default`, `read_file`, `write_file`, `update_file`, `list_files`, `delete_file`, `delete_directory`, `create_directory`, `move_path`, `copy_path`.
- STDIO transport.
- Basic logging, error handling, and sanitization utilities.

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/writeFile/writeFileLogic.ts:
--------------------------------------------------------------------------------

```typescript
import fs from 'fs/promises';
import path from 'path';
import { z } from 'zod';
import { BaseErrorCode, McpError } from '../../../types-global/errors.js';
import { logger } from '../../../utils/internal/logger.js';
import { RequestContext } from '../../../utils/internal/requestContext.js';
import { serverState } from '../../state.js'; // Import serverState for path resolution

// Define the input schema using Zod for validation
export const WriteFileInputSchema = z.object({
  path: z.string().min(1, 'Path cannot be empty')
    .describe('The path to the file to write. Can be relative or absolute. If relative, it resolves against the path set by `set_filesystem_default`. If absolute, it is used directly. Missing directories will be created.'),
  content: z.string() // Allow empty content
    .describe('The content to write to the file. If the file exists, it will be overwritten.'),
});

// Define the TypeScript type for the input
export type WriteFileInput = z.infer<typeof WriteFileInputSchema>;

// Define the TypeScript type for the output (simple success message)
export interface WriteFileOutput {
  message: string;
  writtenPath: string;
  bytesWritten: number;
}

/**
 * Writes content to a specified file, overwriting it if it exists,
 * and creating necessary directories.
 *
 * @param {WriteFileInput} input - The input object containing the file path and content.
 * @param {RequestContext} context - The request context for logging and error handling.
 * @returns {Promise<WriteFileOutput>} A promise that resolves with a success message, the path written to, and bytes written.
 * @throws {McpError} Throws McpError for path resolution errors, I/O errors, or if the path resolves to a directory.
 */
export const writeFileLogic = async (input: WriteFileInput, context: RequestContext): Promise<WriteFileOutput> => {
  const { path: requestedPath, content } = input;
  logger.debug(`writeFileLogic: Received request for path "${requestedPath}"`, context);

  // Resolve the path using serverState (handles relative/absolute logic and sanitization)
  const absolutePath = serverState.resolvePath(requestedPath, context);
  logger.debug(`writeFileLogic: Resolved path to "${absolutePath}"`, { ...context, requestedPath });

  try {
    // Ensure the target path is not a directory before attempting to write
    try {
      const stats = await fs.stat(absolutePath);
      if (stats.isDirectory()) {
        logger.warning(`writeFileLogic: Attempted to write to a directory path "${absolutePath}"`, { ...context, requestedPath });
        throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Cannot write file. Path exists and is a directory: ${absolutePath}`, { ...context, requestedPath, resolvedPath: absolutePath });
      }
    } catch (statError: any) {
      // ENOENT (file/dir doesn't exist) is expected and okay, we'll create it.
      // Other errors during stat (like permission issues) should be thrown.
      if (statError.code !== 'ENOENT') {
        throw statError; // Re-throw other stat errors
      }
      // If ENOENT, proceed to create directory and file
      logger.debug(`writeFileLogic: Path "${absolutePath}" does not exist, will create.`, { ...context, requestedPath });
    }

    // Ensure the directory exists before writing the file
    const dirName = path.dirname(absolutePath);
    logger.debug(`writeFileLogic: Ensuring directory "${dirName}" exists`, { ...context, requestedPath, resolvedPath: absolutePath });
    await fs.mkdir(dirName, { recursive: true });
    logger.debug(`writeFileLogic: Directory "${dirName}" confirmed/created`, { ...context, requestedPath, resolvedPath: absolutePath });

    // Write the file content
    logger.debug(`writeFileLogic: Writing content to "${absolutePath}"`, { ...context, requestedPath });
    await fs.writeFile(absolutePath, content, 'utf8');
    const bytesWritten = Buffer.byteLength(content, 'utf8');
    logger.info(`writeFileLogic: Successfully wrote ${bytesWritten} bytes to "${absolutePath}"`, { ...context, requestedPath });

    return {
      message: `Successfully wrote content to ${absolutePath}`,
      writtenPath: absolutePath,
      bytesWritten: bytesWritten,
    };
  } catch (error: any) {
     logger.error(`writeFileLogic: Error writing file to "${absolutePath}"`, { ...context, requestedPath, error: error.message, code: error.code });
    // Handle specific file system errors
    if (error instanceof McpError) {
        throw error; // Re-throw McpErrors (like the directory check)
    }
    // Handle potential I/O errors during mkdir or writeFile
    throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to write file: ${error.message || 'Unknown I/O error'}`, { ...context, requestedPath, resolvedPath: absolutePath, originalError: error });
  }
};

```

--------------------------------------------------------------------------------
/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, McpError } 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
 */
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.VALIDATION_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
 */
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.VALIDATION_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, McpError } 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.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/tools/deleteDirectory/deleteDirectoryLogic.ts:
--------------------------------------------------------------------------------

```typescript
import fs from 'fs/promises';
import { z } from 'zod';
import { BaseErrorCode, McpError } from '../../../types-global/errors.js';
import { logger } from '../../../utils/internal/logger.js';
import { RequestContext } from '../../../utils/internal/requestContext.js';
import { serverState } from '../../state.js';

// Define the input schema using Zod for validation
export const DeleteDirectoryInputSchema = z.object({
  path: z.string().min(1, 'Path cannot be empty')
    .describe('The path to the directory to delete. Can be relative or absolute.'),
  recursive: z.boolean().default(false)
    .describe('If true, delete the directory and all its contents. If false, only delete if the directory is empty.'),
});

// Define the TypeScript type for the input
export type DeleteDirectoryInput = z.infer<typeof DeleteDirectoryInputSchema>;

// Define the TypeScript type for the output
export interface DeleteDirectoryOutput {
  message: string;
  deletedPath: string;
  wasRecursive: boolean;
}

/**
 * Deletes a specified directory, optionally recursively.
 *
 * @param {DeleteDirectoryInput} input - The input object containing path and recursive flag.
 * @param {RequestContext} context - The request context.
 * @returns {Promise<DeleteDirectoryOutput>} A promise resolving with the deletion status.
 * @throws {McpError} For path errors, directory not found, not a directory, directory not empty (if not recursive), or I/O errors.
 */
export const deleteDirectoryLogic = async (input: DeleteDirectoryInput, context: RequestContext): Promise<DeleteDirectoryOutput> => {
  const { path: requestedPath, recursive } = input;
  const logicContext = { ...context, tool: 'deleteDirectoryLogic', recursive };
  logger.debug(`deleteDirectoryLogic: Received request to delete directory "${requestedPath}"`, logicContext);

  // Resolve the path
  const absolutePath = serverState.resolvePath(requestedPath, context);
  logger.debug(`deleteDirectoryLogic: Resolved path to "${absolutePath}"`, { ...logicContext, requestedPath });

  try {
    // Check if the path exists and is a directory before attempting deletion
    let stats;
    try {
      stats = await fs.stat(absolutePath);
    } catch (statError: any) {
      if (statError.code === 'ENOENT') {
        logger.warning(`deleteDirectoryLogic: Directory not found at "${absolutePath}"`, { ...logicContext, requestedPath });
        throw new McpError(BaseErrorCode.NOT_FOUND, `Directory not found at path: ${absolutePath}`, { ...logicContext, requestedPath, resolvedPath: absolutePath, originalError: statError });
      }
      throw statError; // Re-throw other stat errors
    }

    if (!stats.isDirectory()) {
      logger.warning(`deleteDirectoryLogic: Path is not a directory "${absolutePath}"`, { ...logicContext, requestedPath });
      throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Path is not a directory: ${absolutePath}`, { ...logicContext, requestedPath, resolvedPath: absolutePath });
    }

    // Attempt to delete the directory
    if (recursive) {
      // Use fs.rm for recursive deletion (available in Node.js 14.14.0+)
      await fs.rm(absolutePath, { recursive: true, force: true }); // force helps with potential permission issues on subfiles sometimes
      logger.info(`deleteDirectoryLogic: Successfully deleted directory recursively "${absolutePath}"`, { ...logicContext, requestedPath });
    } else {
      // Use fs.rmdir for non-recursive deletion (fails if not empty)
      await fs.rmdir(absolutePath);
      logger.info(`deleteDirectoryLogic: Successfully deleted empty directory "${absolutePath}"`, { ...logicContext, requestedPath });
    }

    return {
      message: `Successfully deleted directory: ${absolutePath}${recursive ? ' (recursively)' : ' (empty)'}`,
      deletedPath: absolutePath,
      wasRecursive: recursive,
    };

  } catch (error: any) {
    logger.error(`deleteDirectoryLogic: Error deleting directory "${absolutePath}"`, { ...logicContext, requestedPath, error: error.message, code: error.code });

    if (error instanceof McpError) {
      throw error; // Re-throw known McpErrors
    }

    if (error.code === 'ENOENT') {
      // Should have been caught by stat, but handle defensively
      logger.warning(`deleteDirectoryLogic: Directory not found during delete operation "${absolutePath}"`, { ...logicContext, requestedPath });
      throw new McpError(BaseErrorCode.NOT_FOUND, `Directory not found at path: ${absolutePath}`, { ...logicContext, requestedPath, resolvedPath: absolutePath, originalError: error });
    }

    if (error.code === 'ENOTEMPTY' && !recursive) {
      logger.warning(`deleteDirectoryLogic: Directory not empty and recursive=false "${absolutePath}"`, { ...logicContext, requestedPath });
      throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Directory not empty: ${absolutePath}. Use recursive=true to delete non-empty directories.`, { ...logicContext, requestedPath, resolvedPath: absolutePath, originalError: error });
    }

    // Handle other potential I/O errors (e.g., permissions)
    throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to delete directory: ${error.message || 'Unknown I/O error'}`, { ...logicContext, requestedPath, resolvedPath: absolutePath, originalError: error });
  }
};

```

--------------------------------------------------------------------------------
/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 = any>(
    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 (error: any) {
      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/index.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node
import { config, environment } from "./config/index.js";
import { initializeAndStartServer } from "./mcp-server/server.js"; // Changed import
import { BaseErrorCode } from "./types-global/errors.js";
import { ErrorHandler } from "./utils/internal/errorHandler.js";
import { logger } from "./utils/internal/logger.js";
// Import the service instance instead of the standalone function
import { requestContextService } from "./utils/internal/requestContext.js";

// Define a type alias for the server instance for better readability
// initializeAndStartServer returns Promise<void | McpServer>, we'll handle the potential void/undefined later if needed
// For now, assuming it returns McpServer as per current stdio-only logic
type McpServerInstance = Awaited<ReturnType<typeof initializeAndStartServer>>;

/**
 * The main MCP server instance.
 * @type {McpServerInstance | undefined}
 */
let server: McpServerInstance | undefined;

/**
 * Gracefully shuts down the main MCP server.
 * Handles process termination signals (SIGTERM, SIGINT) and critical errors.
 *
 * @param signal - The signal or event name that triggered the shutdown (e.g., "SIGTERM", "uncaughtException").
 */
const shutdown = async (signal: string) => {
  // Define context for the shutdown operation
  const shutdownContext = requestContextService.createRequestContext({
    operation: 'Shutdown',
    signal,
  });

  logger.info(`Received ${signal}. Starting graceful shutdown...`, shutdownContext);

  try {
    // Close the main MCP server
    if (server) {
      logger.info("Closing main MCP server...", shutdownContext);
      await server.close();
      logger.info("Main MCP server closed successfully", shutdownContext);
    } else {
      // Based on the previous error, logger.warn does not exist. Let's use logger.info for now.
      logger.warning("Server instance not found during shutdown.", shutdownContext);
    }

    logger.info("Graceful shutdown completed successfully", shutdownContext);
    process.exit(0);
  } catch (error) {
    // Handle any errors during shutdown
    const errorLogContext = requestContextService.createRequestContext({
      ...shutdownContext, // Spread existing RequestContext properties
      error: error instanceof Error ? error.message : String(error),
      stack: error instanceof Error ? error.stack : undefined
    });
    logger.error("Critical error during shutdown", errorLogContext);
    process.exit(1); // Exit with error code if shutdown fails
  }
};

/**
 * Initializes and starts the main MCP server.
 * Sets up request context, creates the server instance, and registers signal handlers
 * for graceful shutdown and error handling.
 */
const start = async () => {
  // Create application-level request context using the service instance
  const startupContext = requestContextService.createRequestContext({
    operation: 'ServerStartup',
    appName: config.mcpServerName,
    appVersion: config.mcpServerVersion,
    environment: environment // Use imported environment
  });

  logger.info(`Starting ${config.mcpServerName} v${config.mcpServerVersion}...`, startupContext);

  try {
    // Create and store the main server instance
    logger.debug("Creating main MCP server instance", startupContext);
    // Use ErrorHandler to wrap the server creation, ensuring errors are caught and logged
    server = await ErrorHandler.tryCatch(
      async () => await initializeAndStartServer(), // Changed function call
      {
        operation: 'creating main MCP server',
        context: startupContext, // Pass the established startup context
        errorCode: BaseErrorCode.INTERNAL_ERROR // Specify error code for failure
      }
    );

    // If tryCatch encountered an error, it would have thrown,
    // and execution would jump to the outer catch block.

    logger.info(`${config.mcpServerName} is running and awaiting messages`, {
      ...startupContext,
      startTime: new Date().toISOString(),
    });

    // --- Signal and Error Handling Setup ---

    // Handle process signals for graceful shutdown
    process.on("SIGTERM", () => shutdown("SIGTERM"));
    process.on("SIGINT", () => shutdown("SIGINT"));

    // Handle uncaught exceptions
    process.on("uncaughtException", async (error) => {
      const errorContext = requestContextService.createRequestContext({
        ...startupContext, // Include base context for correlation
        event: 'uncaughtException',
        error: error instanceof Error ? error.message : String(error),
        stack: error instanceof Error ? error.stack : undefined
      });
      logger.error("Uncaught exception detected. Initiating shutdown...", errorContext);
      // Attempt graceful shutdown; shutdown() handles its own errors.
      await shutdown("uncaughtException");
      // If shutdown fails internally, it will call process.exit(1).
      // If shutdown succeeds, it calls process.exit(0).
      // If shutdown itself throws unexpectedly *before* exiting, this process might terminate abruptly,
      // but the core shutdown logic is handled within shutdown().
    });

    // Handle unhandled promise rejections
    process.on("unhandledRejection", async (reason: unknown) => {
      const rejectionContext = requestContextService.createRequestContext({
        ...startupContext, // Include base context for correlation
        event: 'unhandledRejection',
        reason: reason instanceof Error ? reason.message : String(reason),
        stack: reason instanceof Error ? reason.stack : undefined
      });
      logger.error("Unhandled promise rejection detected. Initiating shutdown...", rejectionContext);
      // Attempt graceful shutdown; shutdown() handles its own errors.
      await shutdown("unhandledRejection");
      // Similar logic as uncaughtException: shutdown handles its exit codes.
    });
  } catch (error) {
    // Handle critical startup errors (already logged by ErrorHandler or caught above)
    // Log the final failure context, including error details, before exiting
    const finalErrorContext = requestContextService.createRequestContext({
      ...startupContext,
      finalErrorContext: 'Startup Failure',
      error: error instanceof Error ? error.message : String(error),
      stack: error instanceof Error ? error.stack : undefined,
    });
    logger.error("Critical error during startup, exiting.", finalErrorContext);
    process.exit(1);
  }
};

// Start the application
start();

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/movePath/movePathLogic.ts:
--------------------------------------------------------------------------------

```typescript
import fs from 'fs/promises';
import path from 'path';
import { z } from 'zod';
import { BaseErrorCode, McpError } from '../../../types-global/errors.js';
import { logger } from '../../../utils/internal/logger.js';
import { RequestContext } from '../../../utils/internal/requestContext.js';
import { serverState } from '../../state.js';

// Define the input schema using Zod for validation
export const MovePathInputSchema = z.object({
  source_path: z.string().min(1, 'Source path cannot be empty')
    .describe('The current path of the file or directory to move. Can be relative or absolute.'),
  destination_path: z.string().min(1, 'Destination path cannot be empty')
    .describe('The new path for the file or directory. Can be relative or absolute.'),
});

// Define the TypeScript type for the input
export type MovePathInput = z.infer<typeof MovePathInputSchema>;

// Define the TypeScript type for the output
export interface MovePathOutput {
  message: string;
  sourcePath: string;
  destinationPath: string;
}

/**
 * Moves or renames a file or directory.
 *
 * @param {MovePathInput} input - The input object containing source and destination paths.
 * @param {RequestContext} context - The request context.
 * @returns {Promise<MovePathOutput>} A promise resolving with the move status.
 * @throws {McpError} For path errors, source not found, destination already exists (depending on OS/FS behavior), or I/O errors.
 */
export const movePathLogic = async (input: MovePathInput, context: RequestContext): Promise<MovePathOutput> => {
  const { source_path: requestedSourcePath, destination_path: requestedDestPath } = input;
  const logicContext = { ...context, tool: 'movePathLogic' };
  logger.debug(`movePathLogic: Received request to move "${requestedSourcePath}" to "${requestedDestPath}"`, logicContext);

  // Resolve source and destination paths
  const absoluteSourcePath = serverState.resolvePath(requestedSourcePath, context);
  const absoluteDestPath = serverState.resolvePath(requestedDestPath, context);
  logger.debug(`movePathLogic: Resolved source to "${absoluteSourcePath}", destination to "${absoluteDestPath}"`, { ...logicContext, requestedSourcePath, requestedDestPath });

  // Basic check: source and destination cannot be the same
  if (absoluteSourcePath === absoluteDestPath) {
      logger.warning(`movePathLogic: Source and destination paths are identical "${absoluteSourcePath}"`, logicContext);
      throw new McpError(BaseErrorCode.VALIDATION_ERROR, 'Source and destination paths cannot be the same.', { ...logicContext, absoluteSourcePath, absoluteDestPath });
  }

  try {
    // 1. Check if source exists
    try {
      await fs.access(absoluteSourcePath); // Check existence and permissions
      logger.debug(`movePathLogic: Source path "${absoluteSourcePath}" exists and is accessible`, logicContext);
    } catch (accessError: any) {
      if (accessError.code === 'ENOENT') {
        logger.warning(`movePathLogic: Source path not found "${absoluteSourcePath}"`, logicContext);
        throw new McpError(BaseErrorCode.NOT_FOUND, `Source path not found: ${absoluteSourcePath}`, { ...logicContext, requestedSourcePath, absoluteSourcePath, originalError: accessError });
      }
      // Other access errors (e.g., permissions)
      logger.error(`movePathLogic: Cannot access source path "${absoluteSourcePath}"`, { ...logicContext, error: accessError.message });
      throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Cannot access source path: ${accessError.message}`, { ...logicContext, requestedSourcePath, absoluteSourcePath, originalError: accessError });
    }

    // 2. Check if destination *parent* directory exists
    const destDir = path.dirname(absoluteDestPath);
    try {
        await fs.access(destDir);
        logger.debug(`movePathLogic: Destination parent directory "${destDir}" exists`, logicContext);
    } catch (parentAccessError: any) {
        logger.error(`movePathLogic: Destination parent directory does not exist or is inaccessible "${destDir}"`, { ...logicContext, error: parentAccessError.message });
        throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Destination directory does not exist or is inaccessible: ${destDir}`, { ...logicContext, requestedDestPath, absoluteDestPath, destDir, originalError: parentAccessError });
    }

    // 3. Check if destination path already exists (fs.rename behavior varies, so check explicitly)
    try {
        await fs.access(absoluteDestPath);
        // If access succeeds, the destination exists. Throw an error.
        logger.warning(`movePathLogic: Destination path already exists "${absoluteDestPath}"`, logicContext);
        throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Destination path already exists: ${absoluteDestPath}. Cannot overwrite.`, { ...logicContext, requestedDestPath, absoluteDestPath });
    } catch (destAccessError: any) {
        if (destAccessError.code !== 'ENOENT') {
            // If error is something other than "Not Found", it's an unexpected issue.
            logger.error(`movePathLogic: Error checking destination path "${absoluteDestPath}"`, { ...logicContext, error: destAccessError.message });
            throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Error checking destination path: ${destAccessError.message}`, { ...logicContext, requestedDestPath, absoluteDestPath, originalError: destAccessError });
        }
        // ENOENT means destination does not exist, which is good. Proceed.
        logger.debug(`movePathLogic: Destination path "${absoluteDestPath}" does not exist, proceeding with move`, logicContext);
    }


    // 4. Attempt to move/rename
    await fs.rename(absoluteSourcePath, absoluteDestPath);
    logger.info(`movePathLogic: Successfully moved "${absoluteSourcePath}" to "${absoluteDestPath}"`, logicContext);

    return {
      message: `Successfully moved ${absoluteSourcePath} to ${absoluteDestPath}`,
      sourcePath: absoluteSourcePath,
      destinationPath: absoluteDestPath,
    };

  } catch (error: any) {
    logger.error(`movePathLogic: Error moving path "${absoluteSourcePath}" to "${absoluteDestPath}"`, { ...logicContext, error: error.message, code: error.code });

    if (error instanceof McpError) {
      throw error; // Re-throw known McpErrors (like source not found, dest exists)
    }

    // Handle other potential I/O errors (e.g., permissions, cross-device link)
    throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to move path: ${error.message || 'Unknown I/O error'}`, { ...logicContext, absoluteSourcePath, absoluteDestPath, originalError: error });
  }
};

```

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

```typescript
/**
 * @fileoverview Provides a generic `RateLimiter` class for implementing rate limiting logic.
 * It supports configurable time windows, request limits, and automatic cleanup of expired entries.
 * @module src/utils/security/rateLimiter
 */
import { environment } from "../../config/index.js";
import { BaseErrorCode, McpError } from "../../types-global/errors.js";
import { logger, RequestContext, requestContextService } from "../index.js";

/**
 * Defines configuration options for the {@link RateLimiter}.
 */
export interface RateLimitConfig {
  /** Time window in milliseconds. */
  windowMs: number;
  /** Maximum number of requests allowed in the window. */
  maxRequests: number;
  /** Custom error message template. Can include `{waitTime}` placeholder. */
  errorMessage?: string;
  /** If true, skip rate limiting in development. */
  skipInDevelopment?: boolean;
  /** Optional function to generate a custom key for rate limiting. */
  keyGenerator?: (identifier: string, context?: RequestContext) => string;
  /** How often, in milliseconds, to clean up expired entries. */
  cleanupInterval?: number;
}

/**
 * Represents an individual entry for tracking requests against a rate limit key.
 */
export interface RateLimitEntry {
  /** Current request count. */
  count: number;
  /** When the window resets (timestamp in milliseconds). */
  resetTime: number;
}

/**
 * A generic rate limiter class using an in-memory store.
 * Controls frequency of operations based on unique keys.
 */
export class RateLimiter {
  /**
   * Stores current request counts and reset times for each key.
   * @private
   */
  private limits: Map<string, RateLimitEntry>;
  /**
   * Timer ID for periodic cleanup.
   * @private
   */
  private cleanupTimer: NodeJS.Timeout | null = null;

  /**
   * Default configuration values.
   * @private
   */
  private static DEFAULT_CONFIG: RateLimitConfig = {
    windowMs: 15 * 60 * 1000, // 15 minutes
    maxRequests: 100,
    errorMessage:
      "Rate limit exceeded. Please try again in {waitTime} seconds.",
    skipInDevelopment: false,
    cleanupInterval: 5 * 60 * 1000, // 5 minutes
  };

  /**
   * Creates a new `RateLimiter` instance.
   * @param config - Configuration options, merged with defaults.
   */
  constructor(private config: RateLimitConfig) {
    this.config = { ...RateLimiter.DEFAULT_CONFIG, ...config };
    this.limits = new Map();
    this.startCleanupTimer();
  }

  /**
   * Starts the periodic timer to clean up expired rate limit entries.
   * @private
   */
  private startCleanupTimer(): void {
    if (this.cleanupTimer) {
      clearInterval(this.cleanupTimer);
    }

    const interval =
      this.config.cleanupInterval ?? RateLimiter.DEFAULT_CONFIG.cleanupInterval;

    if (interval && interval > 0) {
      this.cleanupTimer = setInterval(() => {
        this.cleanupExpiredEntries();
      }, interval);

      if (this.cleanupTimer.unref) {
        this.cleanupTimer.unref(); // Allow Node.js process to exit if only timer active
      }
    }
  }

  /**
   * Removes expired rate limit entries from the store.
   * @private
   */
  private cleanupExpiredEntries(): void {
    const now = Date.now();
    let expiredCount = 0;

    for (const [key, entry] of this.limits.entries()) {
      if (now >= entry.resetTime) {
        this.limits.delete(key);
        expiredCount++;
      }
    }

    if (expiredCount > 0) {
      const logContext = requestContextService.createRequestContext({
        operation: "RateLimiter.cleanupExpiredEntries",
        cleanedCount: expiredCount,
        totalRemainingAfterClean: this.limits.size,
      });
      logger.debug(
        `Cleaned up ${expiredCount} expired rate limit entries`,
        logContext,
      );
    }
  }

  /**
   * Updates the configuration of the rate limiter instance.
   * @param config - New configuration options to merge.
   */
  public configure(config: Partial<RateLimitConfig>): void {
    this.config = { ...this.config, ...config };
    if (config.cleanupInterval !== undefined) {
      this.startCleanupTimer();
    }
  }

  /**
   * Retrieves a copy of the current rate limiter configuration.
   * @returns The current configuration.
   */
  public getConfig(): RateLimitConfig {
    return { ...this.config };
  }

  /**
   * Resets all rate limits by clearing the internal store.
   */
  public reset(): void {
    this.limits.clear();
    const logContext = requestContextService.createRequestContext({
      operation: "RateLimiter.reset",
    });
    logger.debug("Rate limiter reset, all limits cleared", logContext);
  }

  /**
   * Checks if a request exceeds the configured rate limit.
   * Throws an `McpError` if the limit is exceeded.
   *
   * @param key - A unique identifier for the request source.
   * @param context - Optional request context for custom key generation.
   * @throws {McpError} If the rate limit is exceeded.
   */
  public check(key: string, context?: RequestContext): void {
    if (this.config.skipInDevelopment && environment === "development") {
      return;
    }

    const limitKey = this.config.keyGenerator
      ? this.config.keyGenerator(key, context)
      : key;

    const now = Date.now();
    const entry = this.limits.get(limitKey);

    if (!entry || now >= entry.resetTime) {
      this.limits.set(limitKey, {
        count: 1,
        resetTime: now + this.config.windowMs,
      });
      return;
    }

    if (entry.count >= this.config.maxRequests) {
      const waitTime = Math.ceil((entry.resetTime - now) / 1000);
      const errorMessage = (
        this.config.errorMessage || RateLimiter.DEFAULT_CONFIG.errorMessage!
      ).replace("{waitTime}", waitTime.toString());

      throw new McpError(BaseErrorCode.RATE_LIMITED, errorMessage, {
        waitTimeSeconds: waitTime,
        key: limitKey,
        limit: this.config.maxRequests,
        windowMs: this.config.windowMs,
      });
    }

    entry.count++;
  }

  /**
   * Retrieves the current rate limit status for a specific key.
   * @param key - The rate limit key.
   * @returns Status object or `null` if no entry exists.
   */
  public getStatus(key: string): {
    current: number;
    limit: number;
    remaining: number;
    resetTime: number;
  } | null {
    const entry = this.limits.get(key);
    if (!entry) {
      return null;
    }
    return {
      current: entry.count,
      limit: this.config.maxRequests,
      remaining: Math.max(0, this.config.maxRequests - entry.count),
      resetTime: entry.resetTime,
    };
  }

  /**
   * Stops the cleanup timer and clears all rate limit entries.
   * Call when the rate limiter is no longer needed.
   */
  public dispose(): void {
    if (this.cleanupTimer) {
      clearInterval(this.cleanupTimer);
      this.cleanupTimer = null;
    }
    this.limits.clear();
  }
}

/**
 * Default singleton instance of the `RateLimiter`.
 * Initialized with default configuration. Use `rateLimiter.configure({})` to customize.
 */
export const rateLimiter = new RateLimiter({
  windowMs: 15 * 60 * 1000, // Default: 15 minutes
  maxRequests: 100, // Default: 100 requests per window
});

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/copyPath/copyPathLogic.ts:
--------------------------------------------------------------------------------

```typescript
import fs from 'fs/promises';
import path from 'path';
import { z } from 'zod';
import { BaseErrorCode, McpError } from '../../../types-global/errors.js';
import { logger } from '../../../utils/internal/logger.js';
import { RequestContext } from '../../../utils/internal/requestContext.js';
import { serverState } from '../../state.js';

// Define the input schema using Zod for validation
export const CopyPathInputSchema = z.object({
  source_path: z.string().min(1, 'Source path cannot be empty')
    .describe('The path of the file or directory to copy. Can be relative or absolute.'),
  destination_path: z.string().min(1, 'Destination path cannot be empty')
    .describe('The path where the copy should be created. Can be relative or absolute.'),
  recursive: z.boolean().default(true) // Defaulting to true as it's the common expectation for directory copies
    .describe('If copying a directory, whether to copy its contents recursively. Defaults to true.'),
});

// Define the TypeScript type for the input
export type CopyPathInput = z.infer<typeof CopyPathInputSchema>;

// Define the TypeScript type for the output
export interface CopyPathOutput {
  message: string;
  sourcePath: string;
  destinationPath: string;
  wasRecursive: boolean | null; // null if source was a file
}

/**
 * Copies a file or directory to a new location.
 *
 * @param {CopyPathInput} input - The input object containing source, destination, and recursive flag.
 * @param {RequestContext} context - The request context.
 * @returns {Promise<CopyPathOutput>} A promise resolving with the copy status.
 * @throws {McpError} For path errors, source not found, destination already exists, or I/O errors.
 */
export const copyPathLogic = async (input: CopyPathInput, context: RequestContext): Promise<CopyPathOutput> => {
  const { source_path: requestedSourcePath, destination_path: requestedDestPath, recursive } = input;
  const logicContext = { ...context, tool: 'copyPathLogic', recursive };
  logger.debug(`copyPathLogic: Received request to copy "${requestedSourcePath}" to "${requestedDestPath}"`, logicContext);

  // Resolve source and destination paths
  const absoluteSourcePath = serverState.resolvePath(requestedSourcePath, context);
  const absoluteDestPath = serverState.resolvePath(requestedDestPath, context);
  logger.debug(`copyPathLogic: Resolved source to "${absoluteSourcePath}", destination to "${absoluteDestPath}"`, { ...logicContext, requestedSourcePath, requestedDestPath });

  // Basic check: source and destination cannot be the same
  if (absoluteSourcePath === absoluteDestPath) {
      logger.warning(`copyPathLogic: Source and destination paths are identical "${absoluteSourcePath}"`, logicContext);
      throw new McpError(BaseErrorCode.VALIDATION_ERROR, 'Source and destination paths cannot be the same.', { ...logicContext, absoluteSourcePath, absoluteDestPath });
  }

  try {
    // 1. Check if source exists and get its type (file or directory)
    let sourceStats;
    try {
      sourceStats = await fs.stat(absoluteSourcePath);
      logger.debug(`copyPathLogic: Source path "${absoluteSourcePath}" exists`, logicContext);
    } catch (statError: any) {
      if (statError.code === 'ENOENT') {
        logger.warning(`copyPathLogic: Source path not found "${absoluteSourcePath}"`, logicContext);
        throw new McpError(BaseErrorCode.NOT_FOUND, `Source path not found: ${absoluteSourcePath}`, { ...logicContext, requestedSourcePath, absoluteSourcePath, originalError: statError });
      }
      logger.error(`copyPathLogic: Cannot stat source path "${absoluteSourcePath}"`, { ...logicContext, error: statError.message });
      throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Cannot access source path: ${statError.message}`, { ...logicContext, requestedSourcePath, absoluteSourcePath, originalError: statError });
    }

    const isDirectory = sourceStats.isDirectory();
    const effectiveRecursive = isDirectory ? recursive : null; // Recursive flag only relevant for directories

    // 2. Check if destination *parent* directory exists
    const destDir = path.dirname(absoluteDestPath);
    try {
        await fs.access(destDir);
        logger.debug(`copyPathLogic: Destination parent directory "${destDir}" exists`, logicContext);
    } catch (parentAccessError: any) {
        logger.error(`copyPathLogic: Destination parent directory does not exist or is inaccessible "${destDir}"`, { ...logicContext, error: parentAccessError.message });
        throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Destination directory does not exist or is inaccessible: ${destDir}`, { ...logicContext, requestedDestPath, absoluteDestPath, destDir, originalError: parentAccessError });
    }

    // 3. Check if destination path already exists (fs.cp throws by default if destination exists)
     try {
        await fs.access(absoluteDestPath);
        // If access succeeds, the destination exists. Throw an error.
        logger.warning(`copyPathLogic: Destination path already exists "${absoluteDestPath}"`, logicContext);
        throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Destination path already exists: ${absoluteDestPath}. Cannot overwrite.`, { ...logicContext, requestedDestPath, absoluteDestPath });
    } catch (destAccessError: any) {
        if (destAccessError.code !== 'ENOENT') {
            // If error is something other than "Not Found", it's an unexpected issue.
            logger.error(`copyPathLogic: Error checking destination path "${absoluteDestPath}"`, { ...logicContext, error: destAccessError.message });
            throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Error checking destination path: ${destAccessError.message}`, { ...logicContext, requestedDestPath, absoluteDestPath, originalError: destAccessError });
        }
        // ENOENT means destination does not exist, which is good. Proceed.
        logger.debug(`copyPathLogic: Destination path "${absoluteDestPath}" does not exist, proceeding with copy`, logicContext);
    }

    // 4. Attempt to copy using fs.cp (Node.js v16.7.0+)
    // fs.cp handles both files and directories, respecting the recursive flag for directories.
    // It throws if the destination exists by default.
    await fs.cp(absoluteSourcePath, absoluteDestPath, {
        recursive: effectiveRecursive ?? false, // Pass recursive flag only if it's a directory
        errorOnExist: true, // Explicitly ensure it errors if destination exists (default behavior)
        force: false // Do not overwrite
    });

    logger.info(`copyPathLogic: Successfully copied "${absoluteSourcePath}" to "${absoluteDestPath}" (Recursive: ${effectiveRecursive})`, logicContext);

    return {
      message: `Successfully copied ${absoluteSourcePath} to ${absoluteDestPath}${isDirectory ? (recursive ? ' (recursively)' : ' (non-recursively, directory structure only)') : ''}`,
      sourcePath: absoluteSourcePath,
      destinationPath: absoluteDestPath,
      wasRecursive: effectiveRecursive,
    };

  } catch (error: any) {
    logger.error(`copyPathLogic: Error copying path "${absoluteSourcePath}" to "${absoluteDestPath}"`, { ...logicContext, error: error.message, code: error.code });

    if (error instanceof McpError) {
      throw error; // Re-throw known McpErrors
    }

    // Handle potential I/O errors (e.g., permissions, disk full)
    throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to copy path: ${error.message || 'Unknown I/O error'}`, { ...logicContext, absoluteSourcePath, absoluteDestPath, originalError: error });
  }
};

```

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

```typescript
import path from 'path';
import { config } from '../config/index.js';
import { BaseErrorCode, McpError } from '../types-global/errors.js';
import { logger } from '../utils/internal/logger.js';
import { RequestContext, requestContextService } from '../utils/internal/requestContext.js';
import { sanitization } from '../utils/security/sanitization.js';

/**
 * Simple in-memory state management for the MCP server session.
 * This state is cleared when the server restarts.
 */
class ServerState {
  private defaultFilesystemPath: string | null = null;
  private fsBaseDirectory: string | null = null;

  constructor() {
    this.fsBaseDirectory = config.fsBaseDirectory || null;
    if (this.fsBaseDirectory) {
      // Ensure fsBaseDirectory itself is sanitized and absolute for internal use
      const initContext = requestContextService.createRequestContext({ operation: 'ServerStateInit' });
      try {
        const sanitizedBase = sanitization.sanitizePath(this.fsBaseDirectory, { allowAbsolute: true, toPosix: true });
        this.fsBaseDirectory = sanitizedBase.sanitizedPath;
        logger.info(`Filesystem operations will be restricted to base directory: ${this.fsBaseDirectory}`, initContext);
      } catch (error) {
        logger.error(`Invalid FS_BASE_DIRECTORY configured: ${this.fsBaseDirectory}. It will be ignored.`, { ...initContext, error: error instanceof Error ? error.message : String(error) });
        this.fsBaseDirectory = null; // Disable if invalid
      }
    }
  }

  /**
   * Sets the default filesystem path for the current session.
   * The path is sanitized and validated.
   *
   * @param newPath - The absolute path to set as default.
   * @param context - The request context for logging.
   * @throws {McpError} If the path is invalid or not absolute.
   */
  setDefaultFilesystemPath(newPath: string, context: RequestContext): void {
    logger.debug(`Attempting to set default filesystem path: ${newPath}`, context);
    try {
      // Ensure the path is absolute before storing
      if (!path.isAbsolute(newPath)) {
         throw new McpError(BaseErrorCode.VALIDATION_ERROR, 'Default path must be absolute.', { ...context, path: newPath });
      }
      // Sanitize the absolute path (mainly for normalization and basic checks)
      // We don't restrict to a rootDir here as it's a user-provided default.
      const sanitizedPathInfo = sanitization.sanitizePath(newPath, { allowAbsolute: true, toPosix: true });

      this.defaultFilesystemPath = sanitizedPathInfo.sanitizedPath;
      logger.info(`Default filesystem path set to: ${this.defaultFilesystemPath}`, context);
    } catch (error) {
      logger.error(`Failed to set default filesystem path: ${newPath}`, { ...context, error: error instanceof Error ? error.message : String(error) });
      // Rethrow McpError or wrap other errors
      if (error instanceof McpError) {
        throw error;
      }
      throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid default path provided: ${error instanceof Error ? error.message : String(error)}`, { ...context, path: newPath, originalError: error });
    }
  }

  /**
   * Gets the currently set default filesystem path.
   *
   * @returns The absolute default path or null if not set.
   */
  getDefaultFilesystemPath(): string | null {
    return this.defaultFilesystemPath;
  }

  /**
   * Clears the default filesystem path.
   * @param context - The request context for logging.
   */
  clearDefaultFilesystemPath(context: RequestContext): void {
    logger.info('Clearing default filesystem path.', context);
    this.defaultFilesystemPath = null;
  }

  /**
   * Resolves a given path against the default path if the given path is relative.
   * If the given path is absolute, it's returned directly after sanitization.
   * If the given path is relative and no default path is set, an error is thrown.
   *
   * @param requestedPath - The path provided by the user (can be relative or absolute).
   * @param context - The request context for logging and error handling.
   * @returns The resolved, sanitized, absolute path.
   * @throws {McpError} If a relative path is given without a default path set, or if sanitization fails.
   */
  resolvePath(requestedPath: string, context: RequestContext): string {
    logger.debug(`Resolving path: ${requestedPath}`, { ...context, defaultPath: this.defaultFilesystemPath, fsBaseDirectory: this.fsBaseDirectory });

    let absolutePath: string;

    if (path.isAbsolute(requestedPath)) {
      absolutePath = requestedPath;
      logger.debug('Provided path is absolute.', { ...context, path: absolutePath });
    } else {
      if (!this.defaultFilesystemPath) {
        logger.warning('Relative path provided but no default path is set.', { ...context, path: requestedPath });
        throw new McpError(
          BaseErrorCode.VALIDATION_ERROR,
          'Relative path provided, but no default filesystem path has been set for this session. Please provide an absolute path or set a default path first.',
          { ...context, path: requestedPath }
        );
      }
      absolutePath = path.join(this.defaultFilesystemPath, requestedPath);
      logger.debug(`Resolved relative path against default: ${absolutePath}`, { ...context, relativePath: requestedPath, defaultPath: this.defaultFilesystemPath });
    }
    
    let sanitizedAbsolutePath: string;
    try {
      // Sanitize the path first. allowAbsolute is true as we've resolved it.
      // No rootDir is enforced by sanitizePath itself here; boundary check is next.
      const sanitizedPathInfo = sanitization.sanitizePath(absolutePath, { allowAbsolute: true, toPosix: true });
      sanitizedAbsolutePath = sanitizedPathInfo.sanitizedPath;
      logger.debug(`Sanitized resolved path: ${sanitizedAbsolutePath}`, { ...context, originalPath: absolutePath });
    } catch (error) {
       logger.error(`Failed to sanitize resolved path: ${absolutePath}`, { ...context, error: error instanceof Error ? error.message : String(error) });
       if (error instanceof McpError) {
         throw error; // Rethrow validation errors from sanitizePath
       }
       throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to process path: ${error instanceof Error ? error.message : String(error)}`, { ...context, path: absolutePath, originalError: error });
    }

    // Enforce FS_BASE_DIRECTORY boundary if it's set
    if (this.fsBaseDirectory) {
      // Normalize both paths for a reliable comparison
      const normalizedFsBaseDirectory = path.normalize(this.fsBaseDirectory);
      const normalizedSanitizedAbsolutePath = path.normalize(sanitizedAbsolutePath);

      // Check if the sanitized absolute path is within the base directory
      if (!normalizedSanitizedAbsolutePath.startsWith(normalizedFsBaseDirectory + path.sep) && normalizedSanitizedAbsolutePath !== normalizedFsBaseDirectory) {
        logger.error(
          `Path access violation: Attempted to access path "${sanitizedAbsolutePath}" which is outside the configured FS_BASE_DIRECTORY "${this.fsBaseDirectory}".`,
          { ...context, requestedPath, resolvedPath: sanitizedAbsolutePath, fsBaseDirectory: this.fsBaseDirectory }
        );
        throw new McpError(
          BaseErrorCode.FORBIDDEN,
          `Access denied: The path "${requestedPath}" resolves to a location outside the allowed base directory.`,
          { ...context, requestedPath, resolvedPath: sanitizedAbsolutePath }
        );
      }
      logger.debug(`Path is within FS_BASE_DIRECTORY: ${sanitizedAbsolutePath}`, context);
    }

    return sanitizedAbsolutePath;
  }
}

// Export a singleton instance
export const serverState = new ServerState();

```

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

```typescript
/**
 * @fileoverview Main entry point for the Filesystem 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 filesystem tools, making them discoverable and usable by clients.
 * 3. Selects and starts the appropriate communication transport (currently stdio)
 *    based on configuration.
 * 4. Handles top-level error management during startup.
 *
 * MCP Specification References:
 * - Lifecycle: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/lifecycle.mdx
 * - Overview (Capabilities): https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/index.mdx
 * - Transports: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/transports.mdx
 * @module src/mcp-server/server
 */

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { config, environment } from '../config/index.js';
import { ErrorHandler, logger, requestContextService } from '../utils/index.js'; // Corrected import path
import { registerCopyPathTool } from './tools/copyPath/index.js';
import { registerCreateDirectoryTool } from './tools/createDirectory/index.js';
import { registerDeleteDirectoryTool } from './tools/deleteDirectory/index.js';
import { registerDeleteFileTool } from './tools/deleteFile/index.js';
import { registerListFilesTool } from './tools/listFiles/index.js';
import { registerMovePathTool } from './tools/movePath/index.js';
import { registerReadFileTool } from './tools/readFile/index.js';
import { registerSetFilesystemDefaultTool } from './tools/setFilesystemDefault/index.js';
import { registerUpdateFileTool } from './tools/updateFile/index.js';
import { registerWriteFileTool } from './tools/writeFile/index.js';
import { startHttpTransport } from "./transports/httpTransport.js";

/**
 * Creates and configures a new instance of the `McpServer`.
 *
 * This function defines the server's identity and capabilities as presented
 * to clients during MCP initialization.
 *
 * @returns A promise resolving with the configured `McpServer` instance.
 * @throws {Error} If any tool registration fails.
 * @private
 */
async function createMcpServerInstance(): Promise<McpServer> {
  const context = requestContextService.createRequestContext({
    operation: "createMcpServerInstance",
  });
  logger.info("Initializing MCP server instance", context);

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

  logger.debug("Instantiating McpServer with capabilities", {
    ...context,
    serverInfo: {
      name: config.mcpServerName,
      version: config.mcpServerVersion,
    },
    capabilities: {
      logging: {},
      resources: { listChanged: true }, // Assuming dynamic resources might be added later
      tools: { listChanged: true },     // Filesystem tools are dynamically registered
    },
  });

  const server = new McpServer(
    { name: config.mcpServerName, version: config.mcpServerVersion },
    {
      capabilities: {
        logging: {},
        resources: { listChanged: true },
        tools: { listChanged: true },
      },
    },
  );

  try {
    logger.debug("Registering filesystem tools...", context);
    const registrationPromises = [
      registerReadFileTool(server),
      registerSetFilesystemDefaultTool(server),
      registerWriteFileTool(server),
      registerUpdateFileTool(server),
      registerListFilesTool(server),
      registerDeleteFileTool(server),
      registerDeleteDirectoryTool(server),
      registerCreateDirectoryTool(server),
      registerMovePathTool(server),
      registerCopyPathTool(server)
    ];

    await Promise.all(registrationPromises);
    logger.info("Filesystem tools registered successfully", context);
  } catch (err) {
    logger.error("Failed to register filesystem tools", {
      ...context,
      error: err instanceof Error ? err.message : String(err),
      stack: err instanceof Error ? err.stack : undefined,
    });
    throw err; // Rethrow to be caught by the caller
  }

  return server;
}

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

  if (transportType === "http") {
    logger.debug("Delegating to startHttpTransport...", context);
    await startHttpTransport(createMcpServerInstance, context);
    return;
  }

  if (transportType === "stdio") {
    logger.debug(
      "Creating single McpServer instance for stdio transport...",
      context,
    );
    const server = await createMcpServerInstance();
    logger.debug("Connecting StdioServerTransport...", context);
    try {
      const transport = new StdioServerTransport();
      await server.connect(transport);
      logger.info(`${config.mcpServerName} connected successfully via stdio`, {
        ...context,
        serverName: config.mcpServerName,
        version: config.mcpServerVersion
      });
    } catch (connectionError) {
      // Handle connection errors specifically
      ErrorHandler.handleError(connectionError, {
        operation: 'StdioServerTransport Connection',
        context: context, // Pass the existing context
        critical: true,
        rethrow: true // Rethrow to allow the main startup process to handle exit
      });
      // This line won't be reached if rethrow is true
      throw connectionError;
    }
    return server; // Return the single server instance for stdio.
  }

  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.
 * Orchestrates server startup, transport selection, and top-level error handling.
 *
 * @returns For 'stdio', resolves with `McpServer`.
 *   Rejects on critical failure, leading to process exit.
 */
export async function initializeAndStartServer(): Promise<void | McpServer> {
  const context = requestContextService.createRequestContext({
    operation: "initializeAndStartServer",
  });
  logger.info("Filesystem MCP Server initialization sequence started.", context);
  try {
    const result = await startTransport();
    logger.info(
      "Filesystem MCP Server initialization sequence completed successfully.",
      context,
    );
    return result;
  } catch (err) {
    logger.fatal("Critical error during Filesystem MCP server initialization.", {
      ...context,
      error: err instanceof Error ? err.message : String(err),
      stack: err instanceof Error ? err.stack : undefined,
    });
    ErrorHandler.handleError(err, {
      operation: "initializeAndStartServer",
      context: context,
      critical: true,
    });
    logger.info(
      "Exiting process due to critical initialization error.",
      context,
    );
    process.exit(1); // Exit with a non-zero code to indicate failure.
  }
}

```

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

```typescript
/**
 * @fileoverview MCP Authentication Middleware for Bearer Token Validation (JWT).
 *
 * This middleware validates JSON Web Tokens (JWT) passed via the 'Authorization' header
 * using the 'Bearer' scheme (e.g., "Authorization: Bearer <your_token>").
 * It verifies the token's signature and expiration using the secret key defined
 * in the configuration (`config.mcpAuthSecretKey`).
 *
 * If the token is valid, an object conforming to the MCP SDK's `AuthInfo` type
 * (expected to contain `token`, `clientId`, and `scopes`) is attached to `req.auth`.
 * If the token is missing, invalid, or expired, it sends an HTTP 401 Unauthorized response.
 *
 * @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/authentication/authMiddleware
 */

import { AuthInfo } from "@modelcontextprotocol/sdk/server/auth/types.js"; // Import from SDK
import { NextFunction, Request, Response } from "express";
import jwt from "jsonwebtoken";
import { config, environment } from "../../../config/index.js";
import { logger, requestContextService } from "../../../utils/index.js";

// Extend the Express Request interface to include the optional 'auth' property
// using the imported AuthInfo type from the SDK.
declare global {
  // eslint-disable-next-line @typescript-eslint/no-namespace
  namespace Express {
    interface Request {
      /** Authentication information derived from the JWT, conforming to MCP SDK's AuthInfo. */
      auth?: AuthInfo;
    }
  }
}

// Startup Validation: Validate secret key presence on module load.
if (environment === "production" && !config.mcpAuthSecretKey) {
  const error = new Error(
    "CRITICAL: MCP_AUTH_SECRET_KEY must be set in production environment for JWT authentication."
  );
  logger.fatal(
    "CRITICAL: MCP_AUTH_SECRET_KEY is not set in production environment. Authentication cannot proceed securely.",
  );
  // Force process exit in production to prevent insecure startup
  process.exit(1);
} else if (!config.mcpAuthSecretKey) {
  logger.warning(
    "MCP_AUTH_SECRET_KEY is not set. Authentication middleware will bypass checks (DEVELOPMENT ONLY). This is insecure for production.",
  );
}

/**
 * Express middleware for verifying JWT Bearer token authentication.
 */
export function mcpAuthMiddleware(
  req: Request,
  res: Response,
  next: NextFunction,
): void {
  const context = requestContextService.createRequestContext({
    operation: "mcpAuthMiddleware",
    method: req.method,
    path: req.path,
  });
  logger.debug(
    "Running MCP Authentication Middleware (Bearer Token Validation)...",
    context,
  );

  // Development Mode Bypass
  if (!config.mcpAuthSecretKey) {
    if (environment !== "production") {
      logger.warning(
        "Bypassing JWT authentication: MCP_AUTH_SECRET_KEY is not set (DEVELOPMENT ONLY).",
        context,
      );
      // Populate req.auth strictly according to SDK's AuthInfo
      req.auth = {
        token: "dev-mode-placeholder-token",
        clientId: "dev-client-id",
        scopes: ["dev-scope"],
      };
      // Log dev mode details separately, not attaching to req.auth if not part of AuthInfo
      logger.debug("Dev mode auth object created.", {
        ...context,
        authDetails: req.auth,
      });
      return next();
    } else {
      logger.error(
        "FATAL: MCP_AUTH_SECRET_KEY is missing in production. Cannot bypass auth.",
        context,
      );
      res.status(500).json({
        error: "Server configuration error: Authentication key missing.",
      });
      return;
    }
  }

  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith("Bearer ")) {
    logger.warning(
      "Authentication failed: Missing or malformed Authorization header (Bearer scheme required).",
      context,
    );
    res.status(401).json({
      error: "Unauthorized: Missing or invalid authentication token format.",
    });
    return;
  }

  const tokenParts = authHeader.split(" ");
  if (tokenParts.length !== 2 || tokenParts[0] !== "Bearer" || !tokenParts[1]) {
    logger.warning("Authentication failed: Malformed Bearer token.", context);
    res
      .status(401)
      .json({ error: "Unauthorized: Malformed authentication token." });
    return;
  }
  const rawToken = tokenParts[1];

  try {
    const decoded = jwt.verify(rawToken, config.mcpAuthSecretKey);

    if (typeof decoded === "string") {
      logger.warning(
        "Authentication failed: JWT decoded to a string, expected an object payload.",
        context,
      );
      res
        .status(401)
        .json({ error: "Unauthorized: Invalid token payload format." });
      return;
    }

    // Extract and validate fields for SDK's AuthInfo
    const clientIdFromToken =
      typeof decoded.cid === "string"
        ? decoded.cid
        : typeof decoded.client_id === "string"
          ? decoded.client_id
          : undefined;
    if (!clientIdFromToken) {
      logger.warning(
        "Authentication failed: JWT 'cid' or 'client_id' claim is missing or not a string.",
        { ...context, jwtPayloadKeys: Object.keys(decoded) },
      );
      res.status(401).json({
        error: "Unauthorized: Invalid token, missing client identifier.",
      });
      return;
    }

    let scopesFromToken: string[];
    if (
      Array.isArray(decoded.scp) &&
      decoded.scp.every((s: unknown) => typeof s === "string")
    ) {
      scopesFromToken = decoded.scp as string[];
    } else if (
      typeof decoded.scope === "string" &&
      decoded.scope.trim() !== ""
    ) {
      scopesFromToken = decoded.scope.split(" ").filter((s: string) => s);
      if (scopesFromToken.length === 0 && decoded.scope.trim() !== "") {
        // handles case " " -> [""]
        scopesFromToken = [decoded.scope.trim()];
      } else if (scopesFromToken.length === 0 && decoded.scope.trim() === "") {
        // If scope is an empty string, treat as no scopes rather than erroring, or use a default.
        // Depending on strictness, could also error here. For now, allow empty array if scope was empty string.
        logger.debug(
          "JWT 'scope' claim was an empty string, resulting in empty scopes array.",
          context,
        );
      }
    } else {
      // If scopes are strictly mandatory and not found or invalid format
      logger.warning(
        "Authentication failed: JWT 'scp' or 'scope' claim is missing, not an array of strings, or not a valid space-separated string. Assigning default empty array.",
        { ...context, jwtPayloadKeys: Object.keys(decoded) },
      );
      scopesFromToken = []; // Default to empty array if scopes are mandatory but not found/invalid
      // Or, if truly mandatory and must be non-empty:
      // res.status(401).json({ error: "Unauthorized: Invalid token, missing or invalid scopes." });
      // return;
    }

    // Construct req.auth with only the properties defined in SDK's AuthInfo
    // All other claims from 'decoded' are not part of req.auth for type safety.
    req.auth = {
      token: rawToken,
      clientId: clientIdFromToken,
      scopes: scopesFromToken,
    };

    // Log separately if other JWT claims like 'sub' (sessionId) are needed for app logic
    const subClaimForLogging =
      typeof decoded.sub === "string" ? decoded.sub : undefined;
    logger.debug("JWT verified successfully. AuthInfo attached to request.", {
      ...context,
      mcpSessionIdContext: subClaimForLogging,
      clientId: req.auth.clientId,
      scopes: req.auth.scopes,
    });
    next();
  } catch (error: unknown) {
    let errorMessage = "Invalid token";
    if (error instanceof jwt.TokenExpiredError) {
      errorMessage = "Token expired";
      logger.warning("Authentication failed: Token expired.", {
        ...context,
        expiredAt: error.expiredAt, // Accessing error.expiredAt safely
      });
    } else if (error instanceof jwt.JsonWebTokenError) { // This already implies error is an instance of Error
      errorMessage = `Invalid token: ${error.message}`; // Accessing error.message safely
      logger.warning(`Authentication failed: ${errorMessage}`, { ...context });
    } else if (error instanceof Error) { // Catch other generic Errors
      errorMessage = `Verification error: ${error.message}`; // Accessing error.message safely
      logger.error(
        "Authentication failed: Unexpected error during token verification.",
        { ...context, error: error.message },
      );
    } else { // Handle truly unknown types
      errorMessage = "Unknown verification error";
      logger.error(
        "Authentication failed: Unexpected non-error exception during token verification.",
        { ...context, error: String(error) }, // Convert unknown error to string for logging
      );
    }
    res.status(401).json({ error: `Unauthorized: ${errorMessage}.` });
  }
}

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/updateFile/updateFileLogic.ts:
--------------------------------------------------------------------------------

```typescript
import fs from 'fs/promises';
import { z } from 'zod';
import { BaseErrorCode, McpError } from '../../../types-global/errors.js';
import { logger } from '../../../utils/internal/logger.js';
import { RequestContext } from '../../../utils/internal/requestContext.js';
import { serverState } from '../../state.js';

// Define the structure for a single search/replace block
const DiffBlockSchema = z.object({
  search: z.string().min(1, 'Search pattern cannot be empty'),
  replace: z.string(), // Allow empty replace string for deletions
});

// Define the input schema using Zod for validation
export const UpdateFileInputSchema = z.object({
  path: z.string().min(1, 'Path cannot be empty')
    .describe('The path to the file to update. Can be relative or absolute (resolved like readFile). The file must exist.'),
  blocks: z.array(DiffBlockSchema).min(1, 'At least one search/replace block is required.')
    .describe('An array of objects, each with a `search` (string) and `replace` (string) property.'),
  useRegex: z.boolean().default(false)
    .describe('If true, treat the `search` field of each block as a JavaScript regular expression pattern. Defaults to false (exact string matching).'),
  replaceAll: z.boolean().default(false)
    .describe('If true, replace all occurrences matching the SEARCH criteria within the file. If false, only replace the first occurrence. Defaults to false.'),
});

// Define the TypeScript type for the input
export type UpdateFileInput = z.infer<typeof UpdateFileInputSchema>;

// Define the TypeScript type for a single block based on the schema, adding internal tracking
export type DiffBlock = z.infer<typeof DiffBlockSchema> & { applied?: boolean };

// Define the TypeScript type for the output
export interface UpdateFileOutput {
  message: string;
  updatedPath: string;
  blocksApplied: number;
  blocksFailed: number; // Track blocks that didn't find a match
}

/**
 * Applies an array of search/replace blocks sequentially to the file content.
 *
 * @param {UpdateFileInput} input - The input object containing path, blocks, and options.
 * @param {RequestContext} context - The request context.
 * @returns {Promise<UpdateFileOutput>} A promise resolving with update status.
 * @throws {McpError} For path errors, file not found, I/O errors, or invalid regex patterns.
 */
export const updateFileLogic = async (input: UpdateFileInput, context: RequestContext): Promise<UpdateFileOutput> => {
  // Destructure validated input
  const { path: requestedPath, blocks: inputBlocks, useRegex, replaceAll } = input;
  const logicContext = { ...context, useRegex, replaceAll };
  logger.debug(`updateFileLogic: Received request for path "${requestedPath}" with ${inputBlocks.length} blocks`, logicContext);

  // Resolve the path
  const absolutePath = serverState.resolvePath(requestedPath, context);
  logger.debug(`updateFileLogic: Resolved path to "${absolutePath}"`, { ...context, requestedPath });

  try {
    // 1. Read the existing file content
    let currentContent: string;
    try {
      currentContent = await fs.readFile(absolutePath, 'utf8');
      logger.debug(`updateFileLogic: Successfully read existing file "${absolutePath}"`, { ...context, requestedPath });
    } catch (readError: any) {
      if (readError.code === 'ENOENT') {
        logger.warning(`updateFileLogic: File not found at "${absolutePath}"`, { ...context, requestedPath });
        throw new McpError(BaseErrorCode.NOT_FOUND, `File not found at path: ${absolutePath}. Cannot update a non-existent file.`, { ...context, requestedPath, resolvedPath: absolutePath, originalError: readError });
      }
      throw readError; // Re-throw other read errors
    }

    // 2. Input blocks are already parsed and validated by Zod
    const diffBlocks: DiffBlock[] = inputBlocks.map(block => ({ ...block, applied: false })); // Add internal 'applied' flag

    // 3. Apply blocks sequentially
    let updatedContent = currentContent;
    let blocksApplied = 0;
    let blocksFailed = 0;
    let totalReplacementsMade = 0; // Track individual replacements if replaceAll is true

    for (let i = 0; i < diffBlocks.length; i++) {
      const block = diffBlocks[i];
      // Create context specific to this block's processing
      const blockContext = { ...logicContext, blockIndex: i, searchPreview: block.search.substring(0, 50) };
      let blockMadeChange = false;
      let replacementsInBlock = 0; // Count replacements made by *this specific block*

      try {
        if (useRegex) {
          // Treat search as regex pattern
          // Create the regex. Add 'g' flag if replaceAll is true.
          const regex = new RegExp(block.search, replaceAll ? 'g' : '');
          const matches = updatedContent.match(regex); // Find matches before replacing

          if (matches && matches.length > 0) {
             updatedContent = updatedContent.replace(regex, block.replace);
             replacementsInBlock = matches.length; // Count actual matches found
             blockMadeChange = true;
             logger.debug(`Applied regex block`, blockContext);
          }
        } else {
          // Treat search as exact string
          if (replaceAll) {
            let startIndex = 0;
            let index;
            let replaced = false;
            // Use split/join for robust replacement of all occurrences
            const parts = updatedContent.split(block.search);
            if (parts.length > 1) { // Check if the search string was found at all
                updatedContent = parts.join(block.replace);
                replacementsInBlock = parts.length - 1; // Number of replacements is one less than the number of parts
                replaced = true;
            }

            if (replaced) {
               blockMadeChange = true;
               logger.debug(`Applied string block (replaceAll=true)`, blockContext);
            }
          } else {
            // Replace only the first occurrence
            const index = updatedContent.indexOf(block.search);
            if (index !== -1) {
              updatedContent = updatedContent.substring(0, index) + block.replace + updatedContent.substring(index + block.search.length);
              replacementsInBlock = 1;
              blockMadeChange = true;
              logger.debug(`Applied string block (replaceAll=false)`, blockContext);
            }
          }
        }
      } catch (regexError: any) {
         if (regexError instanceof SyntaxError && useRegex) {
            logger.error('Invalid regex pattern provided in SEARCH block', { ...blockContext, error: regexError.message });
            throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid regular expression pattern in block ${i + 1}: "${block.search}". Error: ${regexError.message}`, blockContext);
         }
         // Re-throw other unexpected errors during replacement
         logger.error('Unexpected error during replacement operation', { ...blockContext, error: regexError.message });
         throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Error processing block ${i + 1}: ${regexError.message}`, blockContext);
      }


      if (blockMadeChange) {
        block.applied = true; // Mark the block as having made a change
        blocksApplied++;
        totalReplacementsMade += replacementsInBlock; // Add replacements from this block to total
      } else {
        blocksFailed++;
        logger.warning(`Diff block search criteria not found`, blockContext);
      }
    }

    // 4. Write the updated content back to the file only if changes were actually made
    if (totalReplacementsMade > 0) { // Check if any replacement occurred across all blocks
      logger.debug(`updateFileLogic: Writing updated content back to "${absolutePath}"`, logicContext);
      await fs.writeFile(absolutePath, updatedContent, 'utf8');
      logger.info(`updateFileLogic: Successfully updated file "${absolutePath}"`, { ...logicContext, requestedPath, blocksApplied, blocksFailed, totalReplacementsMade });
      const replaceMsg = `Made ${totalReplacementsMade} replacement(s) across ${blocksApplied} block(s).`;
      return {
        message: `Successfully updated file ${absolutePath}. ${replaceMsg} ${blocksFailed} block(s) failed (search criteria not found).`,
        updatedPath: absolutePath,
        blocksApplied,
        blocksFailed,
      };
    } else {
      // No replacements were made, even if blocks were provided
      logger.info(`updateFileLogic: No replacements made in file "${absolutePath}"`, { ...logicContext, requestedPath, blocksFailed });
      return {
        message: `No changes applied to file ${absolutePath}. ${blocksFailed} block(s) failed (search criteria not found).`,
        updatedPath: absolutePath,
        blocksApplied: 0, // No blocks resulted in a change
        blocksFailed,
      };
    }

  } catch (error: any) {
    logger.error(`updateFileLogic: Error updating file "${absolutePath}"`, { ...logicContext, requestedPath, error: error.message, code: error.code });
    if (error instanceof McpError) {
      throw error; // Re-throw known McpErrors
    }
    // Handle potential I/O errors during read or write
    throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to update file: ${error.message || 'Unknown I/O error'}`, { ...context, requestedPath, resolvedPath: absolutePath, originalError: error });
  }
};

```

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

```typescript
/**
 * @fileoverview Provides a utility class `IdGenerator` for creating customizable, prefixed unique identifiers,
 * and a standalone `generateUUID` function for generating standard UUIDs.
 * The `IdGenerator` supports entity-specific prefixes, custom character sets, and lengths.
 *
 * Note: Logging has been removed from this module to prevent circular dependencies
 * with the `requestContextService`, which itself uses `generateUUID` from this module.
 * This was causing `ReferenceError: Cannot access 'generateUUID' before initialization`
 * during application startup.
 * @module src/utils/security/idGenerator
 */
import { randomUUID as cryptoRandomUUID, randomBytes } from "crypto";
import { BaseErrorCode, McpError } from "../../types-global/errors.js";
// Removed: import { logger, requestContextService } from "../index.js";

/**
 * Defines the structure for configuring entity prefixes.
 * Keys are entity type names (e.g., "project", "task"), and values are their corresponding ID prefixes (e.g., "PROJ", "TASK").
 */
export interface EntityPrefixConfig {
  [key: string]: string;
}

/**
 * Defines options for customizing ID generation.
 */
export interface IdGenerationOptions {
  length?: number;
  separator?: string;
  charset?: string;
}

/**
 * A generic ID Generator class for creating and managing unique, prefixed identifiers.
 * Allows defining custom prefixes, generating random strings, and validating/normalizing IDs.
 */
export class IdGenerator {
  /**
   * Default character set for the random part of the ID.
   * @private
   */
  private static DEFAULT_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  /**
   * Default separator character between prefix and random part.
   * @private
   */
  private static DEFAULT_SEPARATOR = "_";
  /**
   * Default length for the random part of the ID.
   * @private
   */
  private static DEFAULT_LENGTH = 6;

  /**
   * Stores the mapping of entity types to their prefixes.
   * @private
   */
  private entityPrefixes: EntityPrefixConfig = {};
  /**
   * Stores a reverse mapping from prefixes (case-insensitive) to entity types.
   * @private
   */
  private prefixToEntityType: Record<string, string> = {};

  /**
   * Constructs an `IdGenerator` instance.
   * @param entityPrefixes - An initial map of entity types to their prefixes.
   */
  constructor(entityPrefixes: EntityPrefixConfig = {}) {
    // Logging removed to prevent circular dependency with requestContextService.
    this.setEntityPrefixes(entityPrefixes);
  }

  /**
   * Sets or updates the entity prefix configuration and rebuilds the internal reverse lookup map.
   * @param entityPrefixes - A map where keys are entity type names and values are their desired ID prefixes.
   */
  public setEntityPrefixes(entityPrefixes: EntityPrefixConfig): void {
    // Logging removed.
    this.entityPrefixes = { ...entityPrefixes };

    this.prefixToEntityType = Object.entries(this.entityPrefixes).reduce(
      (acc, [type, prefix]) => {
        acc[prefix.toLowerCase()] = type; // Store lowercase for case-insensitive lookup
        return acc;
      },
      {} as Record<string, string>,
    );
  }

  /**
   * Retrieves a copy of the current entity prefix configuration.
   * @returns The current entity prefix configuration.
   */
  public getEntityPrefixes(): EntityPrefixConfig {
    return { ...this.entityPrefixes };
  }

  /**
   * Generates a cryptographically secure random string.
   * @param length - The desired length of the random string. Defaults to `IdGenerator.DEFAULT_LENGTH`.
   * @param charset - The character set to use. Defaults to `IdGenerator.DEFAULT_CHARSET`.
   * @returns The generated random string.
   */
  public generateRandomString(
    length: number = IdGenerator.DEFAULT_LENGTH,
    charset: string = IdGenerator.DEFAULT_CHARSET,
  ): string {
    const bytes = randomBytes(length);
    let result = "";
    for (let i = 0; i < length; i++) {
      result += charset[bytes[i] % charset.length];
    }
    return result;
  }

  /**
   * Generates a unique ID, optionally prepended with a prefix.
   * @param prefix - An optional prefix for the ID.
   * @param options - Optional parameters for ID generation (length, separator, charset).
   * @returns A unique identifier string.
   */
  public generate(prefix?: string, options: IdGenerationOptions = {}): string {
    // Logging removed.
    const {
      length = IdGenerator.DEFAULT_LENGTH,
      separator = IdGenerator.DEFAULT_SEPARATOR,
      charset = IdGenerator.DEFAULT_CHARSET,
    } = options;

    const randomPart = this.generateRandomString(length, charset);
    const generatedId = prefix
      ? `${prefix}${separator}${randomPart}`
      : randomPart;
    return generatedId;
  }

  /**
   * Generates a unique ID for a specified entity type, using its configured prefix.
   * @param entityType - The type of entity (must be registered).
   * @param options - Optional parameters for ID generation.
   * @returns A unique identifier string for the entity (e.g., "PROJ_A6B3J0").
   * @throws {McpError} If the `entityType` is not registered.
   */
  public generateForEntity(
    entityType: string,
    options: IdGenerationOptions = {},
  ): string {
    const prefix = this.entityPrefixes[entityType];
    if (!prefix) {
      throw new McpError(
        BaseErrorCode.VALIDATION_ERROR,
        `Unknown entity type: ${entityType}. No prefix registered.`,
      );
    }
    return this.generate(prefix, options);
  }

  /**
   * Validates if an ID conforms to the expected format for a specific entity type.
   * @param id - The ID string to validate.
   * @param entityType - The expected entity type of the ID.
   * @param options - Optional parameters used during generation for validation consistency.
   * @returns `true` if the ID is valid, `false` otherwise.
   */
  public isValid(
    id: string,
    entityType: string,
    options: IdGenerationOptions = {},
  ): boolean {
    const prefix = this.entityPrefixes[entityType];
    const {
      length = IdGenerator.DEFAULT_LENGTH,
      separator = IdGenerator.DEFAULT_SEPARATOR,
    } = options;

    if (!prefix) {
      return false;
    }
    // Assumes default charset characters (uppercase letters and digits) for regex.
    const pattern = new RegExp(
      `^${this.escapeRegex(prefix)}${this.escapeRegex(separator)}[A-Z0-9]{${length}}$`,
    );
    return pattern.test(id);
  }

  /**
   * Escapes special characters in a string for use in a regular expression.
   * @param str - The string to escape.
   * @returns The escaped string.
   * @private
   */
  private escapeRegex(str: string): string {
    return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  }

  /**
   * Strips the prefix and separator from an ID string.
   * @param id - The ID string (e.g., "PROJ_A6B3J0").
   * @param separator - The separator used in the ID. Defaults to `IdGenerator.DEFAULT_SEPARATOR`.
   * @returns The ID part without the prefix, or the original ID if separator not found.
   */
  public stripPrefix(
    id: string,
    separator: string = IdGenerator.DEFAULT_SEPARATOR,
  ): string {
    const parts = id.split(separator);
    return parts.length > 1 ? parts.slice(1).join(separator) : id; // Handle separators in random part
  }

  /**
   * Determines the entity type from an ID string by its prefix (case-insensitive).
   * @param id - The ID string (e.g., "PROJ_A6B3J0").
   * @param separator - The separator used in the ID. Defaults to `IdGenerator.DEFAULT_SEPARATOR`.
   * @returns The determined entity type.
   * @throws {McpError} If ID format is invalid or prefix is unknown.
   */
  public getEntityType(
    id: string,
    separator: string = IdGenerator.DEFAULT_SEPARATOR,
  ): string {
    const parts = id.split(separator);
    if (parts.length < 2 || !parts[0]) {
      throw new McpError(
        BaseErrorCode.VALIDATION_ERROR,
        `Invalid ID format: ${id}. Expected format like: PREFIX${separator}RANDOMLPART`,
      );
    }

    const prefix = parts[0];
    const entityType = this.prefixToEntityType[prefix.toLowerCase()];

    if (!entityType) {
      throw new McpError(
        BaseErrorCode.VALIDATION_ERROR,
        `Unknown entity type for prefix: ${prefix}`,
      );
    }
    return entityType;
  }

  /**
   * Normalizes an entity ID to ensure the prefix matches the registered case
   * and the random part is uppercase.
   * @param id - The ID to normalize (e.g., "proj_a6b3j0").
   * @param separator - The separator used in the ID. Defaults to `IdGenerator.DEFAULT_SEPARATOR`.
   * @returns The normalized ID (e.g., "PROJ_A6B3J0").
   * @throws {McpError} If the entity type cannot be determined from the ID.
   */
  public normalize(
    id: string,
    separator: string = IdGenerator.DEFAULT_SEPARATOR,
  ): string {
    const entityType = this.getEntityType(id, separator);
    const registeredPrefix = this.entityPrefixes[entityType];
    const idParts = id.split(separator);
    const randomPart = idParts.slice(1).join(separator);

    return `${registeredPrefix}${separator}${randomPart.toUpperCase()}`;
  }
}

/**
 * Default singleton instance of the `IdGenerator`.
 * Initialize with `idGenerator.setEntityPrefixes({})` to configure.
 */
export const idGenerator = new IdGenerator();

/**
 * Generates a standard Version 4 UUID (Universally Unique Identifier).
 * Uses the Node.js `crypto` module. This function is independent of the IdGenerator instance
 * to prevent circular dependencies when used by other utilities like requestContextService.
 * @returns A new UUID string.
 */
export const generateUUID = (): string => {
  return cryptoRandomUUID();
};

```

--------------------------------------------------------------------------------
/src/mcp-server/tools/listFiles/listFilesLogic.ts:
--------------------------------------------------------------------------------

```typescript
import fs from 'fs/promises';
import path from 'path';
import { z } from 'zod';
import { BaseErrorCode, McpError } from '../../../types-global/errors.js';
import { logger } from '../../../utils/internal/logger.js';
import { RequestContext } from '../../../utils/internal/requestContext.js';
import { serverState } from '../../state.js';

// Define the input schema using Zod for validation
export const ListFilesInputSchema = z.object({
  path: z.string().min(1, 'Path cannot be empty')
    .describe('The path to the directory to list. Can be relative or absolute (resolved like readFile).'),
  includeNested: z.boolean().default(false)
    .describe('If true, list files and directories recursively. Defaults to false (top-level only).'),
  maxEntries: z.number().int().positive().optional().default(50) // Updated default to 50
    .describe('Maximum number of directory entries (files + folders) to return. Defaults to 50. Helps prevent excessive output for large directories.'),
});

// Define the TypeScript type for the input
export type ListFilesInput = z.infer<typeof ListFilesInputSchema>;

// Define the TypeScript type for the output
export interface ListFilesOutput {
  message: string;
  tree: string;
  requestedPath: string;
  resolvedPath: string;
  itemCount: number;
  truncated: boolean; // Added flag
}

interface DirectoryItem {
  name: string;
  isDirectory: boolean;
  children?: DirectoryItem[]; // Only populated if includeNested is true
  error?: string; // Added to indicate read errors for this directory
}

/**
 * Recursively reads directory contents and builds a tree structure.
 *
 * @param {string} dirPath - The absolute path to the directory.
 * @param {boolean} includeNested - Whether to recurse into subdirectories.
 * @param {RequestContext} context - The request context for logging.
 * @param {{ count: number, limit: number, truncated: boolean }} state - Mutable state to track count and limit across recursive calls.
 * @returns {Promise<DirectoryItem[]>} A promise resolving with the list of items.
 * @throws {McpError} If reading the directory fails.
 */
const readDirectoryRecursive = async (
  dirPath: string,
  includeNested: boolean,
  context: RequestContext,
  state: { count: number; limit: number; truncated: boolean } // Pass state object
): Promise<DirectoryItem[]> => {
  if (state.truncated || state.count >= state.limit) {
    state.truncated = true; // Ensure truncated flag is set if limit reached before starting
    return []; // Stop processing if limit already reached
  }

  const items: DirectoryItem[] = [];
  let entries;
  try {
    entries = await fs.readdir(dirPath, { withFileTypes: true });
  } catch (error: any) {
    if (error.code === 'ENOENT') {
      logger.warning(`Directory not found: ${dirPath}`, context);
      throw new McpError(BaseErrorCode.NOT_FOUND, `Directory not found at path: ${dirPath}`, { ...context, dirPath, originalError: error });
    } else if (error.code === 'ENOTDIR') {
       logger.warning(`Path is not a directory: ${dirPath}`, context);
       throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Path is not a directory: ${dirPath}`, { ...context, dirPath, originalError: error });
    }
    logger.error(`Failed to read directory: ${dirPath}`, { ...context, error: error.message });
    throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to read directory: ${error.message}`, { ...context, dirPath, originalError: error });
  }

  for (const entry of entries) {
    if (state.count >= state.limit) {
      state.truncated = true;
      logger.debug(`Max entries limit (${state.limit}) reached while processing ${dirPath}`, context);
      break; // Stop processing entries in this directory
    }

    state.count++; // Increment count for this entry

    const itemPath = path.join(dirPath, entry.name);
    const item: DirectoryItem = {
      name: entry.name,
      isDirectory: entry.isDirectory(),
    };

    if (item.isDirectory && includeNested) {
      // Recursively read subdirectory, passing the shared state object
      try {
        // Pass the same state object down
        item.children = await readDirectoryRecursive(itemPath, includeNested, { ...context, parentPath: dirPath }, state);
      } catch (recursiveError) {
         // Log the error from the recursive call but continue processing other entries
         logger.error(`Error reading nested directory ${itemPath}`, { ...context, error: (recursiveError as Error).message, code: (recursiveError as McpError).code });
         // Log the error and mark the item
         const errorMessage = (recursiveError as McpError)?.message || (recursiveError as Error)?.message || 'Unknown error reading directory';
         logger.error(`Error reading nested directory ${itemPath}`, { ...context, error: errorMessage, code: (recursiveError as McpError)?.code });
         item.error = errorMessage; // Store the error message on the item
         item.children = undefined; // Ensure no children are processed or displayed for errored directories
      }
    }
    items.push(item);

    // Check limit again after potentially adding children (though count is incremented per item)
    if (state.truncated) {
       break; // Exit loop if limit was hit during recursive call
    }
  }

  // Sort items: directories first, then files, alphabetically
  items.sort((a, b) => {
    if (a.isDirectory !== b.isDirectory) {
      return a.isDirectory ? -1 : 1; // Directories first
    }
    return a.name.localeCompare(b.name); // Then sort alphabetically
  });

  return items;
};

/**
 * Formats the directory items into a tree-like string.
 *
 * @param {DirectoryItem[]} items - The items to format.
 * @param {string} prefix - The prefix string for indentation.
 * @param {boolean} truncated - Whether the listing was cut short due to limits.
 * @returns {string} The formatted tree string.
 */
const formatTree = (items: DirectoryItem[], truncated: boolean, prefix = ''): string => {
  let treeString = '';
  items.forEach((item, index) => {
    const isLast = index === items.length - 1;
    const connector = isLast ? '└── ' : '├── ';
    const itemPrefix = item.isDirectory ? '📁 ' : '📄 ';
    const errorMarker = item.error ? ` [Error: ${item.error}]` : ''; // Add error marker if present
    treeString += `${prefix}${connector}${itemPrefix}${item.name}${errorMarker}\n`;

    // Only recurse if it's a directory, has children defined (not errored), and children exist
    if (item.isDirectory && !item.error && item.children && item.children.length > 0) {
      const childPrefix = prefix + (isLast ? '    ' : '│   ');
      // Pass truncated flag down, but don't add the message recursively
      treeString += formatTree(item.children, false, childPrefix);
    } else if (item.isDirectory && item.error) {
      // Optionally add a specific marker for children of errored directories,
      // but the error on the parent line is likely sufficient.
    }
  });

  // Add truncation message at the end of the current level if needed
  if (truncated && prefix === '') { // Only add at the top level formatting call
      treeString += `${prefix}...\n${prefix}[Listing truncated due to max entries limit]\n`;
  }

  return treeString;
};

/**
 * Lists files and directories at a given path, optionally recursively.
 *
 * @param {ListFilesInput} input - The input object containing path and options.
 * @param {RequestContext} context - The request context.
 * @returns {Promise<ListFilesOutput>} A promise resolving with the listing results.
 * @throws {McpError} For path errors, directory not found, or I/O errors.
 */
export const listFilesLogic = async (input: ListFilesInput, context: RequestContext): Promise<ListFilesOutput> => {
  // Destructure validated input, including the new maxEntries
  const { path: requestedPath, includeNested, maxEntries } = input;
  const logicContext = { ...context, includeNested, maxEntries };
  logger.debug(`listFilesLogic: Received request for path "${requestedPath}" with limit ${maxEntries}`, logicContext);

  // Resolve the path
  const absolutePath = serverState.resolvePath(requestedPath, context);
  logger.debug(`listFilesLogic: Resolved path to "${absolutePath}"`, { ...logicContext, requestedPath });

  try {
    // Initialize state for tracking count and limit, using the potentially updated default
    const state = { count: 0, limit: maxEntries, truncated: false };

    // Read directory structure using the state object
    const items = await readDirectoryRecursive(absolutePath, includeNested, logicContext, state);

    // Format the tree, passing the final truncated state
    const rootName = path.basename(absolutePath);
    const tree = `📁 ${rootName}\n` + formatTree(items, state.truncated); // Pass truncated flag

    const message = state.truncated
      ? `Successfully listed ${state.count} items in ${absolutePath} (truncated at limit of ${maxEntries}).` // Use maxEntries from input for message
      : `Successfully listed ${state.count} items in ${absolutePath}.`;

    logger.info(`listFilesLogic: ${message}`, { ...logicContext, requestedPath, itemCount: state.count, truncated: state.truncated, limit: maxEntries });

    return {
      message: message,
      tree: tree,
      requestedPath: requestedPath,
      resolvedPath: absolutePath,
      itemCount: state.count, // Return the actual count processed
      truncated: state.truncated,
    };

  } catch (error: any) {
    // Errors during readDirectoryRecursive are already logged and potentially thrown as McpError
    logger.error(`listFilesLogic: Error listing files at "${absolutePath}"`, { ...logicContext, requestedPath, error: error.message, code: error.code });
    if (error instanceof McpError) {
      throw error; // Re-throw known McpErrors
    }
    // Catch any other unexpected errors
    throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to list files: ${error.message || 'Unknown I/O error'}`, { ...context, requestedPath, resolvedPath: absolutePath, originalError: error });
  }
};

```
Page 1/2FirstPrevNextLast