Skip to content

MCP returns fallback code instead of Code Connect output for large instance subtrees #417

Open

Description

@uthem150

Summary

Figma MCP (get_code_connect_map and get_design_context) stops returning a component's Code Connect template result once the selected instance has many connected descendants. It returns a fallback instead. Dev Mode shows the correct template result for the same node at the same time. MCP gives no error and no "lookup timed out" notice.

The parent template only returns a marker containing the instance name. It does not inspect or execute any children.

In a minimal reproduction made from generic components in a fresh file (no team library involved), we saw a consistent boundary:

  • the template result is returned with 142 connected instances under the selected node;
  • a fallback is returned from 143 connected instances.

This is an observation from our reproduction; we have not found any documentation of such a limit.

Environment

  • @figma/code-connect 2.0.2, parserless templates (*.figma.ts)
  • Components and instances in the same file (no library publish needed to reproduce)
  • Figma remote MCP server (https://mcp.figma.com/mcp), called from Claude Code
  • Observed on 2026-10-24

Reproduction

Components

Component Content
Cell TEXT property label
HeaderCell TEXT property label
Row a slot Cells, filled with Cell instances
Table slots Head (filled with HeaderCell) and Body (filled with Row)
Icon a plain icon
IconCell a cell with a fixed Icon instance as a sublayer

Every component has a Code Connect template, and slot contents are filled per instance.

Templates

Replace the URL in each template with the link to its corresponding component. Give each component its own template ID.

Table: a fixed string only.

// url=<link to the corresponding Figma component>
import figma from 'figma';
const instance = figma.selectedInstance;
export default {
  id: 'Table',
  imports: [],
  example: figma.code`// PROBE-R v1 · ${instance.name}`,
  metadata: { nestable: false },
};

The child templates return simple tags and do not execute other templates. For example, Cell uses:

// url=<link to the corresponding Figma component>
import figma from 'figma';
const instance = figma.selectedInstance;
const label = instance.getString('label');
export default {
  id: 'Cell',
  imports: ["import { Cell } from './Cell'"],
  example: figma.code`<Cell>${label}</Cell>`,
  metadata: { nestable: true },
};

Steps

  1. Publish the templates with figma connect publish.
  2. Place Table instances whose row and header counts give different numbers of connected instances. The count includes the root and fixed sublayer instances. With six cells per row, 19 rows and eight headers give 142 instances including the table itself: 1 + 19 + 19 × 6 + 8. Adding one header gives 143.
  3. For each instance, select it in Dev Mode, and call get_code_connect_map and get_design_context with its node ID.

Results

get_code_connect_map: we judged each result only by whether the PROBE-R v1 mark is present in the root entry's snippet.

Connected instances under the selected node MCP root snippet Dev Mode
35, 42, 63, 84, 91, 98, 112, 119, 126, 133, 140, 141, 142 // PROBE-R v1 · … (sizes run twice gave the same result) template result (checked at 35 and 142)
143, 144, 145, 146, 147, 217 <Table19Rows9Headers /> etc. (see below) template result (checked at 143, 143 with icons, and 217)

What the fallback looks like:

  • get_code_connect_map: the root entry keeps hasTemplate: true. Its snippet is a tag named after the layer, snippetImports is [], and snippetNestedFunctions contains one Tailwind design-code function. Every descendant entry still has its normal template snippet.
  • get_design_context:
    • In the minimal reproduction, the CodeConnectSnippet for the root contains an auto-generated <Table body="…" head="…"> with child tags instead of the template result.
    • In our production design system (a library used from another file), the response was Tailwind design code with no CodeConnectSnippet at all.

Other observations in the minimal reproduction:

  • The same 142/143 boundary appeared when fixed icon instances were included in the count, even though those icons were absent from the map. A table of IconCells with 82 map entries (142 instances including the fixed Icon sublayers) succeeded, and one with 83 map entries (143 instances) fell back.
  • Adding plain layers did not trigger the fallback in our tests. A table with 63 connected instances and 1,086 nodes in total succeeded.
  • A larger response still succeeded. A 140-instance table whose cell templates produce 400 characters each (an 83 KB response) returned the template result.
  • The boundary did not move in any of these cases:
    • adding up to 60 instances without templates;
    • nesting instances up to 5 levels deep;
    • adding hidden connected instances, in each of four ways: a boolean property defaulting to off, visible = false in the definition, a property turned off on the instance, and a layer hidden directly on the instance.
  • A failed request completed in 6.9 seconds, while a successful request took 11.0 seconds. Total request duration alone therefore does not explain the fallback; a timeout within an internal stage remains possible.
Additional observation from a production library (different boundary)

We see the same fallback with our real components: a published library, with instances placed in another file. The boundary there was different.

How we counted:

  • Connected means an instance of a component that has a Code Connect template, counted with the Plugin API with figma.skipInvisibleInstanceChildren = false.
  • Hidden means the instance or one of its ancestors has visible = false. Our tables have 18 such instances, hidden by component properties: header icons, footer cells and a loading spinner.

What we did: starting from an 11-row table, we added icons to a cell slot one at a time and ran each copy twice.

Visible connected Hidden connected Total Result
121 18 139 template result
122 18 140 template result
123 18 141 fallback
124, 125, 126 18 142, 143, 144 fallback

So the production-library tests had a different boundary: 140 instances succeeded and 141 fell back when hidden connected instances were included in our count. Adding hidden instances did not move the boundary in the local reproduction. We have not isolated the cause of this difference.

Expected

MCP returns the same template result that Dev Mode shows. If MCP hits a limit, the response should say so, as the "Code Connect lookup timed out" notice does. Then clients can tell "not connected" apart from "lookup incomplete". Today the fallback looks like a normal snippet.

Questions

  1. Is there a limit on how many connected instances MCP resolves under one selected node? What exactly does it count (for example, hidden instances of library components), and is it documented?
  2. Can a client request only the selected node's template result, without resolving Code Connect output for its descendants?
  3. Could the fallback be marked explicitly, for example with a flag on the entry or a notice in the response?

Related: #407 ("Code Connect lookup timed out" on wide nodes).

Activity

  1. nayukata commented on Oct 27, 2026

    @nayukata

    We see the "library used from another file" case from this issue, but without the instance-count boundary. In our setup it fails even for a single instance.

    Environment: remote MCP server (https://mcp.figma.com/mcp) called from Claude Code, Enterprise plan with a Full seat, @figma/code-connect 2.0.0 with template files, label React. Observed on 2026-10-27.

    Results (get_design_context with clientFrameworks: "react"):

    Node File get_design_context get_code_connect_map
    Button variant (main component) library file CodeConnectSnippet template result
    Button instance inside another component library file CodeConnectSnippet template result
    Single Button instance (its only nested instance is one icon) consuming file Tailwind code, no CodeConnectSnippet template result
    • The same kind of node in the consuming file returned CodeConnectSnippet on 2026-09-09. Our last template change was published on 2026-09-10.
    • The library component has not changed since June, and the consuming file uses the current version.
    • Setting disableCodeConnect: false or clientFrameworks: "React" made no difference.
    • Republishing all templates with the same content made no difference, including after waiting.

    So, apart from the 142/143 boundary, get_design_context may also be failing to resolve Code Connect for any library instance placed in another file, while get_code_connect_map still resolves it.

  2. islobodiuk-figma commented on Oct 29, 2026

    @islobodiuk-figma

    @nayukata this seems related to a regression that we fixed yesterday. Please try it again, and if the problem persist please report a bug.

  3. uthem150 commented on Oct 29, 2026

    @uthem150
    Author

    Following the fix mentioned above, we re-tested the case described in this issue on 2026-10-29, and the instance-count boundary still reproduces.

    get_code_connect_map on a published-library DataGrid instance with many connected descendants (164 entries in the map) still returns hasTemplate: true with the root snippet being <DataGrid /> and a Tailwind design-code function in snippetNestedFunctions, instead of the template result. Descendant entries still return their normal template snippets, and Dev Mode shows the correct template result for the same node (checked on the same day).

    Could you confirm whether the instance-count limit is expected behavior, and whether the fallback could be marked explicitly in the response?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions