metal-gpu-trace 0.4.0__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.
@@ -0,0 +1,56 @@
1
+ name: publish
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ jobs:
8
+ test:
9
+ runs-on: macos-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+ - uses: actions/setup-python@v5
13
+ with:
14
+ python-version: "3.9"
15
+ - run: python -I test_metal_trace_mcp.py
16
+ # the tag, the code and server.json must name the same version
17
+ - run: |
18
+ tag=${GITHUB_REF_NAME#v}
19
+ code=$(sed -n 's/^__version__ = "\(.*\)"/\1/p' metal_trace_mcp.py)
20
+ python -I -c "import json, sys; s = json.load(open('server.json')); sys.exit(0 if s['version'] == s['packages'][0]['version'] == '$tag' == '$code' else 'tag $tag, code $code and server.json differ')"
21
+
22
+ # the GitHub release goes out even when PyPI is not set up yet
23
+ release:
24
+ needs: test
25
+ runs-on: ubuntu-latest
26
+ permissions:
27
+ contents: write
28
+ steps:
29
+ - uses: actions/checkout@v4
30
+ - uses: astral-sh/setup-uv@v6
31
+ - run: uv build
32
+ - name: GitHub release with the changelog section
33
+ env:
34
+ GH_TOKEN: ${{ github.token }}
35
+ run: |
36
+ version=${GITHUB_REF_NAME#v}
37
+ awk -v v="## $version " 'index($0, v) == 1 {on = 1; next} /^## / {on = 0} on' CHANGELOG.md > notes.md
38
+ gh release create "$GITHUB_REF_NAME" dist/* --title "$GITHUB_REF_NAME" --notes-file notes.md
39
+
40
+ publish:
41
+ needs: test
42
+ runs-on: ubuntu-latest
43
+ environment: pypi
44
+ permissions:
45
+ id-token: write
46
+ contents: read
47
+ steps:
48
+ - uses: actions/checkout@v4
49
+ - uses: astral-sh/setup-uv@v6
50
+ - run: uv build
51
+ - uses: pypa/gh-action-pypi-publish@release/v1
52
+ - name: Publish to the MCP Registry
53
+ run: |
54
+ curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_linux_amd64.tar.gz" | tar xz mcp-publisher
55
+ ./mcp-publisher login github-oidc
56
+ ./mcp-publisher publish
@@ -0,0 +1,16 @@
1
+ name: test
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ test:
7
+ runs-on: macos-latest
8
+ strategy:
9
+ matrix:
10
+ python: ["3.9", "3.13"]
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ - uses: actions/setup-python@v5
14
+ with:
15
+ python-version: ${{ matrix.python }}
16
+ - run: python -I test_metal_trace_mcp.py
@@ -0,0 +1,5 @@
1
+ __pycache__/
2
+ perf-runs/
3
+ logs/
4
+ .claude/
5
+ dist/
@@ -0,0 +1,30 @@
1
+ # Changelog
2
+
3
+ Versions follow [Semantic Versioning](https://semver.org); each one is a `vX.Y.Z` tag. `metal-gpu-trace --version`
4
+ and the MCP `initialize` answer (`serverInfo.version`) print the installed one.
5
+
6
+ ## 0.4.0 — 2026-10-17
7
+
8
+ - `perf_diagnose`: says whether a recording splits its GPU work into too many short encoders, as translation layers
9
+ do, with encoders and command buffers per frame, the labels with the most GPU time and the GPU's idle time.
10
+ Defaults (4 encoders per frame, 25 µs, 25 % short) come from bgfx 17-drawstress on an M1 Pro: 7 encoders per frame
11
+ and 50 % under 25 µs under Mullion, 2 and 2 % in native Metal.
12
+ - Summaries gain `encoders` and `gpu_idle`, from `metal-gpu-intervals` and `metal-application-encoders-list`.
13
+ Summaries written by older versions have neither: record again to diagnose them.
14
+ - Every tool parameter has a description; `metal-gpu-trace --version`.
15
+ - Registry files: `server.json` (MCP Registry), `glama.json`; `CHANGELOG.md`.
16
+ - `trace_tables` and `trace_query` take a run name; `perf_runs` says whether each run kept its `.trace`
17
+ (`trace_kept`).
18
+ - A trace without `metal-gpu-intervals` fails with a hint; summaries list other missing tables in `missing_tables`.
19
+ - `capture_summary` returns `calls_total`.
20
+ - Tools carry `title`, `readOnlyHint`, `destructiveHint`, `idempotentHint` and `openWorldHint`; `initialize`
21
+ returns usage `instructions`.
22
+ - A recording's deadline grows with its length (10 s per recorded second plus 120 s), and one that overruns leaves
23
+ no files behind.
24
+ - `perf-runs/` is created only when a recording needs it. Tests run under pytest; CI on macOS with Python 3.9 and
25
+ 3.13.
26
+
27
+ ## 0.2.0
28
+
29
+ - First release on GitHub: `perf_record`, `perf_attach`, `perf_runs`, `perf_compare`, `trace_tables`,
30
+ `trace_query`, `capture_summary`, `capture_records`, `gpu_family`, and the GPU family resources.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hernan Flórez Burgos
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,174 @@
1
+ Metadata-Version: 2.5
2
+ Name: metal-gpu-trace
3
+ Version: 0.4.0
4
+ Summary: Measure Wine and macOS programs' GPU use on Apple silicon with Metal System Trace, from the shell or an MCP client
5
+ Project-URL: Homepage, https://github.com/partprogramming09/metal-gpu-trace
6
+ Project-URL: Changelog, https://github.com/partprogramming09/metal-gpu-trace/blob/main/CHANGELOG.md
7
+ Project-URL: Issues, https://github.com/partprogramming09/metal-gpu-trace/issues
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: apple-silicon,gpu,instruments,mcp,mcp-server,metal,profiling,wine,xctrace
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: MacOS
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Topic :: Software Development :: Testing
18
+ Classifier: Topic :: System :: Benchmark
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+
22
+ # metal-gpu-trace
23
+
24
+ [![test](https://github.com/partprogramming09/metal-gpu-trace/actions/workflows/test.yml/badge.svg)](https://github.com/partprogramming09/metal-gpu-trace/actions/workflows/test.yml)
25
+
26
+ <!-- mcp-name: io.github.partprogramming09/metal-gpu-trace -->
27
+
28
+ Measure how a Wine or macOS program uses the GPU on an Apple silicon Mac, from the command line or through an AI
29
+ coding agent: GPU time per frame, frame pacing, FPS and memory, recorded with Instruments' Metal System Trace; whether
30
+ a translation layer (DXMT, Mullion, MoltenVK) splits the work into too many short encoders; Metal
31
+ `.gputrace` captures read without Xcode; and Apple's published facts on each GPU family to read the numbers against.
32
+ Python 3.9+ standard library, `zsh`, and Xcode (for `xctrace`). MIT license. [Español](es/README.md).
33
+
34
+ | File | What it does |
35
+ | --- | --- |
36
+ | `perf_record.sh` | Records a program for N seconds with Metal System Trace and writes `<label>_<date>.json` (GPU ms per frame, frame intervals, FPS, Metal and resident memory) plus per-frame and memory CSVs. Deletes the `.trace` afterwards unless `KEEP_TRACE=1`. |
37
+ | `perf_report.py` | Summarizes an existing `.trace` the same way. |
38
+ | `gputrace_read.py` | Reads a Metal `.gputrace` capture without Xcode: command buffers, encoders, draws, debug groups. Call names marked `probable` are not confirmed. |
39
+ | `data/apple-gpu-families.json` | Apple7 to Apple10: chips, what each family adds, limits that differ, with Apple's sources. |
40
+ | `metal_trace_mcp.py` | An MCP server over all of the above. |
41
+ | `test_metal_trace_mcp.py` | Self-check without Instruments: `python3 -I test_metal_trace_mcp.py` prints `ok`; `pytest` runs the same tests. |
42
+
43
+ ## Recording
44
+
45
+ ```sh
46
+ PERF_OUT=~/perf-runs zsh perf_record.sh game-d3d12 30 /path/to/wine ~/.wine /path/to/game game.exe
47
+ PERF_OUT=~/perf-runs zsh perf_record.sh app-metal 30 native - /path/to/app ./app
48
+ ```
49
+
50
+ Arguments: label, seconds, a Wine directory holding `bin/wine` (or `native`), the Wine prefix, the working directory,
51
+ the program and its arguments. Without `PERF_OUT`, runs go to `./perf-runs`. Reading the trace afterwards takes about
52
+ a minute on an M1 Pro (87 s for a 10 s recording); through the MCP server the whole call times out after 10 s per
53
+ recorded second plus 120 s, and an overrun recording leaves no files behind. One recording runs at a time; keep other GPU work off the Mac while it records.
54
+
55
+ Under Wine, give it 30 s: Wine's start-up takes part of the recording. A recording with under 100 frames is too short
56
+ to compare.
57
+
58
+ ## Disk use
59
+
60
+ Each recording keeps a JSON summary and three CSVs, about 2 MB for 10 s (most of it the Metal memory samples). The
61
+ `.trace` (tens to hundreds of MB) is deleted unless `KEEP_TRACE=1`. `KEEP_RUNS=N` keeps only the newest N recordings.
62
+
63
+ ## MCP server
64
+
65
+ ### Install
66
+
67
+ The server runs with [`uvx`](https://docs.astral.sh/uv/) straight from this repo, with nothing to clone
68
+ (`brew install uv` installs it). Recording also needs Xcode installed (`xcrun xctrace version` prints a version).
69
+
70
+ ```sh
71
+ uvx --from git+https://github.com/partprogramming09/metal-gpu-trace metal-gpu-trace
72
+ ```
73
+
74
+ This is the command each client below runs. Desktop apps do not see Homebrew's `PATH`: give them the full path that
75
+ `which uvx` prints (`/opt/homebrew/bin/uvx` on Apple silicon). `uvx` keeps the version it first downloaded; run
76
+ the command once with `--refresh` after `uvx` to get the latest.
77
+
78
+ The server reads these environment variables:
79
+
80
+ | Variable | What it sets |
81
+ | --- | --- |
82
+ | `METAL_TRACE_OUT` | Where recordings go. Give an absolute path: the default, `./perf-runs`, depends on the client's working directory. |
83
+ | `METAL_TRACE_WINE` | The Wine directory (holding `bin/wine`) that `perf_record` uses when the call names none. |
84
+ | `WINEPREFIX` | The Wine prefix used when the call names none; `~/.wine` otherwise. |
85
+ | `METAL_TRACE_READ` | More directories the tools may read, separated by `:`. |
86
+ | `METAL_TRACE_LOG` | `off` silences the one-line stderr log per call. |
87
+ | `KEEP_TRACE` | `1` keeps each recording's `.trace`, for `trace_tables`, `trace_query` or Instruments. |
88
+ | `KEEP_RUNS` | Keeps only the newest N recordings. |
89
+ | `PERF_ENV` | `NAME=VALUE` pairs, separated by spaces, set for the recorded program. |
90
+
91
+ ### Claude Code
92
+
93
+ ```sh
94
+ claude mcp add --scope user metal-trace -e METAL_TRACE_OUT=$HOME/perf-runs -e METAL_TRACE_WINE=/path/to/wine \
95
+ -- uvx --from git+https://github.com/partprogramming09/metal-gpu-trace metal-gpu-trace
96
+ claude mcp list
97
+ ```
98
+
99
+ `claude mcp list` shows `metal-trace` as connected. `--scope user` makes it available in every project; leave it out
100
+ to add it to the current project only.
101
+
102
+ ### Claude Desktop, Cursor and other JSON clients
103
+
104
+ These apps do not expand `~` or `$HOME`: write absolute paths.
105
+
106
+ ```json
107
+ {
108
+ "mcpServers": {
109
+ "metal-trace": {
110
+ "command": "/opt/homebrew/bin/uvx",
111
+ "args": ["--from", "git+https://github.com/partprogramming09/metal-gpu-trace", "metal-gpu-trace"],
112
+ "env": {
113
+ "METAL_TRACE_OUT": "/absolute/path/to/perf-runs",
114
+ "METAL_TRACE_WINE": "/absolute/path/to/wine"
115
+ }
116
+ }
117
+ }
118
+ }
119
+ ```
120
+
121
+ | Client | File |
122
+ | --- | --- |
123
+ | Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` (Settings, Developer, Edit Config); restart the app |
124
+ | Cursor | `~/.cursor/mcp.json` |
125
+ | VS Code | `.vscode/mcp.json` in the project; the top key is `servers` instead of `mcpServers`, and the server adds `"type": "stdio"` |
126
+
127
+ ### Codex CLI
128
+
129
+ In `~/.codex/config.toml`:
130
+
131
+ ```toml
132
+ [mcp_servers.metal-trace]
133
+ command = "/opt/homebrew/bin/uvx"
134
+ args = ["--from", "git+https://github.com/partprogramming09/metal-gpu-trace", "metal-gpu-trace"]
135
+ env = { METAL_TRACE_OUT = "/absolute/path/to/perf-runs", METAL_TRACE_WINE = "/absolute/path/to/wine" }
136
+ ```
137
+
138
+ ### Tools
139
+
140
+ | Tool | Reads or records |
141
+ | --- | --- |
142
+ | `perf_record` | Records a program and returns its summary; when it ends it stops every program of its Wine prefix, or every process named like a native program |
143
+ | `perf_attach` | Records a program that is already running, by pid or name, and leaves it running |
144
+ | `perf_runs` | Lists recordings, newest first, one page at a time, and whether each kept its `.trace` |
145
+ | `perf_compare` | Compares two recordings; flags those under 100 frames |
146
+ | `perf_diagnose` | Says whether a recording splits its GPU work into too many short encoders, as translation layers do, with encoders and command buffers per frame, the labels with the most GPU time and the GPU's idle time |
147
+ | `trace_tables` | Lists the tables of a kept `.trace`, named by its run or its path |
148
+ | `trace_query` | One page of rows of one table, optionally one process |
149
+ | `capture_summary` | Summarizes a `.gputrace`; lists its 100 most frequent calls out of `calls_total` |
150
+ | `capture_records` | One page of a `.gputrace`'s records |
151
+ | `gpu_family` | Apple's facts for a family or chip (`Apple7`, `M1 Pro`, `M3 Max`) |
152
+
153
+ The same facts are MCP resources: `metal-trace://gpu-families` (all) and `metal-trace://gpu-families/apple7` to
154
+ `apple10`. They restate Apple's [Metal Feature Set Tables](https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf)
155
+ of 21 May 2026, [`MTLGPUFamily`](https://developer.apple.com/documentation/metal/mtlgpufamily) and the
156
+ [M3 tech talk](https://developer.apple.com/videos/play/tech-talks/111375/); check a device with `supportsFamily`.
157
+
158
+ How it follows the [MCP specification](https://modelcontextprotocol.io/specification/2025-06-18) and
159
+ [Claude Code's limits](https://code.claude.com/docs/en/mcp):
160
+
161
+ - stdout carries only MCP messages; each tool call logs one line to stderr (`METAL_TRACE_LOG=off` silences it), with
162
+ no arguments or paths.
163
+ - Lists come in pages (`offset`, `limit`, default 50, at most 500) with `total` and `next_offset`; a result over
164
+ 40 000 characters is refused with a hint to ask for a smaller page.
165
+ - Files are read only inside `METAL_TRACE_OUT` and the directories in `METAL_TRACE_READ` (separated by `:`).
166
+ - Failures come back as `isError` results that say what to do, without stack traces or local paths. Summaries cut
167
+ absolute paths to file names, so they can be shared.
168
+ - Every `xctrace` call has a timeout.
169
+
170
+ ## Credits
171
+
172
+ - The record layout of a `.gputrace` command stream follows [tmc/gputrace](https://github.com/tmc/gputrace) (MIT); no
173
+ code is copied. The call ids were matched against Xcode's command list on macOS 27.0.1.
174
+ - GPU family facts: Apple's documents linked above, restated, not quoted.
@@ -0,0 +1,153 @@
1
+ # metal-gpu-trace
2
+
3
+ [![test](https://github.com/partprogramming09/metal-gpu-trace/actions/workflows/test.yml/badge.svg)](https://github.com/partprogramming09/metal-gpu-trace/actions/workflows/test.yml)
4
+
5
+ <!-- mcp-name: io.github.partprogramming09/metal-gpu-trace -->
6
+
7
+ Measure how a Wine or macOS program uses the GPU on an Apple silicon Mac, from the command line or through an AI
8
+ coding agent: GPU time per frame, frame pacing, FPS and memory, recorded with Instruments' Metal System Trace; whether
9
+ a translation layer (DXMT, Mullion, MoltenVK) splits the work into too many short encoders; Metal
10
+ `.gputrace` captures read without Xcode; and Apple's published facts on each GPU family to read the numbers against.
11
+ Python 3.9+ standard library, `zsh`, and Xcode (for `xctrace`). MIT license. [Español](es/README.md).
12
+
13
+ | File | What it does |
14
+ | --- | --- |
15
+ | `perf_record.sh` | Records a program for N seconds with Metal System Trace and writes `<label>_<date>.json` (GPU ms per frame, frame intervals, FPS, Metal and resident memory) plus per-frame and memory CSVs. Deletes the `.trace` afterwards unless `KEEP_TRACE=1`. |
16
+ | `perf_report.py` | Summarizes an existing `.trace` the same way. |
17
+ | `gputrace_read.py` | Reads a Metal `.gputrace` capture without Xcode: command buffers, encoders, draws, debug groups. Call names marked `probable` are not confirmed. |
18
+ | `data/apple-gpu-families.json` | Apple7 to Apple10: chips, what each family adds, limits that differ, with Apple's sources. |
19
+ | `metal_trace_mcp.py` | An MCP server over all of the above. |
20
+ | `test_metal_trace_mcp.py` | Self-check without Instruments: `python3 -I test_metal_trace_mcp.py` prints `ok`; `pytest` runs the same tests. |
21
+
22
+ ## Recording
23
+
24
+ ```sh
25
+ PERF_OUT=~/perf-runs zsh perf_record.sh game-d3d12 30 /path/to/wine ~/.wine /path/to/game game.exe
26
+ PERF_OUT=~/perf-runs zsh perf_record.sh app-metal 30 native - /path/to/app ./app
27
+ ```
28
+
29
+ Arguments: label, seconds, a Wine directory holding `bin/wine` (or `native`), the Wine prefix, the working directory,
30
+ the program and its arguments. Without `PERF_OUT`, runs go to `./perf-runs`. Reading the trace afterwards takes about
31
+ a minute on an M1 Pro (87 s for a 10 s recording); through the MCP server the whole call times out after 10 s per
32
+ recorded second plus 120 s, and an overrun recording leaves no files behind. One recording runs at a time; keep other GPU work off the Mac while it records.
33
+
34
+ Under Wine, give it 30 s: Wine's start-up takes part of the recording. A recording with under 100 frames is too short
35
+ to compare.
36
+
37
+ ## Disk use
38
+
39
+ Each recording keeps a JSON summary and three CSVs, about 2 MB for 10 s (most of it the Metal memory samples). The
40
+ `.trace` (tens to hundreds of MB) is deleted unless `KEEP_TRACE=1`. `KEEP_RUNS=N` keeps only the newest N recordings.
41
+
42
+ ## MCP server
43
+
44
+ ### Install
45
+
46
+ The server runs with [`uvx`](https://docs.astral.sh/uv/) straight from this repo, with nothing to clone
47
+ (`brew install uv` installs it). Recording also needs Xcode installed (`xcrun xctrace version` prints a version).
48
+
49
+ ```sh
50
+ uvx --from git+https://github.com/partprogramming09/metal-gpu-trace metal-gpu-trace
51
+ ```
52
+
53
+ This is the command each client below runs. Desktop apps do not see Homebrew's `PATH`: give them the full path that
54
+ `which uvx` prints (`/opt/homebrew/bin/uvx` on Apple silicon). `uvx` keeps the version it first downloaded; run
55
+ the command once with `--refresh` after `uvx` to get the latest.
56
+
57
+ The server reads these environment variables:
58
+
59
+ | Variable | What it sets |
60
+ | --- | --- |
61
+ | `METAL_TRACE_OUT` | Where recordings go. Give an absolute path: the default, `./perf-runs`, depends on the client's working directory. |
62
+ | `METAL_TRACE_WINE` | The Wine directory (holding `bin/wine`) that `perf_record` uses when the call names none. |
63
+ | `WINEPREFIX` | The Wine prefix used when the call names none; `~/.wine` otherwise. |
64
+ | `METAL_TRACE_READ` | More directories the tools may read, separated by `:`. |
65
+ | `METAL_TRACE_LOG` | `off` silences the one-line stderr log per call. |
66
+ | `KEEP_TRACE` | `1` keeps each recording's `.trace`, for `trace_tables`, `trace_query` or Instruments. |
67
+ | `KEEP_RUNS` | Keeps only the newest N recordings. |
68
+ | `PERF_ENV` | `NAME=VALUE` pairs, separated by spaces, set for the recorded program. |
69
+
70
+ ### Claude Code
71
+
72
+ ```sh
73
+ claude mcp add --scope user metal-trace -e METAL_TRACE_OUT=$HOME/perf-runs -e METAL_TRACE_WINE=/path/to/wine \
74
+ -- uvx --from git+https://github.com/partprogramming09/metal-gpu-trace metal-gpu-trace
75
+ claude mcp list
76
+ ```
77
+
78
+ `claude mcp list` shows `metal-trace` as connected. `--scope user` makes it available in every project; leave it out
79
+ to add it to the current project only.
80
+
81
+ ### Claude Desktop, Cursor and other JSON clients
82
+
83
+ These apps do not expand `~` or `$HOME`: write absolute paths.
84
+
85
+ ```json
86
+ {
87
+ "mcpServers": {
88
+ "metal-trace": {
89
+ "command": "/opt/homebrew/bin/uvx",
90
+ "args": ["--from", "git+https://github.com/partprogramming09/metal-gpu-trace", "metal-gpu-trace"],
91
+ "env": {
92
+ "METAL_TRACE_OUT": "/absolute/path/to/perf-runs",
93
+ "METAL_TRACE_WINE": "/absolute/path/to/wine"
94
+ }
95
+ }
96
+ }
97
+ }
98
+ ```
99
+
100
+ | Client | File |
101
+ | --- | --- |
102
+ | Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` (Settings, Developer, Edit Config); restart the app |
103
+ | Cursor | `~/.cursor/mcp.json` |
104
+ | VS Code | `.vscode/mcp.json` in the project; the top key is `servers` instead of `mcpServers`, and the server adds `"type": "stdio"` |
105
+
106
+ ### Codex CLI
107
+
108
+ In `~/.codex/config.toml`:
109
+
110
+ ```toml
111
+ [mcp_servers.metal-trace]
112
+ command = "/opt/homebrew/bin/uvx"
113
+ args = ["--from", "git+https://github.com/partprogramming09/metal-gpu-trace", "metal-gpu-trace"]
114
+ env = { METAL_TRACE_OUT = "/absolute/path/to/perf-runs", METAL_TRACE_WINE = "/absolute/path/to/wine" }
115
+ ```
116
+
117
+ ### Tools
118
+
119
+ | Tool | Reads or records |
120
+ | --- | --- |
121
+ | `perf_record` | Records a program and returns its summary; when it ends it stops every program of its Wine prefix, or every process named like a native program |
122
+ | `perf_attach` | Records a program that is already running, by pid or name, and leaves it running |
123
+ | `perf_runs` | Lists recordings, newest first, one page at a time, and whether each kept its `.trace` |
124
+ | `perf_compare` | Compares two recordings; flags those under 100 frames |
125
+ | `perf_diagnose` | Says whether a recording splits its GPU work into too many short encoders, as translation layers do, with encoders and command buffers per frame, the labels with the most GPU time and the GPU's idle time |
126
+ | `trace_tables` | Lists the tables of a kept `.trace`, named by its run or its path |
127
+ | `trace_query` | One page of rows of one table, optionally one process |
128
+ | `capture_summary` | Summarizes a `.gputrace`; lists its 100 most frequent calls out of `calls_total` |
129
+ | `capture_records` | One page of a `.gputrace`'s records |
130
+ | `gpu_family` | Apple's facts for a family or chip (`Apple7`, `M1 Pro`, `M3 Max`) |
131
+
132
+ The same facts are MCP resources: `metal-trace://gpu-families` (all) and `metal-trace://gpu-families/apple7` to
133
+ `apple10`. They restate Apple's [Metal Feature Set Tables](https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf)
134
+ of 21 May 2026, [`MTLGPUFamily`](https://developer.apple.com/documentation/metal/mtlgpufamily) and the
135
+ [M3 tech talk](https://developer.apple.com/videos/play/tech-talks/111375/); check a device with `supportsFamily`.
136
+
137
+ How it follows the [MCP specification](https://modelcontextprotocol.io/specification/2025-06-18) and
138
+ [Claude Code's limits](https://code.claude.com/docs/en/mcp):
139
+
140
+ - stdout carries only MCP messages; each tool call logs one line to stderr (`METAL_TRACE_LOG=off` silences it), with
141
+ no arguments or paths.
142
+ - Lists come in pages (`offset`, `limit`, default 50, at most 500) with `total` and `next_offset`; a result over
143
+ 40 000 characters is refused with a hint to ask for a smaller page.
144
+ - Files are read only inside `METAL_TRACE_OUT` and the directories in `METAL_TRACE_READ` (separated by `:`).
145
+ - Failures come back as `isError` results that say what to do, without stack traces or local paths. Summaries cut
146
+ absolute paths to file names, so they can be shared.
147
+ - Every `xctrace` call has a timeout.
148
+
149
+ ## Credits
150
+
151
+ - The record layout of a `.gputrace` command stream follows [tmc/gputrace](https://github.com/tmc/gputrace) (MIT); no
152
+ code is copied. The call ids were matched against Xcode's command list on macOS 27.0.1.
153
+ - GPU family facts: Apple's documents linked above, restated, not quoted.
@@ -0,0 +1,63 @@
1
+ {
2
+ "about": "Apple silicon GPU families Apple7 to Apple10: chips, first family with each feature, and limits that differ. Facts restated from Apple's public documents, not Apple's text. Check a device with MTLDevice.supportsFamily; families nest, so an Apple9 GPU also supports Apple7 and Apple8.",
3
+ "sources": {
4
+ "families": "https://developer.apple.com/documentation/metal/mtlgpufamily",
5
+ "feature_set_tables": "https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf",
6
+ "feature_set_tables_date": "2026-05-21",
7
+ "m3_tech_talk": "https://developer.apple.com/videos/play/tech-talks/111375/"
8
+ },
9
+ "guards": {
10
+ "M1 only": "Apple7 && !Apple8",
11
+ "before M3": "!Apple9"
12
+ },
13
+ "families": {
14
+ "Apple7": {
15
+ "mac_chips": ["M1", "M1 Pro", "M1 Max", "M1 Ultra"],
16
+ "other_chips": ["A14"],
17
+ "adds": ["mesh shading", "SIMD-scoped and quad-scoped reductions", "SIMD-scoped matrix multiply",
18
+ "floating-point atomics", "primitive ID", "barycentric coordinates", "border color",
19
+ "layered rendering to multisample textures", "MetalFX temporal upscaling",
20
+ "acquire/release memory ordering atomics", "Metal 4"],
21
+ "limits": {"mesh_grid_threadgroups": 1024, "argument_buffer_samplers_per_stage": 996,
22
+ "mesh_payload_and_geometry_per_draw": "up to 4 GB", "texture_2d_px": 16384}
23
+ },
24
+ "Apple8": {
25
+ "mac_chips": ["M2 series"],
26
+ "other_chips": ["A15", "A16"],
27
+ "adds": ["lossy texture compression", "SIMD shift and fill", "sparse depth/stencil on every device",
28
+ "placement sparse on every device", "query texture LOD on every device",
29
+ "64-bit atomic min/max on ulong in buffers and textures, macOS only"],
30
+ "limits": {"mesh_grid_threadgroups": 1024, "argument_buffer_samplers_per_stage": 996,
31
+ "mesh_payload_and_geometry_per_draw": "up to 4 GB", "texture_2d_px": 16384}
32
+ },
33
+ "Apple9": {
34
+ "mac_chips": ["M3 series", "M4 series"],
35
+ "other_chips": ["A17 Pro", "A18"],
36
+ "adds": ["full 64-bit atomics", "BC pixel formats on every device", "mesh draws in indirect command buffers",
37
+ "indirect mesh draw arguments", "ICB memory barriers for rendering",
38
+ "row-major matrices in acceleration structures", "per-component motion in ray tracing",
39
+ "MetalFX denoised upscaling", "Dynamic Caching (registers allocated as the program runs)"],
40
+ "limits": {"mesh_grid_threadgroups": 1048575, "argument_buffer_samplers_per_stage": 500000,
41
+ "mesh_payload_and_geometry_per_draw": "not stated", "texture_2d_px": 16384}
42
+ },
43
+ "Apple10": {
44
+ "mac_chips": ["M5 series"],
45
+ "other_chips": ["A19"],
46
+ "adds": ["depth bounds testing", "sampler min/max reduction", "sampler LOD bias",
47
+ "pre-raster per-vertex values", "ICB raster and depth/stencil state", "atomics on cube map textures",
48
+ "universal texture compression", "32 768 px textures", "16x16 tiles with 8x MSAA"],
49
+ "limits": {"mesh_grid_threadgroups": 4194303, "argument_buffer_samplers_per_stage": 500000,
50
+ "mesh_payload_and_geometry_per_draw": "not stated", "texture_2d_px": 32768}
51
+ }
52
+ },
53
+ "same_on_all": {"threads_per_threadgroup": 1024, "threadgroup_memory_bytes": 32768,
54
+ "mesh_pipeline_payload_bytes": 16384,
55
+ "argument_buffer_textures_or_buffers_per_stage": 1000000},
56
+ "footnotes": [
57
+ "64-bit atomics: none on Apple7; Apple8 has only ulong min/max, only on macOS; the full set starts with Apple9.",
58
+ "Mesh draws in indirect command buffers start with Apple9; earlier families refuse such mesh pipelines.",
59
+ "Inline constexpr samplers count toward the argument buffer sampler limit.",
60
+ "Mesh shaders on Apple7 and Apple8 can use up to 4 GB of payload and mesh geometry per draw.",
61
+ "Function pointers and ray tracing in render pipelines are not compatible with mesh shading."
62
+ ]
63
+ }