get_vpn_profiles
get_vpn_profiles
Lists all configured VPN profiles including their type, server, and last used date. Use when reconnecting to VPN or diagnosing which profile to repair.
Metadata
Name
get_vpn_profiles
Updated
2 weeks ago
Source
Risk
Low
Requires consent
false
Affected scope
user
Code
/**
* mcp/skills/getVpnProfiles.ts — get_vpn_profiles skill
*
* Lists all configured VPN profiles including their type, server, and last
* used date. Use when reconnecting to VPN or diagnosing which profile to repair.
*
* Platform strategy
* -----------------
* darwin `scutil --nc list` for all Network Configuration VPN entries; checks
* for AnyConnect profiles in /opt/cisco/anyconnect/profile/ and
* GlobalProtect in /Library/Application Support/Palo Alto Networks/GlobalProtect/
* win32 PowerShell Get-VpnConnection (per-user and all-user)
*
* Smoke test
* npx tsx -r dotenv/config mcp/skills/getVpnProfiles.ts
*/
import * as os from "os";
import * as fs from "fs/promises";
import * as nodePath from "path";
import { exec } from "child_process";
import { promisify } from "util";
import { z } from "zod";
import { enumerateVendorVpnProfilesDarwin, WIN32_VPN_VENDOR_PROCS } from "./_shared/vpnProfiles";
const execAsync = promisify(exec);
// -- Meta ---------------------------------------------------------------------
export const meta = {
name: "get_vpn_profiles",
description:
"Lists all configured VPN profiles including their type, server, and last " +
"used date. Use when reconnecting to VPN or diagnosing which profile to repair.",
riskLevel: "low",
destructive: false,
requiresConsent: false,
supportsDryRun: false,
affectedScope: ["user"],
auditRequired: false,
tccCategories: ["FullDiskAccess"],
schema: {} as Record<string, z.ZodTypeAny>,
} as const;
// -- Types --------------------------------------------------------------------
interface VpnProfile {
name: string;
type: string;
server: string | null;
protocol: string | null;
isConnected: boolean;
lastUsed: string | null;
}
interface GetVpnProfilesResult {
profiles: VpnProfile[];
}
// -- 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: 20 * 1024 * 1024 },
);
return stdout.trim();
}
// -- darwin implementation ----------------------------------------------------
async function getVpnProfilesDarwin(): Promise<GetVpnProfilesResult> {
const profiles: VpnProfile[] = [];
// Parse scutil --nc list for native macOS VPN profiles
let scutilOut = "";
try {
({ stdout: scutilOut } = await execAsync("scutil --nc list 2>/dev/null", {
maxBuffer: 5 * 1024 * 1024,
}));
} catch {
scutilOut = "";
}
const scutilLines = scutilOut.trim().split("\n").filter(Boolean);
for (const line of scutilLines) {
// Format: * (Status) <uuid> [Proto/Type] "Name" [...]
const statusMatch = line.match(/\((\w+)\)/);
const nameMatch = line.match(/"([^\"]+)"/);
const typeMatch = line.match(/\[([^\]]+)\]/);
if (!nameMatch) continue;
const name = nameMatch[1];
const status = statusMatch ? statusMatch[1] : "Unknown";
const typeStr = typeMatch ? typeMatch[1] : "VPN";
const isConnected = status === "Connected";
// Attempt to get server for this profile via scutil --nc show
let server: string | null = null;
try {
const { stdout: showOut } = await execAsync(
`scutil --nc show \