Files
Marc Schier 0374e19121 Add MachineTool, WMTP, GMS and CranesHoists companions (#4765)
# Description

_Describe the changes here to communicate to the maintainers why they
should accept this pull request. By default - this will become the
Commit message after merging and thus define history._

Add provider-backed MachineTool, bundled WMTP, GMS and CranesHoists
companions using the existing Machinery/MachineVision fluent, DI and
typed-client approach. **The PR is open/non-draft by user choice.
Current head `ecc4a7d6ca3e8004cf28ab54da605da302d026bf` is mergeable and
has 108 successful checks, six intentionally skipped checks, zero
failures and zero pending checks. All five primary workflows completed
successfully at that exact head.**

| Family | Released specification/model | Packaging |
| --- | --- | --- |
| MachineTool | OPC 40501-1, 1.02.0 | Model, Server, Client and MCP;
normal root versions |
| WMTP | OPC 40505, 1.0.0 | Bundled in MachineTool; distinct CLR/wire
namespace and independent WMTP-only hosting |
| GMS | OPC 40210 reference 1.0.1, normative model 1.0.0 | Model,
Server, Client and MCP; normal root versions |
| CranesHoists | OPC 40020-1, 1.00 | Model, Server, Client and MCP; all
four plus Debug variants forced preview |

There are **12 new packable projects**. No separate WMTP
package/profile/guide, root version bump, tag, release or package
publication is included. Mandatory instance structure is the default;
selected optional features require explicit providers. The
implementation reuses Machinery results/jobs/files/ProcessValues and
Robotics topology, including retained MachineTool Production, defined
GMS jobs/parts, modern ISA95/Machinery jobs, shared-owner hosting and
isolated instances alongside MachineVision.

Runtime and integration fixes preserve raw quality/timestamps, coherent
acquisition, namespace ownership, accepted-configuration state,
borrowed-resource lifetime, session/node/file cleanup, terminal-value
publication, event propagation and endpoint-aware capability
publication. Firmware/licence transfer means provider acceptance, not
installation/activation. Cancellation does not silently abort another
caller's operation. The later upstream MCP expansion is integrated
without losing either family set or its default-off privileged/live
execution policy.

### Conformance and scope

Canonical model inputs are pinned to UA-Nodeset revision
`96a08945425d0295edb3607d2a38f6841ef44046`.

**GMS upstream exception:** [Annex
A.1](https://reference.opcfoundation.org/specs/OPC-40210/annex-a) makes
the released NodeSet normative, but §12.3.2.7 requires undefined
`GMS_ProductionStatisticsType`. Inherited statistics and defined
jobs/parts are implemented without inventing a standard type/NodeId; the
complete GMS Production facet is withheld.

The requirement ledger records **102 positively evidenced CU rows, 96
directly negative-tested rows and six explicit negative-evidence
limitations**. All 648 test-class/name/TRX links were independently
resolved to passing cases. The six limitations concern Stacklight,
retained legacy PowerOnDuration, Machinery PowerOnDuration, WorkingUnit,
Obligation and MaintenanceMode. They are not claims that every
invalid-input or hardware-control behavior was tested. The undefined GMS
requirement is outside the 102 defined CUs. No external certification
claim is made.

Retained baseline complete-assembly line coverage, with generated code
retained and no source exclusions:

| Family | Model | Server | Client |
| --- | ---: | ---: | ---: |
| MachineTool/WMTP | 83.80% | 91.98% | 84.74% |
| GMS | 95.51% | 90.63% | 88.38% |
| CranesHoists | 92.76% | 94.45% | 92.24% |

These nine measurements predate the later focused review fixes and
merges; they are not relabelled as new `ecc4a7d6c` percentages. The
current head's separate CI code-coverage check succeeded. No new
exclusions or threshold relaxations were introduced.

## Related Issues

- Fixes
[#4764](https://github.com/OPCFoundation/UA-.NETStandard/issues/4764),
the implementation/design and acceptance tracker.

## Checklist

_Put an `x` in the boxes that apply. You can complete these step by step
after opening the PR._

- [ ] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [x] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- [x] I have added all necessary documentation.
- [ ] I have verified that my changes do not introduce (new) build or
analyzer warnings.
- [ ] I ran **all** tests locally using the **UA.slnx** solution against
at least .net **framework** and .net **10**, and all passed.
- [ ] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [x] I have addressed **all** PR feedback received.
2026-10-15 15:15:46 +02:00

6.0 KiB
Raw Permalink Blame History

OPC UA GMS MCP tools

Six bounded workflows for OPC 40210 Geometric Measurement Systems, using GmsClient, GmsResultClient, generated proxies and the shared session, subscription and result-file services. This package has no server dependency. It follows the normal repository version policy, including .Debug packages.

The reference edition is 1.0.1; the released namespace model is 1.0.0.

Embedding

services.AddOpcUaMcpCore();
services.AddOpcUaMcpGms();
services.AddMcpServer()
    .WithStdioServerTransport()
    .WithOpcUaMcpFilters()
    .WithOpcUaGmsTools(McpToolProfile.Gms);

Use WithOpcUaGmsTools(McpToolProfileSet.Parse("core,gms")) for composition. Both overloads include GMS for Full and exclude it for other selections. Repeated registration deduplicates both GMS and shared connection tools. Existing named sessions are borrowed from OpcUaSessionManager; disposing a call's facade never closes the host's session or unrelated subscriptions. Configure GmsClientOptions and TimeProvider through DI when tighter native read bounds or an injected observation clock are needed.

Catalog

Tool Workflow
gms_list_systems Actual GMSType/vendor-subtype inventory, not namespace-only detection.
gms_read_system System, Equipment, Calibration, Production, or per-instance Capabilities.
gms_read_results Latest, ById, bounded Query, raw Published slots/UsedTools, or Corrections.
gms_download_result Bounded inline base64 via standard temporary-file transfer; no host paths.
gms_observe Data, Events, initial retained Conditions, or IntermediateResults.
gms_acknowledge_results Standard result acknowledgement with explicit confirmAcknowledgement=true.

Every tool accepts sessionName; omission is valid only when exactly one session is connected. Selectors are exact and case-sensitive. Descriptions and schemas specify hard limits: 1–100 inventory/results/notifications, 1–60 seconds, 1–524288 download bytes, and 1024–1048576 complete UTF-8 reply bytes (default 128 KiB). Host native-read limits may be tighter. A response that exceeds its count or byte budget fails, rather than silently truncating. Query asks the server for one extra identifier to detect count overflow. Its native query object groups filterField, filterValue, and orderBy. Use a ResultId or JobId string equality filter, or an IsPartial Boolean. Omitting query (or using {}) means no filter and ascending ResultId order; orderBy: "None" preserves the server's order. Nondefault query selections require operation: "Query". Unknown or duplicate object properties, invalid selector names, and incorrectly typed values are rejected before session use. The filter remains a standard Machinery ContentFilter, not another protocol.

gms_read_results has seven top-level parameters: systemNodeId, operation, resultId, maximumResults, query, maximumResponseBytes, and sessionName. Move the former top-level filterField, filterValue, and orderBy arguments inside query; they are no longer top-level parameters.

Example MCP arguments:

{
  "systemNodeId": "ns=12;s=InspectionCell",
  "operation": "Query",
  "query": {
    "filterField": "JobId",
    "filterValue": "J-1",
    "orderBy": "ResultId"
  },
  "maximumResults": 20,
  "sessionName": "metrology"
}

For intermediate results, explicitly select instance-qualified characteristic placeholders; their namespace need not be the GMS namespace:

{
  "systemNodeId": "ns=12;s=InspectionCell",
  "kind": "IntermediateResults",
  "characteristics": [{"name": "Bore", "namespaceIndex": 12}],
  "durationSeconds": 5,
  "maximumNotifications": 10
}

Native values use the stack's JSON encoder, preserving wire numeric types, structures, status and source/server timestamps. A missing optional variable, folder or property is distinguished from a present bad/uncertain sample. Method and event values have no invented per-field acquisition quality. Published samples retain their original DataValue; correction measurements retain their independent engineering-unit samples. Native catalogs preserve Number scalar/array representation, and qualification retains its actual remaining-hours countdown, including negative values after expiry.

Side effects and lifetime

Passive reads never acknowledge results/conditions, confirm alarms, start jobs or measurements, fetch calibration-certificate URIs, change equipment, or apply corrections. There are no invented GMS start/acquire/actuation verbs. Acknowledgement is a separate, explicitly authorized standard result operation; per-result domain errors remain distinct from UA service failures, and no automatic retry is performed.

Retained conditions use standard ConditionRefresh and report refreshCompleted; an incomplete or lossy refresh is an MCP error. Intermediate observation awaits real monitored-item creation and its status: setup failure is not an empty successful window. Every observation releases only its own subscription/items on completion, error or cancellation. Metadata and temporary-file handle ownership stays with the existing clients.

MCP 2.2's local client cancellation token does not necessarily send a protocol cancel notification. The adapter honors its server-side cancellation token; clients should send the standard MCP notifications/cancelled notification when immediate server cancellation is needed. The bounded window still terminates naturally. Cleanup may continue briefly under the typed client's independent cleanup token after the user-facing window expires.

Conformance boundary

Capabilities describe actual selected surfaces, not authorization or external certification. Native job/part monitoring and inherited statistics are exposed, but the complete GMS Production facet is not claimed: the released specification requires GMS_ProductionStatisticsType without defining its type or NodeId. This package does not invent that missing definition.