Skip to content

Commit 639897c

Browse files
Merge pull request #1341 from oriontech-me/fix/issue-1340-mcp-server-reopen-by-id
fix(mcp): re-open the flow under test by id in mcp-server (#1340)
2 parents 610eb60 + cf27946 commit 639897c

2 files changed

Lines changed: 92 additions & 25 deletions

File tree

docs/mcp/server/mcp-server.md

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,15 @@ round-tripped; then deletes it.
121121
4. Settings → MCP Servers → Edit: assert `command` is `npx` and `args[0]` is the
122122
sequential-thinking package, then **edit `args[0]`** to
123123
`@modelcontextprotocol/server-everything` (server **B**) and save.
124-
5. Return to the flow, re-select the server on the node, and assert the tool
125-
list now exposes `echo-0-option` — the refresh, not the cached A list.
126-
6. Delete the server; assert it is gone; re-register it as **A** again and assert
127-
the node's tool list is back to `sequentialthinking-0-option`.
124+
5. Return to the flow **by id** (`openFlowById`), re-select the server on the
125+
node, and assert the tool list now exposes `echo-0-option` — the refresh, not
126+
the cached A list.
127+
6. Delete the server; assert it is gone; re-register it as **A** again, return to
128+
the flow by id, and assert the node's tool list is back to
129+
`sequentialthinking-0-option`.
130+
131+
Both re-opens address the flow by **id**, never by the card whose name contains
132+
"New Flow" (#1340) — see the note below.
128133

129134
### 6 — `Streamable HTTP MCP server with server-everything should load tools correctly`
130135

@@ -248,6 +253,35 @@ Unchanged by #1091 (no stdio surface). Derives the project's own
248253

249254
## Notes *(optional)*
250255

256+
- **#1340 — test 5 re-opened a flow by NAME, and it opened the wrong one.** Both
257+
re-opens clicked the first `list-card` whose name contained "New Flow".
258+
Langflow names every blank flow "New Flow"/"New Flow (N)", so under
259+
`fullyParallel` the shared project holds one per worker and `.first()` resolves
260+
whichever the list puts first. Measured on nightly `1.12.0.dev18`: in isolation
261+
the test's own flow ranks first and the click is correct (which is why this
262+
never appeared in the daily history — no recorded failure on this test), but
263+
seeding **one** competing `New Flow …` in the same project before the list
264+
fetch is enough to flip it — the rendered order became
265+
`["New Flow probeB-…", "New Flow (1)", "Basic Prompting"]`, the click opened
266+
the competitor, and the test then died on the `text="MCP Tools"` wait at 30 s,
267+
blaming the node for a flow it was never in. The same locator, in
268+
`auto-save-off.spec.ts`, cost two dailies before it was diagnosed (#1336). Both
269+
re-opens now use `openFlowById` (#1214), the repo's by-id entry, which also
270+
seeds the assistant-onboarding flag and gates on the flow being writable —
271+
neither of which the card click did (#1005). The flow id is read AFTER the
272+
blank-flow navigation, never before it: the bootstrap parks the page on a
273+
placeholder flow Langflow deletes as soon as the modal navigates elsewhere
274+
(#490/#681).
275+
- **Pre-existing flake, NOT introduced by #1340: `openAddMcpServerModal`.** This
276+
test fails roughly 1 run in 3 locally at
277+
`helpers/mcp/open-add-mcp-server-modal.ts:10` (`mcp-server-dropdown`,
278+
`locator.click: Timeout 3000ms exceeded`) — the #1335 signature, in a second
279+
file. Confirmed by a control run of the unmodified spec: same 2/3, same step.
280+
Raising that budget to 30 s locally did not help under `--workers=2+`, where
281+
the dropdown simply never becomes clickable; a 4-worker burst of this spec
282+
fails 3/4 there, always before the re-open. That budget belongs to #1335 and is
283+
deliberately untouched here — it is a shared MCP helper with other callers.
284+
251285
- **Why `npx` and not `uvx` for the servers that must really start.** Before
252286
#1091 tests 1/2/5 registered `uvx mcp-server-fetch` / `mcp-server-time`.
253287
Splitting those into `command` + `args` gets past the new validation but the

tests/tests-automations/regression/mcp/server/mcp-server.spec.ts

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { openAddMcpServerModal } from "../../../../helpers/mcp/open-add-mcp-serv
66
import { zoomOut } from "../../../../helpers/ui/zoom-out";
77
import { getAuthToken } from "../../../../helpers/auth/get-auth-token";
88
import { deleteFlow } from "../../../../helpers/flows/delete-flow";
9+
import { openFlowById } from "../../../../helpers/flows/open-flow-by-id";
910

1011
/**
1112
* Add-MCP-Server modal: stdio / HTTP registration, field persistence and tool
@@ -790,7 +791,48 @@ test(
790791
await page.waitForSelector('[data-testid="blank-flow"]', {
791792
timeout: 30000,
792793
});
794+
// The flow under test has to be addressed by id from here on (#1340), and
795+
// the id has to satisfy BOTH sources — neither alone is enough here:
796+
//
797+
// - `page.url()` alone is the documented trap. `awaitBootstrapTest` reaches
798+
// the templates modal through "New Flow", so before the blank-flow
799+
// navigation the URL still carries the bootstrap PLACEHOLDER — the flow
800+
// Langflow deletes the moment the modal navigates elsewhere, and the one
801+
// authoring-conventions Pattern A warns about (#681/#505).
802+
// - the tracked `POST /flows` 201 ids alone do not say which flow the editor
803+
// ended up on: this page creates the placeholder AND the blank flow, so
804+
// picking one means trusting arrival order of two async body reads, and
805+
// the wrong pick is precisely the id that gets deleted.
806+
//
807+
// So: poll until the editor's URL carries an id this page is known to have
808+
// created and that is not the placeholder. A transient or client-only id
809+
// cannot satisfy the membership test, and a blank-flow click that never
810+
// navigates fails HERE, naming the cause, instead of surfacing later as an
811+
// unattributed timeout. Measured on nightly 1.12.0.dev18: the click issues
812+
// its own `POST /flows` 201 and the URL changes every time (5/5) — the
813+
// placeholder is never reused — so this is about attribution, not a defect.
814+
const placeholderId = new URL(page.url()).pathname.match(
815+
/\/flow\/([0-9a-f-]{36})/,
816+
)?.[1];
793817
await page.getByTestId("blank-flow").click();
818+
const editorFlowId = () =>
819+
new URL(page.url()).pathname.match(/\/flow\/([0-9a-f-]{36})/)?.[1];
820+
await expect
821+
.poll(
822+
() => {
823+
const id = editorFlowId();
824+
return !!id && id !== placeholderId && createdFlowIds.includes(id);
825+
},
826+
{
827+
timeout: 30000,
828+
message:
829+
"the blank-flow click never landed the editor on a newly created " +
830+
"flow: the URL still holds the bootstrap placeholder, or its id is " +
831+
"not among this page's POST /api/v1/flows 201 responses",
832+
},
833+
)
834+
.toBe(true);
835+
const flowUnderTest = editorFlowId()!;
794836
await page.getByTestId("sidebar-nav-mcp").click();
795837
await page.waitForSelector(
796838
'[data-testid="add-component-button-lf-starter_project"]',
@@ -955,17 +997,16 @@ test(
955997

956998
await awaitBootstrapTest(page, { skipModal: true });
957999

958-
// The /flows a11y refactor (Langflow #13891) makes `flow-name-div`
959-
// `pointer-events-none`; open the flow via the card's overlay button.
960-
const flowOpenButton = page
961-
.getByTestId("list-card")
962-
.filter({
963-
has: page.getByTestId("flow-name-div").filter({ hasText: "New Flow" }),
964-
})
965-
.getByTestId("list-card-open-button")
966-
.first();
967-
await flowOpenButton.waitFor({ state: "visible", timeout: 10000 });
968-
await flowOpenButton.click();
1000+
// By id, never a name-filtered `list-card` + `.first()` (#1340). Langflow
1001+
// names every blank flow "New Flow"/"New Flow (N)", so under `fullyParallel`
1002+
// that filter resolves whichever card the shared project's list puts first.
1003+
// Measured on nightly 1.12.0.dev18: seeding ONE competing "New Flow …" in
1004+
// this project before the list fetch is enough — the click opened the
1005+
// competitor, and the test then died on the `text="MCP Tools"` wait below,
1006+
// blaming the node for a flow it was never in. `openFlowById` also seeds the
1007+
// assistant-onboarding flag and gates on the flow being writable, which the
1008+
// card click never did (#1214/#1005).
1009+
await openFlowById(page, flowUnderTest);
9691010

9701011
// Wait for the MCP Tools component to be visible on canvas
9711012
await page.waitForSelector('text="MCP Tools"', {
@@ -1076,16 +1117,8 @@ test(
10761117

10771118
await awaitBootstrapTest(page, { skipModal: true });
10781119

1079-
// See note above: open the flow via the card's overlay button.
1080-
const flowOpenButton2 = page
1081-
.getByTestId("list-card")
1082-
.filter({
1083-
has: page.getByTestId("flow-name-div").filter({ hasText: "New Flow" }),
1084-
})
1085-
.getByTestId("list-card-open-button")
1086-
.first();
1087-
await flowOpenButton2.waitFor({ state: "visible", timeout: 10000 });
1088-
await flowOpenButton2.click();
1120+
// See note above: by id, not by name.
1121+
await openFlowById(page, flowUnderTest);
10891122

10901123
// Wait for the MCP Tools component to be visible on canvas
10911124
await page.waitForSelector('text="MCP Tools"', {

0 commit comments

Comments
 (0)