Skip to main content
Glama
Repliz MCP Server

Repliz MCP Server

by azickri

Repliz MCP Server

A hosted Model Context Protocol server for the Repliz Public API. It gives an AI assistant 119 tools for managing a Repliz workspace in plain language: listing and replying to comments, scheduling posts, handling DMs, browsing content and stats, researching Threads, managing automations and templates, and more.

Users connect by pointing their MCP client at one URL — then either click "Connect" and sign in with their Repliz Access Key and Secret Key (OAuth), or send those keys as headers. There is nothing for them to install, build, or keep updated.

https://mcp.repliz.com/mcp

This repository is the server that serves that URL.


How it works

One process serves everyone. It holds no credentials of its own — each request carries the caller's keys, which are verified against the Repliz API and bound to that caller's session. A session owns its own API client, so no two users ever share one.

MCP client ──(URL + keys)──► this server ──(Basic auth)──► api.repliz.com

Users authenticate in one of three ways. Keys come from the Repliz dashboard under Settings > API.

OAuth — for Claude.ai, ChatGPT, and any client with a "Connect" button. The user adds https://mcp.repliz.com/mcp as a connector, a sign-in page opens, and they paste their keys there once. Enabled when REPLIZ_OAUTH_SECRET is set; see OAuth.

Headers — for API integrations (the Anthropic Messages API, the OpenAI Responses API) and clients with custom-header support (Cursor and most desktop clients):

Authorization: Basic base64(accessKey:secretKey)
X-Repliz-Access-Key: <accessKey>
X-Repliz-Secret-Key: <secretKey>

Related MCP server: RecurPost MCP Server

Running it

Requires Node.js 18+ (Node 20.12+ to auto-load .env).

npm ci
npm run build
cp .env.example .env
npm start

The endpoint is POST/GET/DELETE /mcp, plus GET /health for monitoring. With OAuth on, the server also answers /.well-known/oauth-protected-resource/mcp, /.well-known/oauth-authorization-server, /register, /authorize, /token, and /oauth/login (the sign-in form). Serve it over HTTPS in production, behind a reverse proxy or platform TLS.

Configuration

Everything is optional; the defaults suit a 1 GB VPS.

Variable

Default

Description

PORT

3000

Port to listen on

REPLIZ_BASE_URL

https://api.repliz.com

Repliz API base URL

REPLIZ_MAX_SESSIONS

300

Cap on concurrent sessions; beyond it new connects get 503

REPLIZ_SESSION_IDLE_MINUTES

30

Close a session after this long without a request

REPLIZ_RATE_LIMIT_PER_MINUTE

120

Requests allowed per IP per minute

REPLIZ_TRUST_PROXY

loopback

Which proxies may set X-Forwarded-For

REPLIZ_REQUEST_TIMEOUT_SECONDS

30

Timeout for outbound calls to the Repliz API

REPLIZ_AUTH_CACHE_MINUTES

5

How long a verified credential pair is cached

REPLIZ_MAX_UPLOAD_MB

100

Largest file repliz_upload_file will stream through the server

REPLIZ_ALLOWED_ORIGINS

—

Browser origins allowed to call /mcp; unset means no CORS headers

REPLIZ_UPLOAD_HOSTS

R2 + Repliz storage

Hosts accepted as presigned-upload targets

REPLIZ_OAUTH_SECRET

—

Turns OAuth on; seals every token. At least 32 characters

REPLIZ_PUBLIC_URL

—

Public origin, e.g. https://mcp.repliz.com; required with OAuth

REPLIZ_OAUTH_ACCESS_TOKEN_MINUTES

60

Access token lifetime

REPLIZ_OAUTH_REFRESH_TOKEN_DAYS

30

How long an unused connection stays signed in

There is deliberately no variable for the server's own Repliz credentials. A fallback like that would quietly lend the operator's workspace to any caller who omitted their keys.

OAuth

The Repliz API has no OAuth of its own, so this server acts as the authorization server and wraps the user's keys:

Claude.ai ──► /mcp: 401 ──► discovery ──► /register ──► /authorize (sign-in page)
          ◄── code ◄── keys checked against Repliz ◄── user pastes keys
          ──► /token (PKCE) ──► access + refresh token ──► /mcp with Bearer

To turn it on, add two lines to .env and restart:

REPLIZ_PUBLIC_URL=https://mcp.repliz.com
REPLIZ_OAUTH_SECRET=<output of: openssl rand -base64 32>

There is no token database. Every client id, authorization code, and token is the user's keys (plus expiry and client binding) encrypted with AES-256-GCM under REPLIZ_OAUTH_SECRET. Only this server can open them, they survive a restart, and nothing new needs deploying or backing up. What follows from that:

  • Keep REPLIZ_OAUTH_SECRET stable and private. Changing it signs every OAuth user out — which is also the emergency switch if it leaks.

  • One token cannot be revoked early. Instead, access tokens live an hour; each tool call still presents the sealed keys to Repliz, so a key rotated in the Repliz dashboard stops working at once; and a refresh re-checks the keys, sending the client back to the sign-in page. No revocation endpoint is advertised, because it could only pretend.

  • Refreshing extends the connection. Each refresh issues a new refresh token, so a connection in regular use stays signed in; one left unused for REPLIZ_OAUTH_REFRESH_TOKEN_DAYS has to sign in again.

Clients identify themselves in either of two ways, and need no setup on this side for either:

  • Client ID Metadata Documents (what Claude and ChatGPT prefer). The client_id is an HTTPS URL, such as https://chatgpt.com/oauth/client.json, serving a JSON document that names the client and its redirect URIs. The server fetches it on first use and caches it as its Cache-Control says (between one minute and a day; an hour without the header). Only public clients are accepted: the document must allow token_endpoint_auth_method none, as both Claude's and ChatGPT's do.

  • Dynamic Client Registration (RFC 7591), for everything else. The client id it hands out is the registration itself, sealed.

Accepted redirect URIs are any https:// URL, http:// on localhost only, and a native app's own scheme such as cursor://. Every authorization response carries the issuer (iss, RFC 9207), which ChatGPT needs to use its stable redirect URI. Header authentication keeps working alongside OAuth.

PM2 + Nginx on a VPS

npm ci && npm run build
cp .env.example .env           # PORT and the limits live here
pm2 start dist/index.js --name repliz-mcp
pm2 save && pm2 startup        # survive a reboot

Do not add -i / --instances. PM2's default fork mode is what you want. Sessions live in this process's memory, so cluster mode would spread a user's requests across workers that know nothing about each other, and every follow-up request would be told its session does not exist. Scale by raising REPLIZ_MAX_SESSIONS on a bigger box, not by adding instances.

Nginx needs two non-default settings, because the transport holds a Server-Sent Events stream open and Nginx would otherwise buffer it and cut it after 60 seconds:

location /mcp {
    proxy_pass http://127.0.0.1:5301;
    proxy_http_version 1.1;
    proxy_set_header Connection "";

    proxy_buffering off;
    proxy_cache off;
    proxy_read_timeout 3600s;   # keep above REPLIZ_SESSION_IDLE_MINUTES

    proxy_set_header Host              $host;
    proxy_set_header X-Real-IP         $remote_addr;
    proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

# OAuth discovery, sign-in, and token endpoints live at the root.
location / {
    proxy_pass http://127.0.0.1:5301;
    proxy_set_header Host              $host;
    proxy_set_header X-Real-IP         $remote_addr;
    proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

$proxy_add_x_forwarded_for is correct here: the server trusts only loopback, so it reads the rightmost untrusted entry and a client cannot forge its way into a fresh rate-limit bucket. It matters even more with OAuth on: /register allows 20 registrations per IP per hour, and without X-Forwarded-For every user looks like 127.0.0.1 and shares that one budget.

What a restart does

pm2 restart drops every live session — they exist only in this process's memory. Nothing is queued or half-written, since every tool call is a single synchronous call to the Repliz API, so the cost is small:

Mid-request

That one call fails; retrying works.

Idle

The next request gets 404 Session not found, which the MCP spec defines as "start a new session" — compliant clients re-initialize silently.

After reconnecting

Normal. One extra verification call, because the auth cache is empty again.

OAuth users

Stay signed in. Their tokens are sealed, not stored, so they open again after a restart.

Shutdown on SIGTERM closes sessions and stops listening in about 0.1s, well inside PM2's default kill_timeout.


Sizing

The server is a thin proxy — every tool call is one outbound HTTPS request with no computation in between — so CPU is effectively idle and memory is the only thing to plan for. Measured:

Live sessions

Process RSS

0 (baseline)

~96 MB

10

~103 MB

100

~122 MB

300

~173 MB

1000

~356 MB

That is roughly 0.26 MB per live session on top of a ~96 MB baseline. The 119 tool schemas are built once and shared by every session; only the MCP server object, the transport and the Repliz client — which holds that user's credentials — are per session. The default cap of 300 fits easily on a 1 GB VPS. Sessions are only live while a client is connected and idle ones are reclaimed after 30 minutes, so the steady-state number is far below the total user count.

GET /health reports live sessions, the cap, RSS, and uptime.


Security

Because /mcp is open to the internet:

  • Credentials are verified before a session exists. On initialize the server calls GET /public/account/count with the supplied keys and returns 401 if Repliz rejects them, so an anonymous caller cannot make the server allocate memory. Successes are cached briefly so reconnects stay cheap.

  • Sessions are bound to the credentials that opened them. Every later request is re-checked against a fingerprint of those keys, in constant time. The session id travels as a plain header and can end up in a proxy access log or a client's debug output; without this check, replaying it would hand over the owner's workspace with no credentials at all. A mismatch gets 403, a request with no credentials gets 401.

  • Sessions expire and are capped. Idle sessions are swept, because MCP clients routinely disappear without sending DELETE — the SDK's own client.close() does not send one. At capacity the server sweeps first, then evicts the least recently used session if it has been quiet for over a minute, so dead sessions cannot lock out real users; if every session is genuinely active, the new connect gets 503 rather than cutting someone off.

  • Requests are rate limited per IP, counted against the address chosen by REPLIZ_TRUST_PROXY.

  • The OAuth sign-in page guards the keys typed into it. It cannot be framed (frame-ancestors 'none', X-Frame-Options: DENY), so a clickjacking overlay cannot trick a user into submitting. Any client can register itself, so the page escapes the client's chosen name and states where the user will be sent next — a client calling itself "Claude" still cannot hide its own redirect host. The form carries the authorization request sealed, so its redirect target cannot be altered, and there is no session cookie for a cross-site request to ride. Sign-in attempts are limited to 10 per IP per minute, and PKCE (S256) is mandatory. For a client identified by a metadata document, the page also names the host that published it.

  • Client metadata documents are fetched like untrusted URLs — because the client_id is whatever the caller sends. The fetch refuses private and loopback addresses and redirects, times out after 5 seconds, and reads at most 5 KB. A document whose client_id differs from its URL, that carries a client secret, or that lists a disallowed redirect URI is refused, and nothing fetched is ever relayed back to the caller.

  • Outbound calls time out, so a stalled Repliz request cannot pin a session.

  • Uploads never touch the host's disk. See File uploads.

  • Clean shutdown on SIGTERM / SIGINT.

Two things to be aware of:

  • Credentials are verified when a session opens, not on every call. A revoked key keeps working until its session goes idle; shorten REPLIZ_SESSION_IDLE_MINUTES if that matters.

  • Sessions live in memory, so this is a single-instance server. Horizontal scaling needs a shared session store — see Roadmap.

File uploads

Uploading is three steps: repliz_init_file returns a one-time presigned URL, repliz_upload_file sends the bytes, repliz_complete_file finalizes.

The middle step takes a sourceUrl, never a local path. This server is shared, so its filesystem belongs to the operator: a path parameter would let any connected user read the server's own files and ship them anywhere. Two guards apply:

  • the presigned PUT target must be a Repliz storage host (REPLIZ_UPLOAD_HOSTS)

  • sourceUrl must resolve to a public address — loopback, private LAN ranges, and cloud metadata endpoints such as 169.254.169.254 are refused, as are redirects

Clients that would rather not route media through the server can PUT straight to the presigned URL themselves and then call repliz_complete_file.


Tools

119 tools across the Repliz Public API:

Group

Count

Tools

Accounts

6

list, count, get, statistics, update automation, delete

Account Connect

42

OAuth authorize / exchange / list / connect / reconnect for Facebook, Instagram, Threads, YouTube, LinkedIn, TikTok, Shopee, Twitter/X, Pinterest; QR session / connect / reconnect for WhatsApp; QR session / two-step password / connect / reconnect for Telegram

Comments

5

list, get, reply, update status, delete

Contacts

10

list, count, list tags, get, get by comment, get by chat, update, set follow-up, remove follow-up, delete

Schedule

7

list, get, create, update, retry, delete, bulk delete

Chat

5

list, get, list messages, send message, mark read

Content

9

list, get, list comments, comment, statistics, DM commenter, like comment, delete comment, delete content

Automation

5

list, get, create, update, delete

Templates

5

list, get, create, update, delete

Storage

8

statistics, list, get, init upload, upload, complete, delete, bulk delete

Reports

3

list, get, retry

Research

3

Threads content, user content, user profile

Add-ons

11

TikTok trending music, Shopee products, WhatsApp and Telegram channels & groups, Pinterest boards (list, create, update, delete), Instagram profile (follow check), link metadata, addon allocation

Tool availability follows the caller's Repliz plan; calling one above your tier returns a clear error.

Every tool declares what it does, as MCP tool annotations: 53 only read (readOnlyHint), 45 create or send something without touching what exists (destructiveHint: false), and 21 change or delete (destructiveHint: true). Clients use these to decide what to confirm first; ChatGPT asks before any tool not marked read-only. A new tool must state its effect to compile.


Development

npm run dev     # run from TypeScript source, no build step
npm test        # unit tests (no network, no credentials)
npm run smoke   # end-to-end: boots the real server against a stub Repliz API
npm run build   # compile to dist/

npm test covers the parts where a quiet mistake becomes a security hole: the SSRF and upload-host guards, credential parsing, the session credential binding, the limits that bound memory, and OAuth token sealing, single-use codes, and client binding. npm run smoke covers the wiring — that two users stay apart, that a session id alone opens nothing, that the full OAuth flow works over HTTP, and that the upload guards are reachable through the tool surface. Neither needs credentials or network access, and CI runs both.

src/
  index.ts     # entry point: load .env, start the server
  http.ts      # the HTTP transport, auth, and request lifecycle
  session.ts   # session store, credential fingerprints, rate limiter, auth cache
  server.ts    # createReplizServer(client)
  config.ts    # env config + per-request credential parsing
  client.ts    # Repliz API client (Basic auth, timeouts, errors)
  oauth.ts     # OAuth provider: sealed clients, codes, and tokens
  oauth-page.ts  # the sign-in page and its security headers
  net.ts       # upload-host allowlist and SSRF guards
  tools/       # one module per API domain, registered via tools/index.ts
test/          # unit tests for config, session, oauth, and net
scripts/
  smoke-test.mjs   # end-to-end check against a stub API
api.json           # the source OpenAPI spec, kept as reference

Roadmap

  • Shared session store (Redis) so several replicas can run behind a load balancer without sticky sessions.

License

MIT — see LICENSE.

Available Tools

33 tools
repliz_count_accountsCount AccountsA

Get the total number of accounts connected to your Repliz workspace.

Inputs: no named parameters.

repliz_create_content_commentComment on ContentC

Post a comment on a piece of content, or reply to an existing comment by passing commentId.

Inputs: text (required), accountId (required), contentId (required), commentId.

repliz_create_scheduleCreate Scheduled PostA

Schedule a post to be published to a connected account at a specific time.

Post type support by platform:

  • text: Facebook, Threads

  • image: Facebook, Instagram, Threads, TikTok, LinkedIn

  • video: Facebook, Instagram, Threads, TikTok, YouTube, LinkedIn

  • reel: Facebook

  • album: Facebook, Instagram, Threads, TikTok, LinkedIn

  • link: Facebook (provide meta for the link preview)

  • story: Facebook, Instagram

For media posts, attach medias. For threaded/multi-part posts, attach replies. scheduleAt must be a future ISO 8601 timestamp (UTC).

Inputs: type (required), accountId (required), scheduleAt (required), description (required), +7 more.

repliz_delete_accountDelete AccountA

Disconnect and remove an account from your Repliz workspace. This is irreversible — confirm with the user before calling.

Inputs: accountId (required).

repliz_delete_content_commentDelete Content CommentA

Delete a comment on a piece of content. Irreversible — confirm before calling.

Inputs: accountId (required), commentId (required), contentId (required).

repliz_delete_scheduleDelete Scheduled PostA

Delete a single scheduled post by id. Irreversible — confirm before calling.

Inputs: scheduleId (required).

repliz_delete_schedulesDelete Multiple Scheduled PostsA

Delete several scheduled posts at once by their ids. Irreversible — confirm before calling.

Inputs: scheduleIds (required).

repliz_get_accountGet AccountA

Get details of a single connected account by its Repliz account id.

Inputs: accountId (required).

repliz_get_chatGet ChatB

Get the details of a single conversation by its chat id.

Inputs: chatId (required).

repliz_get_commentGet CommentB

Get the full details of a single comment by its id.

Inputs: commentId (required).

repliz_get_contentGet ContentB

Get the details of a single piece of content.

Inputs: accountId (required), contentId (required).

repliz_get_content_commentsGet Content CommentsA

List comments on a piece of content. Pass commentId to fetch replies to a specific comment. Uses cursor pagination via nextToken.

Inputs: accountId (required), contentId (required), commentId, nextToken.

repliz_get_content_statisticGet Content StatisticsC

Get engagement statistics/insights for a piece of content.

Inputs: accountId (required), contentId (required).

repliz_get_scheduleGet Scheduled PostB

Get the full details of a single scheduled post by its id.

Inputs: scheduleId (required).

repliz_list_accountsList AccountsA

List the social accounts connected to your Repliz workspace. Returns each account's id, platform, username, picture, and status. Supports pagination and filtering by platform type or search term.

Inputs: page, limit, types, search.

repliz_list_chatsList ChatsA

List direct-message conversations across connected accounts. Supports pagination and filtering by status (unread/unreplied), account(s), and search.

Inputs: page, limit, search, status, +1 more.

repliz_list_commentsList CommentsA

List comments in your Repliz inbox across connected accounts. Supports pagination and filtering by status, account(s), and search term.

Inputs: page, limit, search, status, +1 more.

repliz_list_contentList ContentA

List published content (posts/media or stories) for a connected account. Uses cursor pagination via nextToken.

Inputs: accountId (required), type, nextToken.

repliz_list_messagesList Chat MessagesA

List the messages within a conversation, paginated (newest first).

Inputs: chatId (required), page, limit.

repliz_list_schedulesList Scheduled PostsA

List scheduled and published posts. Supports pagination and filtering by account(s), status, and date range.

Inputs: page, limit, status, toDate, +2 more.

repliz_message_content_commentSend DM to Content CommenterB

Send a private message (DM) to the author of a comment on your content. Provide text and/or a button payload.

Inputs: accountId (required), commentId (required), contentId (required), text, +1 more.

repliz_read_chatMark Chat as ReadB

Mark a conversation as read.

Inputs: chatId (required).

repliz_reply_commentReply to CommentB

Post a public reply to a comment. The reply is published to the original platform (Facebook, Instagram, etc.).

Inputs: text (required), commentId (required).

repliz_retry_scheduleRetry Scheduled PostB

Retry publishing a scheduled post that previously failed (status 'error').

Inputs: scheduleId (required).

repliz_search_threads_contentSearch Threads ContentA

Search public Threads posts by keyword. Requires a connected Threads account id (used to authorize the search). Uses cursor pagination via nextToken.

Inputs: search (required), accountId (required), nextToken.

repliz_search_threads_userGet Threads User ProfileB

Look up the public profile of a Threads user by username. Requires a connected Threads account id.

Inputs: username (required), accountId (required).

repliz_search_threads_user_contentGet Threads User ContentA

Fetch public Threads posts authored by a specific username. Requires a connected Threads account id. Uses cursor pagination via nextToken.

Inputs: username (required), accountId (required), nextToken.

repliz_send_messageSend Chat MessageB

Send a message in a conversation. Set type and provide the matching field:

  • text: provide text

  • image: provide image { url, mimetype, thumbnail? }

  • video: provide video { url, duration, mimetype, thumbnail? }

  • audio: provide audio { url, duration, mimetype }

  • document: provide document { url, name, size, mimetype }

  • button: provide button { text, buttons:{ title, url }, image? }

Inputs: type (required), chatId (required), text, audio, +4 more.

repliz_shopee_productsList Shopee ProductsA

List Shopee products for a connected Shopee account. Useful for tagging products on posts. Uses cursor pagination via nextToken.

Inputs: accountId (required), nextToken.

repliz_update_comment_statusUpdate Comment StatusA

Update the handling status of a comment in your Repliz inbox (pending, resolved, or ignored).

Inputs: status (required), commentId (required).

repliz_update_scheduleUpdate Scheduled PostA

Update an existing scheduled post. The account cannot be changed; provide the new content fields. scheduleAt must be a future ISO 8601 timestamp.

Inputs: type (required), scheduleAt (required), scheduleId (required), description (required), +7 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.7/5.0

    Scored across 33 tools

    Disambiguation5/5

    Each tool targets a distinct resource and action (e.g., get_comment vs get_content_comments, list_chats vs get_chat). Descriptions clearly differentiate similar-sounding tools, leaving no ambiguity.

    Naming Consistency5/5

    All tools follow a consistent 'repliz_verb_noun' pattern with lowercase and underscores. Actions like create, get, list, delete, update are used uniformly across resources.

    Tool Count4/5

    33 tools is slightly high but appropriate for a comprehensive social media management server covering accounts, scheduling, content, comments, messaging, and search. Each tool has a clear purpose.

    Completeness4/5

    The tool set covers major workflows: account management, scheduling (CRUD + retry), content/comments, messaging, and platform-specific searches. Minor gaps like immediate publishing (vs scheduling) exist but are not critical.

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      C
      maintenance
      Manage Threads and Bluesky social media from AI assistants. Schedule posts, check analytics, and automate follow-up replies.
      3
      MIT
    • A
      license
      A
      quality
      B
      maintenance
      Provides AI assistants with access to the RecurPost API to manage social media accounts, schedule posts, and organize content libraries. It enables users to automate recurring posts, track engagement metrics, and generate social media content through natural language.
      10
      15 npm
      1
      MIT
    • A
      license
      A
      quality
      D
      maintenance
      Enables AI assistants to manage social media accounts and CRM operations, including posting, analytics, inbox management, and customer management.
      22
      14 PyPI
      Apache 2.0
    • A
      license
      A
      quality
      B
      maintenance
      Enables AI assistants to manage social media publishing across platforms like LinkedIn, Twitter, Facebook, Instagram, Threads, and Bluesky.
      7
      48 npm
      MIT