π¨βπ» Development Guide
Contributing to the SFCC Development MCP Server project.
π Getting Started
Prerequisites
- Node.js 18 or higher
- npm 8 or higher
- Git for version control
- TypeScript knowledge recommended
Local Development Setup
# Clone the repository
git clone https://github.com/taurgis/sfcc-dev-mcp.git
cd sfcc-dev-mcp
# Install dependencies
npm install
# Build TypeScript
npm run build
# Run tests
npm test
# Start in development mode
npm run dev -- --dw-json /path/to/your/dw.json
ποΈ Project Architecture
Directory Structure
sfcc-dev-mcp/
βββ src/ # TypeScript source code
β βββ main.ts # CLI entry point
β βββ index.ts # Package exports
β βββ core/ # Core MCP server functionality
β β βββ server.ts # Main MCP server implementation
β β βββ tool-definitions.ts # MCP tool schema definitions
β βββ clients/ # API clients for different services
β β βββ base/ # Base client classes
β β β βββ http-client.ts # Base HTTP client with authentication
β β β βββ oauth-token.ts # OAuth token management
β β β βββ ocapi-auth-client.ts # OCAPI authentication client
β β βββ ocapi/ # OCAPI-specific clients
β β β βββ site-preferences-client.ts # Site preferences client
β β β βββ system-objects-client.ts # System objects client
β β βββ best-practices-client.ts # Best practices guides client
β β βββ cartridge-generation-client.ts # Cartridge generation client
β β βββ docs-client.ts # SFCC documentation client
β β βββ log-client.ts # Log analysis client
β β βββ ocapi-client.ts # Main OCAPI coordinator
β β βββ sfra-client.ts # SFRA documentation client
β βββ config/ # Configuration management
β β βββ config.ts # Configuration loading
β β βββ configuration-factory.ts # Configuration factory
β β βββ constants.ts # Application constants
β β βββ dw-json-loader.ts # Secure dw.json loading
β βββ utils/ # Utility functions
β β βββ cache.ts # Caching utilities
β β βββ logger.ts # Logging utilities
β β βββ path-resolver.ts # Path resolution utilities
β β βββ query-builder.ts # Query building utilities
β β βββ utils.ts # Common utilities
β β βββ validator.ts # Input validation
β βββ types/ # TypeScript type definitions
β βββ types.ts # Comprehensive type definitions
βββ docs/ # SFCC documentation files
βββ docs-site/ # GitHub Pages documentation
βββ tests/ # Test suite
βββ scripts/ # Build and utility scripts
βββ .github/ # GitHub workflows and templates
Key Components
MCP Server Core (src/core/
)
- server.ts: Main MCP protocol implementation
- tool-definitions.ts: Tool schema definitions and validation
Client Architecture (src/clients/
)
- Base Classes: Shared HTTP client functionality and authentication
- Specialized Clients: Domain-specific API integrations
- Service Coordination: Orchestrates multiple API calls
Configuration System (src/config/
)
- Flexible Loading: Supports dw.json, environment variables, CLI args
- Mode Detection: Automatically determines operating mode
- Validation: Comprehensive configuration validation
π§ Development Workflow
Adding New Tools
- Define Tool Schema in
src/core/tool-definitions.ts
:export const toolDefinitions: ToolDefinition[] = [ // ... existing tools { name: "my_new_tool", description: "Description of what the tool does", inputSchema: { type: "object", properties: { parameter1: { type: "string", description: "Description of parameter" } }, required: ["parameter1"] } } ];
- Implement Tool Handler in appropriate client:
// In src/clients/my-client.ts export class MyClient extends BaseHttpClient { async handleMyNewTool(params: MyNewToolParams): Promise<ToolResponse> { try { // Implementation logic const result = await this.performOperation(params); return { content: [ { type: "text", text: this.formatResult(result) } ] }; } catch (error) { return this.handleError('my_new_tool', error); } } }
- Register Handler in
src/core/server.ts
:// Add to the appropriate handler method case 'my_new_tool': return await this.myClient.handleMyNewTool(params);
- Add Tests in
tests/
:describe('MyClient', () => { describe('handleMyNewTool', () => { it('should handle valid input', async () => { // Test implementation }); it('should handle errors gracefully', async () => { // Error handling tests }); }); });
Testing Strategy
Unit Tests
# Run all tests
npm test
# Run specific test file
npm test base-http-client.test.ts
# Run with coverage
npm run test:coverage
# Watch mode for development
npm run test:watch
Available Test Scripts
# Core testing scripts from package.json
npm test # Run all tests with Jest
npm run test:watch # Run tests in watch mode
npm run test:coverage # Run tests with coverage report
# Linting and code quality
npm run lint # Check code style
npm run lint:fix # Auto-fix linting issues
npm run lint:check # Check with zero warnings
Manual Testing
# Test with real SFCC instance (create your own test-dw.json)
npm run dev -- --dw-json ./test-dw.json --debug true
# Test documentation-only mode
npm run dev -- --debug true
π Documentation Updates
Updating SFCC Documentation
- Add/Update Markdown Files in
docs/
:# Add new class documentation echo "# NewClass\n\nDescription..." > docs/dw_catalog/NewClass.md
- Run Documentation Conversion:
# Convert and process documentation (requires axios and cheerio) npm run convert-docs # Test with limited conversion npm run convert-docs:test # Limited conversion (5 files) npm run convert-docs:limit
- Test Documentation Tools:
# Test documentation access with your changes npm run dev -- --debug true # Then use MCP client to test get_sfcc_class_info with "NewClass"
Updating GitHub Pages
The GitHub Pages site is automatically deployed when changes are pushed to docs-site/
:
- Edit Documentation Pages in
docs-site/
- Test Locally with Jekyll (requires Ruby and Jekyll setup):
cd docs-site # Install Jekyll if not already installed gem install jekyll bundler bundle install bundle exec jekyll serve # Visit http://localhost:4000/sfcc-dev-mcp/
- Commit and Push - GitHub Actions will deploy automatically via
.github/workflows/deploy-pages.yml
π― Coding Standards
TypeScript Guidelines
// Use explicit types
interface ToolParams {
readonly query: string;
readonly limit?: number;
}
// Use proper error handling
async function riskyOperation(): Promise<Result> {
try {
return await performOperation();
} catch (error) {
this.logger.error('Operation failed', { error: error.message });
throw new OperationError('Failed to perform operation', error);
}
}
// Use meaningful names
const searchProductsByCategory = (categoryId: string) => {
// Implementation
};
Code Organization
- Single Responsibility: Each class/function has one clear purpose
- Dependency Injection: Use constructor injection for dependencies
- Error Boundaries: Proper error handling at service boundaries
- Logging: Comprehensive logging for debugging and monitoring
Git Workflow
# Create feature branch from develop
git checkout develop
git pull origin develop
git checkout -b feature/new-tool-name
# Make atomic commits
git add src/clients/my-client.ts
git commit -m "feat: add my new tool implementation"
git add tests/my-client.test.ts
git commit -m "test: add unit tests for my new tool"
git add docs-site/tools.md
git commit -m "docs: update tools documentation"
# Push and create PR to develop branch
git push origin feature/new-tool-name
Commit Message Convention:
feat:
- New featuresfix:
- Bug fixesdocs:
- Documentation updatestest:
- Test additions/modificationsrefactor:
- Code refactoringchore:
- Build process or auxiliary tool changes
π§ͺ Testing Best Practices
Test Structure
describe('FeatureName', () => {
let client: MyClient;
let mockHttpClient: jest.Mocked<HttpClient>;
beforeEach(() => {
mockHttpClient = createMockHttpClient();
client = new MyClient(mockHttpClient, mockLogger);
});
describe('methodName', () => {
it('should handle success case', async () => {
// Arrange
const input = { query: 'test' };
const mockResponse = { data: 'mock response' };
mockHttpClient.get.mockResolvedValue(mockResponse);
// Act
const result = await client.methodName(input);
// Assert
expect(result).toBeDefined();
expect(mockHttpClient.get).toHaveBeenCalledWith('/expected/path');
});
it('should handle error case', async () => {
// Arrange
const input = { query: 'test' };
mockHttpClient.get.mockRejectedValue(new Error('Network error'));
// Act & Assert
await expect(client.methodName(input)).rejects.toThrow('Network error');
});
});
});
Mock Strategy
// Mock external dependencies using Jest
const mockLogger = {
info: jest.fn(),
error: jest.fn(),
debug: jest.fn(),
warn: jest.fn()
};
// Use factories for complex mocks
const createMockSFCCResponse = (overrides = {}) => ({
statusCode: 200,
headers: { 'content-type': 'application/json' },
data: 'Mock response data',
...overrides
});
Testing Files Available
The project has comprehensive test coverage in the tests/
directory:
base-http-client.test.ts
- Base HTTP client testingcache.test.ts
- Caching mechanism testsconfig.test.ts
- Configuration loading testslog-client.test.ts
- Log analysis client testsoauth-token.test.ts
- OAuth token management testssystem-objects-client.test.ts
- System objects client tests- And moreβ¦
π Release Process
Version Management
# Update version
npm version patch # 1.0.0 β 1.0.1
npm version minor # 1.0.0 β 1.1.0
npm version major # 1.0.0 β 2.0.0
# Push tags
git push origin main --tags
Release Checklist
- Update Documentation
- README.md tool counts and features
ai-instructions/github-copilot/copilot-instructions.md
architecture updates.github/copilot-instructions.md
if MCP server architecture changed- GitHub Pages documentation in
docs-site/
- CHANGELOG.md entry (if present)
- Testing
- All unit tests pass (
npm test
) - Linting passes (
npm run lint:check
) - Manual testing with real SFCC instance
- Documentation-only mode validation
- Build succeeds (
npm run build
)
- All unit tests pass (
- Build & Package
- TypeScript compilation successful
- Package size reasonable
- Dependencies audit clean (
npm audit
)
- Release
- GitHub release with changelog
- npm publish (automated via
.github/workflows/publish.yml
) - Documentation deployment (automated)
π€ Contributing Guidelines
Before Contributing
- Check Existing Issues: Search for existing issues or discussions
- Discuss Large Changes: Open an issue for significant modifications
- Follow Conventions: Adhere to established coding and commit patterns
Pull Request Process
- Fork & Branch: Create feature branch from
develop
- Implement Changes: Follow coding standards and testing requirements
- Update Documentation: Ensure documentation reflects changes
- Test Thoroughly: All tests must pass (
npm test
,npm run lint:check
) - Submit PR: Provide clear description and link to related issues
Code Review
- GitHub Actions: CI pipeline must pass (see
.github/workflows/ci.yml
) - Code Quality: ESLint and TypeScript checks must pass
- Test Coverage: Maintain or improve test coverage
- Documentation: Ensure user-facing changes are documented
π Performance Considerations
Optimization Guidelines
- Caching Strategy: Implement intelligent caching for API responses
- Rate Limiting: Respect SFCC API limits and implement backoff
- Memory Management: Monitor memory usage, especially for large datasets
- Asynchronous Operations: Use proper async/await patterns
Monitoring
// Performance monitoring example
const startTime = Date.now();
try {
const result = await performOperation();
this.metrics.recordSuccess('operation_name', Date.now() - startTime);
return result;
} catch (error) {
this.metrics.recordError('operation_name', Date.now() - startTime);
throw error;
}
π Security Considerations
Credential Handling
- No Hardcoding: Never commit credentials to repository
- Secure Storage: Use appropriate credential storage mechanisms
- Minimal Permissions: Request only necessary permissions
- Rotation Support: Design for credential rotation
Input Validation
// Validate all inputs using proper TypeScript types
import { ToolResponse, ValidationError } from '../types/types.js';
interface ToolParams {
readonly query: string;
readonly limit?: number;
}
function validateToolInput(input: unknown): ToolParams {
if (!input || typeof input !== 'object') {
throw new ValidationError('Input must be an object');
}
const { query, limit } = input as any;
if (!query || typeof query !== 'string') {
throw new ValidationError('Query is required and must be a string');
}
if (query.length > 1000) {
throw new ValidationError('Query must be 1000 characters or less');
}
if (limit !== undefined && (typeof limit !== 'number' || limit < 1 || limit > 100)) {
throw new ValidationError('Limit must be a number between 1 and 100');
}
return { query, limit };
}
Next Steps
- π Contributing Guidelines - Detailed contribution process
- ποΈ Issues & Features - Report bugs or request features
- π¬ Discussions - Community discussions and Q&A
- οΏ½ GitHub Actions - View CI/CD pipeline status