Skip to main content
Glama

backlog_context

Retrieve comprehensive task context including relationships, references, and activity to understand dependencies before starting work.

Instructions

Get full context for working on a task — parent epic, sibling tasks, children, cross-referenced items, reverse references (who references me), ancestors, descendants, related resources, semantically related items, recent activity, and session memory in a single call. Use this before starting work on any task to understand its context.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
task_idNoTask or epic ID to get context for. Example: "TASK-0042" or "EPIC-0005". Mutually exclusive with query.
queryNoNatural language query to find the most relevant entity. Example: "search ranking improvements". Mutually exclusive with task_id.
depthNoRelational expansion depth. 1 = direct relations (default). 2 = grandparent/grandchildren. 3 = three hops.
max_tokensNoToken budget for the response. Default: 4000. Increase for more detail, decrease for conciseness.
include_relatedNoInclude semantically related items (default: true). Set false to skip semantic search and reduce latency.
include_activityNoInclude recent activity timeline (default: true). Set false to skip activity and reduce response size.

Implementation Reference

  • Main tool registration and handler function for backlog_context. Defines the tool with input schema validation (task_id, query, depth, max_tokens, include_related, include_activity) and executes the context hydration pipeline via hydrateContext(). Returns formatted JSON response with all context data.
    export function registerBacklogContextTool(server: McpServer) {
      server.registerTool(
        'backlog_context',
        {
          description: 'Get full context for working on a task — parent epic, sibling tasks, children, cross-referenced items, reverse references (who references me), ancestors, descendants, related resources, semantically related items, recent activity, and session memory in a single call. Use this before starting work on any task to understand its context.',
          inputSchema: z.object({
            task_id: z.string().optional().describe('Task or epic ID to get context for. Example: "TASK-0042" or "EPIC-0005". Mutually exclusive with query.'),
            query: z.string().optional().describe('Natural language query to find the most relevant entity. Example: "search ranking improvements". Mutually exclusive with task_id.'),
            depth: z.number().min(1).max(3).optional().describe('Relational expansion depth. 1 = direct relations (default). 2 = grandparent/grandchildren. 3 = three hops.'),
            max_tokens: z.number().min(500).max(32000).optional().describe('Token budget for the response. Default: 4000. Increase for more detail, decrease for conciseness.'),
            include_related: z.boolean().optional().describe('Include semantically related items (default: true). Set false to skip semantic search and reduce latency.'),
            include_activity: z.boolean().optional().describe('Include recent activity timeline (default: true). Set false to skip activity and reduce response size.'),
          }),
        },
        async ({ task_id, query, depth, max_tokens, include_related, include_activity }) => {
          if (!task_id && !query) {
            return {
              content: [{ type: 'text', text: JSON.stringify({ error: 'Either task_id or query is required' }) }],
              isError: true,
            };
          }
    
          const result = await hydrateContext(
            {
              task_id,
              query,
              depth: depth ?? 1,
              max_tokens: max_tokens ?? 4000,
              include_related: include_related ?? true,
              include_activity: include_activity ?? true,
            },
            {
              getTask: (id) => storage.get(id),
              listTasks: (filter) => storage.listSync(filter),
              listResources: () => resourceManager.list(),
              searchUnified: async (q, options) => storage.searchUnified(q, options),
              readOperations: (options) => operationLogger.read(options),
            },
          );
    
          if (!result) {
            const target = task_id || query;
            return {
              content: [{ type: 'text', text: JSON.stringify({ error: `Entity not found: ${target}` }) }],
              isError: true,
            };
          }
    
          return {
            content: [{ type: 'text', text: JSON.stringify(formatResponse(result), null, 2) }],
          };
        },
      );
    }
  • Zod input schema definition for the backlog_context tool. Validates task_id (optional string), query (optional string, mutually exclusive with task_id), depth (1-3), max_tokens (500-32000), include_related (boolean), and include_activity (boolean).
    inputSchema: z.object({
      task_id: z.string().optional().describe('Task or epic ID to get context for. Example: "TASK-0042" or "EPIC-0005". Mutually exclusive with query.'),
      query: z.string().optional().describe('Natural language query to find the most relevant entity. Example: "search ranking improvements". Mutually exclusive with task_id.'),
      depth: z.number().min(1).max(3).optional().describe('Relational expansion depth. 1 = direct relations (default). 2 = grandparent/grandchildren. 3 = three hops.'),
      max_tokens: z.number().min(500).max(32000).optional().describe('Token budget for the response. Default: 4000. Increase for more detail, decrease for conciseness.'),
      include_related: z.boolean().optional().describe('Include semantically related items (default: true). Set false to skip semantic search and reduce latency.'),
      include_activity: z.boolean().optional().describe('Include recent activity timeline (default: true). Set false to skip activity and reduce response size.'),
    }),
  • Type definitions for the context hydration pipeline. Includes ContextRequest (input parameters), ContextResponse (complete output structure with focal, parent, children, siblings, ancestors, descendants, cross_referenced, referenced_by, related_resources, related, activity, session_summary), and supporting types like ContextEntity, ContextResource, ContextActivity, SessionSummary, and ContextMetadata.
    export interface ContextRequest {
      /** Focal entity ID (e.g. TASK-0042, EPIC-0005). Mutually exclusive with query. */
      task_id?: string;
      /** Natural language query to resolve into a focal entity (Phase 2, ADR-0075). */
      query?: string;
      /** Relational expansion depth. 1 = direct relations. Default: 1, max: 3. */
      depth?: number;
      /** Enable semantic enrichment (Stage 3). Default: true for Phase 2. */
      include_related?: boolean;
      /** Enable temporal overlay (Stage 4). Default: true for Phase 2. */
      include_activity?: boolean;
      /** Token budget for the entire response. Default: 4000. */
      max_tokens?: number;
    }
    
    // ── Response entities ────────────────────────────────────────────────
    
    export type Fidelity = 'full' | 'summary' | 'reference';
    
    export interface ContextEntity {
      id: string;
      title: string;
      status: Status;
      type: EntityType;
      parent_id?: string;
      fidelity: Fidelity;
      /** Present when fidelity is 'full' */
      description?: string;
      /** Present when fidelity is 'full' and entity has evidence */
      evidence?: string[];
      /** Present when fidelity is 'full' and entity has blocked_reason */
      blocked_reason?: string[];
      /** Present when fidelity is 'full' or 'summary' and entity has references */
      references?: { url: string; title?: string }[];
      created_at?: string;
      updated_at?: string;
      /** Relevance score from semantic search. Present only for semantically discovered entities. */
      relevance_score?: number;
      /** Distance from focal entity in the relational graph (1 = direct, 2 = two hops, etc.). Phase 3, ADR-0076. */
      graph_depth?: number;
    }
    
    export interface ContextResource {
      uri: string;
      title: string;
      /** Path relative to data directory */
      path: string;
      fidelity: Fidelity;
      /** Brief excerpt. Present at 'summary' and 'full' fidelity. */
      snippet?: string;
      /** Full content. Present at 'full' fidelity only. */
      content?: string;
      /** Relevance score from semantic search. Present only for semantically discovered resources. */
      relevance_score?: number;
    }
    
    export interface ContextActivity {
      ts: string;
      tool: string;
      entity_id: string;
      actor: string;
      summary: string;
    }
    
    // ── Session memory ──────────────────────────────────────────────────
    
    /** Summary of the last work session on this entity. Phase 3, ADR-0076. */
    export interface SessionSummary {
      /** Who last worked on this entity */
      actor: string;
      /** Whether the actor was a user or agent */
      actor_type: 'user' | 'agent';
      /** When the last session started (ISO timestamp) */
      started_at: string;
      /** When the last session ended (ISO timestamp) */
      ended_at: string;
      /** Number of operations in the last session */
      operation_count: number;
      /** Human-readable summary of what was done */
      summary: string;
    }
    
    // ── Response ─────────────────────────────────────────────────────────
    
    export interface ContextResponse {
      /** The primary entity the context is built around */
      focal: ContextEntity;
      /** Parent entity (if focal has a parent) */
      parent: ContextEntity | null;
      /** Direct children of the focal entity */
      children: ContextEntity[];
      /** Siblings (same parent as focal, excluding focal itself) */
      siblings: ContextEntity[];
      /** Ancestors beyond direct parent (grandparent, great-grandparent). Ordered closest-first. Phase 3, ADR-0076. */
      ancestors: ContextEntity[];
      /** Descendants beyond direct children (grandchildren, etc.). Phase 3, ADR-0076. */
      descendants: ContextEntity[];
      /** Entities explicitly referenced by focal's references[] field. Phase 4, ADR-0077. */
      cross_referenced: ContextEntity[];
      /** Entities whose references[] point to the focal entity (reverse cross-references). Phase 5, ADR-0078. */
      referenced_by: ContextEntity[];
      /** Resources related to the focal entity or its parent */
      related_resources: ContextResource[];
      /** Semantically related entities not in the direct graph (Stage 3, ADR-0075). */
      related: ContextEntity[];
      /** Recent operations on focal and related items (Stage 4, ADR-0075). */
      activity: ContextActivity[];
      /** Session memory — who last worked on this and what they did (Stage 3.5, ADR-0076). */
      session_summary: SessionSummary | null;
      /** Pipeline execution metadata */
      metadata: ContextMetadata;
    }
    
    export interface ContextMetadata {
      depth: number;
      total_items: number;
      /** Estimated token count for the response */
      token_estimate: number;
      /** Whether items were dropped or downgraded to fit the token budget */
      truncated: boolean;
      /** Which pipeline stages were executed */
      stages_executed: string[];
      /** How the focal entity was resolved: 'id' (direct lookup) or 'query' (search-based). */
      focal_resolved_from?: 'id' | 'query';
    }
  • Core hydrateContext function that implements the multi-stage context pipeline. Stages: 1) Focal Resolution (resolve task_id or query to entity), 2) Relational Expansion (parent, children, siblings, ancestors, descendants, resources), 2.5) Cross-Reference Traversal (follow references[] + reverse refs), 3) Semantic Enrichment (find related items via search), 3.5) Session Memory (last work session), 4) Temporal Overlay (recent activity), 5) Token Budgeting (prioritize/truncate to fit budget). Returns complete ContextResponse.
    export async function hydrateContext(
      request: ContextRequest,
      deps: HydrationServiceDeps,
    ): Promise<ContextResponse | null> {
      const maxTokens = request.max_tokens ?? 4000;
      const depth = Math.min(request.depth ?? 1, 3);
      const includeRelated = request.include_related ?? true;
      const includeActivity = request.include_activity ?? true;
      const stagesExecuted: string[] = [];
    
      // ── Stage 1: Focal Resolution ──────────────────────────────────
      const searchDeps: SearchDeps | undefined = deps.searchUnified ? {
        search: async (query: string) => {
          const results = await deps.searchUnified!(query, { types: ['task', 'epic'], limit: 1 });
          return results.map(r => ({ item: r.item as Entity, score: r.score }));
        },
      } : undefined;
    
      const focalResult = await resolveFocal(request, deps.getTask, searchDeps);
      if (!focalResult) return null;
      stagesExecuted.push('focal_resolution');
    
      const { focal, focalTask, resolved_from } = focalResult;
    
      // ── Stage 2: Relational Expansion ──────────────────────────────
      const expansionDeps: RelationalExpansionDeps = {
        getTask: deps.getTask,
        listTasks: deps.listTasks,
        listResources: deps.listResources,
      };
      const expansion = expandRelations(focalTask, depth, expansionDeps);
      stagesExecuted.push('relational_expansion');
    
      // ── Stage 2.5: Cross-Reference Traversal (Phase 4+5, ADR-0077+0078) ──
      // Build a visited set from Stages 1-2 output for dedup
      const visited = new Set<string>([focal.id]);
      if (expansion.parent) visited.add(expansion.parent.id);
      for (const c of expansion.children) visited.add(c.id);
      for (const s of expansion.siblings) visited.add(s.id);
      for (const a of expansion.ancestors) visited.add(a.id);
      for (const d of expansion.descendants) visited.add(d.id);
    
      // Resolve the parent Task for reference collection (parent is summary fidelity
      // from Stage 2, but we need the raw Task for its references field)
      const parentTask = expansion.parent ? deps.getTask(expansion.parent.id) ?? null : null;
    
      const crossRefResult = traverseCrossReferences(
        focalTask,
        parentTask,
        visited,   // Mutated: resolved cross-ref IDs are added
        { getTask: deps.getTask, listTasks: deps.listTasks },
      );
      if (crossRefResult.cross_referenced.length > 0 || crossRefResult.referenced_by.length > 0) {
        stagesExecuted.push('cross_reference_traversal');
      }
    
      // ── Stage 3: Semantic Enrichment ───────────────────────────────
      let semanticEntities: ContextResponse['related'] = [];
      let semanticResources: ContextResponse['related_resources'] = [];
    
      if (includeRelated && deps.searchUnified) {
        // Reuse the visited set (now includes cross-referenced + referenced_by IDs) for dedup
        const existingIds = visited;
    
        const existingResourceUris = new Set<string>(
          expansion.related_resources.map(r => r.uri),
        );
    
        const enrichment = await enrichSemantic(
          focalTask,
          existingIds,
          existingResourceUris,
          { searchUnified: deps.searchUnified },
        );
        semanticEntities = enrichment.related_entities;
        semanticResources = enrichment.related_resources;
        stagesExecuted.push('semantic_enrichment');
      }
    
      // ── Stage 3.5: Session Memory ──────────────────────────────────
      let sessionSummary: SessionSummary | null = null;
    
      if (deps.readOperations) {
        const sessionDeps: SessionMemoryDeps = {
          readOperations: deps.readOperations,
        };
        sessionSummary = deriveSessionSummary(focal.id, sessionDeps);
        if (sessionSummary) {
          stagesExecuted.push('session_memory');
        }
      }
    
      // ── Stage 4: Temporal Overlay ──────────────────────────────────
      let activity: ContextResponse['activity'] = [];
    
      if (includeActivity && deps.readOperations) {
        // Query activity for focal + parent + children (focused set)
        const activityEntityIds = [focal.id];
        if (expansion.parent) activityEntityIds.push(expansion.parent.id);
        for (const c of expansion.children) activityEntityIds.push(c.id);
    
        activity = overlayTemporal(
          activityEntityIds,
          { readOperations: deps.readOperations },
          20,
        );
        stagesExecuted.push('temporal_overlay');
      }
    
      // ── Stage 5: Token Budgeting ───────────────────────────────────
      // Combine path-matched and semantic resources for budgeting
      const allResources = [...expansion.related_resources, ...semanticResources];
    
      const budget = applyBudget(
        focal,
        expansion.parent,
        expansion.children,
        expansion.siblings,
        crossRefResult.cross_referenced,
        crossRefResult.referenced_by,
        expansion.ancestors,
        expansion.descendants,
        semanticEntities,
        allResources,
        activity,
        sessionSummary,
        maxTokens,
      );
      stagesExecuted.push('token_budgeting');
    
      // Separate budget entities back into their roles
      const budgetedFocal = budget.entities[0]!;
      let idx = 1;
      let budgetedParent: ContextResponse['parent'] = null;
      if (expansion.parent && idx < budget.entities.length) {
        const candidate = budget.entities[idx]!;
        if (candidate.id === expansion.parent.id) {
          budgetedParent = candidate;
          idx++;
        }
      }
    
      // Use sets for role separation
      const childIds = new Set(expansion.children.map(c => c.id));
      const siblingIds = new Set(expansion.siblings.map(s => s.id));
      const crossRefIds = new Set(crossRefResult.cross_referenced.map(x => x.id));
      const referencedByIds = new Set(crossRefResult.referenced_by.map(r => r.id));
      const ancestorIds = new Set(expansion.ancestors.map(a => a.id));
      const descendantIds = new Set(expansion.descendants.map(d => d.id));
      const semanticIds = new Set(semanticEntities.map(r => r.id));
    
      const budgetedChildren: ContextResponse['children'] = [];
      const budgetedSiblings: ContextResponse['siblings'] = [];
      const budgetedCrossReferenced: ContextResponse['cross_referenced'] = [];
      const budgetedReferencedBy: ContextResponse['referenced_by'] = [];
      const budgetedAncestors: ContextResponse['ancestors'] = [];
      const budgetedDescendants: ContextResponse['descendants'] = [];
      const budgetedRelated: ContextResponse['related'] = [];
    
      for (let i = idx; i < budget.entities.length; i++) {
        const e = budget.entities[i]!;
        if (childIds.has(e.id)) {
          budgetedChildren.push(e);
        } else if (siblingIds.has(e.id)) {
          budgetedSiblings.push(e);
        } else if (crossRefIds.has(e.id)) {
          budgetedCrossReferenced.push(e);
        } else if (referencedByIds.has(e.id)) {
          budgetedReferencedBy.push(e);
        } else if (ancestorIds.has(e.id)) {
          budgetedAncestors.push(e);
        } else if (descendantIds.has(e.id)) {
          budgetedDescendants.push(e);
        } else if (semanticIds.has(e.id)) {
          budgetedRelated.push(e);
        }
      }
    
      const totalItems = 1 + // focal
        (budgetedParent ? 1 : 0) +
        budgetedChildren.length +
        budgetedSiblings.length +
        budgetedCrossReferenced.length +
        budgetedReferencedBy.length +
        budgetedAncestors.length +
        budgetedDescendants.length +
        budget.resources.length +
        budgetedRelated.length +
        budget.activities.length +
        (budget.sessionSummary ? 1 : 0);
    
      return {
        focal: budgetedFocal,
        parent: budgetedParent,
        children: budgetedChildren,
        siblings: budgetedSiblings,
        cross_referenced: budgetedCrossReferenced,
        referenced_by: budgetedReferencedBy,
        ancestors: budgetedAncestors,
        descendants: budgetedDescendants,
        related_resources: budget.resources,
        related: budgetedRelated,
        activity: budget.activities,
        session_summary: budget.sessionSummary,
        metadata: {
          depth,
          total_items: totalItems,
          token_estimate: budget.tokensUsed,
          truncated: budget.truncated,
          stages_executed: stagesExecuted,
          focal_resolved_from: resolved_from,
        },
      };
    }
  • Tool registration module that imports registerBacklogContextTool and calls it within the registerTools function to register all backlog tools including backlog_context with the MCP server.
    import { registerBacklogContextTool } from './backlog-context.js';
    
    export function registerTools(server: McpServer) {
      registerBacklogListTool(server);
      registerBacklogGetTool(server);
      registerBacklogCreateTool(server);
      registerBacklogUpdateTool(server);
      registerBacklogDeleteTool(server);
      registerBacklogSearchTool(server);
      registerBacklogContextTool(server);
    }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/gkoreli/backlog-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server