Skip to main content
Glama
plone

Plone MCP Server

Official
by plone
README.md
# Plone MCP Server

Talk to your Plone website instead of clicking through it. Plone MCP lets AI assistants like Claude create and edit pages, publish content, search the site, and manage translations on your behalf, in plain language - no coding required to use it, and nothing to change on your Plone site to enable it.

It's built on the [Model Context Protocol](https://modelcontextprotocol.io/docs) (MCP), an open standard that lets AI assistants safely connect to external tools and data. Plone MCP exposes Plone's REST API as a set of MCP tools, so any MCP-compatible client - Claude Desktop, Claude Code, and others - can drive your site, and developers can script, automate, or build on top of the same tools.

## Quickstart

Requires [Node.js 22+](https://nodejs.org). Add this to Claude Desktop's config file, then restart Claude Desktop:
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`

```json
{
  "mcpServers": {
    "plone": {
      "command": "npx",
      "args": ["-y", "@plone/mcp"]
    }
  }
}
```

Now ask Claude to connect to your Plone site, e.g. *"Connect to https://demo.plone.org as admin/admin"*.

## Prerequisites

- **Node.js 22+** - Required to run the server (`^20.19.0 || >=22.12.0`; install: `brew install node` on macOS or from [nodejs.org](https://nodejs.org))

- **Plone 6.0+** site with REST API - The CMS you'll be connecting to

> `pnpm` is only needed if you want to clone the repo and develop locally (see [Local Development](#local-development)). The recommended setup below uses `npx` and doesn't require cloning anything.

## Transports

The server ships two entry points:

- **STDIO** (`plone-mcp` bin / `dist/stdio-server.js`) - for local MCP clients such as Claude Desktop.
- **HTTP** (`dist/http-server.js`) - a streamable-HTTP server with per-session state, listening on `PORT` (default `3001`) at `/mcp`. Start it with `make start`.

## Quick Start using Claude Desktop as an example

The [`@plone/mcp`](https://www.npmjs.com/package/@plone/mcp) package is published on npm, so there's nothing to install or build - `npx` fetches and runs it on demand.

1. **Configure Claude Desktop**

Add to Claude's configuration file:

- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`

**With environment variables (optional):**

```json
{
  "mcpServers": {
    "plone": {
      "command": "npx",
      "args": ["-y", "@plone/mcp"],
      "env": {
        "PLONE_BASE_URL": "https://demo.plone.org",
        "PLONE_USERNAME": "admin",
        "PLONE_PASSWORD": "admin"
      }
    }
  }
}
```

**Without environment variables** (useful if you connect to different Plone sites and prefer to pass credentials per session via `plone_configure`):

```json
{
  "mcpServers": {
    "plone": {
      "command": "npx",
      "args": ["-y", "@plone/mcp"]
    }
  }
}
```

2. **Restart Claude Desktop**

3. **Connect to Plone**

Call `plone_configure` once per session:

```javascript
// Using environment variables
plone_configure({});

// OR providing credentials/token directly to the LLM
plone_configure({
  baseUrl: "https://demo.plone.org",
  username: "admin",
  password: "admin",
});

plone_configure({
  baseUrl: "https://demo.plone.org",
  token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
});
```

**Note:** Arguments take precedence over environment variables.

## Local Development

Clone the repo if you want to modify the server, debug it, or run it with the MCP Inspector:

```bash
git clone https://github.com/plone/plone-mcp.git
cd plone-mcp
make install
make build
```

Point Claude Desktop at your local build instead of the `npx` command:

```json
{
  "mcpServers": {
    "plone": {
      "command": "node",
      "args": ["/absolute/path/to/plone-mcp/dist/index.js"]
    }
  }
}
```

Development commands:

```bash
# Install the dependencies
make install

# Build for production (compiles TypeScript and copies blocks.json)
make build

# Run the HTTP server / the STDIO server from the build
make start
make stdio

# Debug with the MCP Inspector
make inspector

# Tests (Vitest)
make test-all       # everything
make test           # unit tests only
make test-coverage  # with coverage

# Static checks
make lint           # ESLint over src/ and __tests__/
make format         # ESLint with --fix
make type-check     # tsc over sources and tests
```

Run `make help` to list every available target.

## Core Features

- **Content Management**: CRUD operations on all Plone content types
- **Block System**: Create and manage Volto blocks
- **Search**: Full-text search with filtering and sorting
- **Workflow**: Manage publication states and transitions
- **Site Info**: Access content types, vocabularies, and site configuration

## Essential Tools

| Tool                        | Description                              | Example                                                                                |
| --------------------------- | ---------------------------------------- | -------------------------------------------------------------------------------------- |
| `plone_configure`           | Connect to Plone (call once per session) | `plone_configure({baseUrl, username, password})` or `plone_configure({})` for env vars |
| `plone_get_content`         | Get content by path                      | `plone_get_content({path: "/news"})`                                                   |
| `plone_create_content`      | Create new content                       | `plone_create_content({parentPath: "/", type: "Document", title: "Page"})`             |
| `plone_update_content`      | Update existing content                  | `plone_update_content({path: "/page", title: "New Title"})`                            |
| `plone_delete_content`      | Delete content                           | `plone_delete_content({path: "/old-page"})`                                            |
| `plone_search`              | Search content                           | `plone_search({query: "news", portal_type: ["Document"]})`                             |
| `plone_transition_workflow` | Change workflow state                    | `plone_transition_workflow({path: "/page", transition: "publish"})`                    |
| `plone_get_navigation_tree` | Get hierarchical site structure          | `plone_get_navigation_tree({root_path: "/", depth: 2})`                                |
| `plone_get_translation`     | List translations of a content item      | `plone_get_translation({path: "/en/my-page"})`                                         |
| `plone_link_translation`    | Link existing content as a translation   | `plone_link_translation({path: "/en/my-page", id: "/de/meine-seite"})`                 |
| `plone_unlink_translation`  | Remove a translation link                | `plone_unlink_translation({path: "/en/my-page", language: "de"})`                      |
| `plone_create_working_copy` | Check out content into a working copy    | `plone_create_working_copy({path: "/my-document"})`                                    |
| `plone_get_working_copy`    | Show working copy relationship and lock  | `plone_get_working_copy({path: "/my-document"})`                                       |
| `plone_checkin_working_copy`| Check in a working copy                  | `plone_checkin_working_copy({path: "/working_copy_of_my-document"})`                   |
| `plone_cancel_working_copy` | Discard a working copy                   | `plone_cancel_working_copy({path: "/working_copy_of_my-document"})`                    |

## Block Management

### Creating Content with Blocks

```javascript
// 1. Prepare blocks (60-second TTL - meant to be used inmediatly before content creation/editing)
plone_create_blocks_layout({
  blocks: [
    {
      type: "text",
      data: { text: "Welcome to our site!" },
    },
    {
      type: "teaser",
      data: {
        href: "/about",
        title: "Learn More",
        description: "Discover what we do",
      },
    },
  ],
});

// 2. Create content (within 60 seconds), the previously prepared blocks will automatically be included in the request
plone_create_content({
  parentPath: "/",
  type: "Document",
  title: "Homepage",
});
```

### Managing Individual Blocks

```javascript
// Add a single block
plone_add_single_block({
  path: "/homepage",
  blockType: "text",
  blockData: { text: "New paragraph" },
  position: 1,
});

// Update a block
plone_update_single_block({
  path: "/homepage",
  blockId: "51176ead-7b59-402d-9412-baed46821b36", // Get ID from plone_get_content
  blockData: { text: "Updated text" },
});

// Remove a block
plone_remove_single_block({
  path: "/homepage",
  blockId: "51176ead-7b59-402d-9412-baed46821b36",
});
```

## Available Block Types

- **text**: Rich text content
- **teaser**: Link preview card with image
- **\__button**: Call-to-action button
- **separator**: Visual divider line

Use `plone_get_block_schemas()` to see all block types and their properties.

## Common Workflows

### Create and Publish a Page

```javascript
// Configure connection
plone_configure({
  baseUrl: "https://mysite.com",
  username: "editor",
  password: "secret",
});

// Create with blocks
plone_create_blocks_layout({
  blocks: [{ type: "text", data: { text: "Article content..." } }],
});
plone_create_content({
  parentPath: "/news",
  type: "News Item",
  title: "Breaking News",
});

// Publish
plone_transition_workflow({
  path: "/news/breaking-news",
  transition: "publish",
});
```

### Search and Filter

```javascript
plone_search({
  query: "annual report",
  portal_type: ["Document", "File"],
  review_state: ["published"],
  sort_on: "modified",
  sort_order: "descending",
  b_size: 10,
});
```

## Important Notes

⚠️ **Prepared blocks expire after 60 seconds** - Always call `plone_create_blocks_layout` immediately before creating/updating content.

⚠️ **Configure once per session** - Run `plone_configure` once at the start of each session before using other tools. Once configured, you can use all other tools without reconfiguring.

## Troubleshooting

| Issue                                | Solution                                                                                                                           |
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| `command not found` when using `npx` | Make sure the `args` in your config use `plone-mcp` as the binary name (not `plone-mcp-server`) — this was renamed in the package. |
| "Plone client not configured"        | Run `plone_configure` once at the start of your session                                                                            |
| "Block not found"                    | Use `plone_get_content` to get valid block IDs                                                                                     |
| Connection errors                    | Verify Plone URL and credentials are correct                                                                                       |
| Blocks not applied                   | Call `plone_create_blocks_layout` immediately before create/update (60s TTL)                                                       |
| TypeScript errors during local build | Run `make install` to ensure all dependencies are installed                                                                        |

## Resources

- [Plone REST API Documentation](https://plonerestapi.readthedocs.io/)
- [MCP Documentation](https://modelcontextprotocol.io/docs)

## License

MIT

TDQS

A3.9/5.0

Scored across 22 tools

Disambiguation5/5

Each tool has a clearly distinct purpose, from authentication to content creation, block manipulation, workflow, translations, and user management. No two tools overlap in function; even block-related tools (create_blocks_layout vs add_single_block) are clearly differentiated.

Naming Consistency5/5

All tools follow a consistent 'plone_' prefix with underscore-separated verb-noun patterns (e.g., plone_get_content, plone_create_user, plone_transition_workflow). The naming is predictable and logical throughout the set.

Tool Count4/5

With 22 tools, the server covers a broad range of Plone CMS operations without being excessive. The count is appropriate for the complexity of the system, though a few tools (e.g., plone_configure and plone_get_site_info) could potentially be merged.

Completeness4/5

The toolset provides comprehensive coverage for core workflows: content CRUD, block management, user creation/update, workflow transitions, translations, search, and schema exploration. Notable gaps include user deletion and folder manipulation (move/rename), but these are minor given the overall robustness.

Maintenance

ActivityMaintained
ResponsivenessResponsive