Skip to main content
Glama
june4432

thermo-control-mcp

by june4432

thermo-control-mcp

English | 한국어

An MCP server that lets Claude Code (or any MCP client) monitor your Mac's thermals and control fan speed — with a root daemon that enforces safety limits no matter what the LLM asks for.

demo

A true story: the battery menu blamed "Terminal", Claude found a runaway language server, boosted the fans, and handed control back. (Rendered with Remotion — source in demo/.)

You: "I'm about to run a 20-minute Rust build. Keep the machine cool."
Claude: [get_thermal_status] → CPU 58°C, fans on system control
        [set_fan_speed percent=80 ttl_seconds=1500] → fans spin up before the heat arrives
        ... build runs without thermal throttling ...
        [set_fan_auto] → back to macOS control

Works on Apple Silicon (M1–M5, including the M3/M4 firmware lock — see How it works). Reading thermals requires no privileges; fan control uses a small root LaunchDaemon.

Why

macOS ramps fans reactively — by the time they spin up, the CPU has already been throttling. An LLM agent knows in advance when it's about to start a heavy build, test suite, or local inference job. This server lets it pre-cool the machine, hold higher fan speeds through sustained load, and hand control back when done.

Related MCP server: Mac MCP

Architecture

Claude Code ── stdio ──> MCP server (Node, unprivileged)
                            │
                            ├─ reads (temps/RPM/power) ─── works even without the daemon
                            │
                            └─ unix socket /var/run/thermod.sock (root:admin 0660)
                                            │
                                     thermod daemon (root, launchd)
                                     ├─ SMC access via IOKit (AppleSMC)
                                     ├─ M3/M4 Ftst unlock sequence
                                     └─ SAFETY POLICY (hardcoded):
                                        · TTL dead-man switch
                                        · 102°C thermal failsafe
                                        · RPM clamped to hardware range
                                        · revert-to-auto on daemon exit

The safety policy lives in the root daemon, not in the MCP layer the LLM talks to. The LLM can ask; the daemon decides.

Safeguard

Behavior

Dead-man switch

Every manual setting carries a TTL (default 15 min, max 2 h). When it expires — or the daemon stops, or the machine reboots — fans revert to macOS control. Agents must re-request to keep control.

Thermal failsafe

If any die sensor reaches 102°C while under manual control, the daemon abandons manual mode and returns fans to the system immediately. Not configurable over the socket.

RPM clamping

Requested speeds are clamped to the fan's hardware-reported [min, max] range. An LLM cannot stop the fans.

Sleep/wake handling

Firmware drops manual control across sleep; the daemon re-asserts it only if a valid, unexpired request is still active.

Local-admin only

The control socket is root:admin mode 0660 — only administrator users on the machine can command it.

Requirements

  • Apple Silicon Mac with fans (MacBook Air has none). Intel Macs may work (the legacy fpe2 format is implemented) but are untested.

  • macOS 13+

  • Xcode Command Line Tools (xcode-select --install) — for building the Swift daemon

  • Node.js 18+

Install

There are two components: the MCP server (npm) and the thermod daemon (built from this repo — it's a root LaunchDaemon, so it intentionally never ships as a prebuilt binary; you build what you run).

git clone https://github.com/june4432/thermo-control-mcp.git
cd thermo-control-mcp

# 1. Build + register the MCP server
npm install && npm run build

# 2. Build + install the root daemon (asks for your password)
sudo ./scripts/install.sh

# 3. Register with Claude Code
claude mcp add thermo-control -- node "$(pwd)/dist/index.js"

If you prefer the MCP server from npm (still needs step 2 above for fan control):

npm install -g thermo-control-mcp
claude mcp add thermo-control -- thermo-control-mcp

Without step 2, get_thermal_status still works (SMC reads are unprivileged); the control tools return an explanatory error.

Uninstall with sudo ./scripts/uninstall.sh — fans revert to system control.

MCP tools

Tool

What it does

get_thermal_status

Per-sensor die temperatures (CPU/GPU/memory), fan RPM/mode/range, power draw (W), current control state and remaining TTL.

get_heat_sources

Diagnose why the machine is hot: temperature summary plus the top CPU-consuming processes (with cumulative CPU time vs uptime, so runaways stand out). Breaks down what macOS's battery menu lumps together as "Terminal".

set_fan_speed

Manual mode at rpm or percent (of each fan's min→max range), optionally per-fan, with ttl_seconds (default 900).

boost_fans

All fans to 100% for ttl_seconds (default 600). Pre-cooling shortcut.

set_fan_auto

Release manual control back to macOS immediately.

You can also poke the daemon directly:

echo '{"cmd":"status"}' | nc -U /var/run/thermod.sock | python3 -m json.tool
echo '{"cmd":"set","percent":70,"ttl_seconds":300}' | nc -U /var/run/thermod.sock
echo '{"cmd":"auto"}' | nc -U /var/run/thermod.sock

And read thermals with no daemon at all: daemon/.build/release/thermod status.

How it works

Fan state lives in SMC keys (FNum, F0Ac actual RPM, F0Tg target, F0Mn/F0Mx range, F0Md mode, all floats little-endian on Apple Silicon), accessed from userspace through the AppleSMC IOKit service. Reads are allowed for any process; writes require root — enforced per-key by the SMC firmware itself.

On M3/M4 machines there is an extra gate: thermalmonitord holds the fans in "system mode" (mode 3) and the firmware rejects manual-mode writes with error 0x82. The daemon uses the community-documented unlock: try the direct write first (sufficient on M1/M2/M5 and Intel); on rejection, write the Ftst (force-test) diagnostic flag to 1, which suppresses thermalmonitord's reclaim logic, then retry the mode write until it lands (typically 3–6 s). Ftst must stay set while manual control is held — one of the reasons this is a persistent daemon rather than a one-shot CLI. The firmware clears Ftst across sleep/wake; the daemon detects and re-asserts.

Mode-key casing changed on M5 (F0md), and Ftst no longer exists there — both are probed at runtime. Pre-T2 Intel Macs have no mode key at all; there the legacy FS! force-bitmask is used instead.

Temperature sensors are discovered dynamically: the daemon enumerates the SMC's full key list (#KEY + read-by-index) and keeps every T… key that decodes as a plausible temperature — 291 sensors on an M4 Pro versus ~20 in a hand-curated list. This covers every chip variant (base/Pro/Max/Ultra) and future generations without per-model tables; curated catalogs only contribute friendly names where known. Values decode by declared SMC type (flt, sp78, fpe2, ui8/16/32), which also makes Intel's signed 7.8 fixed-point temperatures read correctly.

Credit where due: the unlock mechanism and much of the protocol behavior were documented by agoodkind/macos-smc-fan (via decompilation of thermalmonitord and AppleSMC.kext), with additional reference from raminsharifi/MacFanControl, the VirtualSMC SDK, and the Asahi Linux SMC docs. This project implements the protocol independently in Swift (MIT-licensed references only).

Compatibility

Hardware

Status

M4 Pro (Mac16,8)

Verified end-to-end — 291 sensors discovered, manual fan control (2.3k→6.2k RPM), TTL dead-man revert observed live, via both the socket and MCP tools

M1 / M2 / M3 / M5

Implemented per documented behavior (direct mode write on M1/M2/M5, Ftst unlock on M3/M4); sensors discovered dynamically per machine. Untested — reports welcome

Intel (T2)

Direct mode write + fpe2/sp78 decoding implemented, untested

Intel (pre-T2)

Legacy FS! force-bitmask fallback implemented, untested

MacBook Air

No fans — status works, control does not apply

Caveats

  • Don't run this alongside Macs Fan Control, TG Pro, or similar — two controllers will fight over the same SMC keys.

  • The Ftst unlock is an undocumented Apple mechanism. A macOS update could change or remove it. Re-verify after major OS updates.

  • Setting fans low under load is protected by the failsafe, but the failsafe is a backstop, not a thermostat. The intended use is raising fan speed, not silencing a hot machine.

  • macOS firmware retains its own independent thermal protection (throttling, emergency shutdown) regardless of this tool.

Disclaimer

This software manipulates system thermal management using undocumented interfaces. It ships with safety mechanisms, but you use it at your own risk. The authors are not responsible for hardware damage.

License

MIT

Available Tools

4 tools
boost_fansBoost fans to maximumA

Convenience wrapper: run all fans at 100% for a limited time (default 600 s). Good for pre-cooling right before a compile, video export, or LLM inference burst. Reverts automatically.

ParametersJSON Schema
NameRequiredDescriptionDefault
ttl_secondsNoSeconds until automatic revert (default 600)

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Discloses that it runs fans at 100%, is temporary with a default duration, and reverts automatically. No annotations provided, so description carries burden. Could mention what 'reverts' means (e.g., to previous state?), but it's clear enough.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with no fluff. First sentence defines action and default, second gives use cases and behavior. Every word earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers purpose, usage, duration, revert behavior. With one optional parameter and no output schema, the description is sufficient for an agent to select and invoke correctly. Minor gap: revert behavior could be more precise.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema covers parameter fully with description. Description adds context about limited time and default, but does not add new semantic meaning beyond schema. Baseline score applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states it runs all fans at 100% for a limited time, a specific verb-resource-action with distinct behavior. Differentiated from siblings which query status or set auto/specific speeds.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly mentions ideal use cases (pre-cooling before compile, video export, LLM inference burst). Does not explicitly state when not to use, but context of siblings implies alternatives for granular control.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_thermal_statusGet thermal statusA

Read the Mac's current thermal state: per-sensor die temperatures (CPU/GPU/memory), fan RPM (actual/target/min/max) and mode, power draw in watts, and the fan-control state (manual targets, remaining TTL, last automatic revert). Use this before and after changing fan speeds, or to decide whether pre-cooling is worthwhile.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Description fully discloses the tool is a read operation and details the output data. No annotations provided, so description carries full burden; it is transparent about behavior and does not contradict any implicit assumptions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two well-structured sentences, front-loaded with purpose and output details. Every sentence adds value; no fluff.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema, but description comprehensively lists return fields. Parameter count is zero, so complexity is low. Description covers all necessary context for a read-only monitoring tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

No parameters in the input schema, so baseline score of 4 applies. Description doesn't need to add parameter information; it is appropriately concise on this dimension.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Title and description clearly state it reads the thermal state, listing specific data points (sensor temps, fan RPM, power draw, fan-control state). Distinguishes from sibling tools like boost_fans and set_fan_auto by being a read-only status tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use the tool: 'before and after changing fan speeds, or to decide whether pre-cooling is worthwhile.' Provides clear context but lacks explicit when-not-to-use scenarios.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

set_fan_autoReturn fans to system controlA

Release manual fan control immediately and hand thermal management back to macOS (thermalmonitord). Use when the heavy workload is done.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full burden. It discloses immediate release and handoff to macOS (thermalmonitord), which implies safe behavior, but lacks details on potential side effects or prerequisites.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, front-loaded with key action and purpose, every sentence adds value with no waste.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter tool with no output schema, the description fully covers what the tool does and when to use it, making it complete for an AI agent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There are zero parameters (100% schema coverage), and the description adds no parameter info, which is appropriate. Baseline for 0 parameters is 4.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb 'release' and resource 'manual fan control', and clearly distinguishes from siblings like set_fan_speed and boost_fans by indicating it returns control to macOS.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description states 'Use when the heavy workload is done,' providing implicit context for when to use, but does not explicitly list alternatives or when not to use, though siblings make this inferable.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

set_fan_speedSet fan speedA

Put the Mac's fans into manual mode at a given speed. Provide either 'rpm' (absolute) or 'percent' (0-100, mapped onto each fan's min-max range). Values are clamped to the hardware's reported safe range. The setting expires after ttl_seconds (default 900, max 7200) and fans return to system control — call again to renew. A root-owned failsafe overrides manual control if any die sensor reaches 102°C. Typical use: raise fans BEFORE starting a heavy build/inference job so the machine stays out of thermal throttling.

ParametersJSON Schema
NameRequiredDescriptionDefault
fanNoFan index to control; omit to apply to all fans
rpmNoAbsolute target RPM (mutually exclusive with percent)
percentNoSpeed as % of each fan's min→max range
ttl_secondsNoSeconds until automatic revert to system control (default 900)

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully discloses behavioral traits: manual mode, clamping to safe range, TTL expiration, and a failsafe at 102°C. This covers safety and auto-revert behaviors comprehensively.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise and well-structured. The first sentence states the core purpose, followed by parameter explanations, expiration behavior, failsafe, and a typical use case. No redundant sentences.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 4 parameters, no output schema, and no annotations, the description covers all essential aspects: manual mode setup, parameter meaning, clamping, TTL, failsafe, and typical usage. It is complete enough for an AI agent to select and invoke correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, but the description adds meaningful context: the relationship between rpm and percent (percent mapped to min-max range), the mutual exclusivity, and that values are clamped to safe range. It also clarifies that omitting fan applies to all fans.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Put the Mac's fans into manual mode at a given speed.' It specifies the resource ('Mac's fans') and the action ('manual mode'), and distinguishes from siblings like boost_fans, get_thermal_status, and set_fan_auto.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides a typical use case ('raise fans BEFORE starting a heavy build/inference job') and explains the automatic revert after ttl_seconds. However, it does not explicitly state when not to use the tool or compare it to alternatives like boost_fans or set_fan_auto.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 4 tool updatesv0.2.0
    • First observedboost_fans
    • First observedget_thermal_status
    • First observedset_fan_auto
    • First observedset_fan_speed

TDQS

A4.6/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: boosting fans, reading status, setting auto mode, and setting manual speed. No overlap.

Naming Consistency5/5

All tool names follow the consistent snake_case pattern with a verb_noun structure: boost_fans, get_thermal_status, set_fan_auto, set_fan_speed.

Tool Count5/5

4 tools are well-scoped for a thermal control server, covering status, manual control, auto release, and a convenience function.

Completeness5/5

The tool set covers the essential operations for fan control: reading status, setting manual speed, reverting to auto, and a burst mode.

Maintenance

ActivityStale
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers