acm_mcp
MCP server for building Rockwell Automation PLC project plans by browsing the ACM catalog, resolving library dependencies, and assembling project hierarchies with automatic placement.
README
acm_mcp
MCP server for building PLC automation project plans using the Rockwell Automation ACM (Application Code Manager) library catalog.
An LLM client connects to this server to browse the ACM catalog, resolve linked library dependencies, and assemble a project hierarchy — controllers, tasks, programs, routines, and AOI definitions — with automatic placement rules and deduplication.
Quick Start
cd acm_mcp
uv sync # install dependencies (creates .venv)
uv run python server.py # start server on port 8012
The server loads the library catalog from data/data.db on startup and exposes tools over streamable-http at http://127.0.0.1:8012/mcp.
Configuration
All settings are in config.yaml:
server:
host: "127.0.0.1"
port: 8012
log_level: "INFO"
transport: "streamable-http"
ssl_certfile: "" # path to TLS cert for HTTPS (optional)
ssl_keyfile: "" # path to TLS key for HTTPS (optional)
acm_browser:
working_dir: "data" # relative to this directory
pull_libraries: false # set true to re-export from ACM
pull_templates: false
pull_database: false
resolver:
max_depth: 10 # linked library recursion limit
Populating the Database
The server relies on a SQLite database (data/data.db) built from ACM library exports. On startup, it reads the three pull_* flags in config.yaml to decide whether to re-run each step of the pipeline:
| Flag | What it does | ACM Browser call |
|---|---|---|
pull_libraries |
Runs ACMConsole to export registered libraries (HSL4 files) into the working directory | ExportRegisteredLibraries() |
pull_templates |
Converts exported HSL4 files into XML templates using ACMTemplateGen.CLI.exe |
ParseHSL4IntoTemplate() |
pull_database |
Clears and rebuilds data.db by parsing templates, extracting parameters, decoding PDF docs, and linking everything |
fillDatabase() |
First-time setup (no existing data.db)
Set all three flags to true in config.yaml to run the full pipeline:
acm_browser:
working_dir: "data"
pull_libraries: true
pull_templates: true
pull_database: true
Then start the server:
uv run python server.py
This will export libraries from ACM, convert them to templates, and populate data.db. The process requires ACM to be installed on the machine.
Subsequent runs (data.db already exists)
Set all flags to false to skip re-export and reuse the existing database:
acm_browser:
working_dir: "data"
pull_libraries: false
pull_templates: false
pull_database: false
This is the default configuration and makes startup much faster.
Refreshing the catalog
To pick up newly registered ACM libraries, set all three flags back to true and restart the server. You can also selectively re-run steps — for example, set only pull_database: true if you've manually updated template files and just need to rebuild the database.
Tools
| # | Tool | Description |
|---|---|---|
| 1 | resolve_library |
Preview a library's dependencies before adding it. Returns tiered results: tier_1_mandatory (REQUIRED, AOI_AUTO_ADDED, DEDUP, SELECT), tier_2_optional (user decides), tier_3_conditional (conditionals, SELECTs). |
| 2 | search_libraries |
Search the catalog by text query, LibraryType, Category, or ContentType. |
| 3 | fuzzy_search_libraries |
Fuzzy name search across the catalog. Use when you have an approximate or misspelled name. Returns results ranked by similarity score. |
| 4 | list_controllers |
List all available controller types (plain and MachineBuilder variants). |
| 5 | add_controller |
Add a controller as the root node. Accepts optional parameter_overrides (JSON string). MachineBuilder controllers auto-create pre-built tasks. |
| 6 | add_task |
Create an empty task under a controller (PERIODIC, EVENT, or CONTINUOUS). |
| 7 | add_program |
Create an empty program under a task. |
| 8 | add_item |
Add a library item with automatic placement. AOI dependencies are auto-registered; all other dependencies appear in todo for explicit linking via resolve_link. |
| 9 | resolve_link |
Bind an unresolved dependency on a node to a specific instance in the project. Replaces resolve_select — works with all link types (REQUIRED, SELECT, CONDITIONAL-MET, OPTIONAL). |
| 10 | get_unresolved_links |
List all unresolved dependency links across the entire project, with existing instances that match each link's candidates. |
| 11 | remove_item |
Remove a node and all children (cascade). Automatically unresolves any links on remaining nodes that pointed to a removed node, restoring them to their original tag. Reports broken links in the response. |
| 12 | get_project_views |
Render the project as two text trees: Class View (by type) and Preview (hierarchy). |
| 13 | get_project_state |
Get the full project state as structured JSON. |
| 14 | rename_instance |
Rename any node in the project. |
| 15 | set_project_name |
Set the project name (used as filename on export). |
| 16 | export_project |
Save the project state to projects/{name}/{name}.json. |
| 17 | load_project |
Load a previously saved project from disk. |
| 18 | list_projects |
List all saved project files. |
| 19 | instantiate_in_acm |
Generate ACM XML scripts from an exported project JSON and invoke ACMConsole to instantiate the project in ACM. Uses separate BEGINCREATE/ENDCREATE blocks for project, controller, and objects so the controller's pre-built tasks are fully instantiated before objects reference them. Pre-built items (CLX auto-created tasks/programs) are excluded from the objects XML — their parent HSL4 definitions create them automatically. Does not generate an ACD file — use export_acd for that. |
| 20 | export_acd |
Export the ACM project to an ACD file. Must be called after instantiate_in_acm. |
| 21 | reset_project |
Reset the project, clearing all in-memory state. Use export_project first to save work. |
Typical Workflow
- Server injects planning context automatically via FastMCP
instructions(loaded fromprompts/context_injection.md) - Client clarifies vague requests before making tool calls — asks the user what they want, where it goes, and any configuration choices
- Client searches the catalog with
search_libraries,fuzzy_search_libraries, orlist_controllers— presents results to user, never assumes first match - Client calls
resolve_libraryto inspect dependencies before committing - Client adds a controller with
add_controller - Client creates tasks/programs with
add_task/add_program(required for plain controllers; MachineBuilder controllers come with pre-built tasks) - Client adds library items with
add_item— placement is automatic, AOI dependencies are auto-registered - Client presents
todoitems to the user for eachadd_itemresponse — never silently resolves links - Client adds all needed items (in any order), then resolves dependencies with
resolve_linkafter presenting binding choices to the user - Client calls
get_unresolved_linksto see all pending dependencies across the project - Client calls
get_project_viewsafter each change to show the user - Client calls
export_projectto save (blocks if mandatory links are unresolved) - Optionally, client calls
instantiate_in_acmto instantiate the project in ACM, thenexport_acdto generate an ACD file
Project Structure
acm_mcp/
├── server.py # MCP server entry point (21 tools)
├── config.yaml # Server and ACM configuration
├── pyproject.toml # uv project definition
├── uv.lock # Locked dependencies
├── .python-version # Python 3.13
├── models/
│ ├── library.py # Library, PlacementRole, LinkedLibraryRef
│ └── project_state.py # InstanceNode, ProjectState tree
├── services/
│ ├── library_store.py # In-memory catalog with search indexes
│ ├── resolver.py # Recursive linked library resolution
│ ├── project_manager.py # Stateful project builder + export/load
│ └── renderer.py # Class View and Preview tree rendering
├── prompts/
│ └── context_injection.md # LLM planning context (loaded as FastMCP instructions)
├── data/
│ ├── data.db # SQLite catalog (524 libraries)
│ ├── Templates/ # 524 Template.xml source files
│ └── instance_templates/ # XML templates for ACD generation (project, controller, object)
└── projects/ # Saved project files (JSON)
Key Concepts
- Routine-First Thinking: The routine is the fundamental working piece — the function call that invokes the AOI (the function definition). When a user asks to "add a conveyor," start by finding the routine and work outward to program, task, and controller.
- PlacementRole: Determined by ContentType + LibraryType. Controls where items go in the hierarchy (controller, task, program, routine, AOI).
- Linked Library Resolution: Recursive (max depth 10) with explicit, generic (wildcard), template, and conditional resolution modes. Recursion only enters AOI_AUTO_ADDED dependencies — REQUIRED items get their own resolution when manually added, preventing transitive dependencies from leaking onto parent nodes as duplicate ILLib entries.
- Dependency Tags: Each dependency gets one tag:
AOI_AUTO_ADDED,DEDUP,REQUIRED,OPTIONAL,CONDITIONAL-MET,CONDITIONAL-SKIP,CONDITIONAL-UNKNOWN,SELECT,TEMPLATE,NOT-FOUND, orRESOLVED. AOIs are auto-handled; all others require explicit user decisions. - Instance-Level Binding: Links bind to specific project instances via
target_node_id, not catalog numbers. This supports many-to-many scenarios (e.g., multiple Equipment Modules linking to different Unit Modules). - No Auto-Resolver: Dependencies are not silently resolved. The agent must present choices to the user and call
resolve_linkexplicitly. Items can be added in any order. - AOI Deduplication: Asset-Control items are globally deduplicated — one definition serves all instances. Tier 1 and tier 3 condition-met AOIs are auto-registered; tier 2 AOIs require user confirmation.
- Cascade Removal with Link Cleanup:
remove_itemdeletes the target node and all its children, then walks the remaining project tree to find any resolved links that pointed to a removed node. Those links are automatically unresolved and restored to their original tag (e.g., SELECT, REQUIRED). Broken links are reported in the response. Orphaned AOI definitions are not auto-removed — they must be cleaned up manually if no longer needed. - Parameter Overrides: Both
add_controllerandadd_itemacceptparameter_overrides(JSON string). You may include parameters not present in the library definition — ACM will accept them if the names are valid. Parameter names are case-sensitive and must match exactly; incorrect casing causes ACM to silently ignore the parameter. - MachineBuilder Controllers: Come with pre-built periodic tasks at various scan rates. Guard variants include a SafetyTask.
- CLX Auto-Created Children: Some libraries (e.g.,
raM_Robot_Dvc_DeviceHandler) define CLX children — tasks and programs that ACM's HSL4 engine creates automatically during instantiation. These are flaggedis_prebuilt=Truein the project state and excluded from the objects XML to avoid conflicts with unresolvable substitution tokens (e.g.,{MotionGroupName},{RbtDvcItf$RobotName}). ACM resolves these tokens internally through its interface link chain. - ACM Console Script Structure: The instantiation script uses separate BEGINCREATE/ENDCREATE blocks for project, controller, and objects. This ensures the controller's HSL4 fully executes (creating pre-built tasks like
ms0008p08,PowerUp_Handler,SafetyTask) before CREATEOBJECTS runs, allowing objects to reference those tasks as ParObj/Task. - Export Ordering: The
catalog_itemsin exported JSON preserve insertion order (the order items were added by the user), not alphabetical order. This has no effect on ACM instantiation — ACM parses all objects into an insert list before processing — but reflects the logical build sequence. - State: In-memory during the session. Use
export_project/load_projectto persist across sessions.
Client Configuration
{
"mcpServers": {
"acm_mcp": {
"command": "npx.cmd",
"args": [
"mcp-remote",
"http://127.0.0.1:8012/mcp",
"--allow-http"
]
}
}
}
Dependencies
mcp[cli]— FastMCP frameworkpyyaml— Configuration loadinguvicorn— ASGI server for streamable-http transportacm-browser— Local .whl for ACM library catalog extraction (SQLAlchemy + lxml)
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.