sfcc-dev-mcp
Version:
MCP server for Salesforce B2C Commerce Cloud development assistance including logs, debugging, and development tools
172 lines • 5.31 kB
JavaScript
import { Logger } from '../../utils/logger.js';
export class HandlerError extends Error {
toolName;
code;
details;
constructor(message, toolName, code = 'HANDLER_ERROR', details) {
super(message);
this.toolName = toolName;
this.code = code;
this.details = details;
this.name = 'HandlerError';
}
}
export class BaseToolHandler {
context;
logger;
_isInitialized = false;
constructor(context, subLoggerName) {
this.context = context;
this.logger = Logger.getChildLogger(`Handler:${subLoggerName}`);
}
/**
* Check if this handler can handle the given tool
*/
canHandle(toolName) {
return this.getToolNameSet().has(toolName);
}
/**
* Config-driven tool execution
* Handles validation, defaults, execution, and logging uniformly
*/
async handle(toolName, args, startTime) {
if (!this.canHandle(toolName)) {
throw new Error(`Unsupported tool: ${toolName}`);
}
const toolConfig = this.getToolConfig();
const spec = toolConfig[toolName];
if (!spec) {
throw new Error(`No configuration found for tool: ${toolName}`);
}
return this.executeWithLogging(toolName, startTime, () => this.dispatchTool(spec, args), spec.logMessage(this.applyDefaults(spec, args)));
}
/**
* Generic tool dispatch using configuration
* Handles validation, defaults, and execution
*/
async dispatchTool(spec, args) {
const context = await this.createExecutionContext();
const processedArgs = this.createValidatedArgs(spec, args, 'tool');
return spec.exec(processedArgs, context);
}
/**
* Apply default values to arguments
*/
applyDefaults(spec, args) {
if (!spec.defaults) {
return args;
}
const defaults = spec.defaults(args);
return { ...args, ...defaults };
}
/**
* Create validated arguments with defaults applied
*/
createValidatedArgs(spec, args, toolName) {
// Apply defaults first
const processedArgs = this.applyDefaults(spec, args);
// Validate if validator exists
if (spec.validate) {
spec.validate(processedArgs, toolName);
}
return processedArgs;
}
/**
* Initialize the handler (lazy initialization)
*/
async initialize() {
if (this._isInitialized) {
return;
}
await this.onInitialize();
this._isInitialized = true;
}
/**
* Override this method for custom initialization logic
*/
async onInitialize() {
// Default: no-op
}
/**
* Clean up resources when handler is destroyed
*/
async dispose() {
await this.onDispose();
this._isInitialized = false;
}
/**
* Override this method for custom cleanup logic
*/
async onDispose() {
// Default: no-op
}
/**
* Validate required arguments
*/
validateArgs(args, required, toolName) {
for (const field of required) {
if (!args?.[field]) {
throw new HandlerError(`${field} is required`, toolName, 'MISSING_ARGUMENT', { required, provided: Object.keys(args || {}) });
}
}
}
/**
* Create a standardized response
*/
createResponse(data, stringify = true) {
return {
content: [
{ type: 'text', text: stringify ? JSON.stringify(data, null, 2) : data },
],
isError: false,
};
}
/**
* Create an error response
*/
createErrorResponse(error, toolName) {
this.logger.error(`Error in ${toolName}:`, error);
return {
content: [
{
type: 'text',
text: error instanceof HandlerError
? `Error: ${error.message}`
: `Error: ${error.message}`,
},
],
isError: true,
};
}
/**
* Execute a tool operation with standardized logging and error handling
*/
async executeWithLogging(toolName, startTime, operation, logMessage) {
try {
await this.initialize();
if (logMessage) {
this.logger.debug(logMessage);
}
const result = await operation();
this.logger.timing(toolName, startTime);
// Log result metadata for debugging
this.logger.debug(`${toolName} completed successfully`, {
resultType: typeof result,
resultLength: Array.isArray(result) ? result.length : undefined,
hasData: result != null,
});
return this.createResponse(result);
}
catch (error) {
this.logger.timing(`${toolName}_error`, startTime);
return this.createErrorResponse(error, toolName);
}
}
/**
* @deprecated Use executeWithLogging instead
*/
async wrap(toolName, startTime, fn, logMessage) {
return this.executeWithLogging(toolName, startTime, fn, logMessage);
}
}
//# sourceMappingURL=base-handler.js.map