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