facebook-mcp — Facebook Pages MCP Server
A local-first TypeScript Model Context Protocol (MCP) server for the Meta Graph API, focused on Facebook Pages — publishing, reading & insights, and comment/message moderation — driven from an MCP client (Claude, VS Code, the Inspector…) and operated locally by a Page admin using their own Meta developer app. Least-privilege tokens, plan-and-apply write safety, and no telemetry.
🚧 Status: pre-1.0, in active development. Published on npm as
@ivanbaev/facebook-mcp— and the version number is the warning.All seven tool packages are implemented — see the tool table for the exact surface. Nothing has been verified against the live Graph API yet. Every test in this repository runs against fakes and recorded fixtures behind a network fence that throws on a real
fetch, so "the tests pass" means "the code does what the fixtures say", not "Meta accepted it". A live smoke harness exists (scripts/smoke/) but has not been run against a real Page as part of any released state. Treat every capability below as implemented but unproven, expect breaking changes until 1.0, and read Known limitations before you rely on anything.
Contents: Features · Requirements · Setup · Client compatibility · Configure credentials · Permissions · Tools · How this compares · Known limitations · Not in scope · Your responsibilities · Roadmap · Security notes · Documentation · Support · Trademark · License
Features
Implementation status only — see the status note above for what "implemented" does and does not mean.
The default profile exposes a deliberately small, curated tool surface — each tool wraps one real capability rather than mirroring every Graph edge.
- Local-first, no telemetry. Only three Meta hosts are ever reached
(
graph,graph-video,rupload); nothing phones home. - Least-privilege credentials. Works with a never-expiring System User token (Business Manager) or a long-lived Page token — no App Review required for an admin operating their own assets.
- Safe writes. Tiered plan-and-apply gating; irreversible and spend actions require out-of-band confirmation and are never bypassed by an env flag.
- Secret hygiene. Value-based redaction at a single choke-point across logs, errors, tool results, and the write journal.
- Layered architecture. Lint-enforced
core ← api ← mcp ← toolslayering, tools-as-data with a central package registry.
Requirements
- Node.js ≥ 22 (enforced by
enginesand a runtime guard in the launcher; the project targets the version in.nvmrc). - A Meta (Facebook) developer app — a Business-type app at Standard Access is enough; no App Review is needed to operate your own assets.
- A Facebook Page (and, for the opt-in
adspackage, an ad account) you administer.
Setup
The first run is five steps, in this order. Budget about 20 minutes; most of it is Meta's app UI. docs/runbooks/onboarding.md is the long form with troubleshooting.
-
Meta app — create a Business-type app at https://developers.facebook.com/apps and note its App ID and App Secret (Settings → Basic).
setup-tokenneeds both. -
Short-lived token — in the Graph API Explorer pick your app, User Token, tick the scopes you need (at least
pages_show_listandpages_read_engagement), and Generate Access Token. It lives 1–2 hours, so do step 3 right away. -
Exchange it and write the env file —
setup-tokenswaps it for a ~60-day token, resolves your Page and writes the env file:FB_APP_ID='<app id>' FB_APP_SECRET='<app secret>' \ FB_SETUP_TOKEN='<explorer token>' \ npx -y @ivanbaev/facebook-mcp setup-tokenSuccess is
status: completedin the Run block andstatus: writtenin the Env file block. The file lands at~/.config/facebook-mcp/.env($XDG_CONFIG_HOMEif set) or%APPDATA%\facebook-mcp\.envon Windows, mode0600. The server reads it on every start, so the client config below needs noenvblock. More than one Page? Re-run with--page=<id> --force. -
Pre-flight — every package you plan to use should be
usable:npx -y @ivanbaev/facebook-mcp doctor -
Register the server with your client and make the first call — see the next two subsections, then ask the client to "run facebook_whoami". A reply naming your token type and Page is the first successful tool call.
The doctor and setup-token reports name commands in the form you launched
with (npx -y @ivanbaev/facebook-mcp …, facebook-mcp … after a global
install, or node build/index.js … from a source checkout). --help writes
facebook-mcp …; substitute your launch form. Every subcommand takes --help
without touching the network.
(a) Via npx
Claude Code — one command, then check /mcp inside a session:
claude mcp add facebook -- npx -y @ivanbaev/facebook-mcp
It is registered for the current project directory by default; add
--scope user to have it everywhere. Or install it as a plugin, which wires up
the same command:
/plugin marketplace add IvanBBaev/facebook-mcp
/plugin install facebook-mcp
Claude Desktop — Settings → Developer → Edit Config opens
claude_desktop_config.json (macOS:
~/Library/Application Support/Claude/, Windows: %APPDATA%\Claude\). Add the
server, save, then quit and restart Claude Desktop:
{
"mcpServers": {
"facebook": {
"command": "npx",
"args": ["-y", "@ivanbaev/facebook-mcp"]
}
}
}
Claude Desktop does not load your shell profile. If Node comes from a version
manager (nvm, fnm, asdf) and the server fails to start, put the absolute path of
npx (which npx) in command.
Any other stdio client takes the same command / args pair — see
Client compatibility.
Claude Desktop bundle (alternative) — the .mcpb attached to the
latest release
installs by double-click. Its settings form lists every credential as optional:
leave them blank to use the env file from step 3 (a blank field never overrides
it). It carries a build-provenance attestation, so you can prove it came out of
this repository's workflow before you install it:
gh attestation verify facebook-mcp-0.7.0.mcpb --repo IvanBBaev/facebook-mcp
(b) From source
For development, or to run a commit that is not released yet:
git clone https://github.com/IvanBBaev/facebook-mcp.git
cd facebook-mcp
npm install
npm run build
node build/index.js setup-token # with the same FB_* variables as step 3
node build/index.js doctor # pre-flight: token, scopes, package matrix
node build/index.js # or: ./bin/facebook-mcp.mjs
Point your MCP client at that command with an absolute path. The env block is
optional — without it the server reads the env file, and a non-blank value here
overrides the file:
{
"mcpServers": {
"facebook": {
"command": "node",
"args": ["/absolute/path/to/facebook-mcp/build/index.js"],
"env": { "FB_SYSTEM_TOKEN": "…", "FB_PAGE_ID": "…" }
}
}
}
Client compatibility
This is a standard stdio MCP server with no client-specific code, so it should work anywhere the protocol does. "Should" is the operative word: the table records the configuration each client needs and whether that path has actually been exercised — not a promise.
What is verified, by automated test:
- stdio framing — a test spawns the built server as a real subprocess, runs
the full
initialize→tools/list→tools/callhandshake over pipes, and asserts that stdout carries protocol frames only (all logging goes to stderr). - Tool contracts — every tool's input/output schema, annotations, write tier and error mapping are unit-tested against fakes and recorded fixtures.
- The HTTP transport — loopback binding, bearer-token rejection and session handling, again against a real local server.
None of that involves an MCP client or the Meta API. If you get it working with a client, say so in an issue — that is how this table turns into ✅.
Platform notes
CI runs the full check on Linux (Node 22/24/26) and Windows (Node 22). macOS is
the primary development platform but is not in CI. Paths for the env file, the
write journal and FB_MEDIA_DIR follow XDG on POSIX and %APPDATA% on Windows.
Configure credentials
facebook-mcp authenticates with a token you already control — there is no
App Review, no OAuth callback server, and no hosted component. Provide at least
one of the token variables below; the most specific wins (FB_SYSTEM_TOKEN →
FB_ACCESS_TOKEN → FB_PAGE_TOKEN):
FB_SYSTEM_TOKEN— a never-expiring System User token (Business Manager). Recommended: it does not expire and is scoped to the assets you assign it.FB_ACCESS_TOKEN— a Meta user access token (a long-lived one preferred).FB_PAGE_TOKEN— a long-lived Page token, the no-Business-Manager fallback. It belongs to exactly one Page, so setFB_PAGE_IDto that Page alongside it (doctor/facebook_whoamireports it as the acting Page).
Grant only the permissions the packages you enable actually need
(full list below), and set FB_APP_SECRET
so appsecret_proof is attached to every call — that makes a stolen bare token
unusable on its own. Settings are read from an env file at the XDG/%APPDATA%
config path, or from real environment variables (which take precedence).
If you would rather not assemble the env file by hand, run
npx -y @ivanbaev/facebook-mcp setup-token (node build/index.js setup-token
from source) — the guided flow exchanges and classifies a
token and writes the env file for you (--page=<id>, --env-file=<path>,
--force to overwrite, --no-write/--dry-run to rehearse; pass the token via
FB_SETUP_TOKEN rather than on the command line; the exchange also needs
FB_APP_ID and FB_APP_SECRET). See
docs/runbooks/onboarding.md for the full walkthrough.
Then run doctor (npx -y @ivanbaev/facebook-mcp doctor) before anything else: it inspects the
token, reports type, expiry and granted scopes, and prints a per-package
usable / partial / blocked matrix so you find a missing permission before a tool
call does. It ends with a one-line verdict over everything it found.
The doctor is a report, so it exits 0 whatever it finds — a wrapper that runs it
for the text keeps working. Add --strict to make it a gate instead:
UNKNOWN outranks WARN on purpose: "nothing was established" must not hide
behind "degraded". A platform without POSIX permission bits raises no finding for
the credential file — there is no chmod to recommend there, and a warning
nothing can clear would make --strict permanently red on Windows.
node build/index.js --version (or -v) prints the server, Node and MCP SDK
versions on one line and exits — it needs no credential, so it still answers on
an install that cannot start:
facebook-mcp 0.7.0 (node v22.23.0, darwin arm64, sdk 1.30.0)
The bare server version is always the second field, so --version | awk '{print $2}' keeps working. The same three versions plus the pinned Graph API version
are reported by facebook_whoami in its server object (name, version,
apiVersion, sdkVersion). --help (or -h) prints the command summary and
a first-run recipe to stdout and exits 0, again without reading any credential;
setup-token --help and doctor --help do the same instead of running. doctor, setup-token,
--version and --help are the only arguments that exit on their own; anything
else starts the stdio server and waits on JSON-RPC. An argument doctor does not
recognise (say --stric) never gates, but the report names it — option names
only, never a value — so a mistyped --strict in CI is visible.
Environment variables
Provide at least one token; everything else is optional tuning. Variables marked Secret are never logged or returned by a tool.
¹ Provide at least one of FB_SYSTEM_TOKEN, FB_ACCESS_TOKEN or FB_PAGE_TOKEN.
A full, commented template lives in .env.example.
Permissions you need to grant
Grant only what the packages you actually enable require. The scopes below
are the ones this server asks for; node build/index.js doctor compares them
against what your token really has and prints a per-package usable / partial /
blocked matrix. business_management is deliberately not in this list — it
is a setup-only permission that should never ride on a runtime token.
Tools
Packages are the unit of exposure: you enable and disable whole packages with
FB_TOOL_PACKAGES, and FB_PACKAGES_READONLY drops a package's write tools
while keeping its reads.
All three package variables share one namespace of packages and profiles
(core, all, reader, publisher, moderator, ads), and a profile name
wins over a same-spelled package. That matters most in FB_PACKAGES_DENY:
FB_PACKAGES_DENY=core denies the six-package core profile, not the single
core package — and the core package is then forced back on anyway, since its
identity and rate-limit tools are how you diagnose the surface you just narrowed.
A name none of them recognize fails at startup rather than being ignored.
37 tools in 7 packages. 6 packages are on by default; the rest are opt-in via FB_TOOL_PACKAGES. Tier read never mutates. reversible writes are gated by FB_WRITE_MODE; irreversible and spend additionally require a per-call apply plus the plan_id of a preview you just ran, and are never unlocked by an environment variable alone.
How this compares
Point-in-time survey (2026-07-21); the full write-up is in
docs/analysis/02-market-and-positioning.md.
If you only need ads, use Meta's official server. It is free, needs no
developer app of your own, is maintained by the vendor, and covers more ads
surface than this project ever will. The ads package here exists for one
reason: composing ads with Pages, moderation and messaging under a single token
and config, with the same spend gating. That is why it is off by default —
ads are not the reason to install this.
The reason to install this is the Pages side: multi-Page, video and Reels upload, scheduled-post lifecycle, insights that tell you when a metric was renamed or retired, and comment/message moderation — in a typed, tested, MIT-licensed server that runs entirely on your machine.
Known limitations
Current, factual, and deliberately unflattering:
- No live Graph API verification. Nothing here has been proven against Meta's servers. Fixtures encode what the API is documented to do; reality gets a vote.
- Published, but brand new. 0.7.0 is on npm with provenance, the
.mcpbbundle is attached to the release and the MCP Registry listing is active — but no install path has been walked end-to-end by anyone except CI. See Client compatibility for what that means per client. - Clients that cannot prompt need an operator token.
irreversibleandspendwrites need a per-callapplyplus aplan_idand an out-of-band confirmation the model cannot supply itself. Where the client advertises the MCPelicitationcapability, the server asks the human operator through it; where it does not, the caller must pass theFB_CONFIRM_TOKENvalue as the tool'sconfirm_tokenargument. With neither route, those tools returnconfirmation_denied. - Upload progress depends on the client asking for it. Chunked video and
Reel uploads emit
notifications/progressper chunk — but only when the caller supplies aprogressTokenon the request, as the MCP spec requires. A client that does not send one gets no frames, and a long upload just looks slow. - Insights metrics move under you. Meta retired and renamed a large batch of Page/post metrics across three waves (2024-09, 2025-11 and 2026-06-15). The insights tools classify a requested metric and tell you when one is renamed or gone instead of silently returning nothing — but the underlying data loss is Meta's, and no wrapper can undo it.
- Messaging is bound by Meta's 24-hour window. A conversation that has gone quiet for more than 24 hours cannot be replied to without a message tag, and this server does not paper over that.
- Single maintainer, pre-1.0. Interfaces may change without a deprecation period until 1.0.
Not in scope
Things people reasonably expect from a "Facebook MCP server" that this one does not do. Listed so you can rule it out in thirty seconds instead of after an install:
- Personal profiles and Groups. Publishing to a personal timeline has no API, and the Groups API was discontinued in April 2024. This server operates Pages — nothing else.
- Instagram, Threads, WhatsApp. Different products, different permission sets. Possible sibling servers one day; not this one.
- Webhooks / real-time ingestion. A local stdio server has no public URL to receive callbacks on. Reads are polled, by design.
- Boosting an organic post (
/{page-id}/promotions). The organic→ads bridge is the most-requested crossover feature and is deliberately parked post-1.0 — it needs the ads package plus spend gating that has been thought through, not bolted on. - Page Stories. The publishing endpoints exist but are unverified; Stories are not promised for 1.0 and stay out until a live run proves them.
- Events, Live video, album/photo-library reads, Page profile editing, organic post targeting. Real endpoints, outside the post / video / Reels / scheduling / moderation surface this server commits to.
- Multi-tenant or hosted operation, serving other businesses' ad accounts. The design targets one operator, Standard Access, own assets, own machine. Anything else needs App Review and Business Verification, which is a different product.
The full reasoning is in
docs/analysis/01-goals-and-scope.md.
Your responsibilities as the operator
This server automates actions Meta attributes to you, under your app and your token. Automating them does not exempt you from the rules that govern them:
- Meta's Platform Terms and Developer Policies still apply — including the anti-spam and automated-behaviour rules. Bulk or repetitive posting, commenting, or messaging can get a Page restricted or an app disabled, and the fact that a model chose the timing is not a defence.
- Messenger automation must be disclosed. Meta expects users to know when they are talking to an automated system. If you wire the messaging tools to answer people, tell them.
- Content and consent are yours. The tools will publish whatever you point them at; deciding that you have the rights to it — and, for private replies and DMs, that the contact is expected — is not something a wrapper can do.
- This is not legal advice. It is the honest note that a capable automation tool comes with obligations, and this project would rather say so up front.
The security-side counterpart is in
SECURITY.md.
Roadmap
Work is tracked publicly on the facebook-mcp roadmap board and grouped into release milestones:
v1.0.0 ships without the ads package — deliberately. The Pages surface is
the product; ads is opt-in, off by default, and lands in 1.1.0. The code for it
is already in the working tree, which is exactly why the milestone is about
verification, not implementation: milestones close when a surface has been
verified live against Meta's servers and released, not when the code lands.
The binding version map is
docs/analysis/10-v1-release-definition.md.
The design behind each item is written up in advance in
docs/analysis/ — the roadmap is a consequence of
that corpus, not a replacement for it.
Security notes
- Three-host fence. Only
graph.facebook.com,graph-video.facebook.comandrupload.facebook.comare ever contacted — the allowlist is fixed in code and not user-configurable, so a redirected or mistyped host cannot silently receive a token. - Plan-and-apply write gating. Writes default to
plan(a non-mutating preview);applyexecutes. The exception ismoderation, which defaults toapplybecause hiding a comment is high-volume, reversible work — andFB_WRITE_MODEoverrides any package default outright, in either direction, so the mode is an operator decision rather than a per-tool promise. What the mode cannot touch: irreversible and spend actions always require an out-of-band confirmation, and anything that reaches a live audience (publishing a post or a Reel, publishing a scheduled post early, sending a DM) additionally requires aplan_idbound to a preview of that exact call. No env flag bypasses either. - Single-choke-point redaction. Secret values are stripped at one place before anything reaches logs, errors, tool results or the write journal; Page access tokens are derived to a boolean and their values never enter a payload.
- No telemetry, local-first. The server logs only to
stderr, collects nothing, and phones home nowhere. Thehttptransport binds loopback (127.0.0.1) only and fails closed withoutFB_HTTP_TOKEN.
See SECURITY.md for the full model and vulnerability reporting.
Documentation
- Design corpus — the full pre-implementation analysis lives in
docs/analysis/: goals & scope, market positioning, Graph API landscape, auth & security model, architecture, tool catalog, risks, roadmap, corner cases, the v1.0 release definition, and the parallel task-breakdown that drives development. - Runbooks — operational procedures in
docs/runbooks/. - Error catalog — every Graph error the server classifies, whether it is
retried, and what to do:
docs/error-catalog.md. - Documentation site — ivanbbaev.github.io/facebook-mcp.
The environment table, the package/tool tables and the permission tables above,
and the error catalog, are generated from the code by npm run metadata; CI
fails if they drift.
Support
Best-effort, single-maintainer support runs through GitHub — see SUPPORT.md for how to file bugs, feature requests and security reports.
This project is built and maintained in my own time. If it helps, a tip keeps it going:
- GitHub Sponsors — one-off or recurring, no platform fee.
- Ko-fi — quick one-off support (also accepts PayPal), the fallback for anyone without a GitHub account.
- Donatree — every donation method on one page, including local payment options.
Donating buys no priority support and no SLA — see SUPPORT.md. If money is not an option, starring the repository or filing a good bug report helps just as much.
Trademark
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.
License
MIT © 2026 Ivan Baev