# ColaFlow MCP Server Architecture Design **Version:** 1.0 **Date:** 2025-11-04 **Author:** System Architect **Status:** Design Document --- ## Table of Contents 1. [Background & Goals](#1-background--goals) 2. [Architecture Overview](#2-architecture-overview) 3. [Module Design](#3-module-design) 4. [Resources & Tools Design](#4-resources--tools-design) 5. [Diff Preview & Approval Mechanism](#5-diff-preview--approval-mechanism) 6. [Security & Permission Model](#6-security--permission-model) 7. [Audit & Observability](#7-audit--observability) 8. [Database Design](#8-database-design) 9. [Technology Stack & Rationale](#9-technology-stack--rationale) 10. [Implementation Roadmap](#10-implementation-roadmap) 11. [Risks & Mitigation](#11-risks--mitigation) --- ## 1. Background & Goals ### 1.1 Business Context ColaFlow aims to be an **AI-native project management system** where AI tools (ChatGPT, Claude, Gemini) can: - Read project data (projects, issues, sprints, documents) - Write project data with **human approval** via diff preview - Automate workflows while maintaining human oversight and safety ### 1.2 Current System State **M1 Achievements:** - ✅ Identity Module (User, Tenant, Multi-tenancy) - ✅ JWT Authentication & RBAC (TenantOwner, TenantAdmin, TenantMember, TenantGuest, AIAgent) - ✅ Clean Architecture (.NET 9.0, PostgreSQL, EF Core) - ✅ Domain-Driven Design with Aggregates, Value Objects, Domain Events **Current Tech Stack:** - Backend: .NET 9.0, ASP.NET Core - Database: PostgreSQL + EF Core - Authentication: JWT Bearer - Architecture: Clean Architecture (Domain, Application, Infrastructure, API) ### 1.3 Technical Objectives 1. **MCP Server Integration:** Expose ColaFlow resources and tools to AI clients via MCP protocol 2. **Safety First:** All AI write operations require diff preview → human approval → commit 3. **Auditability:** Complete audit trail for all AI actions 4. **Scalability:** Designed for horizontal scaling, multi-tenant isolation 5. **Extensibility:** Support multiple AI models and future integrations ### 1.4 Constraints - Must integrate with existing Clean Architecture - Must reuse Identity Module (User, Tenant, TenantRole) - Must maintain multi-tenant data isolation - Must comply with GDPR and enterprise security standards --- ## 2. Architecture Overview ### 2.1 High-Level Architecture ``` ┌──────────────────────────────────────────────────────────────────┐ │ AI Client Layer │ │ ChatGPT, Claude, Gemini (via MCP SDK) │ └────────────────────────────┬─────────────────────────────────────┘ │ MCP Protocol (JSON-RPC over stdio/SSE) ┌────────────────────────────┴─────────────────────────────────────┐ │ ColaFlow MCP Server │ │ ┌────────────────────────────────────────────────────────────┐ │ │ │ MCP Protocol Handler (JSON-RPC Endpoints) │ │ │ │ - initialize, resources/list, resources/read │ │ │ │ - tools/list, tools/call │ │ │ │ - prompts/list, prompts/get │ │ │ └──────────────────────┬─────────────────────────────────────┘ │ │ │ │ │ ┌──────────────────────┴─────────────────────────────────────┐ │ │ │ MCP Application Services │ │ │ │ - ResourceService (read-only access) │ │ │ │ - ToolInvocationService (write with diff preview) │ │ │ │ - DiffPreviewService (generate, store, approve) │ │ │ │ - PromptTemplateService (AI prompt management) │ │ │ └──────────────────────┬─────────────────────────────────────┘ │ │ │ │ │ ┌──────────────────────┴─────────────────────────────────────┐ │ │ │ MCP Security Layer │ │ │ │ - AIAgent Authentication (API Key, JWT) │ │ │ │ - Permission Validation (field-level, tenant-scoped) │ │ │ │ - Rate Limiting │ │ │ └──────────────────────┬─────────────────────────────────────┘ │ └─────────────────────────┼──────────────────────────────────────┘ │ ┌─────────────────────────┴──────────────────────────────────────┐ │ ColaFlow Application Layer │ │ - ProjectManagement.Application (Epic, Story, Task, Sprint) │ │ - Identity.Application (User, Tenant, Role) │ └─────────────────────────┬──────────────────────────────────────┘ │ ┌─────────────────────────┴──────────────────────────────────────┐ │ Domain Layer │ │ - ProjectManagement.Domain (Aggregates, Entities, VOs) │ │ - Identity.Domain (User, Tenant, Role) │ └─────────────────────────┬──────────────────────────────────────┘ │ ┌─────────────────────────┴──────────────────────────────────────┐ │ Infrastructure Layer │ │ - PostgreSQL (Tenant-isolated data) │ │ - MCP Audit Logs (mcp_audit_logs) │ │ - Diff Preview Storage (mcp_diff_previews) │ └────────────────────────────────────────────────────────────────┘ ``` ### 2.2 MCP Protocol Overview **MCP (Model Context Protocol)** is a standardized protocol for AI-application communication: **Key Concepts:** 1. **Resources:** Read-only data exposures (e.g., `project://12345`, `issue://67890`) 2. **Tools:** AI-invokable functions (e.g., `create_issue`, `update_status`) 3. **Prompts:** Reusable AI prompt templates 4. **Sampling:** AI model invocation (future phase) **Transport:** - **stdio:** Standard input/output for local processes (MCP SDK default) - **SSE (Server-Sent Events):** For web-based AI clients - **JSON-RPC 2.0:** Request/response protocol **Example Tool Call Flow:** ``` 1. AI Client → MCP Server: tools/list 2. MCP Server → AI Client: [create_issue, update_status, ...] 3. AI Client → MCP Server: tools/call { name: "create_issue", arguments: {...} } 4. MCP Server → AI Client: { diffPreview: {...}, approvalRequired: true } 5. Human approves diff 6. AI Client → MCP Server: tools/call { previewId: "abc123", approve: true } 7. MCP Server → AI Client: { success: true, issueId: "12345" } ``` ### 2.3 Module Boundaries **New Modules:** 1. **ColaFlow.Modules.Mcp.Domain** - Aggregates: McpAgent, DiffPreview, AuditLog - Repositories: IMcpAgentRepository, IDiffPreviewRepository, IAuditLogRepository 2. **ColaFlow.Modules.Mcp.Application** - Services: IResourceService, IToolInvocationService, IDiffPreviewService - Commands: ApproveDiffCommand, RejectDiffCommand, RollbackOperationCommand - Queries: ListResourcesQuery, GetDiffPreviewQuery 3. **ColaFlow.Modules.Mcp.Infrastructure** - Persistence: McpDbContext, Repositories - Protocol: JsonRpcHandler, SseTransportHandler - Security: ApiKeyAuthenticationHandler 4. **ColaFlow.Modules.Mcp.API** - Controllers: McpProtocolController (JSON-RPC endpoints) - Middleware: McpAuthenticationMiddleware, McpAuditMiddleware --- ## 3. Module Design ### 3.1 MCP.Domain Module #### 3.1.1 McpAgent Aggregate ```csharp namespace ColaFlow.Modules.Mcp.Domain.Aggregates.McpAgents; /// /// Represents an AI Agent registered to access ColaFlow via MCP /// public sealed class McpAgent : AggregateRoot { // Identity public Guid Id { get; private set; } public TenantId TenantId { get; private set; } public string AgentName { get; private set; } // e.g., "ChatGPT for PM Team" public string AgentType { get; private set; } // e.g., "Claude", "ChatGPT", "Gemini" // Authentication public string ApiKeyHash { get; private set; } // Hashed API key public DateTime ApiKeyExpiresAt { get; private set; } public bool IsActive { get; private set; } // Permissions public McpPermissionLevel PermissionLevel { get; private set; } public List AllowedResources { get; private set; } // ["projects.*", "issues.read"] public List AllowedTools { get; private set; } // ["create_issue", "update_status"] // Audit public DateTime CreatedAt { get; private set; } public Guid CreatedBy { get; private set; } public DateTime? LastUsedAt { get; private set; } public int RequestCount { get; private set; } // Factory Method public static McpAgent Create( TenantId tenantId, string agentName, string agentType, string apiKeyHash, DateTime apiKeyExpiresAt, Guid createdBy) { // ... validation & domain event } // Business Methods public void RecordUsage() { /* ... */ } public void UpdatePermissions(McpPermissionLevel level, List resources, List tools) { /* ... */ } public void RevokeAccess() { /* ... */ } public void RegenerateApiKey(string newApiKeyHash, DateTime expiresAt) { /* ... */ } } public enum McpPermissionLevel { ReadOnly = 1, // Can only read resources WriteWithPreview = 2, // Can write with diff preview (requires approval) DirectWrite = 3 // Can write directly (dangerous, TenantOwner only) } ``` #### 3.1.2 DiffPreview Aggregate ```csharp namespace ColaFlow.Modules.Mcp.Domain.Aggregates.DiffPreviews; /// /// Represents a diff preview for AI-initiated write operations /// public sealed class DiffPreview : AggregateRoot { public Guid Id { get; private set; } public TenantId TenantId { get; private set; } public Guid AgentId { get; private set; } // McpAgent.Id // Operation Details public string ToolName { get; private set; } // e.g., "create_issue" public string InputParametersJson { get; private set; } // JSON serialized // Diff Details public DiffOperation Operation { get; private set; } // Create, Update, Delete public string EntityType { get; private set; } // e.g., "Issue", "Project" public Guid? EntityId { get; private set; } // null for Create public string BeforeStateJson { get; private set; } // null for Create public string AfterStateJson { get; private set; } public string DiffJson { get; private set; } // Structured diff // Risk Assessment public RiskLevel RiskLevel { get; private set; } // Low, Medium, High public List RiskReasons { get; private set; } // ["Deletes 50 tasks", "Changes sprint deadline"] // Approval Workflow public DiffPreviewStatus Status { get; private set; } public Guid? ApprovedBy { get; private set; } public DateTime? ApprovedAt { get; private set; } public Guid? RejectedBy { get; private set; } public DateTime? RejectedAt { get; private set; } public string? RejectionReason { get; private set; } // Rollback public bool IsCommitted { get; private set; } public Guid? CommittedEntityId { get; private set; } public DateTime? CommittedAt { get; private set; } public string? RollbackToken { get; private set; } // For event sourcing rollback // Timestamps public DateTime CreatedAt { get; private set; } public DateTime ExpiresAt { get; private set; } // Auto-reject after 24 hours // Factory & Methods public static DiffPreview Create(/* ... */) { /* ... */ } public void Approve(Guid approvedBy) { /* ... */ } public void Reject(Guid rejectedBy, string reason) { /* ... */ } public void MarkAsCommitted(Guid entityId) { /* ... */ } } public enum DiffOperation { Create, Update, Delete } public enum RiskLevel { Low, Medium, High, Critical } public enum DiffPreviewStatus { Pending, Approved, Rejected, Expired, Committed } ``` #### 3.1.3 McpAuditLog Aggregate ```csharp namespace ColaFlow.Modules.Mcp.Domain.Aggregates.AuditLogs; /// /// Complete audit trail for all MCP operations /// public sealed class McpAuditLog : AggregateRoot { public Guid Id { get; private set; } public TenantId TenantId { get; private set; } public Guid AgentId { get; private set; } // Request Details public string OperationType { get; private set; } // "resources/read", "tools/call" public string ResourceUri { get; private set; } // e.g., "project://12345" public string ToolName { get; private set; } public string InputParametersJson { get; private set; } // Response Details public bool IsSuccess { get; private set; } public string? ErrorMessage { get; private set; } public int? HttpStatusCode { get; private set; } // Diff Preview (if applicable) public Guid? DiffPreviewId { get; private set; } public DiffPreviewStatus? DiffStatus { get; private set; } // Performance public int DurationMs { get; private set; } // Context public string ClientIpAddress { get; private set; } public string UserAgent { get; private set; } public DateTime Timestamp { get; private set; } public static McpAuditLog Create(/* ... */) { /* ... */ } } ``` ### 3.2 MCP.Application Module #### 3.2.1 Resource Service ```csharp namespace ColaFlow.Modules.Mcp.Application.Services; public interface IResourceService { /// /// List all available resources for the current AI Agent /// Task> ListResourcesAsync( TenantId tenantId, Guid agentId, CancellationToken cancellationToken = default); /// /// Read a specific resource /// Task ReadResourceAsync( string resourceUri, // e.g., "project://abc-123" TenantId tenantId, Guid agentId, CancellationToken cancellationToken = default); } public record ResourceDescriptor( string Uri, string Name, string Description, string MimeType); // "application/json", "text/plain" public record ResourceContent( string Uri, string Content, // JSON or text string MimeType); ``` **Implementation:** - Reads from ProjectManagement module (via Application layer) - Applies field-level permissions (hide sensitive fields) - Tenant-scoped queries - Returns JSON representations #### 3.2.2 Tool Invocation Service ```csharp namespace ColaFlow.Modules.Mcp.Application.Services; public interface IToolInvocationService { /// /// List all available tools for the current AI Agent /// Task> ListToolsAsync( TenantId tenantId, Guid agentId, CancellationToken cancellationToken = default); /// /// Invoke a tool (generates diff preview for write operations) /// Task InvokeToolAsync( string toolName, Dictionary arguments, TenantId tenantId, Guid agentId, CancellationToken cancellationToken = default); } public record ToolDescriptor( string Name, string Description, JsonSchema InputSchema); public record ToolInvocationResult { public bool RequiresApproval { get; init; } public Guid? DiffPreviewId { get; init; } public DiffPreviewDto? DiffPreview { get; init; } public object? Result { get; init; } // For read-only tools public bool IsSuccess { get; init; } public string? ErrorMessage { get; init; } } ``` #### 3.2.3 Diff Preview Service ```csharp namespace ColaFlow.Modules.Mcp.Application.Services; public interface IDiffPreviewService { /// /// Generate a diff preview for a proposed write operation /// Task GenerateDiffAsync( string toolName, Dictionary arguments, TenantId tenantId, Guid agentId, CancellationToken cancellationToken = default); /// /// Get diff preview by ID /// Task GetDiffPreviewAsync( Guid previewId, TenantId tenantId, CancellationToken cancellationToken = default); /// /// Approve and commit a diff preview /// Task ApproveAndCommitAsync( Guid previewId, Guid approvedBy, TenantId tenantId, CancellationToken cancellationToken = default); /// /// Reject a diff preview /// Task RejectAsync( Guid previewId, Guid rejectedBy, string reason, TenantId tenantId, CancellationToken cancellationToken = default); } public record CommitResult( bool IsSuccess, Guid? EntityId, string? ErrorMessage); ``` **Diff Generation Algorithm:** 1. Deserialize tool arguments 2. Load current state (if updating) 3. Apply arguments to domain model (dry-run) 4. Generate JSON diff (before/after) 5. Calculate risk level: - Low: Single field updates, comments - Medium: Status changes, assignments - High: Deletions, sprint changes, bulk operations - Critical: Data exports, permission changes 6. Store DiffPreview entity 7. Return preview to AI client ### 3.3 MCP.Infrastructure Module #### 3.3.1 JSON-RPC Protocol Handler ```csharp namespace ColaFlow.Modules.Mcp.Infrastructure.Protocol; public class JsonRpcHandler { private readonly IResourceService _resourceService; private readonly IToolInvocationService _toolService; public async Task HandleRequestAsync( JsonRpcRequest request, TenantId tenantId, Guid agentId, CancellationToken cancellationToken = default) { return request.Method switch { "initialize" => await HandleInitializeAsync(request), "resources/list" => await HandleResourcesListAsync(tenantId, agentId), "resources/read" => await HandleResourceReadAsync(request, tenantId, agentId), "tools/list" => await HandleToolsListAsync(tenantId, agentId), "tools/call" => await HandleToolCallAsync(request, tenantId, agentId), _ => JsonRpcResponse.Error(request.Id, -32601, "Method not found") }; } } ``` #### 3.3.2 Database Schema (EF Core Configurations) ```csharp // McpAgentConfiguration.cs public class McpAgentConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ToTable("mcp_agents", "mcp"); builder.HasKey(a => a.Id); builder.Property(a => a.AgentName).IsRequired().HasMaxLength(200); builder.Property(a => a.ApiKeyHash).IsRequired().HasMaxLength(512); // Tenant isolation builder.HasQueryFilter(a => a.TenantId == /* current tenant */); // Indexes builder.HasIndex(a => new { a.TenantId, a.IsActive }); } } ``` --- ## 4. Resources & Tools Design ### 4.1 Resource Definitions #### 4.1.1 Project Resources ```json { "resources": [ { "uri": "projects://list", "name": "All Projects", "description": "List all accessible projects", "mimeType": "application/json" }, { "uri": "project://{projectId}", "name": "Project Details", "description": "Get detailed information about a specific project", "mimeType": "application/json" }, { "uri": "project://{projectId}/issues", "name": "Project Issues", "description": "List all issues in a project", "mimeType": "application/json" } ] } ``` **Example Resource Response:** ```json { "uri": "project://abc-123", "content": { "id": "abc-123", "name": "ColaFlow MVP", "description": "Build initial MVP version", "status": "Active", "owner": { "id": "user-456", "name": "John Doe", "email": "john@example.com" }, "startDate": "2025-11-01", "endDate": "2025-12-31", "issueCount": 45, "completedIssueCount": 12 }, "mimeType": "application/json" } ``` #### 4.1.2 Issue Resources ```json { "resources": [ { "uri": "issues://search?query={query}", "name": "Search Issues", "description": "Search issues by title, description, or tags" }, { "uri": "issue://{issueId}", "name": "Issue Details", "description": "Get detailed information about a specific issue" } ] } ``` #### 4.1.3 Sprint Resources ```json { "resources": [ { "uri": "sprints://current", "name": "Current Sprint", "description": "Get the currently active sprint" }, { "uri": "sprint://{sprintId}", "name": "Sprint Details", "description": "Get detailed information about a specific sprint" } ] } ``` ### 4.2 Tool Definitions #### 4.2.1 create_issue Tool ```json { "name": "create_issue", "description": "Create a new issue in a project", "inputSchema": { "type": "object", "properties": { "projectId": { "type": "string", "description": "The project ID where the issue will be created" }, "title": { "type": "string", "description": "Issue title (required)" }, "description": { "type": "string", "description": "Detailed description of the issue" }, "issueType": { "type": "string", "enum": ["Story", "Task", "Bug", "Epic"], "description": "Type of issue" }, "priority": { "type": "string", "enum": ["Low", "Medium", "High", "Critical"], "default": "Medium" }, "assigneeId": { "type": "string", "description": "User ID to assign the issue to (optional)" }, "tags": { "type": "array", "items": { "type": "string" }, "description": "Tags for categorization" } }, "required": ["projectId", "title", "issueType"] } } ``` **Diff Preview Example:** ```json { "previewId": "preview-123", "operation": "create", "entityType": "Issue", "changes": { "before": null, "after": { "title": "Implement MCP Server authentication", "description": "Add API key authentication for AI agents", "issueType": "Task", "priority": "High", "projectId": "project-abc", "assigneeId": "user-456", "tags": ["backend", "security"] } }, "riskLevel": "Low", "riskReasons": [], "requiresApproval": true, "expiresAt": "2025-11-05T10:00:00Z" } ``` #### 4.2.2 update_issue_status Tool ```json { "name": "update_issue_status", "description": "Update the status of an existing issue", "inputSchema": { "type": "object", "properties": { "issueId": { "type": "string", "description": "The issue ID to update" }, "status": { "type": "string", "enum": ["ToDo", "InProgress", "Review", "Done"], "description": "New status" }, "comment": { "type": "string", "description": "Optional comment explaining the status change" } }, "required": ["issueId", "status"] } } ``` **Diff Preview Example:** ```json { "previewId": "preview-456", "operation": "update", "entityType": "Issue", "entityId": "issue-789", "changes": { "before": { "status": "InProgress", "updatedAt": "2025-11-03T15:30:00Z" }, "after": { "status": "Done", "updatedAt": "2025-11-04T10:00:00Z" } }, "diff": [ { "field": "status", "oldValue": "InProgress", "newValue": "Done" } ], "riskLevel": "Medium", "riskReasons": ["Marks issue as complete without code review"], "requiresApproval": true } ``` #### 4.2.3 assign_issue Tool ```json { "name": "assign_issue", "description": "Assign an issue to a team member", "inputSchema": { "type": "object", "properties": { "issueId": { "type": "string" }, "assigneeId": { "type": "string" }, "notifyAssignee": { "type": "boolean", "default": true } }, "required": ["issueId", "assigneeId"] } } ``` #### 4.2.4 log_decision Tool ```json { "name": "log_decision", "description": "Log an architectural or product decision", "inputSchema": { "type": "object", "properties": { "projectId": { "type": "string" }, "title": { "type": "string" }, "decision": { "type": "string" }, "rationale": { "type": "string" }, "alternatives": { "type": "array", "items": { "type": "string" } }, "tags": { "type": "array", "items": { "type": "string" } } }, "required": ["projectId", "title", "decision"] } } ``` ### 4.3 Prompt Templates ```json { "prompts": [ { "name": "daily_standup", "description": "Generate daily standup report", "arguments": [ { "name": "date", "description": "Report date (YYYY-MM-DD)", "required": false } ], "template": "Generate a daily standup report for {{date}}. Include:\n1. Completed tasks\n2. In-progress tasks\n3. Blockers\n4. Upcoming priorities" }, { "name": "sprint_planning", "description": "Generate sprint planning summary", "template": "Analyze the backlog and generate sprint planning recommendations:\n1. Suggested stories for next sprint\n2. Estimated story points\n3. Team capacity analysis\n4. Risk assessment" } ] } ``` --- ## 5. Diff Preview & Approval Mechanism ### 5.1 Diff Generation Flow ``` ┌──────────────┐ │ AI Client │ │ (ChatGPT) │ └──────┬───────┘ │ tools/call { name: "create_issue", arguments: {...} } ▼ ┌────────────────────────────────────────────────────────┐ │ ToolInvocationService │ │ ┌──────────────────────────────────────────────────┐ │ │ │ 1. Validate permissions (can use create_issue?) │ │ │ │ 2. Validate arguments (schema validation) │ │ │ │ 3. Call DiffPreviewService.GenerateDiffAsync() │ │ │ └──────────────────────────────────────────────────┘ │ └────────────────────────┬───────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────────────────┐ │ DiffPreviewService.GenerateDiffAsync() │ │ ┌──────────────────────────────────────────────────┐ │ │ │ 1. Load current state (if update/delete) │ │ │ │ 2. Create domain entity (dry-run, no persist) │ │ │ │ 3. Generate JSON diff (JsonDiffPatch library) │ │ │ │ 4. Calculate risk level │ │ │ │ - Check deletion count │ │ │ │ - Check critical field changes │ │ │ │ - Check impact scope │ │ │ │ 5. Create DiffPreview aggregate │ │ │ │ 6. Persist to mcp_diff_previews table │ │ │ │ 7. Return DiffPreview to client │ │ │ └──────────────────────────────────────────────────┘ │ └────────────────────────┬───────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────────────┐ │ Response to AI Client │ │ { │ │ "requiresApproval": true, │ │ "previewId": "preview-123", │ │ "diffPreview": { │ │ "operation": "create", │ │ "entityType": "Issue", │ │ "changes": { before: null, after: {...} }, │ │ "riskLevel": "Low" │ │ } │ │ } │ └──────────────────────────────────────────────────────┘ ``` ### 5.2 Approval Workflow ``` ┌──────────────────────────────────────────────────────────────┐ │ Human User (via Web UI or CLI) │ │ - Views diff preview in Admin Dashboard │ │ - Reviews changes (before/after comparison) │ │ - Decides: Approve or Reject │ └────────────────────────┬─────────────────────────────────────┘ │ ▼ (Approve) ┌────────────────────────────────────────────────────────────┐ │ DiffPreviewService.ApproveAndCommitAsync() │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ 1. Load DiffPreview by ID │ │ │ │ 2. Validate status == Pending │ │ │ │ 3. Validate not expired │ │ │ │ 4. Mark as Approved │ │ │ │ 5. Execute actual operation: │ │ │ │ - Call Application layer command │ │ │ │ - e.g., CreateIssueCommand │ │ │ │ 6. Persist entity to database │ │ │ │ 7. Update DiffPreview.IsCommitted = true │ │ │ │ 8. Generate rollback token (if needed) │ │ │ │ 9. Create McpAuditLog entry │ │ │ └──────────────────────────────────────────────────────┘ │ └────────────────────────────────────────────────────────────┘ │ ▼ (Reject) ┌────────────────────────────────────────────────────────────┐ │ DiffPreviewService.RejectAsync() │ │ ┌──────────────────────────────────────────────────────┐ │ │ │ 1. Load DiffPreview by ID │ │ │ │ 2. Mark as Rejected │ │ │ │ 3. Store rejection reason │ │ │ │ 4. Create McpAuditLog entry │ │ │ │ 5. Notify AI agent (optional) │ │ │ └──────────────────────────────────────────────────────┘ │ └────────────────────────────────────────────────────────────┘ ``` ### 5.3 Risk Level Calculation ```csharp public class RiskCalculator { public RiskLevel CalculateRisk(DiffOperation operation, string entityType, object changes) { var riskScore = 0; var reasons = new List(); // High risk: Deletions if (operation == DiffOperation.Delete) { riskScore += 50; reasons.Add("Deletion operation"); } // Medium risk: Status changes if (changes.ContainsField("status")) { riskScore += 20; reasons.Add("Status change"); } // High risk: Critical entities if (entityType == "Sprint" || entityType == "Epic") { riskScore += 30; reasons.Add($"Critical entity type: {entityType}"); } // High risk: Bulk operations if (changes.AffectedEntityCount > 10) { riskScore += 40; reasons.Add($"Affects {changes.AffectedEntityCount} entities"); } return riskScore switch { >= 80 => RiskLevel.Critical, >= 50 => RiskLevel.High, >= 20 => RiskLevel.Medium, _ => RiskLevel.Low }; } } ``` ### 5.4 Rollback Mechanism **Strategy:** Event Sourcing + Snapshot ```csharp public interface IRollbackService { /// /// Rollback a committed diff preview /// Task RollbackAsync( Guid previewId, Guid requestedBy, string reason, CancellationToken cancellationToken = default); } public class RollbackService : IRollbackService { public async Task RollbackAsync(/* ... */) { // 1. Load DiffPreview var preview = await _repository.GetByIdAsync(previewId); // 2. Validate can rollback if (!preview.IsCommitted) return RollbackResult.Failure("Not committed yet"); if (preview.Operation == DiffOperation.Delete) return RollbackResult.Failure("Cannot rollback deletions"); // 3. Load entity var entity = await LoadEntityAsync(preview.EntityType, preview.CommittedEntityId); // 4. Apply reverse changes if (preview.Operation == DiffOperation.Create) { // Soft delete entity.Delete(); } else if (preview.Operation == DiffOperation.Update) { // Restore from BeforeStateJson var beforeState = JsonSerializer.Deserialize(preview.BeforeStateJson); entity.ApplyState(beforeState); } // 5. Persist await _unitOfWork.CommitAsync(); // 6. Audit await _auditLog.LogRollbackAsync(previewId, requestedBy, reason); return RollbackResult.Success(); } } ``` --- ## 6. Security & Permission Model ### 6.1 Authentication Mechanisms #### 6.1.1 API Key Authentication (Primary) ```csharp public class ApiKeyAuthenticationHandler : AuthenticationHandler { protected override async Task HandleAuthenticateAsync() { // 1. Extract API key from header if (!Request.Headers.TryGetValue("X-MCP-API-Key", out var apiKeyHeaderValues)) return AuthenticateResult.Fail("Missing API Key"); var apiKey = apiKeyHeaderValues.FirstOrDefault(); // 2. Hash and lookup in database var hashedKey = HashApiKey(apiKey); var agent = await _agentRepository.GetByApiKeyHashAsync(hashedKey); if (agent == null || !agent.IsActive) return AuthenticateResult.Fail("Invalid or inactive API Key"); // 3. Check expiration if (agent.ApiKeyExpiresAt < DateTime.UtcNow) return AuthenticateResult.Fail("API Key expired"); // 4. Create claims principal var claims = new[] { new Claim("agent_id", agent.Id.ToString()), new Claim("tenant_id", agent.TenantId.Value.ToString()), new Claim("agent_type", agent.AgentType), new Claim("permission_level", agent.PermissionLevel.ToString()), new Claim(ClaimTypes.Role, "AIAgent") }; var identity = new ClaimsIdentity(claims, Scheme.Name); var principal = new ClaimsPrincipal(identity); var ticket = new AuthenticationTicket(principal, Scheme.Name); // 5. Record usage agent.RecordUsage(); await _agentRepository.UpdateAsync(agent); return AuthenticateResult.Success(ticket); } } ``` **API Key Format:** ``` mcp__ Example: mcp_prod_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6 ``` **API Key Storage:** - Never store plain text API keys - Use BCrypt hashing (like passwords) - Store: `ApiKeyHash`, `ApiKeyExpiresAt`, `IsActive` #### 6.1.2 JWT Authentication (Fallback for Web Clients) ```csharp // Reuse existing JWT authentication from Identity module // Add "AIAgent" role claim for AI-authenticated users services.AddAuthentication(options => { options.DefaultAuthenticateScheme = "ApiKeyOrJwt"; }) .AddPolicyScheme("ApiKeyOrJwt", "API Key or JWT", options => { options.ForwardDefaultSelector = context => { if (context.Request.Headers.ContainsKey("X-MCP-API-Key")) return "ApiKey"; return JwtBearerDefaults.AuthenticationScheme; }; }) .AddScheme("ApiKey", null) .AddJwtBearer(/* ... existing JWT config ... */); ``` ### 6.2 Authorization & Permissions #### 6.2.1 Permission Levels ```csharp public enum McpPermissionLevel { /// /// Read-only access to resources /// - Can call resources/list, resources/read /// - Cannot call any tools /// ReadOnly = 1, /// /// Read + Write with preview (DEFAULT for AI Agents) /// - Can read all resources /// - Can call tools, but generates diff preview /// - All writes require human approval /// WriteWithPreview = 2, /// /// Direct write (DANGEROUS, TenantOwner approval required) /// - Can read all resources /// - Can write directly without approval /// - Should be used only for trusted automation /// DirectWrite = 3 } ``` #### 6.2.2 Field-Level Permissions ```csharp public class FieldLevelPermissionFilter { private static readonly HashSet SensitiveFields = new() { "passwordHash", "apiKeyHash", "salary", "ssn", "creditCard" }; public object FilterSensitiveFields(object entity, TenantRole role) { // AIAgent role: Hide all sensitive fields if (role == TenantRole.AIAgent) { var json = JsonSerializer.Serialize(entity); var document = JsonDocument.Parse(json); foreach (var sensitiveField in SensitiveFields) { RemoveField(document, sensitiveField); } return JsonSerializer.Deserialize(document.RootElement); } return entity; // Human users: no filtering } } ``` #### 6.2.3 Resource-Level Permissions ```csharp public class ResourcePermissionValidator { public bool CanAccessResource(string resourceUri, McpAgent agent) { // Check if agent has explicit permission if (agent.AllowedResources.Contains("*")) return true; // Full access // Check wildcard patterns foreach (var pattern in agent.AllowedResources) { if (MatchesPattern(resourceUri, pattern)) return true; } return false; } private bool MatchesPattern(string uri, string pattern) { // Support wildcards: // "projects.*" matches "projects://list", "project://123" // "issues.read" matches "issue://456" (read-only) // "issues.write" matches tool calls var regex = new Regex(pattern.Replace("*", ".*")); return regex.IsMatch(uri); } } ``` ### 6.3 Rate Limiting ```csharp public class McpRateLimiter { private readonly IDistributedCache _cache; // Redis public async Task CheckRateLimitAsync(Guid agentId, string operation) { var key = $"ratelimit:agent:{agentId}:{operation}"; var current = await _cache.GetStringAsync(key); var limits = operation switch { "resources/read" => (100, TimeSpan.FromMinutes(1)), // 100 req/min "tools/call" => (10, TimeSpan.FromMinutes(1)), // 10 req/min _ => (50, TimeSpan.FromMinutes(1)) }; var count = int.Parse(current ?? "0"); if (count >= limits.Item1) return false; // Rate limit exceeded await _cache.SetStringAsync( key, (count + 1).ToString(), new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = limits.Item2 }); return true; } } ``` ### 6.4 Tenant Isolation ```csharp // Global query filter (already exists in Identity module) public class McpDbContext : DbContext { private readonly ITenantContext _tenantContext; protected override void OnModelCreating(ModelBuilder modelBuilder) { // Apply tenant filter to all MCP entities modelBuilder.Entity() .HasQueryFilter(e => e.TenantId == _tenantContext.TenantId); modelBuilder.Entity() .HasQueryFilter(e => e.TenantId == _tenantContext.TenantId); modelBuilder.Entity() .HasQueryFilter(e => e.TenantId == _tenantContext.TenantId); } } ``` --- ## 7. Audit & Observability ### 7.1 Audit Log Schema ```sql -- mcp.mcp_audit_logs CREATE TABLE mcp.mcp_audit_logs ( id UUID PRIMARY KEY, tenant_id UUID NOT NULL, agent_id UUID NOT NULL, -- Request details operation_type VARCHAR(100) NOT NULL, -- 'resources/read', 'tools/call' resource_uri VARCHAR(500), tool_name VARCHAR(200), input_parameters_json JSONB, -- Response details is_success BOOLEAN NOT NULL, error_message TEXT, http_status_code INTEGER, -- Diff preview (if applicable) diff_preview_id UUID, diff_status VARCHAR(50), -- 'Pending', 'Approved', 'Rejected' -- Performance duration_ms INTEGER NOT NULL, -- Context client_ip_address VARCHAR(50), user_agent TEXT, timestamp TIMESTAMP NOT NULL DEFAULT NOW(), -- Indexes CONSTRAINT fk_tenant FOREIGN KEY (tenant_id) REFERENCES identity.tenants(id), CONSTRAINT fk_agent FOREIGN KEY (agent_id) REFERENCES mcp.mcp_agents(id) ); CREATE INDEX idx_audit_tenant_timestamp ON mcp.mcp_audit_logs(tenant_id, timestamp DESC); CREATE INDEX idx_audit_agent_timestamp ON mcp.mcp_audit_logs(agent_id, timestamp DESC); CREATE INDEX idx_audit_operation ON mcp.mcp_audit_logs(operation_type, timestamp DESC); ``` ### 7.2 Audit Middleware ```csharp public class McpAuditMiddleware { private readonly RequestDelegate _next; public async Task InvokeAsync(HttpContext context, IMcpAuditLogRepository auditRepo) { var startTime = Stopwatch.GetTimestamp(); // Capture request var agentId = context.User.FindFirst("agent_id")?.Value; var tenantId = context.User.FindFirst("tenant_id")?.Value; var requestBody = await CaptureRequestBodyAsync(context.Request); // Execute request Exception? exception = null; try { await _next(context); } catch (Exception ex) { exception = ex; throw; } finally { // Calculate duration var elapsedMs = (int)((Stopwatch.GetTimestamp() - startTime) * 1000.0 / Stopwatch.Frequency); // Create audit log var auditLog = McpAuditLog.Create( tenantId: Guid.Parse(tenantId), agentId: Guid.Parse(agentId), operationType: context.Request.Path, resourceUri: ExtractResourceUri(requestBody), toolName: ExtractToolName(requestBody), inputParametersJson: requestBody, isSuccess: exception == null && context.Response.StatusCode < 400, errorMessage: exception?.Message, httpStatusCode: context.Response.StatusCode, durationMs: elapsedMs, clientIpAddress: context.Connection.RemoteIpAddress?.ToString(), userAgent: context.Request.Headers.UserAgent.ToString()); await auditRepo.AddAsync(auditLog); } } } ``` ### 7.3 Observability Metrics **Key Metrics to Track:** 1. **Request Volume** - Total MCP requests per minute - Requests by operation type (resources/read, tools/call) - Requests by agent 2. **Diff Preview Metrics** - Diff previews generated per hour - Approval rate (approved / total) - Rejection rate - Average time to approval - Expired previews (not approved in time) 3. **Performance** - Average response time by operation - P50, P95, P99 latencies - Database query performance 4. **Security** - Failed authentication attempts - Rate limit violations - Permission denial rate 5. **Business Metrics** - Active AI agents per tenant - Most-used tools - Most-accessed resources **Implementation (Prometheus + Grafana):** ```csharp public class McpMetrics { private static readonly Counter RequestCounter = Metrics.CreateCounter( "mcp_requests_total", "Total MCP requests", new CounterConfiguration { LabelNames = new[] { "operation_type", "agent_type", "status" } }); private static readonly Histogram RequestDuration = Metrics.CreateHistogram( "mcp_request_duration_ms", "MCP request duration in milliseconds", new HistogramConfiguration { LabelNames = new[] { "operation_type" }, Buckets = Histogram.ExponentialBuckets(10, 2, 10) }); private static readonly Gauge ActiveDiffPreviews = Metrics.CreateGauge( "mcp_diff_previews_pending", "Number of pending diff previews", new GaugeConfiguration { LabelNames = new[] { "tenant_id" } }); public void RecordRequest(string operationType, string agentType, int statusCode, int durationMs) { RequestCounter.WithLabels(operationType, agentType, statusCode.ToString()).Inc(); RequestDuration.WithLabels(operationType).Observe(durationMs); } } ``` --- ## 8. Database Design ### 8.1 Entity Relationship Diagram ``` ┌─────────────────────────────────────────────────────────────┐ │ identity.tenants │ │ id, name, slug, status, subscription_plan │ └────────────────────────┬────────────────────────────────────┘ │ │ 1:N ▼ ┌─────────────────────────────────────────────────────────────┐ │ mcp.mcp_agents │ │ id, tenant_id, agent_name, agent_type, api_key_hash, │ │ permission_level, allowed_resources, allowed_tools, │ │ is_active, created_at, last_used_at │ └────────────────────────┬────────────────────────────────────┘ │ │ 1:N ▼ ┌─────────────────────────────────────────────────────────────┐ │ mcp.mcp_diff_previews │ │ id, tenant_id, agent_id, tool_name, input_parameters_json, │ │ operation, entity_type, entity_id, before_state_json, │ │ after_state_json, diff_json, risk_level, status, │ │ approved_by, approved_at, rejected_by, rejected_at, │ │ is_committed, committed_entity_id, rollback_token, │ │ created_at, expires_at │ └─────────────────────────────────────────────────────────────┘ │ │ 1:N ▼ ┌─────────────────────────────────────────────────────────────┐ │ mcp.mcp_audit_logs │ │ id, tenant_id, agent_id, operation_type, resource_uri, │ │ tool_name, input_parameters_json, is_success, │ │ error_message, http_status_code, diff_preview_id, │ │ duration_ms, client_ip_address, user_agent, timestamp │ └─────────────────────────────────────────────────────────────┘ ``` ### 8.2 Database Schema (SQL) ```sql -- Schema: mcp CREATE SCHEMA IF NOT EXISTS mcp; -- Table: mcp_agents CREATE TABLE mcp.mcp_agents ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, agent_name VARCHAR(200) NOT NULL, agent_type VARCHAR(100) NOT NULL, -- 'Claude', 'ChatGPT', 'Gemini' -- Authentication api_key_hash VARCHAR(512) NOT NULL, api_key_expires_at TIMESTAMP NOT NULL, is_active BOOLEAN NOT NULL DEFAULT true, -- Permissions permission_level VARCHAR(50) NOT NULL DEFAULT 'WriteWithPreview', allowed_resources JSONB NOT NULL DEFAULT '[]', -- ["projects.*", "issues.read"] allowed_tools JSONB NOT NULL DEFAULT '[]', -- ["create_issue", "update_status"] -- Audit created_at TIMESTAMP NOT NULL DEFAULT NOW(), created_by UUID NOT NULL, last_used_at TIMESTAMP, request_count INTEGER NOT NULL DEFAULT 0, -- Constraints CONSTRAINT fk_tenant FOREIGN KEY (tenant_id) REFERENCES identity.tenants(id) ON DELETE CASCADE, CONSTRAINT fk_created_by FOREIGN KEY (created_by) REFERENCES identity.users(id) ); -- Indexes CREATE INDEX idx_agents_tenant ON mcp.mcp_agents(tenant_id, is_active); CREATE INDEX idx_agents_api_key ON mcp.mcp_agents(api_key_hash) WHERE is_active = true; CREATE INDEX idx_agents_last_used ON mcp.mcp_agents(last_used_at DESC); -- Table: mcp_diff_previews CREATE TABLE mcp.mcp_diff_previews ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, agent_id UUID NOT NULL, -- Operation details tool_name VARCHAR(200) NOT NULL, input_parameters_json JSONB NOT NULL, -- Diff details operation VARCHAR(50) NOT NULL, -- 'Create', 'Update', 'Delete' entity_type VARCHAR(100) NOT NULL, -- 'Issue', 'Project', 'Sprint' entity_id UUID, -- NULL for Create operations before_state_json JSONB, -- NULL for Create after_state_json JSONB NOT NULL, diff_json JSONB NOT NULL, -- Structured diff -- Risk assessment risk_level VARCHAR(50) NOT NULL, -- 'Low', 'Medium', 'High', 'Critical' risk_reasons JSONB NOT NULL DEFAULT '[]', -- Approval workflow status VARCHAR(50) NOT NULL DEFAULT 'Pending', -- 'Pending', 'Approved', 'Rejected', 'Expired', 'Committed' approved_by UUID, approved_at TIMESTAMP, rejected_by UUID, rejected_at TIMESTAMP, rejection_reason TEXT, -- Rollback is_committed BOOLEAN NOT NULL DEFAULT false, committed_entity_id UUID, committed_at TIMESTAMP, rollback_token VARCHAR(500), -- Timestamps created_at TIMESTAMP NOT NULL DEFAULT NOW(), expires_at TIMESTAMP NOT NULL DEFAULT (NOW() + INTERVAL '24 hours'), -- Constraints CONSTRAINT fk_tenant FOREIGN KEY (tenant_id) REFERENCES identity.tenants(id) ON DELETE CASCADE, CONSTRAINT fk_agent FOREIGN KEY (agent_id) REFERENCES mcp.mcp_agents(id) ON DELETE CASCADE, CONSTRAINT fk_approved_by FOREIGN KEY (approved_by) REFERENCES identity.users(id), CONSTRAINT fk_rejected_by FOREIGN KEY (rejected_by) REFERENCES identity.users(id) ); -- Indexes CREATE INDEX idx_diff_previews_tenant_status ON mcp.mcp_diff_previews(tenant_id, status, created_at DESC); CREATE INDEX idx_diff_previews_agent ON mcp.mcp_diff_previews(agent_id, created_at DESC); CREATE INDEX idx_diff_previews_expires ON mcp.mcp_diff_previews(expires_at) WHERE status = 'Pending'; CREATE INDEX idx_diff_previews_entity ON mcp.mcp_diff_previews(entity_type, entity_id); -- Table: mcp_audit_logs CREATE TABLE mcp.mcp_audit_logs ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL, agent_id UUID NOT NULL, -- Request details operation_type VARCHAR(100) NOT NULL, -- 'resources/read', 'tools/call' resource_uri VARCHAR(500), tool_name VARCHAR(200), input_parameters_json JSONB, -- Response details is_success BOOLEAN NOT NULL, error_message TEXT, http_status_code INTEGER, -- Diff preview (if applicable) diff_preview_id UUID, diff_status VARCHAR(50), -- 'Pending', 'Approved', 'Rejected' -- Performance duration_ms INTEGER NOT NULL, -- Context client_ip_address VARCHAR(50), user_agent TEXT, timestamp TIMESTAMP NOT NULL DEFAULT NOW(), -- Constraints CONSTRAINT fk_tenant FOREIGN KEY (tenant_id) REFERENCES identity.tenants(id) ON DELETE CASCADE, CONSTRAINT fk_agent FOREIGN KEY (agent_id) REFERENCES mcp.mcp_agents(id) ON DELETE CASCADE, CONSTRAINT fk_diff_preview FOREIGN KEY (diff_preview_id) REFERENCES mcp.mcp_diff_previews(id) ); -- Indexes (optimized for time-series queries) CREATE INDEX idx_audit_tenant_timestamp ON mcp.mcp_audit_logs(tenant_id, timestamp DESC); CREATE INDEX idx_audit_agent_timestamp ON mcp.mcp_audit_logs(agent_id, timestamp DESC); CREATE INDEX idx_audit_operation_timestamp ON mcp.mcp_audit_logs(operation_type, timestamp DESC); CREATE INDEX idx_audit_diff_preview ON mcp.mcp_audit_logs(diff_preview_id) WHERE diff_preview_id IS NOT NULL; -- Partitioning for audit logs (for better performance at scale) -- Future optimization: Partition by month -- CREATE TABLE mcp.mcp_audit_logs_2025_11 PARTITION OF mcp.mcp_audit_logs -- FOR VALUES FROM ('2025-11-01') TO ('2025-12-01'); ``` ### 8.3 Data Retention Policy ```sql -- Automatic cleanup of expired diff previews CREATE OR REPLACE FUNCTION mcp.cleanup_expired_diff_previews() RETURNS void AS $$ BEGIN UPDATE mcp.mcp_diff_previews SET status = 'Expired' WHERE status = 'Pending' AND expires_at < NOW(); END; $$ LANGUAGE plpgsql; -- Schedule cleanup job (run every hour) -- Use pg_cron extension or external scheduler SELECT cron.schedule('cleanup-expired-diffs', '0 * * * *', 'SELECT mcp.cleanup_expired_diff_previews()'); -- Archive old audit logs (retain 90 days) CREATE OR REPLACE FUNCTION mcp.archive_old_audit_logs() RETURNS void AS $$ BEGIN DELETE FROM mcp.mcp_audit_logs WHERE timestamp < NOW() - INTERVAL '90 days'; END; $$ LANGUAGE plpgsql; ``` --- ## 9. Technology Stack & Rationale ### 9.1 Backend Technologies | Component | Technology | Rationale | |-----------|-----------|-----------| | **Runtime** | .NET 9.0 | Already in use, excellent performance, modern C# features | | **Web Framework** | ASP.NET Core | Industry standard, high performance, built-in DI | | **Architecture** | Clean Architecture | Already implemented, promotes testability and maintainability | | **ORM** | Entity Framework Core | Already in use, excellent LINQ support, migration management | | **Database** | PostgreSQL 16+ | Already in use, JSONB for flexible schemas, excellent performance | | **Caching** | Redis | Distributed caching for rate limiting, session storage | | **Messaging** | (Future) RabbitMQ | For async processing, event-driven workflows | ### 9.2 MCP Protocol Libraries | Component | Technology | Rationale | |-----------|-----------|-----------| | **JSON-RPC** | Custom implementation | MCP uses JSON-RPC 2.0, simple to implement in C# | | **JSON Serialization** | System.Text.Json | Built-in, high performance, good for JSONB interop | | **JSON Diff** | JsonDiffPatch.NET | Library for generating JSON diffs | | **Transport** | SSE (Server-Sent Events) | For web-based AI clients, long-lived connections | | **Transport (Future)** | WebSocket | For bidirectional communication, streaming | ### 9.3 Security Libraries | Component | Technology | Rationale | |-----------|-----------|-----------| | **API Key Hashing** | BCrypt.Net-Next | Already in use for passwords, proven security | | **JWT** | Microsoft.IdentityModel.Tokens | Already in use, standard JWT implementation | | **Rate Limiting** | Custom + Redis | Distributed rate limiting for multi-instance scenarios | | **CORS** | ASP.NET Core CORS | Built-in, easy configuration | ### 9.4 Observability | Component | Technology | Rationale | |-----------|-----------|-----------| | **Metrics** | Prometheus + Grafana | Industry standard, rich ecosystem | | **Logging** | Serilog | Structured logging, excellent sink support | | **Tracing** | OpenTelemetry | Distributed tracing, MCP request flow visibility | | **APM** | (Optional) Application Insights | For Azure deployments | ### 9.5 Why NOT Use MCP SDK (TypeScript)? **Reasons:** 1. **Language Mismatch:** ColaFlow backend is .NET, MCP SDK is TypeScript/Node.js 2. **Performance:** .NET offers better performance for backend workloads 3. **Integration:** Easier to integrate with existing Clean Architecture 4. **Control:** Custom implementation gives full control over security and audit **Alternative Approach:** - Implement MCP protocol specification in C# (JSON-RPC over HTTP/SSE) - Provides compatibility with any MCP-compliant client - No need to interop between .NET and Node.js --- ## 10. Implementation Roadmap ### Phase 1: Foundation (2 weeks) **Goal:** Basic MCP Server infrastructure **Deliverables:** 1. ✅ MCP.Domain module - McpAgent aggregate - DiffPreview aggregate - McpAuditLog aggregate - Repositories 2. ✅ MCP.Infrastructure module - Database schema migrations - EF Core configurations - Repository implementations 3. ✅ API Key authentication - ApiKeyAuthenticationHandler - Agent registration endpoints - API key generation utility 4. ✅ Basic audit logging - McpAuditMiddleware - Audit log persistence **Acceptance Criteria:** - Can register an AI agent with API key - Can authenticate using API key - All requests are logged to mcp_audit_logs --- ### Phase 2: Resources Implementation (2 weeks) **Goal:** Expose read-only resources to AI clients **Deliverables:** 1. ✅ Resource Service - IResourceService interface - ResourceService implementation - Resource descriptors (projects, issues, sprints) 2. ✅ JSON-RPC protocol handler - JsonRpcHandler for resources/list - JsonRpcHandler for resources/read 3. ✅ Permission validation - Field-level filtering - Resource-level access control 4. ✅ API endpoints - POST /api/mcp/jsonrpc (JSON-RPC endpoint) - Resource URI routing **Acceptance Criteria:** - AI client can list available resources - AI client can read project data - AI client can read issue data - Sensitive fields are filtered out **Testing:** ```bash # Test resources/list curl -X POST http://localhost:5000/api/mcp/jsonrpc \ -H "X-MCP-API-Key: mcp_dev_..." \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "resources/list" }' # Test resources/read curl -X POST http://localhost:5000/api/mcp/jsonrpc \ -H "X-MCP-API-Key: mcp_dev_..." \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 2, "method": "resources/read", "params": { "uri": "project://abc-123" } }' ``` --- ### Phase 3: Tools & Diff Preview (3 weeks) **Goal:** Implement write operations with diff preview **Deliverables:** 1. ✅ Diff Preview Service - DiffPreviewService implementation - Diff generation algorithm - Risk level calculation 2. ✅ Tool Invocation Service - ToolInvocationService implementation - Tool descriptors (create_issue, update_status, assign_issue) - Integration with Application layer commands 3. ✅ JSON-RPC protocol handler for tools - tools/list - tools/call (generates diff preview) 4. ✅ Diff approval endpoints - POST /api/mcp/diffs/{id}/approve - POST /api/mcp/diffs/{id}/reject - GET /api/mcp/diffs (list pending diffs) **Acceptance Criteria:** - AI client can list available tools - AI client can call create_issue (generates diff preview) - Human can view diff preview in Admin UI - Human can approve diff (commits to database) - Human can reject diff (discards preview) **Testing:** ```bash # Test tools/call (creates diff preview) curl -X POST http://localhost:5000/api/mcp/jsonrpc \ -H "X-MCP-API-Key: mcp_dev_..." \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "create_issue", "arguments": { "projectId": "abc-123", "title": "Implement MCP authentication", "issueType": "Task", "priority": "High" } } }' # Response: { "requiresApproval": true, "previewId": "preview-456", ... } # Approve diff curl -X POST http://localhost:5000/api/mcp/diffs/preview-456/approve \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" ``` --- ### Phase 4: Admin Dashboard (2 weeks) **Goal:** UI for managing AI agents and diff previews **Deliverables:** 1. ✅ AI Agent Management UI - List agents - Register new agent - Regenerate API key - Configure permissions 2. ✅ Diff Preview Dashboard - List pending diffs - View diff details (before/after comparison) - Approve/reject buttons - Diff history 3. ✅ Audit Log Viewer - Filter by agent, operation, date range - View request/response details - Export to CSV **Acceptance Criteria:** - Admin can register AI agents via UI - Admin can view pending diff previews - Admin can approve/reject diffs with visual diff viewer - Admin can view audit logs --- ### Phase 5: Advanced Features (2 weeks) **Goal:** Rollback, rate limiting, advanced tools **Deliverables:** 1. ✅ Rollback Service - Rollback committed diffs - Event sourcing integration 2. ✅ Rate Limiting - Redis-based distributed rate limiter - Per-agent rate limits - Per-operation rate limits 3. ✅ Advanced Tools - log_decision - add_comment - create_sprint 4. ✅ Prompt Templates - Prompt template management - prompts/list - prompts/get **Acceptance Criteria:** - Admin can rollback committed changes - Rate limiting prevents abuse - AI clients can use advanced tools - AI clients can retrieve prompt templates --- ### Total Timeline: 11 weeks (~2.5 months) **Milestones:** - Week 2: Basic MCP Server running - Week 4: AI clients can read resources - Week 7: AI clients can create issues with approval - Week 9: Admin UI complete - Week 11: Production-ready with all features --- ## 11. Risks & Mitigation ### 11.1 Technical Risks #### Risk 1: MCP Protocol Compatibility **Description:** MCP specification may change, breaking compatibility **Impact:** High (requires code changes) **Probability:** Medium (MCP is still evolving) **Mitigation:** - Abstract MCP protocol logic into separate layer - Implement version negotiation in `initialize` call - Subscribe to MCP specification updates - Design protocol handlers to be easily extensible --- #### Risk 2: Diff Preview Accuracy **Description:** Generated diffs may not accurately represent changes **Impact:** High (incorrect approvals/rejections) **Probability:** Medium (complex domain logic) **Mitigation:** - Comprehensive unit tests for diff generation - Dry-run domain commands before generating diffs - Use well-tested JSON diff library (JsonDiffPatch.NET) - Visual diff viewer in Admin UI for human verification --- #### Risk 3: Performance at Scale **Description:** Diff generation and audit logging may slow down at scale **Impact:** Medium (slow response times) **Probability:** Low (with proper optimization) **Mitigation:** - Use async processing for audit logs (fire-and-forget) - Implement database connection pooling - Use Redis for caching frequently accessed resources - Partition audit logs by month (PostgreSQL table partitioning) - Monitor performance metrics (Prometheus) --- #### Risk 4: Security Vulnerabilities **Description:** API key leakage, permission bypass, injection attacks **Impact:** Critical (data breach, unauthorized access) **Probability:** Medium (without proper security practices) **Mitigation:** - **API Key Security:** - Never log API keys in plain text - Use BCrypt hashing for storage - Implement key rotation policy (expire after 90 days) - Rate limiting to prevent brute force - **Permission Validation:** - Validate permissions at every layer (Controller, Service, Repository) - Use global query filters for tenant isolation - Field-level filtering for sensitive data - **Injection Prevention:** - Use parameterized queries (EF Core) - Validate all input with FluentValidation - Sanitize JSON input before deserialization - **Regular Security Audits:** - Dependency scanning (Snyk, Dependabot) - Penetration testing - Code review for security issues --- ### 11.2 Business Risks #### Risk 5: User Adoption **Description:** Users may not trust AI-generated changes **Impact:** High (low adoption) **Probability:** Medium (change management challenge) **Mitigation:** - Start with **read-only mode** (no writes) to build trust - Provide **transparent diff previews** with clear before/after - Implement **risk indicators** (color-coded: green/yellow/red) - Offer **rollback capability** for safety net - User training and documentation --- #### Risk 6: Regulatory Compliance **Description:** AI operations may violate GDPR, SOC2, or industry regulations **Impact:** Critical (legal issues, fines) **Probability:** Low (with proper design) **Mitigation:** - **Complete audit trail** (who, what, when, why) - **Right to be forgotten** (GDPR Article 17) - Soft delete AI-generated data - Provide data export/deletion endpoints - **Data minimization** (only expose necessary fields to AI) - **Consent management** (users opt-in to AI features) - **Data residency** (support region-specific data storage) --- ### 11.3 Operational Risks #### Risk 7: Database Growth **Description:** Audit logs and diff previews grow unbounded **Impact:** Medium (storage costs, slow queries) **Probability:** High (without retention policy) **Mitigation:** - **Data retention policy:** - Archive audit logs after 90 days - Delete expired diff previews after 7 days - Compress historical data - **Partitioning:** - Partition audit logs by month (PostgreSQL) - Use TimescaleDB for time-series data (future) - **Monitoring:** - Alert when table sizes exceed thresholds - Regular database maintenance (VACUUM, ANALYZE) --- #### Risk 8: AI Agent Abuse **Description:** Malicious AI agents spam requests, attempt unauthorized access **Impact:** Medium (resource exhaustion, data exfiltration) **Probability:** Medium (if API keys are leaked) **Mitigation:** - **Rate Limiting:** - 100 requests/min for resources/read - 10 requests/min for tools/call - IP-based rate limiting (fallback) - **Anomaly Detection:** - Alert on unusual request patterns - Automatic suspension of suspicious agents - **API Key Rotation:** - Force rotation every 90 days - Revoke keys on suspicious activity - **CAPTCHA for Registration:** - Human verification when registering new agents --- ## 12. Appendix ### 12.1 MCP Protocol Reference **JSON-RPC 2.0 Request Format:** ```json { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "create_issue", "arguments": { ... } } } ``` **JSON-RPC 2.0 Response Format:** ```json { "jsonrpc": "2.0", "id": 1, "result": { ... } } ``` **Error Response:** ```json { "jsonrpc": "2.0", "id": 1, "error": { "code": -32600, "message": "Invalid Request", "data": { "details": "..." } } } ``` ### 12.2 Example Tool Schemas **create_project:** ```json { "name": "create_project", "description": "Create a new project", "inputSchema": { "type": "object", "properties": { "name": { "type": "string" }, "description": { "type": "string" }, "ownerId": { "type": "string" }, "startDate": { "type": "string", "format": "date" }, "endDate": { "type": "string", "format": "date" } }, "required": ["name", "ownerId"] } } ``` **update_sprint:** ```json { "name": "update_sprint", "description": "Update sprint details", "inputSchema": { "type": "object", "properties": { "sprintId": { "type": "string" }, "name": { "type": "string" }, "startDate": { "type": "string", "format": "date" }, "endDate": { "type": "string", "format": "date" }, "goal": { "type": "string" } }, "required": ["sprintId"] } } ``` ### 12.3 Configuration Examples **appsettings.Mcp.json:** ```json { "Mcp": { "ApiKeyExpirationDays": 90, "DiffPreviewExpirationHours": 24, "RateLimit": { "ResourcesRead": 100, "ToolsCall": 10, "WindowMinutes": 1 }, "AuditLog": { "RetentionDays": 90, "EnablePartitioning": true }, "DefaultPermissions": { "Level": "WriteWithPreview", "AllowedResources": ["projects.*", "issues.*", "sprints.*"], "AllowedTools": ["create_issue", "update_status", "assign_issue"] } } } ``` --- ## Summary This architecture design provides a **comprehensive, secure, and scalable MCP Server** for ColaFlow that: 1. **Integrates seamlessly** with existing Clean Architecture and Identity Module 2. **Prioritizes safety** via diff preview and human approval for all AI writes 3. **Ensures auditability** with complete audit logs and observability 4. **Supports multi-tenancy** with tenant-scoped data isolation 5. **Enables extensibility** for future AI models and integrations **Key Design Decisions:** - Custom MCP protocol implementation in C# (not TypeScript SDK) - BCrypt API key authentication (reuses existing security patterns) - Diff preview workflow (safety-first approach) - PostgreSQL JSONB for flexible diff storage - Redis for distributed rate limiting - Prometheus + Grafana for observability **Next Steps:** 1. Review and approve this architecture document 2. Begin Phase 1 implementation (Foundation) 3. Set up CI/CD pipeline for MCP module 4. Create integration tests for MCP protocol --- **Document Status:** Ready for Review **Reviewers:** Product Manager, Backend Team Lead, Security Team **Approval Required:** Yes --- **Revision History:** | Version | Date | Author | Changes | |---------|------|--------|---------| | 1.0 | 2025-11-04 | System Architect | Initial architecture design |