# 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;

/// <summary>
/// Represents an AI Agent registered to access ColaFlow via MCP
/// </summary>
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<string> AllowedResources { get; private set; } // ["projects.*", "issues.read"]
    public List<string> 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<string> resources, List<string> 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;

/// <summary>
/// Represents a diff preview for AI-initiated write operations
/// </summary>
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<string> 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;

/// <summary>
/// Complete audit trail for all MCP operations
/// </summary>
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
{
    /// <summary>
    /// List all available resources for the current AI Agent
    /// </summary>
    Task<List<ResourceDescriptor>> ListResourcesAsync(
        TenantId tenantId,
        Guid agentId,
        CancellationToken cancellationToken = default);

    /// <summary>
    /// Read a specific resource
    /// </summary>
    Task<ResourceContent> 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
{
    /// <summary>
    /// List all available tools for the current AI Agent
    /// </summary>
    Task<List<ToolDescriptor>> ListToolsAsync(
        TenantId tenantId,
        Guid agentId,
        CancellationToken cancellationToken = default);

    /// <summary>
    /// Invoke a tool (generates diff preview for write operations)
    /// </summary>
    Task<ToolInvocationResult> InvokeToolAsync(
        string toolName,
        Dictionary<string, object> 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
{
    /// <summary>
    /// Generate a diff preview for a proposed write operation
    /// </summary>
    Task<DiffPreview> GenerateDiffAsync(
        string toolName,
        Dictionary<string, object> arguments,
        TenantId tenantId,
        Guid agentId,
        CancellationToken cancellationToken = default);

    /// <summary>
    /// Get diff preview by ID
    /// </summary>
    Task<DiffPreview?> GetDiffPreviewAsync(
        Guid previewId,
        TenantId tenantId,
        CancellationToken cancellationToken = default);

    /// <summary>
    /// Approve and commit a diff preview
    /// </summary>
    Task<CommitResult> ApproveAndCommitAsync(
        Guid previewId,
        Guid approvedBy,
        TenantId tenantId,
        CancellationToken cancellationToken = default);

    /// <summary>
    /// Reject a diff preview
    /// </summary>
    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<JsonRpcResponse> 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<McpAgent>
{
    public void Configure(EntityTypeBuilder<McpAgent> 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<string>();

        // 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
{
    /// <summary>
    /// Rollback a committed diff preview
    /// </summary>
    Task<RollbackResult> RollbackAsync(
        Guid previewId,
        Guid requestedBy,
        string reason,
        CancellationToken cancellationToken = default);
}

public class RollbackService : IRollbackService
{
    public async Task<RollbackResult> 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<ApiKeyAuthenticationOptions>
{
    protected override async Task<AuthenticateResult> 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_<environment>_<random32chars>
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<ApiKeyAuthenticationOptions, ApiKeyAuthenticationHandler>("ApiKey", null)
.AddJwtBearer(/* ... existing JWT config ... */);
```

### 6.2 Authorization & Permissions

#### 6.2.1 Permission Levels

```csharp
public enum McpPermissionLevel
{
    /// <summary>
    /// Read-only access to resources
    /// - Can call resources/list, resources/read
    /// - Cannot call any tools
    /// </summary>
    ReadOnly = 1,

    /// <summary>
    /// Read + Write with preview (DEFAULT for AI Agents)
    /// - Can read all resources
    /// - Can call tools, but generates diff preview
    /// - All writes require human approval
    /// </summary>
    WriteWithPreview = 2,

    /// <summary>
    /// Direct write (DANGEROUS, TenantOwner approval required)
    /// - Can read all resources
    /// - Can write directly without approval
    /// - Should be used only for trusted automation
    /// </summary>
    DirectWrite = 3
}
```

#### 6.2.2 Field-Level Permissions

```csharp
public class FieldLevelPermissionFilter
{
    private static readonly HashSet<string> 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<bool> 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<McpAgent>()
            .HasQueryFilter(e => e.TenantId == _tenantContext.TenantId);

        modelBuilder.Entity<DiffPreview>()
            .HasQueryFilter(e => e.TenantId == _tenantContext.TenantId);

        modelBuilder.Entity<McpAuditLog>()
            .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 <human-jwt-token>" \
  -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 |

