-
Notifications
You must be signed in to change notification settings - Fork 1
V3 Architecture
DaemonsMCP Version 3 uses a clean architecture approach with clear separation of concerns, enabling both MCP protocol integration and REST API access to share the same business logic.
V3 is built on four distinct layers following Domain-Driven Design and Clean Architecture principles:
- Domain Layer - Core business entities and contracts
- Application Layer - Business logic, commands, queries (MediatR CQRS)
- Infrastructure Layer - Data access, external services, file operations
- Host Layer - Two independent entry points (MCP Server and REST API)
This separation allows the same codebase to serve both AI assistants via the Model Context Protocol and human users via a web interface, without duplicating business logic.
┌─────────────────────────────────────────────────────────────┐
│ Presentation Layer │
├──────────────────────┬──────────────────────────────────────┤
│ DaemonsMCP.exe │ DaemonsMCP.Api.exe │
│ (MCP Server) │ (ASP.NET Core API) │
│ - MCPSharp │ - REST Endpoints │
│ - JSON-RPC │ - Swagger/OpenAPI │
│ - Tool Handlers │ - CORS Support │
└──────────┬───────────┴────────────┬─────────────────────────┘
│ │
└────────┬───────────────┘
│ Both send MediatR Commands/Queries
│
┌──────────▼───────────────────────────────────────────┐
│ Application Layer │
│ (DaemonsMCP.Application) │
├──────────────────────────────────────────────────────┤
│ Commands (Write Operations) │
│ - CreateProject, UpdateFile, AddItem, etc. │
│ Queries (Read Operations) │
│ - SearchFileSystem, GetItemById, etc. │
│ Validators (FluentValidation) │
│ DTOs & Models │
└──────────┬───────────────────────────────────────────┘
│ Calls repositories & services
│
┌──────────▼───────────────────────────────────────────┐
│ Infrastructure Layer │
│ (DaemonsMCP.Infrastructure) │
├──────────────────────────────────────────────────────┤
│ - ApplicationDbContext (EF Core) │
│ - Repository Implementations │
│ - FileWatcherService (debounced file monitoring) │
│ - ObjectHierarchyIndexingService (Roslyn parser) │
│ - CodeParserService (namespace/class/method) │
└──────────┬───────────────────────────────────────────┘
│ Persists to database
│
┌──────────▼───────────────────────────────────────────┐
│ Domain Layer │
│ (DaemonsMCP.Domain) │
├──────────────────────────────────────────────────────┤
│ Entities: │
│ - Project, FileSystemNode, ObjectHierarchy │
│ - Item, ItemType, StatusType │
│ Repository Interfaces (IProjectRepository, etc.) │
│ Domain Events │
│ Value Objects │
│ Constants & Enums │
└──────────────────────────────────────────────────────┘
│
┌──────────▼───────────────────────────────────────────┐
│ SQL Server Database │
│ Tables: Projects, FileSystemNodes, ObjectHierarchy, │
│ Items, ItemTypes, StatusTypes │
└──────────────────────────────────────────────────────┘
The domain layer contains the core business entities and defines contracts (interfaces) without any implementation details or external dependencies.
Project
- Represents a codebase root directory
- Properties:
Id,Name,RootPath,Description,CreatedAt - Root aggregate for file system operations
FileSystemNode
- Represents files and directories in a hierarchical tree
- Properties:
Id,ProjectId,ParentId,Name,RelativePath,IsDirectory,SizeInBytes - Self-referencing for parent-child relationships
- Enforces forward-slash path normalization
ObjectHierarchy
- Represents parsed code structures (namespaces, classes, methods, etc.)
- Properties:
Id,ProjectId,FileSystemNodeId,ParentId,IdentifierTypeId,Name,FullyQualifiedName,Signature - Links code constructs to source files
- Supports hierarchical queries (namespace → class → method)
Item
- Flexible hierarchical node system for notes, tasks, and documentation
- Properties:
Id,ParentId,Name,Details,ItemTypeId,StatusTypeId,Rank,Created,Modified,Completed - Optional references to
FileSystemNodeorObjectHierarchy. - User-definable types and statuses
ItemType / StatusType
- Configurable taxonomies for organizing Items
- Default types: Feature, Bug, Task, Note, Documentation, Todo, Readme
- Default statuses: Not Started, In Progress, Complete, Cancelled, Blocked
Domain defines contracts for data access without implementation:
IProjectRepository
IFileSystemRepository
IObjectHierarchyRepository
IItemRepository
IItemTypeRepository
IStatusTypeRepository- No dependencies on other layers or external libraries
- Pure C# entities with business logic only
- Interface-based contracts for infrastructure concerns
- Immutable value objects where appropriate
- Domain events for cross-cutting concerns (future enhancement)
The application layer orchestrates business logic using the MediatR CQRS pattern, keeping commands (writes) and queries (reads) strictly separated.
Commands (mutations):
CreateProjectCommandCreateProjectFileCommandCreateProjectFolderCommandUpdateProjectFileCommandSyncProjectFileSystemCommandAddUpdateItemCommandDeleteItemCommandAddUpdateItemTypeCommandAddUpdateStatusTypeCommand
Queries (reads):
GetAllProjectsQueryGetProjectByIdQuerySearchFileSystemQueryGetFileContentsQuerySearchObjectHierarchyQuerySearchItemsQueryGetItemByIdQueryGetReadmeQueryListItemTypesQueryListStatusTypesQuery
Each command/query has a dedicated handler:
// Command pattern
public class CreateProjectCommandHandler
: IRequestHandler<CreateProjectCommand, Result<ProjectDto>>
{
private readonly IProjectRepository _repository;
public async Task<Result<ProjectDto>> Handle(
CreateProjectCommand request,
CancellationToken cancellationToken)
{
// 1. Validate
// 2. Create domain entity
// 3. Persist via repository
// 4. Return DTO
}
}FluentValidation ensures data integrity before handlers execute:
public class CreateProjectCommandValidator
: AbstractValidator<CreateProjectCommand>
{
public CreateProjectCommandValidator()
{
RuleFor(x => x.Name).NotEmpty().MaximumLength(200);
RuleFor(x => x.RootPath).NotEmpty().Must(BeValidPath);
}
}Application layer returns DTOs, never domain entities:
ProjectDtoFileSystemNodeDtoObjectHierarchyDtoItemDtoItemTypeDtoStatusTypeDto
This prevents tight coupling between API consumers and domain model.
All handlers return Result<T> for consistent error handling:
public class Result<T>
{
public bool Success { get; set; }
public T Data { get; set; }
public string ErrorMessage { get; set; }
public string Operation { get; set; }
}Benefits:
- No exceptions for business rule violations
- Consistent error responses across MCP and REST
- Easy to serialize to JSON-RPC or HTTP responses
The infrastructure layer implements all external concerns: database access, file I/O, code parsing, and background services.
ApplicationDbContext:
- Manages all entity configurations
- Implements repository interfaces via generic patterns
- Handles database migrations
- Configures relationships and indexes
Key Configurations:
modelBuilder.Entity<FileSystemNode>()
.HasOne(f => f.Parent)
.WithMany(f => f.Children)
.HasForeignKey(f => f.ParentId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<ObjectHierarchy>()
.HasIndex(o => o.FullyQualifiedName);Concrete implementations of domain interfaces:
public class ProjectRepository : IProjectRepository
{
private readonly ApplicationDbContext _context;
public async Task<Project> GetByIdAsync(int id)
=> await _context.Projects.FindAsync(id);
public async Task<int> AddAsync(Project project)
{
_context.Projects.Add(project);
await _context.SaveChangesAsync();
return project.Id;
}
}Monitors project directories for changes and triggers re-indexing:
Features:
- Debounced file change detection (5-second window)
- Batch processing to avoid excessive indexing
- Queues change, processes when idle
- Thread-safe with proper async/await patterns
- Filters out temp files, binaries, and IDE artifacts
Flow:
File Change Detected → Debounce Timer → Queue File →
Process Batch → Parse C# Files → Update ObjectHierarchy →
Update FileSystemNodes
CodeParserService (Roslyn-based):
- Extracts namespaces, classes, interfaces, methods, properties, fields, events
- Builds hierarchical relationships
- Generates fully qualified names and signatures
- Links to source file locations
ObjectHierarchyIndexingService:
- Manages indexing queue
- Orchestrates batch processing
- Updates database efficiently
- Handles concurrent file modifications
Supported Constructs:
- Namespaces
- Interfaces
- Classes (including nested classes)
- Methods (including constructors, operators)
- Properties (with get/set accessors)
- Fields
- Events
- Method parameters
PathSecurityService:
- Validates all paths are within project root
- Blocks directory traversal attacks (
../,..\) - Normalizes paths to forward slashes
- Prevents access to system directories
public bool IsPathSafe(string projectRoot, string requestedPath)
{
var normalized = NormalizePath(requestedPath);
var fullPath = Path.GetFullPath(Path.Combine(projectRoot, normalized));
var rootPath = Path.GetFullPath(projectRoot);
return fullPath.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase);
}Purpose: Runs as a long-lived daemon serving Claude Desktop via MCPSharp
Responsibilities:
- Implements MCPSharp server protocol
- Registers MCP tools (methods Claude can call)
- Translates JSON-RPC requests to MediatR commands/queries
- Formats responses according to MCP spec
- Maintains persistent connection with Claude Desktop
Tool Handler Pattern:
[McpServerTool]
public class FileSystemToolHandler
{
private readonly IMediator _mediator;
[McpTool("search-file-system")]
public async Task<Result<SearchResults>> SearchFileSystem(
int projectId,
string filter,
bool includeFiles,
bool includeDirectories,
int pageNo,
int pageSize)
{
var query = new SearchFileSystemQuery
{
ProjectId = projectId,
Filter = filter,
// ... map parameters
};
return await _mediator.Send(query);
}
}Key MCP Tool Categories:
- Project management (
list-projects) - File system operations (
search-file-system,get-project-file,create-project-file, etc.) - Code hierarchy (
search-object-hierarchy) - Items system (
search-items,add-update-item, etc.) - Metadata (
list-item-types,list-status-types) - Documentation (
readme)
Purpose: Provides HTTP/REST access for web applications (Angular config viewer)
Responsibilities:
- RESTful endpoints for all operations
- OpenAPI/Swagger documentation
- CORS for cross-origin requests
- JSON serialization
- Health checks
Endpoint Mapping:
app.MapProjectEndpoints() // /api/projects
.MapFileSystemEndpoints() // /api/filesystem
.MapIndexingEndpoints() // /api/indexing
.MapItemsEndpoints() // /api/items
.MapObjectHierarchyEndpoints() // /api/hierarchyMinimal API Pattern:
public static class FileSystemEndpoints
{
public static IEndpointRouteBuilder MapFileSystemEndpoints(
this IEndpointRouteBuilder app)
{
app.MapGet("/api/filesystem/search", async (
[FromServices] IMediator mediator,
[AsParameters] SearchFileSystemRequest request) =>
{
var query = request.ToQuery();
var result = await mediator.Send(query);
return Results.Ok(result);
});
return app;
}
}Both hosts share:
- Same Application Layer (commands/queries)
- Same Infrastructure (repositories, services)
- Same Domain (entities, interfaces)
- Same business logic and validation
1. Claude Desktop sends JSON-RPC request to DaemonsMCP.exe
Tool: "create-project-file"
Params: { projectId: 1, relativePath: "src/NewClass.cs", content: "..." }
2. MCP Tool Handler maps to CreateProjectFileCommand
3. MediatR dispatches to CreateProjectFileCommandHandler
4. Handler validates:
- Project exists
- Path is safe (within project root)
- File doesn't already exist
5. Handler calls IFileSystemRepository.CreateFileAsync()
6. Repository writes file to disk via System.IO
7. Repository creates FileSystemNode entity in database
8. FileWatcherService detects new file
9. Queues file for indexing (if .cs file)
10. ObjectHierarchyIndexingService parses with Roslyn
11. Extracts namespaces/classes/methods → ObjectHierarchy table
12. Handler returns Result<FileSystemNodeDto>
13. MCP Tool Handler formats as JSON-RPC response
14. Claude receives success confirmation
1. Angular app sends HTTP GET to /api/hierarchy/search?searchTerm=UserService
2. Minimal API endpoint receives request
3. Maps to SearchObjectHierarchyQuery
4. MediatR dispatches to SearchObjectHierarchyQueryHandler
5. Handler calls IObjectHierarchyRepository.SearchAsync()
6. Repository executes EF Core query with filters:
SELECT * FROM ObjectHierarchy
WHERE Name LIKE '%UserService%'
ORDER BY FullyQualifiedName
7. Returns ObjectHierarchy entities
8. Handler maps to ObjectHierarchyDto list
9. Returns Result<PagedResult<ObjectHierarchyDto>>
10. API endpoint serializes to JSON
11. Angular app displays results in searchable list
1. Developer saves MyService.cs in Visual Studio
2. FileWatcherService detects change event
3. Starts 5-second debounce timer
4. Additional saves during window → timer resets
5. Timer expires → file added to processing queue
6. ObjectHierarchyIndexingService processes batch:
- Reads file content
- Parses with Roslyn SyntaxTree
- Extracts semantic model
- Walks syntax nodes for namespaces/classes/methods
7. For each construct:
- Creates/updates ObjectHierarchy record
- Sets ParentId for hierarchy
- Stores FullyQualifiedName, Signature
- Links to FileSystemNode
8. Batch completes → queue emptied
9. Next file change starts new cycle
(No user interaction required - fully automatic)
Serilog is configured at the host level:
- Structured logging with context enrichment
- File sinks (general and error logs)
- Configurable log levels per namespace
- Automatic log rotation and retention
Log Locations:
%LOCALAPPDATA%\DaemonsMCP\Logs\D3MCP-{date}.log%LOCALAPPDATA%\DaemonsMCP\Logs\D3MCP-errors-{date}.log
Strategy: No exceptions for business logic failures
Pattern:
- Commands/Queries return
Result<T>with success/failure - Infrastructure exceptions are caught and logged
- MCP responses use proper JSON-RPC error codes
- REST API returns appropriate HTTP status codes
Source Priority:
- Command line arguments
- Environment variables
daemonsmcp.{Environment}.jsondaemonsmcp.json
Key Settings:
- Database connection string
- Log levels
- CORS origins
- File watcher settings (in code)
All layers use constructor injection:
// Application Layer
public class CreateProjectCommandHandler
{
private readonly IProjectRepository _repository;
public CreateProjectCommandHandler(IProjectRepository repository)
{
_repository = repository;
}
}
// Infrastructure registered in host
builder.Services.AddScoped<IProjectRepository, ProjectRepository>();- Clean Architecture - Dependency inversion, layer separation
- CQRS - Command/Query Responsibility Segregation
- Repository Pattern - Data access abstraction
- Mediator Pattern - Decoupled command/query handling
- Result Pattern - No exceptions for business failures
- Aggregate Roots - Project as boundary for file operations
- Value Objects - Immutable descriptors (planned enhancement)
- Domain Events - Cross-cutting notifications (planned enhancement)
- Unit of Work - EF Core DbContext
- Lazy Loading Disabled - Explicit includes for performance
- Debouncing - File watcher change aggregation
- Batch Processing - Indexing queue optimization
- Designed for: Single developer, local machine, small to medium codebases
- Typical project: 10K-100K files, 50K-500K code constructs
- Database: SQL Server Express sufficient
For larger teams/codebases:
- Add caching layer (Redis) for frequent queries
- Implement pagination for all large result sets
- Background job processing (Hangfire/Quartz) for indexing
- Read replicas for query scaling
- Message queue (RabbitMQ) for file change events
For multi-tenant scenarios:
- Add user authentication/authorization
- Row-level security on Projects
- Project quotas and limits
- API rate limiting
| Layer | Technologies |
|---|---|
| Domain | .NET 9.0, C# 13 |
| Application | MediatR, FluentValidation, AutoMapper (planned) |
| Infrastructure | EF Core 9, SQL Server, Microsoft.CodeAnalysis (Roslyn), Serilog |
| MCP Host | MCPSharp, JSON-RPC |
| API Host | ASP.NET Core 9, Minimal APIs, Swagger/OpenAPI |
| Client | Angular 18, PrimeNG, Monaco Editor, TypeScript |
| Database | SQL Server 2019+ |
Separation of Concerns:
- Business logic lives in one place (Application Layer)
- Both MCP and REST share identical logic
- No code duplication
- Changes propagate to all consumers
Testability:
- Each layer can be tested independently
- Mock repositories for unit testing
- Integration tests at handler level
- End-to-end tests via MCP or REST
Maintainability:
- Clear boundaries between layers
- Dependencies flow inward (toward domain)
- Easy to locate and modify features
- CQRS separates read/write concerns
Extensibility:
- Add new tools: Create handler + register tool
- Add new entities: Domain → EF migration → handlers
- Add new clients: Use same Application Layer
- Swap infrastructure: Implement interfaces differently
Planned Enhancements:
- SignalR Hub - Real-time updates between API and MCP
- Domain Events - Cross-cutting concerns via event bus
- Caching Layer - Redis for frequently accessed data
- Multi-language Support - TypeScript, Python, SQL parsers
- Workflow Engine - Light orchestration on top of MediatR
- Audit Logging - Track all changes to entities
- Soft Deletes - Preserve data with IsDeleted flag
- Database Schema - Entity relationships and tables
- Application Layers - Deep dive into each layer
- MCP Tools Reference - Available tools and usage
- REST API Reference - HTTP endpoints
- Extending the System - How to add features