# Description Expose existing stack APIs through **337 additional domain MCP tools**: 114 industrial-companion tools, 35 WoT/xRegistry/document tools, and 188 higher-level stack-workflow tools. The fully enabled catalogue contains **492 tools**; profile selection is not an execution grant. The packages now follow the reviewed structure: **`Opc.Ua.Mcp.Workflows`** contains roles/users, files, alarms and history; **`Opc.Ua.Mcp.WotCon`** contains management/documents and independently registered live WoT execution. GDS and schema tools remain separate packages. The executable composes **18 libraries**. | Workflow | Added domain tools | Profiles | | --- | ---: | --- | | GDS directory, certificates, reference-only credentials/tokens and Part 21 onboarding | 46 | `gds`, `certificates`, `credentials`, `onboarding` | | Roles/users, regular and temporary files, alarms/conditions and historical access | 74 | `roles`, `users`, `files`, `alarms`, `history` | | Bounded runtime types, XSD/BSD/UA JSON, PubSub and REST OpenAPI artifacts | 6 | `schemas` | | Named PubSub transports, remote configuration, declarative adapters/bridges and SKS | 52 | `pubsub-runtime`, `pubsub-configuration`, `pubsub-bridge`, `pubsub-security` | | Live HTTP(S), MQTT, Modbus TCP and OPC UA WoT forms | 10 | `wot-runtime` | - Reuse typed clients, generated proxies, SDK transports, schema providers and injected services rather than introducing another session manager or protocol engine. - Require host enablement plus exact caller/session/secret/network grants. Protected generic Call/Write/node-management/history/file paths enforce the same policy. - Bind classifications, dispatches, file handles and historical cursors to actual UA generations. Recovery cannot retarget an old handle or authorized mutation. Preserve acknowledged results after late cancellation; do not imply atomicity or retry business commands. - Use opaque secret-store references, explicit writable-store registration, issuer/resource/caller binding and reserved capacity before issuance. Report remote success/local persistence failure as partial completion; redact provider-controlled diagnostic text. - Bound new named runtimes to eight per host, two per caller, five-minute idle expiry and a thirty-minute absolute lease by default. Pending/closing resources consume quota; failed cleanup remains visible. Credential invalidation and session recovery close dependent runtimes. - Keep document planning separate from execution, artifacts root-confined and real JSON inputs typed. Preserve old profile ordinals while widening the set to 64 bits. CI grades fully enabled privileged catalogues rather than connection-only views. **Limitations:** live Kafka remains explicitly unavailable because the backend cannot enforce policy on broker-discovered connections; configuration/schema/capability inspection stays available. The latest public Dekaf v1.23.5 was also checked and its DNS injection remains internal. No private-API workaround or bootstrap-only security claim was added. Full legacy Core registration retains its documented JSON-reflection limitation: native tests isolate its four connection descriptors and validate the expanded production registrations, not whole-host NativeAOT support. ## Related Issues - Fixes #4730. ## 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. - [x] 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.
11 KiB
OPCFoundation.NetStandard.Opc.Ua.Mcp.PubSub
OPC UA Model Context Protocol (MCP) tools for PubSub, packaged so they can be
embedded in any MCP server host rather than only run as the shipped opcua-mcp
tool.
Use this package when an application wants to expose OPC UA PubSub publishers and subscribers to an LLM alongside tools of its own.
Tools
| Profile | Workflows |
|---|---|
pubsub |
Legacy UDP/UADP runtime, action and discovery tools; names and arguments remain unchanged |
pubsub-runtime |
Discover backends; validate/create/list/start/stop/remove named applications; typed publication, bounded reads and diagnostics |
pubsub-configuration |
Existing remote server configuration inspection, safe export/import, standard component/field mutations and enable/disable |
pubsub-bridge |
Explicit client-source/sink/action bindings and named declarative transcoding routes |
pubsub-security |
Public group metadata, group creation/removal, rotation/invalidation, protected key pull/push and push-target management |
Selecting a profile, including full, does not grant permission. Live and
administrative operations require explicit host capability, trusted caller,
target and operation rules. Empty rules deny execution. Legacy live start,
publish, actions and discovery now pass through this intentional compatibility
gate too. Legacy status/stop cannot inspect or replace another caller's runtime.
Its lifetime remains until explicit stop or host disposal; new named-runtime
expiry is not silently imposed on it.
Usage
builder.Services.AddOpcUaMcpCore();
builder.Services.AddOpcUaMcpPubSub();
builder.Services.AddMcpServer()
.WithStdioServerTransport()
.WithOpcUaMcpFilters()
.WithOpcUaCoreTools(McpToolProfile.PubSub)
.WithOpcUaPubSubTools(McpToolProfile.PubSub)
.WithTools<MyApplicationTools>();
A profile that does not select PubSub contributes no tools rather than failing, so the same profile value can be passed to every OPC UA tool package a host references.
The existing zero-argument registration remains available. Hosts can provide
AddOpcUaMcpPubSub(new McpPubSubOptions { ... }) for bounds, literal-address
UDP/DTLS rules, exact Ethernet MAC/VLAN/interface rules, exact remote model
operations and adapter node/method scopes. Broker connection/topic grants use
OpcUaMcpOptions.Execution.NetworkRules. Trusted caller/session grants and
capabilities use OpcUaMcpOptions.Execution; registration never enables them.
Use the McpToolProfileSet overload to compose new profiles:
builder.Services.AddMcpServer()
.WithStdioServerTransport()
.WithOpcUaMcpFilters()
.WithOpcUaPubSubTools(McpToolProfileSet.Parse(
"pubsub-runtime,pubsub-configuration,pubsub-bridge,pubsub-security"));
Remote profiles add the shared connection tools only once. They operate on an
existing encrypted named UA session; they do not start or control a server
process. Public constructors on McpPubSubRuntimeManager, the transport
provider and tool classes support direct construction without the executable.
IMcpPubSubTransportProvider, DTLS context/certificate infrastructure and
IEthernetFrameChannelFactory are injectable host services.
Transport control services use programmatic methods, not configuration
properties: MqttConnectionOptions.WithStreamProvider / GetStreamProvider,
WithTopicPolicy / GetTopicPolicy, and
UdpTransportOptions.WithDatagramPolicy / GetDatagramPolicy,
WithLocalAddress / GetLocalAddress. The backing fields remain private so
neither configuration binding nor configuration serialization can traverse
these host-owned services. The normal scalar transport options retain their
existing defaults and binding behavior.
Named runtime contract
pubsub_application_create accepts three typed root arguments: input
(PubSubApplicationSettingsInput), connection (PubSubConnectionInput), and
the native JSON fields array. It returns an opaque owner-bound handle and
creates a stopped application. For example:
{
"input": {
"name": "press-values",
"mode": "Publisher",
"publisherId": { "type": "UInt16", "value": 42 },
"writerGroupId": 10,
"dataSetWriterId": 1
},
"connection": {
"transport": "Mqtt",
"endpoint": "mqtts://broker.example:8883",
"encoding": "Json",
"topic": "plant/press/data",
"metadataTopic": "plant/press/metadata"
},
"fields": [
{ "name": "Temperature", "type": "Double", "initialValue": 21.5 }
]
}
The host must independently allow that broker, resolved network, exact data and
metadata topics, and operations. The existing MQTT backend also configures a
standard status last-will topic, which requires its own explicit publish
grant (including on subscriber connections). Wildcard discovery subscriptions
are disabled; additional topics require explicit auxiliaryTopics and grants.
The example does not itself authorize a connection.
Use pubsub_application_start(handle) to activate and
pubsub_application_publish with
{"handle":"<created handle>","fields":[{"name":"Temperature","value":22.0}]}
to update a sample atomically. A successful sample update is not a delivery
acknowledgement. PublisherId widths, numeric/string distinctions and UADP versus
JSON wire encoding are preserved. Arrays require explicit rank 1; default
rank -1 is scalar.
Omitted optional JSON members retain the same defaults as direct construction:
writer/group identifiers 1, dataset name McpDataSet, publishing interval
100 milliseconds, and scalar field rank -1. Missing required members and
empty required objects remain errors; explicit zero or empty values are never
silently replaced by defaults. The production source-generated context owns
this behavior and does not depend on reflection.
Direct C# callers can continue using complete PubSubApplicationInput and
PubSubBridgeInput plans. Tool overloads split those plans into shallow,
native typed arguments; they do not accept serialized JSON strings as an escape
hatch. Configuration tools likewise accept root fields or variables arrays,
and security group association tools accept a root nodes array.
The shared defaults admit eight leases per host, two per trusted caller,
five-minute idle expiry, thirty-minute hard expiry and thirty-second operations.
Per-runtime defaults bound the decoded buffer to 64 messages/1 MiB, with a
64 KiB per-message budget, 64 fields, 1,024 array elements and 100 frames/second.
Overflows, invalid snapshots and rate drops have distinct counters. Read with
clear=true removes only the returned page. Stop retains the lease, field schema
and buffered data; remove releases the quota slot.
Transport and credential boundaries
- MQTT uses the shipped MQTTnet adapter. Its supported stream-provider seam resolves/checks every reconnect through the immutable host network grant; TLS still uses the original broker hostname. Topic and payload checks run at the actual subscribe/publish/receive boundary before the MCP receive buffer.
- UDP/DTLS requires IP literals, explicit ports, local interface/address and source/reply scopes. The send policy also covers discovery and DTLS replies. Standard multicast discovery is not implied by permission for a data endpoint.
- Ethernet checks full frames against exact source/destination MAC, VLAN, priority, interface and frame limits. No driver is installed and no privilege is elevated. Native Windows activation requires a host-provided backend. An in-memory backend proves the integration seam, not hardware interoperability.
- Built-in live Kafka is blocked. The pinned managed Dekaf 1.21.1 backend lacks a supported public per-broker DNS/connect hook, so bootstrap allowlisting cannot authorize metadata-discovered broker egress. Kafka configuration, profile and schema selection remain available and capability discovery reports this limitation explicitly. There is no native-Confluent fallback.
- Passwords and SKS keys are authorized
alias:/issued:references throughAddOpcUaMcpSecrets, never plaintext tool inputs. Issued-reference expiry or invalidation closes dependent leases. Host aliases are re-resolved at each start and bounded by the runtime lease; aliases currently have no live revocation notification, which status reports explicitly. - SKS pull persists keys before returning references. Store failures report
remote retrieval separately from local persistence. Key invalidation through
these tools invalidates tracked uses of that exact remote group. Rotation is
separate from invalidation and does not silently redeploy keys. Secured
runtimes retain the actual encrypted issuing endpoint as standard
SecurityKeyServicesmetadata while obtaining key material from the protected local reference. This metadata neither triggers an automatic key pull nor grants new outbound authority.
Remote configuration and adapters
Configuration uses generated standard clients and bounded standard file
transfers under TransferRoot. Export removes credential-bearing property bags,
opaque transport settings and substitution values and reports the number
removed. Such an export is explicitly not a complete round trip of private
configuration. Import rejects those fields rather than silently dropping them.
CloseAndUpdate reports the server's applied flag and per-item statuses; it is
not a client-emulated atomic transaction. Unknown outcomes must not be blindly
retried.
Source/sink bindings require exact declared field names/order and authorized Value node scopes. They reuse the Adapter package's cyclic reads and TargetVariables sink. Metadata is an explicit immutable schema, not an implicit model subscription. Replaced sessions or namespace mappings fail closed instead of binding a new server under the old name. Sink diagnostics preserve actual per-item write results; a batch is not advertised as an atomic server write. Method bindings also pass the shared protected-target guard.
A declarative bridge owns both endpoint applications; it never silently borrows or replaces an existing application. Supported transforms are identifier remapping, field rename/projection/exclusion/promotion and metadata-version preservation. The target's cyclic writer is disabled, so it emits only forwarded records. Lowering UADP security requires both explicit host permission and explicit route consent. No scripts, expressions or tool-name callbacks are accepted.
The bridge tool interface has eight root arguments: route input
(PubSubBridgeSettingsInput), application source and target settings,
sourceConnection and targetConnection, native sourceFields and
targetFields arrays, and an optional native renameFields array. Reusable
schema definitions keep the catalogue compact; their resolved object/array
structure remains at most three levels deep.
Related packages
| Package | Adds |
|---|---|
OPCFoundation.NetStandard.Opc.Ua.Mcp.Core |
Part 4 service tools, session management, filters (required) |
OPCFoundation.NetStandard.Opc.Ua.Mcp.Diagnostics |
packet capture, decode and replay, including key logging |
OPCFoundation.NetStandard.Opc.Ua.Mcp.PubSub.Diagnostics |
PubSub packet capture and decode |
OPCFoundation.NetStandard.Opc.Ua.Mcp |
the ready-to-run opcua-mcp server composing all of the above |
License
OPC Foundation MIT License 1.00 — http://opcfoundation.org/License/MIT/1.00/