Skip to content

Install fails on Apple Silicon: npx breaks on sharp source build; pnpm can't resolve transitive mobilecli #353

Description

@deividasstr

Summary

On Apple Silicon macOS, neither documented-ish install path works out of the box — and they fail for different reasons:

  • npx / npm fails during install: a transitive sharp falls back to a source build that errors out, so the server never starts.
  • pnpm dlx installs and starts fine, but the server then throws mobilecli is not available or not working properly on the first device call, because under pnpm's strict node_modules the binary isn't where the resolver looks.

The net effect is that there's no clean install path on Apple Silicon: one tool breaks on sharp, the other on mobilecli.

Environment

@mobilenext/mobile-mcp 0.0.59
mobilecli (transitive, via mobilewright) 0.3.79
sharp (transitive) 0.34.5
OS macOS 26.4.1, Apple Silicon (arm64)
Node v24.8.0
npm 11.6.0
pnpm 10.15.1

Path A — npx / npm: sharp source build fails

$ npm install @mobilenext/mobile-mcp@latest
npm error code 1
npm error path .../node_modules/sharp
npm error command sh -c node install/check.js || npm run build
npm error > sharp@0.34.5 build
npm error sharp: Attempting to build from source via node-gyp
npm error sharp: See https://sharp.pixelplumbing.com/install#building-from-source
npm error sharp: Please add node-addon-api to your dependencies

The platform prebuilt that should avoid the source build is not installed:

$ ls node_modules/@img/
ls: node_modules/@img/: No such file or directory   # @img/sharp-darwin-arm64 missing
$ npm config get omit
                                                     # empty — optional deps are NOT opted out

So npm (11.6.0) does not install @img/sharp-darwin-arm64, sharp falls back to a node-gyp source build, and the whole install (and therefore npx -y @mobilenext/mobile-mcp@latest) hard-fails. This appears to be the well-known npm optional-dependencies resolution bug, but the result is that the documented npx install does not work on Apple Silicon.

Note: with --ignore-scripts the install completes and mobilecli is correctly hoisted to the top level — i.e. Path A's only blocker is sharp:

$ npm install --ignore-scripts @mobilenext/mobile-mcp@latest   # exit 0
$ ls node_modules/mobilecli/bin/mobilecli-darwin-arm64          # present ✓

Path B — pnpm dlx: mobilecli is not available

pnpm installs sharp from its prebuilt fine, so the server starts. But the first tool call fails:

mobilecli is not available or not working properly. Please review the documentation
at https://github.com/mobile-next/mobile-mcp/wiki for installation instructions

Root cause

mobilecli is not a direct dependency of @mobilenext/mobile-mcp — it's pulled in transitively via mobilewright. src/mobilecli.ts (getMobilecliPath) resolves the binary with a manual node_modules path join off __filename, not require.resolve:

// after the MOBILECLI_PATH env check:
const mobilecliPath = join(lastNodeModulesPath, "mobilecli", "bin", binaryName);
if (existsSync(mobilecliPath)) { return mobilecliPath; }
  • Under npm's flat/hoisted layout, the transitive mobilecli lands at the top-level node_modules/mobilecli, so the join resolves. ✅ (This is why most users — npx on Intel/Linux where sharp's prebuilt installs — never see this.)
  • Under pnpm's strict, symlinked layout, mobilecli lives only in the virtual store and is symlinked under mobilewright's subtree — never as a sibling of @mobilenext/mobile-mcp. The manual join (which doesn't follow pnpm's structure, unlike require.resolve) misses it, the version check throws, and the server reports "mobilecli is not available".

Verified the binary is present-but-unreachable in a pnpm dlx tree:

# .pnpm/mobilecli@0.3.79/.../bin/mobilecli-darwin-arm64  → exists and runs ('mobilecli version 0.3.79')
# .pnpm/@mobilenext+mobile-mcp@0.0.59/node_modules/      → mobilewright, zod, express… but NO mobilecli sibling

Why this is an Apple-Silicon trap

The two failures compound: on Apple Silicon you can't use npx (sharp source build fails), so you reach for pnpm — which then can't resolve mobilecli. There's no out-of-the-box combination that works.

Suggested fixes

  1. Declare mobilecli as a direct dependency of @mobilenext/mobile-mcp. Then pnpm symlinks it as a sibling and the existing path-join resolves (npm already hoists it). This is the smallest fix for Path B.
  2. Resolve via require.resolve("mobilecli/package.json") instead of hand-rolling the node_modules path — robust across npm/pnpm/yarn layouts (still needs (1) so it's resolvable under pnpm).
  3. Document MOBILECLI_PATH as a supported escape hatch in the README/wiki (it's honored first, but undocumented).
  4. Path A: ensure the @img/sharp-<platform> prebuilt installs on Node 24 / npm 11 (pin a sharp with working prebuilts, or add node-addon-api), or document that pnpm is required and why.

Current workaround

Install the mobilecli binary out-of-band and point MOBILECLI_PATH at it; the server then skips its broken lookup and works under pnpm dlx.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions