# Connect an AI agent (MCP)

Source: https://postninja.app/docs/mcp

PostNinja runs a remote MCP server. Connect your AI agent once and it can see your accounts, write and schedule posts to X, Instagram, Threads, Facebook, TikTok, YouTube and Pinterest, handle approvals, read analytics and answer comments and direct messages, with 19 tools.

## Server details

|  |  |
| --- | --- |
| Endpoint | `https://postninja.app/api/mcp` |
| Transport | Streamable HTTP, stateless (POST). Works with 2025 and 2026 MCP clients. |
| Sign-in | OAuth 2.1 (apps that sign in), or `Authorization: Bearer pn_…` with an API key |
| Tools | 19: see [MCP tools](https://postninja.app/docs/mcp/tools) |
| Rate limit | 120 requests a minute per API key, or per app and person |

## Sign in or use an API key

|  | Sign in (OAuth) | API key |
| --- | --- | --- |
| Used by | ChatGPT, Claude.ai and Claude Desktop connectors, the Gemini and Grok apps | Claude Code, Cursor, VS Code, Windsurf, Codex, Gemini CLI, n8n, SDKs and scripts |
| Setup | Paste the server URL, log in, click Allow | Create a key under **Settings → API**, add it as a header |
| Acts as | You, with your role in the workspace | An admin of the workspace |
| Access | What you allow: `posts:read`, or `posts:read` and `posts:write` | Read & write, or Read only; optionally limited to some profiles |
| Disconnect | **Settings → API → Connected apps** | Revoke the key under **Settings → API** |

## Permissions

- **`posts:read`** lets an app use the read tools: `list_accounts`, `get_workspace`, `list_profiles`, `list_templates`, `list_posts`, `get_post`, `get_queue`, `get_analytics`, `list_comments`, `list_messages`.
- **`posts:write`** is needed for tools that change something: `create_post`, `update_post`, `delete_post`, `approve_post`, `request_changes`, `update_queue`, `reply_to_comment`, `reply_to_message`, `upload_image`. An app that only has `posts:read` gets an error asking you to reconnect and allow changes.
- **Read only API keys** can call every read tool; tools that change something return an error saying the key can only read.
- **Keys limited to some profiles** (Pro and Agency) only see and post to those profiles' accounts, and can only change those profiles' queues.
- **Roles still apply to apps that sign in.** When approvals are on, a member's posts wait for approval (`awaiting_approval`), and only owners and admins can approve, ask for changes or change the queue. API keys act as admins, so their posts are scheduled straight away.
- Members whose access is limited to some profiles can't connect apps; ask a workspace admin for a limited API key instead.

## Connect your agent

Replace `pn_YOUR_KEY` with a key from **Settings → API**, or keep it in an environment variable (`POSTNINJA_API_KEY`) where the client supports that. After connecting, ask your agent "List my connected accounts" to check it works.

Jump to: [Claude Desktop](#setup-claude-desktop) · [ChatGPT](#setup-chatgpt) · [Claude Code](#setup-claude-code) · [Cursor](#setup-cursor) · [OpenAI Codex](#setup-codex) · [VS Code](#setup-vs-code) · [Windsurf](#setup-windsurf) · [Gemini](#setup-gemini) · [Gemini CLI](#setup-gemini-cli) · [Grok](#setup-grok) · [DeepSeek](#setup-deepseek) · [OpenClaw](#setup-openclaw) · [Cline](#setup-cline) · [Zed](#setup-zed) · [n8n](#setup-n8n) · [OpenAI Agents SDK](#setup-openai-agents-sdk) · [Claude Agent SDK](#setup-claude-agent-sdk) · [Goose](#setup-goose) · [Any MCP client](#setup-mcp-server)

### Claude Desktop

Add PostNinja as a custom connector and sign in; no API key needed. This works in the desktop app and on claude.ai. Prefer an API key? Use the config file instead (second box).

1. In Claude, open Customize → Connectors and click Add custom connector.
2. Name it PostNinja and paste the server URL below.
3. Click Add, then Connect. Log in to PostNinja and click Allow.

Server URL:

```
https://postninja.app/api/mcp
```

Or with an API key: claude_desktop_config.json (Settings → Developer → Edit Config) (macOS: ~/Library/Application Support/Claude/  ·  Windows: %APPDATA%\Claude\):

```
{
  "mcpServers": {
    "postninja": {
      "command": "npx",
      "args": ["mcp-remote", "https://postninja.app/api/mcp", "--header", "Authorization:${AUTH_HEADER}"],
      "env": { "AUTH_HEADER": "Bearer pn_YOUR_KEY" }
    }
  }
}
```

> **Note:** The API key route uses the mcp-remote bridge, which needs Node.js 18 or newer. Quit Claude completely and reopen it after editing the file.
>
> The header is written without a space (Authorization:${AUTH_HEADER}) on purpose: it avoids a known bug with spaces in arguments on Windows.

Example prompts and answers to common questions: [Claude Desktop guide](https://postninja.app/ai-agents/claude-desktop).

### ChatGPT

**Sign in, no API key.** In ChatGPT on the web, with developer mode on (Plus, Pro, Business, Enterprise or Edu):

1. Open Settings → Security and login and turn on Developer mode.
2. Go to chatgpt.com/plugins, click +, then Create MCP App.
3. Name it PostNinja and paste the server URL below as the connection.
4. Click Create, then Connect. Log in to PostNinja and click Allow.
5. In a chat, pick PostNinja from the + menu and ask away.

Server URL:

```
https://postninja.app/api/mcp
```

> **Note:** We're submitting PostNinja to ChatGPT's app directory. Once it's listed, you'll be able to add it in one click without developer mode.
>
> ChatGPT asks before each action that changes something, like scheduling a post.

Example prompts and answers to common questions: [ChatGPT guide](https://postninja.app/ai-agents/chatgpt).

### Claude Code

Run this in your terminal. Add --scope user to make it available in every project.

Terminal:

```
claude mcp add --transport http postninja https://postninja.app/api/mcp \
  --header "Authorization: Bearer pn_YOUR_KEY"
```

Or share it with your team in .mcp.json (reads the key from your environment) (.mcp.json in your project):

```
{
  "mcpServers": {
    "postninja": {
      "type": "http",
      "url": "https://postninja.app/api/mcp",
      "headers": { "Authorization": "Bearer ${POSTNINJA_API_KEY}" }
    }
  }
}
```

Example prompts and answers to common questions: [Claude Code guide](https://postninja.app/ai-agents/claude-code).

### Cursor

Add PostNinja to your MCP config, for this project or for every project.

mcp.json (.cursor/mcp.json (this project) or ~/.cursor/mcp.json (all projects)):

```
{
  "mcpServers": {
    "postninja": {
      "url": "https://postninja.app/api/mcp",
      "headers": { "Authorization": "Bearer ${env:POSTNINJA_API_KEY}" }
    }
  }
}
```

> **Note:** Set POSTNINJA_API_KEY in your environment, or paste the key in place of ${env:POSTNINJA_API_KEY}.

Example prompts and answers to common questions: [Cursor guide](https://postninja.app/ai-agents/cursor).

### OpenAI Codex

Set the key in your shell, then add the server with one command:

Terminal:

```
export POSTNINJA_API_KEY=pn_YOUR_KEY
codex mcp add postninja --url https://postninja.app/api/mcp --bearer-token-env-var POSTNINJA_API_KEY
```

Or in config.toml (~/.codex/config.toml):

```
[mcp_servers.postninja]
url = "https://postninja.app/api/mcp"
bearer_token_env_var = "POSTNINJA_API_KEY"
```

> **Note:** POSTNINJA_API_KEY must be set in the shell that starts Codex.

Example prompts and answers to common questions: [OpenAI Codex guide](https://postninja.app/ai-agents/codex).

### VS Code

Add this to your workspace's .vscode/mcp.json, or run MCP: Open User Configuration to make it global. VS Code asks for the key the first time.

.vscode/mcp.json (.vscode/mcp.json or your user mcp.json):

```
{
  "inputs": [
    { "type": "promptString", "id": "postninja-key", "description": "PostNinja API key (pn_…)", "password": true }
  ],
  "servers": {
    "postninja": {
      "type": "http",
      "url": "https://postninja.app/api/mcp",
      "headers": { "Authorization": "Bearer ${input:postninja-key}" }
    }
  }
}
```

> **Note:** The top-level key is "servers" in VS Code, not "mcpServers".

Example prompts and answers to common questions: [VS Code guide](https://postninja.app/ai-agents/vs-code).

### Windsurf

In the Cascade panel, open the … menu, go to MCPs and click the icon to open the MCP config file. Add PostNinja:

mcp_config.json:

```
{
  "mcpServers": {
    "postninja": {
      "serverUrl": "https://postninja.app/api/mcp",
      "headers": { "Authorization": "Bearer ${env:POSTNINJA_API_KEY}" }
    }
  }
}
```

> **Note:** Windsurf uses "serverUrl" (not "url") for remote servers. Windsurf was renamed Devin Desktop in 2026; the setup is the same.

Example prompts and answers to common questions: [Windsurf guide](https://postninja.app/ai-agents/windsurf).

### Gemini

In the Gemini app, add PostNinja as a custom app and sign in. Building your own agent? Pass PostNinja to the Gemini API as an MCP server instead (code below).

1. On gemini.google.com, open Settings → Connected Apps → Custom apps.
2. Click Add a custom app and paste the server URL from the first box below.
3. Log in to PostNinja when asked and click Allow.

Server URL for the Gemini app:

```
https://postninja.app/api/mcp
```

Interactions API (Antigravity agent):

```
curl "https://generativelanguage.googleapis.com/v1beta/interactions" \
  -H "x-goog-api-key: $GEMINI_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "agent": "antigravity-preview-09-2026",
    "environment": "remote",
    "input": "Schedule a post about our launch for tomorrow 9am on X and Threads",
    "tools": [{
      "type": "mcp_server",
      "name": "postninja",
      "url": "https://postninja.app/api/mcp",
      "headers": { "Authorization": "Bearer pn_YOUR_KEY" }
    }]
  }'
```

Python (google-genai + mcp):

```
import asyncio, os
from google import genai
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client

client = genai.Client()

async def main():
    headers = {"Authorization": f"Bearer {os.environ['POSTNINJA_API_KEY']}"}
    async with streamablehttp_client("https://postninja.app/api/mcp", headers=headers) as (read, write, _):
        async with ClientSession(read, write) as session:
            await session.initialize()
            response = await client.aio.models.generate_content(
                model="gemini-flash-latest",
                contents="Schedule a post about our launch for tomorrow 9am on X",
                config=genai.types.GenerateContentConfig(tools=[session]),
            )
            print(response.text)

asyncio.run(main())
```

> **Note:** MCP in the Interactions API is in preview, and the SDK's MCP support is experimental, so details may change.
>
> Custom apps in the Gemini app are for personal Google accounts, 18 and over, in the US, in English, with Keep Activity on.

Example prompts and answers to common questions: [Gemini guide](https://postninja.app/ai-agents/gemini).

### Gemini CLI

Run this once:

Terminal:

```
gemini mcp add --transport http --header "Authorization: Bearer pn_YOUR_KEY" postninja https://postninja.app/api/mcp
```

Or in settings.json (~/.gemini/settings.json (or .gemini/settings.json in a project)):

```
{
  "mcpServers": {
    "postninja": {
      "httpUrl": "https://postninja.app/api/mcp",
      "headers": { "Authorization": "Bearer pn_YOUR_KEY" }
    }
  }
}
```

> **Note:** Use "httpUrl" (not "url", which means the older SSE transport). Gemini CLI doesn't expand environment variables in headers, so paste the key itself.

Example prompts and answers to common questions: [Gemini CLI guide](https://postninja.app/ai-agents/gemini-cli).

### Grok

In the Grok app, add PostNinja as a custom connector and sign in. In Grok Bot, add it as a custom MCP server with an API key. From code, pass it to the xAI API.

1. On grok.com, go to Connectors → New Connector → Custom.
2. Paste the server URL from the first box below.
3. Log in to PostNinja when asked and click Allow.

Server URL for the Grok app:

```
https://postninja.app/api/mcp
```

Grok Bot: custom MCP server:

```
Name:     postninja
URL:      https://postninja.app/api/mcp
Header:   Authorization: Bearer pn_YOUR_KEY
```

xAI API (Responses API, remote MCP tool):

```
curl https://api.x.ai/v1/responses \
  -H "Authorization: Bearer $XAI_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "model": "grok-4.7",
    "input": "List my scheduled posts for this week",
    "tools": [{
      "type": "mcp",
      "server_url": "https://postninja.app/api/mcp",
      "server_label": "postninja",
      "authorization": "pn_YOUR_KEY"
    }]
  }'
```

> **Note:** Grok Bot is in beta, so its plugin settings may move. Servers you've set up in the Cursor editor aren't carried over to Grok Bot automatically.
>
> The xAI API runs the MCP calls on xAI's servers, so your PostNinja address must be public.

Example prompts and answers to common questions: [Grok guide](https://postninja.app/ai-agents/grok).

### DeepSeek

For example, run Claude Code on DeepSeek's Anthropic-compatible API, then add PostNinja:

Terminal: Claude Code on DeepSeek:

```
export ANTHROPIC_BASE_URL=https://api.deepseek.com/anthropic
export ANTHROPIC_AUTH_TOKEN=YOUR_DEEPSEEK_KEY
export ANTHROPIC_MODEL=deepseek-flash

claude mcp add --transport http postninja https://postninja.app/api/mcp \
  --header "Authorization: Bearer pn_YOUR_KEY"
```

> **Note:** The DeepSeek chat app can't connect to MCP servers. Use one of the agents DeepSeek lists under Agent integrations in its API docs.
>
> Prefer a chat-app agent? OpenClaw also runs DeepSeek models and connects to PostNinja; see the OpenClaw guide.

Example prompts and answers to common questions: [DeepSeek guide](https://postninja.app/ai-agents/deepseek).

### OpenClaw

Add PostNinja to OpenClaw from the CLI, then check the connection:

Terminal:

```
openclaw mcp set postninja '{"url":"https://postninja.app/api/mcp","transport":"streamable-http","headers":{"Authorization":"Bearer pn_YOUR_KEY"}}'
openclaw mcp probe postninja
```

Or in openclaw.json (~/.openclaw/openclaw.json):

```
{
  "mcp": {
    "servers": {
      "postninja": {
        "url": "https://postninja.app/api/mcp",
        "transport": "streamable-http",
        "headers": { "Authorization": "Bearer pn_YOUR_KEY" }
      }
    }
  }
}
```

> **Note:** Use OpenClaw 2026.5.12 or later: older versions could forward custom headers, like your API key, across a redirect.
>
> openclaw mcp doctor warns about keys written straight into the config; OpenClaw's secret settings keep it out of the file.

Example prompts and answers to common questions: [OpenClaw guide](https://postninja.app/ai-agents/openclaw).

### Cline

Click the MCP Servers icon in Cline, choose Configure → Configure MCP Servers, and add:

cline_mcp_settings.json:

```
{
  "mcpServers": {
    "postninja": {
      "type": "streamableHttp",
      "url": "https://postninja.app/api/mcp",
      "headers": { "Authorization": "Bearer pn_YOUR_KEY" },
      "disabled": false
    }
  }
}
```

> **Note:** Set "type": "streamableHttp" explicitly. The Remote Servers form has no headers field, so edit the JSON.

Example prompts and answers to common questions: [Cline guide](https://postninja.app/ai-agents/cline).

### Zed

Open Settings → AI → MCP Servers → Add Remote Server, or add this to settings.json:

settings.json:

```
{
  "context_servers": {
    "postninja": {
      "url": "https://postninja.app/api/mcp",
      "headers": { "Authorization": "Bearer pn_YOUR_KEY" }
    }
  }
}
```

> **Note:** With the Authorization header, Zed connects with your API key. Leave it out and Zed can sign in with OAuth instead, if your Zed version supports it.

Example prompts and answers to common questions: [Zed guide](https://postninja.app/ai-agents/zed).

### n8n

In your workflow, add an AI Agent node, then add the MCP Client Tool sub-node with these settings:

MCP Client Tool settings:

```
Endpoint:          https://postninja.app/api/mcp
Server Transport:  HTTP Streamable
Authentication:    Bearer Auth
  Token:           pn_YOUR_KEY
Tools to Include:  All
```

> **Note:** Use a recent n8n version: older MCP Client Tool versions only spoke SSE. You can also call the REST API from an HTTP Request node.

Example prompts and answers to common questions: [n8n guide](https://postninja.app/ai-agents/n8n).

### OpenAI Agents SDK

Install openai-agents, set POSTNINJA_API_KEY, and connect over Streamable HTTP:

Python:

```
import os
from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp

async with MCPServerStreamableHttp(
    name="PostNinja",
    params={
        "url": "https://postninja.app/api/mcp",
        "headers": {"Authorization": f"Bearer {os.environ['POSTNINJA_API_KEY']}"},
    },
    cache_tools_list=True,
) as postninja:
    agent = Agent(name="Social", instructions="You schedule social posts.", mcp_servers=[postninja])
    result = await Runner.run(agent, "Schedule a post about our launch for tomorrow 9am on X.")
```

Example prompts and answers to common questions: [OpenAI Agents SDK guide](https://postninja.app/ai-agents/openai-agents-sdk).

### Claude Agent SDK

Add the server to your query options and allow its tools:

TypeScript:

```
import { query } from "@anthropic-ai/claude-agent-sdk";

for await (const message of query({
  prompt: "Schedule tomorrow's launch post on X and Threads at 9am",
  options: {
    mcpServers: {
      postninja: {
        type: "http",
        url: "https://postninja.app/api/mcp",
        headers: { Authorization: `Bearer ${process.env.POSTNINJA_API_KEY}` },
      },
    },
    allowedTools: ["mcp__postninja__*"],
  },
})) {
  if (message.type === "result") console.log(message.result);
}
```

> **Note:** Without allowedTools: ["mcp__postninja__*"], the agent sees the tools but isn't allowed to call them.

Example prompts and answers to common questions: [Claude Agent SDK guide](https://postninja.app/ai-agents/claude-agent-sdk).

### Goose

Run goose configure → Add Extension → Remote Extension (Streamable HTTP), or add it to your config file:

config.yaml (~/.config/goose/config.yaml (Windows: %APPDATA%\Block\goose\config\config.yaml)):

```
extensions:
  postninja:
    type: streamable_http
    name: postninja
    enabled: true
    uri: https://postninja.app/api/mcp
    headers:
      Authorization: "Bearer pn_YOUR_KEY"
    timeout: 300
```

> **Note:** Restart Goose after editing the file.

Example prompts and answers to common questions: [Goose guide](https://postninja.app/ai-agents/goose).

### Any MCP client

Point your client at the endpoint below. Apps that sign in with OAuth only need the URL; everything else sends an API key as a Bearer token:

Connection details:

```
Endpoint:   https://postninja.app/api/mcp
Transport:  Streamable HTTP (stateless)
Auth:       OAuth 2.1 sign-in, or Authorization: Bearer pn_YOUR_KEY
```

Typical JSON config:

```
{
  "mcpServers": {
    "postninja": {
      "type": "http",
      "url": "https://postninja.app/api/mcp",
      "headers": { "Authorization": "Bearer pn_YOUR_KEY" }
    }
  }
}
```

> **Note:** Apps that sign in (ChatGPT, Claude.ai, the Gemini and Grok apps) use OAuth 2.1 instead of a key: discovery starts at /.well-known/oauth-protected-resource, with dynamic client registration, client ID metadata documents and PKCE.

Example prompts and answers to common questions: [Any MCP client guide](https://postninja.app/ai-agents/mcp-server).

### Not available yet

- **OpenAI Dots**: Dots reach other tools through ChatGPT's apps. PostNinja already works as a ChatGPT app you add yourself; Dots will pick it up once it's listed in ChatGPT's app directory, which we've applied for. Until then, use it in ChatGPT directly.

## OAuth details for client developers

Building your own MCP client? PostNinja follows the MCP authorization spec. A request without a valid token gets `401` with a `WWW-Authenticate: Bearer resource_metadata="…"` header pointing at the protected resource metadata. From there:

| What | Where |
| --- | --- |
| Protected resource metadata (RFC 9728) | `https://postninja.app/.well-known/oauth-protected-resource` (also `…/oauth-protected-resource/api/mcp`) |
| Authorization server metadata (RFC 8414) | `https://postninja.app/.well-known/oauth-authorization-server` (also `…/oauth-authorization-server/api/auth`) |
| OpenID configuration | `https://postninja.app/.well-known/openid-configuration` |
| Issuer | `https://postninja.app/api/auth` |
| Resource (token audience) | `https://postninja.app/api/mcp` |
| Scopes | `posts:read`, `posts:write`, plus `offline_access` for refresh tokens |
| Client registration | Dynamic client registration (RFC 7591), or a client ID metadata document (your client_id is an https URL) |
| Flow | Authorization code with PKCE. The person logs in and approves your app on a consent page. |

Access tokens are JWTs tied to one workspace. They stop working as soon as the person disconnects the app, leaves the workspace or is suspended. A tool that needs more access answers with an error whose `_meta` carries a `WWW-Authenticate` challenge with `error="insufficient_scope"`.

## What the server tells your agent

MCP clients receive these instructions when they connect, so your agent already knows the basics:

Server instructions:

```text
PostNinja schedules posts to X, Instagram, Threads, Facebook Pages, TikTok, YouTube Shorts and Pinterest. Call list_accounts first to get account ids. Instagram, TikTok, YouTube and Pinterest posts need media (upload_image); YouTube needs exactly one video. Use platformContent to give a platform its own text, e.g. a shorter version for X (280 characters) or hashtags for Instagram. For an X thread, put the follow-up posts in xThread (each costs credits). For an Instagram Story, set instagramFormat to "story" and attach exactly one photo or video. For TikTok, ask the user who can see the post (tiktok.privacy) and whether it's commercial content (tiktok.yourBrand / tiktok.brandedContent). Times are ISO 8601 with a time zone. get_workspace gives the brand voice to write in and whether members' posts need approval; accounts may belong to profiles (list_profiles, e.g. one per client or brand), each with its own brand voice, which wins for that profile's accounts. Posts with status awaiting_approval wait for an owner or admin: approve_post schedules them, request_changes sends them back to the author with a note. list_templates has ready-made posts with {blanks} you can fill in before create_post. Set queue: true on create_post to use the next free slot of the posting queue (get_queue shows the weekly slots) instead of a time. firstComment adds a first comment under the post; youtubeTitle sets a YouTube video's title. get_analytics reports impressions, engagements, follower growth and top posts for a date range; list_comments and reply_to_comment read and answer comments on Instagram, Facebook, Threads and YouTube posts; list_messages and reply_to_message read and answer Instagram and Facebook Page direct messages (replies only within 24 hours of the person's last message).
```

## Errors

- Mistakes the agent can fix (text too long, a missing photo, a time in the past, not enough credits) come back as tool results with `isError: true` and a message written for people. Good agents read it and try again.
- A missing, revoked or expired key or token gets HTTP `401`. Too many requests get HTTP `429` with a `Retry-After` header.
