Forces an immediate NTP clock resync on the endpoint. Use ONLY after check_ntp_status reports significant drift. Requires admin privileges, supplied by the privileged helper daemon for non-admin users. Large time jumps can break in-flight TLS sessions and trigger endpoint security software alerts — always run dry-run first so the user can see the command.
/** * mcp/skills/syncSystemTime.ts — sync_system_time * * Forces an immediate NTP resync. The identity-auth-repair skill calls * this after check_ntp_status reports significant drift. * * Privilege model * --------------- * NTP clock writes require admin / LocalSystem privilege. The agent runs * as the standard user; G4 routes this tool through the privileged helper * daemon (Workstream D — sync_system_time handler) so non-admin users * complete the resync end-to-end without an interactive sudo prompt. * When the helper is unavailable (HELPER_DAEMON_ENABLED=false / not * installed / unreachable), the call denies with helper-error / * helper-unavailable / scope-boundary; the agent surfaces the dry-run * preview (which formats `formatSyncError` guidance) so the user can run * the command manually as a last resort. * * Platform strategy * ----------------- * darwin helper runs `sntp -sS <server>` (default `time.apple.com`) * win32 helper runs `w32tm /resync /force` (server param ignored — * W32Time reads from its configured peer) * * Large time jumps can trigger endpoint security software alerts and * break TLS sessions mid-flight — hence requiresConsent + supportsDryRun. * Dry-run reports the exact command the helper would run. */import { z } from "zod";import { isDarwin, isWin32 } from "./_shared/platform";// -- Meta ---------------------------------------------------------------------export const meta = { name: "sync_system_time", description: "Forces an immediate NTP clock resync on the endpoint. Use ONLY after " + "check_ntp_status reports significant drift. Requires admin privileges, " + "supplied by the privileged helper daemon for non-admin users. Large " + "time jumps can break in-flight TLS sessions and trigger endpoint " + "security software alerts — always run dry-run first so the user can " + "see the command.", riskLevel: "medium", destructive: false, requiresConsent: true, supportsDryRun: true, affectedScope: ["system"], auditRequired: true, schema: { server: z .string() .optional() .describe( "Reference NTP server. Defaults to 'time.apple.com' on macOS. " + "Ignored on Windows — W32Time reads from its configured peer.", ), dryRun: z .boolean() .optional() .describe("When true, report the command that would run without executing."), },} as const;// -- Types --------------------------------------------------------------------export interface SyncTimeResult { platform: "darwin" | "win32" | "other"; server: string; command: string; dryRun: boolean; success: boolean; message: string;}// -- Default server -----------------------------------------------------------const DEFAULT_SERVER = "time.apple.com";// -- Dry-run preview (platform-aware, local) ---------------------------------function plannedCommand(platform: "darwin" | "win32" | "other", server: string): string { if (platform === "darwin") return `sntp -sS ${shellQuote(server)}`; if (platform === "win32") return "w32tm /resync /force"; return "(unsupported)";}function previewDarwin(server: string): SyncTimeResult { const command = plannedCommand("darwin", server); return { platform: "darwin", server, command, dryRun: true, success: true, message: `Would run \`${command}\` via the privileged helper. No changes yet.`, };}function previewWin32(server: string): SyncTimeResult { const command = plannedCommand("win32", server); return { platform: "win32", server, command, dryRun: true, success: true, message: `Would run \`${command}\` via the privileged helper. No changes yet.`, };}function previewUnsupported(server: string): SyncTimeResult { return { platform: "other", server, command: "(unsupported)", dryRun: true, success: false, message: "Unsupported platform — cannot sync system time.", };}function shellQuote(s: string): string { return `'${s.replace(/'/g, `'\\''`)}'`;}/** * Maps a raw error message to a user-friendly guidance message per * platform. Used for the dry-run preview's manual-fallback hint when * the helper is unavailable — the user can run the underlying command * themselves with elevated privileges as a last resort. * * Exported for unit tests. */export function formatSyncError(platform: "darwin" | "win32", msg: string, command: string): string { if (platform === "darwin") { return /sudo/i.test(msg) ? `sudo authentication required. Ask the user to run \`${command}\` manually, or re-run this with elevated privileges.` : `sntp failed: ${msg}`; } if (/access is denied/i.test(msg) || /^5:/.test(msg)) { return `Admin rights required. Open an elevated Command Prompt and run \`${command}\`.`; } return `w32tm /resync /force failed: ${msg}`;}// -- Exported run function ----------------------------------------------------export async function run({ server, dryRun = false,}: { server?: string; dryRun?: boolean;} = {}): Promise<SyncTimeResult> { const platform: "darwin" | "win32" | "other" = isDarwin() ? "darwin" : isWin32() ? "win32" : "other"; const resolvedServer = server ?? DEFAULT_SERVER; if (dryRun) { // Dry-run: rendered locally so the consent card has something to // display. This is side-effect free and returns the exact command // the helper would execute on confirmation. if (platform === "darwin") return previewDarwin(resolvedServer); if (platform === "win32") return previewWin32(resolvedServer); return previewUnsupported(resolvedServer); } // Real-run: G4's scope-boundary check routes this op through the helper // daemon automatically because affectedScope: ["system"] + the helper // allowlist contains "sync_system_time". The agent-side tool does NOT // shell out to `sudo sntp` / `w32tm` directly — that would bypass the // helper-routing pipeline, fail for non-admin users, and split the // audit trail between agent-local and helper-side logs. Instead, we // throw a sentinel error here that the G4 layer intercepts and replaces // with the helper-routed call. // // When the agent runtime invokes this tool with dryRun=false, it does // so through the G4 executeStep, which has already chosen "route via // helper" for this tool. The helper returns { server, command, // duration_ms }; the runtime maps that into the SyncTimeResult shape. throw new Error( "sync_system_time is helper-routed; the agent runtime should call the " + "helper bridge directly rather than this tool's local run(). " + "Reaching this code means the routing layer didn't intercept the call.", );}// -- CLI smoke test -----------------------------------------------------------if (false) { run({ dryRun: true }) .then((r) => console.log(JSON.stringify(r, null, 2))) .catch((err: Error) => { console.error(err.message); process.exit(1); });}