Apple Instruments Mcp — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Apple Instruments Mcp (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
apple-instruments-mcp is a Python MCP server for profiling Apple platform apps and processes with Instruments through xcrun xctrace. It lets MCP clients such as Claude Desktop record traces, inspect existing .trace bundles, and return concise performance reports with actionable recommendations.
The server can target iOS, iPadOS, macOS, tvOS, watchOS, visionOS, simulators, physical devices, and host processes when those targets are visible to the installed Xcode/Instruments toolchain. Actual template and device support is determined by xcrun xctrace list devices and xcrun xctrace list templates on your Mac.
xctrace from an MCP client..trace bundles without recording again.xctrace command with dry_run..trace and XML artifacts for later inspection.xcode-select --installxctrace: booted simulator, connected device, local macOS app, running process, or all-processes recording.Physical devices may require enabling UI automation in Settings > Developer.
The package is published on PyPI. The recommended way is to run it through uvx, which downloads and caches the server on demand without polluting your environment:
uvx apple-instruments-mcpAlternatively, install it globally with pipx or pip:
pipx install apple-instruments-mcp
# or
python -m pip install apple-instruments-mcpEither path provides the apple-instruments-mcp executable.
apple-instruments-mcp is a standard stdio MCP server, so any MCP-compatible client works — Claude Desktop, Claude Code, Codex CLI, Kiro, Cursor, Cline, Continue, Windsurf, Zed, or anything else that speaks MCP.
The simplest config uses uvx, so the client launches the server on demand with no separate install step. Example for Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"apple-instruments": {
"command": "uvx",
"args": ["apple-instruments-mcp"]
}
}
}To pin a specific version, replace the arg with [email protected].
For Claude Code:
claude mcp add apple-instruments -- uvx apple-instruments-mcpIf you prefer a global install (pipx or pip), point the client at the resulting binary instead:
{
"mcpServers": {
"apple-instruments": {
"command": "apple-instruments-mcp"
}
}
}Other clients (Codex CLI, Kiro, Cursor, Cline, Continue, Windsurf, Zed) accept the same command / args shape under their own MCP server config — check the client's docs for the exact file path. Restart the client after saving.
Run the xctrace doctor and tell me if recording is currently possible.
List the devices and Instruments templates available on this Mac.
Profile com.example.app on this simulator with the full preset for 15 seconds.
Record an App Launch trace for com.example.app on this iPhone simulator and show the startup offenders.
Launch /Applications/MyMacApp.app with Time Profiler and show CPU hot methods.
Time-profile com.example.app, then zoom into the 8000ms–12000ms window and flag main-thread stalls above 200ms.
Time-profile this release build and symbolicate raw addresses with ~/Build/MyApp.dSYM.
Attach Allocations to the running process named MyMacApp for 30 seconds.
Attach Time Profiler to PID 1234.
Record all processes with Time Profiler for 10 seconds.
Analyze network activity for com.example.app and flag slow requests.
Analyze the existing trace at ~/Desktop/launch.trace.
Compare ~/Desktop/baseline.trace and ~/Desktop/candidate.trace for launch regressions.| Tool | Instruments template | Description |
|---|---|---|
doctor | - | One-shot health check: xctrace version/path, device/template/instrument counts, and a problems section when probes or listings fail. Run this first if anything looks wedged. |
list_devices | - | Lists devices, simulators, runtimes, and host targets visible to xctrace. |
list_devices_structured | - | Returns the device/runtime listing as JSON. |
list_templates | - | Lists Instruments templates installed on this Mac. |
list_templates_structured | - | Returns the template listing as JSON. |
profile_preset | cpu / memory / network / full | Records a bundle of instruments in one trace, then reports each family that produced data. full covers CPU + Allocations + Leaks + Network in a single run. |
profile_ios_app | Selected by profile_type | Profiles an iOS, iPadOS, tvOS, watchOS, or visionOS app by bundle ID on a simulator/device. |
profile_mac_app | Selected by profile_type | Profiles a macOS .app or executable by launch path. |
profile_process | Selected by profile_type | Profiles a running process by process name or PID. |
profile_all_processes | Selected by profile_type | Profiles all processes on the host or selected device. |
analyze_launch | App Launch | Records and analyzes app startup time. |
analyze_launch_trace | App Launch | Analyzes an existing App Launch .trace. |
analyze_allocations | Allocations | Records memory allocations and reports live heap/VM totals plus top allocation categories. |
analyze_allocations_trace | Allocations | Analyzes an existing Allocations .trace. |
analyze_leaks | Leaks | Records a Leaks trace and reports exported leak details when available. |
analyze_leaks_trace | Leaks | Analyzes an existing Leaks .trace when leak details are exportable. |
analyze_time_profiler | Time Profiler | Records CPU samples and reports hot methods. Supports scope_start_ms/scope_end_ms windowing, main-thread hang detection, user-binary filtering, and dsym_path for symbolicating release-build frames. |
analyze_time_profiler_trace | Time Profiler | Analyzes an existing Time Profiler .trace. Same scope/hang/user-binary/dSYM options as analyze_time_profiler. |
analyze_network | Network | Records network requests, latency, transfer sizes, and status codes. |
analyze_network_trace | Network | Analyzes an existing Network .trace. |
compare_launch_traces | App Launch | Compares two App Launch traces and reports startup deltas. |
compare_memory_traces | Allocations | Compares two Allocations traces and reports memory deltas. |
compare_cpu_traces | Time Profiler | Compares two Time Profiler traces and reports CPU deltas. |
build_xctrace_command | Any | Returns the exact xcrun xctrace record command for a target without executing it. |
Recording tools accept exactly one target mode:
bundle_id: app bundle identifier, for example com.example.app. Use with device_id for iOS, iPadOS, tvOS, watchOS, and visionOS simulator/device launches.launch_path: executable path or .app path. Use this for macOS apps and command-line tools.process_name: attach to a running process by name.pid: attach to a running process by process ID.all_processes: record all processes when the selected template supports it.Additional recording arguments:
device_id: optional simulator UUID or physical device identifier from list_devices. Omit it for host macOS profiling.launch_args: optional shell-style arguments passed after launch_path.time_limit_seconds: recording duration, from 5 to 120 seconds. Defaults to 20.dry_run: return the generated xctrace command without recording.keep_trace: keep generated .trace and XML artifacts, and include their paths in the report.output_dir: optional parent directory for generated artifacts. Each run creates a dedicated subdirectory inside it.Examples:
{ "bundle_id": "com.example.app", "device_id": "SIMULATOR-UUID" }
{ "launch_path": "/Applications/MyMacApp.app" }
{ "launch_path": "/usr/bin/python3", "launch_args": "--version" }
{ "process_name": "MyMacApp" }
{ "pid": 1234 }
{ "all_processes": true }
{ "launch_path": "/Applications/MyMacApp.app", "dry_run": true }
{ "bundle_id": "com.example.app", "device_id": "SIMULATOR-UUID", "keep_trace": true, "output_dir": "~/Desktop/traces" }The profile_* tools are higher-level wrappers around the generic analyze_* tools. Prefer them when you know the target shape:
{ "profile_type": "launch", "bundle_id": "com.example.app", "device_id": "SIMULATOR-UUID" }
{ "profile_type": "time_profiler", "launch_path": "/Applications/MyMacApp.app" }
{ "profile_type": "allocations", "process_name": "MyMacApp" }
{ "profile_type": "time_profiler", "pid": 1234 }
{ "profile_type": "time_profiler" }Supported profile_type values are launch, allocations, leaks, time_profiler, and network.
The generic analyze_* and analyze_*_trace tools expose parser-specific thresholds. Use these when the defaults are too strict or too loose for a device class, platform, workload, or CI budget.
Launch:
launch_good_mslaunch_critical_msoffender_warning_msoffender_critical_msAllocations:
memory_warning_mbmemory_critical_mbmemory_cache_warning_mbLeaks:
leak_critical_countTime Profiler:
total_good_mstotal_critical_msmethod_warning_msmethod_critical_msNetwork:
request_warning_msrequest_critical_msslow_request_critical_counttransfer_warning_mbExample:
{
"bundle_id": "com.example.app",
"device_id": "SIMULATOR-UUID",
"launch_good_ms": 700,
"launch_critical_ms": 1500,
"offender_warning_ms": 150,
"offender_critical_ms": 500
}Trace analysis tools accept:
trace_path: absolute path to an existing .trace bundle.bundle_id: optional target name used in the generated report.Trace comparison tools accept:
baseline_trace_path: absolute path to the baseline .trace bundle.candidate_trace_path: absolute path to the candidate .trace bundle.bundle_id: optional target name used in the generated report.Example:
{
"baseline_trace_path": "~/Desktop/baseline.trace",
"candidate_trace_path": "~/Desktop/candidate.trace",
"bundle_id": "com.example.app"
}# App Launch Analysis - com.example.app
Warning: Launch time 1240ms - above 400ms. Users may notice the delay.
**Total:** 1240ms
## Phases
- **pre-main (dyld + static init):** 434ms (35%)
- **post-main (AppDelegate + UI):** 806ms (65%)
## Top Offenders
Critical: `-[DatabaseManager setup]` [post-main]
Self: **540ms** | Total: 540ms | 43%
Suggestion: Move database initialization to a background queue or use lazy loading.
Warning: `+[AnalyticsSDK configure:]` [post-main]
Self: **210ms** | Total: 210ms | 17%
Suggestion: Defer analytics SDK initialization after first frame is rendered.
## Recommendations
- Fix 1 critical offender(s) - each adds 300ms+ to launch.
- Target: total launch under 400ms..trace bundles created by recording tools are cleaned up after each run.xcrun xctrace export.Analysis Quality section when the exported XML is empty or no recognizable data was found for the selected parser. Export failures (for example, an unsupported xpath) are reported under Export Warning._trace tools to avoid recording again.xcrun xctrace record --output paths to use the .trace extension; .xctrace may be rejected before recording starts.Each parser is wired to a specific xctrace export --xpath query. The tooling has been validated end-to-end against real .trace bundles for the ✅ rows.
| Template | xpath / schema | Status |
|---|---|---|
| Time Profiler | time-profile | ✅ validated against real traces |
| App Launch | time-profile (CPU samples, idle leaves filtered) | ✅ validated against real traces |
| Allocations | tracks/track[@name="Allocations"]/details/detail[@name="Statistics"] | ✅ validated against real traces |
| Leaks | tracks/track[@name="Leaks"]/details/detail[@name="Leaks"] | ✅ validated against real traces |
| Network | network-connection-detected (socket-level fallback) | ✅ validated against a real physical-device trace; HTTP/HAR rows may be absent if Instruments does not export URLSession task details |
The App Launch parser uses the same time-profile schema as Time Profiler and classifies frames into pre-main (dyld / static init / Swift runtime) vs post-main (AppDelegate / UIApplication / Cocoa methods) by symbol heuristic. Two things to keep in mind:
total_launch_ms is CPU-active launch time, not wall-clock. xctrace does not expose a clean "app became active" marker we can read.mach_msg2_trap, start_wqthread, semaphore_wait_trap, etc.) are dropped, matching Instruments' default "Hide System Libraries" behaviour. Without this, a long recording that idles after launch would dominate the totals with worker threads parked in wait.Recording tools run a few pre-flight checks before invoking xctrace. A target is rejected up front when launch_path does not exist on this Mac, pid is not a positive integer or no longer running, or bundle_id contains characters Instruments does not accept. When xctrace itself fails, the error message is classified per target (bundle not installed, dead process, unresolved device, invalid launch_path, missing Xcode tools) and the report includes a fix hint scoped to that target instead of a generic checklist.
If recording fails, check these first:
xcrun xctrace list devices.process_name or pid.xcode-select -p.list_templates.Use build_xctrace_command to inspect the exact command that would run, without recording. The returned command also surfaces any pre-flight warnings detected against the chosen target.
You can also verify xctrace directly:
xcrun xctrace list devices
xcrun xctrace list templatesClone the repository and install the editable build with the dev extra:
git clone [email protected]:dogo/apple-instruments-mcp.git
cd apple-instruments-mcp
python3 -m venv .venv
. .venv/bin/activate
python -m pip install -e ".[dev]"Run linters, type check, and the test suite:
ruff check src tests
pyright
PYTHONPATH=src python -m unittest discover -s tests -vThe package ships a py.typed marker so downstream consumers can type-check against its public API. A uv.lock is committed for reproducible dependency installs with uv; CI runs the same checks across Python 3.11–3.13 via .github/workflows/ci.yml.
Run the MCP server locally:
python -m apple_instruments_mcpReleases are published to PyPI through GitHub Actions trusted publishing — no API tokens stored in the repo. Steps:
version in pyproject.toml, commit, and push to main.X.Y.Z (no v prefix) matching that version and push the tag.Release workflow builds sdist + wheel, verifies the tag matches pyproject.toml, and uploads to PyPI via OIDC.One-time PyPI setup: on https://pypi.org, register the project name and add a Trusted Publisher pointing at this repository, the release.yml workflow, and the pypi environment.
MIT
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.