Skip to main content
Glama

COA Goldfish MCP

by anortham
SyncService.csโ€ข10.9 kB
using System.Text.Json; using COA.Goldfish.McpServer.Services.Storage; using Microsoft.Extensions.Logging; using System.Text; using Microsoft.Extensions.Configuration; namespace COA.Goldfish.McpServer.Services; /// <summary> /// Enterprise-ready sync service with offline-first design /// </summary> public class SyncService : ISyncService { private readonly HttpClient _httpClient; private readonly IStorageService _storage; private readonly IConfiguration _configuration; private readonly ILogger<SyncService> _logger; private readonly Queue<SyncQueueItem> _syncQueue = new(); private readonly object _queueLock = new object(); private SyncConfiguration? _syncConfiguration; public SyncService(HttpClient httpClient, IStorageService storage, IConfiguration configuration, ILogger<SyncService> logger) { _httpClient = httpClient; _storage = storage; _configuration = configuration; _logger = logger; // Load configuration LoadConfiguration(); } public bool IsEnabled => _syncConfiguration?.ApiEndpoint != null; public async Task<SyncStatus> GetStatusAsync() { return new SyncStatus { IsConnected = IsEnabled && await TestConnectionAsync(), LastSyncTime = GetLastSyncTime(), QueuedOperations = GetQueuedOperationsCount(), PendingConflicts = 0, // TODO: Implement conflict detection Configuration = _syncConfiguration }; } public async Task<SyncResult> SyncWorkspaceAsync(string workspaceId, SyncOptions? options = null) { if (!IsEnabled) { return new SyncResult { Success = false, ErrorMessage = "Sync is not configured or enabled" }; } var startTime = DateTime.UtcNow; var result = new SyncResult(); try { _logger.LogInformation("Starting sync for workspace {WorkspaceId}", workspaceId); // Sync all entity types var entitiesToSync = SyncEntityType.All; var syncResult = await SyncEntitiesAsync(workspaceId, entitiesToSync, options); result = syncResult; result.Duration = DateTime.UtcNow - startTime; _logger.LogInformation("Completed sync for workspace {WorkspaceId} in {Duration}ms", workspaceId, result.Duration.TotalMilliseconds); } catch (Exception ex) { _logger.LogError(ex, "Failed to sync workspace {WorkspaceId}", workspaceId); result.Success = false; result.ErrorMessage = ex.Message; result.Duration = DateTime.UtcNow - startTime; } return result; } public async Task<SyncResult> SyncEntitiesAsync(string workspaceId, SyncEntityType entityTypes, SyncOptions? options = null) { var result = new SyncResult { Success = true }; if (!IsEnabled) { result.Success = false; result.ErrorMessage = "Sync is not enabled"; return result; } try { // For now, simulate sync operations await Task.Delay(100); // Simulate network call if (entityTypes.HasFlag(SyncEntityType.Checkpoints)) { await SyncCheckpointsAsync(workspaceId); result.EntitiesSynced += 1; } if (entityTypes.HasFlag(SyncEntityType.TodoLists)) { await SyncTodoListsAsync(workspaceId); result.EntitiesSynced += 1; } if (entityTypes.HasFlag(SyncEntityType.Plans)) { await SyncPlansAsync(workspaceId); result.EntitiesSynced += 1; } _logger.LogDebug("Synced {Count} entity types for workspace {WorkspaceId}", result.EntitiesSynced, workspaceId); } catch (Exception ex) { _logger.LogError(ex, "Failed to sync entities for workspace {WorkspaceId}", workspaceId); result.Success = false; result.ErrorMessage = ex.Message; } return result; } public async Task QueueForSyncAsync(string workspaceId, SyncEntityType entityType, string entityId, SyncOperation operation) { var queueItem = new SyncQueueItem { Id = Guid.NewGuid().ToString(), WorkspaceId = workspaceId, EntityType = entityType, EntityId = entityId, Operation = operation, QueuedAt = DateTime.UtcNow }; lock (_queueLock) { _syncQueue.Enqueue(queueItem); } _logger.LogDebug("Queued {Operation} operation for {EntityType} {EntityId} in workspace {WorkspaceId}", operation, entityType, entityId, workspaceId); await Task.CompletedTask; // Make method async } public async Task<SyncResult> ProcessQueueAsync() { var result = new SyncResult { Success = true }; var processedCount = 0; if (!IsEnabled) { result.Success = false; result.ErrorMessage = "Sync is not enabled"; return result; } try { while (GetQueuedOperationsCount() > 0) { SyncQueueItem? item = null; lock (_queueLock) { if (_syncQueue.Count > 0) { item = _syncQueue.Dequeue(); } } if (item != null) { await ProcessQueueItemAsync(item); processedCount++; } } result.EntitiesSynced = processedCount; _logger.LogInformation("Processed {Count} queued sync operations", processedCount); } catch (Exception ex) { _logger.LogError(ex, "Failed to process sync queue"); result.Success = false; result.ErrorMessage = ex.Message; } return result; } public async Task ConfigureAsync(SyncConfiguration configuration) { _syncConfiguration = configuration; // Configure HTTP client if (!string.IsNullOrEmpty(configuration.AuthenticationToken)) { _httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue( configuration.AuthenticationType ?? "Bearer", configuration.AuthenticationToken); } _logger.LogInformation("Sync configured with endpoint: {Endpoint}", configuration.ApiEndpoint); await Task.CompletedTask; } public async Task<bool> TestConnectionAsync() { if (string.IsNullOrEmpty(_syncConfiguration?.ApiEndpoint)) { return false; } try { var response = await _httpClient.GetAsync($"{_syncConfiguration.ApiEndpoint}/health"); return response.IsSuccessStatusCode; } catch (Exception ex) { _logger.LogDebug(ex, "Connection test failed"); return false; } } public async Task<SyncResult> ForceResyncAsync(string workspaceId) { _logger.LogInformation("Forcing full resync for workspace {WorkspaceId}", workspaceId); var options = new SyncOptions { ForceSync = true, Direction = SyncDirection.Bidirectional }; return await SyncWorkspaceAsync(workspaceId, options); } public async Task<List<SyncConflict>> GetConflictsAsync(string workspaceId) { // TODO: Implement conflict detection await Task.CompletedTask; return new List<SyncConflict>(); } public async Task<SyncResult> ResolveConflictsAsync(List<SyncConflictResolution> resolutions) { // TODO: Implement conflict resolution await Task.CompletedTask; return new SyncResult { Success = true }; } #region Private Methods private void LoadConfiguration() { var config = new SyncConfiguration { ApiEndpoint = _configuration["Goldfish:Sync:ApiEndpoint"], AuthenticationToken = _configuration["Goldfish:Sync:AuthToken"], AuthenticationType = _configuration["Goldfish:Sync:AuthType"] ?? "Bearer", EnableOfflineQueue = _configuration.GetValue("Goldfish:Sync:EnableOfflineQueue", true), AutoResolveConflicts = _configuration.GetValue("Goldfish:Sync:AutoResolveConflicts", false) }; if (!string.IsNullOrEmpty(config.ApiEndpoint)) { _syncConfiguration = config; _logger.LogInformation("Sync configuration loaded from settings"); } else { _logger.LogDebug("No sync configuration found in settings"); } } private DateTime? GetLastSyncTime() { // TODO: Implement last sync time tracking return null; } private int GetQueuedOperationsCount() { lock (_queueLock) { return _syncQueue.Count; } } private async Task SyncCheckpointsAsync(string workspaceId) { // TODO: Implement checkpoint sync await Task.Delay(50); _logger.LogDebug("Synced checkpoints for workspace {WorkspaceId}", workspaceId); } private async Task SyncTodoListsAsync(string workspaceId) { // TODO: Implement todo list sync await Task.Delay(50); _logger.LogDebug("Synced todo lists for workspace {WorkspaceId}", workspaceId); } private async Task SyncPlansAsync(string workspaceId) { // TODO: Implement plans sync await Task.Delay(50); _logger.LogDebug("Synced plans for workspace {WorkspaceId}", workspaceId); } private async Task ProcessQueueItemAsync(SyncQueueItem item) { // TODO: Implement queue item processing await Task.Delay(10); _logger.LogDebug("Processed queue item {ItemId}", item.Id); } #endregion } /// <summary> /// Item in the sync queue for offline operations /// </summary> internal class SyncQueueItem { public string Id { get; set; } = string.Empty; public string WorkspaceId { get; set; } = string.Empty; public SyncEntityType EntityType { get; set; } public string EntityId { get; set; } = string.Empty; public SyncOperation Operation { get; set; } public DateTime QueuedAt { get; set; } public int RetryCount { get; set; } = 0; }

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/anortham/coa-goldfish-mcp'

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