facebook-mcp pre-1.0
Model Context Protocol server

facebook-mcp

Drive your Facebook Page from your AI assistant — publish and schedule, read insights, moderate comments and answer Messenger threads over the Meta Graph API. It runs on your machine, with your token, behind a three-host network fence.

TypeScript MIT Node 22+ Graph API v23.0 37 tools · 7 packages v0.7.0 on npm · pre-1.0
37tools in 7 packages
22 / 14read tools / write tools
3hosts the process may reach
0telemetry calls, by design

Status, honestly. All seven packages are implemented and covered by 1,349 hermetic tests (99.1% statement coverage), but every one of those tests runs against fakes and recorded fixtures behind a network fence. Version 0.7.0 is published on npm with provenance — published, implemented and tested in isolation, not proven in production. Nothing has been verified against the live Graph API yet. Treat this as a pre-1.0 project.

Install

Running in four steps

The package is on npm as @ivanbaev/facebook-mcp — one b in the scope, unlike the GitHub user IvanBBaev — so npx fetches it and there is no install step. Node 22 or newer. Building from source stays supported if you would rather compile it yourself.

Get it

shell
# nothing is installed and nothing runs at install time
npx -y @ivanbaev/facebook-mcp --version

Prefer source? Clone and compile, then read node build/index.js everywhere the steps below write npx -y @ivanbaev/facebook-mcp.

shell — from source
git clone https://github.com/IvanBBaev/facebook-mcp.git
cd facebook-mcp
npm ci
npm run build

Add a token

setup-token validates the token, resolves the Page and writes a 0600 env file for you. Pass the token through FB_SETUP_TOKEN rather than on the command line — an argument would be visible in ps and in your shell history. The value is never echoed back.

shell
# --page pins the Page; add --dry-run first to rehearse without writing
FB_SETUP_TOKEN='<your-meta-token>' \
  npx -y @ivanbaev/facebook-mcp setup-token --page=1234567890

Check it before you wire it up

doctor inspects the token — type, expiry, granted scopes — and prints a usable / partial / blocked matrix per package, so a missing permission surfaces here instead of inside a tool call.

shell
npx -y @ivanbaev/facebook-mcp doctor
npx -y @ivanbaev/facebook-mcp --version   # server + Node version, no credential needed

doctor, setup-token and --version are the only arguments that exit on their own — anything else starts the stdio server and waits on JSON-RPC.

Point your client at it

Let the client run npx, or point it at the absolute path to build/index.js if you built from source. Start read-only — FB_PACKAGES_READONLY drops every write tool while keeping the reads — and open the gate once you trust what you see.

~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "facebook": {
      "command": "npx",
      "args": ["-y", "@ivanbaev/facebook-mcp"],
      "env": {
        "FB_SYSTEM_TOKEN": "<system-user-token>",
        "FB_PAGE_ID": "1234567890",
        "FB_WRITE_MODE": "plan"
      }
    }
  }
}

On Windows the file lives at %APPDATA%\Claude\claude_desktop_config.json.

Capabilities

Seven packages, 37 tools

Packages are the unit of exposure: FB_TOOL_PACKAGES enables or disables whole packages, FB_PACKAGES_DENY removes one regardless, and FB_PACKAGES_READONLY keeps a package's reads while dropping its writes. core is always on.

Package Default Tools Write mode What it covers
core on 4 read Identity, Page discovery and rate-limit diagnostics. Read-only, always registered.
reader on 4 read A Page's own content: posts across four edges, single posts, Reels and reaction totals.
posts on 2 read 6 write plan Publish, schedule, edit and delete Page posts, photos, videos and Reels; poll the upload state of a large video or Reel.
insights on 3 read Page, post and Reel insights in a compact flat shape, with guidance for the metrics Meta renamed or removed. Reels read their own Graph edge, so they get their own tool.
moderation on 2 read 6 write apply Read and moderate comments — list, reply, hide, delete, private reply — and maintain the blocked-users list.
messages on 2 read 1 write plan Messenger for a Page: poll the inbox, read a thread with untrusted content wrapped, send one reply inside the 24-hour window.
ads opt-in 6 read 1 write plan Marketing API: campaign / ad-set / ad listings with delivery truth, insights with async report runs, plan-gated status and budget control.

What it deliberately does not do

Instagram and Threads, Stories, Events, Live video, Page profile management, organic post targeting and boosting a post are all out of scope for now. The business_management permission is never requested.

Insights that still exist

Meta has removed or renamed metrics in three waves — September 2024, November 2025 and June 2026. Metrics are passed through rather than whitelisted, and the tools surface the rename guidance instead of silently returning nothing.

The full 37-tool table, with the exact Graph permission each tool needs, lives in the README.

Safety

A write model you can reason about

Every tool declares a tier. The tier — not the tool, and not the model's confidence — decides what has to happen before the Graph API is touched.

read

22 tools

Never mutates anything. Safe to expose first, and to leave on.

reversible

10 tools

Governed by the effective write mode. Under plan the tool returns a preview with a plan_id and performs zero network mutations; under apply it executes. Creating a post, editing one, replying to or hiding a comment, blocking a user, sending a Messenger reply.

irreversible

4 tools

Ignores FB_WRITE_MODE completely — no environment variable unlocks it. Requires a per-call apply flag, plus the plan_id of a preview you just ran, plus an out-of-band human confirmation. Deleting a post, deleting a comment, sending a private reply, changing an ads object.

spend

0 tools

The money-moving tier. It is defined and enforced, but no shipped tool carries it today — ads control is irreversible instead, and FB_ADS_BUDGET_CEILING caps what a budget change may set.

Which default applies

  1. An explicit FB_WRITE_MODE wins outright, in both directions. Even an invalid value counts as explicit, so a typo can never loosen the gate.
  2. Otherwise the package default applies. posts, messages and ads ship plan; moderation ships apply, because hiding a comment is one click to undo and a moderation queue that previews twice is a moderation queue nobody uses.
  3. Otherwise the compiled-in default, plan.

A package default can never reach past reversible.

Confirmation happens out of band

For the irreversible tier the server asks a human directly, through MCP elicitation — but only when the client actually advertises elicitation.form; an empty elicitation: {} is not enough. When the client cannot prompt, the call falls through to the operator token: FB_CONFIRM_TOKEN, passed back as the tool's confirm_token argument.

There is no silent allow. A refusal names which route failed — no confirmation channel, wrong token, or stale plan — so you can tell a misconfiguration from a denial.

Defence in depth

  • Three hosts, and no others. The HTTP layer refuses any request outside the allowlist, including redirects that leave it.
  • No telemetry, no phone-home. The only outbound traffic is the Graph call you asked for.
  • Secrets never surface. Tokens are redacted from logs, errors and tool results; with FB_APP_SECRET set every call carries an appsecret_proof, so a stolen bare token is unusable.
  • Untrusted content is fenced. Comments and messages come back wrapped and marked as data, so a comment that says "ignore your instructions" reads as text, not as an instruction.
  • Local files are off by default. Uploads are URL-only until FB_MEDIA_DIR is set, and then only inside that directory — resolved through realpath, so a symlink cannot walk out.
  • Every write is journalled. Structured metadata only, no tokens and no PII, 0600, rotated by size — and a journal failure never blocks the write.
  • The HTTP transport fails closed. It binds loopback only and refuses to start without FB_HTTP_TOKEN.
graph.facebook.com graph-video.facebook.com www.facebook.com
Architecture

Four layers, one direction

Imports point leftward only, and the rule is enforced by lint rather than by convention — so the Graph client cannot learn about MCP, and the tool layer cannot reach past the API layer to make its own HTTP call.

tools Zod input schemas, tool registration, result shaping, package composition.
mcp Server bootstrap, transports, the write-mode gate, confirmation, doctor, setup-token.
api One module per Graph surface: posts, media, insights, comments, messaging, ads.
core Settings, the host-fenced HTTP client, retries and rate-limit parsing, errors, redaction, the journal.

Errors that say what to do

Graph error codes are mapped to an action rather than passed through raw: what happened, whether it is retryable, and what you should change. Rate limits are read from the response headers and backed off before the platform starts refusing.

Multi-Page from one process

Named profiles — FB_PROFILE_BRAND_A_PAGE_ID and an optional matching token — let one server drive several Pages; a tool call picks one with profile, and FB_PAGE_ID is the fallback.

Configuration

Credentials and environment

You need a Meta developer app and one access token. Because you administer the Page yourself, no Meta App Review is required — the permissions you grant to your own app are enough.

Token Precedence When to use it
FB_SYSTEM_TOKEN 1st A System User token from Business Manager. Recommended: it does not expire on its own, and it survives your personal password changes.
FB_ACCESS_TOKEN 2nd A long-lived user token — fine for a single operator.
FB_PAGE_TOKEN 3rd A long-lived Page token — the no-Business-Manager fallback.
FB_APP_SECRET optional Not required to run. When set, every request carries an appsecret_proof; with FB_APP_ID it also forms the app token used to inspect tokens in doctor.

Permissions per package

Package Required Graph permissions
core pages_show_list, pages_read_engagement
reader pages_read_engagement, pages_read_user_content
posts pages_manage_posts, pages_read_engagement
insights read_insights
moderation pages_read_user_content, pages_manage_engagement
messages pages_messaging, pages_manage_metadata
ads ads_read, ads_management
All 25 environment variables
Variable Default Description
FB_SYSTEM_TOKEN Secret. System User token (Business Manager). Recommended; wins over the other two.
FB_ACCESS_TOKEN Secret. Meta user access token, long-lived preferred.
FB_PAGE_TOKEN Secret. Long-lived Page token — the no-Business-Manager fallback.
FB_APP_ID Meta app ID. With FB_APP_SECRET it forms the app token used to inspect tokens.
FB_APP_SECRET Secret. When set, appsecret_proof is attached so a stolen bare token is unusable.
FB_PAGE_ID Default Page ID for Page-scoped tools when a call omits profile.
FB_API_VERSION v23.0 Graph API version to pin. Off-default values are accepted, but only the default is tested.
FB_REQUEST_TIMEOUT_MS 60000 Per-request timeout in milliseconds (1–600000).
FB_HOST_CONCURRENCY 4 Max parallel requests per Graph host (1–64).
FB_MAX_RESULT_CHARS 25000 Character budget before a tool result is truncated (500–10 000 000).
FB_WRITE_MODE plan plan previews a write without mutating; apply executes. Never covers the irreversible or spend tiers.
FB_CONFIRM_TOKEN Secret. Out-of-band confirmation token authorizing gated write / spend actions, for clients that cannot prompt.
FB_MEDIA_DIR Directory permitted as a source for local media uploads. Unset ⇒ URL-only, local file access disabled.
FB_JOURNAL_PATH XDG / %APPDATA% state path Path to the append-only, rotating write journal (0600).
FB_TOOL_PACKAGES core profile (all except ads) Comma-separated packages or profiles to enable. core is always forced on; ads is opt-in.
FB_PACKAGES_DENY Packages to exclude even if enabled by FB_TOOL_PACKAGES.
FB_PACKAGES_READONLY Packages whose write tools are not registered; their read tools stay.
FB_TRANSPORT stdio stdio or http (loopback-only Streamable HTTP for local agent clients).
FB_HTTP_TOKEN Secret. Bearer token required by the http transport; it fails closed without it.
FB_HTTP_PORT 3000 TCP port for the http transport; the bind host is fixed to 127.0.0.1.
FB_AD_ACCOUNT_ID Ad account ID for the opt-in ads package.
FB_ADS_BUDGET_CEILING Hard budget ceiling for ads writes, in minor currency units.
FB_LOG_LEVEL info Stderr log verbosity: debug, info, warn, error.
FB_PROFILE_<NAME>_PAGE_ID Page ID for a named profile, e.g. FB_PROFILE_BRAND_A_PAGE_ID.
FB_PROFILE_<NAME>_TOKEN Secret. Optional per-profile token override for the matching Page ID.

A commented template ships as .env.example, and the onboarding runbook walks through creating the app, minting the token and granting each permission.

FAQ

Questions worth asking first

Is facebook-mcp affiliated with Meta?

No. This is an independent, community-built project and is not affiliated with, endorsed by, or sponsored by Meta Platforms, Inc. Facebook, Meta and related marks are trademarks of Meta Platforms, Inc., used here only nominatively to indicate compatibility.

Which credentials does it need?

A Meta developer app plus one access token: a never-expiring System User token from Business Manager (recommended), a long-lived user token, or a long-lived Page token. FB_APP_SECRET is optional — when set, every call carries an appsecret_proof so a stolen bare token is unusable. Because you administer your own Page, no Meta App Review is required.

Is it safe to let an AI assistant write to my Page?

Writes are split into tiers. Reversible writes are governed by FB_WRITE_MODE: the compiled-in default is plan, which returns a preview and performs zero network mutations, and each package may ship its own default — posts, messages and ads default to plan, moderation defaults to apply because hiding a comment is one click to undo. An explicit FB_WRITE_MODE always wins, in both directions.

Irreversible actions — delete a post, delete a comment, send a private reply, change an ads object — ignore FB_WRITE_MODE entirely and require a per-call apply flag, the plan_id of a preview you just ran, and an out-of-band human confirmation the model cannot supply itself. The recommended starting posture is read-only.

What actually works today?

All seven tool packages are implemented: 37 tools across core, reader, posts, insights, moderation, messages and ads. Six are on by default; ads is opt-in via FB_TOOL_PACKAGES.

Version 0.7.0 is published on npm, but the project is still pre-1.0 and the automated test suite runs entirely against fakes and recorded fixtures behind a network fence — no behaviour has been verified against the live Graph API yet. A manual smoke harness exists for exactly that, gated behind FB_SMOKE=1 and never run in CI.

Is it published to npm?

Yes. The package is @ivanbaev/facebook-mcp — the npm scope carries one b, unlike the GitHub user IvanBBaev — and version 0.7.0 is the current latest, so npx -y @ivanbaev/facebook-mcp runs the server without an install step. It was published with npm provenance by the OIDC release workflow from the tagged commit.

The same rail ships the .mcpb desktop bundle on the v0.7.0 GitHub Release, with a build-provenance attestation you can check yourself using gh attestation verify facebook-mcp-0.7.0.mcpb --repo IvanBBaev/facebook-mcp, and the MCP registry listing io.github.IvanBBaev/facebook-mcp is live. It is still a pre-1.0 release: published, implemented and tested in isolation, not proven in production. Building from source stays supported as an alternative.

Which MCP clients does it work with?

Any client that speaks the Model Context Protocol over stdio — Claude Desktop, Claude Code, VS Code and Cursor among them. Optional client capabilities degrade honestly: without elicitation.form support the server falls back to the FB_CONFIRM_TOKEN route for high-consequence writes rather than silently allowing them, and without progress support an upload simply reports no progress. An optional loopback-only Streamable HTTP transport is available for advanced setups.

How can I support the project?

facebook-mcp is MIT-licensed and free, with no paid tier. It is maintained by one person in their own time, so donations are welcome — see Support below. Donating buys no priority support and no SLA; starring the repository or filing a good bug report helps just as much.

Support

Free, MIT, and built in spare time

There is no paid tier and no hosted version. If this saves you an afternoon, a donation keeps the coffee flowing — but a star, a bug report or a fix helps just as much.

Found a bug?

Open an issue with the output of npx -y @ivanbaev/facebook-mcp --version and the summary from doctor — that pair answers most of the first round of questions.

Open an issue →

Found a security problem?

Please report it privately through GitHub's private vulnerability reporting rather than in a public issue.

Security policy →