postiz-mcp
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.
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.
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_postwithtype: "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_HOURif 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 setPOSTIZ_ENABLE_DELETE=trueAND passconfirm: truein the tool call.
Tools
Reads (always on)
postiz_list_integrations- list connected channelspostiz_check_integration- verify API keypostiz_find_next_slot- next free posting slot for a channelpostiz_list_posts- posts in a date windowpostiz_get_missing_content- recover platform content for a Postiz post with a missingreleaseIdpostiz_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 notificationspostiz_get_platform_analytics- followers / impressions / engagementpostiz_get_post_analytics- likes / comments / sharespostiz_list_voices- AI video voice catalogpostiz_get_provider_settings_schema- per-providersettingsschema (X, LinkedIn, Reddit, etc.) bundled at build time
Writes (require POSTIZ_ENABLE_WRITE=true)
postiz_create_post- schedule / publish-now / draftpostiz_connect_integration- generate OAuth URL for a new channelpostiz_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 validmethodNamevalues viapostiz_get_integration_settings(id).toolsfirst.postiz_update_post_status- toggle DRAFT ↔ QUEUEpostiz_update_post_release_id- reattach a Postiz post to its platform-side releasepostiz_upload_file- multipart upload from local file or base64postiz_upload_from_url- server-side fetchpostiz_generate_video- AI video generation
Deletes (require POSTIZ_ENABLE_WRITE=true + POSTIZ_ENABLE_DELETE=true + confirm: true)
postiz_delete_post- cascades to whole grouppostiz_delete_post_group- delete every post in a cross-post grouppostiz_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
- Log into Postiz as an admin
- Settings → Public API → Generate API Key
- 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
License
MIT