Skip to main content
Glama
README.md
# mcp-blotato-higgsfield

An MCP server that combines [Higgsfield](https://higgsfield.ai/mcp) (AI image/video
generation) with **direct, native posting to X, YouTube, Instagram, Facebook, and
Bluesky** — no Blotato or other paid middleman required. A composite
`generate_and_post` tool chains the two halves together.

> This project originally aggregated Blotato's hosted MCP for social publishing.
> Blotato has no free tier — its API/MCP access requires a paid plan starting at
> $29/mo — so publishing was rebuilt to talk to each platform's own free/native API
> directly instead. See "Why not Blotato" below.

## What's in here

- **Higgsfield** — connected as an MCP *client* to Higgsfield's official hosted MCP
  server (it ships one already; we don't reimplement its API). Exposed as
  `higgsfield__<tool>`.
- **Direct social publishers** — hand-written, native API integrations for:
  | Platform | API | Auth |
  |---|---|---|
  | X (Twitter) | REST v1.1 (media) + v2 (tweet) | OAuth 1.0a user-context app |
  | YouTube | YouTube Data API v3 | Google OAuth2 refresh token |
  | Instagram | Meta Graph API (Content Publishing) | Long-lived token + Business/Creator account |
  | Facebook | Meta Graph API | Page access token |
  | Bluesky | AT Protocol | App password (no OAuth, no app review) |

  Each is exposed as `<platform>__post` and only appears in the tool list once
  its credentials are set.
- **`generate_and_post`** — generate media from a prompt via Higgsfield, then fan
  out and publish it to whichever of the above platforms you ask for, in one call.

## Why not Blotato

Blotato is a paid unified API/MCP over these same platforms. Wrapping it yourself
doesn't avoid the cost — its API key is gated the same way whether you call it
through Blotato's hosted MCP or your own client. Going direct means:
- **Free** for X (limited tier), YouTube (free daily quota), and Bluesky (fully open).
- Meta (Instagram/Facebook) is free but requires app review for anything beyond
  your own test accounts.
- You take on what Blotato was abstracting: a separate developer app + OAuth flow
  per platform, and keeping up with each API's own quirks.

## Setup

```bash
npm install
cp .env.example .env   # fill in credentials for the platforms you want, see below
npm run build
```

You don't need every platform configured — the server only advertises tools for
what's set. Start with Bluesky (easiest, no approval process) if you want to try
it end-to-end quickly.

### Higgsfield
Higgsfield's hosted MCP is OAuth-only. Authorize once via `mcp-remote`, which opens
a browser and caches the token locally:
```bash
npx -y mcp-remote https://mcp.higgsfield.ai/mcp
```
Then set in `.env`:
```
HIGGSFIELD_TRANSPORT=stdio
HIGGSFIELD_MCP_COMMAND=npx
HIGGSFIELD_MCP_ARGS=-y mcp-remote https://mcp.higgsfield.ai/mcp
```

### Bluesky (start here — easiest)
1. In the Bluesky app: Settings → App Passwords → create one.
2. Set `BLUESKY_IDENTIFIER` (your handle, e.g. `you.bsky.social`) and
   `BLUESKY_APP_PASSWORD`.

No developer account, no app review, no OAuth. Works immediately.

### X (Twitter)
1. Create a project/app at [developer.x.com](https://developer.x.com).
2. Under the app's "Keys and tokens", generate: API Key/Secret and an
   **Access Token & Secret with Read and Write permissions** (regenerate the
   access token after changing permissions, or it stays read-only).
3. Set `X_API_KEY`, `X_API_SECRET`, `X_ACCESS_TOKEN`, `X_ACCESS_TOKEN_SECRET`.

⚠️ X's free-tier limits (post volume, whether media upload is included) have
changed more than once — check your app's current tier in the developer portal.

### YouTube
1. In [Google Cloud Console](https://console.cloud.google.com), create a project,
   enable the **YouTube Data API v3**, and create an OAuth2 **Desktop app** client
   ID (gives you a client ID + secret).
2. Get a refresh token once via the OAuth consent flow with scope
   `https://www.googleapis.com/auth/youtube.upload` (e.g. using
   [Google's OAuth Playground](https://developers.google.com/oauthplayground)
   with your own client ID/secret plugged in under its settings gear icon).
3. Set `YOUTUBE_CLIENT_ID`, `YOUTUBE_CLIENT_SECRET`, `YOUTUBE_REFRESH_TOKEN`.

Free tier: 10,000 quota units/day; one video upload costs ~1,600 units (~6
uploads/day).

### Instagram
1. Requires an Instagram **Business or Creator** account connected to a Facebook Page.
2. Create a Meta app at [developers.facebook.com](https://developers.facebook.com),
   add the Instagram + Facebook Login products.
3. Generate a long-lived access token with the `instagram_content_publish`
   permission (works immediately for your own account in dev mode; posting on
   behalf of others needs Meta App Review).
4. Set `IG_ACCESS_TOKEN` and `IG_USER_ID` (your Instagram Business Account ID,
   fetched via `GET /me/accounts` → `GET /{page-id}?fields=instagram_business_account`).

### Facebook
1. Same Meta app as above.
2. Generate a **Page access token** (not a user token) with `pages_manage_posts`.
3. Set `FB_PAGE_ACCESS_TOKEN` and `FB_PAGE_ID`.

## Running

```bash
npm run build && npm start
# or, for local development without a build step:
npm run dev
```

This starts an MCP server on **stdio**.

### Add it to Claude Code / Claude Desktop

```json
{
  "mcpServers": {
    "blotato-higgsfield": {
      "command": "node",
      "args": ["/absolute/path/to/mcp_blotato_higgsfield/dist/index.js"],
      "env": {
        "HIGGSFIELD_TRANSPORT": "stdio",
        "HIGGSFIELD_MCP_COMMAND": "npx",
        "HIGGSFIELD_MCP_ARGS": "-y mcp-remote https://mcp.higgsfield.ai/mcp",
        "BLUESKY_IDENTIFIER": "you.bsky.social",
        "BLUESKY_APP_PASSWORD": "...",
        "X_API_KEY": "...", "X_API_SECRET": "...",
        "X_ACCESS_TOKEN": "...", "X_ACCESS_TOKEN_SECRET": "..."
      }
    }
  }
}
```

Or with the Claude Code CLI:
```bash
claude mcp add blotato-higgsfield -- node /absolute/path/to/dist/index.js
```

## How it works

```
src/
  config.ts              # env-driven config for the Higgsfield upstream connection
  clients/
    upstream.ts            # generic MCP client wrapper (http or stdio transport)
  publishers/
    types.ts                # Publisher interface + fetchMediaBytes helper
    x.ts                     # X (Twitter) via twitter-api-v2
    youtube.ts                 # YouTube Data API v3, multipart upload
    instagram.ts                 # Meta Graph API content-publishing flow
    facebook.ts                    # Meta Graph API page posting
    bluesky.ts                      # AT Protocol via @atproto/api
    metaGraph.ts                     # shared helpers for instagram.ts/facebook.ts
    index.ts                         # publisher registry
  tools/
    passthrough.ts          # namespaces + re-lists Higgsfield's MCP tools
    publishers.ts             # exposes each configured publisher as <platform>__post
    composite.ts                # generate_and_post: Higgsfield -> fan out to publishers
  index.ts                # our own MCP server (stdio), wires it all together
```

- On `tools/list`, we return Higgsfield's upstream tools (prefixed `higgsfield__`),
  one `<platform>__post` tool per *configured* publisher, and `generate_and_post`
  when Higgsfield + at least one publisher are configured.
- On `tools/call`, a `higgsfield__*` name routes to the upstream MCP client; a
  `<platform>__post` name routes to that publisher; `generate_and_post` runs the
  composite flow.
- We don't hardcode Higgsfield's exact tool/field names (they can change) —
  `generate_and_post` **discovers** the right Higgsfield tool at runtime by
  matching tool names (e.g. `/generate.*video/i`) and guesses common field names
  against its real input schema. Pass `higgsfield_args` to override explicitly if
  the guess is wrong. Publisher-side fields (media URL, caption, platform-specific
  options) are handled directly by each publisher, since we wrote those ourselves
  against each platform's real, stable API — no guessing needed there.

## Known limitations

- Media handling: X, YouTube, and Bluesky's upload APIs take raw bytes, so those
  publishers download the generated media first. Instagram/Facebook's Graph API
  accepts a remote URL directly, so no download happens there.
- YouTube upload uses a single multipart request rather than the full resumable-
  upload protocol — fine for short AI-generated clips, but large files should use
  resumable upload instead.
- Higgsfield's tool discovery is heuristic (see above) since its schema isn't
  hardcoded here.
- None of the publishers implement an in-process OAuth *authorization* flow —
  you obtain each platform's token/credentials out-of-band (see Setup above) and
  the server just uses them.