---
story_id: story_15
sprint_id: sprint_5
parent_story_id: story_0
status: not_started
priority: P0
assignee: backend
created_date: 2025-11-09
estimated_days: 5
phase: 3
---

# Story 15: Resource Migration to SDK Attributes

**Parent Epic**: [Story 0](sprint_5_story_0.md) - Integrate Microsoft .NET MCP SDK
**Phase**: 3 - Resource Migration (Week 5)
**Priority**: P0 - Critical
**Estimated Effort**: 5 days (1 week)
**Dependencies**: Story 14 (Tool migration complete)

## User Story

**As** a backend developer,
**I want** to migrate all 11 MCP Resources from custom implementation to SDK attribute-based registration,
**So that** we reduce boilerplate code while preserving multi-tenant isolation and Redis caching.

## Business Value

- **Code Reduction**: Remove 200-250 lines of custom Resource infrastructure
- **Performance**: SDK optimizations improve query speed by 30-40%
- **Caching**: Preserve Redis caching with minimal changes
- **Multi-Tenant**: Maintain 100% tenant isolation

## Resources to Migrate (11 Total)

### Core Resources (P0)
1. `projects.list` - List all projects
2. `projects.get/{id}` - Get project details
3. `issues.search` - Search issues with filters
4. `issues.get/{id}` - Get issue details
5. `sprints.current` - Get active Sprint

### Supporting Resources (P1)
6. `sprints.list` - List all Sprints
7. `users.list` - List team members
8. `docs.prd/{projectId}` - Get PRD document

### Advanced Resources (P2)
9. `reports.daily/{date}` - Daily status report
10. `reports.velocity` - Sprint velocity report
11. `audit.history/{entityId}` - Audit log history

## Migration Pattern

### Before (Custom Implementation)
```csharp
public class ProjectsListResource : IMcpResource
{
    public string Uri => "colaflow://projects.list";
    public string Name => "Projects List";
    public string Description => "List all projects";
    public string MimeType => "application/json";

    private readonly IProjectRepository _repo;
    private readonly ITenantContext _tenant;

    public async Task<McpResourceContent> GetContentAsync(
        McpResourceRequest request,
        CancellationToken ct)
    {
        var projects = await _repo.GetAllAsync(_tenant.CurrentTenantId);
        return new McpResourceContent
        {
            Uri = Uri,
            MimeType = MimeType,
            Text = JsonSerializer.Serialize(new { projects })
        };
    }
}
```

### After (SDK Attributes)
```csharp
[McpResource(
    Uri = "colaflow://projects.list",
    Name = "Projects List",
    Description = "List all projects in current tenant",
    MimeType = "application/json"
)]
public class ProjectsListResource
{
    private readonly IProjectRepository _repo;
    private readonly ITenantContext _tenant;
    private readonly IDistributedCache _cache; // Redis preserved

    public ProjectsListResource(
        IProjectRepository repo,
        ITenantContext tenant,
        IDistributedCache cache)
    {
        _repo = repo;
        _tenant = tenant;
        _cache = cache;
    }

    public async Task<McpResourceContent> GetContentAsync(
        McpContext context,
        CancellationToken cancellationToken)
    {
        // Preserve Redis caching
        var cacheKey = $"mcp:projects:list:{_tenant.CurrentTenantId}";
        var cached = await _cache.GetStringAsync(cacheKey, cancellationToken);

        if (cached != null)
        {
            return McpResourceContent.Json(cached);
        }

        // Preserve multi-tenant filtering
        var projects = await _repo.GetAllAsync(
            _tenant.CurrentTenantId,
            cancellationToken);

        var json = JsonSerializer.Serialize(new { projects });

        // Cache for 5 minutes
        await _cache.SetStringAsync(
            cacheKey,
            json,
            new DistributedCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
            },
            cancellationToken);

        return McpResourceContent.Json(json);
    }
}
```

## Key Changes

### What Changes
- ✅ Resource registration (custom → SDK attributes)
- ✅ URI declaration (property → attribute)
- ✅ Metadata (Name, Description in attribute)

### What Stays the Same
- ✅ TenantContext filtering (preserved)
- ✅ Redis caching logic (preserved)
- ✅ Query logic (unchanged)
- ✅ Response format (unchanged)

## Acceptance Criteria

- [ ] All 11 Resources migrated to `[McpResource]` attributes
- [ ] Multi-tenant isolation 100% verified
- [ ] Redis cache hit rate >80% maintained
- [ ] Response time <200ms (P95)
- [ ] Integration tests pass
- [ ] Claude Desktop can query all Resources
- [ ] Zero breaking changes for MCP clients

## Tasks Breakdown

- [ ] [Task 1](sprint_5_story_15_task_1.md) - Migrate P0 Resources (projects, issues, sprints.current) - 2 days
- [ ] [Task 2](sprint_5_story_15_task_2.md) - Migrate P1 Resources (sprints.list, users, docs) - 1 day
- [ ] [Task 3](sprint_5_story_15_task_3.md) - Migrate P2 Resources (reports, audit) - 1 day
- [ ] [Task 4](sprint_5_story_15_task_4.md) - Multi-tenant isolation testing and cache verification - 1 day

**Progress**: 0/4 tasks completed (0%)

## Testing Strategy

### Multi-Tenant Isolation Tests
- Verify TenantContext extraction from McpContext
- Test cross-tenant access attempts (should return 404)
- Validate Global Query Filters applied

### Cache Performance Tests
- Measure cache hit rate (target: >80%)
- Verify cache invalidation on data changes
- Test TTL expiration

### Integration Tests
- Claude Desktop resource queries
- Query parameter handling
- Error responses (404, 403, 500)

## Success Metrics

- **Code Reduction**: -200 lines (Resource infrastructure)
- **Performance**: +30-40% faster queries
- **Cache Hit Rate**: Maintain >80%
- **Multi-Tenant**: 100% isolation verified

## Definition of Done

- [ ] All 11 Resources migrated to SDK attributes
- [ ] Multi-tenant isolation 100% verified
- [ ] Redis caching verified (>80% hit rate)
- [ ] Performance benchmarks show ≥30% improvement
- [ ] Integration tests pass
- [ ] Code reviewed and approved

---

**Created**: 2025-11-09 by Product Manager Agent
**Owner**: Backend Team
**Start Date**: 2025-12-23 (Week 5)
**Target Date**: 2025-12-27 (End of Week 5)
**Status**: Not Started
