Skip to content

feat: add get_pages and set_current_page, and fix get_document_info's single-page index - #186

Open
juansilvadesign wants to merge 1 commit into
grab:mainfrom
juansilvadesign:feat/page-navigation
Open

feat: add get_pages and set_current_page, and fix get_document_info's single-page index#186
juansilvadesign wants to merge 1 commit into
grab:mainfrom
juansilvadesign:feat/page-navigation

Conversation

@juansilvadesign

Copy link
Copy Markdown
Contributor

The bug

get_document_info reads figma.currentPage and then builds its pages array from that same single page:

pages: [
  { id: page.id, name: page.name, childCount: page.children.length },
],

So the response has the shape of a document summary while describing exactly one page — and it never errors, so a caller has no signal that anything is missing.

Auditing a real multi-page file, this produced a ~100x undercount: the tool reported 6 frames where the file's mobile page alone holds 764. The number was well-formed, confident, and wrong.

What this adds

  • get_pages — enumerates figma.root.children document-wide. Top-level child counts are opt-in, because documentAccess: "dynamic-page" requires loadAsync() per page and that cost shouldn't be mandatory. The opt-in path emits progress heartbeats so the server's inactivity timeout doesn't fire while pages load.
  • set_current_page — wraps figma.setCurrentPageAsync, validating that the target is actually a PAGE.
  • get_document_info — now indexes every page, marking each child count "available" (current page) or "not_requested". The index can no longer be mistaken for a complete child census.

Why the pair matters

Together these make an unattended multi-page sweep possible for the first time. Previously set_focus refused cross-page selection:

The selection of a page can only include nodes in that page

…so switching page required a human clicking in the Figma UI, which meant any document-wide automation had a manual step in the middle of it.

Compatibility

Additive. No existing field is removed or changes meaning — get_document_info gains scope, document, and pageCount, and its existing pages entries gain childCountStatus. Callers reading pages[].id / .name are unaffected; callers that assumed pages had length 1 were reading a bug.

Verification

This repo has no test suite or linter, so this was validated against live Figma files, including the one that produced the undercount above:

  • On that file, get_pages returns 6 pages where get_document_info previously reported 1. Top-level child counts: 1 / 826 / 764 / 81 / 1 / 35 — the 764-child page is the one that had been summarised as "6 frames".
  • get_pages({ includeChildCount: true }) completed for every page, so the opt-in loadAsync() cost is real but tolerable, and the heartbeats keep the inactivity timeout from firing.
  • Verified on a second, unrelated 7-page file that get_document_info also reported as 1 page.
  • set_current_page switches pages, and subsequent page-scoped reads operate on the new page.
  • bun run build succeeds.

Source-only — no dist/ changes, matching the convention from #185.

…s page index honest

get_document_info is page-scoped: it reads figma.currentPage and builds its
`pages` array from that same single page, so the response looks like a document
summary while describing one page. Auditing a real file, that produced a ~100x
undercount — 6 frames reported against an actual 764.

- get_pages enumerates figma.root.children document-wide. Top-level child counts
  are opt-in because documentAccess: "dynamic-page" requires loadAsync() per
  page; the opt-in path emits progress heartbeats so the server's inactivity
  timeout does not fire while pages load on large documents.
- set_current_page wraps figma.setCurrentPageAsync. Together with get_pages this
  lets a client sweep every page unattended, which was previously impossible:
  set_focus refuses cross-page selection ("The selection of a page can only
  include nodes in that page"), so switching page required a human in the
  Figma UI.
- get_document_info now indexes every page, marking each child count
  "available" (current page) or "not_requested", so the index can never be
  mistaken for a complete child census.

Additive: no existing field changes meaning or is removed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
juansilvadesign added a commit to juansilvadesign/cursor-talk-to-figma-mcp that referenced this pull request Jul 27, 2026
…rdict

Folds findings that were living only in session memory into the plan, so they
survive a context clear:

- 5.1 authoring-session clustering on get_local_components. The tool counts
  every variant including bulk-pasted kits (KAT: 3,849 of 4,094 were Untitled
  UI), and the working split is the id prefix. pages scoping only covers this
  when the kit happens to sit on its own page.
- 5.2 bound get_document_info payloads — the last document-wide read that can
  still blow the context budget, and the one that forces the spill-to-jq
  workaround because Read cannot chunk single-line JSON.
- 5.3 the two cosmetic wording nits found during the PR-4 verification.
- 5.4 runtime/distribution: npm @latest has none of this work, so .mcp.json
  stays on the fork dist and the DEV plugin until upstream cuts a version bump.
- 5.5 the two write-layer defects that are out of scope but kept costing
  debugging time (mixed-font set_multiple_text_contents, CROP normalisation).

Also marks the "neither tool is a superset" A/B verdict as superseded by the
f430682 fix recorded further down the same section. It reads as the standing
conclusion when skimmed, and it is not — post-fix the fork is a strict superset
on that node.

Nothing in Milestone 5 is started, and none of it blocks the grab#186 to PRs 2-5
cadence.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant