Skip to content

Split the wide generated diagrams into subVIs and remove overlaps - #45

Merged
JanGoebel merged 5 commits into
mainfrom
tidy-block-diagrams
Oct 25, 2026
Merged

JanGoebel merged 5 commits into
mainfrom
tidy-block-diagrams

Conversation

@JanGoebel

@JanGoebel JanGoebel commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner

Summary

The VIs generated since v3.0.0 had block diagrams 2,200 to 3,800 px wide and tangled, four hand-drawn diagrams had objects on top of each other, and Select Value.vim was installed as a loose file outside the library. The wide VIs are now built on named private subVIs, the overlaps are moved apart, and the .vim is replaced by LabVIEW's Select function. Behaviour and every connector pane are unchanged.

VI Diagram width before → after
Report Progress (public) 3266 → about 830 px
Cancel Requested (public) 2921 → about 740 px
Ask User (public) 2540 → 1381 px
Complete Modern MCP Result 3770 → about 990 px
Run Streamed Request 2216 → 770 px
Get Request Stream 1380 → 831 px

Changes

Public VIs, rebuilt on subVIs (connector panes, defaults and descriptions identical):

  • Report Progress.vi, Cancel Requested.vi (modified): use Get Request Stream, Read/Write Stream State, Build Progress Event and Send Stream Event; Report Progress also Should Send Progress. Cancel Requested now writes the stream state on every streamed call instead of only on a change; the stored content is the same.
  • Ask User.vi (modified): uses Read Request Context and Register Input Request; the registration is in a case frame labelled "Register the question".

Private VIs, rebuilt on subVIs:

  • Run Streamed Request.vi (modified): uses Build Stream Object, Stream Was Cancelled, Finish Stream. Reentrant as before.
  • Complete Modern MCP Result.vi (modified): uses Read Server Info, Build Discover Response, Complete Handler Result, Input Required Response and Response Status Code.
  • Get Request Stream.vi (modified): uses Read Request Context.

New private subVIs (Private/SubVIs, LabVIEW 20.0, icon from New-VIIcon.ps1, VI description):

  • Read Request Context.vi: the running request's "MCP Request Context" queue element, and whether there is one.
  • Register Input Request.vi: queues an elicitation/create question.
  • Read Stream State.vi: cancelled? (stored state, stream closed or legacy cancel), last progress, last probe.
  • Write Stream State.vi: stores the stream state; does nothing outside a streamed call.
  • Build Progress Event.vi: the notifications/progress event text.
  • Send Stream Event.vi: queues one SSE event, waits up to 1 s for the stream to take it, reports whether the client is gone.
  • Build Stream Object.vi: the stream object for the SSE response.
  • Stream Was Cancelled.vi: after the handler returns, whether the stream was cancelled.
  • Finish Stream.vi: final data event and end marker, waits up to 5 s, releases both queues.
  • Read Server Info.vi: serverInfo, _meta and capabilities from initialize.
  • Build Discover Response.vi: the server/discover result.
  • Add Result Fields.vi: resultType, serverInfo _meta and cache hints.
  • Remap Resource Not Found.vi: -32002 becomes -32602 for resources/read.
  • Complete Handler Result.vi: completes the handler's response with Add Result Fields, or with Remap Resource Not Found when there is no result.
  • Input Required Response.vi: the input_required response of a tool call waiting for elicitation answers.
  • Response Status Code.vi: 202 for a notification without a response, otherwise 200.
  • Add Cache Hints.vi: ttlMs and cacheScope for lists and resources/read.
  • Should Send Progress.vi: whether Report Progress sends an event.

The generated diagrams are laid out with LabVIEW's Clean Up Diagram where that reads better; Cancel Requested, Ask User and Process HTTP Request keep their earlier layout, which reads better than Clean Up's.

Moves only (no code change):

  • Start.vi: Allowed clients / Access token terminals and Server Access Settings no longer overlap.
  • Add Tool.vi: title and annotations terminals moved left with the other inputs; a constant moved off the wires.
  • Add Prompt.vi: Check MCP Names, the error ring, Select and Or moved apart.
  • Get Connector Pane Info.vi: a subVI moved off a case structure's border.

Process HTTP Request and Select Value.vim:

  • Process HTTP Request.vi (modified): the Select Value.vim call is replaced by the Select function; nodes stacked in three places and Set Header drawn over a case structure are moved apart.
  • Select Value.vim (deleted): it was not a library member and had no other caller.
  • MCP Server.lvlib (modified): the 18 new subVIs added to Private/SubVIs.

Code summary

Orange numbered boxes in each picture match the numbered points below it. Click a picture to see it full size. Every changed spot and every case frame is in the detailed VI diff report.

VI Status Change in one line
Report Progress.vi modified Built on six subVIs (read/write stream state, decide, build and send the progress event); 3266 → about 830 px
Cancel Requested.vi modified Built on the same stream-state and send subVIs; writes the stream state on every streamed call, same content
Ask User.vi modified Uses Read Request Context and Register Input Request; 2540 → 1381 px
Get Request Stream.vi modified Reads the request context through Read Request Context; 1380 → 831 px
Read Request Context.vi added The running request's "MCP Request Context" element, and whether there is one
Register Input Request.vi added Queues an elicitation/create question on "MCP Input Requests"
Read Stream State.vi added Cancelled? (stored state, closed stream or legacy cancel), last progress, last probe
Write Stream State.vi added Stores key, last progress, cancelled and probe; nothing outside a streamed call
Build Progress Event.vi added The notifications/progress event text, with total and message only when set
Send Stream Event.vi added Queues one SSE event, waits up to 1 s, reports whether the client is gone
Should Send Progress.vi added Whether Report Progress sends an event
Complete Modern MCP Result.vi modified Built on five named subVIs; diagram 3,770 → about 990 px wide
Complete Handler Result.vi added Completes the handler's response, or remaps "resource not found" when there is no result
Add Result Fields.vi added Adds resultType, the serverInfo _meta and cache hints
Add Cache Hints.vi added Adds ttlMs and cacheScope for lists and resources/read
Remap Resource Not Found.vi added -32002 becomes -32602 for resources/read
Read Server Info.vi added serverInfo, _meta and capabilities from initialize
Build Discover Response.vi added Builds the server/discover result
Input Required Response.vi added The input_required response of a tool call waiting for elicitation answers
Response Status Code.vi added 202 for a notification without a response, otherwise 200
Run Streamed Request.vi modified Built on three named subVIs, still reentrant; diagram 2,216 → 770 px wide
Build Stream Object.vi added The stream object for the SSE response
Stream Was Cancelled.vi added After the handler returns, whether the stream was cancelled
Finish Stream.vi added Final data event and end marker, waits up to 5 s, releases both queues
Process HTTP Request.vi modified Select function instead of Select Value.vim; stacked nodes and Set Header moved apart
Start.vi modified Moves only: Allowed clients / Access token terminals no longer overlap Server Access Settings
Add Tool.vi modified Moves only: title and annotations terminals moved left, a constant moved off the wires
Add Prompt.vi modified Moves only: Check MCP Names, the error ring, Select and Or no longer overlap
Get Connector Pane Info.vi modified Moves only: a subVI moved off a case structure's border
Select Value.vim deleted Replaced by LabVIEW's Select function in Process HTTP Request

Report Progress.vi — modified

Same behaviour, now built on six named subVIs; the diagram was 3266 px wide and is now about 830 px.

  1. Get Request Stream finds the stream of the running request, as before.
  2. New: Read Stream State reads whether the call is cancelled (stored state, stream closed or legacy notifications/cancelled), the last progress sent and the last probe.
  3. New: Should Send Progress decides whether this call sends a progress event at all.
  4. New: Build Progress Event builds the notifications/progress event text from progress, total and message.
  5. New: Send Stream Event queues the event, waits up to 1 s for the stream to take it and reports whether the client is gone.
  6. New: Write Stream State stores the updated stream state; outside a streamed call it does nothing.

Cancel Requested.vi — modified

Same answer, built on the same subVIs as Report Progress; the diagram went from 2921 to about 740 px.

  1. Get Request Stream finds the stream of the running request.
  2. New: Read Stream State says whether the call is already cancelled and when the stream was last probed.
  3. New: Send Stream Event sends the keep-alive comment as a probe and reports whether the client is gone.
  4. New: Write Stream State now stores the stream state on every streamed call, not only when it changed. What is stored is the same as before.

Ask User.vi — modified

Same behaviour; the request context read and the question registration are now subVIs, and the diagram went from 2540 to 1381 px.

  1. New: Read Request Context reads the running request's "MCP Request Context" queue element and says whether there is one. It replaces the Obtain Queue / Preview Queue Element / Release Queue chain.
  2. The case structure labelled "Register the question". Its visible False frame only passes data through; the True frame is below.

  1. New: Register Input Request queues the elicitation/create question on "MCP Input Requests". Before, the schema check and the enqueue were about twenty nodes inline.

Get Request Stream.vi — modified

The queue read at the start is now done by Read Request Context; the JSON lookups of the stream fields are unchanged, and the diagram shrinks from 1380 to 831 px.

Before:

  1. Removed: obtaining "MCP Request Context" without creating it, previewing its element with no wait, and releasing the queue.
  2. Removed: merging and clearing the queue errors…
  3. Removed: …and merging and clearing the errors of the JSON lookups at the end. error out was, and still is, error in passed through.

After:

  1. New: Read Request Context.vi gives the context text of the running request; the same lookups ($.stream, $.events, $.cancelled, $.id, $.progressToken) read it as before.

Read Request Context.vi — added

Reads the "MCP Request Context" queue element of the running request and says whether there is one; used by Get Request Stream and Ask User.

  1. Obtains the queue without creating it and previews its element with no wait, so the context stays in the queue.
  2. Releases the queue reference and clears the queue errors; error in passes straight to error out.
  3. found? is true only if the preview did not time out and there was no error.

Register Input Request.vi — added

Queues an elicitation/create question on "MCP Input Requests"; used by Ask User.

  1. The schema sent is requestedSchema if it is not empty and is a JSON object, otherwise the confirmation schema {"type":"object","properties":{}}.
  2. The queue element is {<key>:{"method":"elicitation/create","params":{"mode":"form","message":<message>,"requestedSchema":<schema>}}}, with key and message JSON-encoded.
  3. Obtains "MCP Input Requests", creating it if needed…
  4. …enqueues the element and releases the reference; its errors are merged into error out.

Read Stream State.vi — added

Reads the stored state of the streamed call: whether it is cancelled, the last progress sent and the last probe; used by Report Progress and Cancel Requested.

  1. Reads the stored state text from Stream State.vi.
  2. Previews the stream's cancelled queue with no wait: the stream was closed.
  3. The stored state counts only if stream key is not empty and the stored $.key equals it.
  4. True frame: $.cancelled equal to true, $.last as a number (NaN if it is not a JSON number) and $.probe as U32. The False frame returns not cancelled, NaN and 0.
  5. cancelled? is the stored flag, or the closed stream, or a legacy notifications/cancelled from Legacy Cancellations.vi.

Write Stream State.vi — added

Stores the stream state of a streamed call; its True frame (empty stream key, not a streamed call) only passes the error through. The False frame:

  1. last is the progress as JSON, or null while it is NaN.
  2. cancelled is written as true or false.
  3. {"key":…,"last":…,"cancelled":…,"probe":…} is joined and stored with Stream State.vi (store = true).

Build Progress Event.vi — added

Builds the text of one notifications/progress SSE event; used by Report Progress.

  1. The fixed start: data: {"jsonrpc":"2.0","method":"notifications/progress","params":{"progressToken":, then the token and ,"progress": with the JSON number.
  2. ,"total": is added only if total is not NaN and greater than 0.
  3. ,"message": is added only if message is not empty.
  4. Everything is joined and closed with }}.

Send Stream Event.vi — added

Queues one SSE event and reports whether the client is gone; with send? false (False frame) it sends nothing and returns client gone? false. The True frame:

  1. Enqueues the event on the stream's events queue.
  2. Wait For Stream Drain.vi waits up to 1 s for the stream to take it; its errors are cleared.
  3. client gone? is true if the stream did not take the event in time.

Should Send Progress.vi — added

Decides whether Report Progress sends a progress event.

  1. The progress must be new: no progress sent yet (last is NaN), or more than the last one.
  2. Nothing is sent if the call is cancelled, the progress is NaN or there is an error.
  3. send? also needs a streamed call (streaming?).

Complete Modern MCP Result.vi — modified

The whole diagram was one generated block 3,770 px wide; it is now about 990 px, built on five named subVIs. The three Selects on the right choose the final response from their outputs, as before.

  1. New: Read Server Info.vi runs initialize internally and returns serverInfo, its _meta and the capabilities.
  2. New: Build Discover Response.vi builds the server/discover result from them.
  3. New: Complete Handler Result.vi completes the handler's response: result fields and cache hints, or the remapped "resource not found" error.
  4. New: Input Required Response.vi builds the input_required response of a tool call that waits for elicitation answers, and says whether that applies.
  5. New: Response Status Code.vi gives 202 for a notification without a response, otherwise 200.

Complete Handler Result.vi — added

Completes the handler's response: with Add Result Fields when it has a result, otherwise with Remap Resource Not Found.

  1. Looks up $.result in the handler response; whether it is there decides which of the two outputs the Select passes on.
  2. Adds resultType, the serverInfo _meta and cache hints.
  3. Changes a resources/read "not found" error (-32002) to -32602.

Add Result Fields.vi — added

Adds resultType, the serverInfo _meta and the cache hints to a result.

  1. Sets $.result.resultType to "complete".
  2. serverInfo _meta: when the result already has _meta, the serverInfo entry is set inside it; otherwise a new _meta object holding it is set. The Select picks between the two.
  3. Add Cache Hints.vi adds ttlMs and cacheScope for the methods that get them.

Add Cache Hints.vi — added

Adds ttlMs and cacheScope to the result of list methods and resources/read.

  1. Cache Hints.vi gives the hints for this method and whether there are any.
  2. ttlMs (as a decimal number) and cacheScope (as a JSON string) are set in $.result.
  3. The Select passes the response with hints only when the method has hints; otherwise the response is unchanged.

Remap Resource Not Found.vi — added

For resources/read, turns the "resource not found" error code -32002 into -32602 (invalid params).

  1. Reads $.error.code and compares it with -32002.
  2. Checks that the method is resources/read; both checks must be true.
  3. Sets $.error.code to -32602. The Select passes this on only when both checks are true; otherwise the response is unchanged.

Read Server Info.vi — added

Reads serverInfo, its _meta and the capabilities from the server's own initialize answer.

  1. Builds an initialize request (protocol 2025-11-25, client "Handle MCP Request").
  2. Runs it only when read? is true; the visible False frame passes the empty no initialize text instead.
  3. Reads capabilities, serverInfo.name and serverInfo.version from the answer and builds serverInfo and the _meta object holding it.

The True frame:

  1. initialize.vi answers the request.

Build Discover Response.vi — added

Builds the server/discover result as one JSON text.

  1. supportedVersions: the list from Supported Protocol Versions.vi, as JSON.
  2. ttlMs and cacheScope from Cache Hints.vi for server/discover.
  3. Joins id, resultType: "complete", the versions, capabilities, _meta and the cache hints.

Input Required Response.vi — added

Builds the input_required response of a tool call that waits for elicitation answers.

  1. input required? is true when the method is tools/call and there are input requests.
  2. Joins id, resultType: "input_required", the inputRequests and _meta into the response.

Response Status Code.vi — added

The HTTP status of the answer.

  1. True when the response is empty and the request has no id: a notification without a response.
  2. Then 202, otherwise 200.

Run Streamed Request.vi — modified

The generated diagram was 2,216 px wide; it is now 770 px, built on three named subVIs. It stays reentrant, so concurrent streams do not wait for each other.

  1. New: Build Stream Object.vi builds the stream object handed to the SSE response from the two queues.
  2. New: Stream Was Cancelled.vi checks, after the handler returns, whether the stream was cancelled. If so, the Select passes the empty no response text to Finish Stream instead of the handler's response.
  3. New: Finish Stream.vi sends the final data event and the end marker, waits for the stream to take them and releases both queues.

Build Stream Object.vi — added

Builds the stream object {"events":…,"cancelled":…} from the two queue references.

  1. Each queue reference is type cast to a number and written as decimal text.
  2. Joins them into the object.

Stream Was Cancelled.vi — added

After the handler returns: was the stream cancelled? Any one of three signs is enough.

  1. The cancelled flag queue, previewed without waiting.
  2. The stored stream state: its key matches this stream and its cancelled is true.
  3. A legacy notifications/cancelled for this request's id, from Legacy Cancellations.vi.
  4. The Ors combine the three into cancelled?.

Finish Stream.vi — added

Ends the SSE stream: final data event, end marker, wait, release.

  1. The response with CR and LF removed, as one data: event.

  2. When the response is empty, the visible True frame sends nothing. The False frame enqueues the event on events:

    1. Enqueue Element with the data event.
  3. Enqueues the end marker, waits up to 5 s for the stream to take it (Wait For Stream Drain.vi) and releases both queues.

Process HTTP Request.vi — modified

The Select Value.vim call is replaced by LabVIEW's Select function, and nodes that were drawn on top of each other are moved apart; nothing else changes.

Before After
  1. Removed: Select Value.vim, a loose malleable VI outside the library, used only here to choose between two objects of the same class. New: the Select function does the same, so the .vim is deleted.
Before After
  1. The client-access reads: Get Request, Get Client URI and the Authorization header read were stacked on and around Validate HTTP Request. They now have their own place at the top left, with Get Host next to Get Client URI.
  2. The Mcp-Method and Mcp-Name header reads were stacked on each other. They now stand side by side.
  3. The streaming nodes (Create MCP SSE Response, Prepare Request Dispatch, Start Streamed Request) were hidden under other objects and do not show in the before picture at all. They now sit to the left of the case structure.
Before After
  1. Set Header was drawn over the case structure. It now sits to the left of it.

Start.vi — modified

Objects moved only; the code is unchanged.

Before After
  1. The Allowed clients and Access token terminals lay on top of each other and under Server Access Settings. They now sit side by side above it, each with its label readable.

Add Tool.vi — modified

Objects moved only; the code is unchanged.

Before After
  1. The title and annotations terminals sat far below the diagram. They now sit on the left with the other inputs.
  2. The kind constant lay on wires. It now sits in a free spot below them.

Add Prompt.vi — modified

Objects moved only; the code is unchanged.

Before After
  1. The two Check MCP Names calls, the error ring, Select and Or overlapped. They now stand apart in one row, with the error ring below them.

Get Connector Pane Info.vi — modified

Objects moved only; the code is unchanged.

Before After
  1. Add Control Details To Schema sat on the bottom border of the "Array" case structure. It now sits below the structure.

Select Value.vim — deleted

A loose malleable VI outside MCP Server.lvlib, used only in Process HTTP Request; it is replaced there by LabVIEW's Select function.

Verified

All in LabVIEW 2026 64-bit on this machine, on the final files:

  • Full Caraya suite (tests\Run_Tests_CLI.vi through LabVIEW CLI): 64 of 64 tests passed, exit code 0.
  • VI package built locally with vipm build in LabVIEW 2020 64-bit from a clean worktree of ee7ee96, as the test-build job does: built in 265 s. The first CI test build of this PR failed with VIPM error 1004 ("The VI is not in memory"): 15 VIs rebuilt by script were listed in the library but did not record their owning library themselves. LabVIEW now writes that link in all of them (checked in every .vi of src/MCP Server).
  • Protocol tests (tests\Run_Protocol_Tests_CLI.vi): 24 of 24 required checks passed, 0 advisory failures.
  • The suite catches regressions here: the first run of it on this branch failed Tests 58–60 (progress and cancellation) because the regenerated Run Streamed Request.vi had lost its reentrant setting, so Open VI Reference with option 8 failed and every streamed call ended at once. Setting it reentrant again made all 64 pass.
  • Every changed and new VI: executable, saved as LabVIEW 20.0 source-only, member of MCP Server.lvlib, no "insane items" save flag, own icon in the toolkit style (read from the files). Public connector panes compared with main: identical.
  • Execution flags compared with main: identical for Report Progress, Cancel Requested, Add Tool, Add Prompt, Start, Process HTTP Request and Get Connector Pane Info. Automatic error handling is off on all new subVIs, as on the VIs they came from.

Not verified

  • Nothing was opened in LabVIEW 2020; the CI run of this pull request covers that.
  • The Datalogger example was not started; the protocol tests ran against the test server only.
  • The private subVIs could not be run on their own (LabVIEW refuses to run a community-scoped VI from outside the library, error 1390); they are covered by the suite only.

Known rough edges

  • Process HTTP Request keeps its hand-moved layout: no node sits on another any more, but long wire loops remain. Clean Up Diagram made it wider and more tangled, so it was not applied; a hand pass in the editor would help.
  • Clean Up Diagram layouts are LabVIEW's own: some wires still cross in Complete Modern MCP Result and Report Progress.
  • Ask User could be split into further subVIs; its layout was kept because Clean Up made it bigger.
  • Several regenerated VIs have front-panel control style 0 instead of 4 (cosmetic, panels of private VIs).

🤖 Generated with Claude Code

CalmyJane and others added 4 commits October 25, 2026 08:18
The three public VIs were generated as single diagrams 2,500 to 3,300 px
wide, which users open when they want to see how progress, cancellation
and elicitation work. They are now built on small private subVIs, and
their diagrams are 750 to 1,400 px wide. Connector panes, defaults,
descriptions and behaviour are unchanged.

New in Private/SubVIs:
- Read Request Context: reads the "MCP Request Context" queue element of
  the running request and says whether there is one.
- Register Input Request: queues an elicitation/create question on
  "MCP Input Requests".
- Read Stream State: whether the call is cancelled (stored state, stream
  closed or legacy notifications/cancelled), the last progress and probe.
- Write Stream State: stores key, last progress, cancelled and probe;
  does nothing outside a streamed call.
- Build Progress Event: builds the notifications/progress event text.
- Send Stream Event: queues one SSE event, waits up to 1 s for the stream
  to take it and reports whether the client is gone.

Get Request Stream now uses Read Request Context. Cancel Requested writes
the stream state on every streamed call instead of only on a change; the
stored content is the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both private VIs were generated as tangled diagrams 2,200 and 3,800 px
wide. They are now built on named subVIs and laid out: 770 and 1,280 px.
Connector panes and behaviour are unchanged; Run Streamed Request and its
new subVIs are reentrant, so concurrent streams do not wait for each
other.

New in Private/SubVIs:
- Build Stream Object: the stream object handed to the SSE response.
- Stream Was Cancelled: after the handler returns, whether the stream was
  cancelled (flag queue, stream state or legacy cancellation).
- Finish Stream: sends the final data event and the end marker, waits up
  to 5 s for the stream to take them and releases both queues.
- Read Server Info: serverInfo, _meta and capabilities from initialize.
- Build Discover Response: the server/discover result.
- Add Result Fields: resultType, the serverInfo _meta and cache hints.
- Remap Resource Not Found: -32002 becomes -32602 for resources/read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
….vim

Select Value.vim was a loose malleable VI, not a member of MCP Server.lvlib,
used once in Process HTTP Request to choose between two objects of the same
class. LabVIEW's Select function does the same, so the .vim is gone and is
no longer installed as a loose file in vi.lib.

The diagram had nodes stacked on top of each other in three places (the
client-access header reads, the streaming nodes, the Mcp-Method/Mcp-Name
reads) and Set Header drawn over a case structure. They are moved apart;
nothing else in the VI changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Objects drawn on top of each other since the recent changes:
- Start: the Allowed clients and Access token terminals and Server Access
  Settings.
- Add Tool: the title and annotations terminals now sit on the left with
  the other inputs; a constant was lying on wires.
- Add Prompt: Check MCP Names, an error ring, Select and Or overlapped.
- Get Connector Pane Info: a subVI sat on a case structure's border.

Objects were only moved; the code is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Tests

✅ All 64 tests passed.

Per-test results
Test VI Result Checks
Test 1: Initialize Test Initialize.vi ✅ pass Assert True: pass
Test 2: Initialized Test Initialized.vi ✅ pass Assert True: pass
Test 3: tools/list Test tools list.vi ✅ pass Assert True: pass
Test 4: prompts/list test prompts list.vi ✅ pass Assert True: pass
Test 5: resources/list test resources list.vi ✅ pass Assert True: pass
Test 6: tools call test tools call.vi ✅ pass Assert True: pass
Test 7: empty JSON object is an invalid request test empty msg.vi ✅ pass Assert True: pass
Test 8: call broken tool test broken tool.vi ✅ pass Assert True: pass
Test 6: tools call test tools call array.vi ✅ pass Assert True: pass
Test 9: GET is rejected with 405 test http get rejected.vi ✅ pass GET -> 405 with Allow: POST and error -32600: pass
Test 10: Origin validation test http origin.vi ✅ pass Origin http://evil.example -> 403 with error -32600: pass
Origin http://localhost:6274 -> 200 with result: pass
Test 11: MCP-Protocol-Version validation test http protocol version.vi ✅ pass MCP-Protocol-Version 1999-01-01 -> 400 with error -32022 (Unsupported protocol version, the MCP 2026-07-28 code in both eras): pass
MCP-Protocol-Version 2025-06-18 -> 200 with result: pass
Test 12: notification answered with 202 test notification accepted.vi ✅ pass notifications/initialized -> 202 with empty body: pass
Test 13: request with id answered with 200 test request status.vi ✅ pass tools/list with id 1 -> 200 with result and id 1: pass
Test 14: unknown method test unknown method.vi ✅ pass does/not/exist with id 5 -> 200 with error -32601 and id 5: pass
Test 15: unknown notification test unknown notification.vi ✅ pass notifications/cancelled -> 202 with empty body: pass
Test 16: ping test ping.vi ✅ pass ping with id 3 -> 200 with result {} and id 3: pass
Test 17: string request id test string id.vi ✅ pass tools/list with id "abc-1" -> 200 with result and id "abc-1": pass
Test 18: unknown prompt test unknown prompt.vi ✅ pass prompts/get no-such-prompt with id 7 -> JSON-RPC error -32602 with id 7, no status/source: pass
Test 19: unknown resource test unknown resource.vi ✅ pass resources/read of an unknown uri with id 8 -> JSON-RPC error -32002 with id 8, no status/source: pass
Test 20: resources/read returns UTF-8 text unchanged test resource text utf8.vi ✅ pass text of labview://fixture_utf8 decodes to the bytes of fixture_utf8.txt: pass
Test 21: resources/read returns a binary file as Base64 test resource blob.vi ✅ pass blob of labview://fixture_binary is the Base64 of the bytes of fixture_binary.png: pass
Test 22: unknown tool test unknown tool.vi ✅ pass tools/call no_such_tool -> error -32602 with message Unknown tool: no_such_tool and id 22: pass
Test 23: tools/call with arguments that are not an object test tool call without arguments.vi ✅ pass tools/call fake_tool_add with arguments 5 -> error -32602 with message Invalid arguments... and id 23: pass
Test 24: broken tool is a tool execution error test broken tool message.vi ✅ pass tools/call fake_tool_broken_vi -> result with isError true and a text that says not executable: pass
Test 25: prompts/get without arguments test prompt without arguments.vi ✅ pass prompts/get fake_prompt_concatanate_strings without arguments -> result with messages: pass
Test 26: request without method test request without method.vi ✅ pass a message with id 26 and no method -> error -32600 with id 26: pass
Test 27: malformed JSON test malformed json.vi ✅ pass truncated JSON -> error -32700 with id null: pass
Test 28: array inputs say what their items are test tools list array items.vi ✅ pass fake_tool_arrays: pass
Test 29: protocol version negotiation test protocol version negotiation.vi ✅ pass initialize asking for 2024-11-05 -> protocolVersion 2024-11-05: pass
initialize asking for 1999-01-01 -> protocolVersion 2025-11-25 (the newest legacy version): pass
Test 30: resources/templates/list test resource templates list.vi ✅ pass resources/templates/list -> result with resourceTemplates []: pass
Test 31: tools/call without arguments test tool call default arguments.vi ✅ pass tools/call fake_tool_add without arguments -> result with the sum 0, no error, id 31: pass
Test 32: a tool that returns an error sets isError test tool error result.vi ✅ pass fake_tool_return_error with status true -> result with isError true, code and source still in the content: pass
fake_tool_return_error with status false -> result without isError: pass
Test 33: internal errors show no LabVIEW internals test internal error message.vi ✅ pass prompts/get fake_prompt_broken_vi -> error -32603 with a message without call chain, markup, tags or path: pass
Test 34: prompts/list says which arguments are required test prompts list required.vi ✅ pass fake_prompt_required_argument: pass
Test 35: prompts/get checks required arguments test prompt required argument.vi ✅ pass prompts/get fake_prompt_required_argument without topic -> error -32602 that names topic, id 36: pass
prompts/get fake_prompt_required_argument with topic -> result whose text contains both argument values: pass
Test 36: JSON-RPC batch test batch.vi ✅ pass batch of ping (id 38), a notification and tools/list (id b) -> 200 with an array of two answers in that order: pass
batch of two notifications -> 202 with empty body: pass
empty batch -> error -32600 with id null: pass
Test 37: a JSON value that is not an object test non object message.vi ✅ pass body 5 -> error -32600 with id null: pass
body "text" -> error -32600 with id null: pass
body null -> error -32600 with id null: pass
Test 38: tools/call checks required arguments test tool required arguments.vi ✅ pass tools/call fake_tool_required_input without arguments -> result with isError true and the text Missing required argument(s): b, a (the order of inputSchema.required), no JSON-RPC error, id 40: pass
tools/call fake_tool_required_input with arguments {} -> result with isError true naming b and a, no JSON-RPC error, id 41: pass
tools/call fake_tool_required_input with only a -> result with isError true naming only b, no JSON-RPC error, id 42: pass
tools/call fake_tool_required_input with a 2 and b 3 -> result 6, no error, id 43: pass
Test 39: initialize with an unknown protocol version test protocol version fallback.vi ✅ pass initialize asking for 1999-01-01 -> protocolVersion is the first entry of Supported Protocol Versions.vi: pass
Test 40: initialize reports the LabVIEW version test labview version.vi ✅ pass initialize -> serverInfo.additionalDetails.labviewVersion equals the Version Number of this LabVIEW: pass
Test 41: tools/list shows the MCP names of special labels test tools list mcp names.vi ✅ pass tools/list fake_tool_special_labels -> Length (mm) and Factor #1 & 50% are listed as length__mm_ and factor__1___50_ in properties and in required, id 44: pass
tools/list fake_tool_special_labels -> every name in properties and required matches ^[a-zA-Z0-9_-]{1,64}$: pass
Test 42: tools/call reaches special labels by their MCP names test tools call mcp names.vi ✅ pass tools/call fake_tool_special_labels with length__mm_ 2.5 and factor__1___50_ 4 -> result {"result__mm_":10}, no error, id 45: pass
Test 43: Add Tool refuses inputs whose MCP names clash test add tool name clash.vi ✅ pass Add Tool fake tool name clash.vi with the inputs a b and a_b -> error 5010 naming both controls and the MCP name a_b: pass
tools/list after the refused Add Tool -> fake_tool_name_clash is not listed, id 46: pass
Test 44: Add Prompt refuses arguments whose MCP names clash test add prompt name clash.vi ✅ pass Add Prompt fake prompt name clash.vi with the arguments a b and a_b -> error 5010 naming both controls and the MCP name a_b: pass
prompts/list after the refused Add Prompt -> fake_prompt_name_clash is not listed, id 47: pass
Test 45: 3D arrays and arrays of paths, enums and clusters say what their items are test tools list array shapes.vi ✅ pass fake_tool_array_shapes: pass
fake_tool_array_shapes: pass
fake_tool_array_shapes: pass
fake_tool_array_shapes: pass
Test 46: timestamps, clusters and types without a JSON type are described in tools/list test tools list schema details.vi ✅ pass fake_tool_array_shapes: pass
fake_tool_array_shapes: pass
fake_tool_schema_shapes: pass
fake_tool_schema_shapes: pass
fake_tool_schema_shapes: pass
fake_tool_schema_shapes: pass
fake_tool_schema_shapes: pass
Test 47: tools/call converts timestamps, clusters, enums and 2D and 3D arrays test tools call schema shapes.vi ✅ pass fake_tool_array_shapes with a 3D array [[[1,2],[3,4]],[[5,6],[7,8]]] -> no isError, the tool gets all 8 elements: pass
fake_tool_schema_shapes: pass
fake_tool_schema_shapes: pass
fake_tool_schema_shapes: pass
fake_tool_schema_shapes with timestamp 2026-10-17T12:34:56Z -> no isError, time out 2026-10-17T12:34:56.000Z: pass
Test 48: every array schema in tools/list says what its items are test tools list every array has items.vi ✅ pass tools/list of the test server: pass
Test 49: tools/list gives element descriptions, integer ranges, data entry limits, units and defaults test tools list descriptions defaults ranges.vi ✅ pass fake_tool_schema_shapes: pass
fake_tool_schema_details: pass
fake_tool_schema_details: pass
fake_tool_schema_details: pass
fake_tool_schema_details: pass
fake_tool_schema_details: pass
fake_tool_schema_details: pass
fake_tool_schema_details: pass
Test 50: tools/call returns structuredContent without picture outputs, and none on isError results test tools call structured content.vi ✅ pass tools/call fake_tool_return_error with status false (id 502) -> structuredContent carries the error cluster output as an ordinary object: pass
tools/call fake_tool_required_input without arguments (id 503) -> isError true and no structuredContent: pass
fake_tool_picture_outputs: pass
fake_tool_picture_outputs: pass
tools/call fake_tool_return_error with status true (id 501) -> isError true and no structuredContent: pass
tools/call fake_tool_picture_outputs (id 50) -> structuredContent is present and equals the text JSON without its picture placeholder: pass
Test 51: tools/list gives an outputSchema that matches structuredContent test tools list output schema.vi ✅ pass fake_tool_picture_outputs: pass
tools/list fake_tool_picture_outputs -> outputSchema is an object schema whose properties and required are count, label, mode and time: pass
fake_tool_picture_outputs: pass
fake_tool_picture_outputs: pass
tools/list fake_tool_no_inputs_outputs (no outputs) -> no outputSchema: pass
Test 52: tools/list sends title and annotations only when they are set test tools list title annotations.vi ✅ pass tools/list fake_tool_add (registered without title and annotations) -> neither title nor annotations: pass
tools/list fake_tool_annotated (registered with a title) -> title "Annotated Fixture Tool": pass
fake_tool_annotated (readOnlyHint true, destructiveHint false, the others not set) -> annotations with exactly readOnlyHint true and destructiveHint false: pass
Test 53: modern results carry resultType, cache hints and serverInfo; legacy results do not test modern tools list.vi ✅ pass modern tools/list -> _meta io.modelcontextprotocol/serverInfo with name and version: pass
modern tools/list (id 53, MCP 2026-07-28 headers and _meta) -> 200 with tools, resultType complete, ttlMs 60000 and cacheScope public: pass
legacy tools/list (id 531, no _meta) -> tools without resultType, ttlMs, cacheScope or _meta: pass
Test 54: server/discover, and initialize never negotiates 2026-07-28 test server discover.vi ✅ pass initialize asking for 2025-11-25 (id 542) -> 2025-11-25: pass
server/discover -> capabilities tools, prompts and resources (as initialize), _meta serverInfo, ttlMs 60000, cacheScope public: pass
initialize asking for 2026-07-28 (id 541) -> the newest legacy version 2025-11-25, never 2026-07-28: pass
server/discover (id 54) -> 200, resultType complete, supportedVersions with 2026-07-28 and 2025-11-25: pass
Test 55: modern requests are refused when their headers do not match the body test modern headers.vi ✅ pass tools/call with an Mcp-Name that is not params.name (id 554) -> 400 with error -32020: pass
tools/call with Mcp-Name =?base64?...?= of fake_tool_add, lower-case header names (id 552) -> 200 with the tool result: pass
tools/call without an Mcp-Name header (id 551) -> 400 with error -32020: pass
Mcp-Method prompts/list on a tools/list request (id 55) -> 400 with error -32020: pass
MCP-Protocol-Version 2025-11-25 while _meta says 2026-07-28 (id 553) -> 400 with error -32020: pass
Test 56: unsupported versions, missing capabilities and unknown methods in the modern era test modern errors.vi ✅ pass modern resources/read of an unknown uri (id 565) -> error -32602 (legacy keeps -32002): pass
modern request with the legacy version 2025-11-25 in _meta (id 561) -> 400, error -32022, data.supported with 2025-11-25: pass
unknown version 2027-01-01 in header and _meta (id 56) -> 400, error -32022, data.supported with 2026-07-28, data.requested 2027-01-01: pass
modern ping (id 564), which MCP 2026-07-28 removed -> 404 with error -32601: pass
modern request without io.modelcontextprotocol/clientCapabilities (id 562) -> 400 with error -32602: pass
modern request with the unknown method foo/bar (id 563) -> 404 with error -32601: pass
Test 57: elicitation by input_required and retry with inputResponses test elicitation.vi ✅ pass legacy tools/call (id 574) -> outcome not supported: pass
modern tools/call of fake_tool_confirm with the elicitation capability (id 57) -> resultType input_required with inputRequests.confirm_action: pass
retry with inputResponses confirm_action decline (id 572) -> outcome declined: pass
modern tools/call without the elicitation capability (id 573) -> outcome not supported, no input_required: pass
retry with inputResponses confirm_action accept (id 571) -> complete result with outcome confirmed: pass
Test 58: progress notifications as Server-Sent Events for tools/call test progress notifications.vi ✅ pass modern tools/call with the numeric progressToken 583 (id 583) -> text/event-stream, 10 events with "progressToken":583 in order, then the complete result: pass
the tool finished all 10 steps (fixture record): pass
id 581: the JSON-RPC result is the last data event, after progress 10: pass
the same call without progressToken (id 582) -> plain application/json, no progress events: pass
id 581: exactly 10 notifications/progress events, each with progressToken "t58" and total 10, progress 1..10 in increasing order: pass
legacy tools/call with progressToken "t58" and Accept text/event-stream (id 581) -> HTTP 200, text/event-stream: pass
Test 59: closing the stream cancels a streamed tools/call test progress cancel by close.vi ✅ pass modern streamed tools/call over raw TCP (id 591) -> text/event-stream, the first progress event arrives: pass
after the client closed the connection the tool stopped early (fewer than 10 steps) and saw cancelled? TRUE: pass
Test 60: legacy notifications/cancelled stops a streamed tools/call test progress legacy cancel.vi ✅ pass legacy streamed tools/call over raw TCP (id 601) -> text/event-stream, the first progress event arrives: pass
the tool stopped early (fewer than 10 steps) and saw cancelled? TRUE: pass
the stream ended without a result for id 601: pass
notifications/cancelled for id 601 on a second connection -> HTTP 202 within 1 s, while the call runs: pass
Test 61: a client that is not in Allowed clients gets 403 test access allowed clients.vi ✅ pass the 403 body is the JSON-RPC error -32600 "Forbidden: client not allowed." with id null: pass
tools/list from this computer to a server that allows only 192.0.2.1 -> HTTP 403, application/json: pass
the second server (port 37061, Allowed clients = [192.0.2.1]) started: pass
Test 62: Access token requires Authorization: Bearer test access bearer token.vi ✅ pass the token never appears in a response: pass
Authorization: Bearer wrong-token -> HTTP 401 with WWW-Authenticate: Bearer: pass
the 401 body is the JSON-RPC error -32600 "Unauthorized: missing or invalid bearer token." with id null: pass
authorization: Bearer test-token-62 (lower-case header name) -> HTTP 200 with the tools/list result: pass
no Authorization header -> HTTP 401 with WWW-Authenticate: Bearer (header names compared case-insensitively): pass
the second server (port 37062, Access token set) started: pass
Authorization: bearer test-token-62 (lower-case scheme) -> HTTP 200 with the tools/list result, no WWW-Authenticate: pass
Test 63: tools/list leaves out LabVIEW's default error cluster descriptions test tools list error descriptions.vi ✅ pass fake_tool_error_descriptions: pass
tools/list fake_tool_error_descriptions -> error in (no error) and error out are in inputSchema and outputSchema without a description key, and so are their status, code and source; their type is still there: pass

Protocol tests

✅ Test-McpServer.ps1 passed against the test server.

Protocol tester output (0 failed required checks)
Testing MCP server at http://127.0.0.1:36987/mcp/server

[PASS] (required) Lifecycle - initialize answers 200 with a JSON result
[PASS] (required) Lifecycle - initialize answer is JSON-RPC 2.0 with the request id
[PASS] (required) Lifecycle - initialize result has protocolVersion, capabilities and serverInfo.name
[PASS] (required) Lifecycle - Content-Type of the answer is application/json
[PASS] (advisory) Lifecycle - a client asking for 2024-11-05 gets that version back
[PASS] (required) Lifecycle - notifications/initialized is accepted with 202 and no body
[PASS] (required) Transport - GET answers 405 or opens an event stream
[PASS] (advisory) Transport - 405 answer names the allowed method in an Allow header
[PASS] (required) Transport - a request from a foreign Origin is rejected (403)
[PASS] (required) Transport - a request from a localhost Origin is accepted
[PASS] (required) Transport - an unsupported MCP-Protocol-Version header is rejected (400)
[PASS] (required) Transport - the negotiated MCP-Protocol-Version header is accepted
[PASS] (required) JSON-RPC - ping is answered with an empty result
[PASS] (required) JSON-RPC - an unknown method gets error -32601 with the request id
[PASS] (required) JSON-RPC - an unknown notification is not answered
[PASS] (required) JSON-RPC - a string request id is returned unchanged
[PASS] (required) JSON-RPC - malformed JSON gets error -32700
[PASS] (advisory) JSON-RPC - a request without a method gets error -32600
[PASS] (required) Tools - tools/list returns a tools array
[PASS] (required) Tools - every tool has a name and an inputSchema of type object (1 tools)
[PASS] (advisory) Tools - array parameters say what their items are
[PASS] (required) Tools - calling an unknown tool is answered with an error
[PASS] (required) Prompts - prompts/list returns a prompts array
[PASS] (required) Prompts - every prompt has a name (1 prompts)
[PASS] (required) Prompts - getting an unknown prompt is answered with an error
[PASS] (required) Resources - resources/list returns a resources array
[PASS] (required) Resources - every resource has a uri and a name (2 resources)
[PASS] (required) Resources - reading an unknown resource is answered with an error
[PASS] (advisory) Resources - resources/templates/list returns a resourceTemplates array

Required: 24 of 24 passed. Advisory: 0 failed.

VI changes

Open the full VI diff

VIs: 11 modified, 18 added, 1 deleted

VI Changes Changed spot, now
Add Prompt.vi
src/MCP Server
cosmetic only
Add Tool.vi
src/MCP Server
cosmetic only
Ask User.vi
src/MCP Server
8
Cancel Requested.vi
src/MCP Server
1
Complete Modern MCP Result.vi
src/MCP Server
2
Get Connector Pane Info.vi
src/MCP Server
cosmetic only
5 more modified VIs
VI Changes Changed spot, now
Get Request Stream.vi
src/MCP Server
4
Process HTTP Request.vi
src/MCP Server
1
Report Progress.vi
src/MCP Server
4
Run Streamed Request.vi
src/MCP Server
2
Start.vi
src/MCP Server
cosmetic only
Added VIs (18)
VI Block diagram
Add Cache Hints.vi
src/MCP Server
Add Result Fields.vi
src/MCP Server
Build Discover Response.vi
src/MCP Server
Build Progress Event.vi
src/MCP Server
Build Stream Object.vi
src/MCP Server
Complete Handler Result.vi
src/MCP Server
Finish Stream.vi
src/MCP Server
Input Required Response.vi
src/MCP Server
Read Request Context.vi
src/MCP Server
Read Server Info.vi
src/MCP Server
Read Stream State.vi
src/MCP Server
Register Input Request.vi
src/MCP Server
Remap Resource Not Found.vi
src/MCP Server
Response Status Code.vi
src/MCP Server
Send Stream Event.vi
src/MCP Server
Should Send Progress.vi
src/MCP Server
Stream Was Cancelled.vi
src/MCP Server
Write Stream State.vi
src/MCP Server

Deleted: src/MCP Server/Select Value.vim

Report for commit ee7ee96. The installable test package is under Artifacts of this run once the build job has finished.

The package build failed in VIPM ("The VI is not in memory", error 1004
on Complete Modern MCP Result). The VIs rebuilt by script on this branch
were listed in MCP Server.lvlib but did not record their owning library
themselves, so VIPM's rename step could not find them; three of them also
carried LabVIEW's "insane items" save flag. All of them are now written
by LabVIEW itself, record MCP Server.lvlib, and none has the flag.

Diagrams that were still tangled are tidied with LabVIEW's Clean Up
Diagram, and the busiest ones are split further. Cancel Requested, Ask
User and Process HTTP Request keep their earlier layout, which reads
better than Clean Up's.

New in Private/SubVIs:
- Complete Handler Result: completes the handler's response, either
  with Add Result Fields or with Remap Resource Not Found.
- Input Required Response: the input_required response of a tool call
  that waits for elicitation answers.
- Response Status Code: 202 for a notification without response,
  otherwise 200.
- Add Cache Hints: ttlMs and cacheScope for lists and resources/read.
- Should Send Progress: whether Report Progress sends an event.

Behaviour, connector panes, reentrancy and error handling settings are
unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JanGoebel

JanGoebel commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner Author

Update: new commit ee7ee96 (Tidy the new subVIs with Clean Up Diagram and fix the package build)

  • Package build fixed. The CI test build failed with VIPM error 1004 ("The VI is not in memory"): 15 VIs rebuilt by script were listed in MCP Server.lvlib but did not record their owning library themselves. LabVIEW now writes that link in all of them; three also lost LabVIEW's "insane items" save flag.
  • Clean Up Diagram applied to the generated VIs where it reads better; Cancel Requested, Ask User and Process HTTP Request keep their earlier layout.
  • Five more subVIs to untangle Complete Modern MCP Result, Report Progress and Add Result Fields: Complete Handler Result, Input Required Response, Response Status Code, Add Cache Hints, Should Send Progress.
  • Tests (LabVIEW 2026 64-bit, local): Caraya 64 of 64 passed, protocol tests 24 of 24 required checks. Package built locally with vipm build in LabVIEW 2020 64-bit.
  • Still open: the Code summary (after this run's diff report), long wire loops in Process HTTP Request.

🤖 Generated with Claude Code

@JanGoebel
JanGoebel marked this pull request as ready for review October 25, 2026 16:45
@JanGoebel
JanGoebel merged commit 988037c into main Oct 25, 2026
2 checks passed
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.

2 participants