MCP server for letmepost.dev. Publish across 8 social platforms from any agent.
Runs on your machine from its package; the steps are below.
At most: moves money, runs code or commands, deletes data, or sends messages for you.
Odel charges nothing per call. The publisher may charge for its own service.
Runs locally over stdio
This server isn't hosted — your MCP client launches it from a package registry. Use one of the commands below, or drop the config into your client (e.g. Claude Desktop).
Run
npx -y @letmepost/mcp
MCP client config
{
"mcpServers": {
"letmepost": {
"command": "npx",
"args": [
"-y",
"@letmepost/mcp"
]
}
}
}An open-source social media publishing API that fails loudly. One
POST /v1/postsacross every platform. Stable error codes, the rule that failed, the raw platform body, and a remediation — on every failure. Never an empty{ body: {} }. No per-profile tax.
An alternative to: Ayrshare, Postiz, Buffer, Hootsuite, Hypefury.
Website · Docs · Quickstart · Dashboard · API Reference
Supports: Bluesky · LinkedIn · X · Threads · Instagram · Facebook · Pinterest · YouTube
Idempotency-Key. Retries are safe — no double-posting loops when a worker restarts mid-publish.Request:
curl -X POST https://api.letmepost.dev/v1/posts \
-H "Authorization: Bearer lmp_live_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"targets": [{ "accountId": "acc_…" }],
"text": "Shipping letmepost.dev"
}'
Failure:
{
"error": {
"code": "preflight_failed",
"rule": "bluesky.text.max_graphemes",
"platform": "bluesky",
"platformVersion": "atproto-2026-04",
"message": "Post text is 312 graphemes; Bluesky allows at most 300.",
"remediation": "Shorten the post to 300 graphemes or fewer.",
"docUrl": "https://docs.letmepost.dev/errors/preflight_failed",
"ruleUrl": "https://docs.letmepost.dev/preflight/bluesky-text-max_graphemes",
"requestId": "req_01HY6X4AWBJM2K9F2PTQMRD9JQ"
}
}
Same shape across every platform. Same shape on every error class — preflight_failed, platform_auth_failed, platform_rejected, platform_unavailable, rate_limited. The dashboard renders these directly. Your error handler doesn't need a per-platform branch.
Four things developers hit every week with incumbent social-media APIs:
{}. Postiz #1321 was an infinite double-post loop; Ayrshare's error 138 masks half a dozen distinct upstream causes.forbidden, Threads's OAuthException 2207052, Instagram Reels rejecting Google Drive URLs — all catchable client-side, all unhandled by the incumbents.letmepost.dev addresses all four, in one API.
| Platform | Status | Notes |
|---|---|---|
| Bluesky | live | App-password auth, video via dedicated transcoder, 300-grapheme preflight |
| X / Twitter | live | OAuth 2.0 PKCE, 280 graphemes (t.co-aware), 4 images OR 1 video, threads & quote tweets. Per-account launch cap (TWITTER_LAUNCH_CAP_PER_ACCOUNT, default 50) as a backstop |
| live | v5 API, image + video pins, board-required preflight | |
| live | Versioned REST, 3,000-grapheme commentary. Personal posting via w_member_social; org posts still MDP-gated | |
| Threads | live | Standalone OAuth at threads.net, 500-char, 2–20 mixed-media carousels |
| live | Meta Graph, Reels + carousels, FB Login fan-out | |
| Facebook Pages | live | Meta Graph, single video OR 10 photos |
| TikTok | live | Content Posting API v2, OAuth 2.0 PKCE, push_by_file upload to the creator's inbox — they confirm the publish in the app |
| YouTube | planned | Data API v3, CASA-gated for production verification |
live = production-ready end-to-end. trial = connect works but rate-limited or sandboxed. pending = approval in flight; the publisher is built and ships the moment review clears. planned = built into the schema, publisher pending.
Every supported platform is live today. PLATFORM_STATE in packages/schemas/src/platform-state.ts is the source of truth the API enforces, and the marketing site reads it directly — this table is prose and should be checked against it.
TikTok ships the Upload track: a post uploads to the creator's TikTok inbox and they tap publish in the app. Direct Post (video.publish), which publishes without that tap, has cleared TikTok's audit but is a separate endpoint and is not implemented yet. Reddit, Telegram, Discord, Snapchat, Google Business, and WhatsApp are deliberately cut from v1. Reasoning in PRODUCT.md.
# 1. Sign up + grab a key
open https://dashboard.letmepost.dev
# 2. Connect Bluesky (or any other platform via OAuth)
curl -X POST https://api.letmepost.dev/v1/accounts/connect/bluesky \
-H "Authorization: Bearer lmp_live_…"
# 3. Publish
curl -X POST https://api.letmepost.dev/v1/posts \
-H "Authorization: Bearer lmp_live_…" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "targets": [{ "accountId": "acc_…" }], "text": "Hello, world" }'
90-second walkthrough at docs.letmepost.dev/quickstart.
We build in the open. Outside contributions are how this becomes a primitive for every social platform, not just the eight we shipped first.
Pick a starting point:
CONTRIBUTING.md §3 is the canonical recipe — most additions are < 500 LOC.Before you open a PR, skim CONTRIBUTING.md — it documents the layering rules, the error contract, the pre-publish gate pattern, the testing approach (MSW + transaction rollback), and the commit style we follow. It's how new code reviews itself.
CODE_OF_CONDUCT.md · SECURITY.md (vulnerability disclosure) · PRODUCT.md (the seven product principles) · TECH.md (stack contract) · plan.md (v1 status + pre-launch checklist).
Apache 2.0 from day 0. The same code that runs api.letmepost.dev runs on your own infra — no feature gate, no open-core trick.
git clone https://github.com/rosekamallove/letmepost.dev
cd letmepost.dev
pnpm install
cp apps/api/.env.example apps/api/.env # fill in your platform OAuth credentials
docker compose -f docker-compose.dev.yml up -d # spins up Postgres + Redis only
pnpm --filter @letmepost/api db:migrate
pnpm dev # API + dashboard in watch mode
The dev compose file ships Postgres + Redis. The API, worker, and dashboard run via pnpm — see docs.letmepost.dev/self-host/quick-start for the full walkthrough and self-host/deploying for production patterns. BYO Postgres (or Neon), BYO Redis (or Upstash), BYO platform credentials. Hosted is permanently optional.
Prerequisites: Node >=24, pnpm 10.33.0+ (corepack enable).
pnpm install
pnpm dev # API + web + dashboard in watch mode (turbo)
pnpm test # vitest across the workspace
pnpm typecheck
pnpm build
Individual apps:
pnpm --filter @letmepost/api dev # API → http://localhost:3000
pnpm --filter @letmepost/dashboard dev # Dashboard → http://localhost:3001
pnpm --filter @letmepost/web dev # Landing → http://localhost:4321
apps/
api/ # Hono HTTP API + BullMQ workers — the core product
dashboard/ # Next.js operator surface (dashboard.letmepost.dev)
web/ # Astro landing site (letmepost.dev)
packages/
schemas/ # Zod — single source of truth for validation, types, OpenAPI
sdk-ts/ # @letmepost/sdk — official TypeScript client (npm)
config-tsconfig/
Landing as the stack grows: packages/openapi/ (generated 3.1 spec), plus sibling repos letmepost/sdk-python and letmepost/sdk-go auto-generated from the spec. See TECH.md for the full target tree.
Hono · BullMQ · Drizzle · PostgreSQL (Neon) · Redis (Upstash) · better-auth · Zod · Next.js · Astro · TypeScript · Turborepo · pnpm.
API contract details in TECH.md. Product principles in PRODUCT.md. Roadmap in plan.md.
Apache 2.0. Permissive by design — you can build a commercial product on top of letmepost.dev without copyleft contagion. Same code in hosted and self-host.