fix(mcp-server): review fixes for get_selection

Server (src/http-server.ts, src/index.ts, src/selection.ts,
src/new-diagram.ts):
- overlapping get_selection calls take turns (readSelection, one slot per
  session as the export slot) instead of replacing each other's request,
  which left one of them with a false "tab not in front" timeout
- an answer is taken only while a request is pending (both ids undefined
  compared equal)
- the tool sees the page start_session actually opens: with dist/shell
  missing the classic page is in use, which never answers, so the tool
  says so instead of timing out
- the result lists at most 100 cells and counts the rest: a whole large
  diagram selected would fill the model's context
- every <diagram> the model sends without an id gets one, so the page id
  the shell reports exists in the server's document

Shell (shell/mcp-sync-core.ts, shell/use-mcp-sync.ts,
contexts/diagram-context.tsx, lib/drawio/editor-bridge.ts):
- an answer whose POST failed is sent again at the next poll
- no answer while a full load has yet to reach the editor: it still shows
  the previous document, whose cells and pages the answer would name
- a hidden tab (the same session open twice) answers a poll later, so the
  tab in front answers first; alone, it still answers within the timeout
- a cell's container is reported by the model's isLayer, not by comparing
  with the default parent, which is the group the user entered
This commit is contained in:
dayuan.jiang
2026-10-11 20:56:07 +09:00
parent 734a31d4eb
commit 467fd25fd1
12 changed files with 316 additions and 31 deletions
@@ -1,6 +1,7 @@
import { describe, expect, it } from "vitest"
import {
describeSelection,
MAX_LISTED_CELLS,
parseSelectionAnswer,
type SelectionAnswer,
} from "../src/selection.ts"
@@ -52,6 +53,29 @@ describe("get_selection text", () => {
expect(text).toContain('edit_diagram (page_id="p1")')
})
it("counts a large selection instead of listing every cell", () => {
const cells = (n: number) =>
Array.from({ length: n }, (_, i) => ({
id: `c${i + 1}`,
label: `Cell ${i + 1}`,
edge: false,
geometry: { x: i, y: 0, width: 10, height: 10 },
}))
const text = describeSelection(
{ ...page, cells: cells(MAX_LISTED_CELLS + 5) },
null,
)
expect(text).toContain(`${MAX_LISTED_CELLS + 5} cells selected`)
expect(text).toContain(`id="c${MAX_LISTED_CELLS}"`)
expect(text).not.toContain(`id="c${MAX_LISTED_CELLS + 1}"`)
expect(text).toContain("and 5 more cells not listed here")
const exact = describeSelection(
{ ...page, cells: cells(MAX_LISTED_CELLS) },
null,
)
expect(exact).not.toContain("more cells")
})
it("says clearly when nothing is selected", () => {
const text = describeSelection({ ...page, cells: [] }, null)
expect(text).toBe(