Add system_get_info and system_speech_api_status tools - check macOS version and SpeechAnalyzer availability (for Apple Speech API benchmark verification)

This commit is contained in:
aeroreyna
2026-07-13 16:08:59 -04:00
parent 0d446003dd
commit 9498079c5c
2 changed files with 142 additions and 0 deletions
+127
View File
@@ -0,0 +1,127 @@
import { execFile, execFile as execFileCb } from "node:child_process";
import { promisify } from "node:util";
const execFileAsync = promisify(execFile);
export async function getSystemInfo() {
const results = {};
// sw_vers for macOS version
try {
const { stdout } = await execFileAsync("/usr/bin/sw_vers", { timeout: 5000 });
const info = {};
for (const line of stdout.splitlines()) {
const [k, ...rest] = line.split(":");
if (!k) continue;
info[k.trim()] = rest.join(":").trim();
}
results.sw_vers = info;
results.macos_version = info.ProductVersion || info.productVersion || "";
results.build = info.BuildVersion || info.buildVersion || "";
} catch (e) {
results.sw_vers_error = e.message;
}
// uname -a
try {
const { stdout } = await execFileAsync("/usr/bin/uname", ["-a"], { timeout: 3000 });
results.uname = stdout.trim();
} catch (e) {
results.uname_error = e.message;
}
// Check for SpeechAnalyzer / SpeechTranscriber availability (macOS 26+)
// These frameworks exist only on macOS 26+. We probe via swift/python check.
try {
const { stdout } = await execFileAsync("/usr/bin/python3", ["-c", `
import os, glob, sys
# Check if Speech framework contains new symbols (macOS 26)
frameworks = glob.glob('/System/Library/Frameworks/Speech.framework/*') + glob.glob('/System/Library/PrivateFrameworks/*Speech*')
# Simplest: check macOS version parse
import platform
print(platform.mac_ver()[0])
`], { timeout: 5000 });
results.python_mac_ver = stdout.trim();
} catch (e) {
results.python_mac_ver_error = e.message;
}
// Try to detect SpeechAnalyzer via file existence / Swift availability
try {
// On macOS 26, Speech.framework/Versions should have newer build
const { stdout } = await execFileAsync("/bin/ls", ["-la", "/System/Library/Frameworks/Speech.framework/"], { timeout: 3000 });
results.speech_framework_ls = stdout.trim().slice(0, 2000);
} catch (e) {
results.speech_framework_error = e.message;
}
// Check hardware model
try {
const { stdout } = await execFileAsync("/usr/sbin/sysctl", ["-n", "hw.model"], { timeout: 2000 });
results.hw_model = stdout.trim();
} catch {}
try {
const { stdout } = await execFileAsync("/usr/sbin/sysctl", ["-n", "machdep.cpu.brand_string"], { timeout: 2000 });
results.cpu_brand = stdout.trim();
} catch {}
// Is this macOS 26+ ?
if (results.macos_version) {
const major = parseInt(results.macos_version.split(".")[0], 10);
results.is_macos_26_plus = major >= 26;
results.speech_analyzer_expected = major >= 26 ? "likely available (macOS 26+)" : "not available - requires macOS 26+";
}
return results;
}
export async function getSpeechApiStatus() {
const sysInfo = await getSystemInfo();
// Try to run a tiny Swift snippet that imports Speech and checks for SpeechAnalyzer
// Fallback to reporting version info if swiftc not available
let swiftCheck = null;
try {
// Write temp swift file that checks API availability
const { stdout: swiftPath } = await execFileAsync("/usr/bin/which", ["swift"], { timeout: 2000 });
const swiftBin = swiftPath.trim();
if (swiftBin) {
// Create a small swift program to test SpeechAnalyzer availability
const swiftCode = `
import Speech
import Foundation
#if canImport(Speech)
if #available(macOS 26.0, *) {
print("SpeechAnalyzer: available")
// Try to reference the type
let _ = SpeechTranscriber.self
print("SpeechTranscriber: available")
} else {
print("SpeechAnalyzer: requires macOS 26")
}
#else
print("Speech framework not importable")
#endif
`;
const tmpFile = `/tmp/speech_check_${Date.now()}.swift`;
const { writeFile, unlink } = await import("node:fs/promises");
await writeFile(tmpFile, swiftCode, "utf8");
try {
const { stdout, stderr } = await execFileAsync(swiftBin, [tmpFile], { timeout: 15000, maxBuffer: 2 * 1024 * 1024 });
swiftCheck = { stdout: stdout.trim(), stderr: stderr.trim(), ok: true };
} catch (e) {
swiftCheck = { stdout: e.stdout?.toString().trim() || "", stderr: (e.stderr?.toString() || e.message).trim().slice(0, 2000), ok: false };
} finally {
try { await unlink(tmpFile); } catch {}
}
}
} catch (e) {
swiftCheck = { error: e.message };
}
return {
system: sysInfo,
swift_availability: swiftCheck,
conclusion: sysInfo.is_macos_26_plus
? "macOS 26+ detected — SpeechAnalyzer/SpeechTranscriber should be available per Apple docs."
: `macOS ${sysInfo.macos_version || "unknown"} detected — SpeechAnalyzer requires macOS 26+. ${sysInfo.macos_version ? `You are on ${sysInfo.macos_version}, need to upgrade to 26.` : ""}`,
};
}
+15
View File
@@ -18,6 +18,7 @@ import {
listReminderLists,
listReminders,
} from "./integrations/reminders.js";
import { getSystemInfo, getSpeechApiStatus } from "./integrations/system.js";
const jsonResult = (value) => ({
content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
@@ -226,5 +227,19 @@ export function createMacMiniMcpServer() {
handled(() => getDecoStats("firmware")),
);
server.tool(
"system_get_info",
"Get Mac mini system info: macOS version (sw_vers), uname, hardware model, and whether macOS 26+ SpeechAnalyzer is available.",
{},
handled(() => getSystemInfo()),
);
server.tool(
"system_speech_api_status",
"Check if Apple's new SpeechAnalyzer / SpeechTranscriber (macOS 26+) is available — returns macOS version, build, and Swift availability probe.",
{},
handled(() => getSpeechApiStatus()),
);
return server;
}