check_app_permissions
check_app_permissions
Checks what system permissions an application has been granted (Full Disk Access, Accessibility, Camera, Microphone, etc.). Use before reinstalling software or when an app reports permission errors.
Metadata
Name
check_app_permissions
Updated
2 months ago
Source
Risk
Low
Requires consent
false
Affected scope
user
Code
/**
* mcp/skills/checkAppPermissions.ts — check_app_permissions skill
*
* Checks what system permissions an application has been granted (Full Disk
* Access, Accessibility, Camera, Microphone, etc.). Use before reinstalling
* software or when an app reports permission errors.
*
* Platform strategy
* -----------------
* darwin sqlite3 query against ~/Library/Application Support/com.apple.TCC/TCC.db
* win32 PowerShell registry query HKCU:\Software\Microsoft\Windows\CurrentVersion\CapabilityAccessManager
*
* Smoke test
* npx tsx -r dotenv/config mcp/skills/checkAppPermissions.ts "CrowdStrike Falcon"
*/
import * as os from "os";
import * as nodePath from "path";
import { exec } from "child_process";
import { promisify } from "util";
import { z } from "zod";
import { TCC_CATEGORIES } from "./_shared/tcc";
const execAsync = promisify(exec);
// -- Meta ---------------------------------------------------------------------
export const meta = {
name: "check_app_permissions",
description:
"Checks what system permissions an application has been granted " +
"(Full Disk Access, Accessibility, Camera, Microphone, etc.). " +
"Use before reinstalling software or when an app reports permission errors.",
riskLevel: "low",
destructive: false,
requiresConsent: false,
supportsDryRun: false,
affectedScope: ["user"],
auditRequired: false,
schema: {
appName: z
.string()
.describe("Application name to check (e.g. 'CrowdStrike Falcon', 'Jamf Connect')"),
},
} as const;
// -- Types --------------------------------------------------------------------
interface PermissionEntry {
category: string;
status: "allowed" | "denied" | "unknown";
}
interface PermissionsResult {
appName: string;
platform: string;
permissions: PermissionEntry[];
error?: 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 checkAppPermissionsDarwin(appName: string): Promise<PermissionsResult> {
if (!/^[a-zA-Z0-9 _\-.']+$/.test(appName)) {
throw new Error(`[check_app_permissions] Invalid appName: ${appName}`);
}
const tccDb = nodePath.join(
os.homedir(),
"Library",
"Application Support",
"com.apple.TCC",
"TCC.db",
);
const safeAppName = appName.replace(/'/g, "''");
const query = `SELECT service, client, auth_value FROM access WHERE LOWER(client) LIKE LOWER('%${safeAppName}%') OR LOWER(policy_id) LIKE LOWER('%${safeAppName}%');`;
try {
const { stdout } = await execAsync(
`sqlite3 '${tccDb.replace(/'/g, "'\\''")}' "${query}" 2>&1`,
{ maxBuffer: 5 * 1024 * 1024 },
);
const rows = stdout.trim().split("\n").filter(Boolean);
if (rows.length === 0) {
return {
appName,
platform: "darwin",
permissions: [],
error: "No TCC entries found for this application. App may not have requested permissions yet, or requires elevated access to read system TCC database.",
};
}
const permissions: PermissionEntry[] = rows.map((row) => {
const parts = row.split("|");
const authValue = parseInt(parts[2] ?? "0", 10);
const status: PermissionEntry["status"] =
authValue === 2 ? "allowed" :
authValue === 0 ? "denied" :
"unknown";
return {
category: TCC_CATEGORIES[parts[0] ?? ""] ?? parts[0],
status,
};
});
return { appName, platform: "darwin", permissions };
} catch (err) {
const systemTccDb = "/Library/Application Support/com.apple.TCC/TCC.db";
try {
const { stdout } = await execAsync(
`sqlite3 '${systemTccDb}' "${query}" 2>&1`,
{ maxBuffer: 5 * 1024 * 1024 },
);
const rows = stdout.trim().split("\n").filter(Boolean);
const permissions: PermissionEntry[] = rows.map((row) => {
const parts = row.split("|");
const authValue = parseInt(parts[2] ?? "0", 10);
const status: PermissionEntry["status"] =
authValue === 2 ? "allowed" :
authValue === 0 ? "denied" :
"unknown";
return { category: TCC_CATEGORIES[parts[0] ?? ""] ?? parts[0], status };
});
return { appName, platform: "darwin", permissions };
} catch {
return {
appName,
platform: "darwin",
permissions: [],
error: `Could not read TCC database: ${(err as Error).message}. Full Disk Access may be required.`,
};
}
}
}
// -- win32 implementation -----------------------------------------------------
const WIN_CAPABILITIES = [
"webcam",
"microphone",
"location",
"contacts",
"calendar",
"phoneCall",
"email",
"appointments",
"userAccountInformation",
"radios",
"bluetoothSync",
"appDiagnostics",
"gazeInput",
"broadFileSystemAccess",
];
async function checkAppPermissionsWin32(appName: string): Promise<PermissionsResult> {
if (!/^[a-zA-Z0-9 _\-.']+$/.test(appName)) {
throw new Error(`[check_app_permissions] Invalid appName: ${appName}`);
}
const safeApp = appName.replace(/'/g, "''");
const caps = WIN_CAPABILITIES.map((c) => `'${c}'`).join(",");
const ps = `
$ErrorActionPreference = 'SilentlyContinue'
$basePath = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore'
$results = @()
foreach ($cap in @(${caps})) {
$capPath = "$basePath\$cap"
if (Test-Path $capPath) {
$apps = Get-ChildItem -Path $capPath -ErrorAction SilentlyContinue
foreach ($app in $apps) {
if ($app.PSChildName -like "*${safeApp}*") {
$val = (Get-ItemProperty -Path $app.PSPath -Name 'Value' -ErrorAction SilentlyContinue).Value
$results += [PSCustomObject]@{
category = $cap
status = if ($val -eq 'Allow') { 'allowed' } elseif ($val -eq 'Deny') { 'denied' } else { 'unknown' }
}
}
}
}
}
$results | ConvertTo-Json -Depth 2 -Compress`.trim();
try {
const raw = await runPS(ps);
if (!raw || raw === "null") {
return { appName, platform: "win32", permissions: [], error: "No capability entries found for this application." };
}
const parsed = JSON.parse(raw) as PermissionEntry | PermissionEntry[];
const permissions = Array.isArray(parsed) ? parsed : [parsed];
return { appName, platform: "win32", permissions };
} catch (err) {
return {
appName,
platform: "win32",
permissions: [],
error: (err as Error).message,
};
}
}
// -- Exported run function ----------------------------------------------------
export async function run({ appName }: { appName: string }) {
const platform = os.platform();
return platform === "win32"
? checkAppPermissionsWin32(appName)
: checkAppPermissionsDarwin(appName);
}
// -- CLI smoke test -----------------------------------------------------------
if (false) {
run({ appName: process.argv[2] ?? "Slack" })
.then((r) => console.log(JSON.stringify(r, null, 2)))
.catch((err: Error) => { console.error(err.message); process.exit(1); });
}