Skip to main content
Glama
Edge-JB
by Edge-JB

plc_pou

Create, read, write, and manage PLC objects offline in TwinCAT XAE: programs, function blocks, GVLs, and more with batch operations and surgical text editing.

Instructions

PLC object authoring + code edit on the open solution (OFFLINE engineering only — edits land in-memory and reach a runtime only via a later guarded plc_download + twincat_restart_runtime). Tree paths use ^ separators; safety (TISC-rooted) paths are rejected by policy. CREATE — create / create_batch (parent, name, subType, language?, returnType?, extends?, implements?, declText?, before?): CreateChild sub-types 602 Program, 603 Function (returnType required), 604 FunctionBlock, 605 Enum, 606 Struct, 607 Union, 608 Action, 609 Method, 611 Property (returnType required), 615 GVL, 616 Transition, 618 Interface, 619 Visualization, 623 Alias, 629 ParameterList, 631 UML. language IECLANGUAGETYPES 0 NONE/1 ST/2 IL/3 SFC/4 FBD/5 CFC/6 LD (default 1). extends/implements for FB 604 / Program 602 derivation (618 uses extends as its base); declText seeds DUT/GVL decl. For code POUs prefer set_decl after create. FOLDERS — create_folder (parent, name, before?) creates a PLC folder (sub-type 601) under parent (a PLC subtree node, POUs/DUTs/GVLs container, or another folder); returns the same shape as create. create_folder_batch (creates:[{parent,name,before?}], save?) loops continue-on-error, returns {count,succeeded,failed,results} KEEPING success rows (each carries the created child identity) — list a parent-folder entry before its child (array order). create / create_batch already author INTO a folder when parent is the folder's path — no separate action needed. TEMPLATE — import_template (parent, paths[]) imports POU template file(s) (CreateChild sub-type 58). READ (cheap-first: outline for structure; get_decl/get_impl with grep{} or range{} to slice; full text only when the whole body is needed — a large full read returns a hint nudging the next call to slice) — get_decl / get_impl / get_document / get_graphical (path). get_decl/get_impl take an optional range {start,end} (1-based inclusive line slice, clamped) OR grep {pattern, context?} (regex over lines + context each side); mutually exclusive; default full text. Both report lineCount; get_impl also returns language (textual 1 ST/2 IL; graphical 3 SFC/4 FBD/5 CFC/6 LD -> lineCount:0 + {graphical:true, hint}). get_graphical (path) READ-ONLY inspects a graphical (LD/FBD/SFC/CFC) body: returns {language,languageName,itemType,source,readOnly,xml} where xml is the object's network XML (NWL 'BoxTree' for LD/FBD/IL, or the SFC/CFC archive), pulled live from the POU document (for an Action/Method/Transition it reads the PARENT POU's document, since get_document/GetDocumentXml only work on a top-level POU). Diagnostic only — graphical bodies are NOT text-editable; change them in the XAE GUI. Refuses textual languages (use get_impl). outline (path) returns structure WITHOUT full text: header + varBlocks + child code items. WRITE — set_decl / set_decl_batch (path, declText); set_impl / set_impl_batch (path, exactly one of implText|implXml — implXml is TwinCAT object XML, round-trip only, for graphical languages); set_document (path, documentXml). SURGICAL TEXT EDIT (read-modify-write, returns ONLY the changed region +/-2 ctx; target decl|impl, CRLF/LF preserved; refuses graphical impl): replace (find literal substring, replaceWith, expectCount? default 1 — fails without writing on 0 or count mismatch); replace_lines (start, end, text — 1-based inclusive span, OOB throws); insert (exactly one of at|after|before, text); insert_in_var_block (block e.g. VAR_INPUT, text, occurrence? — inserts before that block's END_VAR); append (text — default target impl). All surgical writes accept validate:true to run CheckAllObjects after (default off). DISCOVER (cheap-first: find for path-by-name; search only for content patterns) — tree (plcPath?, path? subtree root, depth?, typeFilter?) does a read-only recursive Child() walk of the IEC project and returns {plcPath,projectPath,rootPath,count,tree:[{path,name,type,itemType,subType?,childCount,children?,truncated?}]} (type is a normalized label: Program/FB/Function/FunctionBlock/Struct/Enum/Union/Alias/GVL/Interface/Method/Property/Action/Transition/Visualization/ParameterList/UML/Folder/Project/Task/Unknown; depth 1 = direct children only; typeFilter is a comma list of type labels to KEEP, ancestors retained as scaffolding). find (plcPath?, path?, name? substring or /regex/, typeFilter?; at least one of name/typeFilter) returns a FLAT {plcPath,projectPath,count,matches:[{path,name,type,itemType,subType?,childCount}]} so a caller can resolve a ^ path from a name without the whole nested blob. GREP — search (pattern [regex/.NET or substring], ignoreCase?, declOnly?|implOnly? [mutually exclusive], plcPath?, path? subtree root, maxResults? default 50/max 5000 — raise for exhaustive scans; truncated:true signals the cap was hit) is a project-wide find-in-code: walks every code object under the IEC project, greps DeclarationText + (ST-only) ImplementationText line-by-line, returns {pattern,plcPath,scanned,searched,count,truncated,matches:[{path,section:'decl'|'impl',line,text}]}; graphical bodies are scanned-but-not-searched. Read-only/offline. Decl/impl text is CACHED (warm repeat sub-100ms vs ~16s cold); the cache self-invalidates on edits through this tool, dirty-checks open IDE editors, and is backstopped by a file-save watcher — pass refresh:true to force a full live re-pull. DELETE — delete (path OR parent+name) GUARDED offline delete of one PLC object via parent.DeleteChild; dryRun:true previews {wouldDelete,target}, confirm="ALLOW_TWINCAT_DELETE" to actually delete; verifies the child exists first, refuses TISC. LIFECYCLE (OFFLINE, unguarded, refuses TISC) — rename (path, newName = bare name) renames in place, returns {path,newName,newPath}. move (path, newParent, before?) reparents one object preserving decl/impl/document/sub-objects via export-import-delete in ONE attach (no native reparent exists); refuses no-op/into-self/into-own-descendant moves; returns {path,newParent,newPath,name,via}. BUILD-CHECK — check_objects (plcPath?, default first PLC under TIPC) runs CheckAllObjects on the nested IEC project (no download). Mutating batch verbs (create_batch, set_decl_batch, set_impl_batch) accept save:true to save once after the batch.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
atNoinsert before this 1-based line (lineCount+1 appends)
endNoreplace_lines: 1-based inclusive last line
findNoreplace: exact literal substring (NOT regex)
grepNoget_decl/get_impl: regex over lines + context each side (default 2); mutually exclusive with range
nameNo
pathNo
saveNo
textNoreplacement / insert / append text
afterNoinsert after this 1-based line
blockNoinsert_in_var_block: VAR-block keyword e.g. VAR_INPUT
depthNotree: max recursion depth (1 = direct children only); default unlimited
itemsNo
pathsNo
rangeNoget_decl/get_impl: 1-based inclusive line slice; mutually exclusive with grep
startNoreplace_lines: 1-based inclusive first line
actionYes
beforeNocreate: sibling name to insert before (string). insert: 1-based line to insert before (int, alias of at)
dryRunNodelete: preview the target without deleting
parentNo
targetNosurgical edit target; default decl (append defaults impl)
confirmNodelete: must equal ALLOW_TWINCAT_DELETE to actually delete
createsNo
detailsNoset_decl_batch/set_impl_batch: include ok:true rows; default failures-only ({count,succeeded,failed} always reported). create_batch/create_folder_batch always keep success rows (they carry child identity), so details is a no-op there.
extendsNo
implXmlNo
newNameNorename: new bare object name (not a path)
patternNosearch: regex (.NET syntax) or plain substring, matched per-line against each object's decl/impl text
plcPathNo
refreshNosearch: force a full live re-pull, bypassing the decl/impl text cache for the searched scope (default false). Open editors are always dirty-checked automatically; use this only as an escape hatch after structural ops or for paranoia.
subTypeNo
declOnlyNosearch: search only DeclarationText; mutually exclusive with implOnly
declTextNo
implOnlyNosearch: search only ImplementationText (ST-only); mutually exclusive with declOnly
implTextNo
languageNo
validateNosurgical writes: run CheckAllObjects after the edit (default off)
newParentNomove: ^-separated destination parent tree path (TISC refused)
ignoreCaseNosearch: case-insensitive match (default false)
implementsNo
maxResultsNosearch: cap on returned match rows (default 50, max 5000; raise for exhaustive scans); stops the walk and sets truncated:true when hit
occurrenceNoinsert_in_var_block: which matching block (1-based, default 1)
returnTypeNo
typeFilterNotree/find: comma list of normalized type labels to keep/match (case-insensitive), e.g. 'FB,Method,Struct'
documentXmlNo
expectCountNoreplace: required occurrence count (default 1)
replaceWithNo
Behavior5/5

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

Despite no annotations, the description fully discloses offline-only, in-memory edits, TISC path rejection, cache self-invalidation, dirty-checking, and the effect of 'validate' and 'refresh' parameters. There are no behavioral contradictions.

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?

The description is extremely long and dense, making it hard for an AI agent to quickly scan. While comprehensive, it lacks structural breaks (bullets, tables) and front-loads details. It could be more concise.

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 46 parameters, no output schema, and zero annotations, the description is remarkably complete. It covers all actions, edge cases (TISC, batch behavior, caching), and provides enough detail for an agent to invoke the tool correctly without ambiguity.

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

Parameters5/5

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

With only 59% schema description coverage, the description adds significant meaning: it explains subType numeric values, language codes, the interplay of range and grep, batch shapes, and contextual hints like 'For code POUs prefer set_decl after create.' This goes well beyond the schema.

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 explicitly states it handles 'PLC object authoring + code edit', lists dozens of specific actions (CREATE, READ, WRITE, SURGICAL TEXT EDIT, DISCOVER, DELETE, etc.), and clearly distinguishes each sub-action. It is specific and aligns with the tool's name.

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?

Extensive guidance is provided: 'cheap-first' hints, when to use outline vs get_decl vs full text, preference for set_decl after create, offline-only constraint, TISC rejection, batch behavior like continue-on-error, and compare with sibling tools like set_decl_batch vs create_batch.

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

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Edge-JB/TwinCAT-XAE-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server