Welcome to the MCP WordPress Server project! This document provides comprehensive guidelines for contributing to this WordPress Model Context Protocol (MCP) server implementation.
- Getting Started
- Development Workflow
- Code Quality Standards
- Testing Requirements
- Coverage Standards
- TypeScript Guidelines
- Logging Guidelines
- Security Guidelines
- Performance Guidelines
- Documentation Standards
- Pull Request Process
- Node.js: Version 18 or higher
- npm: Version 8 or higher
- TypeScript: Version 5.0 or higher
- WordPress: Access to a WordPress site for testing
- Git: Version 2.30 or higher
# Clone the repository
git clone https://github.com/docdyhr/mcp-wordpress.git
cd mcp-wordpress
# Install dependencies
npm ci
# Set up configuration
cp .env.example .env
# Edit .env with your WordPress credentials
# Run initial health check
npm run health# Start development mode
npm run dev
# Run tests in watch mode
npm run test:watch
# Enable debug logging
DEBUG=true npm run devWe follow a feature branch workflow with strict branch protection on main:
# Create feature branch from main
git checkout main
git pull origin main
git checkout -b feature/your-feature-name
# Work on your feature
# Make atomic commits with conventional commit messages
# Push and create PR
git push -u origin feature/your-feature-name
gh pr create --title "feat: description" --body "Detailed description"We use Conventional Commits for automated versioning and changelog generation:
# Format: type(scope): description
feat(tools): add new WordPress custom fields tool
fix(auth): resolve app password authentication issue
chore(deps): update dependencies to latest versions
docs(readme): improve installation instructions
test(posts): add comprehensive post tool unit testsTypes:
feat: New featuresfix: Bug fixeschore: Maintenance tasksdocs: Documentation changestest: Adding or updating testsrefactor: Code refactoringperf: Performance improvementssecurity: Security improvements
We maintain strict ESLint compliance:
# Check for linting errors
npm run lint
# Auto-fix linting issues
npm run lint:fix
# Ensure zero violations before committingKey Rules:
- No unused variables (use
_prefix for intentionally unused) - Strict TypeScript types (no
anytypes allowed) - Consistent import/export patterns
- Proper error handling requirements
We use Prettier for consistent code formatting:
# Format all code
npm run format
# Automatic formatting on commit via husky hooksAll contributions must include tests based on the following hierarchy:
- Coverage Target: β₯50% for new components
- Focus: Individual functions, classes, and modules
- Location:
tests/unit/
// Example unit test structure
describe("WordPressClient", () => {
describe("authentication", () => {
it("should authenticate with app password", async () => {
// Test implementation
});
it("should handle authentication failures gracefully", async () => {
// Test implementation
});
});
});- Focus: Component interactions and API integrations
- Location:
tests/integration/ - WordPress Integration: Use mock/live dual-mode testing
- Coverage: 40/40 tests must pass
- Focus: Input validation, SQL injection prevention, XSS protection
- Location:
tests/security/
- Coverage: 8/8 tests must pass
- Focus: Response times, memory usage, cache efficiency
- Location:
tests/performance/
# Run all tests
npm test
# Run specific test categories
npm run test:unit
npm run test:integration
npm run test:security
npm run test:performance
# Run tests with coverage
npm run test:coverage
# Run tests in watch mode during development
npm run test:watch// Use descriptive test names
it("should create post with featured media and return management links", async () => {
// Arrange
const postData = {
title: "Test Post",
content: "<p>Test content</p>",
featured_media: 42,
};
// Act
const result = await postTool.createPost(postData);
// Assert
expect(result.id).toBeDefined();
expect(result.featured_media).toBe(42);
expect(result._links.self).toBeDefined();
});
// Test error conditions
it("should throw validation error for missing required title", async () => {
await expect(postTool.createPost({})).rejects.toThrow(/title.*required/i);
});
// Test edge cases
it("should handle empty post content gracefully", async () => {
const result = await postTool.createPost({ title: "Test", content: "" });
expect(result.content.rendered).toBe("");
});We enforce incremental coverage improvement to maintain code quality:
{
"lines": 30,
"branches": 25,
"functions": 28,
"statements": 29
}- No regression allowed: Coverage cannot decrease by >1% in any metric
- New code target: β₯50% coverage for new components
- Improvement goal: Gradual increase toward 80% total coverage
High-Priority Components (β₯50% coverage required):
src/client/- WordPress API clientsrc/tools/- MCP tool implementationssrc/security/- Security utilitiessrc/utils/- Core utilities
Moderate-Priority Components (β₯30% coverage):
src/config/- Configuration managementsrc/cache/- Caching systemsrc/server/- MCP server implementation
# Check coverage
npm run test:coverage
# Validate coverage thresholds
npm run coverage:check
# Generate detailed coverage report
npm run coverage:reportWe enforce strict TypeScript compliance with zero tolerance for type violations:
// β NEVER use explicit 'any'
function badFunction(data: any): any {
return data.whatever;
}
// β NEVER bypass type checking
const result = (data as any).someProperty;
// β NEVER ignore TypeScript errors
// @ts-ignore
const unsafeOperation = riskyFunction();// β
Define proper interfaces
interface WordPressPost {
id: number;
title: {
rendered: string;
};
content: {
rendered: string;
};
status: "publish" | "draft" | "pending" | "private";
featured_media?: number | undefined;
}
// β
Use proper type guards
function isWordPressPost(obj: unknown): obj is WordPressPost {
return typeof obj === "object" && obj !== null && "id" in obj && "title" in obj;
}
// β
Handle optional properties correctly
interface ToolParams {
title: string;
content?: string | undefined; // Explicit undefined for exactOptionalPropertyTypes
}- All exports: Must have explicit type definitions
- Function parameters: Must be properly typed
- Return types: Must be explicitly declared
- Error types: Must extend proper error classes
// β
Proper function typing
export async function createPost(params: CreatePostParams): Promise<WordPressPost> {
// Implementation
}
// β
Proper error typing
export class WordPressApiError extends Error {
constructor(
message: string,
public statusCode: number,
public response?: unknown,
) {
super(message);
this.name = "WordPressApiError";
}
}We use centralized structured logging for all components:
// β
ALWAYS use LoggerFactory
import { LoggerFactory } from "../utils/logger.js";
// Create component-specific loggers
const apiLogger = LoggerFactory.api("site1");
const toolLogger = LoggerFactory.tool("wp_create_post", "site2");
const cacheLogger = LoggerFactory.cache();
const securityLogger = LoggerFactory.security();// β NEVER use console.log directly
console.log("Debug info:", data);
// β NEVER use generic console methods
console.error("Error occurred");
console.warn("Warning message");
// β NEVER log sensitive data without sanitization
logger.info("User credentials", { username, password });// β
Use appropriate log levels
logger.debug("Processing request", { endpoint: "/wp/v2/posts" });
logger.info("Post created successfully", { postId: 123, title: "Post Title" });
logger.warn("Cache miss detected", { key: "posts:123", reason: "expired" });
logger.error("API request failed", { statusCode: 401, endpoint: "/wp/v2/posts" });
// β
Use timing for performance monitoring
const result = await logger.time("Database query", async () => {
return await database.query(sql);
});
// β
Sensitive data is automatically sanitized
logger.info("Authentication attempt", {
username: "testuser",
password: "secret123", // Automatically becomes [REDACTED:9chars]
token: "abc123def", // Automatically becomes [REDACTED:9chars]
});
// β
Use contextual logging
const requestLogger = logger.child({ requestId: uuid(), userId: "user123" });
requestLogger.info("Processing user request");- trace: Detailed debugging (disabled in production)
- debug: Development debugging (disabled in production)
- info: General operational information
- warn: Warning conditions that don't stop operation
- error: Error conditions that may stop operation
- fatal: Critical errors that stop the application
All code must follow defensive security practices:
// β
Validate all inputs
function validatePostData(data: unknown): CreatePostParams {
const schema = z.object({
title: z.string().min(1).max(200),
content: z.string().optional(),
status: z.enum(["publish", "draft", "pending", "private"]).optional(),
});
return schema.parse(data);
}
// β
Sanitize HTML content
import { sanitizeHtml } from "../security/sanitization.js";
const safeContent = sanitizeHtml(userContent, {
allowedTags: ["p", "br", "strong", "em"],
allowedAttributes: {},
});// β
Secure credential handling
class AuthenticationManager {
private credentials: Map<string, SiteCredentials> = new Map();
setCredentials(siteId: string, credentials: SiteCredentials): void {
// Credentials are never logged
this.logger.info("Credentials updated", { siteId });
this.credentials.set(siteId, credentials);
}
}
// β
Secure API communication
const response = await fetch(url, {
headers: {
Authorization: `Bearer ${token}`,
"User-Agent": "MCP-WordPress/2.4.0",
"Content-Type": "application/json",
},
// Always use HTTPS in production
});// β
Use parameterized queries
const query = `
SELECT * FROM posts
WHERE status = ? AND author_id = ?
`;
const results = await db.query(query, [status, authorId]);
// β NEVER use string concatenation
const badQuery = `SELECT * FROM posts WHERE status = '${status}'`;All security-related changes must include:
- Input validation tests
- XSS prevention tests
- SQL injection prevention tests
- Authentication bypass tests
- Authorization tests
Code must meet performance standards:
- API calls: <500ms average
- Cache operations: <50ms
- Tool execution: <2000ms
- Database queries: <200ms
- Memory leaks: Zero tolerance
- Memory usage: <80% of available memory
- Cache size: Configurable limits with LRU eviction
// β
Include performance tests for critical paths
describe("Performance Tests", () => {
it("should create post within 2 seconds", async () => {
const start = Date.now();
await postTool.createPost(testData);
const duration = Date.now() - start;
expect(duration).toBeLessThan(2000);
});
it("should handle 100 concurrent requests", async () => {
const promises = Array(100)
.fill(null)
.map(() => postTool.listPosts({ per_page: 10 }));
await expect(Promise.all(promises)).resolves.toBeDefined();
});
});- JSDoc comments: Required for all public APIs
- Type annotations: Required for all exports
- Usage examples: Required for tools and utilities
/**
* Creates a new WordPress post with comprehensive validation
*
* @param params - Post creation parameters
* @param params.title - Post title (required)
* @param params.content - Post content in HTML format
* @param params.status - Publishing status
* @returns Promise resolving to created post with metadata
*
* @example
* ```typescript
* const post = await createPost({
* title: 'My New Post',
* content: '<p>Hello World!</p>',
* status: 'publish'
* });
* console.log(`Created post with ID: ${post.id}`);
* ```
*/
export async function createPost(params: CreatePostParams): Promise<WordPressPost> {
// Implementation
}- Update feature lists for new capabilities
- Add usage examples for new tools
- Update installation instructions if needed
Before submitting a pull request, ensure:
- All tests pass (
npm test) - ESLint compliance (
npm run lint) - TypeScript compilation (
npm run build) - Code formatted (
npm run format)
- Unit tests for new functionality (β₯50% coverage)
- Integration tests for API changes
- Security tests for security-related changes
- Performance tests for performance-related changes
- No coverage regression (>1% decrease)
- New code meets coverage targets
- Coverage report generated (
npm run test:coverage)
- JSDoc comments for public APIs
- README updated if needed
- CLAUDE.md updated for significant changes
- Commit messages follow conventional format
## Description
Brief description of changes and motivation.
## Type of Change
- [ ] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that causes existing functionality to change)
- [ ] Documentation update
## Testing
- [ ] Unit tests added/updated
- [ ] Integration tests added/updated
- [ ] Security tests added/updated (if applicable)
- [ ] Performance tests added/updated (if applicable)
- [ ] All tests pass
## Coverage
- [ ] Coverage maintained or improved
- [ ] New code meets β₯50% coverage target
- [ ] No regression in coverage metrics
## Security
- [ ] Input validation implemented
- [ ] No sensitive data exposed in logs
- [ ] Authentication/authorization properly handled
- [ ] Security tests pass
## Performance
- [ ] Performance impact assessed
- [ ] No memory leaks introduced
- [ ] Response times within targets
- [ ] Performance tests pass (if applicable)
## Documentation
- [ ] Code documented with JSDoc
- [ ] README updated (if applicable)
- [ ] Usage examples provided- Automated Checks: All CI/CD checks must pass
- Code Review: At least one maintainer review required
- Security Review: Required for security-related changes
- Performance Review: Required for performance-related changes
- Merge: Squash and merge after approval
The main branch has strict protection rules:
- β Pull requests required
- β Status checks must pass
- β No direct pushes allowed
- β No merge commits allowed
- β Branch must be up to date
- Documentation: Project README
- Development Guide: CLAUDE.md
- Testing Guide: TESTING_GUIDELINES.md
- Coverage Strategy: COVERAGE_STRATEGY.md
- GitHub Issues: For bug reports and feature requests
- GitHub Discussions: For questions and community discussion
- Code Review: Through pull request comments
- TypeScript errors: Check
exactOptionalPropertyTypescompliance - Test failures: Ensure proper mocking and async handling
- Coverage issues: Add tests for uncovered branches
- Linting errors: Run
npm run lint:fixfor auto-fixes
This project maintains high standards for code quality, testing, and security. By following these guidelines, you help ensure that the MCP WordPress Server remains:
- β Reliable: Comprehensive testing and quality gates
- β Secure: Defense-in-depth security practices
- β Performant: Optimized for speed and efficiency
- β Maintainable: Clean, well-documented, type-safe code
- β Professional: Industry-standard development practices
Thank you for contributing to making WordPress management better for everyone! π