Skip to main content
Glama

backlog_wakeup

Get a dense session-start briefing of active tasks, epics, recent completions with evidence, and activity. Optionally scope to a folder, milestone, or epic for project-focused context.

Instructions

Dense session-start briefing: active tasks, current epics, recent completions (with evidence snippets), and recent activity. No focal entity required — use this at the start of every session to understand what you were working on. Optional scope narrows to a folder (for project-scoped briefing), milestone, or epic.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
scopeNoOptional entity ID to scope the briefing to a subtree. Must be a container (folder/milestone/epic). Use a folder ID for project-scoped wake-up (e.g. "FLDR-0001"). Omit to get everything across the whole backlog.
max_completionsNoMax done tasks in the "recent" section. Default: 5.
max_activityNoMax recent activity-log entries. Default: 5.
evidence_snippet_charsNoMax chars of evidence to include per completion. Default: 160.

Implementation Reference

  • Core wakeup function — the main business logic for backlog_wakeup. Builds a briefing with identity (L0), active tasks and current epics (L1), recent completions and activity (L2). Supports optional scope filtering to a folder/milestone/epic subtree.
    export async function wakeup(
      service: IBacklogService,
      params: WakeupParams = {},
    ): Promise<WakeupResult> {
      const maxCompletions = params.maxCompletions ?? 5;
      const maxActivity = params.maxActivity ?? 5;
      const snippetChars = params.evidenceSnippetChars ?? 160;
    
      const identity = params.readIdentity?.();
    
      // Scope validation + descendant set.
      // Scope is validated at the boundary — see assertValidScope + the
      // WakeupParams.scope JSDoc in ./types.ts for the reasoning behind
      // string-over-brand here.
      let scopeFilter: ((id: string) => boolean) | null = null;
      if (params.scope !== undefined) {
        assertValidScope(params.scope);
        const set = await descendantSet(service, params.scope);
        // Exclude the scope entity itself from result sections — the caller
        // asked for "what's inside this folder", not "this folder".
        set.delete(params.scope);
        scopeFilter = (id: string) => set.has(id);
      }
    
      const inScope = <T extends { id: string }>(xs: T[]): T[] =>
        scopeFilter ? xs.filter(e => scopeFilter!(e.id)) : xs;
    
      // L1 Now — active tasks (in_progress | blocked), epics excluded;
      // and current epics as their own section.
      const active = await service.list({ status: ['in_progress', 'blocked'] });
      const activeTasks = inScope(active.filter(e => e.type !== 'epic'))
        .sort(byUpdatedAtDesc)
        .map(toSummary);
    
      const epics = await service.list({ type: EntityType.Epic, status: ['open', 'in_progress'] });
      const currentEpics = inScope(epics).sort(byUpdatedAtDesc).map(toSummary);
    
      // L2 Recent — last N done tasks by updated_at, + last N ops from the log.
      const done = await service.list({ status: ['done'] });
      const completions = inScope(done)
        .sort(byUpdatedAtDesc)
        .slice(0, maxCompletions)
        .map(e => toCompletion(e, snippetChars));
    
      // Pull extra activity entries when scoped — we filter client-side and
      // want to end up with at least maxActivity after filtering when possible.
      // 5x over-fetch covers the common "most ops touched other scopes" case
      // without unbounded cost.
      const opLimit = scopeFilter ? maxActivity * 5 : maxActivity;
      const ops = params.readOperations
        ? params.readOperations({ limit: opLimit })
        : [];
      const filteredOps = scopeFilter
        ? ops.filter(op => {
            const eid = opEntityId(op);
            return eid !== undefined && scopeFilter!(eid);
          })
        : ops;
      const activity: WakeupActivity[] = filteredOps.slice(0, maxActivity).map(op => {
        const a: WakeupActivity = {
          ts: op.ts,
          tool: op.tool,
          actor: op.actor?.name ?? 'unknown',
        };
        const eid = opEntityId(op);
        if (eid) a.entity_id = eid;
        return a;
      });
    
      return {
        ...(identity ? { identity } : {}),
        ...(params.scope ? { scope: params.scope } : {}),
        now: {
          active_tasks: activeTasks,
          current_epics: currentEpics,
        },
        recent: {
          completions,
          activity,
        },
        metadata: {
          generated_at: new Date().toISOString(),
          identity_present: identity !== undefined,
          active_task_count: activeTasks.length,
          epic_count: currentEpics.length,
          completion_count: completions.length,
          activity_count: activity.length,
        },
      };
    }
  • MCP tool handler registration for backlog_wakeup. Defines the input schema (scope, max_completions, max_activity, evidence_snippet_chars), injects filesystem identity reader and operation logger, calls the core wakeup function, and formats the response.
    export function registerBacklogWakeupTool(
      server: McpServer,
      service: IBacklogService,
      deps?: BacklogWakeupDeps,
    ): void {
      server.registerTool(
        'backlog_wakeup',
        {
          description:
            'Dense session-start briefing: active tasks, current epics, recent completions (with evidence snippets), and recent activity. No focal entity required — use this at the start of every session to understand what you were working on. Optional `scope` narrows to a folder (for project-scoped briefing), milestone, or epic.',
          inputSchema: z.object({
            scope: z.string().optional().describe(
              'Optional entity ID to scope the briefing to a subtree. Must be a container (folder/milestone/epic). Use a folder ID for project-scoped wake-up (e.g. "FLDR-0001"). Omit to get everything across the whole backlog.',
            ),
            max_completions: z.number().min(0).max(50).optional().describe(
              'Max done tasks in the "recent" section. Default: 5.',
            ),
            max_activity: z.number().min(0).max(50).optional().describe(
              'Max recent activity-log entries. Default: 5.',
            ),
            evidence_snippet_chars: z.number().min(40).max(1000).optional().describe(
              'Max chars of evidence to include per completion. Default: 160.',
            ),
          }),
        },
        async ({ scope, max_completions, max_activity, evidence_snippet_chars }) => {
          try {
            const result = await wakeup(service, {
              ...(scope !== undefined ? { scope } : {}),
              ...(max_completions !== undefined ? { maxCompletions: max_completions } : {}),
              ...(max_activity !== undefined ? { maxActivity: max_activity } : {}),
              ...(evidence_snippet_chars !== undefined ? { evidenceSnippetChars: evidence_snippet_chars } : {}),
              readIdentity: readIdentityFile,
              ...(deps?.operationLogger
                ? { readOperations: (o) => deps.operationLogger!.read(o) }
                : {}),
            });
            return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
          } catch (e) {
            if (e instanceof ValidationError) {
              return {
                content: [{ type: 'text', text: JSON.stringify({ error: e.message }) }],
                isError: true,
              };
            }
            throw e;
          }
        },
      );
  • WakeupParams interface — all optional parameters for the wakeup core function: scope, maxCompletions, maxActivity, evidenceSnippetChars, readIdentity, readOperations.
    export interface WakeupParams {
      /**
       * Restrict the briefing to a single subtree — a Folder, Milestone, or Epic
       * entity ID. Every active task, epic, completion, and activity entry is
       * filtered to descendants (transitively) of this entity. The scope entity
       * itself is not included in the result sections.
       *
       * **Typing decision:** ``scope`` is declared as ``string`` to match the
       * whole codebase's ID-field convention (``parent_id``, ``epic_id``,
       * all MCP tool args — every ID is a plain string, validated at boundaries
       * via ``isValidEntityId`` / ``parseEntityId`` from ``@backlog-mcp/shared``).
       * Introducing a branded ``EntityId`` type for one field would fight the
       * rest of the system; it would also be a weaker guarantee than what
       * we actually do — at the core boundary we validate two things that a
       * brand can't express:
       *   1. The ID is well-formed (parses via ``parseEntityId``)
       *   2. The referenced type is a **container** (folder/milestone/epic),
       *      not a leaf (task/artifact/cron)
       * Both checks happen at the start of ``wakeup``; invalid scopes throw
       * ``ValidationError`` with a message that names the offending ID.
       *
       * **What counts as a "container":** any substrate whose definition has
       * ``structure.isContainer === true`` — currently ``folder``, ``milestone``,
       * ``epic``. Leaf substrates (``task``, ``artifact``, ``cron``) are rejected.
       *
       * **Recommended usage:** Folders are the intended "project" abstraction.
       * A top-level Folder (``parent_id === undefined``) acts as a project;
       * nested folders act as sub-areas. Use ``scope: "FLDR-0001"`` for
       * project-scoped wake-up, ``scope: "EPIC-0005"`` to narrow to an epic.
       * Omit ``scope`` to get everything across all projects.
       */
      scope?: string;
      /** Max recent completions in the "recent" section. Default: 5. */
      maxCompletions?: number;
      /** Max recent activity entries (from the operation log). Default: 5. */
      maxActivity?: number;
      /** Evidence snippet max chars on completion summaries. Default: 160. */
      evidenceSnippetChars?: number;
      /**
       * Synchronous identity loader. Return ``undefined`` for "no identity
       * configured" — core will omit the L0 section. Omit to skip entirely.
       */
      readIdentity?: () => string | undefined;
      /**
       * Recent operation reader — returns write-log entries newest-first.
       * Injected because the operation log is outside ``IBacklogService`` and
       * core must not import the concrete logger (keeps core transport-free).
       * Omit to skip the activity section entirely.
       */
      readOperations?: (options: { limit?: number }) => Array<{
        ts: string;
        tool: string;
        params: Record<string, unknown>;
        resourceId?: string;
        actor: { type: string; name: string };
      }>;
    }
  • WakeupEntitySummary, WakeupCompletion, WakeupActivity, and WakeupResult interfaces — the return types for the wakeup briefing.
    export interface WakeupEntitySummary {
      id: string;
      title: string;
      status: Status | string;
      type: string;
      parent_id?: string;
      updated_at?: string;
    }
    
    export interface WakeupCompletion extends WakeupEntitySummary {
      evidence_snippet?: string;
    }
    
    export interface WakeupActivity {
      ts: string;
      tool: string;
      entity_id?: string;
      actor: string;
    }
    
    export interface WakeupResult {
      identity?: string;
      /** Echoes the scope param so callers can confirm what was included. */
      scope?: string;
      now: {
        active_tasks: WakeupEntitySummary[];
        current_epics: WakeupEntitySummary[];
      };
      recent: {
        completions: WakeupCompletion[];
        activity: WakeupActivity[];
      };
      metadata: {
        generated_at: string;
        identity_present: boolean;
        active_task_count: number;
        epic_count: number;
        completion_count: number;
        activity_count: number;
      };
    }
  • Tool registration call in the central registerTools function — wires backlog_wakeup with the operationLogger dependency.
      registerBacklogWakeupTool(server, service, deps?.operationLogger ? { operationLogger: deps.operationLogger } : undefined);
      registerBacklogRecallTool(server, deps?.memoryComposer ? { memoryComposer: deps.memoryComposer } : undefined);
    }
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden. It discloses that the tool retrieves multiple types of data (active tasks, epics, completions with snippets, activity) and that scope is optional. It implies a read-only operation but does not explicitly state side effects or authorization needs.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentence description: first sentence lists contents, second provides usage and scope explanation. Every word is purposeful, no redundancy. Front-loaded with key purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no output schema, the description adequately covers what the tool returns. All 4 parameters are described in schema, and the description adds usage context. It lacks details on ordering or formatting, but those are not critical for selection and invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% with detailed descriptions for each parameter. The tool description adds value by explaining how scope is used ('folder ID for project-scoped wake-up; omit to get everything') and the context of the parameters within the briefing purpose.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly defines the tool as a 'dense session-start briefing' containing active tasks, current epics, recent completions with evidence snippets, and recent activity. It distinguishes itself from siblings like backlog_list by being a comprehensive overview without requiring a focal entity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly states 'use this at the start of every session' and explains the optional scope parameter for project-scoped briefing. It does not explicitly mention when not to use or alternatives, but the guidance is clear and contextually sufficient given the sibling tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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