facebook-mcp
Facebook automation MCP server for post lookup, people/page search, and Messenger sending.
Get a user's latest post publish time (UTC) by username.
List a user's recent posts with text, UTC publish time, and permalink.
Search Facebook people by keyword (requires login).
Search Facebook pages by keyword (requires login).
Send a Messenger message to a user or page (requires authenticated account/browser).
Optionally select a credential account per call or per process.
Provides tools for interacting with Facebook profiles, posts, and search: retrieve a user's latest post publish time, fetch recent posts with text/time/permalink, and search for people or pages by keyword.
Enables sending messages through Facebook Messenger by driving the real Messenger web UI via browser automation.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@facebook-mcp what time did exampleuser last post on Facebook?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
facebook-mcp
Facebook automation exposed as an MCP (Model Context Protocol) server.
Capabilities:
get_user_latest_post_time— a user's latest post publish time (e.g.exampleuser)get_user_recent_posts— recent posts with text, time and permalinksearch_people— keyword search for people (/search/people?q=...)search_pages— keyword search for pages (/search/pages?q=...)send_message— send a Messenger message
Design docs: ARCHITECTURE.en.md (English) · ARCHITECTURE.md (中文).
Status
M0 scaffolding — done
M1 transport (curl_cffi + proxy + retry/throttle) — done
M2 session + routes + profile client +
get_user_latest_post_time— done, verified liveM3 search (
search_people/search_pages) — done, verified live over pure HTTPM4 CDP transport (cookie bootstrap + packet capture) — done
M5
send_message— done, verified live (browser path)
All five tools work against the live site.
How send_message works
Messages travel over wss://gateway.facebook.com/ws/lightspeed (JSON tasks in a
16-byte binary frame) — not over HTTP and not via a service worker. Connect with
the account's cookies, send task 46, and the server answers
replaceOptimsiticMessage with a real message_id.
A Couldn't send verdict is ambiguous: the recipient may have messaging turned
off (common for pages), the thread type may need the DOM path, or the thread may
need extra context. The client reports it as-is; auto mode then tries the UI
once to tell those apart, since the server has already stated nothing was sent.
FB_MESSAGE_TRANSPORT=lightspeed # protocol only (no browser, no fallback)
FB_MESSAGE_TRANSPORT=auto # protocol first, UI fallback (default)
FB_MESSAGE_TRANSPORT=ui # legacy CDP-driven DOM pathThe UI path remains as a fallback (and is the only way to start a conversation),
but sending no longer needs a browser. See ARCHITECTURE.en.md §8.3 for the frame
layout and what the gateway's verdict does and does not prove.
Related MCP server: Facebook MCP Server
Install
/opt/homebrew/bin/python3.11 -m venv .venv
.venv/bin/pip install -e ".[dev]"Requires mcp>=1.2,<2 (official MCP Python SDK, mcp.server.fastmcp).
⚠️ This project intentionally pins
mcp<2.
Configure
cp .env.example .env
# fill in FB_COOKIE / FB_COOKIE_FILE for authenticated operationsA proxy is often required to reach Facebook. The transport resolves it automatically:
FB_PROXY→HTTPS_PROXYenv → macOS system proxy.
Tools
Tool | Needs login | Description |
| no | Latest post publish time (UTC) for a username |
| no | Recent posts (text, time, permalink) |
| yes | Keyword search for people |
| yes | Keyword search for pages |
| yes | Sends over the Lightspeed WebSocket with the account's cookies; the UI fallback needs a browser logged in as the same account. |
Credentials: ~/.facebook-mcp/
Cookies live outside the repository, one directory per Facebook account:
~/.facebook-mcp/ # 0700
└── accounts/
└── default/ # 0700
├── cookies.txt # 0600, Netscape format
└── account.json # 0600, metadata + per-account overridesWhich tools need credentials — measured in an incognito Chrome with zero
Facebook cookies (ARCHITECTURE.en.md §8.1):
Tool | Needs cookies | Evidence when logged out |
| no | 200 + full timeline |
| yes | 404 on the search page; GraphQL replies "Rate limit exceeded" |
| yes for Lightspeed; a same-identity browser for the UI fallback | Lightspeed authenticates with the account jar; the UI path uses Chrome's login, so both must be the same |
Populate an account
# A. Export from a logged-in Chrome (one time)
bash tools/launch_chrome_debug.sh
.venv/bin/python tools/export_cookies.py --account default
# B. Or decrypt straight from your Chrome profile
.venv/bin/python tools/import_cookies.py --account default
# Inspect
.venv/bin/python tools/export_cookies.py --listThen run without a browser:
FB_TRANSPORT_MODE=http .venv/bin/facebook-mcp # uses accounts/defaultOr let the server borrow your live Chrome once: cookies are bootstrapped on the first authenticated call and written into the account store, so later runs are pure HTTP.
FB_TRANSPORT_MODE=auto .venv/bin/facebook-mcpSearch never goes through the browser once cookies exist: require_auth()
ensures a jar, and the request then goes over curl_cffi. Clients set no
transport hint at all — the browser is reached only through cdp_from().
Multiple accounts
Per process (fully isolated, recommended):
{
"mcpServers": {
"facebook-default": {
"command": ".../.venv/bin/facebook-mcp",
"env": { "FB_ACCOUNT": "default", "FB_TRANSPORT_MODE": "http" }
},
"facebook-work": {
"command": ".../.venv/bin/facebook-mcp",
"env": { "FB_ACCOUNT": "work", "FB_TRANSPORT_MODE": "http" }
}
}
}Per call — every tool takes an optional account argument:
search_pages(q="pest control", account="work")
send_message(recipient="exampleuser", text="hi", account="work")Each account can bind its own browser via
cdp_urlin itsaccount.json(e.g.{"cdp_url": "http://127.0.0.1:9223"}), so two accounts never fight over the same Chrome tab. The same file holds that account's live GraphQLdoc_ids under"doc_ids", so a Facebook build change is picked up per account without editing the repository.
Maintenance: Facebook ships a new frontend build
GraphQL doc_ids and variables templates change. Re-capture them:
.venv/bin/python tools/sniff.py --navigate "https://www.facebook.com/search/pages?q=pest%20control" \
--seconds 20 --save-templates--save-templates records the fresh doc_ids in
~/.facebook-mcp/accounts/<name>/account.json and promotes the search
variables template into the package when its shape changed.
doc_idrotation only → no source change, no commit: the registry value inroutes/graphql.pyis just the bootstrap default used before the first capture.variablesshape changed → the promoted template is a tracked file (src/facebook_mcp/routes/templates/search_results.json), so that case does produce a diff and belongs in a commit.
The override is read when the server starts, so restart the MCP server to pick up a refreshed
doc_id.
Helpers
Script | Purpose |
| Ad-hoc live check of a service |
| Capture GraphQL |
| Export Chrome cookies into |
| Decrypt cookies straight from the Chrome profile |
| Launch Chrome with DevTools + dedicated profile |
| Verify the layered import rules (CI-friendly) |
Development
make install # .venv (Python 3.11) + dev dependencies
make hooks # commit-msg hook + commit template
make check # main local checks: lint, format, layers, docs, tests, smokeTarget | What it does |
|
|
|
|
|
|
| verifies the layered import rules + no cycles |
| asserts ARCHITECTURE.md and ARCHITECTURE.en.md stay in sync |
|
|
| MCP stdio handshake + |
| validate commit messages on the current branch |
| per-account Chrome: |
| lint + fmt-check + layers + docs + test + smoke (CI also runs the commit-message check, the credential guard and the wheel build) |
CI (.github/workflows/ci.yml)
Job | Runs |
| ruff lint + format, |
| pytest on Python 3.11/3.12/3.13 (Ubuntu) and 3.11 (macOS) + MCP smoke |
|
|
Tests are fully offline — a local http.server and a mock DevTools
(WebSocket) server stand in for Facebook and Chrome, so CI needs no network or
credentials.
Verify against the live site
.venv/bin/python tools/probe.py profile exampleuser
.venv/bin/python tools/probe.py search pages "pest control"Run
.venv/bin/facebook-mcp # stdio transport (default)
# or
.venv/bin/python -m facebook_mcpHTTP transports are available too (the SDK supports them):
# streamable-http on 127.0.0.1:8000/mcp
.venv/bin/python -c "from facebook_mcp.server import mcp; mcp.run('streamable-http')"MCP client config (example)
{
"mcpServers": {
"facebook": {
"command": "/Users/jin/Documents/GitHub/facebook-mcp/.venv/bin/facebook-mcp",
"env": { "FB_COOKIE_FILE": "/path/to/cookies.txt" }
}
}
}Commit convention
Conventional Commits, enforced locally by
make hooks and in CI by tools/commit_msg.py:
feat(search): paginate results with page_info.end_cursor
fix(accounts): keep cookies out of the repository
docs: explain why send_message needs a browserTypes: feat fix docs test refactor perf build ci chore revert.
Scopes: core transport session routes clients services server accounts cdp http proxy profile post search message tools tests ci docs deps release.
Subject: imperative, lowercase, no trailing period, header ≤ 72 chars.
See CONTRIBUTING.md for the full rules and examples.
Pushing over SSH? Use a repo-scoped deploy key (
~/.ssh/github-action-key-*,0600, never committed, one repo per key). CI needs none:actions/checkoutuses the built-inGITHUB_TOKEN.
License
MIT © facebook-mcp contributors
Not affiliated with, endorsed by, or supported by Meta/Facebook. Use it only with accounts you own or are authorized to automate, and respect Facebook's terms of service and local anti-spam / privacy law.
All usernames, page names and ids in this repository are placeholders (
exampleuser,100000000000001, …). See CONTRIBUTING.md → Documentation privacy.
Available Tools
5 toolsget_user_latest_post_timeA
Get the publish time (UTC) of a user's latest post by username. Example username: 'exampleuser' (no URL, no leading '@'). Optional account name selecting the credential set under ~/.facebook-mcp/accounts//. Defaults to the configured account.
Inputs: username (required), account. Returns: Result of the ``get_user_latest_post_time`` tool.
get_user_recent_postsB
List a user's recent posts (newest first), including text, UTC publish time and permalink. Optional account name selecting the credential set under ~/.facebook-mcp/accounts//. Defaults to the configured account.
Inputs: username (required), limit, account. Returns: result
search_pagesB
Search Facebook pages by keyword, e.g. 'pest control'. Requires a logged-in session. Optional account name selecting the credential set under ~/.facebook-mcp/accounts//. Defaults to the configured account.
Inputs: q (required), limit, account. Returns: result
search_peopleA
Search Facebook people by keyword, e.g. 'pest control'. Requires a logged-in session. Optional account name selecting the credential set under ~/.facebook-mcp/accounts//. Defaults to the configured account.
Inputs: q (required), limit, account. Returns: result
send_messageA
Send a Messenger message to a user or page. 'recipient' accepts a numeric uid, a username/vanity, or a profile URL. Requires a logged-in browser (FB_TRANSPORT_MODE=auto or cdp). Optional account name selecting the credential set under ~/.facebook-mcp/accounts//. Defaults to the configured account.
Inputs: text (required), recipient (required), account. Returns: Result of the ``send_message`` tool.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 5 tools
search_people, search_pages, and send_message are clearly distinct, and the two retrieval tools target different scopes. However, get_user_latest_post_time is essentially a subset of get_user_recent_posts (which returns newest-first posts with publish times), so an agent may struggle to justify choosing one over the other.
All five tools follow a consistent snake_case verb_noun pattern (get_user_*, search_*, send_message). No mixed conventions or vague verbs; the naming is predictable and self-explanatory.
Five tools is a reasonable, well-scoped surface for a Facebook read/search/messaging server. It is on the lean side but each tool earns its place, with no filler.
The surface covers reading posts, searching people/pages, and sending messages, but lacks any content-creation (posting, commenting, liking) or message/conversation retrieval, leaving notable gaps for a Facebook interaction server.
Maintenance
Related MCP Connectors
Create, schedule, and publish social posts, manage accounts, and read analytics as MCP tools.
OAuth-enabled MCP server that extracts structured data from public social media posts.
Discover MCP servers and A2A agents; verify, message, post, follow, react, and receive webhooks.
Search people and companies, enrich profiles, and find email and phone over MCP.
Related MCP Servers
- -licenseNot gradedqualityNot gradedmaintenanceEnables posting text messages to Facebook business pages through MCP. Supports OAuth authentication and manual connection methods for managing Facebook page content.-
- FlicenseAqualityDmaintenanceA Model Context Protocol (MCP) server implementation that provides Facebook Page interaction and management capabilities. This server enables automated posting, comment moderation, and content retrieval.777-
- AlicenseBqualityCmaintenanceEnables MCP-based interaction with the Meta Graph API for Facebook Pages and linked Instagram Business accounts, including posting, commenting, and reading page details.610 npmMIT
- AlicenseAqualityBmaintenanceEnables MCP clients to query public Facebook pages and profiles by handle, retrieving like counts, contact details, owner info, and recent posts with reactions, comments, and shares as structured JSON.163 npm26 PyPI7MIT