Code

/**
 * mcp/skills/resetNetworkSettings.ts — reset_network_settings skill
 *
 * Resets network configuration to defaults by removing custom network locations
 * and recreating the Automatic location.
 * Use when network settings are corrupt or misconfigured and simpler fixes have failed.
 *
 * Platform strategy
 * -----------------
 * darwin  networksetup -setnetworkserviceenabled "<service>" off → on (bounce)
 * win32   PowerShell netsh int ip reset && netsh winsock reset (reboot required)
 *
 * Smoke test
 *   npx tsx -r dotenv/config mcp/skills/resetNetworkSettings.ts
 */
 
import * as os       from "os";
import { exec }      from "child_process";
import { promisify } from "util";
import { z }         from "zod";
 
const execAsync = promisify(exec);
 
// -- Meta ---------------------------------------------------------------------
 
export const meta = {
  name: "reset_network_settings",
  description:
    "Resets network configuration to defaults by removing custom network locations " +
    "and recreating the Automatic location. " +
    "Use when network settings are corrupt or misconfigured and simpler fixes have failed.",
  riskLevel:       "high",
  destructive:     true,
  requiresConsent: true,
  supportsDryRun:  true,
  affectedScope:   ["network", "system"],
  auditRequired:   true,
  escalationHint:  {
    darwin: "sudo networksetup -setnetworkserviceenabled '<service>' off && sudo networksetup -setnetworkserviceenabled '<service>' on  # substitute the service name, e.g. Wi-Fi",
    win32:  "netsh int ip reset && netsh winsock reset  # run from elevated Command Prompt; reboot afterwards",
  },
  schema: {
    // snake_case `interface` matches the privileged helper's struct Params field
    // (required). macOS: network SERVICE name ("Wi-Fi", "Ethernet"). Windows:
    // interface name. G4 forwards executor params verbatim to the helper.
    interface: z
      .string()
      .describe(
        "Network service/interface to reset. macOS: service name (e.g. " +
        "\"Wi-Fi\", \"Ethernet\"). Windows: interface name. Pass the active " +
        "interface from get_network_interfaces.",
      ),
    dryRun: z
      .boolean()
      .optional()
      .describe("If true, show what would be reset without modifying. Default: true"),
  },
} as const;
 
// -- Types --------------------------------------------------------------------
 
interface ResetResult {
  interface:      string;
  reset:          boolean;
  dryRun:         boolean;
  rebootRequired: boolean;
  message:        string;
}
 
// -- PowerShell helper --------------------------------------------------------
 
async function runPS(script: string): Promise<string> {
  const encoded = Buffer.from(script, "utf16le").toString("base64");
  const { stdout } = await execAsync(
    `powershell.exe -NoProfile -NonInteractive -EncodedCommand ${encoded}`,
    { maxBuffer: 10 * 1024 * 1024 },
  );
  return stdout.trim();
}
 
// -- darwin implementation ----------------------------------------------------
 
async function resetDarwin(iface: string, dryRun: boolean): Promise<ResetResult> {
  // Match the privileged helper: bounce the named network SERVICE off→on, which
  // tears down and rebuilds its config (DHCP lease, routes) without the heavier
  // location surgery the old local path did. No reboot needed.
  if (dryRun) {
    return {
      interface:      iface,
      reset:          false,
      dryRun:         true,
      rebootRequired: false,
      message:        `Dry run: would disable then re-enable network service "${iface}" (networksetup -setnetworkserviceenabled off/on). Run with dryRun=false to apply.`,
    };
  }
 
  const safeSvc = iface.replace(/'/g, "'\\''");
  try {
    await execAsync(`networksetup -setnetworkserviceenabled '${safeSvc}' off 2>/dev/null`);
    await new Promise((r) => setTimeout(r, 1500));
    await execAsync(`networksetup -setnetworkserviceenabled '${safeSvc}' on 2>/dev/null`);
    return {
      interface:      iface,
      reset:          true,
      dryRun:         false,
      rebootRequired: false,
      message:        `Network service "${iface}" reset (disabled then re-enabled).`,
    };
  } catch (e) {
    return {
      interface:      iface,
      reset:          false,
      dryRun:         false,
      rebootRequired: false,
      message:        `Failed to reset network service "${iface}": ${(e as Error).message}. Check the service name with get_network_interfaces.`,
    };
  }
}
 
// -- win32 implementation -----------------------------------------------------
 
async function resetWin32(iface: string, dryRun: boolean): Promise<ResetResult> {
  // Windows reset is the global TCP/IP + Winsock stack reset; `netsh` takes no
  // interface argument, so `iface` is accepted for contract parity with the
  // helper but the operation is system-wide (matches the helper's platform note).
  if (dryRun) {
    return {
      interface:      iface,
      reset:          false,
      dryRun:         true,
      rebootRequired: true,
      message:        "Dry run: would run 'netsh int ip reset' and 'netsh winsock reset' (system-wide). A reboot is required after reset. Run with dryRun=false to execute.",
    };
  }
 
  const errors: string[] = [];
  try {
    await execAsync("netsh int ip reset 2>&1", { timeout: 30000 });
  } catch (e) { errors.push(`ip reset: ${(e as Error).message}`); }
 
  try {
    await execAsync("netsh winsock reset 2>&1", { timeout: 30000 });
  } catch (e) { errors.push(`winsock reset: ${(e as Error).message}`); }
 
  return {
    interface:      iface,
    reset:          errors.length === 0,
    dryRun:         false,
    rebootRequired: true,
    message:        errors.length > 0
      ? `Network reset completed with errors: ${errors.join("; ")}. A system reboot is required.`
      : "Network stack reset successfully. A system reboot is required to apply changes.",
  };
}
 
// -- Exported run function ----------------------------------------------------
 
export async function run({
  interface: iface,
  dryRun = true,
}: {
  interface: string;
  dryRun?:   boolean;
}): Promise<ResetResult & { platform: string }> {
  if (!iface || !iface.trim()) {
    throw new Error("[reset_network_settings] 'interface' is required (service/interface name).");
  }
  const platform = os.platform();
  const result   = platform === "win32"
    ? await resetWin32(iface, dryRun)
    : await resetDarwin(iface, dryRun);
 
  return { ...result, platform };
}
 
// -- CLI smoke test -----------------------------------------------------------
 
if (false) {
  run({ interface: "Wi-Fi" })
    .then(r => console.log(JSON.stringify(r, null, 2)))
    .catch((err: Error) => { console.error(err.message); process.exit(1); });
}