Skip to main content
Glama

Canonical Postiz client for any MCP-compatible client. Full coverage of the Postiz public API (integrations, posts, uploads, analytics, video) with env-gated writes, confirm-required deletes, and a built-in rate-limit guard.

Ships as a stdio MCP server and a first-class OpenClaw native plugin from the same package.

Point it at your Postiz and your agent gets 22 typed, schema-validated tools. Reads are always on; writes stay off until you set POSTIZ_ENABLE_WRITE=true. The fatal line above is the server refusing to start without config, not a crash.

Why

If you self-host Postiz and want Claude / Codex / OpenClaw / Hermes / any MCP client to interact with it, this gives you a typed, tested, single-purpose tool surface instead of hand-rolled HTTP calls in every workflow.

Related MCP server: Postoria MCP Server

See also

gitroomhq/postiz-agent is the official Postiz CLI from Nevo David. It's the right pick if you're a Postiz Cloud subscriber wanting OAuth-flow auth, or if you only need a Bash-callable surface in Claude Code.

This package (postiz-mcp) is the right pick if you:

  • Self-host Postiz and want to skip running an OAuth broker

  • Use an MCP-native client (Claude Desktop, OpenClaw, Hermes, Codex CLI) and want typed tool schemas instead of bash-shelling

  • Want defense-in-depth gating (writes off by default, deletes require enableDelete + confirm: true)

  • Want a local rate-limit guard that refuses to send when your hourly budget is exhausted

  • Need Cloudflare Access service-token support

Warnings before you wire this up

  • Postiz writes are public side effects. A successful postiz_create_post with type: "now" (or a near-term schedule) lands on real social accounts. Once published, posts can be deleted from Postiz but the platform-side post stays live - Postiz cannot recall it.

  • The Postiz public API is rate-limited at 30 requests/hour by default. This server tracks the limit locally and refuses to send when the budget is exhausted. Override with POSTIZ_RATE_LIMIT_PER_HOUR if your Postiz instance is configured higher.

  • Writes and deletes are gated off by default. Reads always work. To enable writes you must explicitly set POSTIZ_ENABLE_WRITE=true. To enable deletes you must additionally set POSTIZ_ENABLE_DELETE=true AND pass confirm: true in the tool call.

Tools

Reads (always on)

  • postiz_list_integrations - list connected channels

  • postiz_check_integration - verify API key

  • postiz_find_next_slot - next free posting slot for a channel

  • postiz_list_posts - posts in a date window

  • postiz_get_missing_content - recover platform content for a Postiz post with a missing releaseId

  • postiz_get_integration_settings - live runtime config for ONE connected integration: rules, maxLength (verified-aware), settings DTO, available platform-specific tools. Use before postiz_create_post when content length matters.

  • postiz_list_notifications - Postiz UI notifications

  • postiz_get_platform_analytics - followers / impressions / engagement

  • postiz_get_post_analytics - likes / comments / shares

  • postiz_list_voices - AI video voice catalog

  • postiz_get_provider_settings_schema - per-provider settings schema (X, LinkedIn, Reddit, etc.) bundled at build time

Writes (require POSTIZ_ENABLE_WRITE=true)

  • postiz_create_post - schedule / publish-now / draft

  • postiz_connect_integration - generate OAuth URL for a new channel

  • postiz_invoke_integration_tool - call a per-platform tool method on an integration (e.g. Reddit subreddit search, YouTube playlist lookup) via POST /api/public/v1/integration-trigger/{id}. Discover valid methodName values via postiz_get_integration_settings(id).tools first.

  • postiz_update_post_status - toggle DRAFT ↔ QUEUE

  • postiz_update_post_release_id - reattach a Postiz post to its platform-side release

  • postiz_upload_file - multipart upload from local file or base64

  • postiz_upload_from_url - server-side fetch

  • postiz_generate_video - AI video generation

Deletes (require POSTIZ_ENABLE_WRITE=true + POSTIZ_ENABLE_DELETE=true + confirm: true)

  • postiz_delete_post - cascades to whole group

  • postiz_delete_post_group - delete every post in a cross-post group

  • postiz_delete_integration - disconnect channel + all its scheduled posts

Install

npm install -g postiz-mcp

Or from source:

git clone https://github.com/solomonneas/postiz-mcp.git
cd postiz-mcp
npm install
npm run build

Configuration

Set these environment variables in your MCP client config:

Variable

Required

Default

Description

POSTIZ_URL

yes

-

Base URL, e.g. http://localhost:5000 or https://postiz.example.com

POSTIZ_API_KEY

yes

-

API key from Postiz Settings → Public API

POSTIZ_ENABLE_WRITE

no

false

Set true to expose create / update / upload / connect / generate-video tools

POSTIZ_ENABLE_DELETE

no

false

Set true (in addition to write) to expose delete tools

POSTIZ_REQUEST_TIMEOUT_MS

no

30000

HTTP timeout (ms)

POSTIZ_RATE_LIMIT_PER_HOUR

no

30

Local guard ceiling. The server still trusts response headers when present.

POSTIZ_CF_ACCESS_CLIENT_ID

no

-

Cloudflare Access service token client id (only needed if Postiz is behind CF Access)

POSTIZ_CF_ACCESS_CLIENT_SECRET

no

-

Cloudflare Access service token secret

Getting an API key

  1. Log into Postiz as an admin

  2. Settings → Public API → Generate API Key

  3. Copy the value (starts with pos_ or is a raw UUID depending on your Postiz version)

Claude Desktop

Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

{
  "mcpServers": {
    "postiz": {
      "command": "postiz-mcp",
      "env": {
        "POSTIZ_URL": "http://localhost:5000",
        "POSTIZ_API_KEY": "your-api-key-here",
        "POSTIZ_ENABLE_WRITE": "true",
        "POSTIZ_ENABLE_DELETE": "false"
      }
    }
  }
}

Claude Code

claude mcp add postiz \
  --env POSTIZ_URL=http://localhost:5000 \
  --env POSTIZ_API_KEY=your-api-key-here \
  --env POSTIZ_ENABLE_WRITE=true \
  -- postiz-mcp

Add --scope user to make it available from any directory instead of only the current project.

OpenClaw

postiz-mcp is also an OpenClaw native plugin. From a source checkout:

openclaw plugin add /absolute/path/to/postiz-mcp \
  --config '{
    "baseUrl": "http://localhost:5000",
    "apiKeyEnv": "POSTIZ_API_KEY",
    "enableWrite": true,
    "enableDelete": false
  }'

Then export the API key and restart the gateway:

export POSTIZ_API_KEY=your-api-key-here
systemctl --user restart openclaw-gateway
openclaw plugin list   # confirm "postiz" is enabled

You can also run it as a regular MCP server under OpenClaw:

openclaw mcp set postiz '{
  "command": "postiz-mcp",
  "env": {
    "POSTIZ_URL": "http://localhost:5000",
    "POSTIZ_API_KEY": "your-api-key-here",
    "POSTIZ_ENABLE_WRITE": "true"
  }
}'

Hermes Agent

Hermes Agent reads MCP config from ~/.hermes/config.yaml under mcp_servers. Add an entry:

mcp_servers:
  postiz:
    command: "postiz-mcp"
    env:
      POSTIZ_URL: "http://localhost:5000"
      POSTIZ_API_KEY: "your-api-key-here"
      POSTIZ_ENABLE_WRITE: "true"

Or from a source checkout:

mcp_servers:
  postiz:
    command: "node"
    args: ["/absolute/path/to/postiz-mcp/dist/mcp-server.js"]
    env:
      POSTIZ_URL: "http://localhost:5000"
      POSTIZ_API_KEY: "your-api-key-here"
      POSTIZ_ENABLE_WRITE: "true"

Then reload MCP from inside a Hermes session:

/reload-mcp

Codex CLI

Codex CLI registers MCP servers via codex mcp add:

codex mcp add postiz \
  --env POSTIZ_URL=http://localhost:5000 \
  --env POSTIZ_API_KEY=your-api-key-here \
  --env POSTIZ_ENABLE_WRITE=true \
  -- postiz-mcp

Or from a source checkout:

codex mcp add postiz \
  --env POSTIZ_URL=http://localhost:5000 \
  --env POSTIZ_API_KEY=your-api-key-here \
  --env POSTIZ_ENABLE_WRITE=true \
  -- node /absolute/path/to/postiz-mcp/dist/mcp-server.js

Codex writes the entry to ~/.codex/config.toml under [mcp_servers.postiz]. Verify with:

codex mcp list

Postiz behind Cloudflare Access

If your Postiz is exposed via Cloudflare Tunnel + Access (e.g. https://postiz.example.com), generate a service token in the Cloudflare Zero Trust dashboard and add the env vars:

export POSTIZ_CF_ACCESS_CLIENT_ID=your-cf-id.access
export POSTIZ_CF_ACCESS_CLIENT_SECRET=your-cf-secret

The MCP server forwards them as CF-Access-Client-Id / CF-Access-Client-Secret on every request. If you forget them, you'll get a clear PostizCfAccessChallengeError instead of a confusing HTML response.

Example prompts

  • "List the integrations on my Postiz."

  • "Schedule a Bluesky post for tomorrow 9am: 'Just shipped postiz-mcp.'"

  • "What's the next available LinkedIn slot? Schedule this 4-tweet thread for that time on X with replies set to verified-only."

  • "What posted last week and how did the X post on Tuesday do?"

  • "Show me the X provider settings schema so I can construct a thread payload."

Thread mode (multi-post threads with per-post media + delay)

postiz_create_post's posts[].value[] array is a sequence - every entry posts to the same integration in order, with optional per-entry image[] and delay (minutes between posts). Use this for X threads, LinkedIn carousel-style follow-ups, etc.

{
  "type": "schedule",
  "date": "2026-05-15T09:00:00.000Z",
  "posts": [
    {
      "integrationId": "integration-uuid-here",
      "value": [
        {
          "content": "Launching our new feature today.",
          "image": [{ "id": "abc", "path": "<path returned by postiz_upload_file>" }]
        },
        {
          "content": "Here's what's new under the hood:",
          "delay": 5
        },
        {
          "content": "Try it and let us know what breaks.",
          "delay": 5
        }
      ]
    }
  ]
}

Each value[] after the first uses delay (minutes) to space posts out. image[] is optional per entry and uses paths returned by postiz_upload_file / postiz_upload_from_url - raw filesystem paths and external URLs are rejected.

Provider settings schemas

postiz_get_provider_settings_schema returns the bundled per-provider settings reference (parsed from docs.postiz.com/public-api/providers/{slug}.md). Use it before postiz_create_post when you need provider-specific fields like X's who_can_reply_post or LinkedIn's audience.

The schemas are refreshed monthly by a GitHub Actions workflow that opens a PR if Postiz updated any provider doc. To refresh manually:

npm run refresh-schemas

Development

npm install
npm run typecheck
npm test
npm run build

HTTP mode

Start the built entry point with node dist/mcp-server.js --http. Without --http, the existing stdio transport is used. Keep the existing upstream configuration (POSTIZ_URL, API key, optional Cloudflare Access credentials). HTTP mode also accepts:

Variable

Default

Purpose

POSTIZ_MCP_HTTP_TOKEN

required

Separate bearer secret of at least 32 characters, trimmed. Blank, short and upstream-credential values are rejected.

POSTIZ_MCP_HTTP_HOST

loopback

Listening interface.

POSTIZ_MCP_HTTP_PORT

18815

Listening port.

POSTIZ_MCP_HTTP_LOCAL_FILES

false

Set exactly true to allow filePath uploads from the server's filesystem.

/mcp requires bearer authentication. GET /healthz returns only {"status":"ok"} without authentication. Host and Origin checks run before authentication. Requests are capped at 1 MiB, including base64 uploads. Sessions are capped at 128 and expire after 30 idle minutes. Request and header timeouts are 30 and 10 seconds. SIGTERM and SIGINT close sessions and the listener.

Every connection gets its own MCP server. All connections use the same Postiz account and share its rate budget. Write and delete flags are operator settings, and confirm: true is required on each delete call. There is no stored pending confirmation, draft, or publish ticket. Postiz stores drafts and schedules.

HTTP local file uploads require both the opt-in above and POSTIZ_UPLOAD_ROOTS (comma- or colon-separated directories). Real paths and the opened file are checked against those roots. Symlink escapes are rejected. File metadata and contents are read through one handle, with the existing upload-size cap. Use POSTIZ_MCP_HTTP_LOCAL_FILES=true and POSTIZ_UPLOAD_ROOTS for large media: base64 input must fit inside the 1 MiB request limit, including JSON overhead. Base64 uploads do not need filesystem access. Stdio and plugin uploads keep their existing opt-in through upload roots.

URL uploads are fetched by Postiz, rather than by this MCP process. Recognized literal loopback, private IPv4, and private IPv6 ranges are rejected. IPv4-compatible, NAT64, and 6to4 IPv6 forms are not fully classified. This check does not resolve DNS or inspect redirects: a hostname resolving to a private address or a public URL redirecting there can still reach the Postiz host's network. Restrict that host's outbound network access before enabling writes for clients you do not trust.

Claude Code configuration:

{
  "mcpServers": {
    "postiz": {
      "type": "http",
      "url": "http://localhost:18815/mcp",
      "headersHelper": "/path/to/helper POSTIZ_MCP_HTTP_TOKEN"
    }
  }
}

The helper reads the named environment variable and prints a JSON object such as {"Authorization":"Bearer ..."} to stdout. Keep the secret out of the configuration file.

Codex streamable HTTP configuration:

[mcp_servers.postiz]
url = "http://localhost:18815/mcp"
bearer_token_env_var = "POSTIZ_MCP_HTTP_TOKEN"

An example user service is in docs/systemd/postiz-mcp-http.service. Set its working directory and executable paths for your installation. Create its EnvironmentFile with mode 0600, containing upstream configuration and a separate HTTP token. The example is not installed or started automatically.

License

MIT

Available Tools

20 tools
postiz_check_integrationA

Verify the configured Postiz API key is valid and reaches the configured baseUrl. Useful as a first call before any other tool — if this returns ok, every other tool can authenticate against the same instance.

Inputs: no named parameters.

postiz_connect_integrationA

Generate the OAuth authorization URL for connecting a new social channel. Returns a url the user must open in a browser to finish the flow — Postiz redirects back to its own callback. This tool does NOT run a callback server. Requires enableWrite.

Inputs: provider (required), refresh.

postiz_create_postA

Create, schedule, or immediately publish one or more posts via POST /api/posts. PUBLIC SIDE EFFECT: with type='now' or a near-term schedule, this lands on real social accounts. Use postiz_get_provider_settings_schema first to construct valid settings blocks. Requires enableWrite.

Inputs: type (required), date (required), posts (required), shortLink, +1 more.

postiz_delete_integrationA

Disconnect a connected social channel. Cascades — Postiz also deletes every scheduled post for that integration. Requires enableWrite + enableDelete + confirm=true. Returns ok:false / not_found on 404 (already disconnected).

Inputs: integrationId (required), confirm (required).

postiz_delete_postA

Delete a Postiz post by id. CASCADES — every post in the same group is removed. Already-published platform posts remain live. Requires enableWrite + enableDelete + confirm=true.

Inputs: postId (required), confirm (required).

postiz_delete_post_groupA

Delete every post in a group (cross-post unit) via DELETE /api/posts/group/{group}. Use when you want to retract a whole cross-post in one call. Requires enableWrite + enableDelete + confirm=true.

Inputs: group (required), confirm (required).

postiz_find_next_slotA

Return the next available posting time for a given integration. The slot respects the org's configured posting schedule, so this is the right answer to use as date in postiz_create_post when you don't have a specific time in mind.

Inputs: integrationId (required).

postiz_generate_videoA

Generate an AI video via POST /api/video/generate. COST IMPLICATION: video generation may bill against the configured Postiz video integration's credit pool. Requires enableWrite. Body shape is provider-specific — see Postiz video docs.

Inputs: body (required).

postiz_get_missing_contentA

Fetch recent platform-side content for a post whose Postiz releaseId is marked missing. Pair with postiz_update_post_release_id to reattach.

Inputs: postId (required).

postiz_get_platform_analyticsA

Get follower / impression / engagement analytics for a connected channel via GET /api/analytics/platform. Available metrics depend on what the platform exposes to Postiz.

Inputs: integrationId (required), date.

postiz_get_post_analyticsA

Get per-post engagement metrics (likes, comments, shares) via GET /api/analytics/post. Returns whatever the source platform exposes — different shape per provider.

Inputs: postId (required).

postiz_get_provider_settings_schemaA

Look up the settings block schema for a Postiz provider (X, LinkedIn, Reddit, etc.) — bundled at build time from docs.postiz.com. Returns a default-settings template, the provider's __type value, and (by default) the full markdown reference. Call this before postiz_create_post when you need provider-specific fields.

Inputs: provider (required), includeMarkdown.

postiz_list_integrationsA

List every connected social-media channel for the org behind the configured Postiz API key. Returns id, name, providerIdentifier (the value used as __type in post settings), profile, and disabled state. Use this BEFORE postiz_create_post to get the integration id you need to target.

Inputs: no named parameters.

postiz_list_notificationsA

List notifications, sorted most-recent first. Useful for surfacing posting failures, OAuth re-auth prompts, and new-feature notices Postiz shows in its UI.

Inputs: page.

postiz_list_postsA

List posts in a date range via GET /api/posts. Returns scheduled, queued, and published posts with their integration, content, state, and any platform release URL.

Inputs: startDate, endDate, display, customer.

postiz_list_voicesA

List available AI voices for video generation via GET /api/video/function?functionName=voices. Required input for postiz_generate_video — pick a voice id from the returned catalog.

Inputs: integrationId.

postiz_update_post_release_idA

Update the releaseId (and optionally releaseURL) of a Postiz post via PATCH /api/posts/{id}/release-id. Use to reconcile a Postiz post with the actual platform-side release after a missing-content event. Requires enableWrite.

Inputs: postId (required), releaseId (required), releaseURL.

postiz_update_post_statusA

Transition a Postiz post between DRAFT and QUEUE via PATCH /api/posts/{id}/status. Moving DRAFT→QUEUE re-enters the schedule using the post's existing publishDate. Requires enableWrite.

Inputs: postId (required), state (required).

postiz_upload_fileA

Upload a media file (image, video) to Postiz storage via POST /api/uploads/file. Returns { id, path } that you can pass into postiz_create_post value[].image[]. Requires enableWrite.

Inputs: filePath, base64, fileName, mimeType.

postiz_upload_from_urlA

Upload a media file from a public URL via POST /api/uploads/url. Postiz fetches the URL server-side, so this works for sources the MCP host can't reach. Returns { id, path } usable in postiz_create_post value[].image[]. Requires enableWrite.

Inputs: url (required).

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.1/5.0

    Scored across 20 tools

    Disambiguation5/5

    Each tool targets a unique action and resource type. For example, postiz_create_post, postiz_delete_post, and postiz_list_posts are clearly distinct. Even overlapping actions like postiz_delete_post and postiz_delete_post_group are differentiated by scope (single vs. group). Descriptions clarify boundaries effectively.

    Naming Consistency5/5

    All tools follow a consistent 'postiz_verb_noun' pattern with underscore separation. Verbs like check, connect, create, delete, find, generate, get, list, update, and upload are used uniformly. There is no mixing of camelCase or other conventions.

    Tool Count4/5

    With 20 tools, the set is slightly above the typical 'well-scoped' range (3-15) but still reasonable given the breadth of Postiz's functionality (post management, analytics, AI video, integrations, file uploads). Each tool serves a distinct purpose.

    Completeness4/5

    The tools cover the main lifecycle: integration management, post CRUD, analytics, file uploads, and AI video generation. Minor gaps include no direct 'get post by ID' (though list_posts can retrieve it) and no interaction management (comments, etc.), but core workflows are well-supported.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • F
      license
      Not graded
      quality
      D
      maintenance
      Enables interaction with the Postiz social media management platform through MCP tools. Supports creating and managing posts, retrieving integrations, and accessing account information through multiple transport protocols.
      2
      -
    • A
      license
      Not graded
      quality
      F
      maintenance
      MCP server for managing social media posts across multiple platforms using the Postiz API. Supports creating, updating, deleting posts, and generating videos.
      38 npm
      3
      MIT
    • A
      license
      A
      quality
      B
      maintenance
      Enables managing social media posts, accounts, and AI-powered content features from any MCP client, including scheduling, publishing, analysis, and AI caption generation.
      14
      23 npm
      MIT