Skip to content

Releases: SohrabZ/x-browser-mcp

v0.0.9

Choose a tag to compare

@SohrabZ SohrabZ released this 05 Aug 19:10
5f8247d

Install

go install github.com/SohrabZ/x-browser-mcp@latest

Added

  • read_mentions reads the posts that mention you, and read_notifications reads
    the rest: likes, follows, reposts and X's own recommendations. Both are also
    GET /api/v1/mentions and GET /api/v1/notifications, and read_x_url resolves
    either notification URL.
  • Notifications are their own shape rather than posts, because the page is not a
    timeline of them: on a real account one cell in eighteen held a post. A
    notification carries the words X wrote, the accounts it names, and when — and
    keeps X's aggregation, so a cell saying two accounts liked one post stays one
    notification with two actors. There is no post id or URL, because a cell links
    only to the accounts involved. kind is read from X's own words, so it is empty
    under a different interface language and for the cells X renders as nothing but a
    name and a time.
  • AGENTS.md records what this project has cost to learn: the build gates, the
    design decisions that are not inferable from the code, and the gotchas — X
    ignoring synthesized mouse events on its engagement controls, a reply composer
    discarding text typed before it is focused, rod's idle wait being a minimum
    rather than a maximum. CLAUDE.md imports it rather than repeating it.

Fixed

  • A failure told a caller the message of the error it arrived wrapped in rather
    than the one that was recognised, and a wrapper is where the internals are: a
    profile already in use carries the path of the lock that says so. The MCP tools
    handed that to a model, into the same context as untrusted post text; the HTTP
    write path did the same. Both now report the recognised message and nothing else,
    and an unrecognised failure says only that there was one, with the detail going
    to the log.
  • A link read_x_url could not read — not an x.com URL, no post id in it, a search
    with no query — was reported as a server fault instead of as the caller's
    mistake.
  • Liking, reposting or bookmarking a deleted or private post was reported as a
    server fault. It now says the post is not there, which is an answer rather than
    something to retry.
  • The message quoting a rejected URL back is bounded. It is the caller's own input,
    but one reader of these messages is a model, and an unbounded message is a way to
    fill a context window with a single bad request.

v0.0.8

Choose a tag to compare

@SohrabZ SohrabZ released this 05 Aug 15:02
8f51467

Install

go install github.com/SohrabZ/x-browser-mcp@latest

Added

  • -allowed-host names a further Host the server answers to. Only a bind
    beyond loopback that is reached by hostname needs it, now that every request has
    to address the server by name; a loopback deployment never does.

Changed

  • A read that fails says what failed. Anything that was not "login required" or
    "rate limited" collapsed to internal error, so a list id that does not exist
    read as a server fault. Now 400 for a request that was wrong, 404 for a
    post, account or list X had nothing on, 503 while a login or a write holds the
    profile, and 504 for a read that ran out of time. Only a fault you can do
    nothing about is still hidden.
  • Reaching the server by a hostname other than localhost needs that name passed
    to -allowed-host, including on a bind beyond loopback. See below.

Fixed

  • A page you visit in an ordinary browser could reach the loopback server. A
    domain the attacker controls, re-resolved to 127.0.0.1 after the page has
    loaded, gave it a route to the port, and the read tools would have answered.
    Every request is now checked before it reaches a handler, /mcp included: the
    Host header has to name this server, and any Origin is refused because it
    means a browser is calling. This applies to every bind — rebinding works against
    a LAN address too, and exposing the session to a network is not exposing it to
    every website.
  • A read that broke was reported as a read that found nothing. A failure while
    scraping or scrolling ended the collection loop, and an empty result then read as
    "the account or list may not exist" — sending the caller after a post that was
    probably there, and hiding the fault that stopped it being found.
  • A failure reported the error it arrived wrapped in rather than the one that was
    recognised, so a profile already in use answered with the path of the lock that
    said so.
  • The MCP server told clients a version that was written down, and had been three
    releases stale. It is read from the build.

v0.0.7

Choose a tag to compare

@SohrabZ SohrabZ released this 05 Aug 07:52
40b9742

Install

go install github.com/SohrabZ/x-browser-mcp@latest

Added

  • unbookmark_post removes a post from the bookmarks. Bookmarking was one-way:
    the only way back out was clicking the control by hand, and calling
    bookmark_post again does not do it, because the guard that stops a second
    save from silently removing one sees the bookmark and returns.
  • The write actions are served over the plain HTTP API as well as MCP:
    POST /api/v1/post, /reply, /like, /repost, /bookmark and
    /unbookmark. They take the same confirmation token, and like the MCP tools
    they are not registered at all without -allow-writes — a route that is not
    there cannot be reached by anything that gets hold of the port.

Changed

  • A write over HTTP reports why it failed rather than a generic message, since
    "like did not stick" is the answer. The status says whose problem it is: 403
    refused, 400 malformed, 412 sign-in needed, 429 write budget spent, and
    502 attempted but not applied by X.
  • Two write failure messages said X still showed the action as available, when
    all that had been checked was that the applied state never appeared.

v0.0.6

Choose a tag to compare

@SohrabZ SohrabZ released this 05 Aug 07:26
87f9ca8

Install

go install github.com/SohrabZ/x-browser-mcp@latest

Added

  • -write-timeout (default 2m) bounds one write. Writes shared
    -fetch-timeout before, which is sized for a read: a write starts its own
    browser, presses a control, waits for X's request and reloads the post to
    confirm it, so a read's budget could cut the confirmation off and report a
    timeout rather than a verdict.
  • TESTING.md records how to check that a write actually happened, and why the
    check has to be indirect.

Changed

  • An engagement action costs a request wait and a page reload — roughly 3-4s
    more. The alternative is what it replaces: a tool that reports ok and
    changes nothing.

Fixed

  • A write reported that the click succeeded, not that the action happened. X
    applies engagement over the network and swaps the control before its request
    completes, so a click the page accepted and never sent looked identical to one
    that worked. The request is waited on, and the post reloaded afterwards to
    confirm the action survived a round trip.
  • Anything that disturbed the page while that request was in flight cancelled
    it — tearing the browser down, navigating, or reloading in order to check. A
    check that read the page too eagerly caused the failure it was looking for.
  • Synthesized mouse events do nothing on X's engagement controls. A like
    dispatched through CDP was accepted by the page, raised no error, and made no
    request. Those controls are pressed with a DOM click.
  • A reply never reached the composer. The box stays collapsed until focused and
    text entered into an unfocused one is discarded, so the box looked filled, the
    submit button stayed disabled, and the click landed on a control that could
    not be pressed. It is focused first, and the submit control waited on.
  • An action could be applied to the wrong post. A permalink renders the post's
    ancestors, its replies and any quoted post alongside it, each with a like,
    repost and bookmark row of its own, and the checks matched the whole document
    — so one reply you had already liked was enough for a like to report success
    without touching the post asked for.
  • Bookmark and repost had no confirmation selector, so they could only ever
    report the click. Both have one.
  • Closing a write browser raised Chrome's "Leave site?" prompt. The page-level
    close runs beforeunload hooks and X registers one on its composer, so
    teardown blocked on a dialog nobody was there to answer, holding the profile
    until the shutdown wait gave up. Tabs are discarded at the target level.
  • Each read leaked a page for the life of the process, because discarding a tab
    at the target level skips the cleanup the page-level close does.
  • A post id of anything but digits could not match a status link, so it fell
    back to the first post on the page rather than failing. It is rejected before
    a browser is opened.

v0.0.5

Choose a tag to compare

@SohrabZ SohrabZ released this 05 Aug 06:01
277cf4d

Install

go install github.com/SohrabZ/x-browser-mcp@latest

Added

  • Reads share one warm browser instead of launching and quitting Chrome for
    every request. Median uncached read: 3.58s → 2.02s.
  • -browser-idle (default 3m) sets how long an unused browser stays warm.
    0 restores a browser per read.

Changed

  • Reads queue while a sign-in window or a write holds the profile, rather than
    failing with a profile-in-use error.

Fixed

  • A successful write now clears the read cache. Posting or liking was invisible
    to any read made in the previous five minutes.
  • A Chrome that was killed rather than quit left a lock behind that failed every
    later read until the server was restarted. A lock whose owning process is gone
    is now recognised as stale.

v0.0.4

Choose a tag to compare

@SohrabZ SohrabZ released this 05 Aug 02:31
24dfb86

Install

go install github.com/SohrabZ/x-browser-mcp@latest

Added

  • Reads X Articles — long-form posts carry a title and their body as text.
    These render nothing under tweetText, so they previously came back empty.
  • Pacing is configurable: -read-interval, -read-window, -read-max,
    -write-interval, -write-jitter, -write-window, -write-max.

Changed

  • Read budget raised to 30 per 10 minutes, 3s apart. Reads are not engagement,
    and a limiter that ordinary use trips is not a safeguard. Cached reads still
    cost nothing.
  • Writes are paced like a person rather than an agent: at least 45s apart plus
    a random extra delay, and at most 6 an hour. Engagement arriving at a fixed
    cadence is a signature on its own.

v0.0.3

Choose a tag to compare

@SohrabZ SohrabZ released this 05 Aug 02:05
98d2bf7

Install

go install github.com/SohrabZ/x-browser-mcp@v0.0.3

Added

  • read_x_url — pass any x.com link and it resolves to the right surface: a
    post reads its thread, a profile reads that account, and list, bookmarks,
    home and search URLs read their timelines. Accepts links with the ?s=
    tracking parameter.
  • Posts now carry their attached images, with alt text where X provides it.
  • TESTING.md — automated gates plus the manual browser, agent and
    write-gating passes.
  • SECURITY.md — what the server exposes and how to report issues.

Fixed

  • Image-only posts were scraped and then discarded, because post validation
    required non-empty text. A visual self-thread of ten image replies read as a
    root post with no replies at all.

v0.0.2

Choose a tag to compare

@SohrabZ SohrabZ released this 05 Aug 01:50
af019ca

Browser-backed MCP server for X. Reads and posts from your own logged-in
Chrome profile — no X API key, no developer account, no per-request billing.

Install

go install github.com/SohrabZ/x-browser-mcp@v0.0.2
x-browser-mcp
curl -X POST http://127.0.0.1:18110/api/v1/login/start

Sign in once in the window that opens, then fully quit it.

Read tools

read_x_url · check_login_status · start_login · read_home_timeline ·
search_x · read_user_posts · read_thread · read_bookmarks · read_list

read_x_url takes any x.com link and resolves it — a post reads its thread, a
profile reads that account, and list, bookmarks, home and search URLs read their
timelines. It accepts what people actually copy, including the ?s= tracking
parameter on share links.

Write tools

post_to_x · reply_to_post · like_post · repost_post · bookmark_post

Disabled unless started with -allow-writes. When disabled they are not
registered at all, so a connected model cannot see or call them. When enabled,
each requires a confirmation token printed to the operator's terminal at
startup — the read tools pull attacker-authored post text into the same context
that can act on your account, and text scraped from a web page cannot supply a
token it has never seen. Writes also get their own tighter budget, an
append-only audit log, and nothing destructive: no delete, unfollow, block or DM.

Notes

  • Binds to 127.0.0.1 by default. The API is unauthenticated; keep it there.
  • Session state lives in ~/.x-browser-mcp/ at mode 0700.
  • Reads are paced (15s apart, 8 per 10 minutes) to avoid tripping X.
  • On macOS, do not run this from a launchd agent: Chrome cannot reach the
    login Keychain there and will destroy the saved session on every run.

Requires Go 1.25+ and Google Chrome.