@aashari/boilerplate-mcp-server 1.1.2 → 1.2.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/CHANGELOG.md +14 -0
- package/dist/cli/index.d.ts +6 -0
- package/dist/cli/index.js +20 -8
- package/dist/cli/ipaddress.cli.js +7 -5
- package/dist/controllers/ipaddress.controller.d.ts +14 -4
- package/dist/controllers/ipaddress.controller.js +14 -4
- package/dist/index.d.ts +9 -1
- package/dist/index.js +57 -27
- package/dist/services/vendor.ip-api.com.service.d.ts +22 -5
- package/dist/services/vendor.ip-api.com.service.js +22 -5
- package/dist/tools/ipaddress.tool.d.ts +4 -1
- package/dist/tools/ipaddress.tool.js +32 -23
- package/dist/utils/constants.util.d.ts +21 -0
- package/dist/utils/constants.util.js +24 -0
- package/dist/utils/constants.util.js.bak +24 -0
- package/dist/utils/transport.util.d.ts +23 -14
- package/dist/utils/transport.util.js +84 -64
- package/package.json +2 -2
- package/package.json.bak +2 -2
- package/scripts/update-version.js +4 -17
- package/dist/cli/index.js.bak +0 -31
- package/dist/index.js.bak +0 -88
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [1.2.0](https://github.com/aashari/boilerplate-mcp-server/compare/v1.1.3...v1.2.0) (2025-04-03)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **boilerplate:** improve version handling and module exports ([faa1713](https://github.com/aashari/boilerplate-mcp-server/commit/faa17138ccb1b943197ae91b37a54527481ffbca))
|
|
7
|
+
|
|
8
|
+
## [1.1.3](https://github.com/aashari/boilerplate-mcp-server/compare/v1.1.2...v1.1.3) (2025-03-28)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* correct TypeScript errors in transport utility ([573a7e6](https://github.com/aashari/boilerplate-mcp-server/commit/573a7e63e1985aa5aefd806c0902462fa34c14d7))
|
|
14
|
+
|
|
1
15
|
## [1.1.2](https://github.com/aashari/boilerplate-mcp-server/compare/v1.1.1...v1.1.2) (2025-03-28)
|
|
2
16
|
|
|
3
17
|
|
package/dist/cli/index.d.ts
CHANGED
package/dist/cli/index.js
CHANGED
|
@@ -6,26 +6,38 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.runCli = runCli;
|
|
7
7
|
const commander_1 = require("commander");
|
|
8
8
|
const logger_util_js_1 = require("../utils/logger.util.js");
|
|
9
|
+
const constants_util_js_1 = require("../utils/constants.util.js");
|
|
9
10
|
const ipaddress_cli_js_1 = __importDefault(require("./ipaddress.cli.js"));
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* CLI entry point for the Boilerplate MCP Server
|
|
13
|
+
* Handles command registration, parsing, and execution
|
|
14
|
+
*/
|
|
15
|
+
// Package description
|
|
13
16
|
const DESCRIPTION = 'A boilerplate Model Context Protocol (MCP) server implementation using TypeScript';
|
|
17
|
+
/**
|
|
18
|
+
* Run the CLI with the provided arguments
|
|
19
|
+
*
|
|
20
|
+
* @param args Command line arguments to process
|
|
21
|
+
* @returns Promise that resolves when CLI command execution completes
|
|
22
|
+
*/
|
|
14
23
|
async function runCli(args) {
|
|
15
|
-
const
|
|
16
|
-
|
|
24
|
+
const cliLogger = logger_util_js_1.Logger.forContext('cli/index.ts', 'runCli');
|
|
25
|
+
cliLogger.debug('Initializing CLI with arguments', args);
|
|
17
26
|
const program = new commander_1.Command();
|
|
18
|
-
program.name(
|
|
27
|
+
program.name(constants_util_js_1.CLI_NAME).description(DESCRIPTION).version(constants_util_js_1.VERSION);
|
|
19
28
|
// Register CLI commands
|
|
29
|
+
cliLogger.debug('Registering CLI commands...');
|
|
20
30
|
ipaddress_cli_js_1.default.register(program);
|
|
31
|
+
cliLogger.debug('CLI commands registered successfully');
|
|
21
32
|
// Handle unknown commands
|
|
22
33
|
program.on('command:*', (operands) => {
|
|
23
|
-
|
|
34
|
+
cliLogger.error(`Unknown command: ${operands[0]}`);
|
|
24
35
|
console.log('');
|
|
25
36
|
program.help();
|
|
26
37
|
process.exit(1);
|
|
27
38
|
});
|
|
28
39
|
// Parse arguments; default to help if no command provided
|
|
40
|
+
cliLogger.debug('Parsing CLI arguments');
|
|
29
41
|
await program.parseAsync(args.length ? args : ['--help'], { from: 'user' });
|
|
30
|
-
|
|
42
|
+
cliLogger.debug('CLI command execution completed');
|
|
31
43
|
}
|
|
@@ -11,8 +11,8 @@ const ipaddress_controller_js_1 = __importDefault(require("../controllers/ipaddr
|
|
|
11
11
|
* @param program The Commander program instance
|
|
12
12
|
*/
|
|
13
13
|
function register(program) {
|
|
14
|
-
const
|
|
15
|
-
|
|
14
|
+
const cliLogger = logger_util_js_1.Logger.forContext('cli/ipaddress.cli.ts', 'register');
|
|
15
|
+
cliLogger.debug(`Registering IP address CLI commands...`);
|
|
16
16
|
program
|
|
17
17
|
.command('get-ip-details')
|
|
18
18
|
.description(`Get geolocation and network details about an IP address or the current device.
|
|
@@ -31,21 +31,23 @@ function register(program) {
|
|
|
31
31
|
.option('--extended', 'Include extended data like ASN, mobile and proxy detection')
|
|
32
32
|
.option('--https', 'Use HTTPS for API requests (may require paid API key)')
|
|
33
33
|
.action(async (ipAddress, cmdOptions) => {
|
|
34
|
-
const
|
|
34
|
+
const commandLogger = logger_util_js_1.Logger.forContext('cli/ipaddress.cli.ts', 'get-ip-details');
|
|
35
35
|
try {
|
|
36
|
-
|
|
36
|
+
commandLogger.debug(`Processing IP details request for ${ipAddress || 'current device'}`, cmdOptions);
|
|
37
37
|
// Map CLI options to controller options
|
|
38
38
|
const controllerOptions = {
|
|
39
39
|
includeExtendedData: cmdOptions?.extended || false,
|
|
40
40
|
useHttps: cmdOptions?.https || false,
|
|
41
41
|
};
|
|
42
|
+
commandLogger.debug('Calling controller with options', controllerOptions);
|
|
42
43
|
const result = await ipaddress_controller_js_1.default.get(ipAddress, controllerOptions);
|
|
43
|
-
|
|
44
|
+
commandLogger.debug(`IP details retrieved successfully`);
|
|
44
45
|
console.log(result.content);
|
|
45
46
|
}
|
|
46
47
|
catch (error) {
|
|
47
48
|
(0, error_util_js_1.handleCliError)(error);
|
|
48
49
|
}
|
|
49
50
|
});
|
|
51
|
+
cliLogger.debug('IP address CLI commands registered successfully');
|
|
50
52
|
}
|
|
51
53
|
exports.default = { register };
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import { ControllerResponse } from '../types/common.types.js';
|
|
2
2
|
import { GetIpOptions } from './ipaddress.types.js';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
* @
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* @namespace IpAddressController
|
|
5
|
+
* @description Controller responsible for handling IP address lookup logic.
|
|
6
|
+
* It orchestrates calls to the ip-api.com service, applies defaults,
|
|
7
|
+
* maps options, and formats the response using the formatter.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* @function get
|
|
11
|
+
* @description Fetches details for a specific IP address or the current device's IP.
|
|
12
|
+
* Handles mapping controller options (like includeExtendedData) to service parameters (fields).
|
|
13
|
+
* @memberof IpAddressController
|
|
14
|
+
* @param {string} [ipAddress] - Optional IP address to look up. If omitted, the service will fetch the current device's public IP.
|
|
15
|
+
* @param {GetIpOptions} [options={}] - Optional configuration for the request, such as `includeExtendedData` and `useHttps`.
|
|
16
|
+
* @returns {Promise<ControllerResponse>} A promise that resolves to the standard controller response containing the formatted IP details in Markdown.
|
|
17
|
+
* @throws {McpError} Throws an McpError (handled by `handleControllerError`) if the service call fails or returns an error.
|
|
8
18
|
*/
|
|
9
19
|
declare function get(ipAddress?: string, options?: GetIpOptions): Promise<ControllerResponse>;
|
|
10
20
|
declare const _default: {
|
|
@@ -9,10 +9,20 @@ const ipaddress_formatter_js_1 = require("./ipaddress.formatter.js");
|
|
|
9
9
|
const error_handler_util_js_1 = require("../utils/error-handler.util.js");
|
|
10
10
|
const defaults_util_js_1 = require("../utils/defaults.util.js");
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
13
|
-
* @
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* @namespace IpAddressController
|
|
13
|
+
* @description Controller responsible for handling IP address lookup logic.
|
|
14
|
+
* It orchestrates calls to the ip-api.com service, applies defaults,
|
|
15
|
+
* maps options, and formats the response using the formatter.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* @function get
|
|
19
|
+
* @description Fetches details for a specific IP address or the current device's IP.
|
|
20
|
+
* Handles mapping controller options (like includeExtendedData) to service parameters (fields).
|
|
21
|
+
* @memberof IpAddressController
|
|
22
|
+
* @param {string} [ipAddress] - Optional IP address to look up. If omitted, the service will fetch the current device's public IP.
|
|
23
|
+
* @param {GetIpOptions} [options={}] - Optional configuration for the request, such as `includeExtendedData` and `useHttps`.
|
|
24
|
+
* @returns {Promise<ControllerResponse>} A promise that resolves to the standard controller response containing the formatted IP details in Markdown.
|
|
25
|
+
* @throws {McpError} Throws an McpError (handled by `handleControllerError`) if the service call fails or returns an error.
|
|
16
26
|
*/
|
|
17
27
|
async function get(ipAddress, options = {}) {
|
|
18
28
|
const methodLogger = logger_util_js_1.Logger.forContext('controllers/ipaddress.controller.ts', 'get');
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
3
|
import { Logger } from './utils/logger.util.js';
|
|
3
4
|
import { config } from './utils/config.util.js';
|
|
4
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Start the MCP server with the specified transport mode
|
|
7
|
+
*
|
|
8
|
+
* @param mode The transport mode to use (stdio or sse)
|
|
9
|
+
* @returns Promise that resolves to the server instance when started successfully
|
|
10
|
+
*/
|
|
11
|
+
export declare function startServer(mode?: 'stdio' | 'sse'): Promise<McpServer>;
|
|
5
12
|
export { config };
|
|
6
13
|
export { Logger };
|
|
14
|
+
export { VERSION, PACKAGE_NAME } from './utils/constants.util.js';
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.Logger = exports.config = void 0;
|
|
7
|
+
exports.PACKAGE_NAME = exports.VERSION = exports.Logger = exports.config = void 0;
|
|
8
8
|
exports.startServer = startServer;
|
|
9
9
|
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
10
10
|
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
@@ -13,70 +13,97 @@ Object.defineProperty(exports, "Logger", { enumerable: true, get: function () {
|
|
|
13
13
|
const config_util_js_1 = require("./utils/config.util.js");
|
|
14
14
|
Object.defineProperty(exports, "config", { enumerable: true, get: function () { return config_util_js_1.config; } });
|
|
15
15
|
const error_util_js_1 = require("./utils/error.util.js");
|
|
16
|
+
const constants_util_js_1 = require("./utils/constants.util.js");
|
|
16
17
|
const index_js_1 = require("./cli/index.js");
|
|
18
|
+
// Import tools and resources
|
|
17
19
|
const ipaddress_tool_js_1 = __importDefault(require("./tools/ipaddress.tool.js"));
|
|
18
20
|
const ipaddress_resource_js_1 = __importDefault(require("./resources/ipaddress.resource.js"));
|
|
21
|
+
/**
|
|
22
|
+
* Boilerplate MCP Server
|
|
23
|
+
*
|
|
24
|
+
* A template project for building MCP servers that follow best practices.
|
|
25
|
+
* Demonstrates proper structure, logging, error handling, and MCP protocol integration.
|
|
26
|
+
*/
|
|
19
27
|
// Create file-level logger
|
|
20
28
|
const indexLogger = logger_util_js_1.Logger.forContext('index.ts');
|
|
21
|
-
//
|
|
22
|
-
|
|
29
|
+
// Log initialization at debug level
|
|
30
|
+
indexLogger.debug('Boilerplate MCP server module loaded');
|
|
23
31
|
let serverInstance = null;
|
|
24
32
|
let transportInstance = null;
|
|
33
|
+
/**
|
|
34
|
+
* Start the MCP server with the specified transport mode
|
|
35
|
+
*
|
|
36
|
+
* @param mode The transport mode to use (stdio or sse)
|
|
37
|
+
* @returns Promise that resolves to the server instance when started successfully
|
|
38
|
+
*/
|
|
25
39
|
async function startServer(mode = 'stdio') {
|
|
26
|
-
const
|
|
40
|
+
const serverLogger = logger_util_js_1.Logger.forContext('index.ts', 'startServer');
|
|
27
41
|
// Load configuration
|
|
42
|
+
serverLogger.info('Starting MCP server initialization...');
|
|
28
43
|
config_util_js_1.config.load();
|
|
44
|
+
serverLogger.info('Configuration loaded successfully');
|
|
29
45
|
// Enable debug logging if DEBUG is set to true
|
|
30
46
|
if (config_util_js_1.config.getBoolean('DEBUG')) {
|
|
31
|
-
|
|
47
|
+
serverLogger.debug('Debug mode enabled');
|
|
32
48
|
}
|
|
33
49
|
// Log the DEBUG value to verify configuration loading
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
50
|
+
serverLogger.debug(`DEBUG environment variable: ${process.env.DEBUG}`);
|
|
51
|
+
serverLogger.debug(`IPAPI_API_TOKEN value exists: ${Boolean(process.env.IPAPI_API_TOKEN)}`);
|
|
52
|
+
serverLogger.debug(`Config DEBUG value: ${config_util_js_1.config.get('DEBUG')}`);
|
|
53
|
+
serverLogger.info(`Initializing Boilerplate MCP server v${constants_util_js_1.VERSION}`);
|
|
37
54
|
serverInstance = new mcp_js_1.McpServer({
|
|
38
|
-
name:
|
|
39
|
-
version: VERSION,
|
|
55
|
+
name: constants_util_js_1.PACKAGE_NAME,
|
|
56
|
+
version: constants_util_js_1.VERSION,
|
|
40
57
|
});
|
|
41
58
|
if (mode === 'stdio') {
|
|
59
|
+
serverLogger.info('Using STDIO transport for MCP communication');
|
|
42
60
|
transportInstance = new stdio_js_1.StdioServerTransport();
|
|
43
61
|
}
|
|
44
62
|
else {
|
|
45
63
|
throw (0, error_util_js_1.createUnexpectedError)('SSE mode is not supported yet');
|
|
46
64
|
}
|
|
47
|
-
|
|
48
|
-
|
|
65
|
+
// Register tools and resources
|
|
66
|
+
serverLogger.info('Registering MCP tools and resources...');
|
|
49
67
|
ipaddress_tool_js_1.default.register(serverInstance);
|
|
50
|
-
|
|
51
|
-
// register resources
|
|
68
|
+
serverLogger.debug('Registered IP address tools');
|
|
52
69
|
ipaddress_resource_js_1.default.register(serverInstance);
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
70
|
+
serverLogger.debug('Registered IP lookup resources');
|
|
71
|
+
serverLogger.info('All tools and resources registered successfully');
|
|
72
|
+
try {
|
|
73
|
+
serverLogger.info(`Connecting to ${mode.toUpperCase()} transport...`);
|
|
74
|
+
await serverInstance.connect(transportInstance);
|
|
75
|
+
serverLogger.info('MCP server started successfully and ready to process requests');
|
|
76
|
+
return serverInstance;
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
serverLogger.error(`Failed to start server`, err);
|
|
56
80
|
process.exit(1);
|
|
57
|
-
}
|
|
81
|
+
}
|
|
58
82
|
}
|
|
59
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Main entry point - this will run when executed directly
|
|
85
|
+
* Determines whether to run in CLI or server mode based on command-line arguments
|
|
86
|
+
*/
|
|
60
87
|
async function main() {
|
|
61
|
-
const
|
|
88
|
+
const mainLogger = logger_util_js_1.Logger.forContext('index.ts', 'main');
|
|
62
89
|
// Load configuration
|
|
63
90
|
config_util_js_1.config.load();
|
|
64
91
|
// Log the DEBUG value to verify configuration loading
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
92
|
+
mainLogger.debug(`DEBUG environment variable: ${process.env.DEBUG}`);
|
|
93
|
+
mainLogger.debug(`IPAPI_API_TOKEN value exists: ${Boolean(process.env.IPAPI_API_TOKEN)}`);
|
|
94
|
+
mainLogger.debug(`Config DEBUG value: ${config_util_js_1.config.get('DEBUG')}`);
|
|
68
95
|
// Check if arguments are provided (CLI mode)
|
|
69
96
|
if (process.argv.length > 2) {
|
|
70
97
|
// CLI mode: Pass arguments to CLI runner
|
|
71
|
-
|
|
98
|
+
mainLogger.info('Starting in CLI mode');
|
|
72
99
|
await (0, index_js_1.runCli)(process.argv.slice(2));
|
|
73
|
-
|
|
100
|
+
mainLogger.info('CLI execution completed');
|
|
74
101
|
}
|
|
75
102
|
else {
|
|
76
103
|
// MCP Server mode: Start server with default STDIO
|
|
77
|
-
|
|
104
|
+
mainLogger.info('Starting in server mode');
|
|
78
105
|
await startServer();
|
|
79
|
-
|
|
106
|
+
mainLogger.info('Server is now running');
|
|
80
107
|
}
|
|
81
108
|
}
|
|
82
109
|
// If this file is being executed directly (not imported), run the main function
|
|
@@ -86,3 +113,6 @@ if (require.main === module) {
|
|
|
86
113
|
process.exit(1);
|
|
87
114
|
});
|
|
88
115
|
}
|
|
116
|
+
var constants_util_js_2 = require("./utils/constants.util.js");
|
|
117
|
+
Object.defineProperty(exports, "VERSION", { enumerable: true, get: function () { return constants_util_js_2.VERSION; } });
|
|
118
|
+
Object.defineProperty(exports, "PACKAGE_NAME", { enumerable: true, get: function () { return constants_util_js_2.PACKAGE_NAME; } });
|
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
import { IPDetail, IPApiRequestOptions } from './vendor.ip-api.com.types.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* @
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
|
|
3
|
+
* @namespace VendorIpApiService
|
|
4
|
+
* @description Service layer for interacting directly with the ip-api.com vendor API.
|
|
5
|
+
* Responsible for constructing API requests based on provided parameters
|
|
6
|
+
* and handling the raw response from the `fetchIpApi` utility.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* @function get
|
|
10
|
+
* @description Fetches details for a specific IP address or the current device's IP from ip-api.com.
|
|
11
|
+
* It uses the `fetchIpApi` utility and handles the specific success/failure status returned by ip-api.com.
|
|
12
|
+
* @memberof VendorIpApiService
|
|
13
|
+
* @param {string} [ipAddress] - Optional IP address to look up. If omitted, fetches details for the current device's public IP.
|
|
14
|
+
* @param {IPApiRequestOptions} [options={}] - Optional request options for the ip-api.com service, such as `useHttps`, `fields`, and `lang`.
|
|
15
|
+
* @returns {Promise<IPDetail>} A promise that resolves to the detailed IP information if the API call is successful.
|
|
16
|
+
* @throws {McpError} Throws an `McpError` (specifically `ApiError` or `UnexpectedError`) if:
|
|
17
|
+
* - The `fetchIpApi` call fails (network error, non-2xx response).
|
|
18
|
+
* - The ip-api.com response status is not 'success'.
|
|
19
|
+
* - An unexpected error occurs during processing.
|
|
20
|
+
* @example
|
|
21
|
+
* // Get basic details for 8.8.8.8
|
|
22
|
+
* const details = await get('8.8.8.8');
|
|
23
|
+
* // Get extended details using HTTPS
|
|
24
|
+
* const extendedDetails = await get('1.1.1.1', { useHttps: true, fields: [...] });
|
|
8
25
|
*/
|
|
9
26
|
declare function get(ipAddress?: string, options?: IPApiRequestOptions): Promise<IPDetail>;
|
|
10
27
|
declare const _default: {
|
|
@@ -8,11 +8,28 @@ const serviceLogger = logger_util_js_1.Logger.forContext('services/vendor.ip-api
|
|
|
8
8
|
// Log service initialization
|
|
9
9
|
serviceLogger.debug('IP API service initialized');
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
* @
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
|
|
11
|
+
* @namespace VendorIpApiService
|
|
12
|
+
* @description Service layer for interacting directly with the ip-api.com vendor API.
|
|
13
|
+
* Responsible for constructing API requests based on provided parameters
|
|
14
|
+
* and handling the raw response from the `fetchIpApi` utility.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* @function get
|
|
18
|
+
* @description Fetches details for a specific IP address or the current device's IP from ip-api.com.
|
|
19
|
+
* It uses the `fetchIpApi` utility and handles the specific success/failure status returned by ip-api.com.
|
|
20
|
+
* @memberof VendorIpApiService
|
|
21
|
+
* @param {string} [ipAddress] - Optional IP address to look up. If omitted, fetches details for the current device's public IP.
|
|
22
|
+
* @param {IPApiRequestOptions} [options={}] - Optional request options for the ip-api.com service, such as `useHttps`, `fields`, and `lang`.
|
|
23
|
+
* @returns {Promise<IPDetail>} A promise that resolves to the detailed IP information if the API call is successful.
|
|
24
|
+
* @throws {McpError} Throws an `McpError` (specifically `ApiError` or `UnexpectedError`) if:
|
|
25
|
+
* - The `fetchIpApi` call fails (network error, non-2xx response).
|
|
26
|
+
* - The ip-api.com response status is not 'success'.
|
|
27
|
+
* - An unexpected error occurs during processing.
|
|
28
|
+
* @example
|
|
29
|
+
* // Get basic details for 8.8.8.8
|
|
30
|
+
* const details = await get('8.8.8.8');
|
|
31
|
+
* // Get extended details using HTTPS
|
|
32
|
+
* const extendedDetails = await get('1.1.1.1', { useHttps: true, fields: [...] });
|
|
16
33
|
*/
|
|
17
34
|
async function get(ipAddress, options = {}) {
|
|
18
35
|
const methodLogger = logger_util_js_1.Logger.forContext('services/vendor.ip-api.com.service.ts', 'get');
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* @function register
|
|
4
|
+
* @description Registers the IP address lookup tool ('get-ip-details') with the MCP server.
|
|
5
|
+
*
|
|
6
|
+
* @param {McpServer} server - The MCP server instance.
|
|
4
7
|
*/
|
|
5
8
|
declare function register(server: McpServer): void;
|
|
6
9
|
declare const _default: {
|
|
@@ -8,8 +8,14 @@ const ipaddress_types_js_1 = require("./ipaddress.types.js");
|
|
|
8
8
|
const error_util_js_1 = require("../utils/error.util.js");
|
|
9
9
|
const ipaddress_controller_js_1 = __importDefault(require("../controllers/ipaddress.controller.js"));
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* @function getIpAddressDetails
|
|
12
|
+
* @description MCP Tool handler to retrieve details for a given IP address (or the current IP).
|
|
13
|
+
* It calls the ipAddressController to fetch the data and formats the response for the MCP.
|
|
14
|
+
*
|
|
15
|
+
* @param {IpAddressToolArgsType} args - Arguments provided to the tool, including the optional IP address and options.
|
|
16
|
+
* @param {RequestHandlerExtra} _extra - Additional request context (unused).
|
|
17
|
+
* @returns {Promise<{ content: Array<{ type: 'text', text: string }> }>} Formatted response for the MCP.
|
|
18
|
+
* @throws {McpError} Formatted error if the controller or service layer encounters an issue.
|
|
13
19
|
*/
|
|
14
20
|
async function getIpAddressDetails(args, _extra) {
|
|
15
21
|
const methodLogger = logger_util_js_1.Logger.forContext('tools/ipaddress.tool.ts', 'getIpAddressDetails');
|
|
@@ -39,14 +45,18 @@ async function getIpAddressDetails(args, _extra) {
|
|
|
39
45
|
}
|
|
40
46
|
}
|
|
41
47
|
/**
|
|
42
|
-
*
|
|
48
|
+
* @function register
|
|
49
|
+
* @description Registers the IP address lookup tool ('get-ip-details') with the MCP server.
|
|
50
|
+
*
|
|
51
|
+
* @param {McpServer} server - The MCP server instance.
|
|
43
52
|
*/
|
|
44
53
|
function register(server) {
|
|
45
54
|
const methodLogger = logger_util_js_1.Logger.forContext('tools/ipaddress.tool.ts', 'register');
|
|
46
55
|
methodLogger.debug(`Registering IP address tools...`);
|
|
47
56
|
server.tool('get-ip-details', `Get details about a specific IP address or the current device's public IP address.
|
|
48
57
|
|
|
49
|
-
PURPOSE:
|
|
58
|
+
PURPOSE:
|
|
59
|
+
Retrieves geolocation information (country, city, region, coordinates), ISP, and organization details associated with an IP address. Provides network and geographical context for an IP, which is useful for security analysis, debugging, or location verification.
|
|
50
60
|
|
|
51
61
|
WHEN TO USE:
|
|
52
62
|
- To find the geographical location of a given IP address (country, region, city, coordinates).
|
|
@@ -57,34 +67,33 @@ function register(server) {
|
|
|
57
67
|
- When investigating suspicious IP addresses in logs.
|
|
58
68
|
|
|
59
69
|
WHEN NOT TO USE:
|
|
60
|
-
- For internal/private IP addresses (10.x.x.x, 192.168.x.x
|
|
70
|
+
- For internal/private IP addresses (e.g., 10.x.x.x, 192.168.x.x) as this tool queries a public database.
|
|
61
71
|
- When you need historical IP data (this provides current lookup only).
|
|
62
|
-
- For precise geolocation (IP geolocation
|
|
72
|
+
- For precise geolocation (IP geolocation accuracy is limited).
|
|
63
73
|
- For operations other than retrieving IP geolocation details.
|
|
64
|
-
- When
|
|
74
|
+
- When processing large batches of IPs without considering rate limits.
|
|
65
75
|
|
|
66
|
-
RETURNS:
|
|
76
|
+
RETURNS:
|
|
77
|
+
Formatted Markdown containing:
|
|
67
78
|
- Location information (country, region, city, postal code, coordinates)
|
|
68
|
-
- Network details (ISP, organization, AS number)
|
|
69
|
-
- A link to view the location on a map
|
|
70
|
-
- Timestamp of when the information was retrieved
|
|
71
|
-
|
|
72
|
-
With extended data (when includeExtendedData=true), additional information may include:
|
|
73
|
-
- Mobile network detection
|
|
74
|
-
- Proxy/VPN detection
|
|
75
|
-
- Hosting provider detection
|
|
76
|
-
- Reverse DNS information
|
|
79
|
+
- Network details (ISP, organization, AS number/name)
|
|
80
|
+
- A link to view the location on a map.
|
|
81
|
+
- Timestamp of when the information was retrieved.
|
|
82
|
+
- With 'includeExtendedData=true', additional details like reverse DNS, mobile/proxy/hosting detection may be included.
|
|
77
83
|
|
|
78
84
|
EXAMPLES:
|
|
79
85
|
- Get details for a specific IP: { ipAddress: "8.8.8.8" }
|
|
80
|
-
- Get details with extended data: { ipAddress: "
|
|
81
|
-
- Get details for current device
|
|
86
|
+
- Get details with extended data: { ipAddress: "1.1.1.1", includeExtendedData: true }
|
|
87
|
+
- Get details for current device using HTTPS: { useHttps: true }
|
|
82
88
|
- Get basic details for current device: {}
|
|
83
89
|
|
|
84
90
|
ERRORS:
|
|
85
|
-
- Invalid IP format: If
|
|
86
|
-
- Private/Reserved IP: If the IP address is in a private or reserved range.
|
|
87
|
-
- API errors: If the external ip-api.com service fails
|
|
88
|
-
- Rate limiting: If
|
|
91
|
+
- Invalid IP format: If 'ipAddress' is not a valid IPv4 or IPv6 format.
|
|
92
|
+
- Private/Reserved IP: If the IP address is in a private or reserved range (per ip-api.com rules).
|
|
93
|
+
- API errors: If the external ip-api.com service fails, rejects the request (e.g., bad token), or returns an error status.
|
|
94
|
+
- Rate limiting: If the ip-api.com service rate limits the request.
|
|
95
|
+
- Network errors: If the request to ip-api.com fails due to network issues.`, // Keep Zod schema reference
|
|
96
|
+
ipaddress_types_js_1.IpAddressToolArgs.shape, getIpAddressDetails);
|
|
97
|
+
methodLogger.debug('Successfully registered get-ip-details tool.');
|
|
89
98
|
}
|
|
90
99
|
exports.default = { register };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Application constants
|
|
3
|
+
*
|
|
4
|
+
* This file contains constants used throughout the application.
|
|
5
|
+
* Centralizing these values makes them easier to maintain and update.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Current application version
|
|
9
|
+
* This should match the version in package.json
|
|
10
|
+
*/
|
|
11
|
+
export declare const VERSION = "1.2.0";
|
|
12
|
+
/**
|
|
13
|
+
* Package name with scope
|
|
14
|
+
* Used for initialization and identification
|
|
15
|
+
*/
|
|
16
|
+
export declare const PACKAGE_NAME = "@aashari/boilerplate-mcp-server";
|
|
17
|
+
/**
|
|
18
|
+
* CLI command name
|
|
19
|
+
* Used for binary name and CLI help text
|
|
20
|
+
*/
|
|
21
|
+
export declare const CLI_NAME = "mcp-boilerplate";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Application constants
|
|
4
|
+
*
|
|
5
|
+
* This file contains constants used throughout the application.
|
|
6
|
+
* Centralizing these values makes them easier to maintain and update.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.CLI_NAME = exports.PACKAGE_NAME = exports.VERSION = void 0;
|
|
10
|
+
/**
|
|
11
|
+
* Current application version
|
|
12
|
+
* This should match the version in package.json
|
|
13
|
+
*/
|
|
14
|
+
exports.VERSION = '1.2.0';
|
|
15
|
+
/**
|
|
16
|
+
* Package name with scope
|
|
17
|
+
* Used for initialization and identification
|
|
18
|
+
*/
|
|
19
|
+
exports.PACKAGE_NAME = '@aashari/boilerplate-mcp-server';
|
|
20
|
+
/**
|
|
21
|
+
* CLI command name
|
|
22
|
+
* Used for binary name and CLI help text
|
|
23
|
+
*/
|
|
24
|
+
exports.CLI_NAME = 'mcp-boilerplate';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Application constants
|
|
4
|
+
*
|
|
5
|
+
* This file contains constants used throughout the application.
|
|
6
|
+
* Centralizing these values makes them easier to maintain and update.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.CLI_NAME = exports.PACKAGE_NAME = exports.VERSION = void 0;
|
|
10
|
+
/**
|
|
11
|
+
* Current application version
|
|
12
|
+
* This should match the version in package.json
|
|
13
|
+
*/
|
|
14
|
+
exports.VERSION = '1.1.3';
|
|
15
|
+
/**
|
|
16
|
+
* Package name with scope
|
|
17
|
+
* Used for initialization and identification
|
|
18
|
+
*/
|
|
19
|
+
exports.PACKAGE_NAME = '@aashari/boilerplate-mcp-server';
|
|
20
|
+
/**
|
|
21
|
+
* CLI command name
|
|
22
|
+
* Used for binary name and CLI help text
|
|
23
|
+
*/
|
|
24
|
+
exports.CLI_NAME = 'mcp-boilerplate';
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Interface for IP API credentials
|
|
2
|
+
* Interface for IP API credentials.
|
|
3
|
+
* Note: API token is optional for the free tier.
|
|
3
4
|
*/
|
|
4
5
|
export interface IpApiCredentials {
|
|
5
|
-
apiToken
|
|
6
|
+
apiToken?: string;
|
|
6
7
|
}
|
|
7
8
|
/**
|
|
8
9
|
* Interface for HTTP request options
|
|
@@ -13,16 +14,23 @@ export interface RequestOptions {
|
|
|
13
14
|
body?: unknown;
|
|
14
15
|
}
|
|
15
16
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
17
|
+
* Retrieves IP API credentials from configuration.
|
|
18
|
+
* Specifically checks for IPAPI_API_TOKEN.
|
|
19
|
+
* @returns IpApiCredentials object containing the API token if found.
|
|
18
20
|
*/
|
|
19
21
|
export declare function getIpApiCredentials(): IpApiCredentials;
|
|
20
22
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* @
|
|
23
|
+
* Fetches data specifically from the ip-api.com endpoint.
|
|
24
|
+
* Handles URL construction, authentication (if token provided), and query parameters.
|
|
25
|
+
* Relies on the generic fetchApi function for the actual HTTP request.
|
|
26
|
+
*
|
|
27
|
+
* @param path The specific IP address or path component (e.g., "8.8.8.8"). Empty string for current IP.
|
|
28
|
+
* @param options Additional options like HTTP method, headers, body, and ip-api specific params.
|
|
29
|
+
* @param options.useHttps - Use HTTPS (requires paid plan for ip-api.com). Defaults to false.
|
|
30
|
+
* @param options.fields - Specific fields to request from ip-api.com.
|
|
31
|
+
* @param options.lang - Language code for response data.
|
|
32
|
+
* @returns The response data parsed as type T.
|
|
33
|
+
* @throws {McpError} If the request fails, including network errors, API errors, or parsing issues.
|
|
26
34
|
*/
|
|
27
35
|
export declare function fetchIpApi<T>(path: string, options?: RequestOptions & {
|
|
28
36
|
useHttps?: boolean;
|
|
@@ -30,11 +38,12 @@ export declare function fetchIpApi<T>(path: string, options?: RequestOptions & {
|
|
|
30
38
|
lang?: string;
|
|
31
39
|
}): Promise<T>;
|
|
32
40
|
/**
|
|
33
|
-
* Generic function to fetch data from
|
|
34
|
-
* Handles
|
|
41
|
+
* Generic and reusable function to fetch data from any API endpoint.
|
|
42
|
+
* Handles standard HTTP request setup, response checking, basic error handling, and logging.
|
|
43
|
+
*
|
|
35
44
|
* @param url The full URL to fetch data from.
|
|
36
|
-
* @param options Request options
|
|
37
|
-
* @returns
|
|
38
|
-
* @throws {McpError} If the
|
|
45
|
+
* @param options Request options including method, headers, and body.
|
|
46
|
+
* @returns The response data parsed as type T.
|
|
47
|
+
* @throws {McpError} If the request fails, including network errors, non-OK HTTP status, or JSON parsing issues.
|
|
39
48
|
*/
|
|
40
49
|
export declare function fetchApi<T>(url: string, options?: RequestOptions): Promise<T>;
|
|
@@ -11,64 +11,69 @@ const transportLogger = logger_util_js_1.Logger.forContext('utils/transport.util
|
|
|
11
11
|
// Log transport utility initialization
|
|
12
12
|
transportLogger.debug('Transport utility initialized');
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* Retrieves IP API credentials from configuration.
|
|
15
|
+
* Specifically checks for IPAPI_API_TOKEN.
|
|
16
|
+
* @returns IpApiCredentials object containing the API token if found.
|
|
16
17
|
*/
|
|
17
18
|
function getIpApiCredentials() {
|
|
18
19
|
const methodLogger = logger_util_js_1.Logger.forContext('utils/transport.util.ts', 'getIpApiCredentials');
|
|
19
20
|
const apiToken = config_util_js_1.config.get('IPAPI_API_TOKEN');
|
|
20
21
|
if (!apiToken) {
|
|
21
|
-
methodLogger.debug('No IP API token found.
|
|
22
|
+
methodLogger.debug('No IP API token found (IPAPI_API_TOKEN). Using free tier.');
|
|
23
|
+
return {}; // Return empty object if no token
|
|
22
24
|
}
|
|
23
25
|
else {
|
|
24
|
-
methodLogger.debug('Using IP API token from configuration');
|
|
26
|
+
methodLogger.debug('Using IP API token from configuration.');
|
|
27
|
+
return { apiToken };
|
|
25
28
|
}
|
|
26
|
-
return {
|
|
27
|
-
apiToken,
|
|
28
|
-
};
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* @
|
|
31
|
+
* Fetches data specifically from the ip-api.com endpoint.
|
|
32
|
+
* Handles URL construction, authentication (if token provided), and query parameters.
|
|
33
|
+
* Relies on the generic fetchApi function for the actual HTTP request.
|
|
34
|
+
*
|
|
35
|
+
* @param path The specific IP address or path component (e.g., "8.8.8.8"). Empty string for current IP.
|
|
36
|
+
* @param options Additional options like HTTP method, headers, body, and ip-api specific params.
|
|
37
|
+
* @param options.useHttps - Use HTTPS (requires paid plan for ip-api.com). Defaults to false.
|
|
38
|
+
* @param options.fields - Specific fields to request from ip-api.com.
|
|
39
|
+
* @param options.lang - Language code for response data.
|
|
40
|
+
* @returns The response data parsed as type T.
|
|
41
|
+
* @throws {McpError} If the request fails, including network errors, API errors, or parsing issues.
|
|
36
42
|
*/
|
|
37
43
|
async function fetchIpApi(path, options = {}) {
|
|
38
44
|
const methodLogger = logger_util_js_1.Logger.forContext('utils/transport.util.ts', 'fetchIpApi');
|
|
39
|
-
// Get credentials
|
|
45
|
+
// Get credentials (token might be undefined)
|
|
40
46
|
const credentials = getIpApiCredentials();
|
|
41
|
-
//
|
|
42
|
-
// Use https if explicitly requested
|
|
47
|
+
// Determine protocol based on options
|
|
43
48
|
const protocol = options.useHttps ? 'https' : 'http';
|
|
44
49
|
const baseUrl = `${protocol}://ip-api.com/json`;
|
|
45
|
-
//
|
|
50
|
+
// Format path for URL
|
|
46
51
|
const normalizedPath = path ? `/${path}` : '';
|
|
47
52
|
let url = `${baseUrl}${normalizedPath}`;
|
|
48
|
-
//
|
|
53
|
+
// Build query parameters
|
|
49
54
|
const queryParams = new URLSearchParams();
|
|
50
|
-
// Add API token if
|
|
55
|
+
// Add API token if present
|
|
51
56
|
if (credentials.apiToken) {
|
|
52
57
|
queryParams.set('key', credentials.apiToken);
|
|
53
|
-
methodLogger.debug('
|
|
58
|
+
methodLogger.debug('API token added to query parameters.');
|
|
54
59
|
}
|
|
55
|
-
// Add fields
|
|
56
|
-
if (options.fields
|
|
60
|
+
// Add fields parameter
|
|
61
|
+
if (options.fields?.length) {
|
|
57
62
|
queryParams.set('fields', options.fields.join(','));
|
|
58
|
-
methodLogger.debug(`
|
|
63
|
+
methodLogger.debug(`Requesting fields: ${options.fields.join(',')}`);
|
|
59
64
|
}
|
|
60
|
-
// Add language
|
|
65
|
+
// Add language parameter
|
|
61
66
|
if (options.lang) {
|
|
62
67
|
queryParams.set('lang', options.lang);
|
|
63
|
-
methodLogger.debug(`
|
|
68
|
+
methodLogger.debug(`Requesting language: ${options.lang}`);
|
|
64
69
|
}
|
|
65
|
-
// Append query
|
|
70
|
+
// Append query string if needed
|
|
66
71
|
const queryString = queryParams.toString();
|
|
67
72
|
if (queryString) {
|
|
68
73
|
url += `?${queryString}`;
|
|
69
74
|
}
|
|
70
|
-
methodLogger.debug(`
|
|
71
|
-
//
|
|
75
|
+
methodLogger.debug(`Constructed URL: ${url}`);
|
|
76
|
+
// Delegate the actual fetch call to the generic fetchApi
|
|
72
77
|
return fetchApi(url, {
|
|
73
78
|
method: options.method,
|
|
74
79
|
headers: options.headers,
|
|
@@ -76,68 +81,83 @@ async function fetchIpApi(path, options = {}) {
|
|
|
76
81
|
});
|
|
77
82
|
}
|
|
78
83
|
/**
|
|
79
|
-
* Generic function to fetch data from
|
|
80
|
-
* Handles
|
|
84
|
+
* Generic and reusable function to fetch data from any API endpoint.
|
|
85
|
+
* Handles standard HTTP request setup, response checking, basic error handling, and logging.
|
|
86
|
+
*
|
|
81
87
|
* @param url The full URL to fetch data from.
|
|
82
|
-
* @param options Request options
|
|
83
|
-
* @returns
|
|
84
|
-
* @throws {McpError} If the
|
|
88
|
+
* @param options Request options including method, headers, and body.
|
|
89
|
+
* @returns The response data parsed as type T.
|
|
90
|
+
* @throws {McpError} If the request fails, including network errors, non-OK HTTP status, or JSON parsing issues.
|
|
85
91
|
*/
|
|
86
92
|
async function fetchApi(url, options = {}) {
|
|
87
93
|
const methodLogger = logger_util_js_1.Logger.forContext('utils/transport.util.ts', 'fetchApi');
|
|
88
|
-
// Prepare request options
|
|
94
|
+
// Prepare standard request options
|
|
89
95
|
const requestOptions = {
|
|
90
96
|
method: options.method || 'GET',
|
|
91
97
|
headers: {
|
|
98
|
+
// Standard headers, allow overrides via options.headers
|
|
92
99
|
'Content-Type': 'application/json',
|
|
93
100
|
Accept: 'application/json',
|
|
94
|
-
...options.headers,
|
|
101
|
+
...options.headers,
|
|
95
102
|
},
|
|
96
103
|
body: options.body ? JSON.stringify(options.body) : undefined,
|
|
97
104
|
};
|
|
98
|
-
methodLogger.debug(`
|
|
105
|
+
methodLogger.debug(`Executing API call: ${requestOptions.method} ${url}`);
|
|
106
|
+
const startTime = performance.now(); // Track performance
|
|
99
107
|
try {
|
|
100
108
|
const response = await fetch(url, requestOptions);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
statusText: response.statusText,
|
|
106
|
-
headers: {
|
|
107
|
-
// Log simplified headers
|
|
108
|
-
contentType: response.headers.get('content-type'),
|
|
109
|
-
contentLength: response.headers.get('content-length'),
|
|
110
|
-
},
|
|
111
|
-
});
|
|
109
|
+
const endTime = performance.now();
|
|
110
|
+
const duration = (endTime - startTime).toFixed(2);
|
|
111
|
+
methodLogger.debug(`API call completed in ${duration}ms with status: ${response.status} ${response.statusText}`, { url, status: response.status });
|
|
112
|
+
// Check if the response status is OK (2xx)
|
|
112
113
|
if (!response.ok) {
|
|
113
|
-
const errorText = await response.text();
|
|
114
|
-
methodLogger.error(`API error
|
|
115
|
-
//
|
|
116
|
-
|
|
114
|
+
const errorText = await response.text(); // Get error body for context
|
|
115
|
+
methodLogger.error(`API error response (${response.status}):`, errorText);
|
|
116
|
+
// Classify standard HTTP errors
|
|
117
|
+
if (response.status === 401) {
|
|
118
|
+
// Use createAuthInvalidError for consistency, even if ip-api uses keys
|
|
119
|
+
throw (0, error_util_js_1.createAuthInvalidError)('Authentication failed. Check API token if required.');
|
|
120
|
+
}
|
|
121
|
+
else if (response.status === 403) {
|
|
122
|
+
// Use createAuthInvalidError or a more specific permission error if needed
|
|
123
|
+
throw (0, error_util_js_1.createAuthInvalidError)('Permission denied for the requested resource.');
|
|
124
|
+
}
|
|
125
|
+
else if (response.status === 404) {
|
|
126
|
+
throw (0, error_util_js_1.createApiError)('Resource not found at the specified URL.', response.status, errorText);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
// Generic API error for other non-2xx statuses
|
|
130
|
+
throw (0, error_util_js_1.createApiError)(`API request failed with status ${response.status}: ${response.statusText}`, response.status, errorText);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Attempt to parse the response body as JSON
|
|
134
|
+
try {
|
|
135
|
+
const responseData = await response.json();
|
|
136
|
+
methodLogger.debug('Response body successfully parsed as JSON.');
|
|
137
|
+
// methodLogger.debug('Response Data:', responseData); // Uncomment for full response logging
|
|
138
|
+
return responseData;
|
|
139
|
+
}
|
|
140
|
+
catch (parseError) {
|
|
141
|
+
methodLogger.error('Failed to parse API response JSON:', parseError);
|
|
142
|
+
// Throw a specific error for JSON parsing failure
|
|
143
|
+
throw (0, error_util_js_1.createApiError)(`Failed to parse API response JSON: ${parseError instanceof Error ? parseError.message : String(parseError)}`, response.status, // Include original status for context
|
|
144
|
+
parseError);
|
|
117
145
|
}
|
|
118
|
-
// Attempt to parse JSON
|
|
119
|
-
const responseData = await response.json();
|
|
120
|
-
methodLogger.debug(`Response body parsed successfully.`);
|
|
121
|
-
// methodLogger.debug(`Response body:`, responseData); // Optionally log full body
|
|
122
|
-
return responseData;
|
|
123
146
|
}
|
|
124
147
|
catch (error) {
|
|
125
|
-
|
|
126
|
-
|
|
148
|
+
const endTime = performance.now();
|
|
149
|
+
const duration = (endTime - startTime).toFixed(2);
|
|
150
|
+
methodLogger.error(`API call failed after ${duration}ms for ${url}:`, error);
|
|
151
|
+
// Rethrow if it's already an McpError (e.g., from status checks or parsing)
|
|
127
152
|
if (error instanceof error_util_js_1.McpError) {
|
|
128
153
|
throw error;
|
|
129
154
|
}
|
|
130
|
-
// Handle network
|
|
155
|
+
// Handle potential network errors (TypeError in fetch)
|
|
131
156
|
if (error instanceof TypeError) {
|
|
132
157
|
throw (0, error_util_js_1.createApiError)(`Network error during API call: ${error.message}`, undefined, // No specific HTTP status for network errors
|
|
133
158
|
error);
|
|
134
159
|
}
|
|
135
|
-
//
|
|
136
|
-
if (error instanceof SyntaxError) {
|
|
137
|
-
throw (0, error_util_js_1.createApiError)(`Failed to parse API response JSON: ${error.message}`, undefined, // No specific HTTP status for parsing errors
|
|
138
|
-
error);
|
|
139
|
-
}
|
|
140
|
-
// Wrap unknown errors
|
|
160
|
+
// Wrap any other unexpected errors
|
|
141
161
|
throw (0, error_util_js_1.createUnexpectedError)(`Unexpected error during API call: ${error instanceof Error ? error.message : String(error)}`, error);
|
|
142
162
|
}
|
|
143
163
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aashari/boilerplate-mcp-server",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "TypeScript Model Context Protocol (MCP) server boilerplate providing IP lookup tools/resources. Includes CLI support and extensible structure for connecting AI systems (LLMs) to external data sources like ip-api.com. Ideal template for creating new MCP integrations via Node.js.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"_moduleNotes": "Although source code uses ESM syntax, the build output target is CommonJS to align with Node.js compatibility and patterns seen in related MCP servers. tsconfig.json's 'module': 'NodeNext' handles the input syntax, while tsc outputs CJS.",
|
package/package.json.bak
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aashari/boilerplate-mcp-server",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.1.3",
|
|
4
|
+
"description": "TypeScript Model Context Protocol (MCP) server boilerplate providing IP lookup tools/resources. Includes CLI support and extensible structure for connecting AI systems (LLMs) to external data sources like ip-api.com. Ideal template for creating new MCP integrations via Node.js.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"_moduleNotes": "Although source code uses ESM syntax, the build output target is CommonJS to align with Node.js compatibility and patterns seen in related MCP servers. tsconfig.json's 'module': 'NodeNext' handles the input syntax, while tsc outputs CJS.",
|
|
@@ -45,28 +45,15 @@ const versionFiles = [
|
|
|
45
45
|
match.replace(currentVersion, newVersion),
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
|
-
path: path.join(rootDir, 'src', '
|
|
49
|
-
pattern: /const VERSION = ['"]([^'"]*)['"]/,
|
|
50
|
-
replacement: (match, currentVersion) =>
|
|
51
|
-
match.replace(currentVersion, newVersion),
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
path: path.join(rootDir, 'src', 'index.ts'),
|
|
55
|
-
pattern: /const VERSION = ['"]([^'"]*)['"]/,
|
|
48
|
+
path: path.join(rootDir, 'src', 'utils', 'constants.util.ts'),
|
|
49
|
+
pattern: /export const VERSION = ['"]([^'"]*)['"]/,
|
|
56
50
|
replacement: (match, currentVersion) =>
|
|
57
51
|
match.replace(currentVersion, newVersion),
|
|
58
52
|
},
|
|
59
53
|
// Also update the compiled JavaScript files if they exist
|
|
60
54
|
{
|
|
61
|
-
path: path.join(rootDir, 'dist', '
|
|
62
|
-
pattern: /
|
|
63
|
-
replacement: (match, currentVersion) =>
|
|
64
|
-
match.replace(currentVersion, newVersion),
|
|
65
|
-
optional: true, // Mark this file as optional
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
path: path.join(rootDir, 'dist', 'index.js'),
|
|
69
|
-
pattern: /const VERSION = ['"]([^'"]*)['"]/,
|
|
55
|
+
path: path.join(rootDir, 'dist', 'utils', 'constants.util.js'),
|
|
56
|
+
pattern: /exports.VERSION = ['"]([^'"]*)['"]/,
|
|
70
57
|
replacement: (match, currentVersion) =>
|
|
71
58
|
match.replace(currentVersion, newVersion),
|
|
72
59
|
optional: true, // Mark this file as optional
|
package/dist/cli/index.js.bak
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
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.runCli = runCli;
|
|
7
|
-
const commander_1 = require("commander");
|
|
8
|
-
const logger_util_js_1 = require("../utils/logger.util.js");
|
|
9
|
-
const ipaddress_cli_js_1 = __importDefault(require("./ipaddress.cli.js"));
|
|
10
|
-
// Get the version from package.json
|
|
11
|
-
const VERSION = '1.1.1'; // This should match the version in src/index.ts
|
|
12
|
-
const NAME = '@aashari/boilerplate-mcp-server';
|
|
13
|
-
const DESCRIPTION = 'A boilerplate Model Context Protocol (MCP) server implementation using TypeScript';
|
|
14
|
-
async function runCli(args) {
|
|
15
|
-
const methodLogger = logger_util_js_1.Logger.forContext('cli/index.ts', 'runCli');
|
|
16
|
-
methodLogger.debug('Processing CLI arguments', args);
|
|
17
|
-
const program = new commander_1.Command();
|
|
18
|
-
program.name(NAME).description(DESCRIPTION).version(VERSION);
|
|
19
|
-
// Register CLI commands
|
|
20
|
-
ipaddress_cli_js_1.default.register(program);
|
|
21
|
-
// Handle unknown commands
|
|
22
|
-
program.on('command:*', (operands) => {
|
|
23
|
-
methodLogger.error(`Unknown command: ${operands[0]}`);
|
|
24
|
-
console.log('');
|
|
25
|
-
program.help();
|
|
26
|
-
process.exit(1);
|
|
27
|
-
});
|
|
28
|
-
// Parse arguments; default to help if no command provided
|
|
29
|
-
await program.parseAsync(args.length ? args : ['--help'], { from: 'user' });
|
|
30
|
-
methodLogger.debug('CLI command execution completed');
|
|
31
|
-
}
|
package/dist/index.js.bak
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
-
};
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.Logger = exports.config = void 0;
|
|
8
|
-
exports.startServer = startServer;
|
|
9
|
-
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
10
|
-
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
11
|
-
const logger_util_js_1 = require("./utils/logger.util.js");
|
|
12
|
-
Object.defineProperty(exports, "Logger", { enumerable: true, get: function () { return logger_util_js_1.Logger; } });
|
|
13
|
-
const config_util_js_1 = require("./utils/config.util.js");
|
|
14
|
-
Object.defineProperty(exports, "config", { enumerable: true, get: function () { return config_util_js_1.config; } });
|
|
15
|
-
const error_util_js_1 = require("./utils/error.util.js");
|
|
16
|
-
const index_js_1 = require("./cli/index.js");
|
|
17
|
-
const ipaddress_tool_js_1 = __importDefault(require("./tools/ipaddress.tool.js"));
|
|
18
|
-
const ipaddress_resource_js_1 = __importDefault(require("./resources/ipaddress.resource.js"));
|
|
19
|
-
// Create file-level logger
|
|
20
|
-
const indexLogger = logger_util_js_1.Logger.forContext('index.ts');
|
|
21
|
-
// Define version constant for easier management and consistent versioning
|
|
22
|
-
const VERSION = '1.1.1';
|
|
23
|
-
let serverInstance = null;
|
|
24
|
-
let transportInstance = null;
|
|
25
|
-
async function startServer(mode = 'stdio') {
|
|
26
|
-
const methodLogger = logger_util_js_1.Logger.forContext('index.ts', 'startServer');
|
|
27
|
-
// Load configuration
|
|
28
|
-
config_util_js_1.config.load();
|
|
29
|
-
// Enable debug logging if DEBUG is set to true
|
|
30
|
-
if (config_util_js_1.config.getBoolean('DEBUG')) {
|
|
31
|
-
methodLogger.debug('Debug mode enabled');
|
|
32
|
-
}
|
|
33
|
-
// Log the DEBUG value to verify configuration loading
|
|
34
|
-
methodLogger.info(`DEBUG value: ${process.env.DEBUG}`);
|
|
35
|
-
methodLogger.info(`IPAPI_API_TOKEN value exists: ${Boolean(process.env.IPAPI_API_TOKEN)}`);
|
|
36
|
-
methodLogger.info(`Config DEBUG value: ${config_util_js_1.config.get('DEBUG')}`);
|
|
37
|
-
serverInstance = new mcp_js_1.McpServer({
|
|
38
|
-
name: '@aashari/boilerplate-mcp-server',
|
|
39
|
-
version: VERSION,
|
|
40
|
-
});
|
|
41
|
-
if (mode === 'stdio') {
|
|
42
|
-
transportInstance = new stdio_js_1.StdioServerTransport();
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
throw (0, error_util_js_1.createUnexpectedError)('SSE mode is not supported yet');
|
|
46
|
-
}
|
|
47
|
-
methodLogger.info(`Starting server with ${mode.toUpperCase()} transport...`);
|
|
48
|
-
// register tools
|
|
49
|
-
ipaddress_tool_js_1.default.register(serverInstance);
|
|
50
|
-
methodLogger.debug('Registered IP address tools');
|
|
51
|
-
// register resources
|
|
52
|
-
ipaddress_resource_js_1.default.register(serverInstance);
|
|
53
|
-
methodLogger.debug('Registered IP lookup resources');
|
|
54
|
-
return serverInstance.connect(transportInstance).catch((err) => {
|
|
55
|
-
methodLogger.error(`Failed to start server`, err);
|
|
56
|
-
process.exit(1);
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
// Main entry point - this will run when executed directly
|
|
60
|
-
async function main() {
|
|
61
|
-
const methodLogger = logger_util_js_1.Logger.forContext('index.ts', 'main');
|
|
62
|
-
// Load configuration
|
|
63
|
-
config_util_js_1.config.load();
|
|
64
|
-
// Log the DEBUG value to verify configuration loading
|
|
65
|
-
methodLogger.info(`DEBUG value: ${process.env.DEBUG}`);
|
|
66
|
-
methodLogger.info(`IPAPI_API_TOKEN value exists: ${Boolean(process.env.IPAPI_API_TOKEN)}`);
|
|
67
|
-
methodLogger.info(`Config DEBUG value: ${config_util_js_1.config.get('DEBUG')}`);
|
|
68
|
-
// Check if arguments are provided (CLI mode)
|
|
69
|
-
if (process.argv.length > 2) {
|
|
70
|
-
// CLI mode: Pass arguments to CLI runner
|
|
71
|
-
methodLogger.info('Starting in CLI mode');
|
|
72
|
-
await (0, index_js_1.runCli)(process.argv.slice(2));
|
|
73
|
-
methodLogger.info('CLI execution completed');
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
// MCP Server mode: Start server with default STDIO
|
|
77
|
-
methodLogger.info('Starting in server mode');
|
|
78
|
-
await startServer();
|
|
79
|
-
methodLogger.info('Server is now running');
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
// If this file is being executed directly (not imported), run the main function
|
|
83
|
-
if (require.main === module) {
|
|
84
|
-
main().catch((err) => {
|
|
85
|
-
indexLogger.error('Unhandled error in main process', err);
|
|
86
|
-
process.exit(1);
|
|
87
|
-
});
|
|
88
|
-
}
|