sfcc-dev-mcp
Version:
MCP server for Salesforce B2C Commerce Cloud development assistance including logs, debugging, and development tools
168 lines • 7.2 kB
JavaScript
/**
* MCP Server for SFCC Development
*
* This module implements the Model Context Protocol (MCP) server for accessing
* Salesforce B2C Commerce Cloud development features. It provides a standardized interface
* for AI assistants to interact with SFCC development tools and data.
*/
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
import { Logger } from '../utils/logger.js';
import { ConfigurationFactory } from '../config/configuration-factory.js';
import { SFCC_DOCUMENTATION_TOOLS, BEST_PRACTICES_TOOLS, SFRA_DOCUMENTATION_TOOLS, LOG_TOOLS, JOB_LOG_TOOLS, SYSTEM_OBJECT_TOOLS, CARTRIDGE_GENERATION_TOOLS, CODE_VERSION_TOOLS, } from './tool-definitions.js';
import { LogToolHandler } from './handlers/log-handler.js';
import { JobLogToolHandler } from './handlers/job-log-handler.js';
import { DocsToolHandler } from './handlers/docs-handler.js';
import { BestPracticesToolHandler } from './handlers/best-practices-handler.js';
import { SFRAToolHandler } from './handlers/sfra-handler.js';
import { SystemObjectToolHandler } from './handlers/system-object-handler.js';
import { CodeVersionToolHandler } from './handlers/code-version-handler.js';
import { CartridgeToolHandler } from './handlers/cartridge-handler.js';
/**
* MCP Server implementation for SFCC development assistance
*
* This class sets up the MCP server, defines available tools, and handles
* requests from MCP clients (like AI assistants) to interact with SFCC development features.
*/
export class SFCCDevServer {
server;
logger;
config;
capabilities;
handlers = [];
/**
* Initialize the SFCC Development MCP Server
*
* @param config - SFCC configuration for connecting to the logging system
*/
constructor(config) {
this.logger = Logger.getChildLogger('Server');
this.config = config;
this.logMethodEntry('constructor', { hostname: config.hostname });
this.capabilities = ConfigurationFactory.getCapabilities(config);
this.initializeServer();
this.registerHandlers();
this.setupToolHandlers();
this.logMethodExit('constructor');
}
initializeServer() {
this.server = new Server({
name: 'SFCC Development MCP Server',
version: '1.0.14', // synced with package.json
}, {
capabilities: {
tools: {},
},
});
}
logMethodEntry(methodName, params) {
this.logger.methodEntry(methodName, params);
}
logMethodExit(methodName, result) {
this.logger.methodExit(methodName, result);
}
// Register modular handlers (each encapsulates its own responsibility)
registerHandlers() {
const context = {
logger: this.logger,
config: this.config,
capabilities: this.capabilities,
};
this.handlers = [
new LogToolHandler(context, 'Log'),
new JobLogToolHandler(context, 'JobLog'),
new DocsToolHandler(context, 'Docs'),
new BestPracticesToolHandler(context, 'BestPractices'),
new SFRAToolHandler(context, 'SFRA'),
new SystemObjectToolHandler(context, 'SystemObjects'),
new CodeVersionToolHandler(context, 'CodeVersions'),
new CartridgeToolHandler(context, 'Cartridge'),
];
}
/**
* Set up MCP tool handlers for SFCC operations
*/
setupToolHandlers() {
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
const tools = [];
// Always available tools
tools.push(...SFCC_DOCUMENTATION_TOOLS);
tools.push(...BEST_PRACTICES_TOOLS);
tools.push(...SFRA_DOCUMENTATION_TOOLS);
tools.push(...CARTRIDGE_GENERATION_TOOLS);
// Conditional tools based on available capabilities
if (this.capabilities.canAccessLogs) {
tools.push(...LOG_TOOLS);
tools.push(...JOB_LOG_TOOLS);
}
if (this.capabilities.canAccessOCAPI) {
tools.push(...SYSTEM_OBJECT_TOOLS);
tools.push(...CODE_VERSION_TOOLS);
}
return { tools };
});
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
const startTime = Date.now();
this.logger.methodEntry(`handleToolRequest:${name}`, args);
try {
const handler = this.handlers.find((h) => h.canHandle(name));
if (!handler) {
this.logger.error(`Unknown tool requested: ${name}`);
throw new Error(`Unknown tool: ${name}`);
}
const result = await handler.handle(name, args ?? {}, startTime);
// Log the full response in debug mode
this.logger.debug(`Full response for ${name}:`, {
contentType: result.content?.[0]?.type,
contentLength: result.content?.[0]?.text?.length ?? 0,
responsePreview: result.content?.[0]?.text?.substring(0, 200) + (result.content?.[0]?.text?.length > 200 ? '...' : ''),
fullResponse: result.content?.[0]?.text,
});
return result;
}
catch (error) {
this.logger.error(`Error handling tool "${name}":`, error);
this.logger.timing(`${name}_error`, startTime);
const errorResult = {
content: [
{
type: 'text',
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
},
],
isError: true,
};
// Log error response in debug mode
this.logger.debug(`Error response for ${name}:`, errorResult);
return errorResult;
}
finally {
this.logger.methodExit(`handleToolRequest:${name}`);
}
});
}
/**
* Start the MCP server
*/
async run() {
const transport = new StdioServerTransport();
// Set up graceful shutdown
process.on('SIGINT', () => this.shutdown());
process.on('SIGTERM', () => this.shutdown());
await this.server.connect(transport);
this.logger.log('SFCC Development MCP server running on stdio');
}
/**
* Gracefully shutdown the server and dispose of resources
*/
async shutdown() {
this.logger.log('Shutting down SFCC Development MCP server...');
// Dispose of all handlers
await Promise.all(this.handlers.map(handler => handler.dispose()));
this.logger.log('SFCC Development MCP server shutdown complete');
process.exit(0);
}
}
//# sourceMappingURL=server.js.map