npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ihateposting-mcp

v0.12.9

Published

MCP server for iHatePosting — draft, schedule, and inspect social posts from Claude, Cursor, or any MCP client.

Readme

ihateposting-mcp

npm

MCP server for iHatePosting — draft, schedule, and inspect social posts from Claude, Cursor, ChatGPT, or any MCP client.

Write once, publish everywhere: iHatePosting cross-posts to Bluesky, X, LinkedIn, Facebook, Instagram, Threads, Mastodon, Telegram, Discord, Tumblr, Slack, Pinterest, TikTok and YouTube.

Sign in, or use an API key

Remote clients that support MCP sign-in (OAuth) need no key at all: point them at https://ihateposting.com/mcp/oauth, sign in to iHatePosting in the browser and press Allow (see Remote clients below).

This package, the local stdio server, takes an API key instead: iHatePosting → Settings → Developers → create a key (pk_live_…). Treat it like a password: it can post on your behalf.

Tools

| Tool | What it does | |---|---| | whoami | Which iHatePosting account this key belongs to — the login, not a social handle | | get_platform_rules | Each network's real limits: characters, media, aspect ratios, video specs | | validate_post | Check a draft against those rules before spending a post | | create_post | Create a post — publish now, schedule, or save as a draft | | list_posts | Your posts, newest first, with per-platform status and live URLs — 50 a page by default, up to 200, with filters | | list_accounts | Your connected social accounts and their health | | list_pinterest_boards | The Pinterest boards you can pin to, with the boardId a pin requires | | get_analytics | How the posts did — per channel, with the top ones named | | reschedule_post | Move a scheduled post to a different date and time | | list_media | The images and videos already in the library, with the ids create_post wants | | upload_media | Put a new image or video in the library from a URL (or small base64) and get an id back | | open_upload_widget | In clients that show MCP app widgets, a file picker in the conversation for a file on your computer | | get_post | One post in full: per-platform copy, options, media, per-send outcome | | update_post | Rewrite a draft or scheduled post — replaces what you send | | retry_post | Send a failed post again; a send that published is left alone | | delete_post | Remove a post. This does NOT unpublish anything already live |

Apart from open_upload_widget, which is registered first, they are declared in that order because it is the order a model reads them, and it is the working sequence: know whose account this is, see what the networks allow, check the draft, then spend the post — and only then manage what is already there.

Use it

Claude Desktop / Claude Code / Cursor (local, stdio)

Requires Node.js 20 or later (since 0.10.0).

{
  "mcpServers": {
    "ihateposting": {
      "command": "npx",
      "args": ["-y", "ihateposting-mcp"],
      "env": { "IHATEPOSTING_API_KEY": "pk_live_…" }
    }
  }
}

Remote clients (hosted, HTTP)

Clients that can't run a local process use the hosted server instead.

Sign in — no key (preferred). If the client supports MCP sign-in (OAuth), add this address and nothing else:

https://ihateposting.com/mcp/oauth

The client opens your browser; sign in to iHatePosting and press Allow. It discovers the sign-in from the server's own metadata and registers itself, so there is no client id or secret to enter. Claude's connector directory, Cursor and Grok Build connect this way. Take a sign-in back under Settings → Developers → Connected apps.

With an API key, for clients that cannot sign in, use https://ihateposting.com/api/mcp and pick whichever your client supports:

| Client can send | Use | |---|---| | A custom header | Authorization: Bearer pk_live_… (preferred) | | Only a URL | https://ihateposting.com/api/mcp?key=pk_live_… |

⚠️ A key in a URL can end up in browser history, proxy logs and Referer headers. Prefer the header when your client allows it, and rotate the key (Settings → Developers) if a URL containing it gets shared.

Environment

| Variable | Default | Notes | |---|---|---| | IHATEPOSTING_API_KEY | — | Required (stdio). Your pk_live_… key. | | IHATEPOSTING_API_URL | https://ihateposting.com | Override for self-hosted/staging. | | IHATEPOSTING_CLIENT | — | Optional. Which tool is running this, e.g. claude-desktop; calls are recorded with that name. |

Notes

  • Pinterest also requires a BOARD. list_pinterest_boards is the only way to learn one — a board id is an opaque number, so without it a pin cannot be created at all.
  • Instagram, Pinterest, TikTok and YouTube all require media. upload_media fetches a file from a URL (the reliable way, and the only one for video) or takes small files as base64, and hands back an id for create_post. A file that exists only on your computer is better uploaded with open_upload_widget where the client supports it, or in the composer, and found afterwards with list_media.
  • A post that goes to several platforms is saved as a group, one row per platform. update_post can rewrite a row of a group when you send back that row's own accountIds; adding or swapping an account is refused with a sentence saying so, because it would publish some platforms twice. To change which platforms a group posts to, delete it and create it again.
  • A draft is different: it stays ONE row for every platform it is aimed at until it is scheduled. Deleting it removes every platform (the reply's platformsRemoved names them), and reschedule_post refuses it — schedule a draft with update_post and action schedule, which checks each platform and splits it into one row per platform.
  • delete_post removes iHatePosting's record. A post already live on a network stays live, which is why it asks for force before touching one.
  • Every call is scoped to the key's owner; the server holds no state of its own.

MIT © iHatePosting