Files
UA-.NETStandard/docs/McpServer.md
Marc Schier 0af7ae3a23 Update the guides and the MCP server docs for the 2.0.0 release (#4713)
# Description

The packages on nuget.org are stable 2.0.0 now. The guides still told
readers to use `--prerelease` and preview-only versions, and kept
caveats that applied only to 2.0.0-preview.5 and earlier previews.

Getting started, Your first information model, and Source-generated data
types:

- Add the packages without `--prerelease` and name 2.0.0 as the verified
version.
- With 2.0.0, a client with no server reports `BadNotConnected`, so the
troubleshooting row drops the preview.5 `SocketException`.
- 2.0.0 generates code for methods that declare their arguments inline,
so the tutorial drops the preview.5 workaround note and its
troubleshooting row, and says both forms are possible.

MCP server (`docs/McpServer.md` and the tool's NuGet README
`tools/Opc.Ua.Mcp/McpREADME.md`):

- Install and run commands use the stable tool: `dotnet tool exec
OPCFoundation.NetStandard.Opc.Ua.Mcp`, `dnx …`, and `dotnet tool install
--global …`, without `--prerelease` or `2.0.0-preview.*`.
- The stable tool bundles `Opc.Ua.Mcp.Robotics` and `Opc.Ua.Mcp.Vision`,
so the note that `vision` and `vision,robotics` need the current source
is removed. The docs now state that only the standalone `Mcp.Robotics`
and `Mcp.Vision` libraries are preview packages (`2.0.0-preview.200`).
- Not changed: `tools/Opc.Ua.Mcp/.mcp/server.json` still declares
`"version": "1.0.0"` and lists only the original profiles in its
description.

### Validation

| Command | Result |
| --- | --- |
| Getting started Route B, the first-model tutorial, and every
troubleshooting row, run exactly as documented against the 2.0.0
packages | Output matches the guides; 0 warnings |
| Native AOT publish of the Route B server and client | 0 warnings; the
native executables completed the trust flow and connected |
| `dotnet tool exec OPCFoundation.NetStandard.Opc.Ua.Mcp -- --version`,
`dnx OPCFoundation.NetStandard.Opc.Ua.Mcp -- --version`, `dotnet tool
exec "OPCFoundation.NetStandard.Opc.Ua.Mcp@2.0.*" -- --version` |
`2.0.0+bc5da1006…` for each |
| `dotnet tool install` of `OPCFoundation.NetStandard.Opc.Ua.Mcp`
without `--prerelease` (into a temporary tool path) | Installs 2.0.0 |
| `dotnet tool exec` of an uncached version with stdin redirected, as an
MCP client launches it | Runs without a confirmation prompt |
| MCP stdio `initialize` + `tools/list` against the 2.0.0 tool with
`--profile vision,robotics`, `vision`, `robotics`, `full` | 64, 26, 42,
and 138 tools |
| Markdown link, anchor, contents, and fence checks over `docs/` | 0
issues |

## Related Issues

- No tracking issue. Follow-up to #4555 after the 2.0.0 release.

## Checklist

- [ ] 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.
- [ ] 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.
- [ ] I have addressed **all** PR feedback received.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-09 14:36:18 +02:00

682 lines
30 KiB
Markdown

# OPC UA MCP Server
The OPC UA MCP Server exposes OPC UA Part 4 service calls as
[Model Context Protocol (MCP)](https://modelcontextprotocol.io) tools. AI
assistants such as Claude, GitHub Copilot, VS Code Copilot, and Cursor can
use these tools to connect to OPC UA servers and interact with industrial
automation systems through natural language.
## Contents
- [What It Does](#what-it-does)
- [Tool Profiles](#tool-profiles)
- [Vision-guided Robotics](#vision-guided-robotics)
- [Resources](#resources)
- [Multi-Session Support](#multi-session-support)
- [Installation](#installation)
- [Option 1: Run without installing (.NET 10 SDK)](#option-1-run-without-installing-net-10-sdk)
- [Option 2: Install as a .NET global tool](#option-2-install-as-a-net-global-tool)
- [Option 3: Run from source](#option-3-run-from-source)
- [Option 4: Install from local build](#option-4-install-from-local-build)
- [Configuration](#configuration)
- [Run on demand without a global installation](#run-on-demand-without-a-global-installation)
- [Claude Desktop](#claude-desktop)
- [VS Code / GitHub Copilot](#vs-code--github-copilot)
- [Cursor](#cursor)
- [HTTP Transport (for remote clients)](#http-transport-for-remote-clients)
- [Usage](#usage)
- [Typical Workflow](#typical-workflow)
- [NodeId Formats](#nodeid-formats)
- [Common Well-Known NodeIds](#common-well-known-nodeids)
- [Error Handling](#error-handling)
- [PubSub Tools](#pubsub-tools)
- [Architecture](#architecture)
- [Packages](#packages)
- [Embedding the tools in your own MCP server](#embedding-the-tools-in-your-own-mcp-server)
- [Security Notes](#security-notes)
- [Agent-Usability Quality Gate](#agent-usability-quality-gate)
- [Requirements](#requirements)
## What It Does
The MCP server wraps the OPC UA .NET Standard client library, translating between JSON-based MCP tool calls and OPC UA binary protocol operations. The server exposes tools through a [tool profile](#tool-profiles) — a named, bounded catalog selected at startup — rather than a single fixed tool count. The default `full` profile currently registers every tool below; running a narrower profile (`core`, `services`, `administration`, `pubsub`, `diagnostics`, `robotics`, or `vision`) exposes only the subset relevant to that workflow. The tables below list the complete tool surface, organized by OPC UA Part 4 service set:
| Service Set | Tools | Description |
|---|---|---|
| **Connection** | `GetEndpoints`, `Connect`, `Disconnect`, `GetConnectionStatus` | Endpoint discovery and session lifecycle management |
| **Attribute** | `Read`, `Write`, `HistoryRead`, `HistoryUpdate` | Read/write node attributes and historical data |
| **View** | `Browse`, `BrowseNext`, `TranslateBrowsePaths`, `RegisterNodes`, `UnregisterNodes`, `QueryFirst`, `QueryNext` | Navigate and query the address space |
| **Node Management** | `AddNodes`, `AddReferences`, `DeleteNodes`, `DeleteReferences` | Modify the address space |
| **Method** | `Call` | Invoke OPC UA methods |
| **Subscription** | `CreateSubscription`, `ModifySubscription`, `SetPublishingMode`, `Publish`, `Republish`, `DeleteSubscriptions`, `TransferSubscriptions` | Manage notification subscriptions |
| **MonitoredItem** | `CreateMonitoredItems`, `ModifyMonitoredItems`, `SetMonitoringMode`, `SetTriggering`, `DeleteMonitoredItems` | Monitor data changes and events |
| **Discovery** | `FindServers`, `FindServersOnNetwork`, `RegisterServer`, `RegisterServer2` | Discover servers and register |
| **PKI Management** | `ListCertificates`, `TrustCertificate`, `RemoveCertificate`, `GetPkiStorePaths` | Manage certificate trust lists |
| **Configuration** | `GetConfiguration`, `SetTransportConfiguration`, `SetClientConfiguration`, `SetSecurityConfiguration`, `SetConfiguration` | View/modify in-memory client settings; `SetConfiguration` is the `full`-profile compatibility tool |
| **NodeSet Export** | `ExportNodeSet`, `ExportNodeSetPerNamespace` | Export address space to NodeSet2 XML |
| **Convenience** | `ReadValue`, `ReadValues`, `WriteValue`, `BrowseAll`, `CallMethod`, `ReadNode`, `Cancel` | Simplified high-level operations |
| **Packet Capture** | `list_interfaces`, `start_capture`, `stop_capture`, `list_captures`, `get_capture`, `capture_now`, `list_active_channels`, `dump_keys`, `decode_pcap_with_keys`, `summarize_service_calls`, `replay_pcap`, `stop_replay`, `list_replays` | OPC UA-aware packet capture, offline decode, service-call summaries, replay |
All OPC UA types (NodeId, DataValue, Variant, StatusCode, etc.) are represented as JSON for LLM-friendly interaction. Variant serialization dispatches by `BuiltInType`, so scalar defaults such as Boolean `false` and numeric `0` remain JSON `false` / `0` instead of being confused with `Variant.Null`.
The packet-capture tools are described in detail in [Diagnostics](Diagnostics.md#4-packet-capture-dissection-and-replay). For convenience the per-tool surface is:
| Tool | Description | Parameters |
|---|---|---|
| `list_interfaces` | Enumerates NICs available to SharpPcap; `linkType` may be `null` because enumeration does not open adapters. | None |
| `start_capture` | Starts a capture session. | `source`: `nic \| inproc-client \| inproc-server \| replay`; optional interface, filter, endpoint, limits, folder |
| `stop_capture` | Stops an active session and finalizes artifacts. | `sessionId` |
| `list_captures` | Lists capture sessions. | Optional `state` |
| `get_capture` | Returns an artifact or formatted analysis. | `sessionId`; `format`: `pcap \| pcapng \| json \| csv \| text \| service-timeline`; optional packet/partial controls |
| `capture_now` | Starts, waits, stops, and returns output. | Capture options plus the same output `format` values |
| `list_active_channels` | Lists in-process secure channels with current tokens. | None |
| `dump_keys` | Emits keylog data. | Optional `sessionId`, `format`, `includeExpired` |
| `decode_pcap_with_keys` | Decodes an existing pcap and keylog offline. | `pcapPath`, `keylogPath`, `format`, optional `maxFrames` |
| `summarize_service_calls` | Reports service counts, latency, and errors. | `sessionId` or `pcapPath` + `keylogPath`, optional `top` |
| `replay_pcap` | Replays as a mock server or mock client. | `pcapPath`, `keylogPath`, `mode`, endpoints, `speed` |
| `stop_replay` | Stops an active replay session. | `sessionId` |
| `list_replays` | Lists active and recently-completed replay sessions. | None |
The canonical hyphenated values above are emitted on output. CLR enum
names such as `InProcessClient` and `ServiceTimeline` remain accepted
on input.
## Tool Profiles
The server selects its tool catalog through a **tool profile** — a bounded set of tool classes registered at startup. Profiles let a client request only the tools it needs (smaller catalogs are easier for an LLM to reason about and reduce prompt size), while `full` preserves the complete surface for clients that want everything.
| Profile | Tool classes registered | Typical use case |
|---|---|---|
| `core` | Configuration, Connection, Convenience | Minimal footprint: connect, read/write values, adjust settings |
| `services` | Attribute, Configuration, Connection, Convenience, Discovery, Method, MonitoredItem, Node Management, Subscription, View | Full OPC UA Part 4 client workflows without PKI/PubSub/packet capture |
| `administration` | Configuration, Connection, NodeSet Export, PKI Management | Certificate trust management and NodeSet export |
| `pubsub` | PubSub runtime, discovery, action, and capture tools (plus PubSub decode when diagnostics tools are enabled) | Part 14 PubSub publish/subscribe, discovery, and capture workflows |
| `diagnostics` | Connection, Packet Capture (plus decode/replay when diagnostics tools are enabled) | OPC UA-aware packet capture, offline decode, and replay |
| `robotics` | Connection plus Robot Intent discovery, paged monitoring, control, mission and Vision-guided Pick tools | Commanding and monitoring a Robot Intent controller |
| `vision` | Connection plus the Vision discovery, monitoring, seeing, inference, feedback and geometry tools | Perception-driven agents that need to see through a Vision server, compose poses across the §5.12 frame graph, run or submit inference, and (when composed with `robotics`) act on what they see — see the [Vision developer guide](Vision.md) |
| `full` (default) | Every tool class above | Unrestricted access; the current-major default so existing integrations keep working unchanged |
`full` is the default for the current major version — `core` and the other bounded profiles are opt-in. Select a profile with:
- The `--profile` CLI option, e.g. `opcua-mcp --profile core`
- The `McpServer:ToolProfile` configuration value
- The `OPCUA_MCP_TOOL_PROFILE` environment variable
**Profiles compose.** A `--profile` value can name more than one bounded profile at a time — the `BinPickingClient` sample runs `--profile vision,robotics` and exposes both catalogs from the same MCP host, deduplicating the shared `Connection` tools. The composed set uses the `WithOpcUaCoreTools(McpToolProfileSet)` / `WithOpcUaVisionTools(McpToolProfileSet)` / `WithOpcUaRoboticsTools(McpToolProfileSet)` overloads, and the core-tools overload owns the single `ConnectionTools` registration across every package that references the same MCP server. See the [Vision developer guide](Vision.md#mcp-tools) for the composed 64-tool example and the [BinPickingClient sample](../samples/Robotics/BinPickingClient) for the running catalog.
Because the exact number of tools in each profile (and in `full`) changes as tools are added or removed, this document intentionally does not hard-code a tool count. Use the tables above (or `tools/list`) to enumerate the tools actually exposed by a running server.
### Vision-guided Robotics
`robotics_vision_pick` closes the common perception-to-action path without
making an agent copy a result NodeId and several resource NodeIds between tool
calls. It runs one detection inference through `Opc.Ua.Vision.Client`, on the
same named OPC UA session as the Robot Intent controller, then submits either
one Pick or a two-step Pick/Place mission:
```json
{
"request": {
"controller": "BinPickingController",
"pipeline": "BinPickingPipeline",
"source": "Bin",
"tool": "ParallelGripper",
"destination": "Fixture",
"classLabel": "RedCube",
"minimumConfidence": 0.9,
"missionId": "place-red-cube"
}
}
```
Detection selection is deterministic: exact DetectionId/ClassLabel filters and
the confidence threshold are applied first, then highest confidence wins with
ordinal DetectionId and original result order as tie-breakers. The result
contains Vision result/pipeline/sensor/model/frame provenance, the selected
pose, and the authoritative intent or mission handle. The helper does not
request authority, wait, retry, cancel, or reinterpret a server refusal.
## Resources
The MCP server exposes connected sessions as **MCP resources**, enabling the LLM to discover, inspect, and subscribe to session state.
| Resource URI | Type | Description |
|---|---|---|
| `opcua://sessions` | Direct | List all active sessions with connection status |
| `opcua://sessions/{name}` | Template | Full details of a named session (endpoint, security, namespaces) |
| `opcua://sessions/{name}/namespaces` | Template | Server namespace table for a session |
### Multi-Session Support
The server supports **multiple simultaneous sessions** to different OPC UA servers. Each session is identified by a name.
```
Tool: Connect
endpointUrl: "opc.tcp://server1:62541/ReferenceServer"
name: "refserver" (optional — auto-generated from hostname if omitted)
autoAcceptCerts: true
Tool: Connect
endpointUrl: "opc.tcp://plc1:4840"
name: "plc1"
Tool: Browse
nodeId: "i=85"
sessionName: "refserver" (optional — uses the only session if there's just one)
Tool: ReadValue
nodeId: "ns=2;s=Temperature"
sessionName: "plc1"
```
Sessions are listed via `resources/list` and detailed via `resources/read`.
## Installation
### Option 1: Run without installing (.NET 10 SDK)
Run the published tool directly from nuget.org. `dotnet tool exec` downloads
and caches the tool without adding a global tool installation:
```bash
# Run the latest release
dotnet tool exec OPCFoundation.NetStandard.Opc.Ua.Mcp
# Or stay on one release line, for example 2.0.x
dotnet tool exec "OPCFoundation.NetStandard.Opc.Ua.Mcp@2.0.*"
# Short form of dotnet tool exec
dnx OPCFoundation.NetStandard.Opc.Ua.Mcp
```
### Option 2: Install as a .NET global tool
```bash
dotnet tool install --global OPCFoundation.NetStandard.Opc.Ua.Mcp
```
After installation, the `opcua-mcp` command is available globally.
The tool includes every [tool profile](#tool-profiles), including `robotics`,
`vision`, and the composed `vision,robotics` profile. Only the libraries for
embedding the Robotics and Vision tools in your own server are preview
packages; see [Packages](#packages).
### Option 3: Run from source
```bash
cd tools/Opc.Ua.Mcp
dotnet run -c Release
```
### Option 4: Install from local build
```bash
dotnet pack tools/Opc.Ua.Mcp/Opc.Ua.Mcp.csproj -c Release
dotnet tool install --global --prerelease --add-source tools/Opc.Ua.Mcp/bin/Release OPCFoundation.NetStandard.Opc.Ua.Mcp
```
If multiple prerelease versions are visible from configured sources, add
`--version <the-version-produced-by-dotnet-pack>` to select the local package
explicitly.
## Configuration
### Run on demand without a global installation
MCP clients that accept an executable and argument array can launch the tool
through `dotnet tool exec`. Put MCP tool arguments after `--`:
```json
{
"mcpServers": {
"opcua": {
"command": "dotnet",
"args": [
"tool",
"exec",
"OPCFoundation.NetStandard.Opc.Ua.Mcp",
"--",
"--profile",
"core"
]
}
}
}
```
### Claude Desktop
Add to your `claude_desktop_config.json` (typically at `%APPDATA%\Claude\claude_desktop_config.json` on Windows or `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
```json
{
"mcpServers": {
"opcua": {
"command": "opcua-mcp"
}
}
}
```
### VS Code / GitHub Copilot
Add to your workspace `.vscode/mcp.json`:
```json
{
"servers": {
"opcua": {
"command": "opcua-mcp"
}
}
}
```
### Cursor
Add to your Cursor MCP settings:
```json
{
"mcpServers": {
"opcua": {
"command": "opcua-mcp"
}
}
}
```
### HTTP Transport (for remote clients)
By default, the server uses stdio transport for local tool integration. For remote clients, use the Streamable HTTP transport, exposed only at the `/mcp` path (there is no root route):
```bash
opcua-mcp --transport http --port 5100
```
The server listens at `http://localhost:5100/mcp`. `--transport sse` is a deprecated alias for `http` kept for the current major version only — prefer `http` in new configurations.
## Usage
### Typical Workflow
**1. Discover endpoints (no session required):**
```
Tool: GetEndpoints
endpointUrl: "opc.tcp://localhost:62541/Quickstarts/ReferenceServer"
```
**2. Connect to an OPC UA server (auto-select most secure, anonymous):**
```
Tool: Connect
endpointUrl: "opc.tcp://localhost:62541/Quickstarts/ReferenceServer"
autoAcceptCerts: true
```
**Or connect with specific security and authentication:**
```
Tool: Connect
endpointUrl: "opc.tcp://localhost:62541/Quickstarts/ReferenceServer"
securityMode: "SignAndEncrypt"
securityPolicy: "Basic256Sha256"
authType: "Username"
username: "admin"
password: "password"
autoAcceptCerts: true
```
**2. Explore the address space:**
```
Tool: BrowseAll
nodeId: "i=85" (Objects folder)
maxDepth: 2
maxResults: 50
```
**3. Read values:**
```
Tool: ReadValue
nodeId: "ns=2;s=MyTemperatureSensor"
```
**4. Write values:**
```
Tool: WriteValue
nodeId: "ns=2;s=MySetpoint"
value: "72.5"
dataType: "Double"
```
**5. Call a method:**
```
Tool: CallMethod
objectId: "ns=2;s=MyMachine"
methodId: "ns=2;s=StartProcess"
inputArguments: ["fast", "true"]
```
**6. Monitor changes:**
```
Tool: CreateSubscription
publishingInterval: 1000
Tool: CreateMonitoredItems
subscriptionId: <from above>
nodeIds: ["ns=2;s=Temperature", "ns=2;s=Pressure"]
Tool: Publish
(retrieves queued notifications)
```
**7. Manage PKI (trust rejected certificates):**
```
Tool: ListCertificates
store: "Rejected"
Tool: TrustCertificate
thumbprint: "A1B2C3..." (from ListCertificates results)
Tool: GetPkiStorePaths
(shows where certificate stores are located on disk)
```
**8. Adjust configuration for current session:**
```
Tool: GetConfiguration
(view current settings)
Tool: SetConfiguration
operationTimeout: 60000
maxArrayLength: 131072
autoAcceptUntrustedCertificates: true
```
**9. Export the server's address space to NodeSet2 XML:**
```
Tool: ExportNodeSet
filePath: "C:\\export\\server-nodeset.xml"
startingNodeId: "i=85" (Objects folder)
exportMode: "Complete" (includes values)
```
Or export split by namespace (one file per companion spec):
```
Tool: ExportNodeSetPerNamespace
outputDirectory: "C:\\export\\namespaces"
```
**10. Disconnect:**
```
Tool: Disconnect
```
### NodeId Formats
The MCP server accepts NodeIds in standard OPC UA string format:
| Format | Example | Description |
|---|---|---|
| Numeric | `i=2258` | Numeric identifier in namespace 0 |
| Numeric with namespace | `ns=2;i=1001` | Numeric identifier in namespace 2 |
| String | `ns=2;s=MyVariable` | String identifier |
| GUID | `ns=2;g=12345678-1234-1234-1234-123456789abc` | GUID identifier |
| Opaque | `ns=2;b=Base64EncodedData` | ByteString identifier |
### Common Well-Known NodeIds
| NodeId | Description |
|---|---|
| `i=84` | Root node |
| `i=85` | Objects folder |
| `i=86` | Types folder |
| `i=87` | Views folder |
| `i=2253` | Server object |
| `i=2258` | Server/ServerStatus/CurrentTime |
### Error Handling
When an OPC UA service returns an error, tools return a structured JSON response:
```json
{
"error": true,
"statusCode": "BadNodeIdUnknown",
"message": "The node id refers to a node that does not exist in the server address space."
}
```
This is normal behavior — not all servers support all services. Common status codes:
| Status Code | Meaning |
|---|---|
| `BadServiceUnsupported` | Server doesn't implement this service |
| `BadNodeIdUnknown` | NodeId doesn't exist |
| `BadNotWritable` | Node attribute is read-only |
| `BadMethodInvalid` | Method not found on the specified object |
| `BadUserAccessDenied` | Insufficient permissions |
## PubSub Tools
In addition to the client services above, the server exposes OPC UA PubSub
(Part 14) tools, backed by `Opc.Ua.PubSub` and
`Opc.Ua.PubSub.Diagnostics`. See
[Diagnostics.md §5](Diagnostics.md#5-pubsub-packet-capture-and-dissection) for
the capture / dissection details.
**Configuration and Security Key Service methods.** PubSub configuration
(`AddConnection`, `AddWriterGroup`, `AddReaderGroup`, `AddDataSetWriter`,
`AddDataSetReader`, `Status.Enable` / `Disable`) and the Security Key Service
(`GetSecurityKeys`, `AddSecurityGroup` / `RemoveSecurityGroup`) are standard
server-side `PublishSubscribe` object methods, so they are invoked with the
generic [`Call`](#usage) tool rather than dedicated wrappers — pass the
`PublishSubscribe` object NodeId (or the target connection / group NodeId) and
the corresponding method NodeId (e.g. `i=14443` for
`PublishSubscribe_AddConnection`, `i=15215` for
`PublishSubscribe_GetSecurityKeys`).
**In-process publish/subscribe runtime:**
| Tool | Purpose |
| --- | --- |
| `pubsub_runtime_start_publisher` / `pubsub_runtime_start_subscriber` | Start an in-process UDP publisher / subscriber |
| `pubsub_runtime_publish` | Publish a DataSet update |
| `pubsub_runtime_read_received` | Read DataSets received by the subscriber |
| `pubsub_runtime_status` / `pubsub_runtime_stop` | Status / stop the runtime |
**Discovery** (Part 14 §7.2.4.6 — send a discovery request from the active
runtime and collect publisher responses):
| Tool | Purpose |
| --- | --- |
| `pubsub_discover_metadata` | Learn the field-level schema of a DataSetWriter (names, field count) |
| `pubsub_discover_writer_config` | Learn a publisher's WriterGroupId and DataSetWriterIds |
| `pubsub_discover_publisher_endpoints` | Learn a publisher's transport endpoint URLs |
**Actions** (Part 14 §7.2.5.6 — request/response over PubSub):
| Tool | Purpose |
| --- | --- |
| `pubsub_invoke_action` | Invoke an action target and await the correlated response |
| `pubsub_register_action_responder` | Register a demo/echo responder for round-trip testing |
| `pubsub_bind_action_method` | Bind an action to a server method (ObjectId/MethodId) |
| `pubsub_list_action_targets` / `pubsub_list_action_responders` | List known targets / registered responders |
**Capture and dissection:**
| Tool | Purpose |
| --- | --- |
| `pubsub_start_capture` / `pubsub_stop_capture` / `pubsub_capture_status` | Manage an in-process PubSub capture session |
| `pubsub_write_pcap` | Flush captured frames to `.pcap` / `.pcapng` |
| `pubsub_dissect_capture` | Dissect captured frames (decrypts encrypted UADP when a key log is supplied) |
| `pubsub_decode_pcap` | Decode a libpcap file of UDP PubSub traffic |
| `pubsub_load_keylog` | Load a PubSub key log for offline decryption |
## Architecture
The MCP tools ship as six libraries plus the executable that composes them.
The executable owns only transport, logging and CLI plumbing; every tool lives
in a library that an application can reference on its own.
```
tools/
├── Opc.Ua.Mcp.Core/ # Part 4 service tools, session manager, options, filters
│ ├── OpcUaMcpCoreExtensions.cs # AddOpcUaMcpCore, WithOpcUaCoreTools, WithOpcUaMcpFilters
│ ├── OpcUaSessionManager.cs # OPC UA client session lifecycle
│ ├── McpCapturePath.cs # Path-traversal guard shared by the capture packages
│ ├── Tools/
│ │ ├── ConnectionTools.cs # GetEndpoints, Connect, Disconnect, GetConnectionStatus
│ │ ├── AttributeServiceTools.cs # Read, Write, HistoryRead, HistoryUpdate
│ │ ├── ViewServiceTools.cs # Browse, BrowseNext, TranslateBrowsePaths, etc.
│ │ ├── NodeManagementServiceTools.cs# AddNodes, AddReferences, DeleteNodes, etc.
│ │ ├── MethodServiceTools.cs # Call
│ │ ├── SubscriptionServiceTools.cs # CreateSubscription, Publish, etc.
│ │ ├── MonitoredItemServiceTools.cs # CreateMonitoredItems, etc.
│ │ ├── DiscoveryServiceTools.cs # FindServers, RegisterServer, etc.
│ │ ├── PkiTools.cs # ListCertificates, TrustCertificate, etc.
│ │ ├── ConfigurationTools.cs # Full-profile compatibility configuration tools
│ │ ├── ConfigurationReadTools.cs # Profile-safe configuration reader
│ │ ├── ConfigurationUpdateTools.cs # Focused transport/client/security setters
│ │ ├── NodeSetExportTools.cs # ExportNodeSet, ExportNodeSetPerNamespace
│ │ └── ConvenienceTools.cs # ReadValue, BrowseAll, CallMethod, etc.
│ └── Serialization/
│ └── OpcUaJsonHelper.cs # OPC UA ↔ JSON type conversion
├── Opc.Ua.Mcp.PubSub/ # PubSub runtime, actions and discovery
│ ├── PubSubRuntimeManager.cs
│ └── Tools/{PubSubRuntime,PubSubAction,PubSubDiscovery}Tools.cs
├── Opc.Ua.Mcp.Diagnostics/ # UA-TCP capture, decode and replay
│ └── Tools/{PacketCapture,PacketDecode,PacketReplay}Tools.cs
├── Opc.Ua.Mcp.PubSub.Diagnostics/ # PubSub capture and decode
│ └── Tools/{PubSubCapture,PubSubDecode}Tools.cs
├── Opc.Ua.Mcp.Robotics/ # Robot Intent plus same-session Vision-guided picking
│ ├── RoboticsIntentManager.cs
│ ├── VisionGuidedRoboticsManager.cs
│ └── Tools/Robotics{Discovery,Monitoring,Control,Mission,Vision}Tools.cs
├── Opc.Ua.Mcp.Vision/ # Vision discovery, monitoring, seeing, inference, feedback, geometry
│ ├── VisionClientAccessor.cs
│ └── Tools/Vision{Discovery,Monitoring,Seeing,Inference,Feedback,Geometry}Tools.cs
└── Opc.Ua.Mcp/ # .NET 10 project, packaged as dotnet tool
├── Program.cs # Entry point, stdio + Streamable HTTP transport (/mcp)
├── McpHostBuilder.cs # Composes the six libraries
├── Opc.Ua.Mcp.Config.xml # OPC UA client application config
└── .mcp/server.json # MCP server manifest for NuGet discovery
```
### Packages
| Package | Tools | Depends on |
|---|---|---|
| `OPCFoundation.NetStandard.Opc.Ua.Mcp.Core` | Part 4 services, connection, configuration, PKI, NodeSet export | `Opc.Ua.Core`, `.Configuration`, `.Client` |
| `OPCFoundation.NetStandard.Opc.Ua.Mcp.PubSub` | PubSub runtime, actions, discovery | Core + `Opc.Ua.PubSub` |
| `OPCFoundation.NetStandard.Opc.Ua.Mcp.Diagnostics` | UA-TCP capture, decode, replay | Core + `Opc.Ua.Core.Diagnostics` |
| `OPCFoundation.NetStandard.Opc.Ua.Mcp.PubSub.Diagnostics` | PubSub capture, decode | Core + `Opc.Ua.PubSub.Diagnostics` |
| `OPCFoundation.NetStandard.Opc.Ua.Mcp.Robotics` | Robot Intent discovery, typed control/missions, paged monitoring, Vision-guided Pick | Core + `Opc.Ua.Robotics.Client` + `Opc.Ua.Vision.Client` |
| `OPCFoundation.NetStandard.Opc.Ua.Mcp.Vision` | Vision discovery, monitoring, seeing (`vision_get_frame` returns an MCP `ImageContentBlock`), inference, off-server feedback, §5.12 pose composition | Core + `Opc.Ua.Vision.Client` |
| `OPCFoundation.NetStandard.Opc.Ua.Mcp` | the ready-to-run `opcua-mcp` tool | all of the above |
The libraries multi-target `net8.0;net9.0;net10.0`; the executable targets
`net10.0`. `Mcp.Robotics` and `Mcp.Vision` are preview packages; the other
libraries and the `opcua-mcp` tool, which includes all of them, are stable.
### Embedding the tools in your own MCP server
An application that wants OPC UA tools *and* its own application-level tools
composes them rather than shelling out to `opcua-mcp`:
```csharp
using Microsoft.Extensions.DependencyInjection;
using Opc.Ua.Mcp;
builder.Services.AddOpcUaMcpCore();
builder.Services.AddMcpServer()
.WithStdioServerTransport()
.WithOpcUaMcpFilters()
.WithOpcUaCoreTools(McpToolProfile.Services)
.WithTools<MyApplicationTools>();
```
Add any of the other packages the same way — each contributes a service
registration and a tool registration:
```csharp
builder.Services.AddOpcUaMcpPubSub();
builder.Services.AddOpcUaMcpDiagnostics();
builder.Services.AddOpcUaMcpPubSubDiagnostics();
builder.Services.AddOpcUaMcpRobotics();
builder.Services.AddOpcUaMcpVision();
mcp.WithOpcUaPubSubTools(profile)
.WithOpcUaDiagnosticsTools(profile, diagnosticsEnabled)
.WithOpcUaPubSubDiagnosticsTools(profile, diagnosticsEnabled)
.WithOpcUaRoboticsTools(profile)
.WithOpcUaVisionTools(profile);
```
`WithOpcUaMcpFilters` registers the request and schema filters that make tool
errors actionable and tool schemas explicit. Call it once per server, even when
several OPC UA tool packages are composed.
`McpToolProfile` is shared vocabulary that lives in Core, and each package maps
the profile to its own tools. A profile a package does not own contributes
nothing rather than failing, so the same profile value can be passed to every
package a host references — `Full` in a host that never referenced
`Opc.Ua.Mcp.Diagnostics` simply yields no capture tools.
Bounded profiles that own their own connection tools — `Vision` in the
example above — carry `ConnectionTools` themselves in the single-profile
overload of `WithOpcUa...Tools`. When two or more bounded profiles are
composed through the `McpToolProfileSet` overloads (for example the
[`vision,robotics` composition](Vision.md#mcp-tools) the BinPickingClient
sample runs), the corresponding `WithOpcUaCoreTools(McpToolProfileSet)`
overload owns and deduplicates `ConnectionTools` across every package.
Each Vision or Robotics package's `McpToolProfileSet` overload never
registers `ConnectionTools` directly, so the composed catalog contains
one Connection surface, not several.
The capture tool classes stay `internal` and are reachable only through their
registration extensions.
## Security Notes
- The `autoAcceptCerts` parameter is for **testing only**. In production, configure proper certificate trust using the OPC UA certificate stores under `%LocalApplicationData%/OPC Foundation/pki/`.
- The server supports simultaneous named OPC UA sessions. Use the intended session identifier for each operation and disconnect that session when finished; connecting to another server does not require closing unrelated sessions.
- Application certificates are automatically created on first use and stored in the local certificate store.
- Logs are written to `%LocalApplicationData%/OPC Foundation/Logs/McpServer.log.txt`.
## Agent-Usability Quality Gate
Contributors can run the same deterministic static and live-probe grading used in CI:
```powershell
./tools/Opc.Ua.Mcp/Test-McpGrade.ps1
```
The script builds the server, checks out `mcpgrade` at the reviewed commit pinned in the script,
grades the `core` and `full` Streamable HTTP catalogs, and probes the full stdio catalog. Reports
are written under `artifacts/mcpgrade` by default.
The repository `.mcpgraderc.json` disables N002 after descriptions have been disambiguated.
`RegisterServer`/`RegisterServer2` are OPC UA standard service names, while
`ReadValue`/`ReadValues` and `GetConfiguration`/`SetConfiguration` are compatibility-sensitive
singular/plural and get/set pairs. These names must not be changed solely to satisfy edit-distance
heuristics.
## Requirements
- .NET 10 SDK or later
- An OPC UA server to connect to