mcp-ts-template 1.4.0 → 1.4.1

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 CHANGED
@@ -3,7 +3,7 @@
3
3
  [![TypeScript](https://img.shields.io/badge/TypeScript-^5.8.3-blue.svg)](https://www.typescriptlang.org/)
4
4
  [![Model Context Protocol SDK](https://img.shields.io/badge/MCP%20SDK-1.12.1-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.4.0-blue.svg)](./CHANGELOG.md)
6
+ [![Version](https://img.shields.io/badge/Version-1.4.1-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)
@@ -18,7 +18,7 @@ Whether you're creating a new MCP server to extend an AI's capabilities or integ
18
18
 
19
19
  | Feature Area | Description | Key Components / Location |
20
20
  | :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------- |
21
- | **🔌 MCP Server** | Functional server example with Echo Tool & Resource. Supports `stdio` and **Streamable HTTP** (which utilizes Server-Sent Events for server-to-client streaming) transports. | `src/mcp-server/` |
21
+ | **🔌 MCP Server** | Functional server with example tools (`EchoTool`, `CatFactFetcher` for async/Promise API example) and an `EchoResource`. Supports `stdio` and **Streamable HTTP** transports. | `src/mcp-server/` |
22
22
  | **💻 MCP Client** | Working client aligned with **MCP 2025-03-26 spec**. Connects via `mcp-config.json`. Includes detailed comments. | `src/mcp-client/` |
23
23
  | **🚀 Production Utilities** | Logging, Error Handling, ID Generation, Rate Limiting, Request Context tracking, Input Sanitization. | `src/utils/` |
24
24
  | **🔒 Type Safety/Security** | Strong type checking via TypeScript & Zod validation. Built-in security utilities (sanitization, auth middleware stub for HTTP). | Throughout, `src/utils/security/`, `src/mcp-server/transports/authentication/` |
@@ -180,8 +180,8 @@ The `src/` directory is organized for clarity:
180
180
  - `index.ts`: Barrel file exporting key client functionalities.
181
181
  - `mcp-server/`: Logic for the MCP server _provided by this template_.
182
182
  - `server.ts`: Initializes the server, registers tools/resources.
183
- - `resources/`: Example resource implementations (e.g., EchoResource).
184
- - `tools/`: Example tool implementations (e.g., EchoTool).
183
+ - `resources/`: Example resource implementations (e.g., `EchoResource`).
184
+ - `tools/`: Example tool implementations (e.g., `EchoTool`, and `CatFactFetcher` demonstrating async/Promise API calls).
185
185
  - `transports/`: Handles `stdio` and `http` communication for the server.
186
186
  - `services/`: Contains service integrations.
187
187
  - `duck-db/`: Service for interacting with DuckDB, an in-process analytical data management system.
@@ -222,6 +222,8 @@ This template is designed for extension! Follow the high-level SDK patterns:
222
222
 
223
223
  Refer to the included `EchoTool` and `EchoResource` examples and the [.clinerules](.clinerules) cheatsheet for detailed patterns.
224
224
 
225
+ For an example of a tool that performs asynchronous operations, such as making an external API call, see the `get_random_cat_fact` tool located in `src/mcp-server/tools/catFactFetcher/`. This tool demonstrates how to use `async/await` within your tool's logic and handler, manage Promises, and integrate external data sources.
226
+
225
227
  ## 🌍 Explore More MCP Resources
226
228
 
227
229
  Looking for more examples, guides, and pre-built MCP servers? Check out the companion repository:
@@ -18,6 +18,7 @@ import { config, environment } from "../config/index.js";
18
18
  import { ErrorHandler, logger, requestContextService } from "../utils/index.js";
19
19
  import { registerEchoResource } from "./resources/echoResource/index.js";
20
20
  import { registerEchoTool } from "./tools/echoTool/index.js";
21
+ import { registerCatFactFetcherTool } from "./tools/catFactFetcher/index.js";
21
22
  import { startHttpTransport } from "./transports/httpTransport.js";
22
23
  import { connectStdioTransport } from "./transports/stdioTransport.js";
23
24
  /**
@@ -70,6 +71,7 @@ async function createMcpServerInstance() {
70
71
  logger.debug("Registering resources and tools...", context);
71
72
  await registerEchoResource(server);
72
73
  await registerEchoTool(server);
74
+ await registerCatFactFetcherTool(server);
73
75
  logger.info("Resources and tools registered successfully", context);
74
76
  }
75
77
  catch (err) {
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @fileoverview Defines the core logic, schemas, and types for the `get_random_cat_fact` tool.
3
+ * This tool fetches a random cat fact from the public Cat Fact Ninja API.
4
+ * It demonstrates an asynchronous API call within an MCP tool.
5
+ * @module src/mcp-server/tools/catFactFetcher/catFactFetcherLogic
6
+ */
7
+ import { z } from "zod";
8
+ import { type RequestContext } from "../../../utils/index.js";
9
+ /**
10
+ * Zod schema for validating input arguments for the `get_random_cat_fact` tool.
11
+ */
12
+ export declare const CatFactFetcherInputSchema: z.ZodObject<{
13
+ maxLength: z.ZodOptional<z.ZodNumber>;
14
+ }, "strip", z.ZodTypeAny, {
15
+ maxLength?: number | undefined;
16
+ }, {
17
+ maxLength?: number | undefined;
18
+ }>;
19
+ /**
20
+ * TypeScript type inferred from `CatFactFetcherInputSchema`.
21
+ */
22
+ export type CatFactFetcherInput = z.infer<typeof CatFactFetcherInputSchema>;
23
+ /**
24
+ * Defines the structure of the JSON payload returned by the `get_random_cat_fact` tool handler.
25
+ */
26
+ export interface CatFactFetcherResponse {
27
+ fact: string;
28
+ length: number;
29
+ requestedMaxLength?: number;
30
+ timestamp: string;
31
+ }
32
+ /**
33
+ * Processes the core logic for the `get_random_cat_fact` tool.
34
+ * It calls the Cat Fact Ninja API and returns the fetched fact.
35
+ * @param params - The validated input parameters for the tool.
36
+ * @param context - The request context for logging and tracing.
37
+ * @returns A promise that resolves to an object containing the cat fact data.
38
+ */
39
+ export declare const processCatFactFetcher: (params: CatFactFetcherInput, context: RequestContext) => Promise<CatFactFetcherResponse>;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * @fileoverview Defines the core logic, schemas, and types for the `get_random_cat_fact` tool.
3
+ * This tool fetches a random cat fact from the public Cat Fact Ninja API.
4
+ * It demonstrates an asynchronous API call within an MCP tool.
5
+ * @module src/mcp-server/tools/catFactFetcher/catFactFetcherLogic
6
+ */
7
+ import { z } from "zod";
8
+ import { logger, fetchWithTimeout, // Added import
9
+ } from "../../../utils/index.js";
10
+ import { McpError, BaseErrorCode } from "../../../types-global/errors.js";
11
+ /**
12
+ * Asynchronously fetches a random cat fact from the Cat Fact Ninja API.
13
+ * @param maxLength - Optional maximum length for the cat fact.
14
+ * @param context - The request context for logging.
15
+ * @returns A promise that resolves to the CatFactApiResponse.
16
+ * @throws {McpError} If the API request fails or returns an error.
17
+ */
18
+ async function fetchRandomCatFactFromApi(maxLength, context) {
19
+ let apiUrl = "https://catfact.ninja/fact";
20
+ if (maxLength !== undefined) {
21
+ apiUrl += `?max_length=${maxLength}`;
22
+ }
23
+ logger.info(`Fetching random cat fact from: ${apiUrl}`, context);
24
+ const CAT_FACT_API_TIMEOUT_MS = 5000;
25
+ try {
26
+ // Use the fetchWithTimeout utility
27
+ const response = await fetchWithTimeout(apiUrl, CAT_FACT_API_TIMEOUT_MS, context);
28
+ if (!response.ok) {
29
+ const errorText = await response.text();
30
+ logger.error(`Cat Fact API request to ${apiUrl} failed with status ${response.status}: ${errorText}`, context);
31
+ throw new McpError(BaseErrorCode.SERVICE_UNAVAILABLE, `Cat Fact API request to ${apiUrl} failed: ${response.status} ${response.statusText}`, {
32
+ ...context,
33
+ httpStatusCode: response.status,
34
+ responseBodyBrief: errorText.substring(0, 200), // Log a snippet of the response
35
+ errorSource: "CatFactApiNonOkResponse",
36
+ });
37
+ }
38
+ const data = await response.json();
39
+ logger.info(`Successfully fetched cat fact (length: ${data.length}): "${data.fact.substring(0, 50)}..."`, context);
40
+ return data;
41
+ }
42
+ catch (error) {
43
+ // The fetchWithTimeout utility handles AbortError and throws McpError with BaseErrorCode.TIMEOUT.
44
+ // It also wraps other fetch-related network errors in McpError.
45
+ // So, we primarily need to catch McpErrors here or wrap any truly unexpected errors.
46
+ if (error instanceof McpError) {
47
+ // Log McpErrors specifically if needed, or just re-throw
48
+ // If it's a TIMEOUT error from fetchWithTimeout, it's already logged by the utility.
49
+ // If it's a SERVICE_UNAVAILABLE from fetchWithTimeout (generic network error), also logged.
50
+ // If it's from response.ok check above, it's also an McpError.
51
+ throw error;
52
+ }
53
+ // Fallback for any other unexpected errors not already wrapped by McpError
54
+ logger.error(`Unexpected error during Cat Fact API processing for ${apiUrl}: ${error instanceof Error ? error.message : String(error)}`, context);
55
+ throw new McpError(BaseErrorCode.INTERNAL_ERROR, // Or UNKNOWN_ERROR if more appropriate
56
+ `Unexpected error processing response from Cat Fact API (${apiUrl}): ${error instanceof Error ? error.message : String(error)}`, {
57
+ ...context,
58
+ originalErrorName: error instanceof Error ? error.name : "UnknownError",
59
+ errorSource: "CatFactApiUnexpectedCatch",
60
+ });
61
+ }
62
+ }
63
+ /**
64
+ * Zod schema for validating input arguments for the `get_random_cat_fact` tool.
65
+ */
66
+ export const CatFactFetcherInputSchema = z
67
+ .object({
68
+ maxLength: z
69
+ .number()
70
+ .int("Max length must be an integer.")
71
+ .min(1, "Max length must be at least 1.")
72
+ .optional()
73
+ .describe("Optional: The maximum character length of the cat fact to retrieve."),
74
+ })
75
+ .describe("Input schema for the get_random_cat_fact tool. Allows specifying a maximum length for the fact.");
76
+ /**
77
+ * Processes the core logic for the `get_random_cat_fact` tool.
78
+ * It calls the Cat Fact Ninja API and returns the fetched fact.
79
+ * @param params - The validated input parameters for the tool.
80
+ * @param context - The request context for logging and tracing.
81
+ * @returns A promise that resolves to an object containing the cat fact data.
82
+ */
83
+ export const processCatFactFetcher = async (params, context) => {
84
+ logger.debug("Processing get_random_cat_fact logic with input parameters.", {
85
+ ...context,
86
+ toolInput: { maxLength: params.maxLength },
87
+ });
88
+ const apiResponse = await fetchRandomCatFactFromApi(params.maxLength, context);
89
+ const toolResponse = {
90
+ fact: apiResponse.fact,
91
+ length: apiResponse.length,
92
+ requestedMaxLength: params.maxLength,
93
+ timestamp: new Date().toISOString(),
94
+ };
95
+ logger.debug("Random cat fact fetched and processed successfully.", {
96
+ ...context,
97
+ toolResponseSummary: {
98
+ factLength: toolResponse.length,
99
+ requestedMaxLength: toolResponse.requestedMaxLength,
100
+ },
101
+ });
102
+ return toolResponse;
103
+ };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @fileoverview Handles the registration of the `get_random_cat_fact` tool
3
+ * with an MCP server instance. This tool fetches a random cat fact from the
4
+ * Cat Fact Ninja API.
5
+ * @module src/mcp-server/tools/catFactFetcher/catFactFetcherRegistration
6
+ */
7
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
+ /**
9
+ * Registers the 'get_random_cat_fact' tool and its handler with the MCP server.
10
+ *
11
+ * @param server - The MCP server instance to register the tool with.
12
+ * @returns A promise that resolves when tool registration is complete.
13
+ * @throws {McpError} If registration fails critically.
14
+ */
15
+ export declare const registerCatFactFetcherTool: (server: McpServer) => Promise<void>;
@@ -0,0 +1,80 @@
1
+ /**
2
+ * @fileoverview Handles the registration of the `get_random_cat_fact` tool
3
+ * with an MCP server instance. This tool fetches a random cat fact from the
4
+ * Cat Fact Ninja API.
5
+ * @module src/mcp-server/tools/catFactFetcher/catFactFetcherRegistration
6
+ */
7
+ import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
8
+ import { ErrorHandler, logger, requestContextService, } from "../../../utils/index.js";
9
+ import { CatFactFetcherInputSchema, processCatFactFetcher, } from "./catFactFetcherLogic.js";
10
+ /**
11
+ * Registers the 'get_random_cat_fact' tool and its handler with the MCP server.
12
+ *
13
+ * @param server - The MCP server instance to register the tool with.
14
+ * @returns A promise that resolves when tool registration is complete.
15
+ * @throws {McpError} If registration fails critically.
16
+ */
17
+ export const registerCatFactFetcherTool = async (server) => {
18
+ const toolName = "get_random_cat_fact";
19
+ const toolDescription = "Fetches a random cat fact from the Cat Fact Ninja API. Optionally, a maximum length for the fact can be specified.";
20
+ const registrationContext = requestContextService.createRequestContext({
21
+ operation: "RegisterTool",
22
+ toolName: toolName,
23
+ moduleName: "CatFactFetcherRegistration",
24
+ });
25
+ logger.info(`Attempting to register tool: '${toolName}'`, registrationContext);
26
+ await ErrorHandler.tryCatch(async () => {
27
+ server.tool(toolName, toolDescription, CatFactFetcherInputSchema.shape, // SDK uses this for schema generation & validation
28
+ async (params) => {
29
+ const handlerContext = requestContextService.createRequestContext({
30
+ parentContext: registrationContext,
31
+ operation: "HandleToolRequest",
32
+ toolName: toolName,
33
+ inputSummary: { maxLength: params.maxLength },
34
+ });
35
+ logger.debug(`Handling '${toolName}' tool request.`, handlerContext);
36
+ return await ErrorHandler.tryCatch(async () => {
37
+ const responsePayload = await processCatFactFetcher(params, handlerContext);
38
+ logger.debug(`'${toolName}' tool processed successfully. Preparing result.`, handlerContext);
39
+ return {
40
+ content: [
41
+ {
42
+ type: "text",
43
+ text: JSON.stringify(responsePayload, null, 2),
44
+ },
45
+ ],
46
+ isError: false,
47
+ };
48
+ }, {
49
+ operation: `ExecutingCoreLogicFor_${toolName}`,
50
+ context: handlerContext,
51
+ input: params, // Input is sanitized by ErrorHandler for logging
52
+ errorMapper: (error) => {
53
+ // Ensure a specific McpError is created if not already one
54
+ if (error instanceof McpError)
55
+ return error;
56
+ const errorMessage = `Error processing '${toolName}' tool: ${error instanceof Error ? error.message : "An unknown error occurred"}`;
57
+ return new McpError(BaseErrorCode.INTERNAL_ERROR, errorMessage, {
58
+ ...handlerContext,
59
+ originalErrorName: error instanceof Error ? error.name : typeof error,
60
+ });
61
+ },
62
+ });
63
+ });
64
+ logger.info(`Tool '${toolName}' registered successfully with the MCP server.`, registrationContext);
65
+ }, {
66
+ operation: `RegisteringTool_${toolName}`,
67
+ context: registrationContext,
68
+ errorCode: BaseErrorCode.INITIALIZATION_FAILED,
69
+ errorMapper: (error) => {
70
+ if (error instanceof McpError)
71
+ return error;
72
+ const errorMessage = `Failed to register tool '${toolName}': ${error instanceof Error ? error.message : "An unknown error occurred during registration."}`;
73
+ return new McpError(BaseErrorCode.INITIALIZATION_FAILED, errorMessage, {
74
+ ...registrationContext,
75
+ originalErrorName: error instanceof Error ? error.name : typeof error,
76
+ });
77
+ },
78
+ critical: true, // Registration failure is critical
79
+ });
80
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @fileoverview Barrel file for the `catFactFetcher` tool.
3
+ * This file serves as the public interface for the cat fact fetcher tool module,
4
+ * primarily exporting the `registerCatFactFetcherTool` function.
5
+ * @module src/mcp-server/tools/catFactFetcher/index
6
+ */
7
+ export { registerCatFactFetcherTool } from "./catFactFetcherRegistration.js";
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @fileoverview Barrel file for the `catFactFetcher` tool.
3
+ * This file serves as the public interface for the cat fact fetcher tool module,
4
+ * primarily exporting the `registerCatFactFetcherTool` function.
5
+ * @module src/mcp-server/tools/catFactFetcher/index
6
+ */
7
+ export { registerCatFactFetcherTool } from "./catFactFetcherRegistration.js";
@@ -60,9 +60,10 @@ export class DuckDBQueryExecutor {
60
60
  else {
61
61
  resultObject = await this.dbConnection.stream(sql, params); // Cast to any for object
62
62
  }
63
- const rows = await resultObject.getRows(); // Use getRows() as per docs, and it's async
63
+ const rows = (await resultObject.getRows()); // Use getRows() as per docs, and it's async
64
64
  const columnNames = resultObject.columnNames(); // Sync as per docs
65
- const columnTypes = resultObject.columnTypes() // Sync as per docs
65
+ const columnTypes = resultObject
66
+ .columnTypes() // Sync as per docs
66
67
  .map((ct) => ct.typeId);
67
68
  return {
68
69
  rows: rows,
@@ -83,7 +83,7 @@ async function runDuckDBExample() {
83
83
  { name: "Alice Wonderland", email: "alice@example.com" },
84
84
  { name: "Bob The Builder", email: "bob@example.com" },
85
85
  { name: "Charlie Chaplin", email: "charlie@example.com" },
86
- ].map(user => ({
86
+ ].map((user) => ({
87
87
  id: idGenerator.generateRandomString(6), // Generate 6-digit alphanumeric ID directly
88
88
  ...user,
89
89
  }));
@@ -8,3 +8,4 @@ export * from "./internal/index.js";
8
8
  export * from "./metrics/index.js";
9
9
  export * from "./parsing/index.js";
10
10
  export * from "./security/index.js";
11
+ export * from "./network/index.js";
@@ -9,6 +9,7 @@ export * from "./internal/index.js";
9
9
  export * from "./metrics/index.js";
10
10
  export * from "./parsing/index.js";
11
11
  export * from "./security/index.js";
12
+ export * from "./network/index.js";
12
13
  // It's good practice to have index.ts files in each subdirectory
13
14
  // that export the contents of that directory.
14
15
  // Assuming those will be created or already exist.
@@ -61,7 +61,7 @@ function createWinstonConsoleFormat() {
61
61
  }
62
62
  if (Object.keys(metaCopy).length > 0) {
63
63
  try {
64
- const replacer = (key, value) => typeof value === 'bigint' ? value.toString() : value;
64
+ const replacer = (key, value) => typeof value === "bigint" ? value.toString() : value;
65
65
  const remainingMetaJson = JSON.stringify(metaCopy, replacer, 2);
66
66
  if (remainingMetaJson !== "{}")
67
67
  metaString += `\n Meta: ${remainingMetaJson}`;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @fileoverview Provides a utility function to make fetch requests with a specified timeout.
3
+ * @module src/utils/network/fetchWithTimeout
4
+ */
5
+ import type { RequestContext } from "../internal/requestContext.js";
6
+ /**
7
+ * Options for the fetchWithTimeout utility.
8
+ * Extends standard RequestInit but omits 'signal' as it's handled internally.
9
+ */
10
+ export type FetchWithTimeoutOptions = Omit<RequestInit, "signal">;
11
+ /**
12
+ * Fetches a resource with a specified timeout.
13
+ *
14
+ * @param url - The URL to fetch.
15
+ * @param timeoutMs - The timeout duration in milliseconds.
16
+ * @param context - The request context for logging.
17
+ * @param options - Optional fetch options (RequestInit), excluding 'signal'.
18
+ * @returns A promise that resolves to the Response object.
19
+ * @throws {McpError} If the request times out or another fetch-related error occurs.
20
+ */
21
+ export declare function fetchWithTimeout(url: string | URL, timeoutMs: number, context: RequestContext, options?: FetchWithTimeoutOptions): Promise<Response>;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * @fileoverview Provides a utility function to make fetch requests with a specified timeout.
3
+ * @module src/utils/network/fetchWithTimeout
4
+ */
5
+ import { logger } from "../internal/logger.js"; // Adjusted import path
6
+ import { McpError, BaseErrorCode } from "../../types-global/errors.js";
7
+ /**
8
+ * Fetches a resource with a specified timeout.
9
+ *
10
+ * @param url - The URL to fetch.
11
+ * @param timeoutMs - The timeout duration in milliseconds.
12
+ * @param context - The request context for logging.
13
+ * @param options - Optional fetch options (RequestInit), excluding 'signal'.
14
+ * @returns A promise that resolves to the Response object.
15
+ * @throws {McpError} If the request times out or another fetch-related error occurs.
16
+ */
17
+ export async function fetchWithTimeout(url, timeoutMs, context, options) {
18
+ const controller = new AbortController();
19
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
20
+ const urlString = url.toString();
21
+ const operationDescription = `fetch ${options?.method || "GET"} ${urlString}`;
22
+ logger.debug(`Attempting ${operationDescription} with ${timeoutMs}ms timeout.`, context);
23
+ try {
24
+ const response = await fetch(url, {
25
+ ...options,
26
+ signal: controller.signal,
27
+ });
28
+ clearTimeout(timeoutId);
29
+ logger.debug(`Successfully fetched ${urlString}. Status: ${response.status}`, context);
30
+ return response;
31
+ }
32
+ catch (error) {
33
+ clearTimeout(timeoutId);
34
+ if (error instanceof Error && error.name === "AbortError") {
35
+ logger.error(`${operationDescription} timed out after ${timeoutMs}ms.`, {
36
+ ...context,
37
+ errorSource: "FetchTimeout",
38
+ });
39
+ throw new McpError(BaseErrorCode.TIMEOUT, `${operationDescription} timed out.`, { ...context, errorSource: "FetchTimeout" });
40
+ }
41
+ // Log and re-throw other errors as McpError
42
+ const errorMessage = error instanceof Error ? error.message : String(error);
43
+ logger.error(`Network error during ${operationDescription}: ${errorMessage}`, {
44
+ ...context,
45
+ originalErrorName: error instanceof Error ? error.name : "UnknownError",
46
+ errorSource: "FetchNetworkError",
47
+ });
48
+ if (error instanceof McpError) {
49
+ // If it's already an McpError, re-throw it
50
+ throw error;
51
+ }
52
+ throw new McpError(BaseErrorCode.SERVICE_UNAVAILABLE, // Generic error for network/service issues
53
+ `Network error during ${operationDescription}: ${errorMessage}`, {
54
+ ...context,
55
+ originalErrorName: error instanceof Error ? error.name : "UnknownError",
56
+ errorSource: "FetchNetworkErrorWrapper",
57
+ });
58
+ }
59
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @fileoverview Barrel file for network utilities.
3
+ * @module src/utils/network/index
4
+ */
5
+ export * from "./fetchWithTimeout.js";
6
+ export type { FetchWithTimeoutOptions } from "./fetchWithTimeout.js";
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @fileoverview Barrel file for network utilities.
3
+ * @module src/utils/network/index
4
+ */
5
+ export * from "./fetchWithTimeout.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-ts-template",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
4
4
  "description": "TypeScript template for building Model Context Protocol (MCP) Servers & Clients. Features extensive utilities (logger, requestContext, etc.), STDIO & Streamable HTTP (with authMiddleware), examples, and type safety. Ideal starting point for creating production-ready MCP Servers & Clients.",
5
5
  "main": "dist/index.js",
6
6
  "files": [
@@ -30,14 +30,13 @@
30
30
  "fetch-spec": "ts-node --esm scripts/fetch-openapi-spec.ts",
31
31
  "format": "prettier --write \"**/*.{ts,js,json,md,html,css}\"",
32
32
  "inspector": "mcp-inspector --config mcp.json --server mcp-ts-template",
33
- "start:client-cli": "node dist/mcp-client/cli/mcp-client-cli.js",
34
33
  "db:generate": "MCP_LOG_LEVEL=debug tsc && node dist/storage/duckdbExample.js"
35
34
  },
36
35
  "dependencies": {
37
36
  "@duckdb/node-api": "^1.3.0-alpha.21",
38
37
  "@modelcontextprotocol/sdk": "^1.12.1",
39
38
  "@types/jsonwebtoken": "^9.0.9",
40
- "@types/node": "^22.15.28",
39
+ "@types/node": "^22.15.29",
41
40
  "@types/sanitize-html": "^2.16.0",
42
41
  "@types/validator": "13.15.1",
43
42
  "chalk": "^5.4.1",
@@ -45,7 +44,7 @@
45
44
  "cli-table3": "^0.6.5",
46
45
  "dotenv": "^16.5.0",
47
46
  "express": "^5.1.0",
48
- "ignore": "^7.0.4",
47
+ "ignore": "^7.0.5",
49
48
  "jsonwebtoken": "^9.0.2",
50
49
  "openai": "^5.0.1",
51
50
  "partial-json": "^0.1.7",