Plank CMS Logo

MCP Server

Plank exposes a read-only MCP endpoint per instance at /mcp.

This guide is primarily intended for agents or automated integrations consuming the MCP endpoint. However, it can also be used manually with HTTP clients such as HTTPie, curl, or similar tools.

This endpoint is intended for schema discovery. It is used to inspect content types, their fields, relations, and enabled locales. It is not intended to read public content entries; use the public API at /api for that.

Local setup

If the endpoint will be consumed by an agent, script, or local integration, the usual approach is to store the token in an environment variable. For example:

PLANK_MCP=your_mcp_token

That token is generated from Settings > API Tokens inside the Plank instance.

The variable name is a consumer-side convention, not a server requirement. Plank does not require any specific environment variable name; the only hard requirement is sending the token in Authorization: Bearer <token>.

What it exposes

  • The list of available content types.

  • The list of enabled locales and the default locale.

  • The full schema for each content type.

What it does not expose

  • Public entry reads.

  • Mutations.

  • Editing operations.

Authentication

  • Requires Authorization: Bearer <token>.

  • The token must have access type mcp-server.

  • read-only and full-access tokens do not work against /mcp.

  • mcp-server tokens do not work against /api.

v1 transport

  • Base endpoint: /mcp

  • POST /mcp: JSON-RPC 2.0 messages

  • GET /mcp: minimal endpoint metadata

  • DELETE /mcp: not supported in v1

Use POST /mcp for MCP operations. GET /mcp only exposes minimal endpoint metadata and cannot be used to send JSON-RPC messages such as initialize, resources/list, or resources/read.

Supported methods

  • initialize

  • ping

  • resources/list

  • resources/read

Exposed resources

  • plank://content-types

  • plank://locales

  • plank://content-types/{slug}/schema

Recommended flow

  1. Call initialize.

  2. Call resources/list to discover the available resources.

  3. Call resources/read on plank://content-types.

  4. Call resources/read on plank://content-types/{slug}/schema for each content type you want to inspect.

  5. Call resources/read on plank://locales if you need the locale configuration.

initialize should be called before resources/list and resources/read. Some implementations may tolerate direct calls, but the recommended integration flow is to initialize the JSON-RPC session first.

Manual usage

Although this endpoint is designed for agents or integrations, it can also be used manually.

Important considerations:

  • Use POST /mcp to send JSON-RPC messages.

  • Do not use GET /mcp for initialize, resources/list, or resources/read.

  • Send Content-Type: application/json.

  • Send Authorization: Bearer <token>.

Example with HTTPie:

POST https://your-instance.com/mcp
Auth: Bearer token
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "resources/read",
  "params": {
    "uri": "plank://content-types"
  }
}'

Request examples

initialize

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2025-11-25",
    "capabilities": {},
    "clientInfo": {
      "name": "example-client",
      "version": "0.1.0"
    }
  }
}

resources/list

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "resources/list"
}

resources/read for content-types

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "resources/read",
  "params": {
    "uri": "plank://content-types"
  }
}

resources/read for a content type schema

{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "resources/read",
  "params": {
    "uri": "plank://content-types/post/schema"
  }
}

resources/read for locales

{
  "jsonrpc": "2.0",
  "id": 5,
  "method": "resources/read",
  "params": {
    "uri": "plank://locales"
  }
}

Response examples

initialize response

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-11-25",
    "capabilities": {
      "resources": {}
    },
    "serverInfo": {
      "name": "plank-cms",
      "title": "Plank CMS",
      "version": "0.30.0"
    },
    "instructions": "Use resources/list to discover available resources, then resources/read to load Plank content types, per-type schemas, and locales."
  }
}

resources/list response

{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "resources": [
      {
        "name": "content-types",
        "title": "Content Types",
        "uri": "plank://content-types",
        "description": "Lists the content types available in this Plank instance.",
        "mimeType": "application/json"
      },
      {
        "name": "locales",
        "title": "Locales",
        "uri": "plank://locales",
        "description": "Lists the enabled locales and the default locale for this Plank instance.",
        "mimeType": "application/json"
      },
      {
        "name": "content-type-schema-post",
        "title": "Post schema",
        "uri": "plank://content-types/post/schema",
        "description": "Schema for the \"post\" content type.",
        "mimeType": "application/json",
        "annotations": {
          "lastModified": "2026-05-23T00:00:00.000Z"
        }
      }
    ]
  }
}

resources/read response for plank://content-types

{
  "jsonrpc": "2.0",
  "id": 3,
  "result": {
    "contents": [
      {
        "uri": "plank://content-types",
        "mimeType": "application/json",
        "text": "{\n  \"contentTypes\": [\n    {\n      \"name\": \"Posts\",\n      \"slug\": \"post\",\n      \"kind\": \"collection\",\n      \"previewEnabled\": true,\n      \"isDefault\": true,\n      \"schemaUri\": \"plank://content-types/post/schema\",\n      \"updatedAt\": \"2026-05-23T00:00:00.000Z\"\n    }\n  ]\n}"
      }
    ]
  }
}

resources/read response for plank://locales

{
  "jsonrpc": "2.0",
  "id": 5,
  "result": {
    "contents": [
      {
        "uri": "plank://locales",
        "mimeType": "application/json",
        "text": "{\n  \"defaultLocale\": \"en\",\n  \"locales\": [\n    \"en\",\n    \"es\"\n  ]\n}"
      }
    ]
  }
}

resources/read response for plank://content-types/{slug}/schema

{
  "jsonrpc": "2.0",
  "id": 4,
  "result": {
    "contents": [
      {
        "uri": "plank://content-types/post/schema",
        "mimeType": "application/json",
        "text": "{\n  \"id\": \"c33f35ea-0d98-4a00-95a5-2d8c2d88cf0c\",\n  \"name\": \"Posts\",\n  \"slug\": \"post\",\n  \"kind\": \"collection\",\n  \"previewEnabled\": true,\n  \"tableName\": \"posts\",\n  \"fields\": [\n    {\n      \"name\": \"title\",\n      \"type\": \"string\",\n      \"width\": \"half\",\n      \"required\": true\n    },\n    {\n      \"name\": \"slug\",\n      \"type\": \"uid\",\n      \"width\": \"half\",\n      \"required\": true,\n      \"targetField\": \"title\"\n    }\n  ],\n  \"isDefault\": true,\n  \"createdAt\": \"2026-05-01T00:00:00.000Z\",\n  \"updatedAt\": \"2026-05-23T00:00:00.000Z\"\n}"
      }
    ]
  }
}

Useful payloads after parsing contents[0].text

resources/read returns the useful payload inside result.contents[], and the JSON document itself is serialized as a string in text. The client must parse that value before consuming it.

plank://content-types

{
  "contentTypes": [
    {
      "name": "Posts",
      "slug": "post",
      "kind": "collection",
      "previewEnabled": true,
      "isDefault": true,
      "schemaUri": "plank://content-types/post/schema",
      "updatedAt": "2026-05-23T00:00:00.000Z"
    }
  ]
}

plank://locales

{
  "defaultLocale": "en",
  "locales": ["en", "es"]
}

plank://content-types/{slug}/schema

Returns the full content type object exactly as Plank persists and uses it internally, including:

  • id

  • name

  • slug

  • kind

  • previewEnabled

  • tableName

  • fields

  • metadata such as isDefault, createdAt, and updatedAt when present

Common errors

Missing token

{
  "error": "API token required"
}

Invalid token or wrong access type

The server should respond with an authentication error. The exact shape may vary depending on the HTTP layer or middleware, but the usual cause is one of the following:

  • the token does not exist

  • the token is invalid

  • the token does not have access type mcp-server

Unknown content type

{
  "jsonrpc": "2.0",
  "id": 7,
  "error": {
    "code": -32004,
    "message": "Content type not found",
    "data": {
      "slug": "unknown-slug"
    }
  }
}

Implementation notes

  • The MCP endpoint is intended for schema discovery, not for serving public content.

  • resources/list enumerates MCP resources; it does not return the schema payload itself.

  • resources/read returns a contents array; the useful payload is in contents[n].text.

  • text contains serialized JSON, so the client must call JSON.parse(...).

  • Consumers can translate the schema into TypeScript interfaces, validators, internal clients, or custom tooling.

  • The endpoint can be used by automation or internal integrations without exposing that logic to the public frontend runtime.

Made with Plank CMS

Plank CMS