# PostMCP AI Model Context Protocol (MCP) Server [![npm version](https://img.shields.io/npm/v/@postmcpai/server.svg?style=flat-square)](https://www.npmjs.com/package/@postmcpai/server) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT) [![MCP Compatible](https://img.shields.io/badge/MCP-Compatible-blue.svg?style=flat-square)](https://modelcontextprotocol.io) Official [PostMCP AI](https://postmcpai.com) Model Context Protocol (MCP) Server. Connect your social media publishing pipelines directly into AI assistants, desktop applications, IDE workflows, and web environments like **Claude Desktop**, **Claude.ai**, **Cursor**, and **ChatGPT Custom GPTs**. Supported platforms include **LinkedIn**, **X (Twitter)**, **Facebook**, **Instagram**, **Threads**, **Bluesky**, and **YouTube Shorts**. --- ## ๐Ÿš€ Features & Capabilities - ๐Ÿค– **17 Built-in Tools**: Workspaces, connected accounts and their token health, the post queue, pre-flight checks, create/schedule/reschedule/publish/retry/delete, per-post and per-profile analytics, and image generation. - ๐Ÿ–ผ๏ธ **Carousels & galleries**: Pass `mediaUrls` to publish a carousel on Instagram and Threads, a multi-photo post on Facebook and LinkedIn, or a four-image gallery on X and Bluesky - one call, every network's ceiling checked up front. - โšก **Dual Transport Modes**: Native **Stdio mode** (for local desktop apps & IDEs) and **Streamable HTTP mode** (for web services, Claude.ai, and remote connectors). - ๐Ÿ”‘ **Flexible Authentication**: Auto-detects API key from environment variables (`POSTMCPAI_API_KEY`), URL query parameters (`?apikey=YOUR_KEY`), or HTTP authorization headers (`x-api-key`, `Bearer token`). - ๐Ÿ—‚๏ธ **Multi-Workspace Aware**: The API key carries its own workspace, so a bare key is enough. To act on another one, every tool takes an optional `workspaceId`, also settable per connection (`?projectId=...`, `x-project-id`) or per process (`POSTMCPAI_PROJECT_ID`). - ๐Ÿค– **ChatGPT Actions Compatible**: Includes built-in OpenAPI 3.0 specification generator (`/openapi.json`) and REST endpoints (`/api/tools/:name`) for ChatGPT Custom GPT integration. - ๐Ÿ”’ **OAuth 2.0 & RFC 9728 Support**: Advertises PKCE authorization server metadata for seamless dynamic client registration with Claude.ai. --- ## ๐Ÿ“ Repository Architecture ``` mcp-server/ โ”œโ”€โ”€ bin/ โ”‚ โ””โ”€โ”€ cli.js # Executable CLI entry point (Stdio / HTTP mode runner) โ”œโ”€โ”€ src/ โ”‚ โ”œโ”€โ”€ config.js # Centralized configuration & environment loader โ”‚ โ”œโ”€โ”€ client.js # Backend API client, API key & workspace extraction โ”‚ โ”œโ”€โ”€ platforms.js # Platform limits, credit pricing & post cost helper โ”‚ โ”œโ”€โ”€ tools/ โ”‚ โ”‚ โ”œโ”€โ”€ definitions.js# MCP tool JSON schemas & parameter specifications โ”‚ โ”‚ โ”œโ”€โ”€ handlers.js # MCP tool execution handlers โ”‚ โ”‚ โ””โ”€โ”€ index.js # Tool definitions aggregator โ”‚ โ”œโ”€โ”€ server.js # MCP Server instance factory โ”‚ โ”œโ”€โ”€ routes/ โ”‚ โ”‚ โ”œโ”€โ”€ oauth.js # OAuth 2.0 & RFC 9728 discovery endpoints โ”‚ โ”‚ โ”œโ”€โ”€ openapi.js # OpenAPI 3.0 schema & ChatGPT REST endpoints โ”‚ โ”‚ โ”œโ”€โ”€ mcpHttp.js # MCP Streamable HTTP transport (/mcp) โ”‚ โ”‚ โ””โ”€โ”€ health.js # Health check & system metadata endpoints โ”‚ โ”œโ”€โ”€ app.js # Express application factory โ”‚ โ””โ”€โ”€ index.js # Main library entry point โ”œโ”€โ”€ index.js # Executable wrapper script โ”œโ”€โ”€ package.json โ””โ”€โ”€ README.md ``` --- ## โš™๏ธ Environment Configuration | Environment Variable | Description | Default Value | | :--- | :--- | :--- | | `POSTMCPAI_API_KEY` | **Required.** Your secret API key from the PostMCP AI dashboard. | `None` | | `POSTMCPAI_API_URL` | Backend API root. Only set for a self-hosted or local backend. | `https://api.postmcpai.com` | | `POSTMCPAI_PROJECT_ID` | Optional. Overrides the workspace the API key is bound to. Overridden in turn by a call's `workspaceId`. | The workspace the API key was issued from | | `PORT` | Setting this launches the server in **Remote Streamable HTTP Mode**. | `None` (Defaults to Stdio Mode) | --- ## ๐Ÿ› ๏ธ MCP Tools Reference Every tool below also accepts an optional `workspaceId` (from `list_workspaces`) to act on a specific workspace. ### Reading | Tool Name | Description | Required | Optional | | :--- | :--- | :--- | :--- | | `get_user_info` | Authenticated user: plan, credit balance, active workspace and role. | โ€” | `workspaceId` | | `list_workspaces` | Every workspace the user belongs to, with ids, roles, and connected platforms. | โ€” | โ€” | | `get_connected_accounts` | Connected social profiles with the `profileId` needed to target them. | โ€” | `workspaceId` | | `get_account_health` | Connections whose token expired or is close to it and need reconnecting. | โ€” | `workspaceId` | | `get_profile_analytics` | A connected profile's followers, following, post count and views from its network. Stored reading is free; `refresh` reads the network now for 1 credit. | `platform`, `profileId` | `refresh` | | `list_posts` | Post queue, newest first, with per-profile delivery status, pagination and counts. | โ€” | `status`, `page`, `limit`, `all` | | `get_post` | One post in full: which profiles received it, live URLs, and per-profile errors. | `id` | โ€” | | `get_post_analytics` | Views, likes, comments, shares, saves and clicks per profile, plus the raw platform metrics. Stored reading is free; `refresh` reads the networks now for 1 credit. | `id` | `refresh` | ### Writing | Tool Name | Description | Required | Optional | | :--- | :--- | :--- | :--- | | `preflight_post` | Dry run: character limits, unconnected profiles, missing media, carousel ceilings, credit cost. Publishes nothing. | `content` | `targetAccounts`, `platforms`, `mediaUrl`, `mediaUrls` | | `create_post` | Draft, schedule, or immediately publish a post to named profiles. Each profile becomes its own post with its own id. Several `mediaUrls` publish as a carousel. | `content` | `targetAccounts`, `variants`, `platforms`, `publishImmediately`, `scheduleDate`, `scheduleTime`, `timezone`, `mediaUrl`, `mediaUrls`, `youtube` | | `publish_post_now` | Publish an existing post immediately; also retries a failed post, skipping delivered profiles. | `id` | โ€” | | `update_post` | Update content, target profiles, schedule, media, or status. `mediaUrls` replaces the whole attachment set. | `id` | `content`, `targetAccounts`, `platforms`, `scheduleDate`, `scheduleTime`, `timezone`, `mediaUrl`, `mediaUrls`, `youtube`, `status` | | `reschedule_post` | Move a post to a new slot, keeping copy and targets. Re-arms failed and draft posts. | `id`, `scheduleDate`, `scheduleTime` | `timezone` | | `reset_stuck_post` | Release a post stuck mid-publish so it can be retried. Delivered profiles keep their state. | `id` | `force` | | `delete_post` | Cancel and delete a scheduled or failed post. | `id` | โ€” | | `generate_image` | Generate a post image and return its hosted URL for `mediaUrl`. Costs 20 credits; paid plans only. | `prompt` | `styleImageUrl` | ### Batching | Tool Name | Description | Required | Optional | | :--- | :--- | :--- | :--- | | `multicall` | Run up to 20 of the tools above in one request, in order. Tool names are validated before anything executes, so a typo cannot leave half a batch written. Cannot nest. | `calls` | `stopOnError`, `workspaceId` | ```json { "calls": [ { "id": "img", "tool": "generate_image", "arguments": { "prompt": "launch banner" } }, { "tool": "create_post", "arguments": { "content": "We shipped it ๐Ÿš€", "targetAccounts": [ { "platform": "linkedin", "profileId": "lin_7741903" }, { "platform": "twitter", "profileId": "tw_1293847", "content": "We shipped it ๐Ÿš€" } ], "scheduleDate": "2026-09-01", "scheduleTime": "10:00", "timezone": "Asia/Kolkata" } } ], "stopOnError": true } ``` The reply carries one entry per call โ€” `{ id, tool, ok, result }` or `{ id, tool, ok: false, error }` โ€” plus counts and, when a failure stopped the batch, the calls that were skipped. ### Carousels `mediaUrls` is the ordered attachment set. One URL is an ordinary media post; two or more publish as a multi-media post on every network but YouTube, with the first URL as the cover: | Platform | Items per post | Video in a set of several? | Lands as | | :--- | ---: | :---: | :--- | | Instagram | 2โ€“10 | yes | Carousel | | Threads | 2โ€“20 | yes | Carousel | | Facebook | up to 10 | no | Multi-photo post | | LinkedIn | up to 20 | no | Multi-image post | | X / Twitter | up to 4 | no | Gallery on one tweet | | Bluesky | up to 4 | no | Gallery on one post | | YouTube | 1 | โ€” | One video per upload | ```json { "content": "Five things we learned shipping v2 ๐Ÿ‘‰", "targetAccounts": [ { "platform": "instagram", "profileId": "17841400000000" }, { "platform": "threads", "profileId": "9988776655" }, { "platform": "twitter", "profileId": "tw_1293847", "mediaUrls": ["https://cdn.example.com/v2/1.png", "https://cdn.example.com/v2/2.png", "https://cdn.example.com/v2/3.png", "https://cdn.example.com/v2/4.png"] } ], "mediaUrls": [ "https://cdn.example.com/v2/1.png", "https://cdn.example.com/v2/2.png", "https://cdn.example.com/v2/3.png", "https://cdn.example.com/v2/4.png", "https://cdn.example.com/v2/5.png" ], "scheduleDate": "2026-09-01", "scheduleTime": "10:00", "timezone": "Asia/Kolkata" } ``` - Only Instagram and Threads mix video into a carousel; everywhere else a set of several must be images only, and a video goes out on its own. - `create_post` refuses a set a target will not take **before** any credits are spent, naming the profile and the rule. `preflight_post` with the same `mediaUrls` reports the same thing plus a `mediaSetLimits` map, so check first when one carousel goes to several networks. - A profile can carry its own `mediaUrls` on its `targetAccounts` entry (or in `variants` as `{ "twitter": { "mediaUrls": [...] } }`), replacing the shared set - the way to give X and Bluesky a four-slide cut of a longer carousel. - Every slide is copied into PostMCP's own storage at write time, like a single attachment, so a host that expires the links later does not break the scheduled post. One slide failing at publish time fails that profile's post rather than publishing a shorter carousel; `publish_post_now` retries it. - `update_post` with `mediaUrls` replaces the whole set (add, remove or reorder slides); an empty array removes all media. Every post returned by `list_posts` / `get_post` carries `mediaUrls` alongside `mediaUrl`. ### Notes for clients - **Target profiles, not platforms.** `targetAccounts` sends only to the profiles named; `platforms` fans out to every connected profile on each platform. - **One post per profile.** `create_post` stores a separate post per targeted profile, so each can be edited, retried or cancelled on its own. Give per-profile copy through `targetAccounts[].content` or the `variants` map. - **Always pass `timezone`** when a wall-clock time matters. The backend defaults to UTC, so a 9:00 IST post scheduled without a zone goes out at 14:30 IST. - **Credits** are charged per profile delivered to (X/Twitter costs 5, others 1), plus a one-off 50-credit surcharge when the copy contains a link. `preflight_post` reports this before you commit. - **Analytics are read on request, never in the background.** `list_posts` and `get_connected_accounts` carry what the last reading stored, free. `get_post_analytics` (one post, every network it went to) and `get_profile_analytics` (one connected profile: followers, posts, views) with `refresh: true` read the network now for 1 credit each; without it, the stored reading is free. `engagements` is likes + comments + shares on every network, so it compares across platforms. A `null` means the network does not report that metric (Bluesky has no views), an `error` naming *reconnect* means the account predates the insights permission and its owner must reconnect it, and `unavailable: true` means the network never answers for that kind of post (LinkedIn personal profiles). --- ## ๐Ÿ’ป Client Integration Guides ### 1. Claude Desktop App (Stdio Mode) Add the configuration below to your Claude Desktop config file: - **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json` - **Windows**: `%APPDATA%\Claude\claude_desktop_config.json` ```json { "mcpServers": { "postmcpai": { "command": "npx", "args": ["-y", "@postmcpai/server"], "env": { "POSTMCPAI_API_KEY": "pmcp_sec_your_secret_api_key_here" } } } } ``` --- ### 2. Cursor IDE 1. Open **Cursor Settings** -> **Features** -> **MCP**. 2. Click **+ Add New MCP Server**. 3. Fill in the details: - **Name**: `postmcpai` - **Type**: `command` - **Command**: `npx -y @postmcpai/server` 4. Under **Environment Variables**, add: - `POSTMCPAI_API_KEY` = `pmcp_sec_your_secret_api_key_here` 5. Click **Save**. --- ### 3. Claude.ai & Remote Web Connectors (Streamable HTTP / SSE Mode) Host this server on any cloud service (Render, Railway, Fly.io, Vercel) or tunnel your local machine using `ngrok`. #### Launching in HTTP Mode: ```bash export POSTMCPAI_API_KEY="pmcp_sec_your_secret_api_key_here" export PORT=3000 npm run start:sse ``` #### Connecting to Claude.ai: 1. Provide your public MCP URL with your API key attached: `https://your-hosted-domain.com/mcp?apikey=pmcp_sec_your_secret_api_key_here` 2. Claude.ai will discover tool capabilities via `/mcp` and authenticate seamlessly. 3. That URL is all you need: the key is bound to the workspace it was issued from, so tools act on that workspace without being told. To point the same key at a *different* workspace, append `&projectId=YOUR_WORKSPACE_ID` (or send an `x-project-id` header); individual tool calls can still override either with `workspaceId`. --- ### 4. ChatGPT Custom GPTs (REST Actions) 1. When configuring a **Custom GPT Action**, specify your server URL (e.g. `https://your-hosted-domain.com`). 2. Import the OpenAPI schema directly from: `https://your-hosted-domain.com/openapi.json` 3. Set Authentication to **API Key** (Header Name: `Authorization` or `x-api-key`). --- ### 5. Programmatic Node.js Library Usage You can also use `@postmcpai/server` as a library in your own Node.js backends: ```js import { createServer, createExpressApp, makeBackendRequest } from "@postmcpai/server"; // Create a standalone MCP Server instance const mcpServer = createServer(() => process.env.POSTMCPAI_API_KEY); // Or create an Express app with all remote routes attached const app = createExpressApp(); app.listen(3000); ``` --- ## ๐Ÿงช Local Testing & Development ```bash # Clone the repository git clone https://github.com/postmcp/postmcp-mcp-server.git cd postmcp-mcp-server # Install dependencies npm install # Start in Stdio Mode npm start # Start in HTTP Mode with hot reload npm run dev ``` --- ## ๐Ÿ“„ License Distributed under the [MIT License](LICENSE). Copyright ยฉ 2026 PostMCP AI.