diff --git a/packages/mcp-server/src/selection.ts b/packages/mcp-server/src/selection.ts index db098b37..38f2ee8d 100644 --- a/packages/mcp-server/src/selection.ts +++ b/packages/mcp-server/src/selection.ts @@ -69,14 +69,15 @@ export function parseSelectionAnswer(value: unknown): SelectionAnswer | null { return { pageId: text(given.pageId), pageName: text(given.pageName), cells } } -/** describeSelection lists at most this many cells; the rest are counted */ +/** describeSelection details at most this many cells; the rest by id only */ export const MAX_LISTED_CELLS = 100 /** * The get_selection result text. `externalDrawio` names the draw.io origin * when the preview loads it from another origin, where the page cannot - * read the editor. A selection of a whole large diagram is counted, not - * listed in full: the model needs the ids of a few hundred cells at most. + * read the editor. A selection of a whole large diagram would fill the + * model's context with labels and geometry: the first cells get them, the + * rest are listed by id alone (an edit on the selection needs every id). */ export function describeSelection( answer: SelectionAnswer, @@ -110,10 +111,10 @@ export function describeSelection( const parent = cell.parent ? ` inside "${cell.parent}"` : "" return `- id="${cell.id}" shape ${label}${where}${parent}` }) - const more = answer.cells.length - listed.length - if (more > 0) { + const rest = answer.cells.slice(MAX_LISTED_CELLS) + if (rest.length > 0) { lines.push( - `- and ${more} more cells not listed here; call get_diagram for the rest`, + `- and ${rest.length} more cells, listed by id only: ${rest.map((c) => `"${c.id}"`).join(", ")}`, ) } const count = diff --git a/packages/mcp-server/tests/selection.test.ts b/packages/mcp-server/tests/selection.test.ts index 739cac9f..3fc61e55 100644 --- a/packages/mcp-server/tests/selection.test.ts +++ b/packages/mcp-server/tests/selection.test.ts @@ -53,7 +53,7 @@ describe("get_selection text", () => { expect(text).toContain('edit_diagram (page_id="p1")') }) - it("counts a large selection instead of listing every cell", () => { + it("details the first cells of a large selection and lists the rest by id", () => { const cells = (n: number) => Array.from({ length: n }, (_, i) => ({ id: `c${i + 1}`, @@ -67,8 +67,13 @@ describe("get_selection text", () => { ) expect(text).toContain(`${MAX_LISTED_CELLS + 5} cells selected`) expect(text).toContain(`id="c${MAX_LISTED_CELLS}"`) + // No detail past the cap, but every id: the model acts on all of + // them, and get_diagram cannot tell which cells were selected expect(text).not.toContain(`id="c${MAX_LISTED_CELLS + 1}"`) - expect(text).toContain("and 5 more cells not listed here") + expect(text).not.toContain(`shape "Cell ${MAX_LISTED_CELLS + 1}"`) + expect(text).toContain( + `- and 5 more cells, listed by id only: "c${MAX_LISTED_CELLS + 1}", "c${MAX_LISTED_CELLS + 2}", "c${MAX_LISTED_CELLS + 3}", "c${MAX_LISTED_CELLS + 4}", "c${MAX_LISTED_CELLS + 5}"`, + ) const exact = describeSelection( { ...page, cells: cells(MAX_LISTED_CELLS) }, null,