socialmag

MCP server that posts to Threads / Facebook / Instagram / YouTube / TikTok by driving abang's already-logged-in Brave over CDP. No API keys, no OAuth.

Built to PROPOSAL.md (2026-09-10). Nothing here has been run against a live platform yet — every step whose selector or timing needs a real page is marked # TODO: VALIDATE LIVE.

Run

cd /mnt/playground/Playground_V2
python3 -m socialmag            # stdio MCP server, logs to stderr

⚠️ Must run as a package. python3 socialmag/server.py puts the package dir first on sys.path, where selectors.py and queue.py shadow the stdlib modules asyncio imports — asyncio then dies at import with module 'selectors' has no attribute 'SelectSelector'. Run -m.

Brave must be listening on http://127.0.0.1:9222. Override with SOCIALMAG_CDP_ENDPOINT.

Requires websockets (17.1 present on this box). Everything else is stdlib.

Tools

Tool Args Returns
create_post platforms[], text, media, visibility {platform: {status, detail}}
check_login platforms[] (default all) {platform: true/false/"error: ..."}
get_status — last run's results + queue history

text is required for threads/fb/yt and used as the caption for ig/tiktok. media must be an absolute path to an existing file; it is required for ig/tiktok, optional for threads/fb, and rejected for yt.

Layout

server.py       MCP stdio JSON-RPC loop + the 3 tools
cdp.py          CDP client: CDPClient (browser) + Tab (page verbs)
queue.py        PostQueue — strictly sequential, one tab at a time
selectors.py    every platform selector, one file
platforms/      one module per platform: post() + check_login()

Each platform module exposes:

async def post(cdp_client, text, media_path, options) -> {"status": ..., "detail": ...}
async def check_login(cdp_client) -> bool

post opens its own tab, drives the flow, closes the tab. A failure inside the flow is caught by platforms.guard, which screenshots the page as it actually looked into /tmp/socialmag/ and puts the path in the result.

House rules honoured

  • No silent fallbacks. Missing media, missing text, an unknown platform, a selector that never appears, a CDP call that errors — all raise with the reason. Nothing picks a default and carries on.
  • Unmapped flows refuse rather than guess: Facebook visibility switching and YouTube media both raise, because clicking through an unmapped dialog outward-facing is worse than a stop.
  • Human-like delays (random.uniform) between every discrete action, longer on TikTok.

Not built (out of the brief's scope)

  • post_youtube_video (video upload) — the brief scoped yt to community posts.
  • socialmag.service — explicitly deferred until after live validation.
  • Scheduling, analytics, multi-account (proposal "Future").
S
Description
No description provided
Readme
274 KiB
Languages
Python 74.3%
HTML 18.1%
Shell 7.6%