Skip to content
fb

HTTP and MCP

The same eighteen read operations over HTTP as NDJSON, or over stdio as MCP tools, generated from the operations rather than written twice.

The commands are not the program. Underneath there are eighteen read operations, and the CLI is one of three ways to reach them.

fb serve

fb serve --addr :8080
fb serving on :8080

Every read operation gets a route, records come back as NDJSON, one JSON object per line, flushed as they are produced. A long crawl is consumable while it runs rather than after it finishes.

curl 'localhost:8080/v1/page/100044561550831'
curl 'localhost:8080/v1/page?ref=nasa&about=true'
curl 'localhost:8080/v1/comments?ref=https://www.facebook.com/NASA/posts/1587860636042640/&limit=50'
curl 'localhost:8080/v1/edges?ref=nasa'

Positional arguments work either as the path tail or as a query parameter. The query form is the one to use for a reference containing a slash, which is most of them, because a URL in a path is a fight nobody wins.

The routes:

/v1/page        /v1/feed        /v1/photos      /v1/photo
/v1/post        /v1/comments    /v1/reactions   /v1/videos
/v1/video       /v1/playlists   /v1/events      /v1/event
/v1/event/suggested             /v1/group       /v1/group/feed
/v1/graph       /v1/edges       /v1/discover

Two more that are not operations:

curl localhost:8080/healthz
curl localhost:8080/v1/openapi.json
{"binary":"fb","status":"ok"}

openapi.json is an OpenAPI 3.1 document generated from the operations, not maintained beside them, so it cannot describe a parameter the code does not have. Point a client generator at it and the client is correct by construction.

Errors

An error before any record is written comes back as JSON with the status the exit code maps to:

HTTP/1.1 401 Unauthorized
Content-Type: application/json

{"error":"Facebook answered every request for profile doesnotexist000 with the log-in page: import a session with `fb auth import`","kind":4}

kind is the same number the CLI would have exited with, so a client can switch on it without parsing English.

Once the first record has been written the status is already 200 and there is no taking it back, which is the honest cost of streaming.

Reads only, unless you say otherwise

fb serve exposes read operations and nothing else by default. --allow-writes opens the rest, and there is nothing in fb today that writes to Facebook, so in practice it is a switch waiting for a feature.

There is no authentication on the listener. Bind it to localhost or put something in front of it. Anyone who can reach the port can spend your rate limit and, if you have imported a session, read as you.

fb mcp

fb mcp

The same eighteen operations as MCP tools over stdio.

{"protocolVersion":"2024-11-05","serverInfo":{"name":"fb","version":"0.3.0"},
 "capabilities":{"tools":{}},
 "instructions":"A fast, read-only command line for Facebook"}

Each tool carries the operation's own summary:

page        Read a Page or profile whole
feed        Read a profile's timeline (one post signed out)
photos      Read a profile's photo tab
videos      Read a profile's video tab, grid and shows together
playlists   Read the shows a profile groups its videos into
events      Read a profile's events tab

Wiring it into Claude Code:

claude mcp add fb -- fb mcp

Or by hand, in any MCP client's config:

{"mcpServers": {"fb": {"command": "fb", "args": ["mcp"]}}}

The schemas are generated from the same parameter definitions the flags come from, so a tool call and a command line take exactly the same arguments under the same names.

Why this is not three implementations

An operation declares its parameters, its output schema and what it does, once. The CLI turns that into flags and a help page, fb serve turns it into a route and an OpenAPI entry, fb mcp turns it into a tool and a JSON schema.

Which means a new read command arrives on all three surfaces at once, and the three cannot describe the same operation differently, because there is only one description. A test asserts every operation has a summary and a schema, so nothing reaches the tool list as an unlabelled page2.