ControlForge MCP Server Guide
James M. Belcher
Founder, JMB Technical Services LLC
April 2026 | ControlForge v1.0.540
1. Overview
ControlForge includes a built-in MCP (Model Context Protocol) server that gives AI assistants like Claude Code, Cursor, Windsurf, and other MCP-compatible tools direct access to all PLC functionality. With 80 tools covering programs, tasks, variables, HMI, debugging, protocol analysis, fleet management, and more — an AI assistant can build, deploy, and debug complete PLC applications without touching the IDE.
The MCP server is built into the ControlForge binary. No external dependencies, no separate installation, no Node.js required.
| Capability | Tools Used |
|---|
| Learn the system | controlforge_coding_rules, controlforge_capabilities, controlforge_guide_search, controlforge_functions |
| Write and validate ST code | controlforge_functions (verify), controlforge_program_validate, controlforge_program_create |
| Deploy programs | controlforge_deploy (one-call), or controlforge_program_create → controlforge_task_set_programs → controlforge_task_reload |
| Build HMI dashboards | controlforge_hmi_template, controlforge_hmi_create, controlforge_hmi_update |
| Monitor variables | controlforge_variable_list, controlforge_variable_get, controlforge_variable_set, controlforge_watch_create |
| Debug programs | controlforge_debug_enable, controlforge_debug_set_breakpoint, controlforge_debug_step_over |
| Analyze protocols | controlforge_analyzer_start, controlforge_analyzer_transactions, controlforge_analyzer_decode |
| Manage fleet | controlforge_fleet_discover, controlforge_fleet_list |
| Configure the system | controlforge_config_get, controlforge_config_update, controlforge_nodered_status |
2. Quick Start
2.1 Start ControlForge
./controlforge project.goplc --api-port 8082
2.2 Add the MCP Server to Claude Code
claude mcp add controlforge -- /path/to/controlforge mcp
That's it. Next time you start Claude Code, all 80 tools will be available. The MCP server connects to your running ControlForge instance over HTTP.
2.3 Add to Other MCP Clients
For any MCP-compatible client, configure a stdio transport:
{
"mcpServers": {
"controlforge": {
"command": "/path/to/controlforge",
"args": ["mcp"]
}
}
}
2.4 Authentication
If your ControlForge instance has JWT authentication enabled, set the token as an environment variable:
export CONTROLFORGE_AUTH_TOKEN="your-jwt-token-here"
The MCP server reads CONTROLFORGE_AUTH_TOKEN at runtime and includes it as a Bearer header on every request.
3. How It Works
The MCP server uses JSON-RPC 2.0 over stdio. It acts as a lightweight HTTP client — translating MCP tool calls into REST API requests against a running ControlForge instance.
Every tool accepts host and port parameters, so a single MCP session can manage multiple ControlForge instances. The host parameter supports shorthand: "45" expands to "10.0.0.45".
| Tool | Description |
|---|
controlforge_coding_rules | Read this first. Mandatory ST coding rules — function verification, code style, GVL patterns, common mistakes. No parameters needed. |
controlforge_capabilities | Supported IEC 61131-3 features, data types, and limits |
controlforge_functions | Search available ST functions by name or category. Authoritative source — if a function isn't returned here, it doesn't exist. Supports search, category, limit, names_only filters. |
controlforge_function_blocks | List all function blocks (TON, TOF, CTU, PID, etc.) — stateful blocks requiring instance variables |
controlforge_guide_search | Programming guides — GVL, cross-task communication, hardware I/O patterns |
Example: Search for Modbus functions
controlforge_functions(host="localhost", port=8082, search="modbus", limit=10)
| Tool | Description |
|---|
controlforge_program_list | List all programs |
controlforge_program_get | Get source code and metadata for a program |
controlforge_program_create | Create a new program or GVL |
controlforge_program_update | Update an existing program's source |
controlforge_program_delete | Delete a program |
controlforge_program_validate | Validate ST source without saving — catches syntax errors before deploy |
controlforge_deploy | One-call deploy — validates, creates programs, assigns to task, and reloads. Collects all errors so you can fix them in one pass. |
Example: Deploy a temperature controller
controlforge_deploy(
host="localhost", port=8082,
task="MainTask",
programs=[
{name: "GVL_Temp", source: "VAR_GLOBAL (GVL_Temp)\n temp : REAL := 22.5;\n setpoint : REAL := 25.0;\nEND_VAR"},
{name: "POU_TempCtrl", source: "PROGRAM POU_TempCtrl\nVAR\n error : REAL;\nEND_VAR\n error := GVL_Temp.setpoint - GVL_Temp.temp;\nEND_PROGRAM"}
]
)
| Tool | Description |
|---|
controlforge_task_list | List all tasks with scan times, programs, and stats |
controlforge_task_get | Get task details (config + runtime state) |
controlforge_task_create | Create a new task (cyclic execution container) |
controlforge_task_update | Update scan time, priority, or watchdog settings |
controlforge_task_set_programs | Set which programs/GVLs run in a task |
controlforge_task_reload | Hot-reload a single task without stopping others |
controlforge_task_start | Start a specific task |
controlforge_task_stop | Stop a specific task |
| Tool | Description |
|---|
controlforge_variable_list | List all variables with current values |
controlforge_variable_get | Read a single variable by name |
controlforge_variable_set | Write a value to a variable |
controlforge_variable_bulk_get | Read multiple variables in one call |
controlforge_tag_list | List I/O tags (driver-mapped variables) |
4.5 HMI Pages (6 tools)
| Tool | Description |
|---|
controlforge_hmi_template | Get a starter HTML template with controlforge-hmi.js wired up |
controlforge_hmi_list | List all HMI pages |
controlforge_hmi_get | Get a page's HTML source |
controlforge_hmi_create | Create a new HMI page (served at /hmi/<name>) |
controlforge_hmi_update | Update a page's HTML |
controlforge_hmi_delete | Delete a page |
HMI pages are full HTML documents that use the controlforge-hmi.js helper library:
<script src="/hmi/controlforge-hmi.js"></script>
controlforge.read("GVL_IO.temperature")
controlforge.write("GVL_IO.setpoint", 25.0)
controlforge.subscribe(function(vars) {
}, 500)
controlforge.subscribeTo(["GVL_IO.temp", "GVL_IO.pressure"], callback, 500)
controlforge.connect(function(data) { })
controlforge.info()
controlforge.runtime()
| Tool | Description |
|---|
controlforge_runtime_status | Get runtime state, uptime, scan time |
controlforge_info | Get server info (version, platform) |
controlforge_runtime_start | Start the runtime — begins executing all tasks |
controlforge_runtime_stop | Stop the runtime |
controlforge_runtime_pause | Pause execution (can resume) |
controlforge_runtime_resume | Resume from pause |
controlforge_runtime_reload | Reload all programs without full restart |
| Tool | Description |
|---|
controlforge_debug_enable | Enable the step debugger |
controlforge_debug_disable | Disable the step debugger |
controlforge_debug_state | Get debugger state — stopped line, call stack, variables |
controlforge_debug_set_breakpoint | Set a breakpoint at a program:line |
controlforge_debug_list_breakpoints | List all breakpoints |
controlforge_debug_continue | Continue to next breakpoint |
controlforge_debug_step_into | Step into function/FB call |
controlforge_debug_step_over | Step over (execute without entering) |
controlforge_debug_step_out | Step out of current function/FB |
Example: Debug a program
controlforge_debug_enable(host="localhost", port=8082)
controlforge_debug_set_breakpoint(host="localhost", port=8082, program="POU_Main", line=15)
controlforge_debug_continue(host="localhost", port=8082)
controlforge_debug_state(host="localhost", port=8082) // see where it stopped
controlforge_debug_step_over(host="localhost", port=8082)
| Tool | Description |
|---|
controlforge_watch_list | List all watch windows |
controlforge_watch_create | Create a watch window for a set of variables |
controlforge_watch_get | Poll current values for a watch window |
controlforge_watch_delete | Delete a watch window |
| Tool | Description |
|---|
controlforge_faults | Get active faults and error conditions |
controlforge_diagnostics | Get scan times, memory usage, task stats |
controlforge_logs | Get recent log entries |
controlforge_drivers | List I/O drivers and their connection status |
| Tool | Description |
|---|
controlforge_nodered_status | Check if Node-RED is running |
controlforge_nodered_start | Start Node-RED subprocess |
controlforge_nodered_stop | Stop Node-RED |
controlforge_nodered_restart | Restart Node-RED |
controlforge_fuxa_status | Check if FUXA SCADA is reachable |
controlforge_config_get | Get current runtime configuration (YAML) |
controlforge_config_update | Update runtime configuration |
controlforge_config_export | Export configuration as YAML file |
| Tool | Description |
|---|
controlforge_library_list | List loaded ST libraries and their functions |
controlforge_library_get | Get a library's metadata and source |
| Tool | Description |
|---|
controlforge_fleet_discover | Scan local network for ControlForge instances via mDNS |
controlforge_fleet_list | List known fleet nodes (filter by role, tier, family) |
| Tool | Description |
|---|
controlforge_analyzer_status | Get capture status and statistics |
controlforge_analyzer_start | Start capturing protocol traffic (filter by protocol, device, direction) |
controlforge_analyzer_stop | Stop capture |
controlforge_analyzer_transactions | Get captured transactions with decoded fields |
controlforge_analyzer_decode | Decode a raw hex packet offline |
Supported protocols: Modbus TCP/RTU, FINS TCP/UDP, EtherNet/IP, S7, OPC UA, DNP3, BACnet, SEL
| Tool | Description |
|---|
controlforge_l5x_import | Import Rockwell L5X file → ST programs |
controlforge_l5x_export | Export ST program → L5X XML for Studio 5000 |
| Tool | Description |
|---|
controlforge_saf_status | Get offline buffering stats |
controlforge_saf_pending | Get pending messages not yet forwarded |
| Tool | Description |
|---|
controlforge_io_list | List all I/O point mappings (%IX, %QX, %IW, %QW, %MW) |
controlforge_io_create | Create a new I/O point mapping |
| Tool | Description |
|---|
controlforge_project_save | Save project to disk |
controlforge_cluster_status | Get cluster status for all connected nodes |
5. Recommended Workflow for AI Assistants
When building a PLC application from scratch, follow this order:
Step 1: Learn the System
controlforge_coding_rules() // Mandatory — learn the rules
controlforge_capabilities(host, port) // What data types and features exist
controlforge_guide_search(host, port) // GVL patterns, cross-task, hardware I/O
Step 2: Search for Functions
controlforge_functions(host, port, search="modbus") // Verify EVERY function before using it
controlforge_function_blocks(host, port) // Check available FBs (TON, PID, etc.)
Step 3: Write and Validate Code
controlforge_program_validate(host, port, source="...") // Check syntax before deploying
Step 4: Deploy
controlforge_deploy(host, port, task="MainTask", programs=[...]) // One call does it all
Step 5: Monitor
controlforge_variable_list(host, port) // See all variables
controlforge_variable_get(host, port, name="...") // Read specific values
controlforge_watch_create(host, port, tags=[...]) // Set up monitoring
Step 6: Build HMI
controlforge_hmi_template() // Get starter HTML
controlforge_hmi_create(host, port, name="dashboard", content="...", title="My Dashboard")
Step 7: Debug (if needed)
controlforge_debug_enable(host, port)
controlforge_debug_set_breakpoint(host, port, program="POU_Main", line=15)
controlforge_debug_state(host, port) // Inspect stopped state
6. Testing the MCP Server
A comprehensive test suite is included:
go build -o goplc-dev ./cmd/controlforge
./goplc-dev project.goplc --api-port 8082
python3 tests/mcp_test.py
python3 tests/mcp_test.py --group debug,hmi,deploy
python3 tests/mcp_test.py --host 10.0.0.34 --port 8302
python3 tests/mcp_test.py --list
The test suite exercises all 80 tools in 18 groups and typically completes in under 5 seconds.
7. Troubleshooting
MCP server not responding
- Ensure the binary is built:
go build -o controlforge ./cmd/controlforge
- Test directly:
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | ./controlforge mcp 2>/dev/null
- Ensure a ControlForge instance is running on the target host:port
- Check firewall:
sudo ufw allow 8082/tcp
- Verify with curl:
curl http://localhost:8082/api/info
Authentication errors
- Set
export CONTROLFORGE_AUTH_TOKEN="your-token" before starting the MCP client
- The token is read from the environment on every request
- Run
tools/list to see all registered tools
- Ensure you're running the latest binary (check version with
controlforge_info)