Saltar a contenido

Estado de fase (MASTER-00): POC — documentación involucrada en la validación cuantitativa de contexto. Gobernanza: MASTER-00.

Backend Spec: MCP Task Context Bundle Extraction (mcp.get_task_context)

1. Overview & Objectives

This specification defines the backend endpoint (getTaskContext) for extracting a focused Markdown context bundle for AI coding agents (Claude Code, Cursor, Junie). It processes POST /api/v1/mcp/task-context, authenticates via X-MCP-API-KEY or Bearer JWT, evaluates subscription entitlement (capping maxDepth = 2 for Free tier users), performs reverse topological graph traversal via :docugraph-core, prunes unreferenced subtrees, and returns a formatted context bundle in $<150$ ms.

2. API Endpoint Signature & Traceability

  • HTTP Method & Path: POST /api/v1/mcp/task-context
  • operationId: getTaskContext
  • Upstream Requirements: US-04, IX-04, SCHEMA-01
  • Primary Service Class: McpContextService.kt
  • Ktor Route Handler: McpRoutes.kt

3. Data Models & Kotlin Data Classes

package io.docugraph.backend.models.mcp

import kotlinx.serialization.Serializable

@Serializable
data class TaskContextRequest(
    val taskId: String,
    val maxDepth: Int = 5,
    val includeSourceCode: Boolean = true
)

@Serializable
data class TaskContextResponse(
    val taskId: String,
    val requestedMaxDepth: Int,
    val effectiveMaxDepth: Int,
    val truncated: Boolean,
    val markdownBundle: String,
    val collectedNodes: List<String>
)

4. Sequence & Control Flow

MCP Client / FE-04     Ktor McpRoute           McpContextService       Core Engine             PostgreSQL DB
      |                      |                        |                    |                       |
      |-- POST /mcp/task-context -------------------->|                    |                       |
      |   (taskId, maxDepth) |-- authenticateKey ---->|------------------------------------------>| SELECT mcp_api_keys
      |                      |                        |<- auth & user_id --------------------------|
      |                      |                        |                    |                       |
      |                      |                        |-- check sub tier ------------------------->| SELECT subscriptions
      |                      |                        |<- free / pro_tier -------------------------|
      |                      |                        |                    |                       |
      |                      |                        |-- depth clamp:     |                       |
      |                      |                        |   if free: depth=2 |                       |
      |                      |                        |                    |                       |
      |                      |                        |-- graphTraversal ->|                       |
      |                      |                        |   reverse topo     |-- SELECT nodes/edges->|
      |                      |                        |                    |<- sub-graph data -----|
      |                      |                        |<- markdown bundle -|                       |
      |<- 200 TaskContextResponse --------------------|                    |                       |

5. Reverse Topological Traversal Algorithm

Given a starting target node $v_0 = \text{taskId}$: 1. Perform Breadth-First Search (BFS) / Reverse Depth-First Search (DFS) following incoming depends_on directed edges. 2. Maintain a set of visited node IDs and record path depth $d$. 3. Stop traversal along any branch when $d > \text{effectiveMaxDepth}$. 4. Sort collected nodes in topological order: base dependencies first, target node last. 5. Format output bundle:

# TASK CONTEXT BUNDLE: <taskId>

## 1. Upstream Dependency: <dep_id_1>
... (Markdown content) ...

## 2. Upstream Dependency: <dep_id_2>
... (Markdown content) ...

## 3. Target Task: <taskId>
... (Markdown content) ...

6. Freemium Entitlement Depth Enforcement

val subscription = subscriptionRepository.findByUserId(userId)
val isPro = subscription?.entitlementId == "pro_tier" && subscription.status == "active"

val (effectiveMaxDepth, truncated) = if (isPro) {
    Pair(request.maxDepth, false)
} else {
    val clampedDepth = minOf(request.maxDepth, 2)
    val wasTruncated = request.maxDepth > 2
    Pair(clampedDepth, wasTruncated)
}

7. Database Interaction & SQL Queries

SELECT k.user_id, k.permissions, s.entitlement_id, s.status
FROM mcp_api_keys k
JOIN subscriptions s ON s.user_id = k.user_id
WHERE k.api_key_hash = :keyHash
  AND k.is_revoked = FALSE
  AND (k.expires_at IS NULL OR k.expires_at > CURRENT_TIMESTAMP);

8. Error Handling & HTTP Status Codes

Error Condition HTTP Code Internal Code Response Message
Invalid API key or missing authorization 401 Unauthorized INVALID_API_KEY "Invalid or revoked MCP API Key."
Target taskId not found in database 404 Not Found TASK_NOT_FOUND "Atomic task ID not found in project index."
Traversal timeout (>150ms) 504 Gateway Timeout CONTEXT_TIMEOUT "Context bundle generation timed out."

9. Verification & Test Plan

  • Unit Test: Test graph traversal depth clamping for Free (effectiveMaxDepth = 2) vs Pro (effectiveMaxDepth = requested).
  • Performance Test: Measure context bundle assembly time for graph with 500 nodes (must complete in $<150$ms).