@aashari/boilerplate-mcp-server 1.14.0 → 1.15.0

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/.trigger-ci ADDED
@@ -0,0 +1 @@
1
+ # CI/CD trigger Thu Sep 18 00:40:39 WIB 2025
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ # [1.15.0](https://github.com/aashari/boilerplate-mcp-server/compare/v1.14.0...v1.15.0) (2025-12-01)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * update tests for TOON output format ([6054be3](https://github.com/aashari/boilerplate-mcp-server/commit/6054be313fd54c6678b39da214c13e17b04ec4a6))
7
+
8
+
9
+ ### Features
10
+
11
+ * add TOON output format with JMESPath filtering support ([#126](https://github.com/aashari/boilerplate-mcp-server/issues/126)) ([2b3d8d8](https://github.com/aashari/boilerplate-mcp-server/commit/2b3d8d8a6811a1296475b6971d840b9392ceff08))
12
+
1
13
  # [1.14.0](https://github.com/aashari/boilerplate-mcp-server/compare/v1.13.5...v1.14.0) (2025-09-09)
2
14
 
3
15
 
@@ -21,6 +21,8 @@ function register(program) {
21
21
  .option('-e, --include-extended-data', 'Include extended data (ASN, host, org). Requires API token.')
22
22
  .option('--no-use-https', // commander creates a 'useHttps' boolean, defaulting to true
23
23
  'Use HTTP instead of HTTPS for the API call.')
24
+ .option('--jq <expression>', 'JMESPath expression to filter/transform the response.')
25
+ .option('-o, --output-format <format>', 'Output format: "toon" (default, token-efficient) or "json".', 'toon')
24
26
  .action(async (ipAddress, options) => {
25
27
  const actionLogger = logger.forMethod('action:get-ip-details');
26
28
  try {
@@ -33,6 +35,8 @@ function register(program) {
33
35
  ipAddress,
34
36
  includeExtendedData: options.includeExtendedData || false,
35
37
  useHttps: options.useHttps, // commander handles the default via --no-use-https
38
+ jq: options.jq,
39
+ outputFormat: options.outputFormat,
36
40
  };
37
41
  const result = await ipaddress_controller_js_1.default.get(args);
38
42
  console.log(result.content);
@@ -1,4 +1,13 @@
1
- import { ControllerResponse } from '../types/common.types.js';
1
+ /**
2
+ * Output format type
3
+ */
4
+ type OutputFormat = 'toon' | 'json';
5
+ /**
6
+ * Controller response type
7
+ */
8
+ interface ControllerResponse {
9
+ content: string;
10
+ }
2
11
  /**
3
12
  * @namespace IpAddressController
4
13
  * @description Controller responsible for handling IP address lookup logic.
@@ -14,13 +23,17 @@ import { ControllerResponse } from '../types/common.types.js';
14
23
  * @param {string} [args.ipAddress] - Optional IP address to look up. If omitted, the service will fetch the current device's public IP.
15
24
  * @param {boolean} [args.includeExtendedData=false] - Whether to include extended data fields requiring an API token
16
25
  * @param {boolean} [args.useHttps=true] - Whether to use HTTPS for the API request
17
- * @returns {Promise<ControllerResponse>} A promise that resolves to the standard controller response containing the formatted IP details in Markdown.
26
+ * @param {string} [args.jq] - JMESPath expression to filter the response
27
+ * @param {OutputFormat} [args.outputFormat] - Output format (toon or json)
28
+ * @returns {Promise<ControllerResponse>} A promise that resolves to the standard controller response containing the formatted IP details.
18
29
  * @throws {McpError} Throws an McpError (handled by `handleControllerError`) if the service call fails or returns an error.
19
30
  */
20
31
  declare function get(args?: {
21
32
  ipAddress?: string;
22
33
  includeExtendedData?: boolean;
23
34
  useHttps?: boolean;
35
+ jq?: string;
36
+ outputFormat?: OutputFormat;
24
37
  }): Promise<ControllerResponse>;
25
38
  declare const _default: {
26
39
  get: typeof get;
@@ -5,11 +5,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const logger_util_js_1 = require("../utils/logger.util.js");
7
7
  const vendor_ip_api_com_service_js_1 = __importDefault(require("../services/vendor.ip-api.com.service.js"));
8
- const ipaddress_formatter_js_1 = require("./ipaddress.formatter.js");
9
8
  const error_handler_util_js_1 = require("../utils/error-handler.util.js");
10
9
  const config_util_js_1 = require("../utils/config.util.js");
11
10
  const error_util_js_1 = require("../utils/error.util.js");
12
11
  const error_handler_util_js_2 = require("../utils/error-handler.util.js");
12
+ const jq_util_js_1 = require("../utils/jq.util.js");
13
13
  /**
14
14
  * @namespace IpAddressController
15
15
  * @description Controller responsible for handling IP address lookup logic.
@@ -25,7 +25,9 @@ const error_handler_util_js_2 = require("../utils/error-handler.util.js");
25
25
  * @param {string} [args.ipAddress] - Optional IP address to look up. If omitted, the service will fetch the current device's public IP.
26
26
  * @param {boolean} [args.includeExtendedData=false] - Whether to include extended data fields requiring an API token
27
27
  * @param {boolean} [args.useHttps=true] - Whether to use HTTPS for the API request
28
- * @returns {Promise<ControllerResponse>} A promise that resolves to the standard controller response containing the formatted IP details in Markdown.
28
+ * @param {string} [args.jq] - JMESPath expression to filter the response
29
+ * @param {OutputFormat} [args.outputFormat] - Output format (toon or json)
30
+ * @returns {Promise<ControllerResponse>} A promise that resolves to the standard controller response containing the formatted IP details.
29
31
  * @throws {McpError} Throws an McpError (handled by `handleControllerError`) if the service call fails or returns an error.
30
32
  */
31
33
  async function get(args = {}) {
@@ -71,12 +73,11 @@ async function get(args = {}) {
71
73
  serviceOptions,
72
74
  isTestEnvironment,
73
75
  });
76
+ let data;
74
77
  try {
75
78
  // Call the service with ipAddress and the mapped serviceOptions
76
- const data = await vendor_ip_api_com_service_js_1.default.get(args.ipAddress, serviceOptions);
79
+ data = await vendor_ip_api_com_service_js_1.default.get(args.ipAddress, serviceOptions);
77
80
  methodLogger.debug(`Got the response from the service`, data);
78
- const formattedContent = (0, ipaddress_formatter_js_1.formatIpDetails)(data);
79
- return { content: formattedContent };
80
81
  }
81
82
  catch (error) {
82
83
  // If HTTPS fails with permission/SSL error and useHttps was true, try again with HTTP
@@ -87,17 +88,24 @@ async function get(args = {}) {
87
88
  error.message.includes('Access denied'))) {
88
89
  methodLogger.warn('HTTPS request failed, falling back to HTTP');
89
90
  // Try again with HTTP
90
- const httpData = await vendor_ip_api_com_service_js_1.default.get(args.ipAddress, {
91
+ data = await vendor_ip_api_com_service_js_1.default.get(args.ipAddress, {
91
92
  ...serviceOptions,
92
93
  useHttps: false,
93
94
  });
94
- methodLogger.debug(`Got the response from HTTP fallback`, httpData);
95
- const formattedContent = (0, ipaddress_formatter_js_1.formatIpDetails)(httpData);
96
- return { content: formattedContent };
95
+ methodLogger.debug(`Got the response from HTTP fallback`, data);
96
+ }
97
+ else {
98
+ // For other errors, rethrow
99
+ throw error;
97
100
  }
98
- // For other errors, rethrow
99
- throw error;
100
101
  }
102
+ // Apply JQ filter if provided
103
+ const filteredData = (0, jq_util_js_1.applyJqFilter)(data, args.jq);
104
+ // Determine output format (default to TOON)
105
+ const useToon = args.outputFormat !== 'json';
106
+ // Format the output
107
+ const content = await (0, jq_util_js_1.toOutputString)(filteredData, useToon);
108
+ return { content };
101
109
  }
102
110
  catch (error) {
103
111
  throw (0, error_handler_util_js_1.handleControllerError)(error, (0, error_handler_util_js_2.buildErrorContext)('IP Address', 'get', 'controllers/ipaddress.controller.ts@get', args.ipAddress || 'current device', { args }));
@@ -63,7 +63,29 @@ async function handleGetIpDetails(args) {
63
63
  function registerTools(server) {
64
64
  const methodLogger = logger_util_js_1.Logger.forContext('tools/ipaddress.tool.ts', 'registerTools');
65
65
  methodLogger.debug(`Registering IP address tools...`);
66
- server.tool('ip_get_details', `Retrieves detailed geolocation and network information for a public IP address (\`ipAddress\`). If no IP is provided, automatically uses the server's current public IP. Returns comprehensive location data including country, region, city, coordinates, timezone, and network details like ISP and organization. Use \`includeExtendedData\` to get additional information such as ASN, mobile/proxy detection (requires API token). **Note:** Cannot lookup private IPs (e.g., 192.168.x.x, 10.x.x.x). Powered by ip-api.com. Enable \`useHttps\` for secure connections (required for paid tier).`, GetIpDetailsToolSchema.shape, handleGetIpDetails);
66
+ server.tool('ip_get_details', `Retrieve geolocation and network information for a public IP address. Returns TOON format by default (30-60% fewer tokens than JSON).
67
+
68
+ **IMPORTANT - Cost Optimization:**
69
+ - Use \`jq\` param to extract only needed fields. Unfiltered responses are expensive!
70
+ - Example: \`jq: "{ip: query, country: country, city: city}"\` - extract specific fields
71
+ - If unsure about available fields, first call WITHOUT jq filter to see all fields, then use jq in subsequent calls
72
+
73
+ **Schema Discovery Pattern:**
74
+ 1. First call: \`ipAddress: "8.8.8.8"\` (no jq) - explore available fields
75
+ 2. Then use: \`jq: "{ip: query, location: {city: city, country: country}}"\` - extract only what you need
76
+
77
+ **Output format:** TOON (default, token-efficient) or JSON (\`outputFormat: "json"\`)
78
+
79
+ **Parameters:**
80
+ - \`ipAddress\` - IP to lookup (omit for current device's public IP)
81
+ - \`includeExtendedData\` - Include ASN, host, proxy detection (requires API token)
82
+ - \`useHttps\` - Use HTTPS (default: true)
83
+ - \`jq\` - JMESPath expression to filter response
84
+ - \`outputFormat\` - "toon" (default) or "json"
85
+
86
+ **JQ examples:** \`query\` (IP only), \`{ip: query, country: country}\`, \`{location: {lat: lat, lon: lon}}\`
87
+
88
+ **Note:** Cannot lookup private IPs (192.168.x.x, 10.x.x.x). Powered by ip-api.com.`, GetIpDetailsToolSchema.shape, handleGetIpDetails);
67
89
  methodLogger.debug('Successfully registered ip_get_details tool.');
68
90
  }
69
91
  exports.default = { registerTools };
@@ -1,16 +1,28 @@
1
1
  import { z } from 'zod';
2
+ /**
3
+ * Output format options for API responses
4
+ * - toon: Token-Oriented Object Notation (default, more token-efficient for LLMs)
5
+ * - json: Standard JSON format
6
+ */
7
+ export declare const OutputFormat: z.ZodOptional<z.ZodEnum<["toon", "json"]>>;
2
8
  /**
3
9
  * Zod schema for the IP address tool arguments.
4
10
  */
5
11
  export declare const IpAddressToolArgs: z.ZodObject<{
6
12
  includeExtendedData: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
7
13
  useHttps: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
14
+ jq: z.ZodOptional<z.ZodString>;
15
+ outputFormat: z.ZodOptional<z.ZodEnum<["toon", "json"]>>;
8
16
  }, "strict", z.ZodTypeAny, {
9
17
  useHttps: boolean;
10
18
  includeExtendedData: boolean;
19
+ jq?: string | undefined;
20
+ outputFormat?: "toon" | "json" | undefined;
11
21
  }, {
12
22
  useHttps?: boolean | undefined;
13
23
  includeExtendedData?: boolean | undefined;
24
+ jq?: string | undefined;
25
+ outputFormat?: "toon" | "json" | undefined;
14
26
  }>;
15
27
  /**
16
28
  * TypeScript type inferred from the IpAddressToolArgs Zod schema.
@@ -1,7 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.IpAddressToolArgs = void 0;
3
+ exports.IpAddressToolArgs = exports.OutputFormat = void 0;
4
4
  const zod_1 = require("zod");
5
+ /**
6
+ * Output format options for API responses
7
+ * - toon: Token-Oriented Object Notation (default, more token-efficient for LLMs)
8
+ * - json: Standard JSON format
9
+ */
10
+ exports.OutputFormat = zod_1.z
11
+ .enum(['toon', 'json'])
12
+ .optional()
13
+ .describe('Output format: "toon" (default, 30-60% fewer tokens) or "json". TOON is optimized for LLMs with tabular arrays and minimal syntax.');
5
14
  /**
6
15
  * Zod schema for the IP address tool arguments.
7
16
  */
@@ -19,5 +28,10 @@ exports.IpAddressToolArgs = zod_1.z
19
28
  .optional()
20
29
  .default(true)
21
30
  .describe('Whether to use HTTPS for the API call (recommended).'),
31
+ jq: zod_1.z
32
+ .string()
33
+ .optional()
34
+ .describe('JMESPath expression to filter/transform the response. IMPORTANT: Always use this to extract only needed fields and reduce token costs. Examples: "{ip: query, country: country}" (extract specific fields), "lat" (single field). See https://jmespath.org'),
35
+ outputFormat: exports.OutputFormat,
22
36
  })
23
37
  .strict();
@@ -21,8 +21,14 @@ export declare class CliTestUtil {
21
21
  * Validates that stdout contains expected strings/patterns
22
22
  */
23
23
  static validateOutputContains(output: string, expectedPatterns: (string | RegExp)[]): void;
24
+ /**
25
+ * Validates TOON output format
26
+ * TOON uses "key: value" syntax for objects
27
+ */
28
+ static validateToonOutput(output: string): void;
24
29
  /**
25
30
  * Validates Markdown output format
31
+ * @deprecated Use validateToonOutput for new code - output is now TOON by default
26
32
  */
27
33
  static validateMarkdownOutput(output: string): void;
28
34
  }
@@ -99,8 +99,28 @@ class CliTestUtil {
99
99
  }
100
100
  }
101
101
  }
102
+ /**
103
+ * Validates TOON output format
104
+ * TOON uses "key: value" syntax for objects
105
+ */
106
+ static validateToonOutput(output) {
107
+ // Filter out debug log lines for cleaner validation
108
+ const cleanOutput = output
109
+ .split('\n')
110
+ .filter((line) => !line.match(/^\[\d{2}:\d{2}:\d{2}\]/))
111
+ .join('\n');
112
+ // TOON format characteristics:
113
+ // - Key-value pairs in "key: value" format
114
+ // - No curly braces for objects
115
+ // - Arrays use [count]{fields}: notation
116
+ const toonPatterns = [
117
+ /^\w+:\s*.+/m, // key: value pattern
118
+ ];
119
+ expect(toonPatterns.some((pattern) => pattern.test(cleanOutput))).toBe(true);
120
+ }
102
121
  /**
103
122
  * Validates Markdown output format
123
+ * @deprecated Use validateToonOutput for new code - output is now TOON by default
104
124
  */
105
125
  static validateMarkdownOutput(output) {
106
126
  // Filter out debug log lines for cleaner validation
@@ -8,7 +8,7 @@
8
8
  * Current application version
9
9
  * This should match the version in package.json
10
10
  */
11
- export declare const VERSION = "1.14.0";
11
+ export declare const VERSION = "1.15.0";
12
12
  /**
13
13
  * Package name with scope
14
14
  * Used for initialization and identification
@@ -11,7 +11,7 @@ exports.CLI_NAME = exports.PACKAGE_NAME = exports.VERSION = void 0;
11
11
  * Current application version
12
12
  * This should match the version in package.json
13
13
  */
14
- exports.VERSION = '1.14.0';
14
+ exports.VERSION = '1.15.0';
15
15
  /**
16
16
  * Package name with scope
17
17
  * Used for initialization and identification
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Apply a JMESPath filter to JSON data
3
+ *
4
+ * @param data - The data to filter (any JSON-serializable value)
5
+ * @param filter - JMESPath expression to apply
6
+ * @returns Filtered data or original data if filter is empty/invalid
7
+ *
8
+ * @example
9
+ * // Get single field
10
+ * applyJqFilter(data, "name")
11
+ *
12
+ * // Get nested field
13
+ * applyJqFilter(data, "body.storage.value")
14
+ *
15
+ * // Get multiple fields as object
16
+ * applyJqFilter(data, "{id: id, title: title}")
17
+ *
18
+ * // Array operations
19
+ * applyJqFilter(data, "results[*].title")
20
+ */
21
+ export declare function applyJqFilter(data: unknown, filter?: string): unknown;
22
+ /**
23
+ * Convert data to JSON string for MCP response
24
+ *
25
+ * @param data - The data to stringify
26
+ * @param pretty - Whether to pretty-print the JSON (default: true)
27
+ * @returns JSON string
28
+ */
29
+ export declare function toJsonString(data: unknown, pretty?: boolean): string;
30
+ /**
31
+ * Convert data to output string for MCP response
32
+ *
33
+ * By default, converts to TOON format (Token-Oriented Object Notation)
34
+ * for improved LLM token efficiency (30-60% fewer tokens).
35
+ * Falls back to JSON if TOON conversion fails or if useToon is false.
36
+ *
37
+ * @param data - The data to convert
38
+ * @param useToon - Whether to use TOON format (default: true)
39
+ * @param pretty - Whether to pretty-print JSON (default: true)
40
+ * @returns TOON formatted string (default), or JSON string
41
+ */
42
+ export declare function toOutputString(data: unknown, useToon?: boolean, pretty?: boolean): Promise<string>;
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.applyJqFilter = applyJqFilter;
7
+ exports.toJsonString = toJsonString;
8
+ exports.toOutputString = toOutputString;
9
+ const jmespath_1 = __importDefault(require("jmespath"));
10
+ const logger_util_js_1 = require("./logger.util.js");
11
+ const toon_util_js_1 = require("./toon.util.js");
12
+ const logger = logger_util_js_1.Logger.forContext('utils/jq.util.ts');
13
+ /**
14
+ * Apply a JMESPath filter to JSON data
15
+ *
16
+ * @param data - The data to filter (any JSON-serializable value)
17
+ * @param filter - JMESPath expression to apply
18
+ * @returns Filtered data or original data if filter is empty/invalid
19
+ *
20
+ * @example
21
+ * // Get single field
22
+ * applyJqFilter(data, "name")
23
+ *
24
+ * // Get nested field
25
+ * applyJqFilter(data, "body.storage.value")
26
+ *
27
+ * // Get multiple fields as object
28
+ * applyJqFilter(data, "{id: id, title: title}")
29
+ *
30
+ * // Array operations
31
+ * applyJqFilter(data, "results[*].title")
32
+ */
33
+ function applyJqFilter(data, filter) {
34
+ const methodLogger = logger.forMethod('applyJqFilter');
35
+ // Return original data if no filter provided
36
+ if (!filter || filter.trim() === '') {
37
+ methodLogger.debug('No filter provided, returning original data');
38
+ return data;
39
+ }
40
+ try {
41
+ methodLogger.debug(`Applying JMESPath filter: ${filter}`);
42
+ const result = jmespath_1.default.search(data, filter);
43
+ methodLogger.debug('Filter applied successfully');
44
+ return result;
45
+ }
46
+ catch (error) {
47
+ methodLogger.error(`Invalid JMESPath expression: ${filter}`, error);
48
+ // Return original data with error info if filter is invalid
49
+ return {
50
+ _jqError: `Invalid JMESPath expression: ${filter}`,
51
+ _originalData: data,
52
+ };
53
+ }
54
+ }
55
+ /**
56
+ * Convert data to JSON string for MCP response
57
+ *
58
+ * @param data - The data to stringify
59
+ * @param pretty - Whether to pretty-print the JSON (default: true)
60
+ * @returns JSON string
61
+ */
62
+ function toJsonString(data, pretty = true) {
63
+ if (pretty) {
64
+ return JSON.stringify(data, null, 2);
65
+ }
66
+ return JSON.stringify(data);
67
+ }
68
+ /**
69
+ * Convert data to output string for MCP response
70
+ *
71
+ * By default, converts to TOON format (Token-Oriented Object Notation)
72
+ * for improved LLM token efficiency (30-60% fewer tokens).
73
+ * Falls back to JSON if TOON conversion fails or if useToon is false.
74
+ *
75
+ * @param data - The data to convert
76
+ * @param useToon - Whether to use TOON format (default: true)
77
+ * @param pretty - Whether to pretty-print JSON (default: true)
78
+ * @returns TOON formatted string (default), or JSON string
79
+ */
80
+ async function toOutputString(data, useToon = true, pretty = true) {
81
+ const jsonString = toJsonString(data, pretty);
82
+ // Return JSON directly if TOON is not requested
83
+ if (!useToon) {
84
+ return jsonString;
85
+ }
86
+ // Try TOON conversion with JSON fallback
87
+ return (0, toon_util_js_1.toToonOrJson)(data, jsonString);
88
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Convert data to TOON format with JSON fallback.
3
+ *
4
+ * TOON (Token-Oriented Object Notation) is 30-60% more token-efficient than JSON
5
+ * for tabular data, making it ideal for LLM responses.
6
+ *
7
+ * @param data - The data to convert
8
+ * @param jsonFallback - JSON string to return if TOON encoding fails
9
+ * @returns TOON formatted string, or JSON fallback on failure
10
+ *
11
+ * @example
12
+ * const json = JSON.stringify(data, null, 2);
13
+ * const output = await toToonOrJson(data, json);
14
+ */
15
+ export declare function toToonOrJson(data: unknown, jsonFallback: string): Promise<string>;
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toToonOrJson = toToonOrJson;
4
+ const logger_util_js_1 = require("./logger.util.js");
5
+ const logger = logger_util_js_1.Logger.forContext('utils/toon.util.ts');
6
+ /**
7
+ * Cached TOON encoder to avoid repeated dynamic imports
8
+ */
9
+ let toonEncode = null;
10
+ /**
11
+ * Dynamically loads the TOON encoder module.
12
+ * Uses dynamic import because @toon-format/toon is an ESM-only package.
13
+ *
14
+ * @returns Promise resolving to the encode function or null if loading fails
15
+ */
16
+ async function loadToonEncoder() {
17
+ const methodLogger = logger.forMethod('loadToonEncoder');
18
+ // Return cached encoder if available
19
+ if (toonEncode) {
20
+ return toonEncode;
21
+ }
22
+ try {
23
+ methodLogger.debug('Loading TOON encoder module...');
24
+ // Dynamic import for ESM module in CommonJS project
25
+ const toon = await import('@toon-format/toon');
26
+ toonEncode = toon.encode;
27
+ methodLogger.debug('TOON encoder loaded successfully');
28
+ return toonEncode;
29
+ }
30
+ catch (error) {
31
+ methodLogger.error('Failed to load TOON encoder', error);
32
+ return null;
33
+ }
34
+ }
35
+ /**
36
+ * Convert data to TOON format with JSON fallback.
37
+ *
38
+ * TOON (Token-Oriented Object Notation) is 30-60% more token-efficient than JSON
39
+ * for tabular data, making it ideal for LLM responses.
40
+ *
41
+ * @param data - The data to convert
42
+ * @param jsonFallback - JSON string to return if TOON encoding fails
43
+ * @returns TOON formatted string, or JSON fallback on failure
44
+ *
45
+ * @example
46
+ * const json = JSON.stringify(data, null, 2);
47
+ * const output = await toToonOrJson(data, json);
48
+ */
49
+ async function toToonOrJson(data, jsonFallback) {
50
+ const methodLogger = logger.forMethod('toToonOrJson');
51
+ try {
52
+ const encode = await loadToonEncoder();
53
+ if (!encode) {
54
+ methodLogger.debug('TOON encoder not available, using JSON fallback');
55
+ return jsonFallback;
56
+ }
57
+ const result = encode(data, { indent: 2 });
58
+ methodLogger.debug('Successfully converted to TOON format');
59
+ return result;
60
+ }
61
+ catch (error) {
62
+ methodLogger.error('TOON encoding failed, using JSON fallback', error);
63
+ return jsonFallback;
64
+ }
65
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aashari/boilerplate-mcp-server",
3
- "version": "1.14.0",
3
+ "version": "1.15.0",
4
4
  "description": "TypeScript MCP server boilerplate with STDIO and HTTP transport support, CLI tools, and extensible architecture",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -60,6 +60,7 @@
60
60
  "@types/cors": "^2.8.19",
61
61
  "@types/express": "^5.0.3",
62
62
  "@types/jest": "^30.0.0",
63
+ "@types/jmespath": "^0.15.2",
63
64
  "@types/node": "^24.3.1",
64
65
  "@typescript-eslint/eslint-plugin": "^8.43.0",
65
66
  "@typescript-eslint/parser": "^8.43.0",
@@ -75,10 +76,12 @@
75
76
  },
76
77
  "dependencies": {
77
78
  "@modelcontextprotocol/sdk": "^1.17.5",
79
+ "@toon-format/toon": "^2.0.1",
78
80
  "commander": "^14.0.0",
79
81
  "cors": "^2.8.5",
80
82
  "dotenv": "^17.2.2",
81
83
  "express": "^5.1.0",
84
+ "jmespath": "^0.16.0",
82
85
  "zod": "^3.25.76"
83
86
  },
84
87
  "publishConfig": {
package/package.json.bak CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aashari/boilerplate-mcp-server",
3
- "version": "1.13.5",
3
+ "version": "1.14.0",
4
4
  "description": "TypeScript MCP server boilerplate with STDIO and HTTP transport support, CLI tools, and extensible architecture",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -60,6 +60,7 @@
60
60
  "@types/cors": "^2.8.19",
61
61
  "@types/express": "^5.0.3",
62
62
  "@types/jest": "^30.0.0",
63
+ "@types/jmespath": "^0.15.2",
63
64
  "@types/node": "^24.3.1",
64
65
  "@typescript-eslint/eslint-plugin": "^8.43.0",
65
66
  "@typescript-eslint/parser": "^8.43.0",
@@ -75,10 +76,12 @@
75
76
  },
76
77
  "dependencies": {
77
78
  "@modelcontextprotocol/sdk": "^1.17.5",
79
+ "@toon-format/toon": "^2.0.1",
78
80
  "commander": "^14.0.0",
79
81
  "cors": "^2.8.5",
80
82
  "dotenv": "^17.2.2",
81
83
  "express": "^5.1.0",
84
+ "jmespath": "^0.16.0",
82
85
  "zod": "^3.25.76"
83
86
  },
84
87
  "publishConfig": {