Skip to main content
Glama

🏭 OPC UA MCP Server

Let Claude, Codex, Gemini and any other MCP agent read sensors, browse the plant and β€” only when you allow it β€” control equipment on any OPC UA server.

npm version PyPI version npm downloads CI MCP License: MIT

Quick start Β· Connect your agent Β· Tools Β· Production Β· Docs

OPC UA MCP Server in Claude Desktop

This project is a bridge between an AI agent and an OPC UA server. OPC UA is the protocol used to expose industrial equipment and its data; MCP (Model Context Protocol) lets an agent call tools to work with that data. The bridge runs on your computer and connects to an existing OPC UA server, or to a local simulator for a first test.

Your MCP client starts the bridge as a local stdio process: it exchanges MCP messages over standard input and output. One process connects one client context to one OPC UA endpoint. Use a separate process for each endpoint. Remote MCP and shared multi-client operation are gated by the identity and isolation RFC.

Features

  • πŸ”Œ Any OPC UA server β€” PLC, SCADA gateway or historian. Nothing to install on the plant side.

  • 🧰 The whole operator toolkit β€” read, browse, history and aggregates, subscriptions, events and alarms, writes and method calls.

  • πŸ›‘οΈ Read-only by default β€” writes and method calls need an explicit control profile and an authenticated server (pinned certificate or CA trust store). The operator profile also requires allowlisted targets, and every control call is audited.

  • 🐍 Python or Node β€” two first-class runtimes with the same tool contract. Use whichever you have, accounting for the documented runtime differences.

  • πŸ“¦ One-file Claude Desktop install β€” a .mcpb bundle with nothing else to set up.

flowchart LR
    A["AI agent<br/>(Claude, Codex, Gemini, Cursor…)"] -->|MCP over stdio| B["OPC UA MCP Server<br/>(Python or Node)"]
    B -->|OPC UA| C["OPC UA server<br/>(PLC / SCADA / historian)"]

Related MCP server: OT-AIops

Quick start

Follow these steps in order: choose an installation, start or identify an OPC UA server, configure your MCP client, then check the connection. You need an MCP client such as one of the agents listed below.

1. Choose an installation

Choose one runtime; you do not need both. The package routes fetch the server on demand, so you do not need to clone this repository to use an existing OPC UA server.

Route

Prerequisites

Command your MCP client launches

Node package

Node.js 22.13+, including npm and npx

npx -y opcua-mcp-server

Python package

Python 3.10+ and uv (which supplies uvx)

uvx opcua-mcp-server

Claude Desktop bundle

Claude Desktop; no separate Node or Python installation

Install the .mcpb file as described below

Single-file executable

Download for your platform; no Node or Python installation

Use its absolute path as the client command; see installation instructions

For a package route, check that it can run from your terminal. Run the command for your chosen runtime:

# Node
npx -y opcua-mcp-server --version

# Or Python
uvx opcua-mcp-server --version

These commands print a version and exit. In normal use, your MCP client launches the server without --version; you do not also start the bridge in a separate terminal. A bridge started by itself waits for MCP messages, rather than showing an interactive prompt or a web page.

2. Choose an OPC UA endpoint

For an existing server, get its endpoint URL, including any path, from its administrator (for example opc.tcp://192.168.0.10:4840). The bridge reads it from OPCUA_SERVER_URL, which you set in your MCP client's configuration in step 3. If unset, the default is opc.tcp://localhost:4840.

The default profile is observe (read-only), and the default OPC UA connection is unencrypted and anonymous. Use those defaults for the local mock. For real equipment, prepare the security configuration before connecting.

Try it without a plant

If you do not have an OPC UA server, start the bundled simulated plant. This optional route needs Git, Python 3.10+ and uv, even if you chose the Node bridge. Run these commands in a terminal:

git clone https://github.com/IndustriAgents/OPCUA-MCP.git
cd OPCUA-MCP
uv sync --all-packages
uv run --no-sync opcua-mock-server

Wait for Server is running and ready for connections, then leave this terminal running. The mock listens on port 4840; use opc.tcp://localhost:4840/freeopcua/server/ as your endpoint. Stop it with Ctrl-C when finished. If the port is busy, choose another port with uv run --no-sync opcua-mock-server --endpoint opc.tcp://127.0.0.1:14840/freeopcua/server/ and use that URL in your client configuration.

The plant supports browsing, sensor reads, data-change subscriptions, raw value history, plain events and event history. It also has writable actuators and methods, subject to the bridge's control policy. It does not implement processed aggregates or Alarms & Conditions: its plain alarm events cannot be listed as retained conditions, acknowledged or acted on with alarm-action tools. Use the separate mocks for those operations:

Mock

Endpoint

Coverage

Plant

opc.tcp://localhost:4840/freeopcua/server/

Sensors, actuators, methods, raw history, subscriptions and plain events

Aggregates

opc.tcp://localhost:4841/UA/Aggregate

Processed history, such as average buckets

Alarms

opc.tcp://localhost:4842/UA/Alarms

Active conditions and acknowledgement

The separate mocks need Node and npm; their READMEs give installation and startup commands. The MCP Inspector walkthrough has more examples, and the compatibility matrix records tested coverage.

Connect your agent

3. Configure the client and launch the bridge

Pick one client below. The examples use the local plant endpoint from step 2; replace opc.tcp://localhost:4840/freeopcua/server/ with your endpoint if needed. For a secure connection, add the settings from Going to production to the same env block (or the bundle's settings fields).

To use Python in a JSON configuration, set "command": "uvx" and "args": ["opcua-mcp-server"]. In a CLI registration command, replace npx -y opcua-mcp-server with uvx opcua-mcp-server. In TOML, use command = "uvx" and args = ["opcua-mcp-server"].

Easiest β€” the bundle. Download opcua-mcp-server-<version>.mcpb from the latest release, drag it into Settings β†’ Extensions, and fill in OPC UA endpoint. No Node, no Python, no JSON.

Or let the server write the config (needs Node or Python):

npm install -g opcua-mcp-server      # or: uv tool install opcua-mcp-server
opcua-mcp-server --install claude-desktop --url opc.tcp://localhost:4840/freeopcua/server/ --dry-run

It checks the config with the server's own startup validation and writes absolute paths, because Claude Desktop does not inherit your shell's PATH. The profile defaults to read-only; encryption, a pinned server certificate, a control profile, a policy file and an audit file are all flags, and passwords are never taken as flags. Check the preview, then repeat the command without --dry-run to write the configuration and restart Claude Desktop. Every flag and safety rule, editing claude_desktop_config.json by hand, and fixing a server that fails to start: docs/install.md.

claude mcp add opcua -e OPCUA_SERVER_URL=opc.tcp://localhost:4840/freeopcua/server/ -- npx -y opcua-mcp-server

Add --scope project to share it with your team through .mcp.json.

codex mcp add opcua --env OPCUA_SERVER_URL=opc.tcp://localhost:4840/freeopcua/server/ -- npx -y opcua-mcp-server

Or add it to ~/.codex/config.toml:

[mcp_servers.opcua]
command = "npx"
args = ["-y", "opcua-mcp-server"]
env = { OPCUA_SERVER_URL = "opc.tcp://localhost:4840/freeopcua/server/" }

Or let the server write that entry, with the same checks as for Claude Desktop: opcua-mcp-server --install codex --url opc.tcp://localhost:4840/freeopcua/server/ --dry-run. This installer command requires a permanent package installation first: npm install -g opcua-mcp-server or uv tool install opcua-mcp-server. Check the preview and repeat without --dry-run to write it.

Add to ~/.gemini/settings.json, or .gemini/settings.json in a project:

{
  "mcpServers": {
    "opcua": {
      "command": "npx",
      "args": ["-y", "opcua-mcp-server"],
      "env": { "OPCUA_SERVER_URL": "opc.tcp://localhost:4840/freeopcua/server/" }
    }
  }
}

In the agent panel, open β‹― β†’ MCP Servers β†’ Manage MCP Servers β†’ View raw config, add the entry below to mcp_config.json, and restart Antigravity:

{
  "mcpServers": {
    "opcua": {
      "command": "npx",
      "args": ["-y", "opcua-mcp-server"],
      "env": { "OPCUA_SERVER_URL": "opc.tcp://localhost:4840/freeopcua/server/" }
    }
  }
}

Add to ~/.cursor/mcp.json, or .cursor/mcp.json in a project:

{
  "mcpServers": {
    "opcua": {
      "command": "npx",
      "args": ["-y", "opcua-mcp-server"],
      "env": { "OPCUA_SERVER_URL": "opc.tcp://localhost:4840/freeopcua/server/" }
    }
  }
}

Add to .vscode/mcp.json β€” note the top-level key is servers:

{
  "servers": {
    "opcua": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "opcua-mcp-server"],
      "env": { "OPCUA_SERVER_URL": "opc.tcp://localhost:4840/freeopcua/server/" }
    }
  }
}

Add to ~/.codeium/windsurf/mcp_config.json:

{
  "mcpServers": {
    "opcua": {
      "command": "npx",
      "args": ["-y", "opcua-mcp-server"],
      "env": { "OPCUA_SERVER_URL": "opc.tcp://localhost:4840/freeopcua/server/" }
    }
  }
}

Most clients accept this mcpServers entry. The server speaks MCP over stdio.

{
  "mcpServers": {
    "opcua": {
      "command": "npx",
      "args": ["-y", "opcua-mcp-server"],
      "env": { "OPCUA_SERVER_URL": "opc.tcp://localhost:4840/freeopcua/server/" }
    }
  }
}
TIP

No Node or Python on the machine? Download a single-file executable from the latest release and use its path as the command β€” see docs/install.md. Several PLCs? Each entry talks to one endpoint, so add one entry per PLC (why).

4. Check the connection

Restart your desktop client or start a new CLI client session after configuring it, then enable or approve the opcua server if your client asks. The client starts the bridge automatically.

Ask: "Use get_server_status to check the OPC UA connection." A working connection reports connected: true. Then ask: "Browse the OPC UA variables and read the current temperature." Against the plant mock, this works with the default read-only profile.

If it fails, check that the OPC UA server is still running and that OPCUA_SERVER_URL matches its endpoint. If the bridge cannot launch, check its logs and the client command's path; desktop clients may need absolute paths. The installation guide explains client command paths, and testing troubleshooting covers startup, connection and tool errors.

What you can ask

  • "Show me all the variables in the system."

  • "What was the temperature over the last hour? Give me the hourly average."

  • "Watch the tank level and tell me what it does over the next minute."

  • "What alarms are active right now?"

  • "Set the valve position to 80%." β€” needs the operator profile

  • "Start production on line 1 at 100 units/hour." β€” needs the operator profile

These prompts depend on the connected server's nodes and capabilities. Server-computed averages need processed aggregate support, and active alarms need Alarms & Conditions; see the mock coverage above. Writes and method calls also need allowlisted targets and the security settings below, in addition to the operator profile.

Every reading comes back with its data type, status, timestamps and engineering units β€” never a bare number. What a result looks like.

Tools

15 tools, identical on both runtimes and defined once in contract/tools.json. Arguments, results and limits: docs/tools.md.

Access

Offered under

Tools

read

every profile, including the default observe

read_opcua_nodes Β· browse_opcua_nodes Β· read_opcua_history † Β· read_event_history † Β· get_server_status Β· list_subscriptions Β· list_active_alarms

monitor

every profile, including the default observe

subscribe_opcua_nodes Β· unsubscribe_opcua_nodes Β· subscribe_events Β· read_events

alarm-action

operator with OPCUA_ALLOW_ACKNOWLEDGE_ALARMS, or full

acknowledge_alarm Β· act_on_alarm

control

operator, for allowlisted targets only, or full

write_opcua_nodes Β· call_opcua_method

Control and alarm tools also need a verified server (a secured channel and a pinned OPCUA_SERVER_CERT or an OPCUA_SERVER_TRUST_STORE) unless a lab override is set. † Works only on a server that advertises the feature it needs, such as historical access.

Going to production

WARNING

Out of the box the agent can only read, but the OPC UA channel isunencrypted and anonymous β€” fine for the mock or a lab, not for real equipment. Secure the channel before connecting to anything that matters.

1. Secure the channel and pin the server β€” add these settings to your MCP client's env block, using paths to your actual certificate files. The following blocks show environment names and values; in JSON, enter them as quoted key/value pairs. Generate certificates and arrange trust with the OPC UA administrator as described in docs/certificates.md.

OPCUA_SECURITY_POLICY=Basic256Sha256     # implies SignAndEncrypt
OPCUA_CLIENT_CERT=/etc/opcua/client.pem  # this client's identity
OPCUA_CLIENT_KEY=/etc/opcua/client_key.pem
OPCUA_SERVER_CERT=/etc/opcua/server.pem  # pin the server you meant to reach

Choose one user authentication method alongside those channel settings. For username/password authentication, provide both:

OPCUA_USERNAME=mcp-operator
OPCUA_PASSWORD=<your-password>

Supply the real password through your client's secret storage or environment; the installer never accepts a password flag. See password handling for client-specific setup.

Alternatively, for X.509 user authentication, leave OPCUA_USERNAME and OPCUA_PASSWORD unset and provide both of these instead:

OPCUA_USER_CERT=/etc/opcua/user.pem
OPCUA_USER_KEY=/etc/opcua/user_key.pem

The user certificate and key identify the user; they are separate from OPCUA_CLIENT_CERT and OPCUA_CLIENT_KEY, which secure the channel. Keep the channel settings in either case: X.509 user authentication requires a security policy other than None.

Instead of pinning with OPCUA_SERVER_CERT, you can use OPCUA_SERVER_TRUST_STORE together with OPCUA_SERVER_APPLICATION_URI for CA trust-store verification; see the certificate guide.

2. Decide what the agent may do with OPCUA_PROFILE:

Profile

What the agent can do

observe (default)

Read, browse, history, subscriptions, events. No writes, no method calls.

operator

The above, plus only the nodes and methods you allowlist in OPCUA_ALLOWED_WRITE_NODES / OPCUA_ALLOWED_METHODS.

full

Every tool. Only for a tightly scoped OPC UA account.

3. Keep a record β€” set OPCUA_AUDIT_FILE for an append-only log of every control call.

Misconfiguration stops the server at startup with a message naming the variable, rather than failing later against live equipment. The MCP policy is defence in depth, not a replacement for OPC UA authorisation: scope the OPC UA account to the same nodes and methods.

Every setting, value bounds on writes and allowlists that survive a server restart: docs/configuration.md. Threat model: SECURITY.md. Certificates: docs/certificates.md.

Documentation

Guide

What's inside

Install

The .mcpb bundle, single-file executables, choosing a runtime, troubleshooting

Configuration

Every setting, profiles, allowlists, value bounds, secure connections, reconnection

Tools

Full tool reference, result shapes, request limits, partial results

Examples

Every tool with real calls and responses against the mock plant

Security

What is and is not verified, the audit trail, reporting a vulnerability

Certificates

Generating a client certificate a server will accept

Compatibility

Servers tested, and where the Python and Node runtimes differ

Architecture

How it fits together, and why

Testing

The test suite, MCP Inspector and agent walkthroughs

Roadmap Β· Changelog

What is next, and what has landed

Contributing

Contributions are welcome β€” see CONTRIBUTING.md for the project layout, local development and adding a tool to both runtimes.

The most useful thing you can send is a result from a real OPC UA server: open a compatibility report saying which server, which version and which tools worked. Test only on equipment you are authorised to use, and keep writes and method calls to a simulator or lab.

License

MIT β€” see LICENSE.

Tool inputs and structured results use JSON Schema draft 2020-12 with generated TypeScript/Python types and CI drift checks. See contract generation for the authoring workflow.

Available Tools

11 tools
browse_opcua_nodesA
Read-onlyIdempotent

Explore the OPC UA address space: list a node's children, walk a subtree, resolve a human-readable browse path to a node ID, or search for nodes by name. With the defaults it lists the immediate children of node_id. Raise depth to walk further down, set node_class to 'Variable' to find only readable tags, and include_values to read each one as it is found. browse_path addresses a node by name instead of by ID ('/Objects/Reactor/Temperature'), which is how to start when you do not know the numeric IDs; with depth 0 it simply resolves that path and returns the node it names. name_filter keeps only nodes whose browse name contains the text. Every node also reports its type_definition β€” what it is, as against what class it belongs to: 'AnalogItemType' for a tag that publishes a unit and a range, a subtype of 'AlarmConditionType' for something act_on_alarm applies to, 'FolderType' for structure. Every traversal is bounded by max_nodes and says in truncated whether it stopped early, so a short list is never mistaken for a complete one.

NameRequiredDescriptionDefault
depthNoHow many levels below the starting node to walk. 1 (the default) lists its immediate children; 0 returns only the starting node itself, which is how to resolve a `browse_path` to a node ID. Clamped to 64.
node_idNoWhere to start, as a node ID. Defaults to the Objects folder 'ns=0;i=85', the root of the plant's own address space. Ignored when `browse_path` is given.
max_nodesNoStop after inspecting this many nodes (default 500, clamped to 5000). A traversal that stops early sets 'truncated' in the result.
node_classNoKeep only nodes of this OPC UA NodeClass: 'Variable' for readable tags, 'Object' for structure, 'Method' for callable operations. Every class is returned when unset. Filtering does not prune the walk β€” an Object is still descended into when `depth` allows.
browse_pathNoWhere to start, as a slash-separated browse path resolved through the server ('/Objects/Reactor/Temperature', or 'Reactor/Temperature' relative to `node_id`). Use this when you know what a thing is called but not its node ID. A path the server cannot resolve is an error, not an empty result.
name_filterNoKeep only nodes whose browse name contains this text, case-insensitively. Searching for 'temp' below the Objects folder is how to find a tag whose exact name you do not know.
include_valuesNoRead each Variable's current value as it is found (default false). Costs one extra read per variable, so leave it off when mapping a large address space and read what you need with read_opcua_nodes afterwards.

Output Schema

NameRequiredDescription
resultYes
completenessYes

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/non-destructive, and the description adds context beyond them: traversals are bounded by max_nodes, truncation is reported via 'truncated' so a short list isn't mistaken for complete, include_values costs one extra read per variable, node_class filtering does not prune the walk, and an unresolvable browse_path is an error rather than an empty result. These are exactly the non-obvious behaviors an agent needs.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

It is front-loaded with the four modes before drilling into each parameter, and every sentence carries operational information. It is on the long side as a single undivided paragraph, which slightly hurts scannability, but there is no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

A 7-parameter traversal tool with an output schema is well covered: the description supplies the semantics the schema can't (type_definition, truncation, filter-vs-prune, path resolution errors) and doesn't waste space re-documenting return shapes the output schema already defines. Nothing an agent needs to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description earns a bump by adding semantics the schema does not: the depth-0 path-resolution idiom, the fact that node_class filtering is applied post-walk rather than pruning, and the explanation that type_definition describes what a node *is* versus its NodeClass. Minor gap: max_nodes' interaction with depth beyond the truncation note is only lightly treated.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb+resource ('Explore the OPC UA address space') and then enumerates the four concrete modes: list children, walk a subtree, resolve a browse path, search by name. It is clearly distinguishable from read_opcua_nodes (referenced explicitly as the way to fetch values afterwards) and from act_on_alarm (mentioned via 'AlarmConditionType').

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It states the default behavior ('with the defaults it lists the immediate children of node_id') and then gives conditional routing: use browse_path when you don't know numeric IDs, set depth 0 to resolve a path, use name_filter for unknown names, and leave include_values off when mapping a large address space, reading with read_opcua_nodes instead. That is explicit when-to-use, when-not-to-use, and named alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_server_statusA
Read-onlyIdempotent

Report whether this MCP server is connected to the OPC UA server and what that server says about itself: its endpoint and connection security, its ServerStatus (state, current time, start time, build info) and its NamespaceArray as index -> URI. The one tool that answers while the connection is down β€” it reports 'connected': false and why, rather than failing β€” so it is both the health check and the first thing to call when another tool cannot reach the server. The namespace array is also how to tell which 'ns=' index belongs to which namespace URI on this server. 'diagnostics' is the server's own ServerDiagnosticsSummary when it publishes one β€” session and subscription counts, and how many sessions and requests it has rejected β€” which is what separates 'the server is slow' from 'the server is refusing us'. OPC UA leaves diagnostics optional, so null there means this server does not say, not zero. 'capabilities' says which optional features β€” value history, event history, aggregates and their names β€” the server was found to offer, on which session and when: the answer to why read_opcua_history or read_event_history was refused.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
resultYes

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnlyHint/idempotentHint annotations, it discloses the key non-obvious behavior: it does not fail when the connection is down, it returns 'connected': false with a reason. It also warns that null diagnostics means the server does not publish them, not zero. That null-versus-zero distinction is genuinely important, though the description leans on detailed return-field semantics that the output schema already carries.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose is front-loaded in the first sentence, which is good, but the description runs long for a parameterless status tool and devotes several clauses to output fields the output schema already documents. Those clauses are informative but not all of them earn their space here.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter read-only diagnostic with an output schema and full annotation coverage, nothing an agent needs is missing. Between the annotations and the output schema the contract is fully covered, and the description fills the remaining gaps around failure behavior and diagnostic semantics.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Zero parameters, so there is nothing to disambiguate; the baseline applies. The description correctly spends no space on arguments and instead explains what the no-argument result means.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names the specific verb and resource β€” report whether the MCP server is connected to the OPC UA server, plus endpoint, security, ServerStatus and NamespaceArray. The second sentence explicitly separates it from the rest of the sibling set: it is the health check and the one tool that answers while the connection is down.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Guidance is explicit and conditional: call it first when another tool cannot reach the server, and consult 'capabilities' when read_opcua_history or read_event_history was refused. It also names the concrete use case of mapping 'ns=' indexes to namespace URIs, which no sibling covers.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_active_alarmsA
Read-onlyIdempotent

List the alarm and condition instances the server is currently retaining β€” those that are active, unacknowledged, or both. Asks the server for them directly (the ConditionRefresh method), so the answer is the current state of the plant rather than whatever this MCP server happened to be listening for. Use this to answer 'what is wrong right now'; use read_events to see what has happened since you last looked.

NameRequiredDescriptionDefault
node_idNoThe notifier node whose conditions to list. Defaults to the Server object 'ns=0;i=2253'.
timeout_secondsNoHow long to wait for the server to finish sending its retained conditions (default 5). Running out is an error, not an empty list: a partial answer cannot be told apart from 'no alarms'.

Output Schema

NameRequiredDescription
resultYes

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/non-destructive, but the description adds real value beyond them: it discloses that the data comes from a direct ConditionRefresh round-trip rather than passively collected subscription data, which changes how the agent should interpret completeness. The one remaining gap is return shape and volume, which the output schema covers.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, each earning its place: identity/scope first, mechanism second, routing guidance third. No filler or repetition of the title.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With a rich output schema, full parameter documentation, and annotations covering the safety profile, the description supplies everything an agent needs: what it returns, how it is fetched, and when to prefer it over the history sibling.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the schema already documents both node_id and the timeout's error-vs-empty-list semantics. The description adds no parameter-level detail beyond what is in the schema, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (list) and resource (alarm and condition instances the server is currently retaining), then defines the scope precisely as 'active, unacknowledged, or both'. An agent can distinguish this from read_events without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit decision rule: use this for 'what is wrong right now', use read_events for what has happened since you last looked. It names the sibling alternative and the condition that selects it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_subscriptionsA
Read-onlyIdempotent

List the active OPC UA data-change subscriptions, each with the value changes buffered for it since it was created. This is how to collect what subscribe_opcua_nodes has been watching; the same document is also readable as the 'opcua://subscriptions' resource.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
resultYes
completenessYes

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, and destructiveHint=false, so the safety profile is covered; the description adds real behavioral context by explaining that results include changes buffered since subscription creation. The notable omission is whether listing drains or leaves the buffer intact, which matters for repeated calls. Strong but not fully complete on mutation-side effects of a read.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences, both load-bearing: the first defines the payload and its buffered content, the second routes the agent from the subscribe sibling and offers the resource alternative. Nothing is repeated from the name or annotations.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, the description needn't explain return structure, and it correctly focuses on provenance of the data and the alternate access path. The only meaningful gap is buffer-consumption semantics, which an agent calling this repeatedly would want to know.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so there is nothing for the description to disambiguate and the baseline of 4 applies. The description does not need to explain any inputs.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb and resource ('List the active OPC UA data-change subscriptions') and adds scope that distinguishes it: each subscription comes with the value changes buffered for it since creation. An agent can tell this apart from subscribe_opcua_nodes, read_opcua_nodes, and list_active_alarms without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It states explicitly when to reach for this tool ('This is how to collect what subscribe_opcua_nodes has been watching') and names an equivalent alternative path (the 'opcua://subscriptions' resource). Both the use condition and the alternate access route are spelled out rather than inferred.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

read_event_historyA
Read-onlyIdempotent

Read events the OPC UA server stored, for a time range that has already passed. subscribe_events only sees what arrives after you subscribe, so it cannot answer 'what happened overnight' or 'what fired in the ten minutes before the line stopped' β€” the events are gone by the time anyone asks. This reads them back out of the server's own event archive instead. Same records as read_events and the same fields, so an alarm looks identical whether it was watched live or recovered afterwards. Needs a server that reports AccessHistoryEventsCapability β€” storing values and storing events are separate features and many servers do one without the other β€” and against one that does not, a call is refused with capability_not_supported and what to use instead. Point node_id at the notifier that emits the events β€” the Server object by default, which is where events are collected on most servers. The structured result's completeness says whether the range held more events than came back.

NameRequiredDescriptionDefault
node_idNoThe notifier node whose stored events to read. Defaults to the Server object 'ns=0;i=2253', which is where most servers collect events. This is the same node subscribe_events takes.
end_timeNoEnd of the time range, ISO-8601 with a timezone. Defaults to now.
num_valuesNoMaximum number of events to return, capped at 5000. 0 (the default) means as many as that cap allows, not every event in the range β€” an alarm burst can be tens of thousands of events, and asking for all of them is a request that never returns.
start_timeNoStart of the time range, ISO-8601 with a timezone: '2026-09-09T12:00:00Z' or '2026-09-09T14:00:00+02:00'. A timestamp with no timezone is refused rather than guessed. Defaults to one hour before `end_time`.
severity_minNoKeep only events of at least this OPC UA severity (1-1000). Default 0, which keeps every event. Filtered here rather than by the server, exactly as subscribe_events does, so a server that mishandles a filter cannot silently drop events.

Output Schema

NameRequiredDescription
resultYes
completenessYes

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnly/idempotent/non-destructive; the description adds substantially beyond them: a server capability prerequisite, the specific failure code capability_not_supported, the 5000-event cap, the risk of unbounded bursts, and that filtering happens client-side to avoid server misbehavior. It also explains that the `completeness` field reports truncation, which is real behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

It is front-loaded with the purpose and the sibling distinction before prerequisites and parameters, and no sentence is filler. It runs long and restates a couple of schema facts (defaults, node id value), which keeps it from a 5.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a five-parameter historical-read tool it covers the prerequisite, the failure mode, defaults, cap behavior, and truncation signal; an output schema exists so return-shape explanation is not owed. Nothing needed to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds cross-tool meaning: node_id is the notifier and the same node subscribe_events takes, and the result's `completeness` ties back to range size. Most other parameter detail (ISO-8601 formats, defaults, cap) duplicates the schema rather than extending it.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence names a specific verb and resource ('Read events the OPC UA server stored') and immediately scopes the time range to the past. It explicitly distinguishes itself from both subscribe_events (live only) and read_events (identical records, different path), so an agent can pick it without inspecting schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives a concrete when-to-use framing ('what happened overnight', 'what fired in the ten minutes before the line stopped') plus the exclusion that subscribe_events cannot answer retrospective questions. It also names when the call will be refused (no AccessHistoryEventsCapability) and what that refusal returns.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

read_eventsA

Read the events buffered by subscribe_events, oldest first. The events returned are removed from the buffer, so a second call returns only what has arrived since. If the buffer overflowed in the meantime, the result says how many were dropped rather than quietly skipping them β€” in the structured result's completeness, which also says whether more are still buffered.

NameRequiredDescriptionDefault
limitNoMaximum number of events to return (default 50).
node_idNoThe subscribed notifier node to read from, as passed to subscribe_events. Defaults to the Server object 'ns=0;i=2253'.

Output Schema

NameRequiredDescription
resultYes
completenessYes

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnlyHint=false and idempotentHint=false, and the description explains exactly why: returned events are removed from the buffer, making the call non-idempotent. It further discloses overflow behavior and points at the `completeness` field in the structured result, adding real behavioral context beyond the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, each carrying distinct information: source and ordering, drain semantics, and overflow reporting. Front-loaded with the core action and no redundant restatement of the tool name.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-optional-parameter read tool with an output schema, the description covers the essential non-obvious behavior: buffer draining, ordering, and where overflow/drop counts appear. Nothing an agent needs in order to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so `limit` and `node_id` are already documented in the schema. The description adds no additional meaning about parameter behavior or interactions, which is acceptable given the schema does the heavy lifting, but it does not exceed the baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Read the events buffered by subscribe_events') plus ordering semantics ('oldest first'), which cleanly separates this from history-oriented siblings like read_event_history. An agent can tell what it returns and where the data comes from without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description makes the prerequisite explicit by tying the tool to subscribe_events, and clarifies the calling pattern ('a second call returns only what has arrived since'). It does not explicitly contrast with read_event_history or state when to prefer one over the other, so it is clear context without full exclusion guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

read_opcua_historyA
Read-onlyIdempotent

Read what a node's value has been over time. Without aggregate_function this returns the raw stored readings, with ReturnBounds=false: no preceding/following boundary values or BadBoundNotFound placeholders. With it, the server computes one summary value per processing_interval over the range instead β€” 'Average' for a trend, 'Maximum'/'Minimum' for excursions, 'Count' for how often something was recorded β€” which is how to ask about a week of data without transferring a week of data. Needs a server that keeps history (AccessHistoryDataCapability) or offers aggregates, and aggregate_function needs the aggregates; a call the connected server cannot serve is refused with capability_not_supported and a suggestion, before anything is sent. get_server_status reports which of the two the server offers, and its aggregate function names. Aggregate reads follow native continuation points within the same call, preserving the original interval anchor; they return the completed range or an explicit error within the 5000-value bound. The structured result's completeness says whether the range held more than came back and how to continue; never infer that from the number of records.

NameRequiredDescriptionDefault
node_idYesThe OPC UA node ID to read history for. Example: 'ns=2;i=2'.
end_timeNoEnd of the time range, ISO-8601 with a timezone. Defaults to now.
num_valuesNoMaximum number of raw readings to return, capped at 5000. 0 (the default) means as many as that cap allows, not every reading in the range β€” a raw read of a year of fast data is a request that never returns. When the cap is reached the result says so, and the way to see the whole range is `aggregate_function` rather than a bigger number. Ignored when `aggregate_function` is given, where the number of results is decided by `processing_interval`.
start_timeNoStart of the time range, ISO-8601 with a timezone: '2026-09-09T12:00:00Z' or '2026-09-09T14:00:00+02:00'. A timestamp with no timezone is refused rather than guessed. Required when `aggregate_function` is given; for a raw read, defaults to one hour before `end_time`.
aggregate_functionNoSummarise each interval with this OPC UA aggregate instead of returning raw readings, by its OPC UA name: 'Average', 'Minimum', 'Maximum', 'Count', 'Total', 'Interpolative', ... Which ones the connected server offers is its own choice, listed by get_server_status under capabilities.aggregate_functions; a name it does not offer is refused with the list it does.
processing_intervalNoWidth in milliseconds of each interval the aggregate is computed over. 0 (the default) makes the whole range a single interval, giving one result. Only meaningful with `aggregate_function`.

Output Schema

NameRequiredDescription
resultYes
completenessYes

TDQS

A4.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint and destructiveHint=false, so the safety profile is covered. The description adds genuinely useful behavior beyond that: ReturnBounds=false with no boundary values or BadBoundNotFound placeholders, a pre-flight capability_not_supported refusal with a suggestion before anything is sent, continuation-point handling that preserves the interval anchor, and the 5000-value bound. It stops short of describing the shape of the returned records, leaving that to the output schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core action and the raw-versus-aggregate fork before any caveats, which is the right order. It is a dense single block, though, and a few phrases ("which is how to ask about a week of data without transferring a week of data") spend words on rhetoric rather than new information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers what an agent needs to call this correctly on a complex 6-parameter tool: mode selection, capability preconditions, error behavior, size limits, and an explicit instruction to read the structured result's completeness field rather than counting records. Return-value detail is delegated to the output schema, which exists.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the parameter docs are already thorough, including the num_values cap semantics, the timezone requirement on start_time, and the interaction between aggregate_function and processing_interval. The description mostly restates those rules in prose, adding only the reasoning that aggregate reads preserve the original interval anchor, so the baseline of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb and resource ("Read what a node's value has been over time") and immediately splits the tool into its two distinct modes: raw stored readings versus aggregate summaries. The capability gating and the pointer to get_server_status make it distinguishable from read_opcua_nodes and the other history siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use each mode: omit aggregate_function for raw readings, supply it to ask about a week of data without transferring a week of data, and use it rather than raising num_values when the cap is hit. It also names prerequisites (AccessHistoryDataCapability or aggregate support) and routes the agent to get_server_status to discover which applies.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

read_opcua_nodesA
Read-onlyIdempotent

Read the current value of one or more OPC UA nodes in a single request. Pass every node you need in one call: the OPC UA server answers them together, so reading fifty nodes costs one round trip rather than fifty. Each node comes back as its own record with the value, its data type, the OPC UA status code, and the source and server timestamps β€” the quality and the age are what decide whether a value can be acted on, and a bare number carries neither. A node the server rejects is one 'Bad…' status among the results, not a failure of the call.

NameRequiredDescriptionDefault
node_idsYesThe OPC UA node IDs to read, in the format 'ns=<namespace>;i=<identifier>'. Example: ['ns=2;i=2', 'ns=2;i=3']. A single node is a one-element list. At most 500 per call.
include_write_accessNoAlso report, per node, whether it can be written and what a write must look like (write_access): its type, whether it is an array, its enumeration states and labels, and the bounds a value must stay within. Set it before writing a node you have not written before. Costs one extra read of the nodes' attributes the first time a node is asked about in a session. Default false.

Output Schema

NameRequiredDescription
resultYes

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the readOnly/idempotent annotations: it explains the single-round-trip batching behavior, that each node returns its own record with value, data type, status code and both timestamps, and critically that a rejected node surfaces as a per-node 'Bad…' status rather than failing the whole call. That partial-failure semantics is exactly the kind of detail an agent cannot infer from structured fields.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four sentences, front-loaded with the action and the batching rule, then the return shape, then partial-failure semantics. The aside about quality and age ('a bare number carries neither') is rhetorical padding but does justify the returned metadata, so the cost is small.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers everything an agent needs before calling: what it reads, how to batch, what comes back per node, and how failures are represented. With an output schema present and annotations declaring the safety profile, the remaining details are already structured.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the node ID format, the 500-item cap and the include_write_access cost model are already fully documented in the schema. The description reinforces the batching intent of node_ids but adds no format or constraint detail beyond the schema, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Read the current value of one or more OPC UA nodes in a single request') and scopes it as a single batched read, which clearly separates it from siblings like read_opcua_history, browse_opcua_nodes, and subscribe_opcua_nodes.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives a concrete usage rule β€” 'Pass every node you need in one call' β€” and quantifies the benefit (fifty nodes, one round trip), which tells the agent to batch rather than loop. It never explicitly names an alternative tool or states when not to use this one (e.g. for historical values or continuous monitoring), so it stops short of full guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

subscribe_eventsA

Start buffering OPC UA events (alarms, condition changes, plain events) from a notifier node. Returns at once; the events themselves are collected in the background and read with read_events. Calling it again for the same node replaces the subscription and discards what it had buffered. Use this to watch for things happening; use list_active_alarms for what is wrong right now.

NameRequiredDescriptionDefault
node_idNoThe notifier node to subscribe to. Defaults to the Server object 'ns=0;i=2253', which is where most servers publish every event they raise.
buffer_sizeNoHow many events to hold before the oldest are dropped (default 100, clamped to 10000). read_events reports how many were dropped.
severity_minNoBuffer only events of at least this OPC UA severity (1-1000). Default 0, which keeps every event.

Output Schema

NameRequiredDescription
resultYes

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond annotations, it discloses important behavior: the call returns immediately, events buffer in the background, repeated calls replace the subscription, and buffered events are discarded on replacement. These details go well beyond the readOnly/idempotent/destructive annotations and help the agent avoid accidental buffer loss.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with purpose, then covers asynchronous return, replacement semantics, and alternatives in four efficient sentences. Every sentence provides actionable information with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the annotations, full schema descriptions, and output schema, the description is complete for correct invocation. It explains the asynchronous subscription model, replacement behavior, buffer discard effect, and the key sibling alternative.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already documents node_id, buffer_size, and severity_min, including defaults and clamping. The description does not add meaningful parameter-level detail beyond referring generally to a notifier node, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description gives a specific verb, resource, and scope: 'Start buffering OPC UA events (alarms, condition changes, plain events) from a notifier node.' It distinguishes the tool from siblings by naming read_events for retrieval and list_active_alarms for current problems.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly says to use this tool 'to watch for things happening' and to use list_active_alarms 'for what is wrong right now.' It also directs the agent to read_events for collected events, giving clear routing between subscription, retrieval, and current-state tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

subscribe_opcua_nodesA

Watch one or more OPC UA nodes for value changes instead of polling them. The OPC UA server pushes each new value into a buffer this MCP server keeps; collect them with list_subscriptions or the 'opcua://subscriptions' resource. This is the right way to answer 'tell me when this changes' or 'what has this been doing' over a short window β€” repeatedly calling read_opcua_nodes both misses changes between calls and costs a round trip each time. Each node gets its own subscription record and its own ID; cancel them with unsubscribe_opcua_nodes. Subscriptions are re-established automatically if the connection drops.

NameRequiredDescriptionDefault
node_idsYesThe OPC UA node IDs to monitor. Example: ['ns=2;i=3']. A single node is a one-element list. Each gets its own OPC UA subscription, and this server holds at most 200 at a time.
buffer_sizeNoHow many of the most recent changes to retain per subscription; older ones are discarded. Default 20, clamped to 1..1000.
deadband_typeNoFilter out changes too small to be worth reporting (OPC UA Part 4 Β§7.22). 'none' (the default) reports every change the server samples, which on a noisy analogue tag fills the buffer with sensor jitter in about a second. 'absolute' compares against `deadband_value` in the node's own engineering units. 'percent' reads `deadband_value` as a percentage of the node's EURange, and needs the node to publish one.
deadband_valueNoHow far the value must move to be reported, in engineering units for 'absolute' and in percent of EURange for 'percent'. Ignored when `deadband_type` is 'none' or unset; required when it is not.
sampling_intervalNoHow often (ms) the OPC UA server samples each node. 0 (the default) means sample at `publishing_interval`; a shorter interval queues several readings per publish.
data_change_triggerNoWhat counts as a change. 'statusValue' (the default) reports a change of value or of OPC UA status. 'status' reports only status transitions β€” useful for watching a tag go bad without caring what it reads. 'statusValueTimestamp' also reports a re-sample that changed nothing but the timestamp, which is how to tell 'the value is steady' from 'the value stopped updating'.
publishing_intervalNoHow often (ms) the OPC UA server publishes queued changes. Default 1000. Clamped to at least 50.

Output Schema

NameRequiredDescription
resultYes
completenessYes

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint=false, idempotentHint=false, destructiveHint=false, which says little about lifecycle. The description adds real behavior: the server buffers pushed values, each node gets its own subscription record and ID, subscriptions are re-established automatically if the connection drops, and results are retrieved through a separate tool or resource rather than returned here.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core verb and the anti-pattern it replaces, and every subsequent sentence carries distinct information (collection path, per-node records, cancellation, reconnect behavior). It runs four sentences, slightly longer than strictly necessary, but no sentence is filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a stateful, non-idempotent subscription tool with 7 parameters, the description covers the full lifecycle an agent needs: creation, retrieval of buffered values, cancellation, capacity context, and automatic re-establishment after disconnects. An output schema exists, so return-value detail is correctly left out.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the per-parameter docs are unusually rich (deadband semantics, trigger modes, clamping), so the schema does the heavy lifting. The description only adds that subscriptions are per-node and separately identified, which lightly informs node_ids but adds nothing about buffer_size, deadband, or intervals. Baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence gives a specific verb ('Watch') and resource ('one or more OPC UA nodes for value changes') and immediately frames it against polling. Later sentences distinguish it from read_opcua_nodes (polling) and point to list_subscriptions/unsubscribe_opcua_nodes, so an agent can separate it from every relevant sibling without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit when-to-use framing ('tell me when this changes' or 'what has this been doing' over a short window) plus an explicit when-not alternative: repeatedly calling read_opcua_nodes misses changes between calls and costs a round trip. It also names the follow-up tools (list_subscriptions, the opcua://subscriptions resource) and the cancel path (unsubscribe_opcua_nodes).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

unsubscribe_opcua_nodesA

Cancel one or more active data-change subscriptions and discard what they had buffered. Returns the subscriptions as they were at the moment they were cancelled, so anything still buffered can be read one last time. An ID that is not active is an error naming it, and no subscription is cancelled.

NameRequiredDescriptionDefault
subscription_idsYesThe subscription IDs returned by subscribe_opcua_nodes, as listed by list_subscriptions. Example: ['sub-1'].

Output Schema

NameRequiredDescription
resultYes
completenessYes

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnlyHint=false and destructiveHint=false, and the description adds real behavioral detail beyond them: buffered data is discarded but returned so it can be read one last time, and an unknown ID causes an error with no cancellation happening at all (all-or-nothing failure). It doesn't cover authorization requirements or whether cancellation is awaited/bounded by a timeout, but the failure semantics are unusually well disclosed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short sentences, front-loaded with the action, then return content, then failure behavior. No filler or repetition of the name.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists so return values need not be explained, and the description still covers the key mutation semantics (discard + last-read return, atomic failure on invalid IDs). For a one-parameter mutation tool this is close to complete; only auth/permission and concurrency caveats are absent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the single parameter is documented there, including the ID format and origin (subscribe_opcua_nodes, list_subscriptions). The description only echoes 'one or more active', which the schema already enforces via minItems 1; no additional semantics are added.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Cancel one or more active data-change subscriptions'), plus the exact consequence ('discard what they had buffered'). That is clearly distinguishable from subscribe_opcua_nodes, list_subscriptions, and the read_* siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The context is clear: this is for tearing down active subscriptions, and the ID source is implied by 'active'. However, it never names an alternative or an exclusion (e.g., when to prefer list_subscriptions to check state first, or what to do for a non-existent ID besides expecting an error).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 11 tool updatesv0.1.0
    • First observedbrowse_opcua_nodes
    • First observedget_server_status
    • First observedlist_active_alarms
    • First observedlist_subscriptions
    • First observedread_event_history
    • First observedread_events
    • First observedread_opcua_history
    • First observedread_opcua_nodes
    • First observedsubscribe_events
    • First observedsubscribe_opcua_nodes
    • First observedunsubscribe_opcua_nodes

TDQS

A4.3/5.0

Scored across 11 tools

Disambiguation4/5

Most tools target clearly different operations: browsing vs current reads vs history vs live data-change subscriptions vs live events vs alarm listing. The closest pairs are read_events/read_event_history and subscribe_events/subscribe_opcua_nodes, whose names are similar but whose descriptions clearly separate live buffering from archived retrieval and event streams from data-change streams. Slight ambiguity remains from those parallel names, but the descriptions prevent likely misselection.

Naming Consistency4/5

All names follow a snake_case verb_noun pattern, which is readable and predictable. The main inconsistency is the optional 'opcua' qualifier: read_opcua_nodes, browse_opcua_nodes, read_opcua_history, subscribe_opcua_nodes, and unsubscribe_opcua_nodes use it, while read_event_history, subscribe_events, read_events, list_subscriptions, and list_active_alarms do not. This is a minor deviation rather than a chaotic naming scheme.

Tool Count5/5

At 11 tools, the set is well within a reasonable range for an OPC UA monitoring and browsing server. Each tool covers a distinct capability: status, browse, current read, historical read, data-change subscription lifecycle, event subscription/read, event history, active alarms, and subscription listing. No tool appears redundant or purely filler.

Completeness3/5

The read/monitoring surface is fairly complete: browse, current values, historical values, live data changes, event buffering, event history, alarms, and server capability discovery are all present. However, core OPC UA write operations such as writing node values or calling methods are absent, and alarm acknowledgement is not exposed even though alarm listing is. There is also a reference to an 'act_on_alarm' tool in the browse description that is not present in the tool set, indicating a notable surface gap.

Maintenance

ActivityActive
ResponsivenessSlow

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Connects AI agents to OPC UA-enabled industrial systems for real-time monitoring and control of operational data. It enables users to read, write, and browse industrial device nodes through natural language interactions.
    MIT
  • A
    license
    B
    quality
    A
    maintenance
    Provides AI agents with safe, governed read access to industrial control systems (OPC-UA, Modbus, S7, Mitsubishi, MTConnect, MQTT/Sparkplug) plus cross-protocol diagnostics for troubleshooting data breaks, alarm floods, and unhealthy tags.
    156
    9
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables LLMs to read, write, browse, search, and subscribe to live data on OPC-UA industrial automation servers, with caching, discovery index, and support for stdio or HTTP transports.
    5
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to discover, browse, read, search, and safely write OPC UA industrial device data within controlled edge networks, with guarded two-phase writes and evidence-based root-cause diagnostics.
    1
    Apache 2.0