Releases: sweetrb/apple-numbers-mcp
Release list
v1.1.19
Security
-
export-table,create-spreadsheetandimport-csvwould read and write ANY absolute path the caller named — this server had no filesystem boundary at all. It was the outlier of the four Apple MCP servers; mail, notes and photos each have one.resolvePath()expanded~and calledresolve()and did nothing else, andvalidateOutputPath()checked only for a.numbersextension, so anoutputPathof/Library/LaunchAgents/com.evil.plist,/etc/cron.d/x, or a path inside an app bundle was written unconditionally, andimport-csv'sinputPathcould read any file on disk. Those paths reach the tools from the model, so a prompt-injected or simply confused agent had a write-anywhere primitive. All three now resolve through a new allowlist (src/utils/exportPath.ts, mirroring apple-photos-mcp's module of the same name): the path must resolve — after~expansion and symlink resolution — to a location under the home directory,/tmp,/private/tmp, or/Volumes, and anything else is rejected with an error naming the resolved path and those roots. Symlinks are resolved before the check even when the target does not exist yet (the deepest existing ancestor is canonicalized and the not-yet-created remainder re-appended), so a link under/tmppointing at/etcis refused; and the.numbersextension check now runs on the canonical path, so a symlink cannot present a.numbersname for something that isn't one. Two details are copied deliberately from apple-mail-mcp's implementation: canonicalization usesfs.realpathSync.nativerather than the JS emulation — which resolves symlinks but preserves the caller's casing, so on case-insensitive APFS one spelling of a path compares differently from another spelling of the same file — and membership is a path-segment test (candidate === root || candidate.startsWith(root + sep)) rather than a barestartsWith, so/Volumes-eviland<home>-evilcannot ride in on a shared prefix. Overwrite semantics are unchanged: within the allowed roots an existing file at the target path is still overwritten, as all three tools have always documented — the path is caller-supplied rather than attacker-named, so this adds the root boundary only. The guard was verified by breaking it three ways and watching the new tests fail: removing the check fails 21 of 67, swapping the native realpath for the JS one fails 1, and replacing the segment test with a barestartsWithfails 5. Verified against the real numbers-parser sidecar as well as in unit tests — a real CSV imports and a real.numberstable exports under/tmp, an export re-run over an existing file still overwrites it, and writes to/etc,/Applicationsand a/tmpsymlink pointing at/private/etcare all refused with nothing created. -
The boundary resolves a dangling symlink rather than walking past it.
existsSync
follows links, so a broken link read as "not created yet", was re-appended verbatim and
never canonicalized — and the sidecar then followed it on write. Presence is now tested
withlstat, and a link whose target does not exist yet is resolved by hand so the check
runs against the location the write would actually create. -
os.tmpdir()(/var/folders/<hash>/T) is an allowed root. It is what Node, Python's
tempfileand$TMPDIRall return on macOS — and what this repo's own fixtures use — so
omitting it refused the most ordinary scratch destination there is.
v1.1.18
Documentation
- Retagged the project-scope
.mcp.jsonentrypoint excerpt fromjson totext. The block is a single"args": [...]key/value fragment, not a JSON document, so it never parsed — a reader copying it as JSON got a syntax error. Added guards that keep every documented example honest: every ```json fence across README.md, CLAUDE.md and docs/ must parse, every documentedAPPLE_*_MCP_*environment variable must exist under src/, and the README `## Tool Reference` must document exactly the tools the built server advertises — in both directions, so neither an undocumented new tool nor a leftover entry for a removed one can pass.
v1.1.17
Fixed
-
Every tool was rejected by the client, because the advertised schemas declared JSON Schema draft-07. MCP has standardized on 2020-12, and hosts now refuse anything else —
Tool '<name>' has an invalid outputSchema: JSON Schema declares an unsupported dialect ("$schema": "http://json-schema.org/draft-07/schema#"). The default validator supports JSON Schema 2020-12 only.The server starts and connects normally, so the failure presents as all 26 tools silently unavailable rather than as a crash. The dialect comes from the MCP SDK, not from this repo:server/mcp.jscalls its Zod converter with notarget,mapMiniTarget(undefined)resolves todraft-7, and everyinputSchemaandoutputSchemais stamped draft-07 on the way out. Upgrading Zod does not fix it — both the v3 (zod-to-json-schema) and v4 (zod/v4-minitoJSONSchema) branches fall back to draft-07 without a target, verified empirically against SDK 1.30.0 + Zod 4.4.3 — so no dependency bump could have cleared this. The outgoingtools/listpayload is now normalized at the transport boundary, the only public seam that does not reach into SDK internals: the 2020-12 dialect is re-stamped at each schema root, nested$schemadeclarations are stripped (illegal on a subschema), and the keywords that changed between the drafts are rewritten —definitions→$defswith its#/definitions/…$refs repointed, tuple-formitems→prefixItems,additionalItems→items,dependenciessplit intodependentRequired/dependentSchemas, and booleanexclusiveMinimum/exclusiveMaximumcollapsed onto the numeric bound. Today's emitted schemas use none of those constructs, so the rewrite is a no-op on current output — it exists so a Zod construct added later cannot quietly reintroduce a draft-07-only keyword alongside a 2020-12 declaration, which would be worse than the bug it replaces. No tool, Zod schema, or handler changed; all 26 tools still register, and each now advertiseshttps://json-schema.org/draft/2020-12/schemaon both its input and output schema. Reported against the sibling server as sweetrb/apple-mail-mcp#147; all four Apple MCP servers were affected identically and are fixed in lockstep. -
The dialect converter is now POSITION-AWARE, so it cannot corrupt a tool parameter that happens to be named after a schema keyword. The first cut of the converter recursed uniformly and then switched on every key it met — but the keys of a
propertiesmap are caller-chosen tool parameter names, not schema keywords. A tool declaring a parameter nameddefinitionswould have had it renamed to$defson the wire; one named$schemawould have been silently deleted whilerequiredwent on naming it, producing a schema no input can satisfy;dependencieswould have been restructured intodependentRequired/dependentSchemasandadditionalItemsdropped outright. The same class applied to instance data:enum,const,defaultandexampleshold a caller's literal values, and recursing into them rewrote those literals as if they were schema keywords — adefaultof{ "definitions": 1 }came back as{ "$defs": 1 }. The walk now distinguishes the three positions: a name → schema map (properties,patternProperties,$defs,dependentSchemas) has only its values converted and its keys copied verbatim; data keywords (enum,const,default,examples,required,dependentRequired) pass through untouched; everything else is a schema and recurses as before. Verified latent, not live: no server in the fleet hits a corrupting name today — apple-notes-mcp'sget-checklist-statehas an output property nameditems, which happens to land in a safe branch — so this is fixed before the release rather than after. The advertisedtools/listpayload for all 26 tools is byte-identical across the fix (SHA-2568eb7f7b7…45ac3f10before and after), which is exactly the expected result for a latent defect.
Added
- The
outputSchemacontract test now asserts the advertised dialect. The existing checks boot the real built server over stdio and inspect what it advertises — every tool has anoutputSchema, none requires a field, none setsadditionalProperties: false— but none of them looked at$schema, so a dialect that made the client discard every tool passed CI cleanly. The suite now fails any advertised schema that does not declare 2020-12, that mentionsdraft-07anywhere, that uses a draft-07-only keyword (definitions,dependencies,additionalItems), or that declares$schemaon more than one node. Unit tests for the converter itself cover each keyword rewrite and the transport wrapper.
v1.1.16
Changed
- Bumped the
numbers-parserruntime pin from 4.18.5 to 4.19.0. This is the Python sidecar that backs every read and every value/structure write, so it is a runtime dependency of the published package —requirements.txtis shipped bytes and the bump owes a version bump, which Dependabot cannot add on a pip PR. Upstream fixes cell-border rendering (masaccio/numbers-parser#152), the performance ofTable.set_cell_border(), and row height / column width after border manipulation; the minor bump is because border behaviour changed — layers of borders are no longer retained. This server never calls the border API, so that change is not reachable from any tool here. Verified against real spreadsheets rather than trusting CI, since the unit tests mock the sidecar:info/read/create/set-cellall exit 0 with clean JSON on stdout and empty stderr; a 226-row × 8-column real inventory file round-tripped aset-cellwrite and re-opened with its row and column counts intact;doctorreportsnumbers-parser 4.19.0healthy; andget-file-info/read-tablereturn correct data end-to-end through the MCP server. (#60)
Security
- Floored
js-yamlto^4.3.1, clearing GHSA-5p4m-2wfm-xmqj (high). Quadratic CPU consumption while resolving!!omapkeys — a malicious YAML document can be made to burn CPU superlinearly in the number of map entries. The advisory notes the CVE-2026-59870 fix was never backported to the 3.x line, so 4.3.1 is the first complete release.js-yamlreaches the tree aseslint->js-yaml, which is development scope, and it does not appear in the committedbuild/index.js— verified, 0 references — so no published artifact ever carried it and this owes no version bump. Nojs-yamloverride existed here before; apple-mail-mcp carried one pinned at^4.2.0— below this fix — which is how the gap was found.
v1.1.15
Fixed
- Every tool advertised an output schema that rejected undeclared keys, discarding otherwise-correct results. The MCP client validates a result's
structuredContentagainst the JSON Schema the server advertised, not against the server's own zod object — and a bare zod raw shape renders asadditionalProperties: false. So any field a handler emits that its schema doesn't enumerate is a hard client-side-32602 … data must NOT have additional properties, throwing away a payload the handler computed correctly. The server never notices, because zod's own parse silently strips unknown keys instead of failing, which is exactly why theregisterTool/outputSchemamigration's "all fields optional, no.strict()" read as permissive: it covered optionality, not undeclared keys. All 26 tools in this repo were advertisingadditionalProperties: false. Every tool now registers through a wrapper applying.passthrough(), advertisingadditionalProperties: true— the contract that migration intended. Found while fixing the same defect in the sibling apple-mail-mcp (sweetrb/apple-mail-mcp#135), where it was not latent: it brokeget-mail-statson every call for anyone with IMAP configured.
Added
- The outputSchema contract test now asserts that every tool tolerates undeclared keys. The existing checks — every tool has an
outputSchema, none requires a field — could not see this class, because they inspect the advertised schema and round-trip only the diagnostic tools; a tool whose payload carries an undeclared key passes CI and fails in the user's client. The suite now fails any tool advertisingadditionalProperties: false, so this cannot silently return.
v1.1.14
Changed
- Dependency bump via Dependabot; committed bundle rebuilt. (automated)
v1.1.13
Fixed
doctorreported "Numbers.app not found" on every Mac that had upgraded, even with Numbers running in front of you. The check tested two hard-coded paths,/Applications/Numbers.appand/System/Applications/Numbers.app. Apple's 2026 iWork refresh renamed the bundle toNumbers Creator Studio.appand moved the bundle ID fromcom.apple.iWork.Numberstocom.apple.Numbers, so neither path could ever match again and the check emitted a permanent falsewarntelling users the formula and formatting tools were unavailable when they were fine. Only the diagnostic was wrong — the AppleScript tools themselves target the app by name (CFBundleNameis stillNumbers), soset-formula,set-cell-style,set-column-width/set-row-heightandmerge-cells/unmerge-cellswere working the whole time. Detection now tries the known paths (including the new name) and then falls back to Launch Services, resolvingpath to application idforcom.apple.Numbersand then the pre-2026com.apple.iWork.Numbers. That is a registry lookup: it does not launch Numbers and needs no Automation permission. Resolving by bundle ID rather than by path is what makes the check survive the next rename too, and accepting both IDs keeps it correct on machines that never upgraded. The reported detail now names the resolved bundle path, so a surprising location is visible rather than silently accepted. A Launch Services answer pointing at a path that no longer exists (a stale registration) still warns.
v1.1.12
Security
- Floored
honoto^4.12.34, clearing GHSA-8j4g-w8fx-2239 (moderate). This was deferred earlier the same day: the fix release was still inside the repo's 24-hourminimumReleaseAgesoak — it missed by under three minutes — and nominimumReleaseAgeExcludecarve-out was added, because the soak is the point. It matured at 2026-08-04T02:36:40Z and is floored now.pnpm auditreports no known vulnerabilities.
Added
version-guardnow requires every version bump to be documented under a real## [X.Y.Z]CHANGELOG heading. The guard already refused a bump to a version that was already on npm, but it never checked that the new version was described anywhere. Notes parked under## [Unreleased]are orphaned the moment the release ships: nothing in the release path renames that section — theversionlifecycle script only syncs the plugin manifests — so the published version goes out undocumented while its release notes sit under a heading still claiming they are unreleased. apple-notes-mcp shipped 2.6.10 and 2.6.11 exactly that way before this check existed. A bump whose version has no matching heading now hard-fails the PR, with an error naming the heading to add. Keep an empty## [Unreleased]at the top regardless —dependabot-rebuild.ymlhard-exits without that marker, and since it already inserts a real heading, bot PRs pass unchanged. The guard file lives in.github/, which does not ship, so this owes no version bump. Matches apple-mail-mcp#124, keeping the guard identical across the four servers. (#50)
Removed
.hermes-plugin/packaging docs (README.md,config.yaml). Hermes Agent has no plugin/marketplace drop-in, so a directory of manifest-looking files was easy to misread as an installable package. The setup it documented is not lost — thehermes mcp addcommand, the~/.hermes/config.yamlmcp_servers:snippet, and the restart note now live inline in the README's "Other Hosts" section. Matches apple-mail-mcp#116, keeping multi-host packaging parity across the four Apple MCP servers. No effect on the published package:.hermes-plugin/was never inpackage.jsonfiles[].
Fixed
version-guardno longer demands a version bump for byte-neutralsrc/changes. The shipped-bytes detector treated every non-test file undersrc/as shipped, but TypeScript there reaches users only after esbuild inlines it intobuild/index.js— so a comment-, formatting- or type-only edit that leaves the committed bundle byte-identical was hard-blocked, leaving only two bad options: publish a release of literally nothing, or do not write the comment.src/**/*.tsis now a first-cause detector that implies a bump only whenbuild/**changed too. The exemption is sound rather than merely convenient: ci.yml's "Verify committed build/ matches source" step rebuilds and requiresgit diff --quiet build/, and it runs in thetestjob whosetest (22)/test (24)contexts are required by branch protection — so at merge time an unchangedbuild/provably matchessrc/. Everything else undersrc/(the verbatim-shipped*_reader.pysidecars),requirements.txtandbuild/**stay unconditional detectors, and the rule is written fail-safe: only.tscounts as bundle-only, so any new file type undersrc/still requires a bump.- Dependabot auto-bump silently stopped staging its own changes.
dependabot-rebuild.yml's bump step writes the patch version, syncs the plugin manifests and prepends a CHANGELOG entry, then staged them withgit add package.json CHANGELOG.md build .claude-plugin .agents codex .hermes-plugin .antigravity-plugin. Once.hermes-plugin/was removed that pathspec matched nothing, andgit addis all-or-nothing — it exited 128 and staged none of the others, with2>/dev/null || truehiding the failure. The following step re-adds onlybuild/, so a Dependabot PR would have committed a rebuilt bundle with no version bump and no changelog entry, failingrequire-version-bumpand blocking the automation that is meant to run without a human. Dropped the stale path, and dropped the error suppression so a future missing path fails loudly instead of silently skipping the bump. pnpm versionno longer breaks with the.hermes-plugin/removal. Theversionlifecycle script listed.hermes-pluginin itsgit add;git addexits 128 on a pathspec that matches nothing, which would have broken the documented release step (pnpm version <patch|minor|major> --no-git-tag-version) for every subsequent release. The stale path is dropped from thegit addlist.
Security
fast-uri3.1.4 → 3.1.5, clearing GHSA-7p8r-x3mc-p8w7 (high) — and this one was in the shipped bundle.fast-urireaches the published package for real:@modelcontextprotocol/sdk→ajv(andajv-formats) pull it, and esbuild inlines its source intobuild/index.js, so the committed bundle carried the vulnerable copy and changed by 1,127 bytes when the fix landed — hence the version bump. The floor was already here and was the thing holding the tree back.pnpm-workspace.yamlreadfast-uri: 3.1.4, written as an exact pin when 3.1.4 was the newest release; the moment 3.1.5 shipped that pin stopped acting as a floor and became a ceiling, pinning the tree to the vulnerable version so the advisory could never clear no matter how many times Dependabot re-ran. Rewritten as the caret range^3.1.5, which stays insideajv's expected major while letting future patch fixes flow in. Every floor in this file is now a caret range for that reason — an exact pin is a security floor that silently expires. Matches apple-mail-mcp 2.10.2 (#128), which hit the identical trap.ip-address10.2.0 → 10.4.0, clearing GHSA-mwp4-54f8-5fhr (high), GHSA-4xrf-jv44-h6hh and GHSA-22jq-vg5j-6vgg.@modelcontextprotocol/sdk→express-rate-limitcapped it at 10.2.0, below the 10.3.1 fix; floored at^10.3.1, which resolves to 10.4.0. Unlikefast-urithis is not in the shipped bundle — the SDK's HTTP transports are tree-shaken out of the stdio-only server, andgrep -c ip-address build/index.jsreturns 0 — so the exposure was to the install tree, not to anything this package runs. Fixed anyway to keep the dependency graph clean and the Dependabot alert list actionable.- Deferred:
hono(GHSA-8j4g-w8fx-2239, moderate), still resolving to 4.12.27. The fix is 4.12.34, published 2026-08-03T02:36:40Z — roughly three minutes inside this repo's 1440-minuteminimumReleaseAgewindow at the time of the change, sopnpm installrefused it withERR_PNPM_NO_MATURE_MATCHING_VERSION. That gate is deliberate supply-chain policy and clearing it by three minutes is exactly the kind of exception it exists to prevent, so nominimumReleaseAgeExcludecarve-out was added and the floor was left out rather than forced.honois not in the shipped bundle either (grep -creturns 0; it arrives via@hono/node-server, whose HTTP transport this stdio server never loads), so the published package is unaffected.pnpm auditwill keep reporting this one moderate finding until the floorhono: ^4.12.34is added in a follow-up, which is now a one-line change. - Floored all three dev-only
brace-expansionmajors on their complete fixes for GHSA-mh99-v99m-4gvg / CVE-2026-14257 (high) —1.1.16→1.1.18,2.1.2→2.1.4, and both5.0.7and5.0.8→5.0.9. Three separate majors are reachable through the dev toolchain (eslint→minimatch@3on v1,minimatch@9on v2,minimatch@10on v5), and they are not API-compatible — minimatch 3 requires the v1 CommonJS API, so a single floor spanning them fails withexpand is not a function. Each major therefore carries its own two-sided floor; the bounds must be two-sided because a bare<5.0.9also matches1.1.18and2.1.4under semver and would drag the CommonJS path onto the v5 ESM API. The advisory's own first-patched versions (1.1.17/2.1.3/5.0.8) are not sufficient: they bound the accumulator incombinebut never threadmaxLengthintoexpandSequence, so the sequence path ({1..N},{a..z..k}) stays capped only by item count and a padded sequence still materialises ~100,000 intermediate strings before the outer bound truncates (measured 4,606 ms / 176 MB RSS on1.1.17vs 9 ms / 61 MB on1.1.18, identical final output). Two of the four paths resolved here (1.1.16,5.0.7) were below even the advisory's floor. Adopted only after every release cleared this repo's 24-hourminimumReleaseAgegate, with nominimumReleaseAgeExcludecarve-out and no audit suppression —pnpm auditwill keep reporting the advisory until GitHub's metadata (which still lists5.0.8as first-patched, and so marks the entire v1 line vulnerable under semver) catches up. Dev toolchain only:brace-expansionis not in the shipped bundle, so the published package is unaffected, the committed bundle is byte-identical, and no version bump is owed. Matches apple-mail-mcp#123 — thanks to @jjoanna2-debug for the original finding. - postcss 8.5.16 → 8.5.24 (dev-only transitive, via vite/vitest). Clears Dependabot alert #6 (GHSA high): "PostCSS: Path Traversal in Previous Source Map Auto-Loading (sourceMappingURL) leads to Arbitrary .map File Disclosure", whose vulnerable range is
<= 8.5.17. postcss is not a runtime dependency and is not inlined into the committed bundle (verified byte-identical after a rebuild), so nothing that ships to npm changes — this was a stale lockfile resolution, not a code defect. The sibling repos were already above the range (mail 8.5.23 behind an explicit^8.5.15override floor, notes and photos 8.5.19), which is why the alert fired only here.
v1.1.11
Fixed
- The documented backend split was wrong on every agent-facing surface, and the correction reaches the shipped tool descriptions.
CLAUDE.md,skills/apple-numbers/SKILL.md,docs/LIMITATIONS.md,docs/AUTOMATION-PERMISSION.mdand the README's Requirements bullet all described the divide as reads use numbers-parser, writes drive Numbers.app via AppleScript. The real divide is values vs. formatting: eleven write tools —create-spreadsheet,set-cell,set-cells-batch,add-rows,update-rows,delete-rows,add-sheet,add-table,rename-sheet,rename-table,import-csv— run entirely on the Python sidecar and never send an Apple event, so they need neither Numbers.app nor an Automation grant. Only eight reach AppleScript:set-formula(s),set-cell(s)-style,set-column-width/set-row-height,merge-cells/unmerge-cells. The cost was concrete — an agent readingCLAUDE.mdwould gate or refuse a perfectly executable headlessset-cellorimport-csvon a missing Numbers.app, or send a user through the Automation-permission ritual for a call that never consults it, and would misread an unrelated sidecar error as a TCC failure. The repo also contradicted itself (README:156 and four tool descriptions already had it right), so a caller had no way to tell which statement to trust. All surfaces repartitioned,doctor'snumbers_app/automation_permissiondetails rescoped, and the seven sidecar tool descriptions that named no backend now say "via the numbers-parser sidecar (does not require Numbers.app)" like the other four already did. docs/AUTOMATION-PERMISSION.mdprescribedset-cellas the "definitive test" of the Automation grant — a probe that cannot fail for lack of it.set-cellgoes to the sidecar, so it succeeds whether the permission is granted, denied, or never prompted for; a user following the page concluded the grant was in place and then hit "Not authorized to send Apple events" on their firstset-formula, which is exactly the failure that page exists to prevent. The probe is nowset-cell-style(orset-formula), with an explicit note that value writes prove nothing.- The eight AppleScript tools'
Safety:lines said Numbers.app must "be running". Backwards:buildScript()issuesopen POSIX file, which launches Numbers.app and opens the document — the app must be installed, not running. Corrected on all eight, and the seven that lacked it now carry the disclosureset-formulaalready had, plus two side effects no doc mentioned: the call saves the whole document (committing any unsaved hand edits the user has open in it) and leaves it open, which then races every later numbers-parser read of that file. - README documented
set-formulas-batch's array parameter asentries; the schema requiresformulas. A call written from the README fails Zod validation before the handler runs — the unknownentrieskey is stripped and the requiredformulasis missing.entriesis correct for the adjacentset-cells-style-batch, which is where the copy-paste came from. - README's "Iterative Edits" example styled a header row with
bold: true, a fieldcellStyleSchemadoes not have. The unknown key is stripped to{},buildStyleCommands()returns[],runAppleScript()is skipped entirely — and the tool still reports "Styled 1 cells". The example now usesfontName: "Helvetica-Bold", matching the parameter table at README:509, which already documented that there is nobold/italicflag. skills/apple-numbers/SKILL.md'simport-csvexample passedpath. The tool has nopathparameter and requires bothinputPathandoutputPath; the neighbouringsearchexample (which really does takepath) is what seeded it. Fixed in the canonicalskills/copy and re-synced tocodex/skillsand.antigravity-plugin/skills.CLAUDE.mdstill saiddoctorreports three checks.python_interpretermade it four in 1.1.6 — and it is the check that surfaces the single most common setup failure, a stock macOS Python 3.9. README anddocs/AUTOMATION-PERMISSION.mdwere updated then;CLAUDE.mdand its "Tools at a glance" row were missed.docs/LIMITATIONS.mdandCLAUDE.mdadvertised Linux support thatpackage.jsonmakes impossible. LIMITATIONS.md gave Linux read-only deployment a dedicated section heading and named it as a supported fallback, but"os": ["darwin"]has been inpackage.jsonsince the initial commit, sonpm installhard-fails there withEBADPLATFORM. The read path genuinely is platform-independent (there is noprocess.platformbranch anywhere insrc/) — the package is not, and the pin is correct, since the formula/format tools cannot work off macOS. Reworded to match README:156 rather than dropping the pin..github/PULL_REQUEST_TEMPLATE.md's CONTRIBUTING link 404'd. GitHub resolves a PR template's relative links against.github/, and.github/CONTRIBUTING.mdhas never existed — the file is at the repo root. Contributors lost the "rebuild and commitbuild/index.js" instruction, whichci.yml's Verify committed build/ matches source step then fails them on. Now an absolute URL, per the house standard for cross-file links.- The Antigravity marketplace still advertised a Hermes plugin.
.hermes-plugin/was removed in #42 precisely to stop that misreading, and the README, CHANGELOG and both sync scripts already say there is no Hermes drop-in. The Codex manifest'slongDescriptionlikewise still called the sidecar "cross-platform" and attributed all writes to AppleScript.
Added
- Documented
export-table's destructive write. It was the only file-writing tool with noSafety:line in its description and no⚠️ note in the README, despite writingoutputPathunconditionally —outputPath: "~/.zshrc"truncates that file — while both sibling file-creating tools carry one. - Documented
import-csv's type coercion and column-set rules. CSV/TSV fields are auto-typed, so01234imports as the number1234and1e5as the number100000— the opposite of the conservative coercion the write tools were given in 1.1.4, and irreversible once written. JSON values pass through untouched, but an array of objects takes its column set from the first object only, silently dropping keys that appear later, and an array of arrays gets syntheticColumn_Nheaders with the first row kept as data.format: "auto"also falls back to CSV for any unrecognized extension. None of this was stated in the README, the skill, the docs or the tool description. - Documented
add-sheet/add-tablegeometry. Omitheadersand you get a 12 × 8 grid of empty cells; pass them and you get 1 ×headers.length. ThenumRows/numColsoverrides exist all the way down the stack but are not exposed through MCP, so the extra rows can only be removed afterwards withdelete-rows. - Clarified that
read-tablereturns the dimensions of the selection. ItsnumRows/numColscount what came back, not the table — under the exact field namesget-file-infouses for the table's real size. The description called them "dimensions"; it now names the fields and points atget-file-info. - Documented the fixed per-call timeouts — 30 s for sidecar calls, 60 s for AppleScript — and that neither is configurable.
APPLE_NUMBERS_MCP_SETUP_TIMEOUTsits one row below in the same README table and governs only the venv bootstrap, which sent anyone hittingOperation timed out after 30000mslooking for a knob that does not exist. findSystemPython()'s "Python 3 not found on PATH" error now names thedoctortool, matchingsetupHint()eight lines away in the same file and the house standard for setup-failure messages.- Plugin/marketplace manifest descriptions now mention formulas and formatting — roughly a third of the tool surface, and one of the two capabilities
package.json's description leads with. The strings were written before the AppleScript formatting tools landed and were never revisited.
v1.1.10
Security
- Override the MCP SDK's transitive
@hono/node-serverandfast-uridependencies to patched releases (@hono/node-server2.0.10,fast-uri3.1.4), clearing the Hono static-file path-traversal advisory and the twofast-urihost-confusion advisories that the SDK's own ranges still resolve to. Fleet-wide companion to sweetrb/apple-notes-mcp#104 (@oliverames).