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>
8.6 KiB
8.6 KiB
story_id, sprint_id, phase, status, priority, story_points, assignee, estimated_days, created_date, dependencies
| story_id | sprint_id | phase | status | priority | story_points | assignee | estimated_days | created_date | dependencies | |
|---|---|---|---|---|---|---|---|---|---|---|
| story_5_8 | sprint_5 | Phase 2 - Resources | not_started | P1 | 5 | backend | 2 | 2025-11-06 |
|
Story 5.8: Redis Caching Integration
Phase: Phase 2 - Resources (Week 3-4) Priority: P1 HIGH Estimated Effort: 5 Story Points (2 days)
User Story
As a System Architect I want Redis caching for frequently accessed MCP Resources So that AI agents get fast responses and database load is reduced
Business Value
Performance optimization delivers:
- 30-50% faster response times (from 200ms to 80ms)
- Reduced database load (80% cache hit rate target)
- Better scalability (support more concurrent AI agents)
- Improved user experience (faster AI responses)
Acceptance Criteria
AC1: Redis Integration
- Redis client configured (StackExchange.Redis)
- Connection pooling and retry logic
- Health checks for Redis availability
AC2: Cache-Aside Pattern
- Check cache before database query
- Populate cache on cache miss
- Return cached data on cache hit
AC3: TTL Configuration
projects.list: 5 minutes TTLusers.list: 5 minutes TTLsprints.current: 2 minutes TTLissues.search: 2 minutes TTL (cache by query params)
AC4: Cache Invalidation
- Invalidate on data updates (domain events)
- Manual cache clear API endpoint
- Tenant-scoped cache keys
AC5: Performance Metrics
- Track cache hit rate (target > 80%)
- Track cache miss rate
- Track response time improvement
- Export metrics for monitoring
AC6: Testing
- Unit tests for caching logic
- Integration tests (cache hit/miss scenarios)
- Performance benchmarks (with/without cache)
Technical Design
Cache Service Interface
public interface IMcpCacheService
{
Task<T?> GetAsync<T>(string key, CancellationToken cancellationToken = default);
Task SetAsync<T>(string key, T value, TimeSpan ttl, CancellationToken cancellationToken = default);
Task RemoveAsync(string key, CancellationToken cancellationToken = default);
Task RemoveByPatternAsync(string pattern, CancellationToken cancellationToken = default);
}
public class RedisMcpCacheService : IMcpCacheService
{
private readonly IConnectionMultiplexer _redis;
private readonly ILogger<RedisMcpCacheService> _logger;
public async Task<T?> GetAsync<T>(string key, CancellationToken ct)
{
var db = _redis.GetDatabase();
var value = await db.StringGetAsync(key);
if (!value.HasValue)
{
_logger.LogDebug("Cache miss: {Key}", key);
return default;
}
_logger.LogDebug("Cache hit: {Key}", key);
return JsonSerializer.Deserialize<T>(value!);
}
public async Task SetAsync<T>(string key, T value, TimeSpan ttl, CancellationToken ct)
{
var db = _redis.GetDatabase();
var json = JsonSerializer.Serialize(value);
await db.StringSetAsync(key, json, ttl);
_logger.LogDebug("Cache set: {Key} (TTL: {TTL}s)", key, ttl.TotalSeconds);
}
public async Task RemoveAsync(string key, CancellationToken ct)
{
var db = _redis.GetDatabase();
await db.KeyDeleteAsync(key);
_logger.LogDebug("Cache removed: {Key}", key);
}
public async Task RemoveByPatternAsync(string pattern, CancellationToken ct)
{
var server = _redis.GetServer(_redis.GetEndPoints().First());
var keys = server.Keys(pattern: pattern);
var db = _redis.GetDatabase();
foreach (var key in keys)
{
await db.KeyDeleteAsync(key);
}
_logger.LogDebug("Cache removed by pattern: {Pattern}", pattern);
}
}
Cached Resource Example
public class ProjectsListResource : IMcpResource
{
private readonly IProjectRepository _projectRepo;
private readonly ITenantContext _tenantContext;
private readonly IMcpCacheService _cache;
public async Task<McpResourceContent> GetContentAsync(
McpResourceRequest request,
CancellationToken cancellationToken)
{
var tenantId = _tenantContext.CurrentTenantId;
var cacheKey = $"mcp:{tenantId}:projects.list";
// Try cache first
var cached = await _cache.GetAsync<ProjectListDto[]>(cacheKey, cancellationToken);
if (cached != null)
{
return CreateResponse(cached);
}
// Cache miss - query database
var projects = await _projectRepo.GetAllAsync(tenantId, cancellationToken);
var projectDtos = projects.Select(MapToDto).ToArray();
// Populate cache
await _cache.SetAsync(cacheKey, projectDtos, TimeSpan.FromMinutes(5), cancellationToken);
return CreateResponse(projectDtos);
}
}
Cache Key Format
mcp:{tenantId}:{resourceUri}[:{params_hash}]
Examples:
- mcp:00000000-0000-0000-0000-000000000001:projects.list
- mcp:00000000-0000-0000-0000-000000000001:issues.search:abc123
- mcp:00000000-0000-0000-0000-000000000001:sprints.current
Cache Invalidation (Domain Events)
public class ProjectUpdatedEventHandler : INotificationHandler<ProjectUpdatedEvent>
{
private readonly IMcpCacheService _cache;
public async Task Handle(ProjectUpdatedEvent e, CancellationToken ct)
{
// Invalidate projects.list for this tenant
await _cache.RemoveAsync($"mcp:{e.TenantId}:projects.list", ct);
// Invalidate specific project
await _cache.RemoveAsync($"mcp:{e.TenantId}:projects.get/{e.ProjectId}", ct);
}
}
Tasks
Task 1: Redis Client Setup (2 hours)
- Add StackExchange.Redis NuGet package
- Configure connection string in
appsettings.json - Create
RedisMcpCacheServiceimplementation - Add health checks
Files to Create:
ColaFlow.Modules.Mcp/Services/RedisMcpCacheService.cs
Task 2: Cache Service Interface (2 hours)
- Create
IMcpCacheServiceinterface - Implement Get/Set/Remove methods
- Add logging and metrics
Files to Create:
ColaFlow.Modules.Mcp/Contracts/IMcpCacheService.cs
Task 3: Update Resources with Caching (6 hours)
- Update
ProjectsListResourceto use cache - Update
UsersListResourceto use cache - Update
SprintsCurrentResourceto use cache - Update
IssuesSearchResourceto use cache (with query hash)
Task 4: Cache Invalidation Event Handlers (3 hours)
- ProjectCreated/Updated/Deleted → invalidate projects cache
- EpicCreated/Updated/Deleted → invalidate issues cache
- StoryCreated/Updated/Deleted → invalidate issues cache
- TaskCreated/Updated/Deleted → invalidate issues cache
Files to Create:
ColaFlow.Modules.Mcp/EventHandlers/CacheInvalidationEventHandlers.cs
Task 5: Performance Metrics (2 hours)
- Track cache hit/miss rates
- Track response time improvement
- Log metrics to structured logs
Task 6: Unit & Integration Tests (4 hours)
- Test cache hit scenario
- Test cache miss scenario
- Test cache invalidation
- Performance benchmarks (with/without cache)
Files to Create:
ColaFlow.Modules.Mcp.Tests/Services/RedisMcpCacheServiceTests.csColaFlow.Modules.Mcp.Tests/Integration/CachingIntegrationTests.cs
Testing Strategy
Performance Benchmarks
Scenario: projects.list (100 projects)
- Without cache: 180ms (database query)
- With cache: 60ms (67% improvement)
- Cache hit rate: 85%
Integration Tests
- Test cache hit after first request
- Test cache miss on first request
- Test cache invalidation on update
- Test TTL expiration
Dependencies
Prerequisites:
- Story 5.5 (Core Resources) - Resources to cache
- Redis server running (Docker or cloud)
Optional: Not blocking for M2 MVP
Risks & Mitigation
| Risk | Impact | Probability | Mitigation |
|---|---|---|---|
| Redis unavailable | Medium | Low | Fallback to database, circuit breaker |
| Cache inconsistency | Medium | Medium | Short TTL, event-driven invalidation |
| Memory usage | Low | Low | Set max memory limit in Redis |
Definition of Done
- Redis integration working
- Cache hit rate > 80%
- Response time improved by 30%+
- Cache invalidation working
- All tests passing
- Performance benchmarks documented
Notes
Why This Story Matters
- Performance: 30-50% faster response times
- Scalability: Reduce database load by 80%
- Cost: Lower database resource usage
- User Experience: Faster AI responses
Redis Configuration
{
"Redis": {
"ConnectionString": "localhost:6379",
"InstanceName": "colaflow:",
"DefaultTTL": 300
}
}