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.
- package/README.md +71 -63
- package/dist/config/index.js +6 -3
- package/dist/index.js +69 -22
- package/dist/mcp-client/{configLoader.d.ts → client-config/configLoader.d.ts} +19 -4
- package/dist/mcp-client/{configLoader.js → client-config/configLoader.js} +44 -66
- package/dist/mcp-client/core/clientCache.d.ts +59 -0
- package/dist/mcp-client/core/clientCache.js +117 -0
- package/dist/mcp-client/core/clientConnectionLogic.d.ts +33 -0
- package/dist/mcp-client/core/clientConnectionLogic.js +108 -0
- package/dist/mcp-client/core/clientManager.d.ts +61 -0
- package/dist/mcp-client/core/clientManager.js +160 -0
- package/dist/mcp-client/index.d.ts +4 -4
- package/dist/mcp-client/index.js +5 -5
- package/dist/mcp-client/transports/httpClientTransport.d.ts +26 -0
- package/dist/mcp-client/transports/httpClientTransport.js +57 -0
- package/dist/mcp-client/transports/index.d.ts +8 -0
- package/dist/mcp-client/transports/index.js +8 -0
- package/dist/mcp-client/{transport.d.ts → transports/stdioClientTransport.d.ts} +6 -19
- package/dist/mcp-client/transports/stdioClientTransport.js +77 -0
- package/dist/mcp-client/transports/transportFactory.d.ts +23 -0
- package/dist/mcp-client/transports/transportFactory.js +81 -0
- package/dist/mcp-server/server.d.ts +3 -2
- package/dist/mcp-server/server.js +6 -6
- package/dist/mcp-server/tools/imageTest/index.d.ts +6 -0
- package/dist/mcp-server/tools/imageTest/index.js +6 -0
- package/dist/mcp-server/tools/imageTest/logic.d.ts +16 -0
- package/dist/mcp-server/tools/imageTest/logic.js +68 -0
- package/dist/mcp-server/tools/imageTest/registration.d.ts +10 -0
- package/dist/mcp-server/tools/imageTest/registration.js +38 -0
- package/dist/mcp-server/transports/httpTransport.d.ts +3 -2
- package/dist/mcp-server/transports/httpTransport.js +5 -4
- package/dist/services/index.d.ts +1 -1
- package/dist/services/index.js +1 -1
- package/dist/services/llm-providers/index.d.ts +1 -1
- package/dist/services/llm-providers/index.js +1 -1
- package/dist/services/llm-providers/llmFactory.d.ts +4 -4
- package/dist/services/llm-providers/llmFactory.js +26 -18
- package/dist/services/llm-providers/openRouter/index.d.ts +1 -1
- package/dist/services/llm-providers/openRouter/index.js +1 -1
- package/dist/services/llm-providers/openRouter/openRouterProvider.js +8 -4
- package/dist/utils/internal/logger.js +10 -4
- package/package.json +5 -2
- package/dist/mcp-client/client.d.ts +0 -98
- package/dist/mcp-client/client.js +0 -315
- 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
|
[](https://www.typescriptlang.org/)
|
|
4
|
-
[](https://github.com/modelcontextprotocol/typescript-sdk)
|
|
5
5
|
[](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/changelog.mdx)
|
|
6
|
-
[](./CHANGELOG.md)
|
|
7
7
|
[](https://opensource.org/licenses/Apache-2.0)
|
|
8
8
|
[](https://github.com/cyanheads/mcp-ts-template/issues)
|
|
9
9
|
[](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
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
- `
|
|
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
|
|
222
|
-
| :----------------------- | :------------------------------ |
|
|
223
|
-
| **Core Components** | MCP Server | Core server logic, tool/resource registration, transport handling. Includes Echo Tool & Resource examples.
|
|
224
|
-
| | MCP Client | Logic for connecting to external MCP servers (updated to **MCP 2025-03-26 spec**).
|
|
225
|
-
| | Configuration | Environment-aware settings with Zod validation.
|
|
226
|
-
| | HTTP Transport | Express-based server with SSE, session management, CORS, port retries.
|
|
227
|
-
| | Stdio Transport | Handles MCP communication over standard input/output.
|
|
228
|
-
| **Utilities (Core)** | Logger | Structured, context-aware logging (files with rotation & MCP notifications).
|
|
229
|
-
| | ErrorHandler | Centralized error processing, classification, and logging.
|
|
230
|
-
| | RequestContext | Request/operation tracking and correlation.
|
|
231
|
-
| **Utilities (Metrics)** | TokenCounter | Estimates token counts using `tiktoken`.
|
|
232
|
-
| **Utilities (Parsing)** | DateParser | Parses natural language date strings using `chrono-node`.
|
|
233
|
-
| | JsonParser | Parses potentially partial JSON, handles `<think>` blocks.
|
|
234
|
-
| **Utilities (Security)** | IdGenerator | Generates unique IDs (prefixed or UUIDs).
|
|
235
|
-
| | RateLimiter | Request throttling based on keys.
|
|
236
|
-
| | Sanitization | Input validation/cleaning (HTML, paths, URLs, numbers, JSON) & log redaction (`validator`, `sanitize-html`).
|
|
237
|
-
| **Services** | OpenRouter Provider | Service for interacting with OpenRouter API via OpenAI SDK compatibility.
|
|
238
|
-
| | LLM Provider Factory | Centralized factory for creating LLM client instances (e.g., OpenRouter, Gemini (partial integration in LLMFactory but not usable yet)).
|
|
239
|
-
| **Type Safety** | Global Types | Shared TypeScript definitions for consistent interfaces (Errors, MCP types).
|
|
240
|
-
| | Zod Schemas | Used for robust validation of configuration files and tool/resource inputs.
|
|
241
|
-
| **Error Handling** | Pattern-Based Classification | Automatically categorize errors based on message patterns.
|
|
242
|
-
| | Consistent Formatting | Standardized error responses with additional context.
|
|
243
|
-
| | Safe Try/Catch Patterns | Centralized error processing helpers (`ErrorHandler.tryCatch`).
|
|
244
|
-
| | Client/Transport Error Handling | Specific handlers for MCP client and transport error handling.
|
|
245
|
-
| **Security** | Input Validation | Using `validator` and `zod` for various data type checks.
|
|
246
|
-
| | Input Sanitization | Using `sanitize-html` to prevent injection attacks.
|
|
247
|
-
| | Sensitive Data Redaction | Automatic redaction in logs.
|
|
248
|
-
| | Configuration Fallback | Safely falls back to `mcp-config.json.example` if primary client config is missing.
|
|
249
|
-
| **Scripts** | Clean Script | Removes `dist` and `logs` directories (or custom targets).
|
|
250
|
-
| | Make Executable Script | Sets executable permissions (`chmod +x`) on specified files (Unix-like only).
|
|
251
|
-
| | Tree Script | Generates a directory structure tree, respecting `.gitignore`.
|
|
252
|
-
| | Fetch OpenAPI Spec Script | Fetches an OpenAPI spec (YAML/JSON) from a URL with fallbacks, saves locally.
|
|
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
|
|
package/dist/config/index.js
CHANGED
|
@@ -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,
|
|
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)
|
|
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) &&
|
|
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
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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.
|
|
66
|
+
else if (mcpClosed && httpClosed) {
|
|
67
|
+
logger.info("Graceful shutdown completed successfully. Exiting.", shutdownContext);
|
|
68
|
+
process.exit(0);
|
|
57
69
|
}
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
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
|
|
126
|
-
if (transportType === "stdio" &&
|
|
127
|
-
|
|
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
|
-
|
|
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 "
|
|
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
|
|
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
|
|
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
|
|
5
|
-
*
|
|
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 "
|
|
13
|
-
import { logger, requestContextService, } from "
|
|
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
|
-
|
|
41
|
-
|
|
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
|
-
|
|
55
|
-
|
|
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
|
|
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
|
|
77
|
-
|
|
78
|
-
if (existsSync(primaryConfigPath)) {
|
|
79
|
-
logger.
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
164
|
+
// Return a deep copy to prevent accidental modification of the cached config
|
|
165
|
+
return JSON.parse(JSON.stringify(serverConfig));
|
|
188
166
|
}
|