goal_state
Retrieve comprehensive single-goal details including quality assessment, progress, bottlenecks, knowledge gaps, pending decisions, and recent activity. Consolidates multiple goal queries into one call.
Instructions
Comprehensive single-goal read: details, quality assessment, progress, bottlenecks, knowledge gaps, pending decisions, recent activity. Replaces get_goal + goal_path + goal_progress + goal_knowledge_gaps + assess_goal_quality.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| goal_id | Yes |
Implementation Reference
- src/tools/bdi/beliefs.js:220-231 (schema)Goal state tool input schema definition (name 'goal_state', description, inputSchema with required goal_id).
const goalStateDefinition = { name: 'goal_state', description: "Comprehensive single-goal read: details, quality assessment, progress, " + "bottlenecks, knowledge gaps, pending decisions, recent activity. " + "Replaces get_goal + goal_path + goal_progress + goal_knowledge_gaps + assess_goal_quality.", inputSchema: { type: 'object', properties: { goal_id: { type: 'string' } }, required: ['goal_id'], }, }; - src/tools/bdi/beliefs.js:233-302 (handler)Goal state handler: fetches goal details, quality, progress, knowledge gaps, and path in parallel via Promise.allSettled, computes top-5 bottlenecks, surfaces linked plans/tasks, and returns formatted response with partial-failure metadata.
async function goalStateHandler(args, apiClient) { const { goal_id } = args; const [goalRes, qualityRes, progressRes, gapsRes, pathRes] = await Promise.allSettled([ apiClient.goals.get(goal_id), apiClient.goals.getQuality(goal_id), apiClient.goals.getProgress(goal_id), apiClient.goals.getKnowledgeGaps(goal_id), apiClient.goals.getPath(goal_id), ]); const failures = []; const unwrap = (s, label, def) => { if (s.status === 'fulfilled') return s.value; failures.push({ source: label, message: s.reason?.message }); return def; }; const goal = unwrap(goalRes, 'goals.get', null); if (!goal) return errorResponse('not_found', `Goal ${goal_id} not found`); const quality = unwrap(qualityRes, 'goals.quality', {}); const progress = unwrap(progressRes, 'goals.progress', {}); const gaps = unwrap(gapsRes, 'goals.knowledgeGaps', { gaps: [] }); const path = unwrap(pathRes, 'goals.path', { tasks: [] }); const bottlenecks = safeArray(path.tasks || path) .filter((t) => t.status !== 'completed') .sort((a, b) => (b.direct_downstream_count || 0) - (a.direct_downstream_count || 0)) .slice(0, 5) .map((t) => ({ node_id: t.id, title: t.title, status: t.status, direct_downstream_count: t.direct_downstream_count || 0, })); // Surface the goal's linked plans + tasks. The underlying GET /goals/:id // already returns the `links` array; the previous handler discarded it, // so quality.actionability could report "26 plans linked" while the // response refused to name a single one. Callers had no read-side way // to enumerate the plans served by a goal short of REST. const links = safeArray(goal.links); const linked_plans = links .filter((l) => (l.linkedType || l.linked_type) === 'plan') .map((l) => ({ id: l.linkedId || l.linked_id, link_id: l.id })); const linked_tasks = links .filter((l) => (l.linkedType || l.linked_type) === 'task') .map((l) => ({ id: l.linkedId || l.linked_id, link_id: l.id })); return formatResponse({ as_of: asOf(), goal: { id: goal.id, title: goal.title, description: goal.description, type: goal.type, goal_type: goal.goalType || goal.goal_type, status: goal.status, priority: goal.priority, owner_id: goal.ownerId || goal.owner_id, success_criteria: goal.successCriteria || goal.success_criteria, promoted_at: goal.promotedAt || goal.promoted_at, }, linked_plans, linked_tasks, quality: { score: quality.score, dimensions: quality.dimensions, suggestions: quality.suggestions, last_assessed_at: quality.as_of, }, progress: progress, bottlenecks, knowledge_gaps: safeArray(gaps.gaps || gaps), meta: { partial: failures.length > 0, failures }, }); } - src/tools/bdi/beliefs.js:555-574 (registration)Exports goalStateDefinition in definitions array and goalStateHandler in handlers map under 'goal_state' key.
module.exports = { definitions: [ briefingDefinition, taskContextDefinition, goalStateDefinition, recallKnowledgeDefinition, listPlansDefinition, searchDefinition, planAnalysisDefinition, ], handlers: { briefing: briefingHandler, task_context: taskContextHandler, goal_state: goalStateHandler, recall_knowledge: recallKnowledgeHandler, list_plans: listPlansHandler, search: searchHandler, plan_analysis: planAnalysisHandler, }, }; - src/tools/bdi/index.js:36-40 (registration)BDI tool dispatch: looks up handler by name and calls it with args and apiClient.
async function bdiToolHandler(name, args, apiClient) { if (!names.has(name)) return undefined; const handler = handlers[name]; return handler(args || {}, apiClient); } - src/tools.js:19-62 (registration)Top-level MCP tool wiring: registers ListToolsRequestSchema (returns all definitions) and CallToolRequestSchema (dispatches to bdiToolHandler).
function setupTools(server, apiClientOverride) { const apiClient = apiClientOverride || defaultApiClient; if (process.env.NODE_ENV === 'development') { console.error(`Setting up MCP tools (${bdiToolDefinitions.length} BDI tools)`); } server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: bdiToolDefinitions }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; if (process.env.NODE_ENV === 'development') { console.error(`Calling tool: ${name}`); } if (!bdiToolNames.has(name)) { return { isError: true, content: [{ type: 'text', text: `Unknown tool: ${name}. v0.9.0 ships 15 BDI tools. Run get_started to see them, or check ../docs/MIGRATION_v0.9.md for the legacy → BDI mapping.`, }], }; } try { return await bdiToolHandler(name, args, apiClient); } catch (err) { if (process.env.NODE_ENV === 'development') { console.error(`Tool ${name} threw:`, err); } return { isError: true, content: [{ type: 'text', text: `Tool ${name} failed: ${err.message || String(err)}`, }], }; } }); }