Skip to main content
Glama
leeguooooo
by leeguooooo
README.md10 kB
# Services Layer ## Overview The services layer provides clean, high-level interfaces for email operations, encapsulating business logic and reducing coupling between tool handlers and low-level operations. ## Architecture ``` Handlers → Services → Operations/Legacy ``` Services handle: - Business logic and validation - Automatic selection of optimized vs. standard operations - Parallel vs. sequential operation decisions - Error handling and result formatting - Account management integration ## Services ### EmailService Handles core email operations. **Methods**: ```python def list_emails(limit, unread_only, folder, account_id) -> Dict[str, Any]: """List emails with automatic optimization""" def get_email_detail(email_id, folder, account_id) -> Dict[str, Any]: """Get detailed email content""" def mark_emails(email_ids, mark_as, folder, account_id) -> Dict[str, Any]: """Mark emails as read/unread (auto-parallel for multiple emails)""" def delete_emails(email_ids, folder, permanent, trash_folder, account_id) -> Dict[str, Any]: """Delete or move emails to trash (auto-parallel for multiple emails)""" def search_emails(query, search_in, date_from, date_to, folder, ...) -> Dict[str, Any]: """Search emails across accounts""" ``` **Example**: ```python from services import EmailService email_service = EmailService(account_manager) # List unread emails result = email_service.list_emails( limit=50, unread_only=True, folder='INBOX' ) # Mark multiple emails as read (automatically uses parallel operations) result = email_service.mark_emails( email_ids=['1', '2', '3'], mark_as='read', folder='INBOX' ) ``` --- ### CommunicationService Handles sending, replying, and forwarding emails. **Methods**: ```python def send_email(to, subject, body, cc, bcc, attachments, is_html, account_id) -> Dict[str, Any]: """Send a new email""" def reply_email(email_id, body, reply_all, folder, attachments, is_html, account_id) -> Dict[str, Any]: """Reply to an email (preserves thread)""" def forward_email(email_id, to, body, folder, include_attachments, account_id) -> Dict[str, Any]: """Forward an email""" ``` **Example**: ```python from services import CommunicationService comm_service = CommunicationService(account_manager) # Send email result = comm_service.send_email( to=['user@example.com'], subject='Hello', body='World', is_html=False ) # Reply to email result = comm_service.reply_email( email_id='123', body='Thanks for your message!', reply_all=False ) ``` --- ### FolderService Handles folder operations and email organization. **Methods**: ```python def list_folders(account_id) -> Dict[str, Any]: """List all email folders""" def move_emails_to_folder(email_ids, target_folder, source_folder, account_id) -> Dict[str, Any]: """Move emails to different folder (auto-parallel for multiple emails)""" def flag_email(email_id, flag_type, set_flag, folder, account_id) -> Dict[str, Any]: """Flag/star or unflag an email""" def get_email_attachments(email_id, folder, account_id) -> Dict[str, Any]: """Extract attachments from email""" ``` **Example**: ```python from services import FolderService folder_service = FolderService(account_manager) # List folders result = folder_service.list_folders() # Move emails to Archive (automatically uses parallel operations) result = folder_service.move_emails_to_folder( email_ids=['1', '2', '3'], target_folder='Archive', source_folder='INBOX' ) # Flag important email result = folder_service.flag_email( email_id='123', flag_type='flagged', set_flag=True ) ``` --- ### SystemService Handles system-level operations. **Methods**: ```python def check_connection() -> Dict[str, Any]: """Test email server connections (IMAP and SMTP)""" def list_accounts() -> Dict[str, Any]: """List all configured email accounts""" ``` **Example**: ```python from services import SystemService system_service = SystemService(account_manager) # Check connections result = system_service.check_connection() # List accounts result = system_service.list_accounts() ``` --- ## Service Design Principles ### 1. Consistent Return Format All service methods return a dictionary with at least: ```python { 'success': bool, 'error': str, # Only present if success=False # ... additional data } ``` ### 2. Automatic Optimization Services automatically select the best implementation: ```python # EmailService.list_emails() automatically chooses: if unread_only and not account_id and folder == 'INBOX': # Use optimized multi-account fetch return optimized_fetch(...) else: # Use standard fetch return standard_fetch(...) ``` ### 3. Parallel Operations Services automatically use parallel operations for batch processing: ```python # EmailService.mark_emails() automatically decides: if len(email_ids) > 1: try: # Try parallel operations return parallel_ops.execute_batch_operation(...) except ImportError: # Fall back to sequential return sequential_processing(...) ``` ### 4. Error Handling Services provide consistent error handling: ```python try: # Operation return {'success': True, 'data': result} except Exception as e: logger.error(f"Operation failed: {e}", exc_info=True) return {'error': str(e), 'success': False} ``` --- ## Integration with Handlers Handlers access services through `ToolContext`: ```python class ToolContext: def __init__(self, account_manager, messages_func): self.account_manager = account_manager self.get_message = messages_func # Initialize all services self.email_service = EmailService(account_manager) self.communication_service = CommunicationService(account_manager) self.folder_service = FolderService(account_manager) self.system_service = SystemService(account_manager) ``` Handler example: ```python def handle_list_emails(args: Dict[str, Any], ctx: ToolContext): """Handler delegates to service""" result = ctx.email_service.list_emails( limit=args.get('limit', 50), unread_only=args.get('unread_only', False), folder=args.get('folder', 'INBOX'), account_id=args.get('account_id') ) if 'error' in result: return format_error(result['error']) return format_success(result) ``` --- ## Benefits ### For Maintainability - ✅ Single place to modify business logic - ✅ Clear separation of concerns - ✅ Reduced code duplication - ✅ Easier to understand and modify ### For Testing - ✅ Services can be tested independently - ✅ Handlers can use mock services - ✅ No need to mock low-level operations - ✅ Cleaner test code ### For Extension - ✅ New services can be added easily - ✅ Service methods can be reused - ✅ Easy to switch implementations - ✅ Plugin-friendly architecture --- ## Adding a New Service 1. Create service file in `src/services/` 2. Implement service class with methods 3. Add to `src/services/__init__.py` 4. Initialize in `ToolContext` 5. Use in handlers Example: ```python # 1. src/services/calendar_service.py class CalendarService: def __init__(self, account_manager): self.account_manager = account_manager def get_events(self, start_date, end_date): # Implementation pass # 2. src/services/__init__.py from .calendar_service import CalendarService # 3. src/core/tool_handlers.py class ToolContext: def __init__(self, account_manager, messages_func): # ... self.calendar_service = CalendarService(account_manager) # 4. Use in handler def handle_list_events(args, ctx): result = ctx.calendar_service.get_events(...) return format_result(result) ``` --- ## Migration Guide If you're updating existing code to use services: ### Before (Direct operations) ```python from ..legacy_operations import fetch_emails def handle_list_emails(args, ctx): result = fetch_emails( args.get('limit', 50), args.get('unread_only', False), args.get('folder', 'INBOX'), args.get('account_id') ) return format(result) ``` ### After (Using service) ```python def handle_list_emails(args, ctx): result = ctx.email_service.list_emails( limit=args.get('limit', 50), unread_only=args.get('unread_only', False), folder=args.get('folder', 'INBOX'), account_id=args.get('account_id') ) return format(result) ``` **Changes**: 1. Remove direct imports of operations 2. Use `ctx.<service_name>.<method>()` 3. Pass named parameters for clarity 4. Service handles optimization/parallel logic --- ## Best Practices 1. **Always use services in handlers**: Never bypass services to call operations directly 2. **Let services handle complexity**: Don't duplicate optimization logic in handlers 3. **Use named parameters**: Makes code more readable and maintainable 4. **Handle service errors**: Check `success` field in returned dictionary 5. **Log at service level**: Services should log errors with context 6. **Keep services focused**: Each service should handle one domain --- ## FAQ **Q: Why not just use operations directly?** A: Services provide abstraction, optimization selection, error handling, and consistency. They make the codebase more maintainable. **Q: Can I add business logic to handlers?** A: No, handlers should only validate input and format output. Business logic belongs in services. **Q: How do I add a new email operation?** A: Add method to appropriate service (likely EmailService), implement using operations, then use in handler. **Q: Can services call other services?** A: Yes, but be careful of circular dependencies. Keep service interactions simple. **Q: What if I need direct operation access?** A: Consider if the logic belongs in a service. If it's truly unique, you can still access operations, but this should be rare.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/leeguooooo/email-mcp-service'

If you have feedback or need assistance with the MCP directory API, please join our Discord server