Skip to main content
Glama

COA Goldfish MCP

by anortham
Program.cs19.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(); } } }

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