An MCP server that extends the official @plone/mcp server with seven additional tools for managing Plone sites built with the cs_dynamicpages product. Call plone_configure once per session, then use the full official toolset plus the dynamic-layout tools.
- Official toolset included: All
@plone/mcptools (blocks, workflow, translations, users, navigation tree, vocabularies, search) are available as-is. - Dynamic Layout Management: Analyze full page structures, including rows and featured items.
- Full CRUD Operations: Create, patch, move, and delete Plone content and layout components.
- Universal Content Support: Generic
plone_create_contenttool for any Plone content type (Folder, Document, Link, etc.). - Local File Integration:
plone_upload_local_assetuploads files straight from your filesystem.
| Tool | Description |
|---|---|
plone_get_site_definitions |
Fetches site-specific definitions (Schemas and Row Types) from Plone. |
plone_get_dynamic_page_content |
Returns the full JSON structure of a DynamicPage (Rows + Featured Items). Handles up to 1000 items. |
plone_create_dynamic_page_row |
Creates a new layout section (DynamicPageRow) in a page, optionally with nested featured items. |
plone_create_dynamic_page_row_featured |
Creates a featured item (DynamicPageRowFeatured) within an existing row. |
plone_move_dynamic_page_row |
Reorders layout rows (top, bottom, or a specific position). |
plone_upload_file |
Uploads images or files using Base64 data. |
plone_upload_local_asset |
Reads a file from the local filesystem and uploads it to Plone. |
Everything else comes from the official package: plone_configure, plone_create_content, plone_update_content, plone_delete_content, plone_search, plone_get_navigation_tree, plone_get_block_schemas, workflow and translations tools, etc.
Besides tools, the server exposes read-only MCP resources, discoverable by any MCP client:
| Resource | URI | Content |
|---|---|---|
| Architecture | cs-dynamicpages://docs/architecture |
Markdown reference: hierarchy, row types, common fields and migration mapping. |
| Migration protocol | cs-dynamicpages://docs/migration |
Markdown "Skeleton + Mapping" workflow for replicating pages. |
| Row types | cs-dynamicpages://row-types |
JSON list of available row types, from the site registry. |
| DynamicPageRow schema | cs-dynamicpages://schemas/row |
JSON schema of DynamicPageRow. |
| DynamicPageRowFeatured schema | cs-dynamicpages://schemas/row-featured |
JSON schema of DynamicPageRowFeatured. |
The two documentation resources work without a Plone connection. The other three require plone_configure; until then they return a short message explaining how to configure the connection instead of failing.
The official @plone/mcp resources (plone://site, plone://types, plone://content{+path}) remain available.
Earlier versions of this server shipped their own set_session_context / check_credentials_status / create_dynamic_page_row tools and authenticated with a __ac cookie. As of v2 the server delegates to @plone/mcp:
- Connect with
plone_configure({ baseUrl, token })orplone_configure({})usingPLONE_BASE_URL/PLONE_TOKEN(or username/password). The client does not support cookie auth; use a token or basic auth. - Tool arguments use site-relative paths (e.g.
/rows,/en/home). Full++api++URLs are also accepted and normalized to site-relative paths automatically. create_content/search_content/patch_content/delete_contentmap toplone_create_content/plone_search/plone_update_content/plone_delete_content.
The server runs over STDIO (no Plone-side changes required) and can be launched either directly from the GitHub repository via npx or from a local clone of this repo.
Add the following configuration to your Opencode (opencode.json):
{
"mcp": {
"cs-dynamicpages-mcp": {
"type": "local",
"command": ["npx", "-y", "github:codesyntax/cs-dynamicpages-mcp"],
"enabled": true
}
}
}npx fetches the repository from GitHub and launches the committed build automatically; nothing else needs to be installed. github: resolves to git+ssh, so it requires an SSH key for GitHub; without one, use the HTTPS form: npx -y git+https://github.com/codesyntax/cs-dynamicpages-mcp.git.
Authentication is provided through the server's environment block — see Credentials & environment variables. A .env file is not read.
Clone the repository and install its dependencies:
git clone https://github.com/codesyntax/cs-dynamicpages-mcp
cd cs-dynamicpages-mcp
npm installThen point your MCP client at the local entry point instead of the GitHub package:
{
"mcp": {
"cs-dynamicpages-mcp": {
"type": "local",
"command": ["npx", "tsx", "src/local.ts"],
"cwd": "/absolute/path/to/cs-dynamicpages-mcp",
"enabled": true
}
}
}The cwd is required because MCP clients launch the command from an arbitrary
directory; without it the relative src/local.ts will not resolve. You can also
run the server directly from the clone with npm start (after npm run bundle).
The compiled entry point (dist/local.js) is committed to the repository, so it is
available without building:
{
"mcp": {
"cs-dynamicpages-mcp": {
"type": "local",
"command": ["node", "/absolute/path/to/cs-dynamicpages-mcp/dist/local.js"],
"enabled": true
}
}
}@plone/mcp reads its configuration from the process environment. Supported variables:
| Variable | Description |
|---|---|
PLONE_BASE_URL |
Base URL of your Plone site (fallback for plone_configure). |
PLONE_TOKEN |
Bearer token for authentication (alternative to username/password). |
PLONE_USERNAME / PLONE_PASSWORD |
Basic auth credentials. |
ENABLED_TOOLS |
Optional comma-separated allow-list of tool names; applies to both the official tools and the dynamic pages tools. |
PLONE_SESSION_TTL |
Session TTL in milliseconds (from @plone/mcp). |
PLONE_PREPARED_BLOCKS_TTL |
Prepared-blocks TTL in milliseconds (from @plone/mcp). |
Provide them through the MCP server's environment block in opencode.json:
{
"mcp": {
"cs-dynamicpages-mcp": {
"type": "local",
"command": ["npx", "-y", "github:codesyntax/cs-dynamicpages-mcp"],
"environment": {
"PLONE_BASE_URL": "https://your-plone",
"PLONE_TOKEN": "eyJhbGciOi..."
},
"enabled": true,
"timeout": 60000
}
}
}Or with basic auth instead of a token:
"environment": {
"PLONE_BASE_URL": "https://your-plone",
"PLONE_USERNAME": "admin",
"PLONE_PASSWORD": "secret"
}Then call plone_configure({}) once per session. The credentials live in the server process environment; the model never needs to read them.
⚠️ A.envfile is not loaded into the MCP server. Verified with OpenCode: with a variable defined only in.env, the server process receivedundefined, and{env:VAR}resolved to an empty string (OpenCode runs a long-lived background service, so the MCP process inherits the service environment, not the environment of the shell that runs a single command).
If you prefer not to store secrets in opencode.json, use substitution ("PLONE_TOKEN": "{env:PLONE_TOKEN}") and make sure the variable is present in the environment that starts the OpenCode service (then restart it, e.g. opencode service restart). Exporting it only in the shell of a one-off command does not reach the server.
Note: credentials can also be passed at runtime with plone_configure({ baseUrl, token }) (or username/password).
Requires a Node.js version supported by @plone/mcp: ^20.19.0 || >=22.12.0 (Node.js 22+ recommended).
src/local.ts: Entry point for localstdioexecution (the extended server).src/extended-server.ts: Creates the@plone/mcpserver and registers the dynamic pages tools.src/plone-mcp.ts: Single adapter re-exporting the@plone/mcpinternals the wrapper relies on.src/tools/: One file per dynamic pages tool (plone_*.ts), plusshared.ts(helpers and schemas) andindex.ts(thedynamicPagesToolslist andregisterDynamicPagesTools), mirroring the@plone/mcplayout.src/dynamicPages/: Pure helpers (payload builders, ordering computation, hierarchy reassembly) shared by the tools.scripts/build.mjs: esbuild bundling ofsrc/local.tsintodist/local.js.
npm install # .npmrc sets legacy-peer-deps for npm 10 + vitest
npm run bundle # rebuild dist/local.js (esbuild) — run before committing
npm test # vitest unit + handler seam tests
npm run typecheck # tsc --noEmit
npm start # run the compiled stdio server (node dist/local.js)
npm run dev # run from source with tsx (src/local.ts)dist/local.js is intentionally committed: git installs (npx github:…) must not
run a build, because that would install devDependencies and npm 10.9.x crashes on
vitest's peer set. Remember to run npm run bundle and commit dist/ after
changing the source.
The build script is named
bundle(notbuild) on purpose: npm/pacote treat abuildscript as a git-dependency preparation trigger, which would reinstall devDependencies onnpx github:….
This project pins @plone/mcp to the published 1.0.0-alpha.2 package. To track
unreleased changes from the official repository instead, point the dependency at
the branch (git+https://github.com/plone/plone-mcp.git#main). Note that a git
dependency requires npm >= 11 (or legacy-peer-deps on npm 10), because
@plone/mcp compiles itself on install.
This server handles credentials strictly within your local environment. The @plone/mcp code runs from node_modules on your machine, and your API tokens are only ever sent to the Plone site you configure. Ensure you trust the source before providing sensitive API tokens.