Skip to main content
Glama
swathigampa354-ship-it

social-mcp

Social MCP

AI-controlled social media automation platform. An agent talks to high-level MCP tools; a campaign engine, PostgreSQL, Redis/BullMQ, and an agent-browser worker decide how to post.

TikTok is the first platform. The data model already has Instagram, YouTube, X, and Facebook.

This is a new TypeScript system. It reuses concepts from AutoSocial Studio (MIT) — isolated per-account sessions, queues, a posting daemon — and does not copy that repository. See docs/ATTRIBUTION.md.


Project overview

The AI agent says what it wants:

Create campaign Alpha. Add these campaign videos and these normal videos. Assign Alpha to 20 TikTok accounts. Use a 3 campaign / 1 normal pattern. Post twice a day. Generate a 7-day schedule.

The MCP server performs the calculations. The agent never clicks through TikTok for normal operations.

AI Agent
  │
  ├── Social MCP   (high-level tools: campaigns, schedules, posts)
  └── agent-browser MCP   (debug / manual login only)

Social MCP
  → PostgreSQL (source of truth)
  → Campaign engine (pattern cursor per account)
  → Scheduler (per-account posting times)
  → Redis + BullMQ
  → Posting worker
  → agent-browser (isolated session per account)
  → TikTok

Architecture

apps/
  api/            REST management API (Vercel-compatible)
  mcp-server/     High-level MCP tools over stdio
  worker/         BullMQ workers + agent-browser + TikTok adapter
packages/
  shared/         types, config, errors, auth
  database/       Prisma client
  campaign-engine/  3 campaign / 1 normal (configurable)
  scheduler/      staggered posting times
  queue/          BullMQ + lock helpers
  storage/        local + S3 video/session storage
  core/           domain services used by API and MCP
prisma/           schema + SQL migrations

Campaign pattern

Default:

{ "pattern": ["campaign", "campaign", "campaign", "normal"] }

Each TikTok account stores campaign_pattern_position in PostgreSQL.

Post status

Pattern cursor

pending/queued/processing

reserved on the post, cursor unchanged

posted

cursor confirmed (position + 1)

failed

retry the same post first

unknown

do not auto-retry (duplicate risk)

cancelled

reservation released, cursor unchanged

The cursor survives process restart, worker crash, and paused schedules because it lives in the database, not in memory.

Posting frequency

Default: 2 posts/day per account.

Times are per account, not global:

  • Account 1 → 10:00, 18:00

  • Account 2 → 11:30, 20:00

If two accounts share a time, the scheduler adds a 1-minute stagger so workers do not collide.


Installation

Requires Node.js 20+.

git clone https://github.com/swathigampa354-ship-it/AutoSocial.git
cd AutoSocial
cp .env.example .env
npm install

Environment variables

See .env.example. Required for local use:

Variable

Purpose

DATABASE_URL

PostgreSQL

REDIS_URL

Redis / BullMQ

API_SECRET

API + MCP access key

OBJECT_STORAGE_PROVIDER

local or s3

LOCAL_STORAGE_DIR

Dev video storage

SESSION_STORAGE_DIR

Encrypted session blobs (references only in DB)

SESSION_ENCRYPTION_KEY

AES-256-GCM key (64-char hex in production)

AGENT_BROWSER_PATH

agent-browser binary

S3_*

Used when provider is s3

Do not put browser cookies, session JSON, or API secrets in git.


Database setup

docker compose up -d postgres redis
npx prisma migrate deploy --schema prisma/schema.prisma
npx prisma generate --schema prisma/schema.prisma

Redis setup

docker compose up -d redis
# REDIS_URL=redis://localhost:6379

The database is the source of truth. Redis only holds jobs. A restart re-enqueues due pending posts.


Running locally

cp .env.example .env
docker compose up -d postgres redis
npx prisma migrate deploy
npm run dev:api        # http://localhost:3000
npm run dev:worker     # posting + scheduler poll
npm run dev:mcp        # stdio MCP server (spawned by the agent)

Or all containers:

docker compose up --build

The default worker image does not include Chrome. Live TikTok publishing needs Dockerfile.worker.browser or a host with agent-browser install.


Running the worker

npm run dev:worker

Flow when a post is due:

  1. Scheduler poll finds pending posts with scheduled_at <= now

  2. Account must be active and not paused

  3. Post is locked pending → queued → processing (compare-and-set)

  4. Worker loads that account’s encrypted session

  5. agent-browser --session social-<accountId>

  6. TikTok upload workflow

  7. Verify result

    • success → posted, cursor advances

    • clear failure → failed, retry same post

    • uncertain → unknown, no automatic republish

If the worker dies mid-publish, recovery marks the row unknown after 15 minutes so a second worker cannot double-post.


Running the MCP server

npm run dev:mcp

Stdio JSON-RPC. The process inherits DATABASE_URL / REDIS_URL from the environment.


Connecting an AI agent

Claude Desktop / Cursor

Copy scripts/mcp-claude-desktop.json into the client config (update paths and secrets):

{
  "mcpServers": {
    "social-mcp": {
      "command": "npx",
      "args": ["tsx", "/absolute/path/to/apps/mcp-server/src/server.ts"],
      "env": {
        "DATABASE_URL": "postgresql://social:social@localhost:5432/social_mcp?schema=public",
        "REDIS_URL": "redis://localhost:6379"
      }
    },
    "agent-browser": {
      "command": "agent-browser",
      "args": ["mcp"]
    }
  }
}

Keep Social MCP and agent-browser MCP as separate servers.

  • Social MCP = production scheduling

  • agent-browser MCP = headed login, debugging, selector inspection

High-level tools

Account: social_create_account, social_list_accounts, social_get_account, social_update_account, social_enable_account, social_disable_account, social_get_account_status

Video: social_add_video, social_list_videos, social_get_video, social_delete_video

Campaign: social_create_campaign, social_list_campaigns, social_get_campaign, social_update_campaign, social_add_campaign_video, social_assign_campaign_to_accounts, social_set_campaign_pattern, social_get_campaign_status

Schedule: social_generate_schedule, social_schedule_post, social_get_schedule, social_pause_schedule, social_resume_schedule, social_cancel_post

Post: social_create_post, social_get_post, social_get_post_status, social_retry_post

Status: social_get_system_status, social_get_account_statistics, social_get_campaign_statistics, social_get_failed_posts

Agent ops: social_setup_campaign, social_get_next_actions, social_get_recommendations


Adding accounts

curl -X POST http://localhost:3000/accounts \
  -H "x-api-key: $API_SECRET" \
  -H "content-type: application/json" \
  -d '{"username":"brand_one","timezone":"Asia/Calcutta","postingTimes":["10:00","18:00"]}'

Or via MCP: social_create_account.

Then capture a TikTok login once (headed, not production scheduling):

npm i -g agent-browser
agent-browser install
ACCOUNT_ID=<cuid from API>
agent-browser --session social-$ACCOUNT_ID --restore open https://www.tiktok.com
# log in manually, then:
agent-browser --session social-$ACCOUNT_ID state save /tmp/tiktok-state.json

Place the saved state in session storage (the worker encrypts it when SESSION_ENCRYPTION_KEY is set). Database rows only store storage_reference, never cookies.


Adding videos

Videos are not stored in git.

  • Development: OBJECT_STORAGE_PROVIDER=local

  • Production: S3-compatible (S3_ENDPOINT, S3_BUCKET, keys)

social_add_video accepts bytesBase64 or an existing storageUrl. Mark each file campaign or normal.


Creating campaigns

social_create_campaign { name: "Campaign Alpha" }
social_add_campaign_video { campaignId, videoId }   # repeat
social_assign_campaign_to_accounts { campaignId, accountIds: [...] }
social_set_campaign_pattern { campaignId, pattern: ["campaign","campaign","campaign","normal"] }
social_generate_schedule { days: 7 }

Scheduling posts

social_generate_schedule walks each account’s posting times, asks the campaign engine for the next content type + video, and inserts idempotent posts rows (acct:<id>:slot:<iso>). Re-running the tool will not duplicate slots.

Pause / resume is per account and does not reset campaign_pattern_position.


Campaign pattern explanation

Example, Account A starting at position 0:

  1. Campaign

  2. Campaign

  3. Campaign

  4. Normal

  5. Campaign …

Account B can sit at a different cursor. Failed post 3 is retried as post 3 — the engine does not skip ahead to Normal.


API

Authenticated with x-api-key or Authorization: Bearer. /health is open.

  • /accounts

  • /campaigns

  • /videos

  • /posts

  • /schedules

  • /status

Validation is Zod. Errors return { error, message }.


Deployment

Do not run the browser worker as a Vercel serverless function.

Vercel
  ├── API
  ├── MCP HTTP endpoint (future)
  ├── Dashboard
  └── Webhooks

Persistent infrastructure
  ├── PostgreSQL
  ├── Redis
  └── Object storage (S3)

Worker environment (pick one)
  ├── VPS / Docker
  ├── Render / Railway worker
  ├── container job
  └── Vercel Sandbox (future)

API entrypoint: apps/api/src/vercel.ts.


Security notes

  • Management API is not public without API_SECRET.

  • Browser session files are encrypted at rest when SESSION_ENCRYPTION_KEY is set.

  • Session JSON is never logged. Cookie-like keys are redacted.

  • Sessions are isolated: social-<accountId>. Using account A’s session for account B throws.

  • Do not commit .env, storage/sessions, or agent-browser state.


Content shortage (STRICT)

If a pattern slot needs normal and no ready normal video exists, schedule generation refuses to create posts. Campaign videos are never substituted. Policy is STRICT (no silent FALLBACK).

Account locking

Workers take Redis lock lock:account:{accountId} so two browser sessions cannot post as the same TikTok account at once.

Known limitations

  • Live TikTok publishing has not been verified in this environment. The adapter implements the workflow and stops cleanly when no session exists.

  • TikTok Studio UI changes. Selectors live in apps/worker/src/tiktok/selectors.ts only.

  • Instagram / YouTube / X / Facebook are schema-ready, not implemented.

  • MCP is stdio. There is no hosted MCP HTTP gateway yet.

  • Default Docker worker image has no Chrome; use Dockerfile.worker.browser.

  • Caption/language localization of TikTok UI may require extending publishNames.


Troubleshooting

Symptom

What to do

prisma migrate fails

Postgres not up; check DATABASE_URL

Worker idle

Redis down, or accounts schedulePaused / DISABLED

Posts stuck processing

Wait 15m for unknown recovery, inspect logs, do not force retry if status is unknown

unknown status

Verify on TikTok manually. Never auto-repost.

Session expired

Headed re-login with agent-browser for that account id only

agent-browser binary not found

npm i -g agent-browser && agent-browser install

Duplicate key on generate

Harmless — idempotency reused the existing slot


Tests

npm test

Coverage:

  • Campaign engine: 3+1 loop, persistence, failed retry, unknown block

  • Scheduler: 7-day / 2-per-day, stagger, past-slot skip, idempotency keys

  • Locks: two workers cannot publish the same post

  • Account isolation: session A never binds to account B

  • API: auth, validation, error mapping


Tech stack

TypeScript, Node.js 20, PostgreSQL, Prisma, Redis, BullMQ, MCP SDK, agent-browser, Zod, Express, local/S3 storage.

No deviations from the recommended architecture. node-cron is not used.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/swathigampa354-ship-it/social-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server