Skip to main content
Glama
TAgents

Planning System MCP Server

by TAgents

update_task

Update a task's status atomically, with optional log entry, claim release, knowledge episode, and session-blocking decision.

Instructions

Atomic task state transition. Updates status, optionally appends a log entry, optionally releases the claim. Idempotent on identical inputs. Replaces quick_status + add_log + release_task fan-out.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
task_idYes
plan_idNoPlan that owns the task (auto-resolved from task if omitted)
statusNo
log_messageNoOptional progress note
log_typeNoDefaults from status: blocked→challenge, others→progress.
release_claimNoDefault: auto (true if status is completed/blocked). Set explicitly to override.
add_learningNoOptional: also write a knowledge episode (recommended on completion)
session_idNoOptional work-session id returned by claim_next_task. Uses the agent-loop completion/block endpoint when status is completed or blocked.
decisionNoOptional decision to queue when blocking a session through the agent-loop endpoint.

Implementation Reference

  • The updateTaskHandler function: The core handler that executes the 'update_task' tool logic. It performs atomic task state transitions — updates status, optionally appends a log entry, releases the claim (auto if completed/blocked), and optionally writes a knowledge episode. Also supports agent-loop session-based completion/block endpoints.
    async function updateTaskHandler(args, apiClient) {
      const { task_id, status, log_message, add_learning, release_claim, session_id, decision } = args;
      let planId = args.plan_id;
    
      if (session_id && (status === 'completed' || status === 'blocked')) {
        try {
          const path = status === 'blocked' ? 'block' : 'complete';
          const response = await apiClient.axiosInstance.post(`/agent/work-sessions/${session_id}/${path}`, {
            summary: log_message,
            learning: add_learning ? { content: add_learning } : undefined,
            decision,
          });
          return formatResponse(response.data);
        } catch {
          // Fall back to legacy fan-out for older APIs or if the session was not found.
        }
      }
    
      // Resolve plan_id from task if not provided.
      if (!planId) {
        try {
          const node = await apiClient.axiosInstance.get(`/nodes/${task_id}`).then((r) => r.data);
          planId = node.plan_id || node.planId;
        } catch (err) {
          return errorResponse('not_found', `Could not resolve plan_id from task ${task_id}: ${err.message}`);
        }
      }
    
      const result = {
        as_of: asOf(),
        task_id,
        plan_id: planId,
        applied: { status_changed: false, log_added: false, claim_released: false, learning_recorded: false },
        failures: [],
      };
    
      // 1. Status update
      if (status) {
        try {
          await apiClient.nodes.updateNode(planId, task_id, { status });
          result.applied.status_changed = true;
          result.status = status;
        } catch (err) {
          result.failures.push({ step: 'update_status', error: err.response?.data?.error || err.message });
        }
      }
    
      // 2. Log entry
      if (log_message) {
        const logType = args.log_type || STATUS_TO_LOG_TYPE[status] || 'progress';
        try {
          const log = await apiClient.logs.addLog(planId, task_id, {
            content: log_message,
            log_type: logType,
          });
          result.applied.log_added = true;
          result.log_id = log?.id || log?.log?.id;
        } catch (err) {
          result.failures.push({ step: 'add_log', error: err.response?.data?.error || err.message });
        }
      }
    
      // 3. Claim release — auto if status is terminal, explicit override otherwise.
      const shouldRelease =
        typeof release_claim === 'boolean'
          ? release_claim
          : status === 'completed' || status === 'blocked';
      if (shouldRelease) {
        try {
          await apiClient.axiosInstance.delete(`/nodes/${task_id}/claim`);
          result.applied.claim_released = true;
        } catch (err) {
          // Releasing an unclaimed task is not a hard error — just record it.
          result.failures.push({ step: 'release_claim', error: err.response?.data?.error || err.message });
        }
      }
    
      // 4. Optional learning write to knowledge graph.
      if (add_learning) {
        try {
          await apiClient.graphiti.addEpisode({
            name: `Task: ${task_id}`,
            content: add_learning,
            source: 'task_update',
            plan_id: planId,
            node_id: task_id,
          });
          result.applied.learning_recorded = true;
        } catch (err) {
          result.failures.push({ step: 'add_learning', error: err.response?.data?.error || err.message });
        }
      }
    
      return formatResponse(result);
    }
  • The updateTaskDefinition object: Defines the tool's name ('update_task'), description, and inputSchema with properties: task_id (required), plan_id, status (enum), log_message, log_type, release_claim, add_learning, session_id, and decision.
    const updateTaskDefinition = {
      name: 'update_task',
      description:
        "Atomic task state transition. Updates status, optionally appends a log " +
        "entry, optionally releases the claim. Idempotent on identical inputs. " +
        "Replaces quick_status + add_log + release_task fan-out.",
      inputSchema: {
        type: 'object',
        properties: {
          task_id: { type: 'string' },
          plan_id: {
            type: 'string',
            description: 'Plan that owns the task (auto-resolved from task if omitted)',
          },
          status: {
            type: 'string',
            enum: ['not_started', 'in_progress', 'completed', 'blocked', 'plan_ready'],
          },
          log_message: { type: 'string', description: 'Optional progress note' },
          log_type: {
            type: 'string',
            enum: ['progress', 'decision', 'blocker', 'completion', 'challenge'],
            description: "Defaults from status: blocked→challenge, others→progress.",
          },
          release_claim: {
            type: 'boolean',
            description: "Default: auto (true if status is completed/blocked). Set explicitly to override.",
          },
          add_learning: {
            type: 'string',
            description: 'Optional: also write a knowledge episode (recommended on completion)',
          },
          session_id: {
            type: 'string',
            description: 'Optional work-session id returned by claim_next_task. Uses the agent-loop completion/block endpoint when status is completed or blocked.',
          },
          decision: {
            type: 'object',
            description: 'Optional decision to queue when blocking a session through the agent-loop endpoint.',
          },
        },
        required: ['task_id'],
      },
    };
  • The STATUS_TO_LOG_TYPE mapping at lines 244-250 maps status values to log types (used by the handler).
    async function updateTaskHandler(args, apiClient) {
      const { task_id, status, log_message, add_learning, release_claim, session_id, decision } = args;
      let planId = args.plan_id;
    
      if (session_id && (status === 'completed' || status === 'blocked')) {
        try {
          const path = status === 'blocked' ? 'block' : 'complete';
          const response = await apiClient.axiosInstance.post(`/agent/work-sessions/${session_id}/${path}`, {
            summary: log_message,
            learning: add_learning ? { content: add_learning } : undefined,
            decision,
          });
          return formatResponse(response.data);
        } catch {
          // Fall back to legacy fan-out for older APIs or if the session was not found.
        }
      }
    
      // Resolve plan_id from task if not provided.
      if (!planId) {
        try {
          const node = await apiClient.axiosInstance.get(`/nodes/${task_id}`).then((r) => r.data);
          planId = node.plan_id || node.planId;
        } catch (err) {
          return errorResponse('not_found', `Could not resolve plan_id from task ${task_id}: ${err.message}`);
        }
      }
    
      const result = {
        as_of: asOf(),
        task_id,
        plan_id: planId,
        applied: { status_changed: false, log_added: false, claim_released: false, learning_recorded: false },
        failures: [],
      };
    
      // 1. Status update
      if (status) {
        try {
          await apiClient.nodes.updateNode(planId, task_id, { status });
          result.applied.status_changed = true;
          result.status = status;
        } catch (err) {
          result.failures.push({ step: 'update_status', error: err.response?.data?.error || err.message });
        }
      }
    
      // 2. Log entry
      if (log_message) {
        const logType = args.log_type || STATUS_TO_LOG_TYPE[status] || 'progress';
        try {
          const log = await apiClient.logs.addLog(planId, task_id, {
            content: log_message,
            log_type: logType,
          });
          result.applied.log_added = true;
          result.log_id = log?.id || log?.log?.id;
        } catch (err) {
          result.failures.push({ step: 'add_log', error: err.response?.data?.error || err.message });
        }
      }
    
      // 3. Claim release — auto if status is terminal, explicit override otherwise.
      const shouldRelease =
        typeof release_claim === 'boolean'
          ? release_claim
          : status === 'completed' || status === 'blocked';
      if (shouldRelease) {
        try {
          await apiClient.axiosInstance.delete(`/nodes/${task_id}/claim`);
          result.applied.claim_released = true;
        } catch (err) {
          // Releasing an unclaimed task is not a hard error — just record it.
          result.failures.push({ step: 'release_claim', error: err.response?.data?.error || err.message });
        }
      }
    
      // 4. Optional learning write to knowledge graph.
      if (add_learning) {
        try {
          await apiClient.graphiti.addEpisode({
            name: `Task: ${task_id}`,
            content: add_learning,
            source: 'task_update',
            plan_id: planId,
            node_id: task_id,
          });
          result.applied.learning_recorded = true;
        } catch (err) {
          result.failures.push({ step: 'add_learning', error: err.response?.data?.error || err.message });
        }
      }
    
      return formatResponse(result);
    }
  • Registration of update_task handler in the module.exports.handlers map: 'update_task: updateTaskHandler'.
    update_task: updateTaskHandler,
  • Registration of updateTaskDefinition in the module.exports.definitions array.
    updateTaskDefinition,
Behavior4/5

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

With no annotations provided, the description carries full burden. It discloses key behaviors: atomic operation, idempotence, and the optional actions. This is sufficient to understand the tool's nature, though additional details about potential side effects (e.g., knowledge episode writing) are left to the schema.

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?

The description is extremely concise with two sentences containing no filler. Every phrase adds value, clearly stating purpose, optional actions, idempotency, and replacement of sibling tools.

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 9 parameters and no output schema, the description provides sufficient high-level context for an agent to understand the tool's role. It explains the atomic transition and replacement of fan-out tools. Minor gap: does not explain default log_type behavior or valid status transitions, but these are covered by schema enums.

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

Parameters3/5

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

Schema coverage is high (78%) and most parameters have descriptions. The description does not add detail beyond the schema, providing only high-level context. Since baseline is 3 for high coverage, this score is appropriate.

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?

Description specifies 'Atomic task state transition' with clear verb and resource. It details that it updates status, optionally appends log entry, and optionally releases claim. It also distinguishes itself by stating it replaces three previous tools (quick_status, add_log, release_task), which helps with identification.

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 says 'Replaces quick_status + add_log + release_task fan-out,' indicating when to use this tool instead of multiple others. However, it does not provide explicit when-not-to-use guidance or mention alternatives like the sibling 'release_task' for cases where only claim release is needed.

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/TAgents/agent-planner-mcp'

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