Program.cs•19.3 kB
using COA.Mcp.Framework.Server;
using COA.Mcp.Framework.TokenOptimization;
using COA.Mcp.Framework.TokenOptimization.Caching;
using COA.Mcp.Framework.TokenOptimization.Storage;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.EntityFrameworkCore;
using COA.Goldfish.McpServer.Services.Storage;
using COA.Goldfish.McpServer.Services;
using COA.Goldfish.McpServer.Tools;
using COA.Goldfish.McpServer.Providers;
using Serilog;
using System.IO;
using System.Reflection;
namespace COA.Goldfish.McpServer;
public class Program
{
    /// <summary>
    /// Configure shared services used by the MCP server
    /// </summary>
    private static void ConfigureSharedServices(IServiceCollection services, IConfiguration configuration)
    {
        // Register configuration
        services.AddSingleton<IConfiguration>(configuration);
        // Register Memory Cache for query caching
        services.AddMemoryCache();
        
        // Entity Framework Core
        var connectionString = configuration.GetConnectionString("DefaultConnection") 
                               ?? configuration["Goldfish:Database:ConnectionString"]
                               ?? GetDefaultConnectionString();
        
        // Replace {BasePath} placeholder if present
        if (connectionString.Contains("{BasePath}"))
        {
            var userProfile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
            var goldFishDir = Path.Combine(userProfile, ".coa", "goldfish");
            Directory.CreateDirectory(goldFishDir);
            connectionString = connectionString.Replace("{BasePath}", goldFishDir);
        }
        
        services.AddDbContext<GoldfishDbContext>(options =>
        {
            options.UseSqlite(connectionString);
            options.EnableSensitiveDataLogging(false);
            options.EnableServiceProviderCaching(true);
        });
        
        // Core Goldfish services
        services.AddScoped<IStorageService, StorageService>();
        services.AddScoped<DatabaseInitializer>();
        services.AddScoped<IPathResolutionService, PathResolutionService>();
        services.AddScoped<WorkspaceService>();
        services.AddScoped<ISearchService, SearchService>();
        services.AddScoped<SyncService>();
        // Token Optimization services
        services.AddSingleton<ITokenEstimator, DefaultTokenEstimator>();
        
        // Configure cache eviction policy - LRU with 50MB limit for Goldfish
        services.AddSingleton<ICacheEvictionPolicy>(sp => 
            new LruEvictionPolicy(maxMemoryBytes: 50_000_000, targetMemoryUsageRatio: 0.8));
        
        // Register caching services with eviction policy
        services.AddSingleton<IResponseCacheService, ResponseCacheService>();
        services.AddSingleton<IResourceStorageService, ResourceStorageService>();
        services.AddSingleton<ICacheKeyGenerator, CacheKeyGenerator>();
        
        // HTTP client for sync service
        services.AddHttpClient<SyncService>();
        
        // Behavioral adoption template services
        services.AddSingleton<TemplateProvider>();
        services.AddScoped<GoldfishResourceProvider>();
    }
    
    /// <summary>
    /// Get default SQLite connection string with user profile location
    /// </summary>
    private static string GetDefaultConnectionString()
    {
        var userProfile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
        var goldFishDir = Path.Combine(userProfile, ".coa", "goldfish");
        Directory.CreateDirectory(goldFishDir);
        var dbPath = Path.Combine(goldFishDir, "goldfish.db");
        return $"Data Source={dbPath}";
    }
    /// <summary>
    /// Configure Serilog with file logging only (no console to avoid breaking STDIO)
    /// </summary>
    private static void ConfigureSerilog(IConfiguration configuration, string[]? args = null)
    {
        var userProfile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
        var logsPath = Path.Combine(userProfile, ".coa", "goldfish", "logs");
        Directory.CreateDirectory(logsPath);
        
        var logFile = Path.Combine(logsPath, "goldfish-.log");
        
        // Determine process mode for logging context
        var processMode = "STDIO";
        if (args?.Contains("--mode") == true && args?.Contains("http") == true)
        {
            processMode = "HTTP";
        }
        else if (args?.Contains("--service") == true)
        {
            processMode = "SERVICE";
        }
        Log.Logger = new LoggerConfiguration()
            .ReadFrom.Configuration(configuration)
            .Enrich.WithProperty("ProcessMode", processMode)
            .WriteTo.File(
                logFile,
                rollingInterval: RollingInterval.Day,
                rollOnFileSizeLimit: true,
                fileSizeLimitBytes: 10 * 1024 * 1024, // 10MB
                retainedFileCountLimit: 7, // Keep 7 days of logs
                shared: true, // Allow multiple processes to write to same file
                outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{ProcessMode}] {SourceContext} {Message:lj}{NewLine}{Exception}"
            )
            .CreateLogger();
    }
    public static async Task Main(string[] args)
    {
        // Load configuration early for logging setup
        var configuration = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .AddEnvironmentVariables()
            .Build();
        // Configure Serilog early - FILE ONLY (no console to avoid breaking STDIO)
        ConfigureSerilog(configuration, args);
        try
        {
            Log.Information("Starting Goldfish MCP Server");
            
            // Use framework's builder
            var builder = new McpServerBuilder()
                .WithServerInfo("Goldfish", "1.0.0")
                .ConfigureLogging(logging =>
                {
                    logging.ClearProviders();
                    logging.AddSerilog(); // Use Serilog for all logging
                });
            // Configure shared services
            ConfigureSharedServices(builder.Services, configuration);
            
            // Register tools in DI first (required for constructor dependencies)
            builder.Services.AddScoped<CheckpointTool>();
            builder.Services.AddScoped<TodoTool>();
            builder.Services.AddScoped<PlanTool>();
            builder.Services.AddScoped<RecallTool>();
            builder.Services.AddScoped<ChronicleTool>();
            builder.Services.AddScoped<StandupTool>();
            builder.Services.AddScoped<WorkspaceTool>();
            builder.Services.AddScoped<SearchTool>();
            builder.Services.AddScoped<SearchCheckpointsTool>();
            builder.Services.AddScoped<SearchPlansTool>();
            builder.Services.AddScoped<SearchTodosTool>();
            builder.Services.AddScoped<SearchChronicleTool>();
            // Discover and register all tools from assembly
            builder.DiscoverTools(typeof(Program).Assembly);
            // Configure behavioral adoption using Framework features
            var templateVariables = new COA.Mcp.Framework.Services.TemplateVariables
            {
                AvailableTools = new[] { "checkpoint", "todo", "plan", "recall", "chronicle", "standup", "workspace", "search", "search_checkpoints", "search_plans", "search_todos", "search_chronicle" },
                ToolPriorities = new Dictionary<string, int>
                {
                    {"checkpoint", 100},
                    {"todo", 95}, 
                    {"plan", 90},
                    {"standup", 85},
                    {"recall", 80},
                    {"chronicle", 75},
                    {"workspace", 70}
                },
                EnforcementLevel = COA.Mcp.Framework.Configuration.WorkflowEnforcement.StronglyUrge,
                ToolComparisons = new Dictionary<string, COA.Mcp.Framework.Configuration.ToolComparison>
                {
                    ["Session management"] = new COA.Mcp.Framework.Configuration.ToolComparison
                    {
                        Task = "Session management",
                        ServerTool = "mcp__goldfish__checkpoint",
                        Advantage = "SQLite-backed session state with automatic workspace detection",
                        BuiltInTool = "Manual note-taking",
                        Limitation = "No persistent state, session context lost between conversations",
                        PerformanceMetric = "Persistent state vs ephemeral sessions"
                    },
                    ["Task tracking"] = new COA.Mcp.Framework.Configuration.ToolComparison
                    {
                        Task = "Task tracking", 
                        ServerTool = "mcp__goldfish__todo",
                        Advantage = "Smart TODO management with active list concept and keyword resolution",
                        BuiltInTool = "Manual task lists",
                        Limitation = "No persistence, no progress tracking, easy to lose tasks",
                        PerformanceMetric = "Structured task management vs ad-hoc notes"
                    },
                    ["Strategic planning"] = new COA.Mcp.Framework.Configuration.ToolComparison
                    {
                        Task = "Strategic planning",
                        ServerTool = "mcp__goldfish__plan", 
                        Advantage = "Comprehensive planning with discovery accumulation and automatic TODO generation",
                        BuiltInTool = "Conversation notes",
                        Limitation = "No structured planning, no progress tracking, plans get lost",
                        PerformanceMetric = "Structured planning workflow vs informal discussions"
                    },
                    ["Integrated development workflow"] = new COA.Mcp.Framework.Configuration.ToolComparison
                    {
                        Task = "Professional development workflow",
                        ServerTool = "mcp__goldfish__plan + mcp__codesearch__text_search + mcp__goldfish__checkpoint",
                        Advantage = "Combined workflow organization AND technical precision - prevents both chaos and compilation errors",
                        BuiltInTool = "Ad-hoc coding without planning or type verification",
                        Limitation = "No persistence, no structure, no type safety - leads to broken code and lost work",
                        PerformanceMetric = "Recoverable workflows with type safety vs starting from scratch with compilation errors"
                    },
                    ["Session continuity with technical context"] = new COA.Mcp.Framework.Configuration.ToolComparison
                    {
                        Task = "Resume development work",
                        ServerTool = "mcp__goldfish__recall + mcp__codesearch__recent_files + mcp__goldfish__workspace",
                        Advantage = "Restore both workflow context AND technical understanding of recent changes",
                        BuiltInTool = "Memory + manual codebase exploration",
                        Limitation = "Forget what you were doing, lose technical insights, re-discover same information",
                        PerformanceMetric = "Instant context restoration vs 15+ minutes of re-discovery work"
                    },
                    ["Complex feature development"] = new COA.Mcp.Framework.Configuration.ToolComparison
                    {
                        Task = "Complex feature development",
                        ServerTool = "plan → todo → checkpoint cycle",
                        Advantage = "Strategic planning breaks complexity into manageable pieces",
                        BuiltInTool = "Diving straight into code",
                        Limitation = "No structure, easy to get lost, missing requirements",
                        PerformanceMetric = "Structured completion vs chaotic development"
                    },
                    ["Cross-session continuity"] = new COA.Mcp.Framework.Configuration.ToolComparison
                    {
                        Task = "Maintaining context across sessions",
                        ServerTool = "checkpoint + recall workflow",
                        Advantage = "Perfect memory of what was working on and why",
                        BuiltInTool = "Starting fresh each session",
                        Limitation = "Lost context, repeated questions, forgotten decisions",
                        PerformanceMetric = "Instant context restoration vs 15+ minutes orientation"
                    }
                },
                CustomVariables = new Dictionary<string, object>
                {
                    ["has_tool"] = true,
                    ["enforcement_level"] = "strongly_urge",
                    ["behavioral_adoption"] = true,
                    ["database_backend"] = "SQLite with Entity Framework Core"
                }
            };
            
            // Load comprehensive behavioral methodology template
            string templateContent;
            var assembly = typeof(Program).Assembly;
            var resourceName = "COA.Goldfish.McpServer.Templates.goldfish-instructions.scriban";
            
            // BEHAVIORAL ADOPTION PIPELINE: Stage 1 - Template Loading
            Log.Information("[BEHAVIORAL-ADOPTION] Stage 1: Template Loading - Attempting to load from embedded resource: {ResourceName}", resourceName);
            
            // List available embedded resources for debugging
            var availableResources = assembly.GetManifestResourceNames();
            Log.Information("[BEHAVIORAL-ADOPTION] Available embedded resources: {Resources}", string.Join(", ", availableResources));
            
            using (var stream = assembly.GetManifestResourceStream(resourceName))
            {
                if (stream == null)
                {
                    Log.Error("[BEHAVIORAL-ADOPTION] Critical: Comprehensive behavioral adoption template not found: {ResourceName}", resourceName);
                    throw new InvalidOperationException($"Required template resource not found: {resourceName}");
                }
                
                using (var reader = new StreamReader(stream))
                {
                    templateContent = await reader.ReadToEndAsync();
                }
            }
            
            // BEHAVIORAL ADOPTION PIPELINE: Stage 2 - Template Content Verification
            var templateHash = templateContent.GetHashCode().ToString("X8");
            Log.Information("[BEHAVIORAL-ADOPTION] Stage 2: Template Content Loaded - Length: {Length} characters, Hash: {Hash}", templateContent.Length, templateHash);
            Log.Debug("[BEHAVIORAL-ADOPTION] Template content preview: {Preview}...", templateContent.Substring(0, Math.Min(200, templateContent.Length)));
            
            // BEHAVIORAL ADOPTION PIPELINE: Stage 3 - Variable Preparation
            Log.Information("[BEHAVIORAL-ADOPTION] Stage 3: Variable Preparation - Preparing {ToolCount} tools, {ComparisonCount} comparisons", 
                templateVariables.AvailableTools.Length, templateVariables.ToolComparisons.Count);
            
            // Configure template instructions
            builder.WithTemplateInstructions(options =>
            {
                options.EnableTemplateInstructions = true;
                options.CustomTemplate = templateContent;
                options.TemplateContext = "goldfish";
                
                options.CustomTemplateVariables = new Dictionary<string, object>
                {
                    ["available_tools"] = templateVariables.AvailableTools,
                    ["tool_priorities"] = templateVariables.ToolPriorities,
                    ["enforcement_level"] = templateVariables.EnforcementLevel.ToString().ToLower(),
                    ["tool_comparisons"] = templateVariables.ToolComparisons.Values.ToList(),
                    ["has_tool"] = true
                };
                
                // BEHAVIORAL ADOPTION PIPELINE: Stage 4 - Template Configuration
                Log.Information("[BEHAVIORAL-ADOPTION] Stage 4: Template Configuration Complete - Variables: {VariableCount}", options.CustomTemplateVariables.Count);
                Log.Information("[BEHAVIORAL-ADOPTION] Template variables configured:");
                Log.Information("[BEHAVIORAL-ADOPTION] - Available tools: {Tools}", string.Join(", ", templateVariables.AvailableTools));
                Log.Information("[BEHAVIORAL-ADOPTION] - Tool comparisons: {Count} comparisons", templateVariables.ToolComparisons.Count);
                Log.Information("[BEHAVIORAL-ADOPTION] - Enforcement level: {Level}", templateVariables.EnforcementLevel);
                Log.Information("[BEHAVIORAL-ADOPTION] - Template context: {Context}", options.TemplateContext);
                Log.Information("[BEHAVIORAL-ADOPTION] - Template instructions enabled: {Enabled}", options.EnableTemplateInstructions);
            });
            // BEHAVIORAL ADOPTION PIPELINE: Stage 5 - Server Startup
            Log.Information("[BEHAVIORAL-ADOPTION] Stage 5: Starting Goldfish MCP Server with Enhanced Behavioral Adoption");
            Log.Information("[BEHAVIORAL-ADOPTION] Template hash: {Hash} | Enforcement: {Level} | Tools: {Count}", 
                templateHash, templateVariables.EnforcementLevel, templateVariables.AvailableTools.Length);
            
            // Use STDIO transport with proper stdin/stdout streams for MCP protocol compliance
            // This prevents stream concurrency issues by using raw streams instead of Console.In/Out
            builder.UseStdioTransport(options =>
            {
                options.Input = new StreamReader(Console.OpenStandardInput());
                options.Output = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true };
            });
            
            // Add hosted service for database initialization
            builder.Services.AddHostedService<DatabaseInitializationService>();
            
            // BEHAVIORAL ADOPTION PIPELINE: Stage 6 - Final Preparation
            Log.Information("[BEHAVIORAL-ADOPTION] Stage 6: STDIO Transport Configured - Ready to deliver behavioral adoption instructions");
            Log.Information("[BEHAVIORAL-ADOPTION] Pipeline Complete: Template loaded → Variables prepared → Instructions configured → Server ready");
            Log.Information("[BEHAVIORAL-ADOPTION] Expected behavior: Claude should follow systematic development workflow and maintain organized task management");
            
            await builder.RunAsync();
        }
        catch (Exception ex)
        {
            Log.Fatal(ex, "Goldfish startup failed");
            throw;
        }
        finally
        {
            Log.CloseAndFlush();
        }
    }
}