Files
TORCH/src/mcp/tools.mjs
T

782 lines
40 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { McpServer } from '@modelcontextprotocol/server';
import { z } from 'zod';
import { TorchError } from '../kernel/errors.mjs';
import { planManagerCheckIn } from '../observability/manager-checkin.mjs';
const optionalReferenceShape = {
task: z.string().min(1).optional(),
path: z.string().min(1).optional(),
commit: z.string().min(1).optional(),
handoff: z.string().min(1).optional(),
};
export const TORCH_CORE_MCP_TOOL_NAMES = Object.freeze([
'torch_identity',
'torch_list_agents',
'torch_plan_manager_check_in',
'torch_get_agent',
'torch_get_roster',
'torch_who_owns',
'torch_neighbours',
'torch_send_message',
'torch_read_messages',
'torch_ack_message',
'torch_report_status',
'torch_report_complete',
'torch_report_blocked',
'torch_request_approval',
'torch_list_approvals',
'torch_decide_approval',
'torch_request_coordination',
'torch_request_handoff',
]);
export const TORCH_MCP_TOOL_NAMES = Object.freeze([
...TORCH_CORE_MCP_TOOL_NAMES,
'torch_list_backlog',
'torch_next_backlog_task',
'torch_backlog_health',
'torch_backlog_activity',
'torch_plan_landed_task_closure',
'torch_reconcile_landed_tasks',
'torch_plan_backlog_claim',
'torch_claim_next_backlog_task',
'torch_get_backlog_task',
'torch_create_backlog_task',
'torch_classify_backlog_task',
'torch_transition_backlog_task',
'torch_list_checks',
'torch_list_check_receipts',
'torch_plan_check',
'torch_run_check',
'torch_prepare_check',
'torch_list_prepared_checks',
'torch_run_prepared_check',
'torch_cancel_prepared_check',
'torch_list_resources',
'torch_resource_status',
'torch_acquire_resource',
'torch_release_resource',
'torch_cancel_resource_request',
'torch_list_integrations',
'torch_request_integration',
'torch_evaluate_integration',
'torch_authorize_integration',
'torch_plan_integration_landing',
'torch_land_integration',
'torch_record_context_usage',
'torch_context_report',
'torch_list_schedules',
'torch_schedule_runs',
'torch_plan_schedule',
'torch_run_schedule',
'torch_list_fleet_changes',
'torch_get_fleet_change',
'torch_assess_fleet_evolution',
'torch_propose_domain',
'torch_propose_domain_retirement',
'torch_propose_domain_merge',
'torch_propose_domain_split',
'torch_assess_hierarchy_needs',
'torch_list_hierarchy_proposals',
'torch_get_hierarchy_proposal',
'torch_propose_hierarchy_change',
'torch_plan_hierarchy_pilot',
'torch_publish_artifact',
]);
function claimedIdentity(controlPlane, actorId, claim, field) {
const actor = actorId ? controlPlane.assertIdentity(actorId) : null;
const claimed = claim ? controlPlane.assertIdentity(claim) : null;
if (actor && claimed && actor !== claimed) {
throw new TorchError(`Bound Fleet identity ${actor} cannot act as ${claimed} in ${field}`, {
code: 'FLEET_IDENTITY_MISMATCH', details: { actor, claimed, field },
});
}
if (actor) return actor;
if (claimed) return claimed;
throw new TorchError(`${field} is required when the MCP server has no bound Fleet identity`, {
code: 'FLEET_IDENTITY_REQUIRED', details: { field },
});
}
export function createTorchToolset(controlPlane, {
actorId, backlogService, checkService, resourceService, integrationService, contextTelemetryService, scheduleService,
evolutionService, hierarchyService, repositoryRoot,
artifactService,
} = {}) {
const tools = new Map([
['torch_identity', {
description: 'Return the stable TORCH Fleet identity and current runtime presence for one area.',
schema: { area_id: z.string().min(1).optional() },
invoke: ({ area_id: areaId }) => controlPlane.identity(claimedIdentity(controlPlane, actorId, areaId, 'area_id')),
}],
['torch_list_agents', {
description: 'List all persistent Fleet identities and their live presence.',
schema: {},
invoke: () => ({ agents: controlPlane.listAgents() }),
}],
['torch_plan_manager_check_in', {
description: 'Refresh the bound manager identity’s direct-report presence, durable messages, and named approval waits from current state. Read-only; only works for an identity with direct reports.',
schema: { stale_work_review: z.boolean().optional(),
stale_days: z.number().int().min(1).max(365).optional(),
max_commits: z.number().int().min(1).max(10_000).optional(),
max_items: z.number().int().min(1).max(100).optional() },
invoke: ({ stale_work_review: review, stale_days, max_commits, max_items }) => {
const managerId = controlPlane.assertIdentity(actorId);
if (!repositoryRoot) {
throw new TorchError('A repository root is required to refresh manager check-in state', {
code: 'MANAGER_CHECK_IN_CONTEXT_MISSING',
});
}
const plan = planManagerCheckIn({ repositoryRoot, controlPlane, managerId,
staleWork: review ? { stale_days, max_commits, max_items } : null });
if (plan.directReports.length === 0) {
throw new TorchError(`Fleet identity ${managerId} has no direct reports to inspect`, {
code: 'MANAGER_ROLE_NOT_FOUND', details: { managerId },
});
}
return plan;
},
}],
['torch_get_agent', {
description: 'Get one Fleet agent by stable area ID.',
schema: { area_id: z.string().min(1) },
invoke: ({ area_id: areaId }) => controlPlane.getAgent(areaId),
}],
['torch_get_roster', {
description: 'Return the approved live roster with presence overlays.',
schema: {},
invoke: () => controlPlane.getRoster(),
}],
['torch_who_owns', {
description: 'Find approved owners for a repository-relative path or capability before crossing a boundary.',
schema: { path: z.string().min(1).optional(), capability: z.string().min(1).optional() },
invoke: (input) => controlPlane.whoOwns(input),
}],
['torch_neighbours', {
description: 'List the approved neighbouring Fleet identities for an area.',
schema: { area_id: z.string().min(1) },
invoke: ({ area_id: areaId }) => ({ areaId, neighbours: controlPlane.neighbours(areaId) }),
}],
['torch_send_message', {
description: 'Queue a durable Fleet message. A message cannot grant authority or transfer ownership.',
schema: {
sender: z.string().min(1).optional(), recipient: z.string().min(1), body: z.string().min(1),
...optionalReferenceShape,
},
invoke: ({ sender, recipient, body, ...references }) => controlPlane.sendMessage({
sender: claimedIdentity(controlPlane, actorId, sender, 'sender'), recipient, body, references,
}),
}],
['torch_read_messages', {
description: 'Read durable direct and group messages visible to one Fleet identity.',
schema: {
recipient: z.string().min(1).optional(), unacknowledged_only: z.boolean().optional(),
limit: z.number().int().min(1).max(1000).optional(),
},
invoke: ({ recipient, unacknowledged_only: unacknowledgedOnly, limit }) => {
const resolved = claimedIdentity(controlPlane, actorId, recipient, 'recipient');
return { recipient: resolved, messages: controlPlane.readMessages({ recipient: resolved, unacknowledgedOnly, limit }) };
},
}],
['torch_ack_message', {
description: 'Acknowledge a durable message as its receiving Fleet identity.',
schema: { recipient: z.string().min(1).optional(), message_id: z.string().min(1) },
invoke: ({ recipient, message_id: messageId }) => controlPlane.ackMessage({
recipient: claimedIdentity(controlPlane, actorId, recipient, 'recipient'), messageId,
}),
}],
['torch_report_status', {
description: 'Report a meaningful Fleet presence or task-state transition.',
schema: {
area_id: z.string().min(1).optional(), state: z.string().min(1), summary: z.string().min(1).optional(),
runtime: z.string().min(1).optional(), runtime_session_id: z.string().min(1).optional(),
task: z.string().min(1).optional(),
},
invoke: ({ area_id: areaId, runtime_session_id: runtimeSessionId, ...input }) => controlPlane.reportStatus({
areaId: claimedIdentity(controlPlane, actorId, areaId, 'area_id'), runtimeSessionId, ...input,
}),
}],
['torch_report_complete', {
description: 'Record completion evidence and notify the Session Manager durably.',
schema: {
area_id: z.string().min(1).optional(), summary: z.string().min(1), task: z.string().min(1).optional(),
evidence: z.string().min(1).optional(), commit: z.string().min(1).optional(),
},
invoke: ({ area_id: areaId, ...input }) => controlPlane.reportComplete({
areaId: claimedIdentity(controlPlane, actorId, areaId, 'area_id'), ...input,
}),
}],
['torch_report_blocked', {
description: 'Record a blocker and notify the Session Manager durably.',
schema: {
area_id: z.string().min(1).optional(), summary: z.string().min(1), task: z.string().min(1).optional(),
evidence: z.string().min(1).optional(), path: z.string().min(1).optional(),
commit: z.string().min(1).optional(),
},
invoke: ({ area_id: areaId, ...input }) => controlPlane.reportBlocked({
areaId: claimedIdentity(controlPlane, actorId, areaId, 'area_id'), ...input,
}),
}],
['torch_request_approval', {
description: 'Create a durable approval request addressed to one named Fleet identity or the configured owner.',
schema: {
requester: z.string().min(1).optional(), approver: z.string().min(1), task: z.string().min(1).optional(),
title: z.string().min(1).max(240), summary: z.string().min(1).max(4000), evidence: z.string().min(1).max(4000).optional(),
},
invoke: ({ requester, ...input }) => controlPlane.requestApproval({
...input, requester: claimedIdentity(controlPlane, actorId, requester, 'requester'),
}),
}],
['torch_list_approvals', {
description: 'List pending and historical approval requests visible to the bound Fleet identity.',
schema: { actor_id: z.string().min(1).optional(), status: z.enum(['pending', 'approved', 'rejected', 'cancelled']).optional() },
invoke: ({ actor_id: actorIdClaim, status }) => ({ approvals: controlPlane.listApprovals({
actorId: claimedIdentity(controlPlane, actorId, actorIdClaim, 'actor_id'), status,
}) }),
}],
['torch_decide_approval', {
description: 'Approve or reject a request only when the bound identity is its named approver; decisions are audited and notified durably.',
schema: {
actor_id: z.string().min(1).optional(), approval_id: z.string().min(1),
decision: z.enum(['approved', 'rejected']), note: z.string().max(2000).optional(),
expected_revision: z.number().int().positive(),
},
invoke: ({ actor_id: actorIdClaim, approval_id: approvalId, decision, note, expected_revision: expectedRevision }) =>
controlPlane.decideApproval({
approvalId, decidedBy: claimedIdentity(controlPlane, actorId, actorIdClaim, 'actor_id'),
decision, note, expectedRevision,
}),
}],
['torch_request_coordination', {
description: 'Ask the Session Manager for a durable cross-domain coordination ruling.',
schema: {
sender: z.string().min(1).optional(), participants: z.array(z.string().min(1)).optional(),
body: z.string().min(1), task: z.string().min(1).optional(), path: z.string().min(1).optional(),
},
invoke: ({ sender, ...input }) => controlPlane.requestCoordination({
sender: claimedIdentity(controlPlane, actorId, sender, 'sender'), ...input,
}),
}],
['torch_request_handoff', {
description: 'Request, but do not perform, a task or path ownership handoff.',
schema: {
sender: z.string().min(1).optional(), recipient: z.string().min(1).optional(),
path: z.string().min(1).optional(), task: z.string().min(1).optional(), reason: z.string().min(1),
},
invoke: ({ sender, ...input }) => controlPlane.requestHandoff({
sender: claimedIdentity(controlPlane, actorId, sender, 'sender'), ...input,
}),
}],
]);
if (backlogService) {
tools.set('torch_list_backlog', {
description: 'List durable tracked backlog tasks, optionally filtered by state or owner.',
schema: { state: z.string().min(1).optional(), owner: z.string().min(1).optional() },
invoke: (input) => ({ tasks: backlogService.list(input) }),
});
tools.set('torch_next_backlog_task', {
description: 'Resume this specialist\'s active assignment before returning its first eligible ready task. Read-only.',
schema: { area_id: z.string().min(1).optional() },
invoke: ({ area_id: requestedArea }) => {
const bound = controlPlane.assertIdentity(actorId);
const areaId = requestedArea ?? bound;
if (bound !== 'session-manager' && areaId !== bound) {
throw new TorchError('A specialist may only inspect its own next backlog item', {
code: 'BACKLOG_AUTHORITY_REQUIRED', details: { actorId: bound, areaId },
});
}
return backlogService.next({ areaId });
},
});
tools.set('torch_backlog_health', {
description: 'Report unusual backlog conditions without changing any task.',
schema: {
stale_after_days: z.number().int().min(0).optional(),
stale_observed_commits: z.number().int().min(0).optional(),
},
invoke: ({ stale_after_days: staleAfterDays, stale_observed_commits: staleObservedCommits }) =>
backlogService.health({ staleAfterDays, staleObservedCommits }),
});
tools.set('torch_backlog_activity', {
description: 'Observe commit-linked task activity and neglected requests on managed branches. Read-only; closure text is intent, not authority.',
schema: { stale_days: z.number().int().min(1).max(365).optional(), max_commits: z.number().int().min(1).max(10_000).optional() },
invoke: ({ stale_days: staleDays, max_commits: maxCommits }) => backlogService.activity({ staleDays, maxCommits }),
});
for (const [name, method] of [
['torch_plan_landed_task_closure', 'planLandedClosure'], ['torch_reconcile_landed_tasks', 'reconcileLanded'],
]) tools.set(name, {
description: 'Manager-only exact landed task closure: preview or explicitly reconcile Closes intent with current evidence.',
schema: { integration_request: z.string().min(1), approved: z.boolean().optional() },
invoke: ({ integration_request: integrationRequest, approved }) => backlogService[method]({ integrationRequest, actorId, approved }),
});
for (const [name, method] of [
['torch_plan_backlog_claim', 'planClaim'], ['torch_claim_next_backlog_task', 'claimNext'],
]) tools.set(name, {
description: method === 'planClaim'
? 'Preview policy-controlled self-claim eligibility for the bound specialist without mutation.'
: 'Atomically claim the next explicitly routed ready task under approved policy, or resume current work. Never grants manager authority.',
schema: { area_id: z.string().min(1).optional() },
invoke: ({ area_id: areaId }) => backlogService[method]({
areaId: claimedIdentity(controlPlane, actorId, areaId, 'area_id'),
}),
});
tools.set('torch_get_backlog_task', {
description: 'Read one durable tracked backlog task and its transition history.',
schema: { task_id: z.string().min(1) },
invoke: ({ task_id: taskId }) => backlogService.get(taskId),
});
tools.set('torch_create_backlog_task', {
description: 'Create proposed work as the bound Session Manager identity.',
schema: {
actor_id: z.string().min(1).optional(), title: z.string().min(1), description: z.string().min(1),
priority: z.enum(['urgent', 'high', 'normal', 'low']).optional(),
affected_domains: z.array(z.string().min(1)).optional(),
dependencies: z.array(z.string().min(1)).optional(),
feature: z.string().min(1).max(120).optional(), milestone: z.string().min(1).max(120).optional(),
acceptance_criteria: z.array(z.string().min(1)).min(1),
observed_at: z.string().min(1).optional(),
},
invoke: ({
actor_id: actorIdClaim, affected_domains: affectedDomains,
acceptance_criteria: acceptanceCriteria, observed_at: observedAt, ...input
}) => backlogService.create({
...input, affectedDomains, acceptanceCriteria, observedAt,
actorId: claimedIdentity(controlPlane, actorId, actorIdClaim, 'actor_id'),
}),
});
tools.set('torch_classify_backlog_task', {
description: 'Set or explicitly clear a task\'s feature and milestone labels as the Session Manager. Revision-checked and non-destructive to status or ownership.',
schema: {
task_id: z.string().min(1), actor_id: z.string().min(1).optional(),
expected_revision: z.number().int().min(1),
feature: z.string().min(1).max(120).optional(), milestone: z.string().min(1).max(120).optional(),
clear_feature: z.boolean().optional(), clear_milestone: z.boolean().optional(),
reason: z.string().min(1),
},
invoke: ({
task_id: taskId, actor_id: actorIdClaim, expected_revision: expectedRevision,
clear_feature: clearFeature, clear_milestone: clearMilestone, ...input
}) => backlogService.classify({
...input, taskId, expectedRevision, clearFeature, clearMilestone,
actorId: claimedIdentity(controlPlane, actorId, actorIdClaim, 'actor_id'),
}),
});
tools.set('torch_transition_backlog_task', {
description: 'Apply one revision-checked backlog state transition under bound Fleet authority.',
schema: {
task_id: z.string().min(1), actor_id: z.string().min(1).optional(), to: z.string().min(1),
expected_revision: z.number().int().min(1), owner: z.string().min(1).optional(),
evidence: z.array(z.string().min(1)).optional(), commit: z.string().min(1).optional(),
integration_request: z.string().min(1).optional(), blocked_reason: z.string().min(1).optional(),
note: z.string().min(1).optional(),
},
invoke: ({
task_id: taskId, actor_id: actorIdClaim, expected_revision: expectedRevision,
integration_request: integrationRequest, blocked_reason: blockedReason, ...input
}) => backlogService.transition({
...input, taskId, expectedRevision, integrationRequest, blockedReason,
actorId: claimedIdentity(controlPlane, actorId, actorIdClaim, 'actor_id'),
}),
});
}
if (checkService) {
tools.set('torch_list_checks', {
description: 'List approved project check definitions.', schema: {},
invoke: () => ({ checks: checkService.listChecks() }),
});
tools.set('torch_list_check_receipts', {
description: 'List immutable check receipts, optionally filtered by exact commit, check, or bound area.',
schema: {
commit: z.string().min(1).optional(), check_id: z.string().min(1).optional(),
area_id: z.string().min(1).optional(),
},
invoke: ({ commit, check_id: checkId, area_id: areaId }) => ({
receipts: checkService.receipts({
commit, checkId,
areaId: areaId ? claimedIdentity(controlPlane, actorId, areaId, 'area_id') : undefined,
}),
}),
});
tools.set('torch_plan_check', {
description: 'Preview a configured check against the bound area and report blockers without executing it.',
schema: { check_id: z.string().min(1), area_id: z.string().min(1).optional() },
invoke: ({ check_id: checkId, area_id: areaId }) => checkService.plan({
checkId, areaId: claimedIdentity(controlPlane, actorId, areaId, 'area_id'),
}),
});
tools.set('torch_run_check', {
description: 'Execute an approved shell-free check in the bound clean worktree and record an exact-SHA receipt.',
schema: { check_id: z.string().min(1), area_id: z.string().min(1).optional() },
invoke: ({ check_id: checkId, area_id: areaId }) => checkService.run({
checkId, areaId: claimedIdentity(controlPlane, actorId, areaId, 'area_id'),
}),
});
tools.set('torch_prepare_check', {
description: 'Capture approved frozen check inputs before waiting for resource leases; does not execute the check.',
schema: { check_id: z.string().min(1), area_id: z.string().min(1).optional() },
invoke: ({ check_id: checkId, area_id: areaId }) => checkService.prepare({
checkId, areaId: claimedIdentity(controlPlane, actorId, areaId, 'area_id'),
}),
});
tools.set('torch_list_prepared_checks', {
description: 'List durable frozen checks for the bound identity, including interrupted running checks.',
schema: { area_id: z.string().min(1).optional() },
invoke: ({ area_id: areaId }) => ({ prepared: checkService.prepared({
areaId: claimedIdentity(controlPlane, actorId, areaId, 'area_id'),
}) }),
});
for (const [name, method] of [
['torch_run_prepared_check', 'runPrepared'], ['torch_cancel_prepared_check', 'cancelPrepared'],
]) tools.set(name, {
description: method === 'runPrepared'
? 'Run an owned frozen check after acquiring its resources; record its captured SHA, not the current worktree SHA.'
: 'Cancel an owned unstarted frozen check and remove only its owned input copy.',
schema: { prepared_id: z.string().min(1), area_id: z.string().min(1).optional() },
invoke: ({ prepared_id: preparedId, area_id: areaId }) => checkService[method]({
preparedId, areaId: claimedIdentity(controlPlane, actorId, areaId, 'area_id'),
}),
});
}
if (resourceService) {
tools.set('torch_list_resources', {
description: 'List configured scarce resources and capacities.', schema: {},
invoke: () => ({ resources: resourceService.list() }),
});
tools.set('torch_resource_status', {
description: 'Show holders, stale leases, and the FIFO queue for a resource.',
schema: { resource_id: z.string().min(1) },
invoke: ({ resource_id: resourceId }) => resourceService.status(resourceId),
});
tools.set('torch_acquire_resource', {
description: 'Acquire or join the FIFO queue for a scarce resource as the bound Fleet identity.',
schema: { resource_id: z.string().min(1), area_id: z.string().min(1).optional() },
invoke: ({ resource_id: resourceId, area_id: areaId }) => resourceService.acquire({
resourceId, areaId: claimedIdentity(controlPlane, actorId, areaId, 'area_id'),
}),
});
tools.set('torch_release_resource', {
description: 'Release a resource lease held by the bound Fleet identity.',
schema: { resource_id: z.string().min(1), area_id: z.string().min(1).optional() },
invoke: ({ resource_id: resourceId, area_id: areaId }) => resourceService.release({
resourceId, areaId: claimedIdentity(controlPlane, actorId, areaId, 'area_id'),
}),
});
tools.set('torch_cancel_resource_request', {
description: 'Cancel the bound Fleet identity queued request without leaking a lease.',
schema: { resource_id: z.string().min(1), area_id: z.string().min(1).optional() },
invoke: ({ resource_id: resourceId, area_id: areaId }) => resourceService.cancel({
resourceId, areaId: claimedIdentity(controlPlane, actorId, areaId, 'area_id'),
}),
});
}
if (integrationService) {
tools.set('torch_list_integrations', {
description: 'List native integration queue records and their exact readiness states.',
schema: { state: z.string().min(1).optional() },
invoke: ({ state }) => ({ requests: integrationService.list({ state }) }),
});
tools.set('torch_request_integration', {
description: 'Request integration of the bound domain branch tip and its exact commit receipts.',
schema: { area_id: z.string().min(1).optional(), commit: z.string().min(1).optional() },
invoke: ({ area_id: areaId, commit }) => integrationService.request({
areaId: claimedIdentity(controlPlane, actorId, areaId, 'area_id'), commit,
}),
});
tools.set('torch_evaluate_integration', {
description: 'Re-evaluate source, convergence, and exact-SHA checks for an integration request.',
schema: { request_id: z.string().min(1) },
invoke: ({ request_id: requestId }) => integrationService.evaluate(requestId),
});
tools.set('torch_authorize_integration', {
description: 'Authorize a ready integration request as the bound identity when policy grants authority.',
schema: { request_id: z.string().min(1), area_id: z.string().min(1).optional() },
invoke: ({ request_id: requestId, area_id: areaId }) => integrationService.authorize({
requestId, actorId: claimedIdentity(controlPlane, actorId, areaId, 'area_id'),
}),
});
tools.set('torch_plan_integration_landing', {
description: 'Preview all main-protection gates without changing canonical main.',
schema: { request_id: z.string().min(1), area_id: z.string().min(1).optional() },
invoke: ({ request_id: requestId, area_id: areaId }) => integrationService.planLanding({
requestId, actorId: claimedIdentity(controlPlane, actorId, areaId, 'area_id'),
}),
});
tools.set('torch_land_integration', {
description: 'Fast-forward canonical main only after durable authorization and every protection gate passes.',
schema: { request_id: z.string().min(1), area_id: z.string().min(1).optional() },
invoke: ({ request_id: requestId, area_id: areaId }) => integrationService.land({
requestId, actorId: claimedIdentity(controlPlane, actorId, areaId, 'area_id'),
}),
});
}
if (contextTelemetryService) {
tools.set('torch_record_context_usage', {
description: 'Record measured or explicitly estimated context usage for the bound Fleet identity and its verified outcomes.',
schema: {
area_id: z.string().min(1).optional(), source: z.string().min(1), measurement: z.enum(['measured', 'estimated']),
runtime: z.string().min(1).optional(), runtime_session_id: z.string().min(1).optional(),
task: z.string().min(1).optional(), commit: z.string().min(1).optional(),
cached_input: z.number().int().nonnegative().optional(), uncached_input: z.number().int().nonnegative().optional(),
cache_creation: z.number().int().nonnegative().optional(), cache_read: z.number().int().nonnegative().optional(),
compactions: z.number().int().nonnegative().optional(), resumed_prompt_bytes: z.number().int().nonnegative().optional(),
cost_microusd: z.number().int().nonnegative().optional(), verified_items: z.number().int().nonnegative().optional(),
},
invoke: ({
area_id: areaId, runtime_session_id: runtimeSessionId, cached_input: cachedInput,
uncached_input: uncachedInput, cache_creation: cacheCreation, cache_read: cacheRead,
resumed_prompt_bytes: resumedPromptBytes, cost_microusd: costMicrousd,
verified_items: verifiedItems, ...input
}) => contextTelemetryService.record({
...input, runtimeSessionId, cachedInput, uncachedInput, cacheCreation, cacheRead,
resumedPromptBytes, costMicrousd, verifiedItems,
areaId: claimedIdentity(controlPlane, actorId, areaId, 'area_id'),
}),
});
tools.set('torch_context_report', {
description: 'Aggregate context locality by Fleet identity while keeping measured and estimated evidence separate.',
schema: {
area_id: z.string().min(1).optional(), task: z.string().min(1).optional(),
commit: z.string().min(1).optional(), measurement: z.enum(['measured', 'estimated']).optional(),
},
invoke: ({ area_id: areaId, ...input }) => contextTelemetryService.report({
...input, areaId: areaId ? claimedIdentity(controlPlane, actorId, areaId, 'area_id') : actorId,
}),
});
}
if (scheduleService) {
tools.set('torch_list_schedules', {
description: 'List validated schedules and their current authority, lifetime, and due status.',
schema: {}, invoke: () => ({ schedules: scheduleService.list({ actorId }) }),
});
tools.set('torch_schedule_runs', {
description: 'List durable run evidence for configured schedules.',
schema: { schedule_id: z.string().min(1).optional(), limit: z.number().int().min(1).max(1000).optional() },
invoke: ({ schedule_id: scheduleId, limit }) => ({ runs: scheduleService.runs({ scheduleId, limit }) }),
});
tools.set('torch_plan_schedule', {
description: 'Preview a configured schedule and its blockers without executing it.',
schema: { schedule_id: z.string().min(1) },
invoke: ({ schedule_id: scheduleId }) => scheduleService.plan({ scheduleId, actorId }),
});
tools.set('torch_run_schedule', {
description: 'Run a read-only configured schedule as the bound Fleet identity. Coordination and mutating schedules cannot run through this MCP tool; they require an owner-approved CLI action or an approved, digest-matched system timer.',
schema: { schedule_id: z.string().min(1) },
invoke: ({ schedule_id: scheduleId }) => scheduleService.run({ scheduleId, actorId, approved: false }),
});
}
if (evolutionService) {
tools.set('torch_list_fleet_changes', {
description: 'List durable proposed, approved, provisioning, or active Fleet organization changes.',
schema: { state: z.enum(['proposed', 'approved', 'provisioning', 'active', 'retired', 'rejected']).optional() },
invoke: ({ state }) => ({ changes: evolutionService.list({ state }) }),
});
tools.set('torch_get_fleet_change', {
description: 'Read one durable Fleet organization change and its owner-approval state.',
schema: { change_id: z.string().min(1) },
invoke: ({ change_id: changeId }) => evolutionService.get(changeId),
});
tools.set('torch_assess_fleet_evolution', {
description: 'Inspect durable backlog, handoff, coordination, and pending-change evidence for recurring boundaries that may merit a new persistent domain. Advisory and read-only.',
schema: { assessor: z.string().min(1).optional() },
invoke: ({ assessor }) => evolutionService.assessDomainNeeds({
assessor: claimedIdentity(controlPlane, actorId, assessor, 'assessor'),
}),
});
tools.set('torch_propose_domain', {
description: 'Propose an evidence-backed persistent domain as the bound Session Manager. This never approves or activates it.',
schema: {
proposer: z.string().min(1).optional(),
domain: z.object({
id: z.string().min(1), title: z.string().min(1), kind: z.string().min(1).optional(),
scope: z.array(z.string().min(1)).min(1), not_scope: z.array(z.string().min(1)).optional(),
owned_paths: z.array(z.string().min(1)).min(1), shared_paths: z.array(z.string().min(1)).optional(),
neighbours: z.array(z.string().min(1)).optional(), required_checks: z.array(z.string().min(1)).optional(),
resources: z.array(z.string().min(1)).optional(),
runtime: z.string().min(1).optional(), branch: z.string().min(1).optional(),
model: z.string().min(1).optional(),
worktree_name: z.string().min(1).optional(),
}),
rationale: z.string().min(1),
expected_benefit: z.object({
summary: z.string().min(1), recurring_work: z.string().min(1),
context_locality: z.string().min(1), coordination_cost: z.string().min(1),
}),
evidence: z.array(z.string().min(1)).min(1),
},
invoke: ({ proposer, expected_benefit: benefit, ...input }) => evolutionService.proposeDomain({
...input,
proposer: claimedIdentity(controlPlane, actorId, proposer, 'proposer'),
expectedBenefit: {
summary: benefit.summary, recurringWork: benefit.recurring_work,
contextLocality: benefit.context_locality, coordinationCost: benefit.coordination_cost,
},
}),
});
tools.set('torch_propose_domain_retirement', {
description: 'Propose retiring a persistent domain when its coordination cost exceeds its continuing benefit. This never approves or activates retirement.',
schema: {
proposer: z.string().min(1).optional(), area_id: z.string().min(1), rationale: z.string().min(1),
expected_benefit: z.object({
summary: z.string().min(1), recurring_work: z.string().min(1),
context_locality: z.string().min(1), coordination_cost: z.string().min(1),
}),
evidence: z.array(z.string().min(1)).min(1),
},
invoke: ({ proposer, area_id: areaId, expected_benefit: benefit, ...input }) => evolutionService.proposeRetirement({
...input, areaId,
proposer: claimedIdentity(controlPlane, actorId, proposer, 'proposer'),
expectedBenefit: {
summary: benefit.summary, recurringWork: benefit.recurring_work,
contextLocality: benefit.context_locality, coordinationCost: benefit.coordination_cost,
},
}),
});
const boundaryProposalSchema = {
proposer: z.string().min(1).optional(),
source_domains: z.array(z.string().min(1)).min(1),
result_domains: z.array(z.object({
id: z.string().min(1), title: z.string().min(1), kind: z.string().min(1).optional(),
scope: z.array(z.string().min(1)).min(1), not_scope: z.array(z.string().min(1)).optional(),
owned_paths: z.array(z.string().min(1)).min(1), shared_paths: z.array(z.string().min(1)).optional(),
neighbours: z.array(z.string().min(1)).optional(), required_checks: z.array(z.string().min(1)).optional(),
resources: z.array(z.string().min(1)).optional(), runtime: z.string().min(1).optional(),
model: z.string().min(1).optional(), branch: z.string().min(1).optional(),
worktree_name: z.string().min(1).optional(),
})).min(1),
ownership_assignments: z.array(z.object({
source_domain_id: z.string().min(1), source_path: z.string().min(1), result_domain_id: z.string().min(1),
})).min(1),
rationale: z.string().min(1),
expected_benefit: z.object({
summary: z.string().min(1), recurring_work: z.string().min(1),
context_locality: z.string().min(1), coordination_cost: z.string().min(1),
}),
evidence: z.array(z.string().min(1)).min(1),
};
const invokeBoundaryProposal = (operation) => ({
proposer, source_domains: sourceDomains, result_domains: resultDomains,
ownership_assignments: ownershipAssignments, expected_benefit: benefit, ...input
}) => evolutionService[operation]({
...input, sourceDomains, resultDomains, ownershipAssignments,
proposer: claimedIdentity(controlPlane, actorId, proposer, 'proposer'),
expectedBenefit: {
summary: benefit.summary, recurringWork: benefit.recurring_work,
contextLocality: benefit.context_locality, coordinationCost: benefit.coordination_cost,
},
});
tools.set('torch_propose_domain_merge', {
description: 'Propose an evidence-backed merge of persistent domains. Approval records intent but cannot automatically migrate ownership.',
schema: boundaryProposalSchema,
invoke: invokeBoundaryProposal('proposeMerge'),
});
tools.set('torch_propose_domain_split', {
description: 'Propose an evidence-backed split of a persistent domain. Approval records intent but cannot automatically migrate ownership.',
schema: boundaryProposalSchema,
invoke: invokeBoundaryProposal('proposeSplit'),
});
}
if (hierarchyService) {
tools.set('torch_assess_hierarchy_needs', {
description: 'Measure recurring coordination, cross-domain work, handoffs, and approval waits in the configured observation window. Read-only; it never creates or activates a role.',
schema: { assessor: z.string().min(1).optional() },
invoke: ({ assessor }) => hierarchyService.assess({
assessor: claimedIdentity(controlPlane, actorId, assessor, 'assessor'),
}),
});
tools.set('torch_list_hierarchy_proposals', {
description: 'List durable hierarchy proposals and their owner-review state.',
schema: { state: z.enum(['proposed', 'pilot-approved', 'deferred', 'rejected', 'piloting', 'adopted', 'reversed']).optional() },
invoke: ({ state }) => ({ proposals: hierarchyService.list({ state }) }),
});
tools.set('torch_get_hierarchy_proposal', {
description: 'Read a durable hierarchy proposal, evidence, pilot criteria, and owner decision.',
schema: { proposal_id: z.string().min(1) },
invoke: ({ proposal_id: proposalId }) => hierarchyService.get(proposalId),
});
tools.set('torch_propose_hierarchy_change', {
description: 'Submit a sustained-evidence, owner-review hierarchy proposal. This cannot change active roles or start sessions.',
schema: {
proposer: z.string().min(1).optional(),
proposal: z.record(z.string(), z.unknown()),
},
invoke: ({ proposer, proposal }) => hierarchyService.propose({
proposer: claimedIdentity(controlPlane, actorId, proposer, 'proposer'), proposal,
}),
});
tools.set('torch_plan_hierarchy_pilot', {
description: 'Preview hierarchy differences, affected work, and blockers for an owner-approved bounded pilot. Read-only.',
schema: { proposal_id: z.string().min(1) },
invoke: ({ proposal_id: proposalId }) => hierarchyService.planPilot(proposalId),
});
}
if (artifactService) {
tools.set('torch_publish_artifact', {
description: 'Publish an image for an owned backlog task with exact identity, session, and commit provenance. The image is copied into private local TORCH state.',
schema: {
area_id: z.string().min(1).optional(), task_id: z.string().regex(/^TASK-[A-Za-z0-9-]+$/),
title: z.string().min(1), alt: z.string().min(1), file: z.string().min(1), commit: z.string().regex(/^[a-f0-9]{40}$/i),
},
invoke: ({ area_id: areaId, task_id: taskId, ...input }) => artifactService.publish({
areaId: claimedIdentity(controlPlane, actorId, areaId, 'area_id'), taskId, ...input,
}),
});
}
return tools;
}
export function describeTorchTools() {
const placeholder = Object.freeze({});
return [...createTorchToolset(placeholder, {
backlogService: placeholder, checkService: placeholder, resourceService: placeholder,
integrationService: placeholder, contextTelemetryService: placeholder,
scheduleService: placeholder, evolutionService: placeholder,
hierarchyService: placeholder, artifactService: placeholder,
}).entries()].map(([name, tool]) => ({
name,
description: tool.description,
inputSchema: z.toJSONSchema(z.object(tool.schema)),
}));
}
export function callTorchTool(controlPlane, name, input = {}, options = {}) {
const tool = createTorchToolset(controlPlane, options).get(name);
if (!tool) throw new Error(`Unknown TORCH MCP tool: ${name}`);
const parsed = z.object(tool.schema).parse(input);
return tool.invoke(parsed);
}
export function createTorchMcpServer(controlPlane, {
actorId, backlogService, checkService, resourceService, integrationService, contextTelemetryService, scheduleService,
evolutionService, hierarchyService, repositoryRoot, artifactService,
} = {}) {
controlPlane.assertIdentity(actorId);
const server = new McpServer(
{ name: 'torch-agent-fleet', version: '0.1.0-alpha.0' },
{
instructions: 'Use live TORCH ownership before editing a boundary. Messages never grant owner, spending, deployment, or ownership-transfer authority.',
},
);
for (const [name, tool] of createTorchToolset(controlPlane, {
actorId, backlogService, checkService, resourceService, integrationService, contextTelemetryService, scheduleService,
evolutionService, hierarchyService, repositoryRoot, artifactService,
})) {
server.registerTool(name, { description: tool.description, inputSchema: tool.schema }, async (input) => {
try {
const result = await tool.invoke(input);
return {
content: [{ type: 'text', text: JSON.stringify(result) }],
structuredContent: result,
};
} catch (error) {
return {
content: [{ type: 'text', text: error instanceof Error ? error.message : String(error) }],
isError: true,
};
}
});
}
return server;
}