PHPackages                             kaufmanndigital/neos-mcp - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. kaufmanndigital/neos-mcp

ActiveNeos-package[API Development](/categories/api)

kaufmanndigital/neos-mcp
========================

Neos MCP Server — exposes Neos Content Repository actions via Model Context Protocol

2.0.0(1mo ago)36442[3 issues](https://github.com/KaufmannDigital/KaufmannDigital.MCP/issues)GPL-3.0-or-laterPHPPHP &gt;=8.1

Since Mar 16Pushed 1mo agoCompare

[ Source](https://github.com/KaufmannDigital/KaufmannDigital.MCP)[ Packagist](https://packagist.org/packages/kaufmanndigital/neos-mcp)[ Docs](https://github.com/KaufmannDigital/KaufmannDigital.MCP)[ RSS](/packages/kaufmanndigital-neos-mcp/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (10)Dependencies (6)Versions (18)Used By (0)

KaufmannDigital.MCP — Neos MCP Server
=====================================

[](#kaufmanndigitalmcp--neos-mcp-server)

> **⚠ Work in Progress**This package is experimental and under active development. It is not recommended for production use. APIs and tool signatures may change without notice. Use at your own risk.

A [Model Context Protocol](https://modelcontextprotocol.io/) server for Neos CMS. Allows AI assistants (Claude Code, Codex, etc.) to directly access and manipulate the Neos Content Repository via a simple HTTP+JSON-RPC interface.

**USE WITH CAUTION!!!**

> **Giving an AI direct access to your CR could end in absolute chaos. Know what AI is doing and double-check everything, before allowing AI to do changes. Be sure to have Backups available!**

---

Prerequisites
-------------

[](#prerequisites)

- **Neos CMS** 7.x or 8.x
- **Flowpack.ElasticSearch.ContentRepositoryAdaptor** — required for `search_nodes` and `find_by_property`
- PHP 8.1+

---

Security Considerations
-----------------------

[](#security-considerations)

> **Read this section before deploying.**

- **The API token (and IP-Filter) is the only access control.** Anyone who obtains the token has full read/write access to your Neos content repository, including the ability to create, update, and publish nodes.
- **Use a strong, randomly generated token** (at minimum 32 characters). Never commit it to version control.
- **The `upload_asset` tool accepts local filesystem paths.** If enabled, a token holder can import any file readable by the web server process (e.g. config files). Restrict access to trusted users only.
- **All write operations bypass Flow's authorization checks** (`withoutAuthorizationChecks`). This is intentional for AI-driven automation, but means no Neos backend role restrictions apply.
- **Only expose the `/mcp` endpoint in development environments** or behind a firewall. Do not expose it on a public production server.
- **Use HTTP only within ddev/localhost.** If you expose the endpoint over a network, use HTTPS to protect the token in transit.

---

Installation
------------

[](#installation)

```
composer require kaufmanndigital/neos-mcp
```

---

Configuration
-------------

[](#configuration)

**1. Set the API token and optionally restrict access by IP** in `Configuration/Development/Settings.yaml` (never `Settings.yaml` — to keep it out of version control):

```
KaufmannDigital:
  MCP:
    Token: 'your-strong-random-token-here'
    allowedIps:
      - '127.0.0.1'       # localhost IPv4
      - '::1'             # localhost IPv6
      - '172.16.0.0/12'   # Docker bridge (ddev, docker-compose, ...)
      - '1.2.3.4'         # your office IP
```

> **`allowedIps` is required — an empty list blocks all requests (deny by default).**Supports exact IPs and CIDR notation for both IPv4 and IPv6.
>
> **Note on ddev:** Requests from the host arrive at PHP with a Docker bridge IP (`172.x.x.x`), not your machine's actual IP. The `172.16.0.0/12` CIDR covers the entire Docker bridge range and is the recommended way to allow local ddev access.

**2. Configure MCP (example for Claude Code)** (`~/.claude.json`):

```
{
  "mcpServers": {
    "neos": {
      "type": "http",
      "url": "https:///mcp",
      "headers": { "X-Api-Token": "your-strong-random-token-here" }
    }
  }
}
```

> **Note:** Use HTTP (not HTTPS) when connecting from Claude Code via ddev — ddev's self-signed certificates are not trusted by Bun's HTTP client.

---

Tools
-----

[](#tools)

### `get_node`

[](#get_node)

Loads a single node by its UUID.

ParameterTypeRequiredDescription`nodeIdentifier`string✓Node UUID`workspaceName`stringWorkspace (default: `live`)`includeChildren`booleanInclude direct child nodes (default: `false`)`dimensions`objectContent dimensions to resolve the node in, e.g. `{"language":["de_DE"]}` (default: ContentRepository default — see [Content Dimensions](#content-dimensions))`responseProperties`arrayFields to return (default: `identifier` only)---

### `search_nodes`

[](#search_nodes)

Full-text search across all nodes via Elasticsearch.

ParameterTypeRequiredDescription`query`stringSearch term (optional if `nodeType` is set)`nodeType`stringFilter by node type, e.g. `Neos.Neos:Document``workspaceName`stringWorkspace (default: `live`)`limit`integerMax results (default: `10`)`responseProperties`arrayFields to return (default: `identifier` only)---

### `find_by_property`

[](#find_by_property)

Finds nodes with an exact property value match via Elasticsearch.

ParameterTypeRequiredDescription`propertyName`string✓Property name to match`propertyValue`any✓Exact value to search for`nodeType`stringFilter by node type (optional)`workspaceName`stringWorkspace (default: `live`)`limit`integerMax results (default: `10`)`responseProperties`arrayFields to return (default: `identifier` only)---

### `get_children`

[](#get_children)

Returns direct child nodes of a given node.

ParameterTypeRequiredDescription`nodeIdentifier`string✓Parent node UUID`nodeTypeFilter`stringNode type filter, e.g. `Neos.Neos:Document``workspaceName`stringWorkspace (default: `live`)`dimensions`objectContent dimensions to resolve the node in, e.g. `{"language":["de_DE"]}` (default: ContentRepository default — see [Content Dimensions](#content-dimensions))`responseProperties`arrayFields to return (default: `identifier` only)---

### `create_node`

[](#create_node)

Creates a new node under a given parent node.

ParameterTypeRequiredDescription`parentNodeIdentifier`string✓Parent node UUID`nodeType`string✓Node type name, e.g. `Neos.Neos:Document``properties`objectKey/value map of properties to set`workspaceName`stringWorkspace (default: `live`)`dimensions`objectContent dimensions to create the node in, e.g. `{"language":["de_DE"]}` (default: ContentRepository default — see [Content Dimensions](#content-dimensions))`responseProperties`arrayFields to return (default: `identifier` only)---

### `delete_node`

[](#delete_node)

Deletes a node by UUID.

ParameterTypeRequiredDescription`nodeIdentifier`string✓Node UUID to delete`workspaceName`stringWorkspace (default: `live`)`publishAfterDelete`booleanPublish delete from user workspace to base workspace (default: `false`)`dimensions`objectContent dimensions to resolve the node in, e.g. `{"language":["de_DE"]}` (default: ContentRepository default — see [Content Dimensions](#content-dimensions))`responseProperties`arrayFields to return for deleted node (default: `identifier` only)---

### `update_property`

[](#update_property)

Sets a single property on a node.

ParameterTypeRequiredDescription`nodeIdentifier`string✓Node UUID`propertyName`string✓Property name`propertyValue`any✓New value`workspaceName`string✓Workspace to write to`dimensions`objectContent dimensions to resolve the node in, e.g. `{"language":["de_DE"]}` (default: ContentRepository default — see [Content Dimensions](#content-dimensions))`responseProperties`arrayFields to return (default: `identifier` only)---

### `batch_update_property`

[](#batch_update_property)

Sets properties on multiple nodes in a single call.

ParameterTypeRequiredDescription`nodes`array✓List of `{nodeIdentifier, properties}` objects`workspaceName`string✓Workspace to write to`dimensions`objectContent dimensions to resolve the nodes in, e.g. `{"language":["de_DE"]}` (default: ContentRepository default — see [Content Dimensions](#content-dimensions))`responseProperties`arrayFields per node (default: `identifier` only)---

### `publish_nodes`

[](#publish_nodes)

Publishes nodes from a user workspace to the base workspace (usually `live`).

ParameterTypeRequiredDescription`nodeIdentifiers`array✓UUIDs of nodes to publish`workspaceName`string✓Source workspace`responseProperties`arrayFields per node (default: `identifier` only)---

### `upload_asset`

[](#upload_asset)

Imports a file into the Neos media library from a URL or a local absolute path.

> **Security note:** Local path access is restricted to the directory configured via `localImportBasePath` in `Settings.yaml` (default: `Data/MCP-Import`). Files outside that directory are rejected.

ParameterTypeRequiredDescription`url`string✓URL or absolute local path (e.g. `/var/www/html/images/file.jpg`)`filename`stringOriginal filename — required when using a local path`title`stringTitle/label for the asset in the media library`caption`stringCaption/description text for the asset`copyrightNotice`stringCopyright notice, e.g. the photographer name`tag`stringTag to assign (created if it does not exist)---

### `update_asset`

[](#update_asset)

Updates metadata fields of an existing asset, identified by its asset identifier. Only the provided fields are changed.

ParameterTypeRequiredDescription`identifier`string✓Asset identifier (UUID) of the asset to update`title`stringTitle/label for the asset`caption`stringCaption/description text for the asset`copyrightNotice`stringCopyright notice, e.g. the photographer name`tags`arrayTag labels to assign (created if they do not exist); added to existing tags unless `replaceTags` is `true``replaceTags`booleanReplace all existing tags with the given `tags` instead of adding (default: `false`)---

responseProperties — Minimizing token costs
-------------------------------------------

[](#responseproperties--minimizing-token-costs)

All tools return **only the `identifier` by default**. Use `responseProperties` to request additional fields — keeping this minimal significantly reduces AI context usage.

**Available values:**

- Node properties: any property name defined on the node type, e.g. `"title"`, `"releaseDate"`, `"uriPathSegment"`
- Meta fields: `"nodeType"`, `"label"`, `"name"`, `"path"`, `"workspace"`, `"hidden"`

**Examples:**

```
// Search: request title and date for identification
"responseProperties": ["title", "releaseDate"]

// After write operations: omit responseProperties entirely — only identifier is needed
```

---

Content Dimensions
------------------

[](#content-dimensions)

The node-resolving tools (`get_node`, `get_children`, `create_node`, `delete_node`, `update_property`, `batch_update_property`) build their `ContentContext` from the site's content dimension configuration.

- **No `dimensions` argument (default):** the tools do **not** set any dimensions, so the `ContentContextFactory` automatically applies the configured `Neos.ContentRepository.contentDimensions` defaults (e.g. `language` → its `default` preset). This means you get the site's primary dimension out of the box without any package configuration.
- **Explicit `dimensions` override:** pass an object mapping each dimension to its value(s), e.g. `{"language":["en_DE"]}`, to resolve nodes in another dimension. `targetDimensions` are derived automatically from the first value of each dimension.
- The override may be passed as a JSON object or a JSON-encoded string — both are accepted (some MCP clients serialize object arguments as strings).
- If a node has no variant in the requested dimension, the tool returns `Node not found` (no silent fallback to the default dimension).

```
// Resolve the English (en_DE) variant of a node
{ "nodeIdentifier": "…", "dimensions": { "language": ["en_DE"] }, "responseProperties": ["title"] }
```

> The Elasticsearch-based tools (`search_nodes`, `find_by_property`) and `publish_nodes` always operate on the ContentRepository default dimension and do not accept a `dimensions` argument.

---

Architecture
------------

[](#architecture)

```
McpController
  └── McpHandler          JSON-RPC dispatcher
        └── Tool/*        One class per operation (Flow Singletons)
              └── NodeSerializer   Shared node serialization

```

- **Write tools** bypass Flow's authorization via `SecurityContext::withoutAuthorizationChecks()`
- **Asset resolution:** For properties typed as a `Neos\Media\Domain\Model\*` class or interface (e.g. `ImageInterface`), a UUID string is resolved to the Asset object via `AssetRepository::findByIdentifier()`. This handles interface-typed properties that `EntityManager::find()` cannot.
- **DateTime resolution:** ISO-8601 strings are automatically converted to `DateTime` objects
- **ElasticSearchQueryBuilder** is prototype-scoped — always instantiate via `$objectManager->get()` for a fresh instance per call

---

Maintainer
----------

[](#maintainer)

This package is maintained by the [Neos Agency Kaufmann Digital](https://www.kaufmann.digital).
Feel free to send us your questions or requests to

### Issues and Pull-Requests are welcome!

[](#issues-and-pull-requests-are-welcome)

You got stuck while installing or configuring? You are missing something? You found a bug?
No problem, just create an issue or open a pull request. We'll have a look at it ASAP.

License
-------

[](#license)

Licensed under GPL-3, see [LICENSE](LICENSE)

###  Health Score

42

—

FairBetter than 88% of packages

Maintenance70

Regular maintenance activity

Popularity23

Limited adoption so far

Community11

Small or concentrated contributor base

Maturity52

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 92.6% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~8 days

Total

13

Last Release

49d ago

Major Versions

1.3.1 → 2.0.02026-06-22

### Community

Maintainers

![](https://www.gravatar.com/avatar/1ec6c2ae665e40ee04396a70c824f37be27b5f98330b66c18256ebd21dfb52b5?d=identicon)[kaufmanndigital](/maintainers/kaufmanndigital)

---

Top Contributors

[![Nikdro](https://avatars.githubusercontent.com/u/9807101?v=4)](https://github.com/Nikdro "Nikdro (25 commits)")[![LIS-CorneliusLink](https://avatars.githubusercontent.com/u/58025655?v=4)](https://github.com/LIS-CorneliusLink "LIS-CorneliusLink (1 commits)")[![sebastiansommer](https://avatars.githubusercontent.com/u/1651774?v=4)](https://github.com/sebastiansommer "sebastiansommer (1 commits)")

### Embed Badge

![Health badge](/badges/kaufmanndigital-neos-mcp/health.svg)

```
[![Health](https://phpackages.com/badges/kaufmanndigital-neos-mcp/health.svg)](https://phpackages.com/packages/kaufmanndigital-neos-mcp)
```

###  Alternatives

[neos/neos-base-distribution

Neos Base Distribution

4466.7k](/packages/neos-neos-base-distribution)[neos/demo

Site package for the Neos Demo Site

18191.3k12](/packages/neos-demo)[flowpack/media-ui

This module allows managing media assets including pictures, videos, audio and documents.

2197.8k2](/packages/flowpack-media-ui)[sandstorm/neostwofactorauthentication

1428.7k](/packages/sandstorm-neostwofactorauthentication)[sitegeist/lostintranslation

Automatic content translations for Neos using the DeepL Api

1343.3k4](/packages/sitegeist-lostintranslation)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
