2254 lines
73 KiB
Markdown
2254 lines
73 KiB
Markdown
# 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 |
|
|
|