mcp-ts-template 1.2.7 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +71 -63
  2. package/dist/config/index.js +6 -3
  3. package/dist/index.js +69 -22
  4. package/dist/mcp-client/{configLoader.d.ts → client-config/configLoader.d.ts} +19 -4
  5. package/dist/mcp-client/{configLoader.js → client-config/configLoader.js} +44 -66
  6. package/dist/mcp-client/core/clientCache.d.ts +59 -0
  7. package/dist/mcp-client/core/clientCache.js +117 -0
  8. package/dist/mcp-client/core/clientConnectionLogic.d.ts +33 -0
  9. package/dist/mcp-client/core/clientConnectionLogic.js +108 -0
  10. package/dist/mcp-client/core/clientManager.d.ts +61 -0
  11. package/dist/mcp-client/core/clientManager.js +160 -0
  12. package/dist/mcp-client/index.d.ts +4 -4
  13. package/dist/mcp-client/index.js +5 -5
  14. package/dist/mcp-client/transports/httpClientTransport.d.ts +26 -0
  15. package/dist/mcp-client/transports/httpClientTransport.js +57 -0
  16. package/dist/mcp-client/transports/index.d.ts +8 -0
  17. package/dist/mcp-client/transports/index.js +8 -0
  18. package/dist/mcp-client/{transport.d.ts → transports/stdioClientTransport.d.ts} +6 -19
  19. package/dist/mcp-client/transports/stdioClientTransport.js +77 -0
  20. package/dist/mcp-client/transports/transportFactory.d.ts +23 -0
  21. package/dist/mcp-client/transports/transportFactory.js +81 -0
  22. package/dist/mcp-server/server.d.ts +3 -2
  23. package/dist/mcp-server/server.js +6 -6
  24. package/dist/mcp-server/tools/imageTest/index.d.ts +6 -0
  25. package/dist/mcp-server/tools/imageTest/index.js +6 -0
  26. package/dist/mcp-server/tools/imageTest/logic.d.ts +16 -0
  27. package/dist/mcp-server/tools/imageTest/logic.js +68 -0
  28. package/dist/mcp-server/tools/imageTest/registration.d.ts +10 -0
  29. package/dist/mcp-server/tools/imageTest/registration.js +38 -0
  30. package/dist/mcp-server/transports/httpTransport.d.ts +3 -2
  31. package/dist/mcp-server/transports/httpTransport.js +5 -4
  32. package/dist/services/index.d.ts +1 -1
  33. package/dist/services/index.js +1 -1
  34. package/dist/services/llm-providers/index.d.ts +1 -1
  35. package/dist/services/llm-providers/index.js +1 -1
  36. package/dist/services/llm-providers/llmFactory.d.ts +4 -4
  37. package/dist/services/llm-providers/llmFactory.js +26 -18
  38. package/dist/services/llm-providers/openRouter/index.d.ts +1 -1
  39. package/dist/services/llm-providers/openRouter/index.js +1 -1
  40. package/dist/services/llm-providers/openRouter/openRouterProvider.js +8 -4
  41. package/dist/utils/internal/logger.js +10 -4
  42. package/package.json +5 -2
  43. package/dist/mcp-client/client.d.ts +0 -98
  44. package/dist/mcp-client/client.js +0 -315
  45. package/dist/mcp-client/transport.js +0 -161
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # MCP TypeScript Template 🚀
2
2
 
3
3
  [![TypeScript](https://img.shields.io/badge/TypeScript-^5.8.3-blue.svg)](https://www.typescriptlang.org/)
4
- [![Model Context Protocol SDK](https://img.shields.io/badge/MCP%20SDK-1.11.5-green.svg)](https://github.com/modelcontextprotocol/typescript-sdk)
4
+ [![Model Context Protocol SDK](https://img.shields.io/badge/MCP%20SDK-1.12.0-green.svg)](https://github.com/modelcontextprotocol/typescript-sdk)
5
5
  [![MCP Spec Version](https://img.shields.io/badge/MCP%20Spec-2025--03--26-lightgrey.svg)](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/changelog.mdx)
6
- [![Version](https://img.shields.io/badge/Version-1.2.7-blue.svg)](./CHANGELOG.md)
6
+ [![Version](https://img.shields.io/badge/Version-1.3.2-blue.svg)](./CHANGELOG.md)
7
7
  [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
8
8
  [![Status](https://img.shields.io/badge/Status-Stable-green.svg)](https://github.com/cyanheads/mcp-ts-template/issues)
9
9
  [![GitHub](https://img.shields.io/github/stars/cyanheads/mcp-ts-template?style=social)](https://github.com/cyanheads/mcp-ts-template)
@@ -97,39 +97,39 @@ Get the example server running in minutes:
97
97
 
98
98
  Configure the MCP server's behavior using these environment variables:
99
99
 
100
- | Variable | Description | Default |
101
- | :------------------------ | :-------------------------------------------------------------------------------------------------- | :----------------------------------------- |
102
- | `MCP_TRANSPORT_TYPE` | Server transport: `stdio` or `http`. | `stdio` |
103
- | `MCP_HTTP_PORT` | Port for the HTTP server (if `MCP_TRANSPORT_TYPE=http`). | `3010` |
104
- | `MCP_HTTP_HOST` | Host address for the HTTP server (if `MCP_TRANSPORT_TYPE=http`). | `127.0.0.1` |
105
- | `MCP_ALLOWED_ORIGINS` | Comma-separated allowed origins for CORS (if `MCP_TRANSPORT_TYPE=http`). | (none) |
106
- | `MCP_SERVER_NAME` | Optional server name (used in MCP initialization). | (from package.json) |
107
- | `MCP_SERVER_VERSION` | Optional server version (used in MCP initialization). | (from package.json) |
108
- | `MCP_LOG_LEVEL` | Server logging level (`debug`, `info`, `warning`, `error`, etc.). | `debug` |
109
- | `LOGS_DIR` | Directory for log files. | `logs/` (in project root) |
110
- | `NODE_ENV` | Runtime environment (`development`, `production`). | `development` |
111
- | `MCP_AUTH_SECRET_KEY` | **Required for HTTP transport.** Secret key (min 32 chars) for signing/verifying auth tokens (JWT). | (none - **MUST be set in production**) |
112
- | `OPENROUTER_APP_URL` | URL of the application (used by OpenRouter service for HTTP Referer). | `http://localhost:3000` |
113
- | `OPENROUTER_APP_NAME` | Name of the application (used by OpenRouter service for X-Title header). | 'mcp-ts-template' |
114
- | `OPENROUTER_API_KEY` | API key for OpenRouter.ai service. Optional, but service will be unconfigured without it. | (none) |
115
- | `LLM_DEFAULT_MODEL` | Default model to use for LLM calls via OpenRouter. | `google/gemini-2.5-flash-preview-05-20` |
116
- | `LLM_DEFAULT_TEMPERATURE` | Default temperature for LLM calls (0-2). Optional. | (none) |
117
- | `LLM_DEFAULT_TOP_P` | Default top_p for LLM calls (0-1). Optional. | (none) |
118
- | `LLM_DEFAULT_MAX_TOKENS` | Default max_tokens for LLM calls. Optional. | (none) |
119
- | `LLM_DEFAULT_TOP_K` | Default top_k for LLM calls (non-negative integer). Optional. | (none) |
120
- | `LLM_DEFAULT_MIN_P` | Default min_p for LLM calls (0-1). Optional. | (none) |
100
+ | Variable | Description | Default |
101
+ | :------------------------ | :-------------------------------------------------------------------------------------------------- | :-------------------------------------- |
102
+ | `MCP_TRANSPORT_TYPE` | Server transport: `stdio` or `http`. | `stdio` |
103
+ | `MCP_HTTP_PORT` | Port for the HTTP server (if `MCP_TRANSPORT_TYPE=http`). | `3010` |
104
+ | `MCP_HTTP_HOST` | Host address for the HTTP server (if `MCP_TRANSPORT_TYPE=http`). | `127.0.0.1` |
105
+ | `MCP_ALLOWED_ORIGINS` | Comma-separated allowed origins for CORS (if `MCP_TRANSPORT_TYPE=http`). | (none) |
106
+ | `MCP_SERVER_NAME` | Optional server name (used in MCP initialization). | (from package.json) |
107
+ | `MCP_SERVER_VERSION` | Optional server version (used in MCP initialization). | (from package.json) |
108
+ | `MCP_LOG_LEVEL` | Server logging level (`debug`, `info`, `warning`, `error`, etc.). | `debug` |
109
+ | `LOGS_DIR` | Directory for log files. | `logs/` (in project root) |
110
+ | `NODE_ENV` | Runtime environment (`development`, `production`). | `development` |
111
+ | `MCP_AUTH_SECRET_KEY` | **Required for HTTP transport.** Secret key (min 32 chars) for signing/verifying auth tokens (JWT). | (none - **MUST be set in production**) |
112
+ | `OPENROUTER_APP_URL` | URL of the application (used by OpenRouter service for HTTP Referer). | `http://localhost:3000` |
113
+ | `OPENROUTER_APP_NAME` | Name of the application (used by OpenRouter service for X-Title header). | 'mcp-ts-template' |
114
+ | `OPENROUTER_API_KEY` | API key for OpenRouter.ai service. Optional, but service will be unconfigured without it. | (none) |
115
+ | `LLM_DEFAULT_MODEL` | Default model to use for LLM calls via OpenRouter. | `google/gemini-2.5-flash-preview-05-20` |
116
+ | `LLM_DEFAULT_TEMPERATURE` | Default temperature for LLM calls (0-2). Optional. | (none) |
117
+ | `LLM_DEFAULT_TOP_P` | Default top_p for LLM calls (0-1). Optional. | (none) |
118
+ | `LLM_DEFAULT_MAX_TOKENS` | Default max_tokens for LLM calls. Optional. | (none) |
119
+ | `LLM_DEFAULT_TOP_K` | Default top_k for LLM calls (non-negative integer). Optional. | (none) |
120
+ | `LLM_DEFAULT_MIN_P` | Default min_p for LLM calls (0-1). Optional. | (none) |
121
121
 
122
122
  **Note on HTTP Port Retries:** If the `MCP_HTTP_PORT` is busy, the server automatically tries the next port (up to 15 times).
123
123
 
124
124
  **Security Note for HTTP Transport:** When using `MCP_TRANSPORT_TYPE=http`, authentication is **mandatory** as per the MCP specification. This template includes JWT-based authentication middleware (`src/mcp-server/transports/authentication/authMiddleware.ts`). You **MUST** set a strong, unique `MCP_AUTH_SECRET_KEY` in your production environment for this security mechanism to function correctly. Failure to do so will result in bypassed authentication checks in development and fatal errors in production.
125
125
 
126
- ### Client Configuration (`mcp-config.json`)
126
+ ### Client Configuration (`src/mcp-client/client-config/mcp-config.json`)
127
127
 
128
- Configure the connections for the built-in **MCP client** using `src/mcp-client/mcp-config.json`. If this file is missing, it falls back to `src/mcp-client/mcp-config.json.example`.
128
+ Configure the connections for the built-in **MCP client** using `src/mcp-client/client-config/mcp-config.json`. If this file is missing, it falls back to `src/mcp-client/client-config/mcp-config.json.example`.
129
129
 
130
130
  This file defines external MCP servers the client can connect to. The client implementation adheres to the **MCP 2025-03-26 specification**.
131
131
 
132
- **Example `mcp-config.json` (see `mcp-config.json.example` for the full version):**
132
+ **Example `mcp-config.json` (see `src/mcp-client/client-config/mcp-config.json.example` for the full version):**
133
133
 
134
134
  ```json
135
135
  {
@@ -156,7 +156,7 @@ This file defines external MCP servers the client can connect to. The client imp
156
156
  - **`env`**: Optional environment variables to set for the server process (`stdio`).
157
157
  - **`transportType`**: `stdio` (default) or `http`.
158
158
 
159
- See `src/mcp-client/configLoader.ts` for the Zod validation schema and `src/mcp-client/mcp-config.json.example` for a complete example.
159
+ See `src/mcp-client/client-config/configLoader.ts` for the Zod validation schema and `src/mcp-client/client-config/mcp-config.json.example` for a complete example.
160
160
 
161
161
  ## 🏗️ Project Structure
162
162
 
@@ -164,10 +164,18 @@ The `src/` directory is organized for clarity:
164
164
 
165
165
  - `config/`: Loads environment variables and package info.
166
166
  - `mcp-client/`: Logic for the client connecting to _external_ MCP servers (updated to MCP 2025-03-26 spec).
167
- - `client.ts`: Core connection management, initialization, capability declaration.
168
- - `configLoader.ts`: Loads and validates `mcp-config.json`.
169
- - `transport.ts`: Creates `stdio` or `http` client transports based on config.
170
- - `mcp-config.json.example`: Example client config. Copy to `mcp-config.json`.
167
+ - `client-config/`: Handles loading and validation of `mcp-config.json`.
168
+ - `configLoader.ts`: Loads and validates server configurations.
169
+ - `mcp-config.json.example`: Example configuration file.
170
+ - `core/`: Core client logic including connection management and caching.
171
+ - `clientManager.ts`: Manages client instances and their lifecycle.
172
+ - `clientConnectionLogic.ts`: Handles the details of connecting and initializing with servers.
173
+ - `clientCache.ts`: Caches active client connections.
174
+ - `transports/`: Manages different communication transports (Stdio, HTTP).
175
+ - `transportFactory.ts`: Creates appropriate transport instances.
176
+ - `stdioClientTransport.ts`: Implements Stdio transport.
177
+ - `httpClientTransport.ts`: Implements HTTP transport.
178
+ - `index.ts`: Barrel file exporting key client functionalities.
171
179
  - `mcp-server/`: Logic for the MCP server _provided by this template_.
172
180
  - `server.ts`: Initializes the server, registers tools/resources.
173
181
  - `resources/`: Example resource implementations (e.g., EchoResource).
@@ -218,38 +226,38 @@ This project is licensed under the Apache License 2.0. See the [LICENSE](LICENSE
218
226
 
219
227
  ## Detailed Features Table
220
228
 
221
- | Category | Feature | Description | Location(s) |
222
- | :----------------------- | :------------------------------ | :----------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------ |
223
- | **Core Components** | MCP Server | Core server logic, tool/resource registration, transport handling. Includes Echo Tool & Resource examples. | `src/mcp-server/` |
224
- | | MCP Client | Logic for connecting to external MCP servers (updated to **MCP 2025-03-26 spec**). | `src/mcp-client/` |
225
- | | Configuration | Environment-aware settings with Zod validation. | `src/config/`, `src/mcp-client/configLoader.ts` |
226
- | | HTTP Transport | Express-based server with SSE, session management, CORS, port retries. | `src/mcp-server/transports/httpTransport.ts` |
227
- | | Stdio Transport | Handles MCP communication over standard input/output. | `src/mcp-server/transports/stdioTransport.ts` |
228
- | **Utilities (Core)** | Logger | Structured, context-aware logging (files with rotation & MCP notifications). | `src/utils/internal/logger.ts` |
229
- | | ErrorHandler | Centralized error processing, classification, and logging. | `src/utils/internal/errorHandler.ts` |
230
- | | RequestContext | Request/operation tracking and correlation. | `src/utils/internal/requestContext.ts` |
231
- | **Utilities (Metrics)** | TokenCounter | Estimates token counts using `tiktoken`. | `src/utils/metrics/tokenCounter.ts` |
232
- | **Utilities (Parsing)** | DateParser | Parses natural language date strings using `chrono-node`. | `src/utils/parsing/dateParser.ts` |
233
- | | JsonParser | Parses potentially partial JSON, handles `<think>` blocks. | `src/utils/parsing/jsonParser.ts` |
234
- | **Utilities (Security)** | IdGenerator | Generates unique IDs (prefixed or UUIDs). | `src/utils/security/idGenerator.ts` |
235
- | | RateLimiter | Request throttling based on keys. | `src/utils/security/rateLimiter.ts` |
236
- | | Sanitization | Input validation/cleaning (HTML, paths, URLs, numbers, JSON) & log redaction (`validator`, `sanitize-html`). | `src/utils/security/sanitization.ts` |
237
- | **Services** | OpenRouter Provider | Service for interacting with OpenRouter API via OpenAI SDK compatibility. | `src/services/llm-providers/openRouter/openRouterProvider.ts` |
238
- | | LLM Provider Factory | Centralized factory for creating LLM client instances (e.g., OpenRouter, Gemini (partial integration in LLMFactory but not usable yet)). | `src/services/llm-providers/llmFactory.ts` |
239
- | **Type Safety** | Global Types | Shared TypeScript definitions for consistent interfaces (Errors, MCP types). | `src/types-global/` |
240
- | | Zod Schemas | Used for robust validation of configuration files and tool/resource inputs. | Throughout (`config`, `mcp-client`, tools, etc.) |
241
- | **Error Handling** | Pattern-Based Classification | Automatically categorize errors based on message patterns. | `src/utils/internal/errorHandler.ts` |
242
- | | Consistent Formatting | Standardized error responses with additional context. | `src/utils/internal/errorHandler.ts` |
243
- | | Safe Try/Catch Patterns | Centralized error processing helpers (`ErrorHandler.tryCatch`). | `src/utils/internal/errorHandler.ts` |
244
- | | Client/Transport Error Handling | Specific handlers for MCP client and transport error handling. | `src/mcp-client/client.ts`, `transport.ts` |
245
- | **Security** | Input Validation | Using `validator` and `zod` for various data type checks. | `src/utils/security/sanitization.ts`, etc. |
246
- | | Input Sanitization | Using `sanitize-html` to prevent injection attacks. | `src/utils/security/sanitization.ts` |
247
- | | Sensitive Data Redaction | Automatic redaction in logs. | `src/utils/security/sanitization.ts` |
248
- | | Configuration Fallback | Safely falls back to `mcp-config.json.example` if primary client config is missing. | `src/mcp-client/configLoader.ts` |
249
- | **Scripts** | Clean Script | Removes `dist` and `logs` directories (or custom targets). | `scripts/clean.ts` |
250
- | | Make Executable Script | Sets executable permissions (`chmod +x`) on specified files (Unix-like only). | `scripts/make-executable.ts` |
251
- | | Tree Script | Generates a directory structure tree, respecting `.gitignore`. | `scripts/tree.ts` |
252
- | | Fetch OpenAPI Spec Script | Fetches an OpenAPI spec (YAML/JSON) from a URL with fallbacks, saves locally. | `scripts/fetch-openapi-spec.ts` |
229
+ | Category | Feature | Description | Location(s) |
230
+ | :----------------------- | :------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------ |
231
+ | **Core Components** | MCP Server | Core server logic, tool/resource registration, transport handling. Includes Echo Tool & Resource examples. | `src/mcp-server/` |
232
+ | | MCP Client | Logic for connecting to external MCP servers (updated to **MCP 2025-03-26 spec**). Refactored for modularity. | `src/mcp-client/` (see subdirs: `core/`, `client-config/`, `transports/`) |
233
+ | | Configuration | Environment-aware settings with Zod validation. | `src/config/`, `src/mcp-client/client-config/configLoader.ts` |
234
+ | | HTTP Transport | Express-based server with SSE, session management, CORS, port retries. | `src/mcp-server/transports/httpTransport.ts` |
235
+ | | Stdio Transport | Handles MCP communication over standard input/output. | `src/mcp-server/transports/stdioTransport.ts` |
236
+ | **Utilities (Core)** | Logger | Structured, context-aware logging (files with rotation & MCP notifications). | `src/utils/internal/logger.ts` |
237
+ | | ErrorHandler | Centralized error processing, classification, and logging. | `src/utils/internal/errorHandler.ts` |
238
+ | | RequestContext | Request/operation tracking and correlation. | `src/utils/internal/requestContext.ts` |
239
+ | **Utilities (Metrics)** | TokenCounter | Estimates token counts using `tiktoken`. | `src/utils/metrics/tokenCounter.ts` |
240
+ | **Utilities (Parsing)** | DateParser | Parses natural language date strings using `chrono-node`. | `src/utils/parsing/dateParser.ts` |
241
+ | | JsonParser | Parses potentially partial JSON, handles `<think>` blocks. | `src/utils/parsing/jsonParser.ts` |
242
+ | **Utilities (Security)** | IdGenerator | Generates unique IDs (prefixed or UUIDs). | `src/utils/security/idGenerator.ts` |
243
+ | | RateLimiter | Request throttling based on keys. | `src/utils/security/rateLimiter.ts` |
244
+ | | Sanitization | Input validation/cleaning (HTML, paths, URLs, numbers, JSON) & log redaction (`validator`, `sanitize-html`). | `src/utils/security/sanitization.ts` |
245
+ | **Services** | OpenRouter Provider | Service for interacting with OpenRouter API via OpenAI SDK compatibility. | `src/services/llm-providers/openRouter/openRouterProvider.ts` |
246
+ | | LLM Provider Factory | Centralized factory for creating LLM client instances (e.g., OpenRouter, Gemini (partial integration in LLMFactory but not usable yet)). | `src/services/llm-providers/llmFactory.ts` |
247
+ | **Type Safety** | Global Types | Shared TypeScript definitions for consistent interfaces (Errors, MCP types). | `src/types-global/` |
248
+ | | Zod Schemas | Used for robust validation of configuration files and tool/resource inputs. | Throughout (`config`, `mcp-client`, tools, etc.) |
249
+ | **Error Handling** | Pattern-Based Classification | Automatically categorize errors based on message patterns. | `src/utils/internal/errorHandler.ts` |
250
+ | | Consistent Formatting | Standardized error responses with additional context. | `src/utils/internal/errorHandler.ts` |
251
+ | | Safe Try/Catch Patterns | Centralized error processing helpers (`ErrorHandler.tryCatch`). | `src/utils/internal/errorHandler.ts` |
252
+ | | Client/Transport Error Handling | Specific handlers for MCP client and transport error handling. | `src/mcp-client/core/`, `src/mcp-client/transports/` |
253
+ | **Security** | Input Validation | Using `validator` and `zod` for various data type checks. | `src/utils/security/sanitization.ts`, etc. |
254
+ | | Input Sanitization | Using `sanitize-html` to prevent injection attacks. | `src/utils/security/sanitization.ts` |
255
+ | | Sensitive Data Redaction | Automatic redaction in logs. | `src/utils/security/sanitization.ts` |
256
+ | | Configuration Fallback | Safely falls back to `mcp-config.json.example` if primary client config is missing. | `src/mcp-client/client-config/configLoader.ts` |
257
+ | **Scripts** | Clean Script | Removes `dist` and `logs` directories (or custom targets). | `scripts/clean.ts` |
258
+ | | Make Executable Script | Sets executable permissions (`chmod +x`) on specified files (Unix-like only). | `scripts/make-executable.ts` |
259
+ | | Tree Script | Generates a directory structure tree, respecting `.gitignore`. | `scripts/tree.ts` |
260
+ | | Fetch OpenAPI Spec Script | Fetches an OpenAPI spec (YAML/JSON) from a URL with fallbacks, saves locally. | `scripts/fetch-openapi-spec.ts` |
253
261
 
254
262
  ---
255
263
 
@@ -29,7 +29,7 @@ dotenv.config();
29
29
  const findProjectRoot = (startDir) => {
30
30
  let currentDir = startDir;
31
31
  while (true) {
32
- const packageJsonPath = join(currentDir, 'package.json');
32
+ const packageJsonPath = join(currentDir, "package.json");
33
33
  if (existsSync(packageJsonPath)) {
34
34
  return currentDir;
35
35
  }
@@ -165,9 +165,12 @@ const env = parsedEnv.success ? parsedEnv.data : EnvSchema.parse({});
165
165
  * @returns The validated, absolute path to the directory, or null if invalid.
166
166
  */
167
167
  const ensureDirectory = (dirPath, rootDir, dirName) => {
168
- const resolvedDirPath = path.isAbsolute(dirPath) ? dirPath : path.resolve(rootDir, dirPath);
168
+ const resolvedDirPath = path.isAbsolute(dirPath)
169
+ ? dirPath
170
+ : path.resolve(rootDir, dirPath);
169
171
  // Ensure the resolved path is within the project root boundary
170
- if (!resolvedDirPath.startsWith(rootDir + path.sep) && resolvedDirPath !== rootDir) {
172
+ if (!resolvedDirPath.startsWith(rootDir + path.sep) &&
173
+ resolvedDirPath !== rootDir) {
171
174
  if (process.stdout.isTTY) {
172
175
  console.error(`Error: ${dirName} path "${dirPath}" resolves to "${resolvedDirPath}", which is outside the project boundary "${rootDir}".`);
173
176
  }
package/dist/index.js CHANGED
@@ -22,16 +22,21 @@
22
22
  * @module src/index
23
23
  */
24
24
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
25
+ import http from "http"; // Import http module
25
26
  import { config, environment } from "./config/index.js";
26
27
  import { initializeAndStartServer } from "./mcp-server/server.js";
27
28
  import { requestContextService } from "./utils/index.js";
28
29
  import { logger } from "./utils/internal/logger.js";
29
30
  /**
30
31
  * Holds the main MCP server instance, primarily for STDIO transport.
31
- * For HTTP transport, server instances are typically managed per session.
32
32
  * @private
33
33
  */
34
- let server;
34
+ let mcpStdioServer;
35
+ /**
36
+ * Holds the Node.js HTTP server instance if HTTP transport is used.
37
+ * @private
38
+ */
39
+ let actualHttpServer;
35
40
  /**
36
41
  * Gracefully shuts down the main MCP server and associated resources.
37
42
  * Called on process termination signals or critical unhandled errors.
@@ -46,25 +51,63 @@ const shutdown = async (signal) => {
46
51
  triggerEvent: signal,
47
52
  });
48
53
  logger.info(`Received ${signal}. Initiating graceful shutdown...`, shutdownContext);
49
- try {
50
- if (server) {
51
- logger.info("Attempting to close main MCP server...", shutdownContext);
52
- await server.close();
53
- logger.info("Main MCP server closed successfully.", shutdownContext);
54
+ let mcpClosed = false;
55
+ let httpClosed = false;
56
+ let closeError = null;
57
+ const checkAndExit = () => {
58
+ if (closeError) {
59
+ logger.error("Critical error encountered during shutdown process.", {
60
+ ...shutdownContext,
61
+ errorMessage: closeError.message,
62
+ errorStack: closeError.stack,
63
+ });
64
+ process.exit(1);
54
65
  }
55
- else {
56
- logger.notice("No global server instance found to close during shutdown (this may be normal for HTTP transport).", shutdownContext);
66
+ else if (mcpClosed && httpClosed) {
67
+ logger.info("Graceful shutdown completed successfully. Exiting.", shutdownContext);
68
+ process.exit(0);
57
69
  }
58
- logger.info("Graceful shutdown completed successfully. Exiting.", shutdownContext);
59
- process.exit(0);
70
+ };
71
+ if (mcpStdioServer) {
72
+ logger.info("Attempting to close main MCP server (STDIO)...", shutdownContext);
73
+ mcpStdioServer.close()
74
+ .then(() => {
75
+ logger.info("Main MCP server (STDIO) closed successfully.", shutdownContext);
76
+ mcpClosed = true;
77
+ checkAndExit();
78
+ })
79
+ .catch((err) => {
80
+ logger.error("Error closing MCP server (STDIO).", { ...shutdownContext, error: err });
81
+ mcpClosed = true; // Consider it closed even on error to allow exit
82
+ if (!closeError)
83
+ closeError = err;
84
+ checkAndExit();
85
+ });
60
86
  }
61
- catch (error) {
62
- logger.error("Critical error encountered during shutdown process.", {
63
- ...shutdownContext,
64
- errorMessage: error instanceof Error ? error.message : String(error),
65
- errorStack: error instanceof Error ? error.stack : undefined,
87
+ else {
88
+ mcpClosed = true; // No STDIO McpServer to close
89
+ }
90
+ if (actualHttpServer) {
91
+ logger.info("Attempting to close HTTP server...", shutdownContext);
92
+ actualHttpServer.close((err) => {
93
+ if (err) {
94
+ logger.error("Error closing HTTP server.", { ...shutdownContext, error: err });
95
+ if (!closeError)
96
+ closeError = err;
97
+ }
98
+ else {
99
+ logger.info("HTTP server closed successfully.", shutdownContext);
100
+ }
101
+ httpClosed = true;
102
+ checkAndExit();
66
103
  });
67
- process.exit(1);
104
+ }
105
+ else {
106
+ httpClosed = true; // No HTTP server to close
107
+ }
108
+ // Initial check in case no servers needed closing
109
+ if (mcpClosed && httpClosed) {
110
+ checkAndExit();
68
111
  }
69
112
  };
70
113
  /**
@@ -122,14 +165,18 @@ const start = async () => {
122
165
  logger.info(`Starting ${config.mcpServerName} (Version: ${config.mcpServerVersion}, Transport: ${transportType}, Env: ${environment})...`, startupContext);
123
166
  try {
124
167
  logger.debug("Calling initializeAndStartServer to set up MCP transport...", startupContext);
125
- const potentialServerInstance = await initializeAndStartServer();
126
- if (transportType === "stdio" &&
127
- potentialServerInstance instanceof McpServer) {
128
- server = potentialServerInstance;
168
+ const serverInstance = await initializeAndStartServer();
169
+ if (transportType === "stdio" && serverInstance instanceof McpServer) {
170
+ mcpStdioServer = serverInstance;
129
171
  logger.info("STDIO McpServer instance stored globally for shutdown.", startupContext);
130
172
  }
173
+ else if (transportType === "http" && serverInstance instanceof http.Server) {
174
+ actualHttpServer = serverInstance;
175
+ logger.info("HTTP transport initialized, http.Server instance stored globally for shutdown.", startupContext);
176
+ }
131
177
  else if (transportType === "http") {
132
- logger.info("HTTP transport initialized. Server lifecycle managed by HTTP listener and session handlers.", startupContext);
178
+ // This case should ideally not be reached if initializeAndStartServer correctly returns http.Server
179
+ logger.warning("HTTP transport initialized, but no http.Server instance was returned to index.ts. Shutdown might be incomplete.", startupContext);
133
180
  }
134
181
  logger.info(`${config.mcpServerName} is now running and ready to accept connections via ${transportType} transport.`, {
135
182
  ...startupContext,
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import { RequestContext } from "../utils/index.js";
2
+ import { RequestContext } from "../../utils/index.js";
3
3
  /**
4
4
  * Zod schema for a single MCP server's configuration entry.
5
5
  * Defines the command, arguments, environment variables, and transport type for an MCP server.
@@ -10,16 +10,22 @@ export declare const McpServerConfigEntrySchema: z.ZodObject<{
10
10
  args: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
11
11
  env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
12
12
  transportType: z.ZodDefault<z.ZodEnum<["stdio", "http"]>>;
13
+ disabled: z.ZodOptional<z.ZodBoolean>;
14
+ autoApprove: z.ZodOptional<z.ZodBoolean>;
13
15
  }, "strip", z.ZodTypeAny, {
14
16
  transportType: "stdio" | "http";
15
17
  command: string;
16
18
  args: string[];
19
+ disabled?: boolean | undefined;
17
20
  env?: Record<string, string> | undefined;
21
+ autoApprove?: boolean | undefined;
18
22
  }, {
19
23
  command: string;
24
+ disabled?: boolean | undefined;
20
25
  transportType?: "stdio" | "http" | undefined;
21
26
  args?: string[] | undefined;
22
27
  env?: Record<string, string> | undefined;
28
+ autoApprove?: boolean | undefined;
23
29
  }>;
24
30
  /**
25
31
  * Represents the configuration for a single MCP server.
@@ -36,30 +42,40 @@ export declare const McpClientConfigFileSchema: z.ZodObject<{
36
42
  args: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
37
43
  env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
38
44
  transportType: z.ZodDefault<z.ZodEnum<["stdio", "http"]>>;
45
+ disabled: z.ZodOptional<z.ZodBoolean>;
46
+ autoApprove: z.ZodOptional<z.ZodBoolean>;
39
47
  }, "strip", z.ZodTypeAny, {
40
48
  transportType: "stdio" | "http";
41
49
  command: string;
42
50
  args: string[];
51
+ disabled?: boolean | undefined;
43
52
  env?: Record<string, string> | undefined;
53
+ autoApprove?: boolean | undefined;
44
54
  }, {
45
55
  command: string;
56
+ disabled?: boolean | undefined;
46
57
  transportType?: "stdio" | "http" | undefined;
47
58
  args?: string[] | undefined;
48
59
  env?: Record<string, string> | undefined;
60
+ autoApprove?: boolean | undefined;
49
61
  }>>;
50
62
  }, "strip", z.ZodTypeAny, {
51
63
  mcpServers: Record<string, {
52
64
  transportType: "stdio" | "http";
53
65
  command: string;
54
66
  args: string[];
67
+ disabled?: boolean | undefined;
55
68
  env?: Record<string, string> | undefined;
69
+ autoApprove?: boolean | undefined;
56
70
  }>;
57
71
  }, {
58
72
  mcpServers: Record<string, {
59
73
  command: string;
74
+ disabled?: boolean | undefined;
60
75
  transportType?: "stdio" | "http" | undefined;
61
76
  args?: string[] | undefined;
62
77
  env?: Record<string, string> | undefined;
78
+ autoApprove?: boolean | undefined;
63
79
  }>;
64
80
  }>;
65
81
  /**
@@ -68,13 +84,12 @@ export declare const McpClientConfigFileSchema: z.ZodObject<{
68
84
  */
69
85
  export type McpClientConfigFile = z.infer<typeof McpClientConfigFileSchema>;
70
86
  /**
71
- * Loads, validates, and caches the MCP client configuration from `mcp-config.json`
72
- * or `mcp-config.json.example`.
87
+ * Loads, validates, and caches the MCP client configuration from `mcp-config.json`.
73
88
  * The configuration is validated against {@link McpClientConfigFileSchema}.
74
89
  *
75
90
  * @param parentContext - Optional parent request context for logging and tracing.
76
91
  * @returns The loaded and validated MCP server configurations object.
77
- * @throws {McpError} If neither config file can be read, or if parsing or validation fails.
92
+ * @throws {McpError} If the config file cannot be read, or if parsing or validation fails.
78
93
  */
79
94
  export declare function loadMcpClientConfig(parentContext?: RequestContext | null): McpClientConfigFile;
80
95
  /**
@@ -1,16 +1,16 @@
1
1
  /**
2
- * @fileoverview Loads and validates MCP client configuration from JSON files.
2
+ * @fileoverview Loads and validates MCP client configuration from a JSON file.
3
3
  * This module defines Zod schemas for the configuration structure, provides functions
4
- * to load configuration from `mcp-config.json` (with a fallback to `mcp-config.json.example`),
5
- * and retrieves specific server configurations.
6
- * @module src/mcp-client/configLoader
4
+ * to load configuration from `mcp-config.json`, and retrieves specific server
5
+ * configurations.
6
+ * @module src/mcp-client/client-config/configLoader
7
7
  */
8
8
  import { existsSync, readFileSync } from "fs";
9
9
  import { dirname, join } from "path";
10
10
  import { fileURLToPath } from "url";
11
11
  import { z } from "zod";
12
- import { BaseErrorCode, McpError } from "../types-global/errors.js";
13
- import { logger, requestContextService, } from "../utils/index.js";
12
+ import { BaseErrorCode, McpError } from "../../types-global/errors.js";
13
+ import { logger, requestContextService, } from "../../utils/index.js";
14
14
  // --- Zod Schemas for Configuration Validation ---
15
15
  /**
16
16
  * Zod schema for environment variables passed to an MCP server process.
@@ -37,8 +37,14 @@ export const McpServerConfigEntrySchema = z.object({
37
37
  .enum(["stdio", "http"])
38
38
  .default("stdio")
39
39
  .describe("Communication transport type ('stdio' or 'http')."),
40
- // disabled: z.boolean().optional().describe("If true, this server configuration is ignored."),
41
- // autoApprove: z.boolean().optional().describe("If true, skip user approval prompts for this server (use with caution)."),
40
+ disabled: z
41
+ .boolean()
42
+ .optional()
43
+ .describe("If true, this server configuration is ignored."),
44
+ autoApprove: z
45
+ .boolean()
46
+ .optional()
47
+ .describe("If true, skip user approval prompts for this server (use with caution)."),
42
48
  });
43
49
  /**
44
50
  * Zod schema for the root structure of the `mcp-config.json` file.
@@ -51,18 +57,18 @@ export const McpClientConfigFileSchema = z.object({
51
57
  });
52
58
  // --- Configuration Loading Logic ---
53
59
  const __dirname = dirname(fileURLToPath(import.meta.url));
54
- const primaryConfigPath = join(__dirname, "mcp-config.json");
55
- const exampleConfigPath = join(__dirname, "mcp-config.json.example");
60
+ // Path when running from dist: __dirname is .../dist/mcp-client/client-config
61
+ // We want to reach: .../src/mcp-client/client-config/mcp-config.json
62
+ const primaryConfigPath = join(__dirname, "../../../src/mcp-client/client-config/mcp-config.json");
56
63
  let loadedConfig = null;
57
64
  let loadedConfigPath = null;
58
65
  /**
59
- * Loads, validates, and caches the MCP client configuration from `mcp-config.json`
60
- * or `mcp-config.json.example`.
66
+ * Loads, validates, and caches the MCP client configuration from `mcp-config.json`.
61
67
  * The configuration is validated against {@link McpClientConfigFileSchema}.
62
68
  *
63
69
  * @param parentContext - Optional parent request context for logging and tracing.
64
70
  * @returns The loaded and validated MCP server configurations object.
65
- * @throws {McpError} If neither config file can be read, or if parsing or validation fails.
71
+ * @throws {McpError} If the config file cannot be read, or if parsing or validation fails.
66
72
  */
67
73
  export function loadMcpClientConfig(parentContext) {
68
74
  const context = requestContextService.createRequestContext({
@@ -73,60 +79,30 @@ export function loadMcpClientConfig(parentContext) {
73
79
  logger.debug(`Returning cached MCP client config from: ${loadedConfigPath}`, context);
74
80
  return loadedConfig;
75
81
  }
76
- let fileContent = null;
77
- let configPathToLog = "";
78
- if (existsSync(primaryConfigPath)) {
79
- logger.info(`Attempting to load primary MCP config: ${primaryConfigPath}`, context);
80
- try {
81
- fileContent = readFileSync(primaryConfigPath, "utf-8");
82
- configPathToLog = primaryConfigPath;
83
- logger.info(`Successfully read primary config file.`, {
84
- ...context,
85
- filePath: configPathToLog,
86
- });
87
- }
88
- catch (readError) {
89
- logger.warning(`Failed to read primary config file at ${primaryConfigPath}, attempting fallback.`, {
90
- ...context,
91
- filePath: primaryConfigPath,
92
- error: readError instanceof Error ? readError.message : String(readError),
93
- });
94
- }
95
- }
96
- else {
97
- logger.info(`Primary config file not found at ${primaryConfigPath}, attempting fallback.`, {
82
+ let fileContent;
83
+ const configPathToLog = primaryConfigPath; // Only attempt to load the primary config
84
+ if (!existsSync(primaryConfigPath)) {
85
+ logger.error(`MCP client config file not found at ${primaryConfigPath}.`, {
98
86
  ...context,
99
87
  filePath: primaryConfigPath,
100
88
  });
89
+ throw new McpError(BaseErrorCode.CONFIGURATION_ERROR, `MCP client config file not found: ${primaryConfigPath} does not exist.`, context);
101
90
  }
102
- if (!fileContent) {
103
- if (existsSync(exampleConfigPath)) {
104
- logger.info(`Attempting to load example MCP config: ${exampleConfigPath}`, context);
105
- try {
106
- fileContent = readFileSync(exampleConfigPath, "utf-8");
107
- configPathToLog = exampleConfigPath;
108
- logger.info(`Successfully read example config file.`, {
109
- ...context,
110
- filePath: configPathToLog,
111
- });
112
- }
113
- catch (readError) {
114
- logger.error(`Failed to read example config file as well.`, {
115
- ...context,
116
- filePath: exampleConfigPath,
117
- error: readError instanceof Error ? readError.message : String(readError),
118
- });
119
- throw new McpError(BaseErrorCode.CONFIGURATION_ERROR, `Failed to read MCP client config: Neither ${primaryConfigPath} nor ${exampleConfigPath} could be read.`, { originalError: readError });
120
- }
121
- }
122
- else {
123
- logger.error(`Neither primary nor example config file found.`, {
124
- ...context,
125
- primaryPath: primaryConfigPath,
126
- examplePath: exampleConfigPath,
127
- });
128
- throw new McpError(BaseErrorCode.CONFIGURATION_ERROR, `MCP client config file not found: Looked for ${primaryConfigPath} and ${exampleConfigPath}.`);
129
- }
91
+ logger.info(`Attempting to load MCP config from: ${primaryConfigPath}`, context);
92
+ try {
93
+ fileContent = readFileSync(primaryConfigPath, "utf-8");
94
+ logger.info(`Successfully read config file: ${primaryConfigPath}`, {
95
+ ...context,
96
+ filePath: configPathToLog,
97
+ });
98
+ }
99
+ catch (readError) {
100
+ logger.error(`Failed to read MCP client config file: ${primaryConfigPath}`, {
101
+ ...context,
102
+ filePath: primaryConfigPath,
103
+ error: readError instanceof Error ? readError.message : String(readError),
104
+ });
105
+ throw new McpError(BaseErrorCode.CONFIGURATION_ERROR, `Failed to read MCP client config file ${primaryConfigPath}: ${readError instanceof Error ? readError.message : String(readError)}`, { originalError: readError, ...context });
130
106
  }
131
107
  try {
132
108
  const parsedJson = JSON.parse(fileContent);
@@ -140,6 +116,7 @@ export function loadMcpClientConfig(parentContext) {
140
116
  const errorMessages = validationResult.error.errors
141
117
  .map((e) => `${e.path.join(".")}: ${e.message}`)
142
118
  .join("; ");
119
+ // The comment about ErrorHandlerOption was here, removing it.
143
120
  throw new Error(`Validation failed: ${errorMessages}`);
144
121
  }
145
122
  loadedConfig = validationResult.data;
@@ -158,7 +135,7 @@ export function loadMcpClientConfig(parentContext) {
158
135
  error: errorMessage,
159
136
  stack: error instanceof Error ? error.stack : undefined,
160
137
  });
161
- throw new McpError(BaseErrorCode.CONFIGURATION_ERROR, `Failed to load/validate MCP client config from ${configPathToLog}: ${errorMessage}`, { originalError: error });
138
+ throw new McpError(BaseErrorCode.CONFIGURATION_ERROR, `Failed to load/validate MCP client config from ${configPathToLog}: ${errorMessage}`, { originalError: error, ...context });
162
139
  }
163
140
  }
164
141
  /**
@@ -181,8 +158,9 @@ export function getMcpServerConfig(serverName, parentContext) {
181
158
  const serverConfig = config.mcpServers[serverName];
182
159
  if (!serverConfig) {
183
160
  logger.error(`Configuration for MCP server "${serverName}" not found in ${configPath}.`, context);
184
- throw new McpError(BaseErrorCode.CONFIGURATION_ERROR, `Configuration for MCP server "${serverName}" not found in ${configPath}.`);
161
+ throw new McpError(BaseErrorCode.CONFIGURATION_ERROR, `Configuration for MCP server "${serverName}" not found in ${configPath}.`, context);
185
162
  }
186
163
  logger.debug(`Retrieved configuration for server "${serverName}" from ${configPath}`, context);
187
- return { ...serverConfig }; // Return a copy
164
+ // Return a deep copy to prevent accidental modification of the cached config
165
+ return JSON.parse(JSON.stringify(serverConfig));
188
166
  }