# MCP Implementation: Global Rules
## Core Development Principles
These principles must be followed throughout all implementation phases:
### 1. Asynchronous Operations ONLY
- Use async/await patterns for all operations
- No blocking calls anywhere in the codebase
- Handle all Promises with proper error catching
- Use Promise.all for parallel operations
```javascript
// CORRECT
async function getMailAndEvents() {
try {
const [mail, events] = await Promise.all([
mailService.getRecentMail(),
calendarService.getTodayEvents()
]);
return { mail, events };
} catch (error) {
ErrorService.createError('api', 'Failed to fetch data', 'error', { error });
throw error;
}
}
// INCORRECT - Don't nest Promises this way
function getMailAndEvents() {
return mailService.getRecentMail()
.then(mail => {
return calendarService.getTodayEvents()
.then(events => {
return { mail, events };
});
});
}
```
### 2. Error Handling
- Use standardized error creation: `ErrorService.createError(category, message, severity, context)`
- Categorize all errors appropriately
- Include enough context for debugging
- Never expose internal error details to clients
- Log all errors with appropriate severity
```javascript
try {
// Operation that might fail
} catch (error) {
const mcpError = ErrorService.createError(
'graph',
'Failed to fetch messages',
'error',
{ requestPath: '/me/messages', statusCode: error.statusCode }
);
// Log error
MonitoringService.logError(mcpError);
// Return user-friendly error
throw new ApiError('Could not retrieve messages', 500);
}
```
### 3. Module System
- Each Microsoft service integration must be implemented as a standalone module
- Modules must register their capabilities during initialization
- Modules must handle specific intents related to their functionality
- Use dependency injection for services
- Each module must have a consistent interface
```javascript
// Module interface
module.exports = {
id: 'mail', // Unique identifier
name: 'Outlook Mail', // Human-readable name
capabilities: ['readMail', 'sendMail'], // Supported intents
init(services) {
// Initialize with dependencies
return this;
},
async handleIntent(intent, entities, context) {
// Handle specific intents
}
};
```
### 4. Data Validation
- Use Joi schemas to validate all inputs
- Validate API inputs at the controller level
- Validate outputs before sending to client
- Include descriptive error messages for validation failures
```javascript
const eventSchema = Joi.object({
subject: Joi.string().required(),
start: Joi.object({
dateTime: Joi.date().iso().required(),
timeZone: Joi.string().default('UTC')
}).required(),
// Additional fields...
});
// Validate in controller
const { error, value } = eventSchema.validate(req.body);
if (error) {
return res.status(400).json({
error: 'Invalid event data',
details: error.details[0].message
});
}
```
### 5. Caching Strategy
- Implement appropriate caching for all Graph API calls
- Use in-memory cache for Phase 1, optionally Redis for later phases
- Set appropriate TTL for different data types
- Implement cache invalidation
- Fall back gracefully on cache misses
```javascript
// Example caching pattern
async function getUserProfile(userId) {
const cacheKey = `user:profile:${userId}`;
// Try to get from cache first
const cachedProfile = await cacheService.get(cacheKey);
if (cachedProfile) {
return cachedProfile;
}
// If not in cache, fetch from API
const profile = await graphClient.api('/me').get();
// Normalize and cache the result
const normalizedProfile = normalizeUserProfile(profile);
await cacheService.set(cacheKey, normalizedProfile, 60 * 60); // 1 hour TTL
return normalizedProfile;
}
```
### 6. Data Normalization
- Normalize all data from Microsoft Graph to remove unnecessary fields
- Follow consistent normalization patterns across modules
- Store normalized data in cache
- Ensure consistent field naming conventions
```javascript
// Email normalization example
function normalizeEmail(graphEmail) {
return {
id: graphEmail.id,
subject: graphEmail.subject,
from: {
name: graphEmail.from?.emailAddress?.name,
email: graphEmail.from?.emailAddress?.address
},
received: graphEmail.receivedDateTime,
preview: graphEmail.bodyPreview?.substring(0, 150),
isRead: graphEmail.isRead,
importance: graphEmail.importance,
hasAttachments: graphEmail.hasAttachments
// No need to include the full body or internal routing info
};
}
```
### 7. File Structure & Documentation
- Follow the defined file structure
- Document each file's purpose at the top
- Include JSDoc comments for all public functions
- Use consistent file naming conventions
- Group related functionality
```javascript
/**
* @fileoverview Handles Microsoft Graph Mail API operations.
* This service provides functions for retrieving, searching, and sending emails.
*/
/**
* Searches for emails matching the specified criteria.
*
* @param {Object} options - Search options
* @param {string} options.query - Search query string
* @param {number} [options.limit=20] - Maximum number of results
* @param {Date} [options.since] - Only include emails after this date
* @returns {Promise<Array<Object>>} Normalized email objects
*/
async function searchEmails(options) {
// Implementation...
}
```
### 8. Authentication
- Use MSAL for Microsoft authentication
- Implement public client flow (no app secret)
- Handle token refresh gracefully
- Securely store tokens using system keychain
- Implement proper sign-out functionality
### 9. API Design
- Create RESTful endpoints for all functionality
- Use consistent URL patterns
- Implement proper HTTP status codes
- Return standardized responses
- Include error details in error responses
```javascript
// Standardized success response structure
{
"success": true,
"data": { /* result data */ },
"meta": { /* pagination, etc. */ }
}
// Standardized error response structure
{
"success": false,
"error": {
"message": "User-friendly error message",
"code": "ERROR_CODE",
"details": "Additional error details"
}
}
```
### 10. Multi-Phase Support
- Design with all phases in mind
- Use feature flags for phased functionality
- Keep code modular to enable easy addition of new features
- Document which phase each feature belongs to
- Design interfaces that will accommodate future extensions
```javascript
// Feature flag example
const FEATURES = {
REDIS_CACHE: process.env.ENABLE_REDIS === 'true',
TEAMS_INTEGRATION: process.env.ENABLE_TEAMS === 'true',
PROACTIVE_NOTIFICATIONS: process.env.ENABLE_NOTIFICATIONS === 'true'
};
// Usage
if (FEATURES.REDIS_CACHE) {
// Initialize Redis cache
} else {
// Use in-memory cache
}
```
### 11. Testing
- Write tests before or alongside implementation
- Test both success and error paths
- Mock external dependencies
- Use descriptive test names
- Update test documentation after each test
### 12. Memory Management
- Be mindful of memory usage, especially for large datasets
- Implement pagination for large result sets
- Properly clean up event listeners and subscriptions
- Monitor memory usage in long-running processes
### 13. Versioning
- Use semantic versioning for the application
- Include version information in API responses
- Document breaking changes between versions
- Maintain backward compatibility when possible
### 14. Configuration
- Use environment variables for configuration
- Provide sensible defaults
- Validate configuration at startup
- Document all configuration options
## Remember
- Keep each file focused on a single responsibility
- Document each file's purpose before implementation
- Test each component after implementation
- Update the "memory" about each file's purpose and API
- Consider the end-user experience in all design decisions.