Macos Mcp Server

io.github.cyanheads由 GitHub 帳號 cyanheads 發布v0.4.0更新於 Oct 30, 2026

Control macOS settings, apps, windows, audio, Music, displays, screenshots, and Focus mode via MCP.

概覽

AI 產生的概覽

本機 MCP 伺服器,讓助理檢視並控制 Mac:應用程式、視窗、音訊、音樂、顯示器、截圖、專注模式與 Finder。

可以做什麼
  • 讀取系統狀態:電池、電源、Wi-Fi、主機名稱、macOS 版本、開機時間、顯示器數量,以及權限檢查
  • 管理應用程式與視窗:列出、啟動、結束、隱藏、聚焦、移動、調整大小、最小化、全螢幕、關閉
  • 控制音量與音樂:音量與靜音、音訊裝置列出與切換、播放、播放清單、隨機與重複
  • 擷取與安排螢幕:主螢幕、指定顯示器、區域或視窗截圖,以及顯示器清單與版面預設
  • 調整系統狀態:深色/淺色模式、鎖定或休眠顯示器、通知、專注模式,以及 Finder 路徑、選取項目、顯示、開啟與移到垃圾桶
適用情境
適合讓助理直接操作它所執行的這台 Mac,而不只是討論:自動化視窗與應用程式操作、截圖做視覺檢查、切換音訊或顯示器版面,或在排解問題時讀取系統狀態。
執行需求
在 macOS 13 或更新版本上本機執行,透過 npx 或 bunx 以 stdio 啟動;需要 Node.js v24+ 或 Bun v1.4.0+。音訊路由與顯示器版面可選用 Homebrew 的 switchaudio-osx 與 displayplacer。需為終端機或宿主應用程式授予輔助使用、螢幕錄製,以及 Finder、音樂和 System Events 的自動化權限。設定專注模式需要一個名為 "Set Focus" 的快捷指令。
安裝前請注意
它會改動這台機器:結束或強制結束應用程式、關閉視窗、把檔案移到垃圾桶、鎖定螢幕,以及變更音量、外觀和專注模式。強制結束會送出 SIGKILL,不會提示儲存。截圖會寫入磁碟,預設在桌面,除非設定 MACOS_SCREENSHOT_DIR。設定專注模式依賴使用者自建的快捷指令,且不會回讀結果。可透過 MCP_AUTH_MODE 啟用帶 jwt 或 oauth 驗證的選用 HTTP 傳輸。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Macos Mcp Server,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

@cyanheads/macos-mcp-server

Control macOS settings, apps, windows, audio, Music, displays, screenshots, and Focus mode via MCP. STDIO or Streamable HTTP.

14 Tools • 3 Resources

macOS-only. This server controls the local macOS system — it requires the host machine to be running macOS. HTTP transport is supported for completeness, but the practical use case is stdio: run it locally and point your MCP client at it.


Overview

Control your Mac from an agent: launch, quit, and arrange apps and windows, switch audio and display devices, control Music playback, capture screenshots, work with Finder, send notifications, and toggle Focus mode.

Tools

ToolDescription
macos_get_infoSystem snapshot: battery level and charging status, power source, Wi-Fi association and network name, hostname, macOS version, uptime, and display count; a failed probe leaves its field null and named in unavailable
macos_check_permissionsReports Accessibility, Screen Recording, Automation > Finder, and Automation > Music status for the calling process from Apple's prompt-free checks — null with a reason when a check can't tell
macos_manage_appsList running apps, get the frontmost app, launch, quit, force-quit, hide, or show applications
macos_manage_windowsList, focus, move, resize, move_resize, minimize, fullscreen, or close windows across all visible apps
macos_control_volumeGet or set system output volume (0–100) and mute state
macos_control_audioList audio devices, get current defaults, or switch the default input/output device
macos_control_musicPlay, pause, stop, skip, play a playlist by name, set shuffle or repeat, or read what the Music app is playing
macos_control_appearanceGet or set dark/light mode
macos_control_systemLock the screen or put the display to sleep
macos_take_screenshotCapture the main display, one display by display_index, a named app window, or a pixel region; saves a PNG without replacing existing files unless overwrite is true, optional JPEG preview
macos_manage_displaysList connected displays (with each one's index) and the configured layout names, and apply a named display layout preset
macos_send_notificationPost a notification to macOS Notification Center
macos_manage_focusGet or set Do Not Disturb / Focus mode
macos_manage_finderFrontmost path, current selection, reveal, open with app, or move to Trash

Resources

ResourceDescription
macos://system/infoCurrent macOS system snapshot: battery, power source, Wi-Fi association and network name, hostname, version, uptime, display count; a failed probe leaves its field null and named in unavailable
macos://audio/devicesAll audio input and output devices, including which is the current default. Requires SwitchAudioSource CLI.
macos://displaysConnected display inventory including persistent IDs, index (the display_index macos_take_screenshot and macos_manage_windows use), type, resolution, origin, rotation, scaling, and enabled state. Requires displayplacer CLI.

Resource data is also accessible via macos_get_info, macos_control_audio (action=list), and macos_manage_displays (action=list).

Capability reference

macos_get_info tool

  • Battery level (0–100), charging state, and power source (AC, Battery, UPS); null on desktops with no battery. charging is false when the battery is held at a charge limit (AC attached; not charging) and true while finishing charge
  • Wi-Fi association from the Wi-Fi device's link state; the network name is null while connected when macOS withholds it (reading it requires Location Services access for the host app). The name renders in content[] as a code span, so markdown characters in it read back as the name
  • Hostname and uptime (from Node's os module), macOS version string (e.g. "15.1")
  • Connected display count (0 on a headless Mac)
  • A probe that fails leaves its field null and names it in unavailable; nothing is filled in. battery: null with battery absent from unavailable means the Mac has no battery
  • No permissions required

macos_check_permissions tool

  • Reports Accessibility (every macos_manage_windows action, list included; macos_manage_apps hide and show, and the window_title frontmost reads; the ⌃⌘Q keystroke macos_control_system lock sends before it falls back to the screen saver), Screen Recording (window screenshots), Automation > Finder (macos_manage_finder frontmost_path, get_selection, and trash), and Automation > Music (macos_control_music) from Apple's prompt-free checks: AXIsProcessTrusted, CGPreflightScreenCaptureAccess, and AEDeterminePermissionToAutomateTarget with prompting off
  • Each field is true, false, or null when the check can't tell; unknown_reasons names why each null is unknown. Automation > Finder and Automation > Music read null until macOS has asked for consent and while that app isn't running. Notifications is always null: macOS has no prompt-free check for it
  • Returns the calling process name (e.g. "ghostty", "node") so you know which process to grant permissions for
  • Read-only — no check sends an Apple event to another app, captures the screen, or can show a permission prompt
  • Run this first when debugging why another tool is failing

macos_manage_apps tool

  • list — running user-facing apps with name, bundle ID, PID, visible, and frontmost flags
  • frontmost — name, bundle ID, PID, and frontmost window title of the active app
  • launch — open or activate by app_name or bundle_id; hidden=true starts in the background
  • quit (graceful AppleScript quit) vs. force_quit (SIGKILL, no save prompt)
  • hide / show — toggle visibility; requires Accessibility
  • quit, force_quit, hide, and show first check that the app is running — resolved by application name the way tell application does ("Visual Studio Code" finds the Code process), without launching it — and return not_running when it isn't
  • Typed errors: app_not_found (launch of an app that isn't installed), no_frontmost_app (System Events reports no frontmost process), not_running, accessibility_required

macos_manage_windows tool

  • list — all visible windows across apps, with position, size, minimized and fullscreen state (each read from the window's AXMinimized / AXFullScreen attribute, null when it can't be read), and display_index: the display the window overlaps most (the index macos_manage_displays action=list reports), or null when the display frames can't be read or the window overlaps none. Two windows that share a title, and the windows of two processes that share a name, are each listed
  • focus, move, resize, move_resize, minimize, fullscreen, close — target by window_title (exact, ignoring case, in any app; when two windows of one app share the title, the frontmost of them) or app_name (that app's frontmost window); window_title takes precedence and app_name is then ignored
  • app_name matches a running app by its application name ("Visual Studio Code"), localized name ("Code"), or process name ("firefox"), ignoring case; it is resolved from the running-application list, so no app is launched or sent an Apple event to find it
  • move needs x/y, resize needs width/height (both greater than 0), move_resize needs all four
  • minimize — minimized: true (the default) minimizes, false restores; it writes the window's AXMinimized attribute only when the window is not already in that state, and succeeds only when the window reads back in the requested state
  • fullscreen — fullscreen: true enters, false exits, omitted toggles the window's current state. When the state differs it raises the target window, as focus does, and sets its AXFullScreen attribute (no keystroke, so the window that had keyboard focus is never toggled); it waits up to 4 s for the window to read back in the requested state
  • Every action, list included, scripts System Events and requires Accessibility; a denial returns accessibility_required and never reads as "no windows". focus raises the window and brings its app forward; no action launches an app
  • Each write finds, changes, and reads back the same window in one script; window.app is the process the window was found under; close returns no window
  • Typed errors: not_running (app_name names no running app), window_not_found, accessibility_required, minimize_unsupported and fullscreen_unsupported (the window's AXMinimized / AXFullScreen attribute is missing or not settable, or the window did not reach the requested state within 4 s); an osascript timeout or failure keeps its own error instead of reading as window_not_found

macos_control_volume tool

  • get — current output volume (0–100) and mute state of the default output. On an output with no software volume control (many HDMI and DisplayPort displays), level and muted are null and output_device names the device (null when it cannot be identified)
  • set — requires level (0–100), muted, or both; level=0 does not mute. On an output with no software control for the requested value it fails with volume_unsupported naming the device and changes nothing
  • Always returns current state, with the action echoed
  • Typed errors: volume_unsupported; a cancelled call stops the device lookup and settles as cancelled, never as output_device: null

macos_control_audio tool

  • list — all input/output devices with an is_default flag; filter with type
  • current — default input and output device names
  • switch_output / switch_input — case-insensitive partial name match ("MacBook" matches "MacBook Pro Microphone")
  • Volume level is separate (macos_control_volume)
  • Requires SwitchAudioSource CLI (brew install switchaudio-osx); typed errors device_not_found, switchaudio_unavailable

macos_control_music tool

  • play resumes or starts playback; play_playlist plays the playlist named by playlist (exact name as the Music sidebar shows it; the first match plays). Both launch Music when it is closed
  • pause and stop halt playback; next and previous skip tracks
  • set_shuffle requires shuffle (boolean); set_repeat requires repeat (off, one, or all)
  • get_current only reads
  • Every action returns the state Music reports afterward: player_state (playing, paused, stopped, fast_forwarding, rewinding), track (name, artist, album), shuffle, and repeat; play_playlist also returns playlist, the name as Music reports it
  • get_current, pause, and stop leave a closed Music closed and return running: false with player_state, shuffle, and repeat null; next, previous, set_shuffle, and set_repeat fail with not_running
  • track is null when Music has nothing loaded. It is also null, with track_unavailable saying why, when the player is not stopped but Music refuses to return a track outside the library (Apple Music radio or catalog playback on recent macOS); transport actions still work
  • Scripts Music and requires Automation > Music; the first call shows the macOS consent dialog, and a call that times out while it is open succeeds when retried after answering it
  • Typed errors: not_running, playlist_not_found (nothing was played), accessibility_required (Automation > Music denied)
  • System output level belongs to macos_control_volume

macos_control_appearance tool

  • get — returns dark_mode: true/false, with the action echoed
  • set requires mode: "dark" | "light" | "toggle" — dark/light are idempotent, toggle flips on each call
  • Scripts System Events; typed error accessibility_required when Automation > System Events is denied

macos_control_system tool

  • lock — sends ⌃⌘Q through System Events (needs Accessibility and Automation > System Events). When either is denied, it starts the screen saver instead, which locks the session only when "Require password after screen saver begins or display is turned off" is set to Immediately in System Settings > Lock Screen; method reports keystroke or screen_saver. A timed-out or otherwise failed keystroke is reported as-is, and a cancelled call never falls back
  • Typed error accessibility_required when the keystroke is denied and the screen saver can't start, with the hint for the pane that was denied
  • sleep_display — pmset displaysleepnow; no permissions required
  • Both operations are immediate and reversible with any input (wake/unlock)

macos_take_screenshot tool

  • target: screen (the main display only, the one with the menu bar), display (0-based integer display_index, default 0: the index macos_manage_displays action=list reports), region (pixel rect, width/height greater than 0) — no Screen Recording required; window (by app_name, matched like macos_manage_windows: application name, localized name, or process name, ignoring case; captures that app's frontmost on-screen window) requires Screen Recording. When the Screen Recording check can't tell, the window capture still runs and notice says why
  • Always saves a full-resolution PNG. Omitting path (or passing "") writes a new screenshot-<timestamp>.png in MACOS_SCREENSHOT_DIR (~/Desktop when unset), adding -2, -3, … rather than replacing a file. A custom path must end in .png (any case); ~/ expands to the home directory; its directory must exist and, with symlinks resolved, lie inside MACOS_SCREENSHOT_DIR, /tmp, or the home directory. The returned path is the resolved one (a /tmp path reports under /private/tmp)
  • Never replaces an existing entry: a path that already exists fails with path_exists unless overwrite: true, which replaces only the regular .png file found at path when the call checks it (never a symbolic link or directory). An entry created at path after that check, or put in that file's place, is never replaced and fails with path_exists
  • Every call settles within 55 s of its start, the app_name lookup included: past that it fails as a Timeout (-32004) and writes nothing. A cancelled request writes nothing either
  • include_data=true returns a JPEG preview, longer side at most 1024 px (a smaller capture keeps its own size), quality 70: as the first content[] block (image/jpeg) and as base64 preview plus preview_width/preview_height in structuredContent
  • width/height (and the preview's) are read from the image file headers
  • Typed errors: screen_recording_required, window_not_found (an app that is not running, or one with no on-screen window; an osascript timeout or failure during the window lookup keeps its own error), display_not_found, path_not_writable (also a directory that cannot be resolved or searched, an over-long one included), path_exists; a path not ending in .png, holding U+0000, or whose file name or full resolved path is longer than the file system allows (255 bytes per name, 1024 for the whole path) is rejected as -32602 with reason invalid_arguments

macos_manage_displays tool

  • list — per display: index (0-based position in NSScreen.screens; the display_index macos_take_screenshot and macos_manage_windows use; null for a display with no NSScreen entry, such as a disabled one), persistent ID, type ("MacBook built in screen" or "<N> inch external screen"), resolution, refresh rate ("N/A" when macOS reports none), origin, rotation, scaling, and enabled state (also true for a display mirroring another); plus current_config (a displayplacer command that reproduces the active arrangement) and layouts (the names configured in MACOS_DISPLAY_LAYOUTS, [] when none). When MACOS_DISPLAY_LAYOUTS is invalid, list still succeeds, omits layouts, and its notice names the variable. When the NSScreen read fails, index is omitted and notice says why; that includes a read that waited 15 s for concurrent System Events enumerations to finish
  • apply_layout — activates a named preset from MACOS_DISPLAY_LAYOUTS (one of the names list returns as layouts); raw displayplacer args are never accepted from the caller. An invalid MACOS_DISPLAY_LAYOUTS fails apply_layout as layouts_invalid (ConfigurationError), never layout_not_found
  • Requires displayplacer CLI (brew install displayplacer); typed errors displayplacer_not_found, displayplacer_output_unrecognized, layout_not_found, layouts_invalid
  • A failed displayplacer run (non-zero exit, signal, timeout) fails the call instead of returning what it printed; a display block missing a line whose value the listing returns, or a listing missing the arrangement command, fails as displayplacer_output_unrecognized. displayplacer 1.3.0 and 1.4.0 are both read

macos_send_notification tool

  • title required; body, subtitle, and sound=true (default notification sound) are optional
  • Each call creates a new notification — not idempotent
  • No permission required to post; an active Focus such as Do Not Disturb silences it unless that Focus allows the posting app

macos_manage_focus tool

  • get — best-effort; reads ~/Library/DoNotDisturb/DB/Assertions.json and returns status: "active" | "inactive" | "unknown"; unknown (with a reason) when that file cannot be read or parsed, which can happen when the server process is not allowed to read it; a cancelled call settles as cancelled, never as unknown
  • set — runs a shortcut named "Set Focus" from your Shortcuts library and passes it {"mode": …, "enabled": …} as JSON input. The shortcut is not built in: create it with Get Dictionary from Input feeding the Set Focus action. mode must exactly match a configured Focus name, including case (e.g. "Do Not Disturb", "Work"); enabled defaults to true. The result echoes mode and enabled; success: true means the shortcut finished without error (the Focus state is not read back)
  • Typed errors: shortcuts_unavailable (shortcuts CLI missing, or no shortcut named "Set Focus"), shortcut_failed (the shortcut exited with an error; carries its error text). A shortcut still running after 15 s is stopped and reported as a Timeout error; a cancelled call stops the run and settles as cancelled

macos_manage_finder tool

  • frontmost_path — POSIX path of the active Finder window, or null when none is open; a cancelled call settles as cancelled, never as null
  • get_selection — POSIX paths of selected items
  • reveal (open -R), open_with (open -a <App>, or the default app when app_name is omitted), trash (moves to Trash — recoverable, never a permanent delete)
  • open_with and trash check that the path exists before calling open or Finder
  • frontmost_path, get_selection, and trash script Finder and require Automation > Finder permission; reveal and open_with need none
  • Typed errors: finder_not_open, path_not_found, app_not_found (unknown open_with app), trash_refused (Finder declined an existing item — locked, in use, or no Trash on the volume), accessibility_required

macos://system/info resource

  • Current macOS system snapshot as application/json — battery, power source, Wi-Fi association and network name, hostname, version, uptime, display count, and unavailable
  • Same data is also reachable via macos_get_info

macos://audio/devices resource

  • All audio input and output devices, including which is the current default, as application/json
  • Requires SwitchAudioSource CLI
  • Same data is also reachable via macos_control_audio (action=list)

macos://displays resource

  • Connected display inventory — index, persistent IDs, type, resolution, origin, rotation, scaling, enabled state, plus current_config — as application/json; carries notice (and no index) when the NSScreen read fails
  • Requires displayplacer CLI
  • Same data is also reachable via macos_manage_displays (action=list)

Features

Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.

macOS-specific:

  • osascript service wraps both JXA (runJxa) and AppleScript (runAppleScript) with a configurable timeout
  • SwitchAudioSource and displayplacer integrations are optional dependencies, found on PATH, in /opt/homebrew/bin, or in /usr/local/bin on every call — the affected tools fail with ServiceUnavailable and an install instruction when no location has the CLI
  • macOS system binaries (osascript, screencapture, sips, pmset, networksetup, system_profiler, …) run by absolute path, so the host PATH never decides which binary runs
  • screencapture writes into a private directory beside the destination and the PNG is placed without replacing anything the call did not check; sips makes the JPEG preview; image sizes come from the PNG/JPEG headers
  • pmset for battery, networksetup + ifconfig for Wi-Fi, sw_vers for version, system_profiler -json SPDisplaysDataType for displays, Node's os for hostname and uptime
  • Permission-first design — macos_check_permissions reports exactly which process needs which grant before a tool hits Forbidden

Agent-friendly output:

  • Permission errors carry the accessibility_required reason and grant instructions for the permission actually denied — Privacy & Security > Accessibility, or Privacy & Security > Automation for the named app
  • A missing per-action argument (set without mode, quit without app_name), or text no AppleScript literal can carry (U+0000, an unpaired surrogate), is rejected before anything runs, as -32602 with reason invalid_arguments and a hint naming what to send; each multi-action tool advertises the per-action requirements in its inputSchema
  • Errors carry the failing program's own error text, never its command line or script source
  • Optional CLI dependencies surface ServiceUnavailable with the exact brew install command needed
  • macos_manage_windows reports display_index (the display a window overlaps most, or null when unknown) on every window so agents can reason about multi-monitor layouts
  • macos_take_screenshot separates the full-resolution disk write from an optional preview, sent as an image content block and as base64 in structuredContent so both surfaces carry it

Getting started

This server is local-only — it controls the macOS system it runs on. Use STDIO transport with your MCP client.

Add the following to your MCP client configuration file:

json
{  "mcpServers": {    "macos-mcp-server": {      "type": "stdio",      "command": "bunx",      "args": ["@cyanheads/macos-mcp-server@latest"],      "env": {        "MCP_TRANSPORT_TYPE": "stdio",        "MCP_LOG_LEVEL": "info"      }    }  }}

Or with npx (no Bun required):

json
{  "mcpServers": {    "macos-mcp-server": {      "type": "stdio",      "command": "npx",      "args": ["-y", "@cyanheads/macos-mcp-server@latest"],      "env": {        "MCP_TRANSPORT_TYPE": "stdio",        "MCP_LOG_LEVEL": "info"      }    }  }}

For Streamable HTTP, set the transport and start the server:

sh
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http# Server listens at http://localhost:3010/mcp

Prerequisites

  • macOS 13 (Ventura) or higher.
  • Bun v1.4.0 or higher (or Node.js v24+).
  • Optional: SwitchAudioSource for audio routing (brew install switchaudio-osx).
  • Optional: displayplacer 1.3.0 or 1.4.0 for display management (brew install displayplacer).

Either optional CLI works from Homebrew on Apple Silicon (/opt/homebrew/bin) or Intel (/usr/local/bin), or from anywhere on PATH.

Some tools require macOS permissions granted to the terminal or MCP host app:

PermissionRequired by
Accessibilitymacos_manage_windows (every action, list included), macos_manage_apps (hide/show; frontmost reads window_title with it), macos_control_system (lock keystroke; without it, lock starts the screen saver, which locks only when a password is required immediately)
Automation > System Eventsmacos_manage_windows, macos_manage_apps (every action but launch and quit), macos_control_appearance, macos_control_system (lock keystroke; same screen-saver fallback); macos_check_permissions cannot report it
Screen Recordingmacos_take_screenshot with target=window
Automation > Findermacos_manage_finder with action=frontmost_path, get_selection, or trash
Automation > Musicmacos_control_music (every action; get_current, pause, and stop on a closed Music script nothing)

Use macos_check_permissions to check current status before running permission-gated operations.

Installation

  1. Clone the repository:
sh
git clone https://github.com/cyanheads/macos-mcp-server.git
  1. Navigate into the directory:
sh
cd macos-mcp-server
  1. Install dependencies:
sh
bun install
  1. Configure environment:
sh
cp .env.example .env# edit .env if you want to set MACOS_SCREENSHOT_DIR or MACOS_DISPLAY_LAYOUTS

Configuration

VariableDescriptionDefault
MACOS_SCREENSHOT_DIRDefault directory for screenshot files, and one of the directories a custom path may resolve into (with /tmp and the home directory). ~ and symlinks are resolved.~/Desktop
MACOS_DISPLAY_LAYOUTSJSON object mapping layout names to non-empty displayplacer argument strings. Used by macos_manage_displays action=apply_layout; action=list returns the names as layouts. An invalid value fails only apply_layout.{}
MCP_TRANSPORT_TYPETransport: stdio or http.stdio
MCP_HTTP_PORTPort for HTTP server.3010
MCP_AUTH_MODEAuth mode: none, jwt, or oauth.none
MCP_LOG_LEVELLog level.info
MCP_SESSION_MODESession storage: auto, stateful, or stateless (HTTP only). This server holds no per-session state, so .env.example sets stateless explicitly.stateless (declared in src/index.ts)
OTEL_ENABLEDEnable OpenTelemetry instrumentation.false

See .env.example for the full list of optional overrides.

Display layout example:

sh
# Get the current displayplacer command for your setup:displayplacer list
# Then configure named layouts in your env:MACOS_DISPLAY_LAYOUTS='{"office":"\"id:1234 res:2560x1440 hz:60 color_depth:8 scaling:on origin:(0,0) degree:0\" \"id:5678 res:1920x1080 hz:60 color_depth:8 scaling:on origin:(2560,0) degree:0\""}'

Each screen gets its own quoted segment, the form displayplacer list prints its arrangement command in; a layout value without quoted segments reaches displayplacer as a single argument. macos_manage_displays action=list returns current_config with the same per-screen quoted segments, and returns the configured names as layouts.

Running the server

Local development

sh
# One-time buildbun run rebuild
# Run the built serverbun run start:stdio
# Run checksbun run devcheck   # Lint, format, typecheck, security, changelog syncbun run test       # Vitest test suitebun run lint:mcp   # Validate MCP definitions against spec

Project structure

DirectoryPurpose
src/index.tscreateApp() entry — registers tools/resources and inits services
src/config/server-config.tsMACOS_SCREENSHOT_DIR and MACOS_DISPLAY_LAYOUTS env parsing
src/mcp-server/tools/definitions/14 tool definitions (macos-*.tool.ts)
src/mcp-server/tools/action-requirements.tsPer-action argument requirements — enforced by the input schema and advertised in inputSchema
src/mcp-server/resources/definitions/3 resource definitions (macos-*.resource.ts)
src/services/osascript/osascript JXA + AppleScript runner with configurable timeout and permission-denial classification, AppleScript string literals for caller text, and the running-app name resolver
src/utils/exec-failure.tsCaller-safe error text for a failed CLI call (never the command line)
src/services/binaries/binary-paths.tsAbsolute paths of the macOS system binaries, and the per-call PATH + Homebrew lookup for displayplacer and SwitchAudioSource
src/services/permissions/Prompt-free Accessibility, Screen Recording, Automation > Finder, and Automation > Music checks
src/services/audio/SwitchAudioSource device listing and switching
src/services/display/displayplacer list and apply-layout, plus the JXA NSScreen read that gives each display its index
src/services/screencapture/screencapture PNG capture, path checks and no-replace placement, sips JPEG preview
src/services/system-info/Battery, Wi-Fi, version, and display count via pmset/networksetup/ifconfig/sw_vers/system_profiler; hostname and uptime via node:os
tests/tools/Tool tests mirroring definitions

Development guide

See CLAUDE.md for development guidelines and architectural rules. The short version:

  • Handlers throw, framework catches — no try/catch in tool logic
  • Use ctx.log for request-scoped logging; no console calls
  • Read actual system/CLI state and never fabricate it — return null or unknown when the OS can't answer (e.g. battery on desktops, Focus status when the Focus state file cannot be read) rather than guessing
  • Services are singletons initialized in createApp() and accessed via get*Service() accessors

Contributing

Issues are welcome. Run checks and tests before submitting:

sh
bun run devcheckbun run test

License

Apache-2.0 — see LICENSE for details.

來源:README.md,提交 c669e89

工具

0
SourceWeft 尚未在沙箱中啟動過該軟體包,因此其工具未知。

版本歷史

6
  1. v0.4.0最新Oct 29, 2026
  2. v0.3.0Oct 22, 2026
  3. v0.2.1Oct 9, 2026
  4. v0.2.0Sep 24, 2026
  5. v0.1.6Sep 20, 2026
  6. v0.1.5Sep 16, 2026

評分

已滿足適用檢查項的 50%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分

    請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。

  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分