TYPO3 inline relations (IRRE - Inline Relational Record Editing) allow records to be related to each other in parent-child relationships. The MCP tools support both reading and writing inline relations with some limitations.
- Tables that exist independently (e.g.,
tt_content) - Have
hideTable = falsein TCA - Displayed as UIDs in read results
- Can be managed through foreign field updates
Example:
// News with content elements
$result = $readTool->execute([
'table' => 'tx_news_domain_model_news',
'uid' => 123
]);
// Returns:
// ['content_elements' => [456, 789, 1011]] // Array of UIDs- Tables that only exist as children (e.g.,
sys_file_reference,tx_news_domain_model_link) - Have
hideTable = truein TCA AND are not listed in theadditionalStandaloneTablesextension setting - Displayed as full embedded records in read results
- Should be created together with parent record
Some hideTable=true tables have inline structure that's too complex to be safely
edited through the parent — for example translations independent of the parent's
language, or rows whose visibility depends on permissions of a different table.
For these, embedding makes the parent's update path either lossy (drops
translations the LLM didn't echo back) or unsafe (replaces rows the LLM never
intended to touch).
The extension setting additionalStandaloneTables opts a hideTable table out
of embedding. Such tables stay hidden in the TYPO3 backend list module (TCA is
not modified), but the MCP tools treat them as ordinary independent tables:
ListTablesshows themReadTable/WriteTableallow direct CRUD- The parent's inline field collapses to a list of child UIDs, just like for any independent relation
sys_file_metadata is on this list by default. It has hideTable=true plus
its own languageField, while its parent sys_file is read-only and not
language-aware — embedding made translations invisible and edits dangerous.
Exposing it standalone gives the LLM normal update and translate semantics
on title/alternative/description, while the read path on sys_file still
yields metadata: [<uid>, ...] as a discovery hint.
Mount restrictions on sys_file propagate transitively to
sys_file_metadata via a subquery in SysFileMetadataRestrictionListener —
non-admin users only see metadata for files they could read on sys_file.
The same subquery filters orphaned metadata pointing at non-existent files.
Example:
// Page with file references
$result = $readTool->execute([
'table' => 'pages',
'uid' => 123
]);
// Returns:
// ['media' => [
// ['uid' => 1, 'title' => 'Image 1', 'uid_local' => 5, ...],
// ['uid' => 2, 'title' => 'Image 2', 'uid_local' => 6, ...]
// ]] // Full record dataFor independent relations, update the foreign field on child records:
// Create content element related to news
$writeTool->execute([
'table' => 'tt_content',
'action' => 'create',
'pid' => 1,
'data' => [
'header' => 'Related content',
'CType' => 'text',
'tx_news_related_news' => 789 // Foreign field
]
]);For independent relations, pass array of UIDs when updating parent:
// Update news with content elements
$writeTool->execute([
'table' => 'tx_news_domain_model_news',
'action' => 'update',
'uid' => 123,
'data' => [
'content_elements' => [456, 789, 1011] // Array of UIDs
]
]);For dependent relations, pass record data when creating parent:
// Create news with embedded links
$writeTool->execute([
'table' => 'tx_news_domain_model_news',
'action' => 'create',
'pid' => 1,
'data' => [
'title' => 'News with links',
'related_links' => [
['title' => 'Link 1', 'uri' => 'https://example.com'],
['title' => 'Link 2', 'uri' => 't3://page?uid=42']
]
]
]);- Creating embedded relations through parent record works with a two-step process:
- DataHandler creates the child records with placeholder IDs
- Direct database UPDATE sets the foreign field after creation
- The foreign field (e.g.,
parentintx_news_domain_model_link) is NOT defined in the child table's TCA columns by design - DataHandler only processes fields that are defined in TCA columns, so it ignores the foreign field
- Solution: After DataHandler creates child records, we directly update the foreign field in the database
- Working Example: Creating news with embedded links now works correctly
sys_file_referenceis fully supported as an embedded inline relation (hideTable=true)- It supports workspaces natively (
versioningWS=truein TCA) - TCA
type=filefields are expanded byTcaPreparationinto inline relations withforeign_table=sys_file_reference foreign_match_fields(tablenames,fieldname) ensure references are scoped to the correct parent field- File references are enriched with metadata from
sys_file(filename, identifier, mime type, public URL) sys_fileitself is read-only - files are managed through the filesystem, not direct DB edits- File uploads are NOT supported - only referencing existing files via
uid_local
- Both ReadTableTool and WriteTableTool automatically initialize workspace context via
WorkspaceContextService - The service finds the first writable workspace or creates a new "MCP Workspace" if needed
- All operations (read/write) happen in the same workspace automatically
- No manual workspace management is needed in tests or usage
- Workspace transparency is maintained - tools work consistently across workspaces
- Sorting is handled through
sortingorsorting_foreignfields - Position management (before/after) not fully implemented for inline relations
- Use Foreign Field Method: Most reliable method for creating inline relations
- Check Table Configuration: Verify if table has
hideTableto determine relation type - Validate Data: Ensure proper validation for inline relation data
- Handle Errors: Check DataHandler error log for detailed error messages
- Test Thoroughly: Inline relations can behave differently in various contexts
Inline relations are defined in TCA with type 'inline':
'content_elements' => [
'config' => [
'type' => 'inline',
'foreign_table' => 'tt_content',
'foreign_field' => 'tx_news_related_news',
'foreign_sortby' => 'sorting',
]
]DataHandler supports multi-table operations with placeholders:
$dataMap = [
'parent_table' => [
'NEW123' => ['field' => 'value']
],
'child_table' => [
'NEW456' => ['other_field' => 'value'] // Note: foreign field NOT included
]
];
// After DataHandler processing, foreign fields are updated directly:
$connection->update('child_table', ['parent_field' => $parentUid], ['uid' => $childUid]);- File Uploads: Allow uploading new files and creating sys_file records (currently only existing files can be referenced)
- Batch Operations: Support for bulk inline relation updates
- Position Management: Full support for positioning inline records (before/after specific records)
- Validation Enhancement: More comprehensive validation for embedded record data
- Performance Optimization: Batch foreign field updates instead of individual UPDATE queries