feat(backend): Implement MCP Protocol Handler (Story 5.1)

Implemented JSON-RPC 2.0 protocol handler for MCP communication, enabling AI agents to communicate with ColaFlow using the Model Context Protocol.

**Implementation:**
- JSON-RPC 2.0 data models (Request, Response, Error, ErrorCode)
- MCP protocol models (Initialize, Capabilities, ClientInfo, ServerInfo)
- McpProtocolHandler with method routing and error handling
- Method handlers: initialize, resources/list, tools/list, tools/call
- ASP.NET Core middleware for /mcp endpoint
- Service registration and dependency injection setup

**Testing:**
- 28 unit tests covering protocol parsing, validation, and error handling
- Integration tests for initialize handshake and error responses
- All tests passing with >80% coverage

**Changes:**
- Created ColaFlow.Modules.Mcp.Contracts project
- Created ColaFlow.Modules.Mcp.Domain project
- Created ColaFlow.Modules.Mcp.Application project
- Created ColaFlow.Modules.Mcp.Infrastructure project
- Created ColaFlow.Modules.Mcp.Tests project
- Registered MCP module in ColaFlow.API Program.cs
- Added /mcp endpoint via middleware

**Acceptance Criteria Met:**
 JSON-RPC 2.0 messages correctly parsed
 Request validation (jsonrpc: "2.0", method, params, id)
 Error responses conform to JSON-RPC 2.0 spec
 Invalid requests return proper error codes (-32700, -32600, -32601, -32602)
 MCP initialize method implemented
 Server capabilities returned (resources, tools, prompts)
 Protocol version negotiation works (1.0)
 Request routing to method handlers
 Unit test coverage > 80%
 All tests passing

**Story**: docs/stories/sprint_5/story_5_1.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Yaojia Wang
2025-11-07 19:38:34 +01:00
parent d3ef2c1441
commit 48a8431e4f
43 changed files with 7003 additions and 0 deletions

View File

@@ -0,0 +1,279 @@
---
story_id: story_5_7
sprint_id: sprint_5
phase: Phase 2 - Resources
status: not_started
priority: P0
story_points: 5
assignee: backend
estimated_days: 2
created_date: 2025-11-06
dependencies: [story_5_5, story_5_2]
---
# Story 5.7: Multi-Tenant Isolation Verification
**Phase**: Phase 2 - Resources (Week 3-4)
**Priority**: P0 CRITICAL
**Estimated Effort**: 5 Story Points (2 days)
## User Story
**As a** Security Engineer
**I want** 100% multi-tenant data isolation for all MCP operations
**So that** AI agents cannot access data from other tenants
## Business Value
Multi-tenant security is CRITICAL for M2. A single data leak could:
- Violate customer trust
- Cause legal liability (GDPR, SOC2)
- Damage brand reputation
- Block enterprise adoption
**This Story ensures zero cross-tenant data access.**
## Acceptance Criteria
### AC1: TenantContext Service
- [ ] Extract `TenantId` from API Key
- [ ] Set `CurrentTenantId` in request context
- [ ] Accessible to all MCP operations
### AC2: Global Query Filters
- [ ] EF Core Global Query Filters applied to all entities
- [ ] All queries automatically filtered by `TenantId`
- [ ] Cannot be disabled by accident
### AC3: Repository Level Isolation
- [ ] All repositories enforce `TenantId` filter
- [ ] No queries bypass tenant check
- [ ] Aggregate roots include `TenantId` property
### AC4: Integration Tests
- [ ] Create 2 test tenants (Tenant A, Tenant B)
- [ ] Create test data in each tenant
- [ ] Test 1: Tenant A API Key cannot read Tenant B projects
- [ ] Test 2: Tenant A API Key cannot read Tenant B issues
- [ ] Test 3: Tenant A API Key cannot read Tenant B users
- [ ] Test 4: Direct ID access fails for other tenant's data
- [ ] Test 5: Search queries never return cross-tenant results
- [ ] All tests must pass (100% isolation)
### AC5: Security Audit
- [ ] Code review by security team
- [ ] Static analysis (no raw SQL without TenantId)
- [ ] Verify all API endpoints enforce tenant filter
## Technical Design
### TenantContext Service
```csharp
public interface ITenantContext
{
Guid CurrentTenantId { get; }
}
public class McpTenantContext : ITenantContext
{
private readonly IHttpContextAccessor _httpContextAccessor;
public Guid CurrentTenantId
{
get
{
var tenantId = _httpContextAccessor.HttpContext?.Items["TenantId"];
if (tenantId == null)
throw new McpUnauthorizedException("Tenant context not set");
return (Guid)tenantId;
}
}
}
```
### Global Query Filters
```csharp
public class ColaFlowDbContext : DbContext
{
private readonly ITenantContext _tenantContext;
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Apply tenant filter to all tenant-scoped entities
modelBuilder.Entity<Project>()
.HasQueryFilter(p => p.TenantId == _tenantContext.CurrentTenantId);
modelBuilder.Entity<Epic>()
.HasQueryFilter(e => e.TenantId == _tenantContext.CurrentTenantId);
modelBuilder.Entity<Story>()
.HasQueryFilter(s => s.TenantId == _tenantContext.CurrentTenantId);
modelBuilder.Entity<WorkTask>()
.HasQueryFilter(t => t.TenantId == _tenantContext.CurrentTenantId);
modelBuilder.Entity<Sprint>()
.HasQueryFilter(s => s.TenantId == _tenantContext.CurrentTenantId);
modelBuilder.Entity<User>()
.HasQueryFilter(u => u.TenantId == _tenantContext.CurrentTenantId);
}
}
```
### Integration Test Example
```csharp
[Fact]
public async Task TenantA_CannotReadTenantB_Projects()
{
// Arrange
var tenantA = await CreateTestTenant("Tenant A");
var tenantB = await CreateTestTenant("Tenant B");
var apiKeyA = await CreateApiKey(tenantA.Id, "API Key A");
var projectB = await CreateProject(tenantB.Id, "Project B");
// Act - Tenant A tries to access Tenant B's project
var result = await CallMcpResource(
apiKeyA,
$"colaflow://projects.get/{projectB.Id}");
// Assert
Assert.Equal(404, result.StatusCode); // NOT 403 (don't leak existence)
Assert.Null(result.Data);
}
[Fact]
public async Task IssuesSearch_NeverReturnsCrossTenant_Results()
{
// Arrange
var tenantA = await CreateTestTenant("Tenant A");
var tenantB = await CreateTestTenant("Tenant B");
await CreateIssue(tenantA.Id, "Issue A");
await CreateIssue(tenantB.Id, "Issue B");
var apiKeyA = await CreateApiKey(tenantA.Id, "API Key A");
// Act - Tenant A searches all issues
var result = await CallMcpResource(
apiKeyA,
"colaflow://issues.search");
// Assert
var issues = result.Data["issues"];
Assert.Single(issues); // Only Tenant A's issue
Assert.Equal("Issue A", issues[0]["title"]);
}
```
## Tasks
### Task 1: TenantContext Service (2 hours)
- [ ] Create `ITenantContext` interface
- [ ] Implement `McpTenantContext` (extract from HttpContext)
- [ ] Register in DI container
- [ ] Update API Key middleware to set TenantId
**Files to Create**:
- `ColaFlow.Modules.Mcp/Services/McpTenantContext.cs`
### Task 2: Global Query Filters (3 hours)
- [ ] Add `TenantId` to all aggregate roots (if missing)
- [ ] Configure Global Query Filters in DbContext
- [ ] Test filters apply automatically
- [ ] Verify cannot be disabled
**Files to Modify**:
- `ColaFlow.Infrastructure/Data/ColaFlowDbContext.cs`
### Task 3: Repository Validation (2 hours)
- [ ] Audit all repository methods
- [ ] Ensure all queries include TenantId
- [ ] Add TenantId to method signatures if needed
### Task 4: Integration Tests - Cross-Tenant Access (6 hours)
- [ ] Create multi-tenant test infrastructure
- [ ] Test 1: Projects cross-tenant access
- [ ] Test 2: Issues cross-tenant access
- [ ] Test 3: Users cross-tenant access
- [ ] Test 4: Direct ID access (404, not 403)
- [ ] Test 5: Search never returns cross-tenant results
**Files to Create**:
- `ColaFlow.Modules.Mcp.Tests/Integration/MultiTenantIsolationTests.cs`
### Task 5: Security Audit (3 hours)
- [ ] Code review by security team
- [ ] Static analysis (grep for raw SQL)
- [ ] Verify all API endpoints use TenantContext
- [ ] Document security architecture
**Files to Create**:
- `docs/security/multi-tenant-isolation-audit.md`
## Testing Strategy
### Integration Tests (Critical)
- 2 test tenants with separate data
- 10+ test scenarios covering all Resources
- 100% isolation verified
- Tests must fail if isolation broken
### Security Audit Checklist
- [ ] All entities have TenantId property
- [ ] Global Query Filters configured
- [ ] No raw SQL without TenantId
- [ ] All API endpoints use TenantContext
- [ ] Return 404 (not 403) for cross-tenant access
- [ ] Audit logs include TenantId
## Dependencies
**Prerequisites**:
- Story 5.2 (API Key Management) - API Key linked to Tenant
- Story 5.5 (Core Resources) - Resources to test
**Critical Path**: BLOCKS M2 production deployment
## Risks & Mitigation
| Risk | Impact | Probability | Mitigation |
|------|--------|-------------|------------|
| Tenant leak vulnerability | CRITICAL | Low | 100% test coverage, code review |
| Global filter bypass | HIGH | Low | EF Core best practices, testing |
| Performance impact | Medium | Low | Indexed TenantId columns |
## Definition of Done
- [ ] TenantContext service working
- [ ] Global Query Filters applied
- [ ] ALL integration tests passing (100%)
- [ ] Security audit complete (no findings)
- [ ] Code reviewed by security team
- [ ] Documentation updated
## Notes
### Why This Story Matters
- **CRITICAL SECURITY**: Single most important security Story in M2
- **Compliance**: Required for GDPR, SOC2, enterprise customers
- **Trust**: Multi-tenant leaks destroy customer trust
- **Legal**: Data breaches have severe legal consequences
### Security Best Practices
1. **Defense in Depth**: Multiple layers (API Key, TenantContext, Global Filters)
2. **Fail Closed**: No data if TenantId missing (throw exception)
3. **404 not 403**: Don't leak existence of other tenant's data
4. **Audit Everything**: Log all tenant context access
5. **Test Religiously**: 100% integration test coverage
### Reference Materials
- Multi-Tenant Security: https://learn.microsoft.com/en-us/ef/core/querying/filters
- Sprint 5 Plan: `docs/plans/sprint_5.md`