The MCP server
pdcomm-modbus-mcp provides the Modbus simulator as an MCP (Model Context Protocol) server. AI tools such as Claude Code and Cursor start the server and call the service commands as MCP tools. Commands and results are the same as in the headless simulator.
- The transport is stdio: newline-delimited JSON-RPC 2.0 messages on standard input and output.
- One server process is one session. It doesn’t see a GUI or any other process that’s already running. To move state, load a project file with
modbus_project_importor communicate over real TCP. - Connected as a server, the MCP server keeps answering requests while it waits for input.
Connect an AI tool
Section titled “Connect an AI tool”The server command is the absolute path of bin/pdcomm-modbus-mcp in the SDK (pdcomm-modbus-mcp.exe on Windows). It takes no arguments.
Add it to the project’s .mcp.json:
{ "mcpServers": { "pdcomm-modbus": { "type": "stdio", "command": "/opt/pdcomm/pdcomm-modbus/bin/pdcomm-modbus-mcp" } }}Enter /mcp in a session to check the connection.
Add it to ~/.cursor/mcp.json or the project’s .cursor/mcp.json:
{ "mcpServers": { "pdcomm-modbus": { "type": "stdio", "command": "/opt/pdcomm/pdcomm-modbus/bin/pdcomm-modbus-mcp" } }}Any client that supports the stdio transport only needs the command path. Most use the same mcpServers format.
/opt/pdcomm/pdcomm-modbus is an example; use the path where you extracted the SDK. For a diagnostic log, add PDCOMM_LOG and PDCOMM_LOG_FILE under env in the client configuration. Log records never go to standard output.
The server provides 22 tools, one per service command with a modbus_ prefix. A tool’s arguments are the command’s other members; the server sets action from the tool name.
| Tool | Example arguments | Purpose |
|---|---|---|
modbus_capabilities, modbus_state |
{} |
Build features; current configuration and connection |
modbus_configure |
{"config":{...}} |
Change the configuration (while disconnected) |
modbus_connect, modbus_disconnect |
{} |
Open and close the connection |
modbus_request |
{"pdu":"0300000001","unit":1} |
Client request |
modbus_execute |
{"pdu":"060000002A"} |
Run against the local model |
modbus_data_get, modbus_data_set, modbus_model_set |
{"table":"holding_registers","address":0,"values":[1]} |
Read and change the data model |
modbus_diagnostics, modbus_diagnostics_set |
{} |
Diagnostics counters and register |
modbus_trace, modbus_trace_clear |
{} |
Request and frame record |
modbus_project_export, modbus_project_import |
{"project":{...}} |
Save and load projects |
modbus_fault |
{"transport":"rtu","bytes":"01030000000AC5CD"} |
Decode synthetic frames |
modbus_serial_ports |
{} |
List serial ports |
modbus_poll |
{} |
Serve once (the server polls by itself, so you rarely need it) |
modbus_logging_configure, modbus_logging_status, modbus_logging_flush |
{"config":{"level":"debug"}} |
Diagnostic log control |
JSON command reference describes every command’s members.
A session by hand
Section titled “A session by hand”To check the server without an AI tool, enter JSON-RPC messages one line at a time:
"$MODBUS_SDK/bin/pdcomm-modbus-mcp"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"tutorial","version":"1"}}}{"jsonrpc":"2.0","method":"notifications/initialized"}{"jsonrpc":"2.0","id":2,"method":"tools/list"}{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"modbus_execute","arguments":{"pdu":"060000002A"}}}{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"modbus_execute","arguments":{"pdu":"0300000001"}}}{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"modbus_request","arguments":{"pdu":"0300000001"}}}{"jsonrpc":"2.0","id":6,"method":"tools/call","params":{"name":"modbus_execute","arguments":{"pdu":"0300000001","unit":300}}}{"id":1,"jsonrpc":"2.0","result":{"capabilities":{"tools":{"listChanged":false}},"protocolVersion":"2025-11-25","serverInfo":{"name":"pdcomm-modbus","version":"1.0.0"}}}{"id":2,"jsonrpc":"2.0","result":{"tools":[{"description":"Configure diagnostic logging.","inputSchema":{"$schema":"https://json-schema.org/draft/2020-12/schema","additionalProperties":false,"properties":{"config":{...}},"required":["config"],"type":"object"},"name":"modbus_logging_configure","title":"Configure logging"},...]}}{"id":3,"jsonrpc":"2.0","result":{"content":[{"text":"{\"response\":[6,0,0,0,42],\"source\":\"local\",\"status\":\"success\"}","type":"text"}],"isError":false}}{"id":4,"jsonrpc":"2.0","result":{"content":[{"text":"{\"response\":[3,2,0,42],\"source\":\"local\",\"status\":\"success\"}","type":"text"}],"isError":false}}{"id":5,"jsonrpc":"2.0","result":{"content":[{"text":"Connect a client before sending a request","type":"text"}],"isError":true}}{"id":6,"jsonrpc":"2.0","result":{"content":[{"text":"Invalid arguments: unit must be at most 255","type":"text"}],"isError":true}}- Notifications (
notifications/initialized) get no reply. - The server accepts protocol versions
2025-11-25,2025-06-18and2024-11-05, and answers any other requested version with2025-11-25. - The
tools/listreply above is shortened. Each tool has a closed input schema (JSON Schema 2020-12) with the type, range and description of every argument. - The result is a JSON string in
content[0].text; parse it again on the client side. - Arguments that don’t match the input schema never reach the simulator. With protocol 2025-11-25 the call returns
isErrorand a text that starts withInvalid arguments:, as in the reply to id 6. With 2025-06-18 and 2024-11-05 it returns the JSON-RPC error{"error":{"code":-32602,"message":"Invalid arguments: unit must be at most 255"},"id":6,"jsonrpc":"2.0"}. - Closing the input ends the server.
Error layers
Section titled “Error layers”A failure in an MCP reply belongs to one of four layers:
| Layer | Shape | Examples |
|---|---|---|
| Modbus exception | isError is false; the result’s status is exception |
{"response":[131,2],...,"status":"exception"} |
| Service error | isError is true; text holds the error message |
Connect a client before sending a request, Timeout |
| Invalid arguments | isError is true with protocol 2025-11-25, or a -32602 error with earlier versions; the message starts with Invalid arguments: |
Invalid arguments: unit must be at most 255 |
| JSON-RPC error | An error object |
-32700 (JSON parse failure), -32601 (Unknown method or server is not initialized), -32602 (Unknown tool: <name>, Arguments must be an object) |
tools/listortools/callbeforeinitializereturns-32601.- A tool that doesn’t exist, such as
modbus_shutdown, returns-32602 Unknown tool: modbus_shutdown. - After a JSON parse error, the server exits with code 1. Tool calls with
isErrordon’t affect the exit code, so don’t judge every call by the process exit code alone.
Next steps
Section titled “Next steps”- Look up service error messages in Service errors.
- To search the PDComm documentation from AI tools, see Use the docs from AI tools.