Files
socialmag/README.md
T

3.4 KiB

socialmag

MCP server that posts, reads, and replies across 6 platforms — Threads, Facebook, Instagram, YouTube, TikTok, and Twitter/X — by driving abang's already-logged-in Brave over CDP. No API keys, no OAuth.

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 Description
create_post Post to one or more platforms (threads, fb, ig, yt, tiktok, twitter)
check_login Check which platforms are signed in
get_status Last post results + open browser tabs
get_feed Read Twitter timeline or search with X operators
reply_tweet Reply to a tweet found by search
get_notifications Check platform activity/notifications (Threads)
open_link Navigate platform's browser tab to a URL (tab reuse)

create_post — 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; required for ig/tiktok, optional for threads/fb, rejected for yt.

Dashboard

dashboard.html is a local viewer — open it in a browser tab to see platform status, post timeline, and Twitter intel. Reads from dashboard.json (written by the server at runtime), auto-refreshes every 10 seconds.

Layout

server.py       MCP stdio JSON-RPC loop + 7 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 + PLATFORMS dict, one file
platforms/      one module per platform: post() + check_login()
dashboard.html  local status/timeline viewer (reads dashboard.json)

Each platform module exposes:

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

Twitter additionally exposes get_feed, reply, and check_login (reusing the existing selector set). Threads exposes get_notifications.

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").