apple-system-mcp 1.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- apple_system_mcp-1.0.1/LICENSE +21 -0
- apple_system_mcp-1.0.1/PKG-INFO +167 -0
- apple_system_mcp-1.0.1/README.md +137 -0
- apple_system_mcp-1.0.1/pyproject.toml +57 -0
- apple_system_mcp-1.0.1/setup.cfg +4 -0
- apple_system_mcp-1.0.1/src/apple_system_mcp/__init__.py +1 -0
- apple_system_mcp-1.0.1/src/apple_system_mcp/config.py +36 -0
- apple_system_mcp-1.0.1/src/apple_system_mcp/models.py +151 -0
- apple_system_mcp-1.0.1/src/apple_system_mcp/permissions.py +66 -0
- apple_system_mcp-1.0.1/src/apple_system_mcp/system_bridge.py +678 -0
- apple_system_mcp-1.0.1/src/apple_system_mcp/tools.py +850 -0
- apple_system_mcp-1.0.1/src/apple_system_mcp.egg-info/PKG-INFO +167 -0
- apple_system_mcp-1.0.1/src/apple_system_mcp.egg-info/SOURCES.txt +17 -0
- apple_system_mcp-1.0.1/src/apple_system_mcp.egg-info/dependency_links.txt +1 -0
- apple_system_mcp-1.0.1/src/apple_system_mcp.egg-info/entry_points.txt +2 -0
- apple_system_mcp-1.0.1/src/apple_system_mcp.egg-info/requires.txt +7 -0
- apple_system_mcp-1.0.1/src/apple_system_mcp.egg-info/top_level.txt +1 -0
- apple_system_mcp-1.0.1/tests/test_system_bridge.py +36 -0
- apple_system_mcp-1.0.1/tests/test_tools.py +286 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Apple MCP Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: apple-system-mcp
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: Local Apple System MCP server for macOS
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/JonathanRReed/Apple-MCPs
|
|
7
|
+
Project-URL: Repository, https://github.com/JonathanRReed/Apple-MCPs
|
|
8
|
+
Project-URL: Changelog, https://github.com/JonathanRReed/Apple-MCPs/blob/main/CHANGELOG.md
|
|
9
|
+
Project-URL: Issues, https://github.com/JonathanRReed/Apple-MCPs/issues
|
|
10
|
+
Keywords: mcp,model-context-protocol,macos,apple,ai-agent,automation
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Environment :: MacOS X
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: MacOS
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: apple-mcp-common<2,>=1.0.0
|
|
24
|
+
Requires-Dist: mcp<3,>=2.0.0
|
|
25
|
+
Requires-Dist: pydantic>=2.12.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest<10,>=8; extra == "dev"
|
|
28
|
+
Requires-Dist: ruff<1,>=0.12; extra == "dev"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
<!-- mcp-name: io.github.jonathanrreed/apple-system-mcp -->
|
|
32
|
+
|
|
33
|
+
# Apple System MCP
|
|
34
|
+
|
|
35
|
+
Local MCP server for macOS system context, truthful Focus support metadata, assistant-relevant settings reads, explicit settings writes, and bounded GUI fallback automation.
|
|
36
|
+
|
|
37
|
+
## Capabilities
|
|
38
|
+
|
|
39
|
+
- battery status
|
|
40
|
+
- frontmost app
|
|
41
|
+
- running applications
|
|
42
|
+
- clipboard read and write
|
|
43
|
+
- local notifications
|
|
44
|
+
- open an application
|
|
45
|
+
- appearance settings
|
|
46
|
+
- accessibility settings
|
|
47
|
+
- Dock settings
|
|
48
|
+
- Finder settings
|
|
49
|
+
- truthful Focus support metadata
|
|
50
|
+
- combined system context snapshots
|
|
51
|
+
- appearance mode write
|
|
52
|
+
- Finder visibility and bar writes
|
|
53
|
+
- Dock write controls for autohide and recent apps
|
|
54
|
+
- accessibility writes for reduce motion, increase contrast, and reduce transparency
|
|
55
|
+
- GUI fallback tools for menu clicks, key presses, text entry, button clicks, and pop-up selection
|
|
56
|
+
- preference-domain inspection via `defaults export`
|
|
57
|
+
- resources: `system://status`, `system://applications`, `system://settings`, `system://context`
|
|
58
|
+
- prompt: `system_capture_context`
|
|
59
|
+
- tool discovery helpers `search_tools` and `get_tool_info` for context-constrained clients
|
|
60
|
+
|
|
61
|
+
## Install On This Mac
|
|
62
|
+
|
|
63
|
+
<details>
|
|
64
|
+
<summary>Quick start (uvx, from PyPI)</summary>
|
|
65
|
+
|
|
66
|
+
With [uv](https://docs.astral.sh/uv/getting-started/installation/) installed:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
uvx apple-system-mcp
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
No clone, no venv management.
|
|
73
|
+
|
|
74
|
+
</details>
|
|
75
|
+
|
|
76
|
+
<details>
|
|
77
|
+
<summary>From a clone</summary>
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
git clone https://github.com/JonathanRReed/Apple-MCPs.git
|
|
81
|
+
cd Apple-MCPs
|
|
82
|
+
uv sync --all-packages
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
This builds one workspace environment with every server's entry point in `.venv/bin` (for example `.venv/bin/apple-system-mcp`). You can also point an MCP client at `AppleSystem-MCP/start.sh`, which prefers `uv run` and falls back to a plain venv bootstrap (Python 3.11+ required).
|
|
86
|
+
|
|
87
|
+
</details>
|
|
88
|
+
|
|
89
|
+
## Install In AI Agents
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"mcpServers": {
|
|
94
|
+
"apple-system": {
|
|
95
|
+
"command": "uvx",
|
|
96
|
+
"args": ["apple-system-mcp"],
|
|
97
|
+
"env": {
|
|
98
|
+
"APPLE_SYSTEM_MCP_SAFETY_MODE": "safe_manage"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Running from a clone instead? Use `/path/to/Apple-MCPs/AppleSystem-MCP/start.sh` as the command with empty `args`.
|
|
106
|
+
|
|
107
|
+
Claude Code:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
claude mcp add --transport stdio --scope project apple-system -- uvx apple-system-mcp
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Transport
|
|
114
|
+
|
|
115
|
+
`stdio` is the default and recommended transport. Set `APPLE_SYSTEM_MCP_TRANSPORT=streamable-http` (with optional `APPLE_SYSTEM_MCP_HOST` and `APPLE_SYSTEM_MCP_PORT`) to serve Streamable HTTP instead.
|
|
116
|
+
|
|
117
|
+
## Prompting Notes
|
|
118
|
+
|
|
119
|
+
- `tools/list` returns the full System tool surface. Context-constrained clients can use `search_tools` first, then `get_tool_info` for the System tool they need.
|
|
120
|
+
- Use this server when the user’s current desktop context matters.
|
|
121
|
+
- Read battery state and the frontmost app before interruptive actions.
|
|
122
|
+
- Use the settings tools before falling back to raw `defaults read` in prompts or E2E checks.
|
|
123
|
+
- Use `APPLE_SYSTEM_MCP_SAFETY_MODE=safe_manage` for assistant-grade operation.
|
|
124
|
+
- Keep `safe_readonly` only for audit-only or context-only deployments.
|
|
125
|
+
- Keep GUI fallback narrow. Prefer native app-domain MCPs first, then explicit System tools, then GUI tools only when native support is missing.
|
|
126
|
+
- Focus reporting is best-effort and truthful. This server does not claim Notification Center history support when macOS does not expose it cleanly on an unsigned local install.
|
|
127
|
+
|
|
128
|
+
## Health And Recovery
|
|
129
|
+
|
|
130
|
+
- `system_health`
|
|
131
|
+
- `system_permission_guide`
|
|
132
|
+
- `system_get_settings_snapshot`
|
|
133
|
+
- `system_get_focus_status`
|
|
134
|
+
- `system_get_context_snapshot`
|
|
135
|
+
- `system_read_preference_domain`
|
|
136
|
+
|
|
137
|
+
## Assistant-Grade Control Surface
|
|
138
|
+
|
|
139
|
+
The launch System write surface is explicit, not generic:
|
|
140
|
+
|
|
141
|
+
- `system_set_appearance_mode`
|
|
142
|
+
- `system_set_show_all_extensions`
|
|
143
|
+
- `system_set_show_hidden_files`
|
|
144
|
+
- `system_set_finder_path_bar`
|
|
145
|
+
- `system_set_finder_status_bar`
|
|
146
|
+
- `system_set_dock_autohide`
|
|
147
|
+
- `system_set_dock_show_recents`
|
|
148
|
+
- `system_set_reduce_motion`
|
|
149
|
+
- `system_set_increase_contrast`
|
|
150
|
+
- `system_set_reduce_transparency`
|
|
151
|
+
|
|
152
|
+
The bounded GUI fallback surface is:
|
|
153
|
+
|
|
154
|
+
- `system_gui_list_menu_bar_items`
|
|
155
|
+
- `system_gui_click_menu_path`
|
|
156
|
+
- `system_gui_press_keys`
|
|
157
|
+
- `system_gui_type_text`
|
|
158
|
+
- `system_gui_click_button`
|
|
159
|
+
- `system_gui_choose_popup_value`
|
|
160
|
+
|
|
161
|
+
## Launch Checklist
|
|
162
|
+
|
|
163
|
+
- Add `uvx apple-system-mcp` (or a clone's `AppleSystem-MCP/start.sh`) to your MCP client
|
|
164
|
+
- Reload or reconnect the client so the System tool surface is loaded into context
|
|
165
|
+
- Call `system_health` first
|
|
166
|
+
- If a scoped system action is blocked, call `system_permission_guide`
|
|
167
|
+
- Call `system_status` to verify the full context surface is available
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<!-- mcp-name: io.github.jonathanrreed/apple-system-mcp -->
|
|
2
|
+
|
|
3
|
+
# Apple System MCP
|
|
4
|
+
|
|
5
|
+
Local MCP server for macOS system context, truthful Focus support metadata, assistant-relevant settings reads, explicit settings writes, and bounded GUI fallback automation.
|
|
6
|
+
|
|
7
|
+
## Capabilities
|
|
8
|
+
|
|
9
|
+
- battery status
|
|
10
|
+
- frontmost app
|
|
11
|
+
- running applications
|
|
12
|
+
- clipboard read and write
|
|
13
|
+
- local notifications
|
|
14
|
+
- open an application
|
|
15
|
+
- appearance settings
|
|
16
|
+
- accessibility settings
|
|
17
|
+
- Dock settings
|
|
18
|
+
- Finder settings
|
|
19
|
+
- truthful Focus support metadata
|
|
20
|
+
- combined system context snapshots
|
|
21
|
+
- appearance mode write
|
|
22
|
+
- Finder visibility and bar writes
|
|
23
|
+
- Dock write controls for autohide and recent apps
|
|
24
|
+
- accessibility writes for reduce motion, increase contrast, and reduce transparency
|
|
25
|
+
- GUI fallback tools for menu clicks, key presses, text entry, button clicks, and pop-up selection
|
|
26
|
+
- preference-domain inspection via `defaults export`
|
|
27
|
+
- resources: `system://status`, `system://applications`, `system://settings`, `system://context`
|
|
28
|
+
- prompt: `system_capture_context`
|
|
29
|
+
- tool discovery helpers `search_tools` and `get_tool_info` for context-constrained clients
|
|
30
|
+
|
|
31
|
+
## Install On This Mac
|
|
32
|
+
|
|
33
|
+
<details>
|
|
34
|
+
<summary>Quick start (uvx, from PyPI)</summary>
|
|
35
|
+
|
|
36
|
+
With [uv](https://docs.astral.sh/uv/getting-started/installation/) installed:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
uvx apple-system-mcp
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
No clone, no venv management.
|
|
43
|
+
|
|
44
|
+
</details>
|
|
45
|
+
|
|
46
|
+
<details>
|
|
47
|
+
<summary>From a clone</summary>
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
git clone https://github.com/JonathanRReed/Apple-MCPs.git
|
|
51
|
+
cd Apple-MCPs
|
|
52
|
+
uv sync --all-packages
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
This builds one workspace environment with every server's entry point in `.venv/bin` (for example `.venv/bin/apple-system-mcp`). You can also point an MCP client at `AppleSystem-MCP/start.sh`, which prefers `uv run` and falls back to a plain venv bootstrap (Python 3.11+ required).
|
|
56
|
+
|
|
57
|
+
</details>
|
|
58
|
+
|
|
59
|
+
## Install In AI Agents
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"mcpServers": {
|
|
64
|
+
"apple-system": {
|
|
65
|
+
"command": "uvx",
|
|
66
|
+
"args": ["apple-system-mcp"],
|
|
67
|
+
"env": {
|
|
68
|
+
"APPLE_SYSTEM_MCP_SAFETY_MODE": "safe_manage"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Running from a clone instead? Use `/path/to/Apple-MCPs/AppleSystem-MCP/start.sh` as the command with empty `args`.
|
|
76
|
+
|
|
77
|
+
Claude Code:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
claude mcp add --transport stdio --scope project apple-system -- uvx apple-system-mcp
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Transport
|
|
84
|
+
|
|
85
|
+
`stdio` is the default and recommended transport. Set `APPLE_SYSTEM_MCP_TRANSPORT=streamable-http` (with optional `APPLE_SYSTEM_MCP_HOST` and `APPLE_SYSTEM_MCP_PORT`) to serve Streamable HTTP instead.
|
|
86
|
+
|
|
87
|
+
## Prompting Notes
|
|
88
|
+
|
|
89
|
+
- `tools/list` returns the full System tool surface. Context-constrained clients can use `search_tools` first, then `get_tool_info` for the System tool they need.
|
|
90
|
+
- Use this server when the user’s current desktop context matters.
|
|
91
|
+
- Read battery state and the frontmost app before interruptive actions.
|
|
92
|
+
- Use the settings tools before falling back to raw `defaults read` in prompts or E2E checks.
|
|
93
|
+
- Use `APPLE_SYSTEM_MCP_SAFETY_MODE=safe_manage` for assistant-grade operation.
|
|
94
|
+
- Keep `safe_readonly` only for audit-only or context-only deployments.
|
|
95
|
+
- Keep GUI fallback narrow. Prefer native app-domain MCPs first, then explicit System tools, then GUI tools only when native support is missing.
|
|
96
|
+
- Focus reporting is best-effort and truthful. This server does not claim Notification Center history support when macOS does not expose it cleanly on an unsigned local install.
|
|
97
|
+
|
|
98
|
+
## Health And Recovery
|
|
99
|
+
|
|
100
|
+
- `system_health`
|
|
101
|
+
- `system_permission_guide`
|
|
102
|
+
- `system_get_settings_snapshot`
|
|
103
|
+
- `system_get_focus_status`
|
|
104
|
+
- `system_get_context_snapshot`
|
|
105
|
+
- `system_read_preference_domain`
|
|
106
|
+
|
|
107
|
+
## Assistant-Grade Control Surface
|
|
108
|
+
|
|
109
|
+
The launch System write surface is explicit, not generic:
|
|
110
|
+
|
|
111
|
+
- `system_set_appearance_mode`
|
|
112
|
+
- `system_set_show_all_extensions`
|
|
113
|
+
- `system_set_show_hidden_files`
|
|
114
|
+
- `system_set_finder_path_bar`
|
|
115
|
+
- `system_set_finder_status_bar`
|
|
116
|
+
- `system_set_dock_autohide`
|
|
117
|
+
- `system_set_dock_show_recents`
|
|
118
|
+
- `system_set_reduce_motion`
|
|
119
|
+
- `system_set_increase_contrast`
|
|
120
|
+
- `system_set_reduce_transparency`
|
|
121
|
+
|
|
122
|
+
The bounded GUI fallback surface is:
|
|
123
|
+
|
|
124
|
+
- `system_gui_list_menu_bar_items`
|
|
125
|
+
- `system_gui_click_menu_path`
|
|
126
|
+
- `system_gui_press_keys`
|
|
127
|
+
- `system_gui_type_text`
|
|
128
|
+
- `system_gui_click_button`
|
|
129
|
+
- `system_gui_choose_popup_value`
|
|
130
|
+
|
|
131
|
+
## Launch Checklist
|
|
132
|
+
|
|
133
|
+
- Add `uvx apple-system-mcp` (or a clone's `AppleSystem-MCP/start.sh`) to your MCP client
|
|
134
|
+
- Reload or reconnect the client so the System tool surface is loaded into context
|
|
135
|
+
- Call `system_health` first
|
|
136
|
+
- If a scoped system action is blocked, call `system_permission_guide`
|
|
137
|
+
- Call `system_status` to verify the full context surface is available
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "apple-system-mcp"
|
|
7
|
+
version = "1.0.1"
|
|
8
|
+
description = "Local Apple System MCP server for macOS"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
requires-python = ">=3.11"
|
|
13
|
+
keywords = ["mcp", "model-context-protocol", "macos", "apple", "ai-agent", "automation"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 5 - Production/Stable",
|
|
16
|
+
"Environment :: MacOS X",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Operating System :: MacOS",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Programming Language :: Python :: 3.13",
|
|
22
|
+
"Programming Language :: Python :: 3.14",
|
|
23
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"apple-mcp-common>=1.0.0,<2",
|
|
27
|
+
"mcp>=2.0.0,<3",
|
|
28
|
+
"pydantic>=2.12.0",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/JonathanRReed/Apple-MCPs"
|
|
33
|
+
Repository = "https://github.com/JonathanRReed/Apple-MCPs"
|
|
34
|
+
Changelog = "https://github.com/JonathanRReed/Apple-MCPs/blob/main/CHANGELOG.md"
|
|
35
|
+
Issues = "https://github.com/JonathanRReed/Apple-MCPs/issues"
|
|
36
|
+
|
|
37
|
+
[project.scripts]
|
|
38
|
+
apple-system-mcp = "apple_system_mcp.tools:main"
|
|
39
|
+
|
|
40
|
+
[project.optional-dependencies]
|
|
41
|
+
dev = [
|
|
42
|
+
"pytest>=8,<10",
|
|
43
|
+
"ruff>=0.12,<1",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[tool.setuptools]
|
|
47
|
+
package-dir = {"" = "src"}
|
|
48
|
+
|
|
49
|
+
[tool.setuptools.packages.find]
|
|
50
|
+
where = ["src"]
|
|
51
|
+
|
|
52
|
+
[tool.pytest.ini_options]
|
|
53
|
+
pythonpath = ["src"]
|
|
54
|
+
testpaths = ["tests"]
|
|
55
|
+
|
|
56
|
+
[tool.uv.sources]
|
|
57
|
+
apple-mcp-common = { workspace = true }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Apple System MCP package."""
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from functools import lru_cache
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass(frozen=True)
|
|
7
|
+
class Settings:
|
|
8
|
+
server_name: str
|
|
9
|
+
version: str
|
|
10
|
+
safety_mode: str
|
|
11
|
+
transport: str
|
|
12
|
+
host: str
|
|
13
|
+
port: int
|
|
14
|
+
log_level: str
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _parse_int(value: str | None, default: int) -> int:
|
|
18
|
+
if value is None:
|
|
19
|
+
return default
|
|
20
|
+
try:
|
|
21
|
+
return int(value)
|
|
22
|
+
except ValueError:
|
|
23
|
+
return default
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@lru_cache(maxsize=1)
|
|
27
|
+
def load_settings() -> Settings:
|
|
28
|
+
return Settings(
|
|
29
|
+
server_name="Apple System MCP",
|
|
30
|
+
version="1.0.1",
|
|
31
|
+
safety_mode=os.environ.get("APPLE_SYSTEM_MCP_SAFETY_MODE", "safe_manage").strip().lower() or "safe_manage",
|
|
32
|
+
transport=os.environ.get("APPLE_SYSTEM_MCP_TRANSPORT", "stdio").strip().lower() or "stdio",
|
|
33
|
+
host=os.environ.get("APPLE_SYSTEM_MCP_HOST", "127.0.0.1"),
|
|
34
|
+
port=_parse_int(os.environ.get("APPLE_SYSTEM_MCP_PORT"), 8000),
|
|
35
|
+
log_level=os.environ.get("APPLE_SYSTEM_MCP_LOG_LEVEL", "INFO").strip().upper() or "INFO",
|
|
36
|
+
)
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ToolError(BaseModel):
|
|
7
|
+
error_code: str = Field(description="Stable machine-readable error code")
|
|
8
|
+
message: str = Field(description="Human-readable error message")
|
|
9
|
+
suggestion: str | None = Field(default=None, description="Suggested next step")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ErrorResponse(BaseModel):
|
|
13
|
+
ok: bool = False
|
|
14
|
+
error: ToolError
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class HealthResponse(BaseModel):
|
|
18
|
+
ok: bool = True
|
|
19
|
+
server_name: str
|
|
20
|
+
version: str
|
|
21
|
+
safety_mode: str
|
|
22
|
+
transport: str
|
|
23
|
+
capabilities: list[str]
|
|
24
|
+
supports: list[str]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class BatteryStatus(BaseModel):
|
|
28
|
+
percentage: int | None = None
|
|
29
|
+
power_source: str | None = None
|
|
30
|
+
charging: bool | None = None
|
|
31
|
+
raw: str
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class AppRecord(BaseModel):
|
|
35
|
+
name: str
|
|
36
|
+
bundle_id: str | None = None
|
|
37
|
+
process_id: int | None = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ClipboardResponse(BaseModel):
|
|
41
|
+
ok: bool = True
|
|
42
|
+
text: str
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class NotificationResponse(BaseModel):
|
|
46
|
+
ok: bool = True
|
|
47
|
+
delivered: bool
|
|
48
|
+
title: str
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class OpenAppResponse(BaseModel):
|
|
52
|
+
ok: bool = True
|
|
53
|
+
opened: bool
|
|
54
|
+
application: str
|
|
55
|
+
bundle_id: str | None = None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class RunningAppsResponse(BaseModel):
|
|
59
|
+
ok: bool = True
|
|
60
|
+
apps: list[AppRecord]
|
|
61
|
+
count: int
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class StatusResponse(BaseModel):
|
|
65
|
+
ok: bool = True
|
|
66
|
+
battery: BatteryStatus | None = None
|
|
67
|
+
frontmost_app: str | None = None
|
|
68
|
+
frontmost_application: AppRecord | None = None
|
|
69
|
+
running_apps_count: int | None = None
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class SettingsDomainsResponse(BaseModel):
|
|
73
|
+
ok: bool = True
|
|
74
|
+
domains: list[dict[str, str]]
|
|
75
|
+
count: int
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class SettingsSectionResponse(BaseModel):
|
|
79
|
+
ok: bool = True
|
|
80
|
+
section: str
|
|
81
|
+
values: dict[str, object]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class SettingsSnapshotResponse(BaseModel):
|
|
85
|
+
ok: bool = True
|
|
86
|
+
appearance: dict[str, object]
|
|
87
|
+
accessibility: dict[str, object]
|
|
88
|
+
dock: dict[str, object]
|
|
89
|
+
finder: dict[str, object]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class FocusStatusResponse(BaseModel):
|
|
93
|
+
ok: bool = True
|
|
94
|
+
focus_supported: bool
|
|
95
|
+
focus_active: bool | None = None
|
|
96
|
+
focus_name: str | None = None
|
|
97
|
+
observed_at: str
|
|
98
|
+
source: str
|
|
99
|
+
confidence: float | None = None
|
|
100
|
+
notes: list[str] = Field(default_factory=list)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class SystemContextResponse(BaseModel):
|
|
104
|
+
ok: bool = True
|
|
105
|
+
observed_at: str
|
|
106
|
+
battery: BatteryStatus | None = None
|
|
107
|
+
frontmost_app: str | None = None
|
|
108
|
+
frontmost_application: AppRecord | None = None
|
|
109
|
+
running_apps_count: int | None = None
|
|
110
|
+
focus: FocusStatusResponse
|
|
111
|
+
notification_history_supported: bool = False
|
|
112
|
+
notification_history_notes: list[str] = Field(default_factory=list)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class PreferenceDomainResponse(BaseModel):
|
|
116
|
+
ok: bool = True
|
|
117
|
+
domain: str
|
|
118
|
+
current_host: bool = False
|
|
119
|
+
values: dict[str, object]
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class SettingMutationResponse(BaseModel):
|
|
123
|
+
ok: bool = True
|
|
124
|
+
section: str
|
|
125
|
+
setting: str
|
|
126
|
+
requested_value: bool | str
|
|
127
|
+
observed_value: bool | str | None = None
|
|
128
|
+
restarted_processes: list[str] = Field(default_factory=list)
|
|
129
|
+
used_gui_fallback: bool = False
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class GuiMenuItemsResponse(BaseModel):
|
|
133
|
+
ok: bool = True
|
|
134
|
+
application: str
|
|
135
|
+
bundle_id: str | None = None
|
|
136
|
+
process_id: int | None = None
|
|
137
|
+
menu_bar_items: list[str]
|
|
138
|
+
count: int
|
|
139
|
+
used_gui_fallback: bool = True
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class GuiActionResponse(BaseModel):
|
|
143
|
+
ok: bool = True
|
|
144
|
+
action: str
|
|
145
|
+
application: str | None = None
|
|
146
|
+
bundle_id: str | None = None
|
|
147
|
+
process_id: int | None = None
|
|
148
|
+
target: str | None = None
|
|
149
|
+
selector_type: str | None = None
|
|
150
|
+
value: bool | str | list[str] | None = None
|
|
151
|
+
used_gui_fallback: bool = True
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from apple_system_mcp.config import load_settings
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class SafetyError(Exception):
|
|
10
|
+
error_code: str
|
|
11
|
+
message: str
|
|
12
|
+
suggestion: str | None = None
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
READ_ONLY_ACTIONS = {
|
|
16
|
+
"system_health",
|
|
17
|
+
"system_status",
|
|
18
|
+
"system_get_battery",
|
|
19
|
+
"system_get_frontmost_app",
|
|
20
|
+
"system_list_running_apps",
|
|
21
|
+
"system_get_clipboard",
|
|
22
|
+
"system_list_settings_domains",
|
|
23
|
+
"system_get_appearance_settings",
|
|
24
|
+
"system_get_accessibility_settings",
|
|
25
|
+
"system_get_dock_settings",
|
|
26
|
+
"system_get_finder_settings",
|
|
27
|
+
"system_get_settings_snapshot",
|
|
28
|
+
"system_get_focus_status",
|
|
29
|
+
"system_get_context_snapshot",
|
|
30
|
+
"system_read_preference_domain",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
MANAGE_ACTIONS = {
|
|
34
|
+
"system_set_clipboard",
|
|
35
|
+
"system_show_notification",
|
|
36
|
+
"system_open_application",
|
|
37
|
+
"system_set_appearance_mode",
|
|
38
|
+
"system_set_show_all_extensions",
|
|
39
|
+
"system_set_show_hidden_files",
|
|
40
|
+
"system_set_finder_path_bar",
|
|
41
|
+
"system_set_finder_status_bar",
|
|
42
|
+
"system_set_dock_autohide",
|
|
43
|
+
"system_set_dock_show_recents",
|
|
44
|
+
"system_set_reduce_motion",
|
|
45
|
+
"system_set_increase_contrast",
|
|
46
|
+
"system_set_reduce_transparency",
|
|
47
|
+
"system_gui_list_menu_bar_items",
|
|
48
|
+
"system_gui_click_menu_path",
|
|
49
|
+
"system_gui_press_keys",
|
|
50
|
+
"system_gui_type_text",
|
|
51
|
+
"system_gui_click_button",
|
|
52
|
+
"system_gui_choose_popup_value",
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def ensure_action_allowed(action: str) -> None:
|
|
57
|
+
safety_mode = load_settings().safety_mode
|
|
58
|
+
if action in READ_ONLY_ACTIONS:
|
|
59
|
+
return
|
|
60
|
+
if action in MANAGE_ACTIONS and safety_mode in {"safe_manage", "full_access"}:
|
|
61
|
+
return
|
|
62
|
+
raise SafetyError(
|
|
63
|
+
error_code="SAFETY_RESTRICTION",
|
|
64
|
+
message=f"{action} is not allowed while APPLE_SYSTEM_MCP_SAFETY_MODE={safety_mode}.",
|
|
65
|
+
suggestion="Use safe_manage or full_access for system mutation tools.",
|
|
66
|
+
)
|