macos-mcp · the ios toolset · answered from Apple's published specs, 14 September 2026

Will it drive the new iPhone?

Apple announced iPhone 18 Pro and 18 Pro Max on 9 September 2026, on sale 18 September, and its first foldable — iPhone Duo — on sale 23 October. Each one gets a different answer here, and all three come out of Apple's own published display specifications and the shipped code rather than out of an opinion: one fits the profile table exactly, one now has a row of its own, and one is refused unless you tell the toolset its size.

On this page
  1. Will macos-mcp work with the new iPhone?
  2. Why a phone the table has never seen still works
  3. What each device_match.quality value means
  4. What you cannot do: pin your way out
  5. The foldable: iPhone Duo needs one argument, and refuses without it
  6. How to help
  7. Next
  8. Questions

Requires iPhone Mirroring, whatever the phone is: iOS 18+, the same Apple Account on both devices, the handset locked and nearby. Apple does not offer iPhone Mirroring in the EU.

8 iphone_* tools12 device profiles18 Pro: the 16 Pro's panelDuo: needs device_size0 false successes

Will macos-mcp work with the new iPhone?

Yes for iPhone 18 Pro and 18 Pro Max, and the reason is better than the general one. Apple publishes 2622×1206 at 460 ppi for the 18 Pro and 2868×1320 at 460 ppi for the 18 Pro Max. Those are, pixel for pixel, the panels the iPhone 16 Pro and 16 Pro Max carry — and those two are the newest rows in the shipped table, at 402×874 and 440×956 points. The worry this page was written around, that the table is a generation behind, does not bite on the Pro line: Apple has not moved the Pro panel since the 16 Pro, and iPhone 17 and 17 Pro ship the same 2622×1206 too. The label on the row is a generation old. The shape in it is current.

What that is, precisely, is an inference — and the table does not accept inferences. A point size is pixels divided by a scale factor, and Apple publishes the pixels and the density, never the scale factor. Identical pixels at an identical density is a strong signal that the existing row already fits, and it is still not a measurement, so no row is renamed or added on the strength of it. That rule is why this page can be trusted about the two cases below, where the arithmetic does settle the matter.

Apple's published displayAspect from those pixelsWhat the shipped code does with it
iPhone 18 Pro — 2622×1206, 460 ppi2.17413Exactly the iphone-16-pro row (402×874 pt, 2.17413). Should report matched.
iPhone 18 Pro Max — 2868×1320, 460 ppi2.17273Exactly iphone-16-pro-max (440×956 pt, 2.17273). Should report matched.
iPhone 17, 17 Pro — 2622×1206, 460 ppi2.17413The same row again. The 17 line was never outside the table's shapes.
iPhone Air — 2736×1260, 460 ppi2.17143Now its own row, iphone-air at 420×912 — added 14 September 2026. Before it, the Air sat 0.0013 from iphone-16-pro-max against a 0.01 tolerance and so reported matched under that larger phone's name, every point 4.8% out.
iPhone Duo, open — 1878×2670, 430 ppi1.42173Below the 1.5 floor → refused as content_unreadable, unless you pass a device_size that agrees with the shape.
iPhone Duo, closed — 1398×2034, 460 ppi1.45494Also below the floor, and the same argument answers it.

Aspect ratios here are computed from Apple's published pixel counts, which needs no assumption about scale factor — that is why the iPhone Duo rows are a conclusion and the point sizes beside the Pro rows are still an inference awaiting a measurement.

iPhone Air was the one to fix, because it failed worse than a missing row does. A phone with no row at all reports quality: "nearest" with an empty candidate list, which is the toolset saying "nothing fits" out loud. The Air instead landed inside the tolerance of a row belonging to a physically larger phone, so it came back matched — indistinguishable on the wire from a genuine fit — while every point it reported was 4.8% out in both axes. Taps still landed, because the mapping is proportional and iphone_find and iphone_tap run through the same content rect; what was wrong was the number in the transcript.

It is fixed, and the fix is labelled. iphone-air is in the table at 420×912 points — and it is the one row here that was derived rather than measured, which the source says on the row itself. The split is worth keeping straight: the shape is arithmetic, 2736÷1260 exactly, and shape is the only thing the matcher reads; the absolute point size divides those pixels by a scale factor of 3, which every measured 460 ppi row in the table also does but which nobody here has confirmed on an Air. A derived row that says it is derived beats a wrong row wearing another phone's name. The first real iphone_status from an Air promotes it — and if that read-back disagrees, the read-back wins.

And there is now a user-side remedy that needs no row at all. Every iphone_* tool takes an optional device_size — your phone's own logical size in points — which reports quality: "supplied". It is not a pin: the shape is still checked against the window, and deviation tells you whether the size you gave matches what is on screen. See below.

Why a phone the table has never seen still works

1. The table stops at the iPhone 16 Pro — and so, it turns out, did Apple

DeviceProfile.known is twelve entries, keyed by portrait point size and named for the models sharing it — 320×568 for the iPhone SE (1st gen) and 5s, 375×812 for the X, XS, 11 Pro and the minis, 393×852 for the 14 Pro, 15, 15 Pro and 16, up to 402×874 for the iPhone 16 Pro, 440×956 for the 16 Pro Max and, since 14 September 2026, 420×912 for the iPhone Air. No row says 17 or 18 on it, and that is not the omission it looks like.

Which mattered less than it looked. Three generations of Pro have shipped the same 6.3-inch panel — 2622×1206 at 460 ppi on the 16 Pro, the 17 Pro and now the 18 Pro — and the 18 Pro Max repeats the 16 Pro Max's 2868×1320, so a row measured off an iPhone 16 Pro describes the shape of an 18 Pro as well. Entries are keyed by size and named for the models that share it, exactly so a new phone with an old shape needs no code; that design decision, made before any of these phones existed, is the reason this page can say yes. What the missing name costs is that device_match reports an older model's label, which is a labelling problem and not a targeting one.

The rule about spec sheets now has exactly one exception, and it is marked as one in the source rather than quietly taken. Eleven of the twelve rows were read back off a real window. iphone-air was derived from Apple's published 2736×1260, because leaving it out was the worse error: without a row an Air reports matched under the iPhone 16 Pro Max's name, and a wrong answer that looks right beats no answer only in the sense that it is harder to notice. The row carries its own provenance comment, the derived half is named (the scale factor, not the shape), and a real iphone_status from an Air promotes it or corrects it.

2. Absent from the table is not absent from the tools

Coordinates are device points, and PhoneGeometry turns one into a place on your Mac's screen by proportion: divide by the profile's logical size, multiply by the observed content rect. Both directions run through the same contentRect, so a device_center that iphone_find just reported maps back to the pixel it was read from. A profile that is the wrong shape by two percent moves every coordinate by the same two percent, and the source states the consequence outright: a wrong-by-2% profile still maps taps to the correct control, because the mapping is proportional. The missing row is a labelling problem, not a functional one.

3. The quality of the match travels with the match

A shape is compared against the table within a 1% tolerance, and the answer carries how good it is. matched means at least one profile fits; nearest means none does and the profile reported is only the closest; pinned means you passed device, so the shape was never consulted. In the nearest case device_candidates comes back empty on purpose — that emptiness is the finding, so a guess never arrives looking like a fit. Older code put the nearest profile in that list, making a guess and a fit indistinguishable on the wire; a real mirrored window forced the split, at 369×807 and aspect 2.187 with nothing inside the tolerance and iPhone 16 Pro, 2.1741, sitting 0.0129 away. The repo does not name that handset's model, so neither does this page.

That measurement is also the reason this page will not promise matched for an iPhone 18 Pro, even though its panel is the row: 2.187 observed against 2.1741 true is a 0.6% detection error, and the tolerance is 1%, so which of the two answers comes back depends on how cleanly the content rect was detected in that session — the window's shape, not the phone's identity, is what the code can see. Either answer is honest. nearest with an empty candidate list is the toolset telling you it could not place the shape, and it is the state to expect on any handset whose proportions the table does not hold — at which point device_size is how you supply the size the table is missing. Verified, not claimed.

4. The shape gate is deliberately generous

DeviceProfile.isPlausiblePhoneShape admits aspect ratios from 1.5 to 2.6, and the comment above it says why: the band is generous "so an unreleased phone is never refused for being new. It exists to catch shapes that are not phones, not to police the ones that are." The table's own profiles cluster near 1.78 and 2.16–2.17, so a conventional new iPhone sits inside it comfortably. The gate followed a measured defect: on a dark Settings page the detector found only the status bar and reported a 337×63.5 region at aspect 5.31, so an iphone_key back swipe built on that band landed in a settings row and the change check called it verified. That shape is refused as content_unreadable now, before it becomes a coordinate space. No false successes.

What each device_match.quality value means

qualityWhat was observeddevice_candidatesWhat to do
matchedAt least one profile fits the shape within tolerance.Every profile that fits, closest first.Nothing — though the name may be an older model sharing that shape.
nearestNothing fits; the profile reported is the closest shape, deviation away.Empty — the finding itself.Keep going: taps land correctly. Nothing makes the point size exact until a profile exists.
pinnedYou passed device, so shape was never consulted; deviation is zero.The one id you pinned.Read aspect_ratio to check the pin is sane — nothing else did.
suppliedYou passed device_size. The size is yours; the shape is still consulted, and deviation is the distance between them.Empty — no table profile is in play.Nothing while approximate is false. When it turns true, your size and the window disagree: wrong phone, or a window resized to a shape the phone does not have.

What you cannot do: pin your way out

Every iphone_* tool takes an optional device argument, and it is worth being exact about what it fixes. IOSToolset.profile resolves the id through DeviceProfile.known(id:) and throws invalid_argument for anything else, listing the twelve ids it accepts. The argument takes only a profile already in the table. A phone the table does not know has no id, so there is nothing to pin, and pinning a neighbour hands you the neighbour's size — which is the thing you were trying to correct.

So the remedy is a size, not an id. Since 14 September 2026 every iphone_* tool also takes device_size: the phone's own logical size in points, which needs no row to exist and no id to name it. Points, not pixels — divide a published pixel count by the display scale, 3 on every current iPhone. It reports quality: "supplied" with device: "custom", and the two arguments are mutually exclusive: passing both is a typed invalid_argument rather than a silent precedence rule, because which one won would not be visible in the result.

iphone_status {}
→ phone.device_match: { quality: "nearest", approximate: true,
                        aspect_ratio: 2.187, deviation: 0.0129,
                        tolerance: 0.01, candidate_count: 0 }
  phone.device_candidates: []

# An id you do not have. There is no row to name, so there is nothing to pin.
iphone_tap { "x": 52, "y": 566, "device": "iphone-fold-2029" }
→ invalid_argument: param "device",
  expected "one of: iphone-se-1, iphone-se, … iphone-air"

# The size you do have. No row required.
iphone_status { "device_size": { "width": 420, "height": 912 } }
→ phone.device: "custom"
  phone.device_match: { quality: "supplied", approximate: false,
                        aspect_ratio: 2.1714, deviation: 0.0,
                        tolerance: 0.01, candidate_count: 0 }

# Both at once is refused, not ranked.
iphone_tap { "x": 52, "y": 566, "device": "iphone-16-pro",
             "device_size": { "width": 420, "height": 912 } }
→ invalid_argument: param "device_size"

Pinning a neighbouring id is still worse than leaving it alone: a pinned call never consults the shape, so it returns quality: "pinned", approximate: false, deviation: 0 — exactness the answer does not have. It trades an approximation that admits it is one for an approximation that does not.

That is precisely what device_size does differently, and it is the reason it is a separate quality rather than another way to pin. A supplied size is checked against the window: deviation is the distance between the shape you claimed and the shape on screen, and when it exceeds the tolerance the result comes back approximate: true with a detail naming the two likely causes — that size is not this phone's, or the Mirroring window is showing a shape the phone does not have, which macOS 27 now allows. A caller can be wrong about which phone they are holding, and this is the only place that would ever notice.

The foldable: iPhone Duo needs one argument, and refuses without it

Apple announced its first foldable iPhone on 9 September 2026 — iPhone Duo, a 7.6-inch inner display and a 5.4-inch cover display, on sale 23 October. The published pixel counts settle what the shipped code does with it: on its own it refuses, open and closed; told the size, it works.

Open: 1878×2670, an aspect ratio of 1.422. Closed: 1398×2034, an aspect of 1.455. DeviceProfile.isPlausiblePhoneShape admits 1.5 to 2.6, so both fall below the floor — not near it, below it — and MirroringSession answers content_unreadable, naming the region and aspect it measured, rather than building a device space on a shape it does not recognise as a phone. An aspect ratio computed from pixel counts carries no assumption about scale factor, so unlike the point sizes above, this one is arithmetic rather than inference.

That is the gate doing exactly what it was added to do. It exists because a dark Settings page once detected as a 337×63.5 sliver at aspect 5.31 and everything downstream built a coordinate space on it, so iphone_key back swiped into a settings row and the frame-change check called it verified. The band is a sanity check on the detector, not a compatibility list — but a device Apple had not shipped when it was written is exactly the shape it cannot tell apart from a misdetection.

So it now has one way to tell them apart, added 14 September 2026: your own device_size, when it agrees with the shape on screen. Supply 626×890 and a 1.42 window stops being an implausible shape and starts being the phone you said you were holding. Agreement is the entire condition, and it is what makes this narrow rather than a hole: the 2026-09-08 sliver is still refused with a phone-sized size supplied, because 5.31 comes nowhere near 2.17 — that incident is a test in the suite, and it runs before the feature's own tests do. A size inside the normal band can never unlock anything at all: the nearest edge of 1.5–2.6 is further from an ordinary phone than the agreement tolerance, so the relaxation is unreachable unless you say, in numbers, that your device is unusual. And when it does fire, the result carries a shape_vouched object saying the gate was passed on your word rather than on the shape.

What is still not claimed: nobody here has held an iPhone Duo, so 626×890 is this page's arithmetic on Apple's published pixels — a starting point for device_size, not a profile, and there is no iphone-duo row. Telling the two folded states apart is the caller's job, because from outside the window they are two different shapes and nothing reports which one is which. Whether iPhone Mirroring supports the Duo at all is Apple's question, and this page will not answer it on Apple's behalf.

How to help

A profile is an id, a display name and a portrait size in points, and two specific read-backs would improve this page. An iphone_status from an iPhone Air would promote iphone-air from derived to measured — or correct it, which is just as useful. One from an iPhone 18 Pro would do the same for the inference at the top of this page. Open an issue with the numbers and the iphone_status output behind them at github.com/Ark0N/macos-mcp/issues. A measurement always outranks a derivation here, including the one row that is currently the latter.

Next

The ios toolset is the whole picture: eight tools, OCR as the primary locator because the mirrored window has no accessibility tree, every action read back, never assumed, off by default. iOS 27 and agents covers the software side — including the one thing macOS 27 changed about the Mirroring window that this page's shape matching depends on — and macOS 27 Golden Gate the platform side. macos-mcp is 44 tools, 24 on by default; signed, notarized, audited; MIT, no telemetry.

Questions

Will macos-mcp work with the iPhone 18 Pro?

Yes, and better than the general answer. Apple published 2622-by-1206 pixels at 460 ppi for iPhone 18 Pro and 2868-by-1320 at 460 ppi for iPhone 18 Pro Max — pixel-for-pixel the panels the iPhone 16 Pro and 16 Pro Max carry, which are the two newest rows in the shipped profile table at 402x874 and 440x956 points. Apple has not changed Pro display geometry since the 16 Pro, so the shape the table holds is the shape the new phones have, and a match should report matched rather than nearest. That is an inference from Apple's published pixels, not a measurement: Apple publishes pixels and pixel density, not the scale factor a point size is derived from, and nothing is promoted in the table until somebody reads it back off a real mirrored window.

Does macos-mcp have a device profile named for the iPhone 17 or 18?

No row carries those names, and for the Pro models it does not matter: iPhone 17, 17 Pro and 18 Pro all ship the 2622-by-1206 panel the iPhone 16 Pro row was measured from, and iPhone 18 Pro Max ships the 16 Pro Max's 2868-by-1320. The shape fits; the label is a generation old. The handset that had no row was iPhone Air at 2736-by-1260, and its case was worse than a missing row: its shape sits 0.0013 from the iPhone 16 Pro Max row against a 0.01 tolerance, so it reported matched under that name with device points about 4.8% larger than the phone's own. It has its own row as of 14 September 2026 — iphone-air, 420x912 — and that row is the one entry in the table derived from Apple's published pixels rather than read back off a real window, which the source says on the row itself. Taps landed correctly throughout, because the mapping is proportional and both directions run through the same content rect; what was wrong was the number reported.

What does device_match.quality: nearest mean?

It means no profile in the shipped table fits the window's shape within the 1% tolerance, and the profile being reported is only the closest one. device_candidates is empty in that case, deliberately, so a guess never looks like a fit. It is not a failure: taps still land correctly because the mapping is proportional, and deviation against tolerance tells you how far off the reported point size is. It is measured behaviour, not theory — a real 369x807 mirrored window came back at aspect 2.187 with nothing inside the tolerance, iPhone 16 Pro at 2.1741 sitting 0.0129 away. The field has four values: matched, nearest, pinned when you passed device, and supplied when you passed device_size — the last one being the only one that both takes your word for the size and still reports how far that size is from the shape on screen.

What do I do if my iPhone has no profile in the table?

Pass device_size instead of device. The device argument takes only an id that already exists — IOSToolset.profile resolves it through DeviceProfile.known(id:) and throws invalid_argument otherwise, listing the twelve ids it accepts — so a phone with no row has nothing to pin, and pinning a neighbour hands you the neighbour's size, which is the thing you were correcting. device_size takes the phone's own logical size in points, needs no row, and reports quality: supplied with device: custom. It differs from a pin in the way that matters: a pin tells the code not to consult the window's shape at all, while a supplied size is still checked against it, so deviation tells you whether the size you gave matches what is on screen and the result turns approximate when it does not. The two are mutually exclusive — passing both is a typed invalid_argument, not a precedence rule.

Do taps land in the right place if the device profile is wrong?

Yes, within the accuracy of the detected content rect. PhoneGeometry divides a device point by the profile's logical size and multiplies by the observed content rect, and both directions — OCR pixels in, screen points out — go through the same contentRect, so a device_center that iphone_find just reported maps back to the pixel it was read from. A profile that is the wrong shape by two percent moves every coordinate by the same two percent. The source says it directly: a wrong-by-2% profile still maps taps to the correct control, because the mapping is proportional.

Will macos-mcp drive the foldable iPhone Duo?

With one argument, yes; without it, no — and the arithmetic says why before any hardware arrives. Apple published 1878-by-2670 pixels for the 7.6-inch inner display and 1398-by-2034 for the 5.4-inch cover display, which are aspect ratios of 1.422 and 1.455, and an aspect from pixels needs no assumption about scale factor. DeviceProfile.isPlausiblePhoneShape admits 1.5 to 2.6, so both displays fall below the floor and the toolset refuses with content_unreadable rather than handing out a coordinate it does not trust. Passing device_size changes that: a shape outside the band is accepted when the size you supplied agrees with it, which is the discriminator the band alone lacks — a misdetected region does not match a size you measured. The measured case that produced the gate, a dark screen detected at aspect 5.31, is still refused with a phone-sized device_size supplied, and that is a test rather than a claim. There is no iphone-duo profile: nobody here has held one, so 626x890 is arithmetic on Apple's published pixels and belongs in device_size, not in the table. Telling open from closed is the caller's job, because from outside the window they are simply two different shapes.

Can I control a new iPhone from my Mac on launch day?

If iPhone Mirroring itself works with it, yes. macos-mcp drives the handset through Apple's own iPhone Mirroring window, so the requirements are Apple's: iOS 18 or later, the same Apple Account on the iPhone and the Mac, the phone locked and nearby, and macOS 15 or later on Apple Silicon. Apple does not offer iPhone Mirroring in the EU, and nothing here works around that. The ios toolset is off by default; an agent has to call enable_toolset ios first.