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();
}
}
}