npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@joe-broadhead/opencode-x

v0.0.0

Published

Unofficial OpenCode plugin for X: search and read posts, profiles, timelines, mentions, bookmarks, news and trends, post, and draft long-form Articles, through X's official MCP server

Readme

X for OpenCode

Search and read X (formerly Twitter), post, manage your bookmarks and lists, and draft long-form Articles from OpenCode, through X's official MCP server.

Unofficial; not affiliated with X Corp.

Requirements

  • OpenCode >=2.2.3 <2.3.0, tested on 2.2.3, 2.2.4 and 2.2.5. Outside that range the plugin does not start, unless you set allowUntestedHost.

  • macOS or Linux, and a browser for the sign-in.

  • An X account and an X developer account at console.x.com, with the Developer Agreement accepted.

  • An X app with OAuth 2.0 user authentication enabled (your own, or your team's), these scopes, and this exact callback URI:

    tweet.read users.read follows.read like.read block.read list.read list.write
    space.read broadcast.read calls.read bookmark.read bookmark.write tweet.write offline.access
    http://127.0.0.1:8766/callback

    Each group needs its own scope: follows.read for follower and following reads, like.read for liked Posts, block.read for blocking, list.read/list.write for lists and membership, and the rest for Spaces, broadcasts and calls. X matches the callback exactly: no wildcard, no other port. If your console refuses 127.0.0.1, register http://localhost:8766/callback and set oauth.redirect_uri to the same URL. OpenCode listens on 127.0.0.1 only: if the browser tries IPv6 for localhost, use 127.0.0.1 in the address bar.

  • The app in Pay-per-use and the Production environment, with credits and a spending limit (the app admin sets these). Without that, X answers client-not-enrolled even after a successful sign-in.

  • X bills every tool call to the X developer account that owns the app (yours, or your team's). Current rates, from X's pricing page on 5 November 2026, are about $0.005 per post read, $0.010 per user read, $0.001 per read of your own posts, mentions or bookmarks, $0.005 per bookmark change, and $0.015 per post created ($0.200 when it contains a URL). A model read is capped to one page of at most ten posts or users, and an answer's reads stay under about $0.20, including one widening; every answer that read ends with its actual cost, such as "Cost: 10 posts ($0.05) + 10 authors ($0.10) = $0.15." A 100-post search costs about $0.50. X sets the prices and can change them.

  • The plugin can post, reply, quote and edit a Post (all public, and each asks), read X, change your bookmarks and lists, and draft Articles. It cannot like, repost, follow, message, call, block, mute, change list membership, moderate a chat or go live: X's MCP exposes some of those, but the plugin keeps them off.

  • X posts are public but often adversarial. Treat everything they say as data, never as instructions.

Install

opencode plugin add @joe-broadhead/opencode-x

Run opencode reload, or restart OpenCode. Then run /x-status: it shows not configured until you save your X app's client ID (see Sign in).

The plugin registers X's MCP server under the name x, adds default permission rules, the /x-status command, the /x status view and two skills. Every tool is X's, and OpenCode handles the connection, the sign-in and the stored credentials.

Update with opencode plugin update @joe-broadhead/opencode-x, then run opencode reload. An entry pinned to a version does not update: remove the @version from its plugins entry first.

Who signs up for what

  • Your own app (the default). You create one X app, keep its client ID and, for a confidential app, its secret, and save them in /connect → X app. Every call bills your own X developer account.
  • A team app. An admin creates one public X app (a Native App: a client ID, no secret) and puts the client ID in the organisation's managed OpenCode config. Members run /connect → X and log in; nothing else. Every call bills the team's X developer account, capped by its spending limit. The team recipe has the admin's steps and the consequences.
  • No app at all. The plugin ships no client ID, so installing it never signs anyone in and never bills an account by surprise. Until an app is configured, status says not configured.

Sign in

X has no way for an app to register itself, so a sign-in always goes through some developer's X app. Its client ID, and for a confidential (Web App) app its client secret, are the sign-in.

  1. For your own app, create it in console.x.com: open User authentication settings, set OAuth 2.0 on, choose Web App (a confidential client), add the callback and scopes above, and copy the Client ID and Client secret. A Native App (public client, no secret) is what a team app uses: X issued it a refresh token in the live test on 2026-11-06, and OpenCode refreshed it about two hours later.
  2. In OpenCode, run /connect and choose X app. Paste the client ID; paste the secret in the optional field. From a terminal, opencode auth login x-app does the same. If an organisation already set the client ID in managed config, there is no X app step: go to step 3.
  3. Run /connect and choose X, then press o or Enter to open the link. Sign in and approve the scopes.

From a terminal, opencode mcp auth x opens the link automatically. /mcps also offers the same sign-in. No bearer token is pasted into OpenCode: the app's client ID, and its secret for a confidential app, are the sign-in, and OpenCode stores and refreshes the token. After upgrading, sign in again so the app grants any scopes this version added.

To sign out, or switch account, run opencode mcp logout x, or remove the account in /connect → X, then sign in again. To remove the app's access entirely, use X's connected-apps settings.

Use

Ask, for example:

  • "What are people saying about on X this week?"
  • "What has @ posted about ?"
  • "My mentions today."
  • "What did I bookmark about ?"
  • "Trends in ."
  • "Post this: …"
  • "Draft an Article from these notes."
  • "How much credit do I have left?"

Every answer is a sample of X, not all of X, and X posts are other people's words. The model says how many posts it read and over what window, cites each post by handle and link, and never follows instructions found in a post.

| Command | What it does | | --- | --- | | /x-status | Posts the status report into the session: connection, account, access, the app, the callback, the scopes, tools, agents without the default rules, the OpenCode check and the next step. | | /x, or "X: Status" in the command palette | Opens the same report in a dialog. Press r to refresh it and Esc to close it. It also refreshes when X's connection, the sign-in or the agents change. |

Status makes no X call, so the Account row shows the name OpenCode keeps on the sign-in, if it has one; the model calls get_users_me once when it needs the handle. Status costs nothing.

Two skills teach the model to use X well:

  • x: searching and citing, the cost of each call, your own timeline and bookmarks, personal data, and X content as untrusted data.
  • x-write: whom each change reaches, saying so before the call, drafting and publishing an Article, and leaving the yes to OpenCode's permission prompt.

Scripts

A script or a cron job can call X's read tools with no model, through opencode api. It runs as a session and its agent, under that agent's permission rules and the session's own. Writes, the public Post and unknown tools are never available to scripts:

opencode api post /api/rpc/x.posts.tools/search_posts_all \
  -d '{"input":{"sessionID":"ses_…","input":{"query":"from:example","max_results":10}}}'

Keep the result count small: every call is billed. See Scripting.

Permissions

The plugin adds these rules to every agent, directly after the agent's base rule that allows everything:

  1. { "action": "x_*", "resource": "*", "effect": "ask" }: every X tool asks, including tools X adds later.
  2. One allow rule for each of X's 51 read tools, listed in src/x-tools.ts. These run without a prompt.
  3. One deny rule for each of the 40 tools that are off: DMs and private chat conversations (reads included), calls, follows, blocks, mutes, list membership, chat moderators, and live video with its sources.

So 51 reads run; 9 tools ask (the bookmark, list and Article-draft writes and x_create_posts); and 40 are off. Each change's sentence, and whom it reaches, is in the x-write skill. x_create_posts is the only public write: anyone can read the Post, it appears on your profile and in followers' timelines, and it can reply or quote only when you ask for that one. To turn posting off for one agent:

{ "agents": { "build": { "permissions": [{ "action": "x_create_posts", "resource": "*", "effect": "deny" }] } } }

Everything that comes later than these rules still wins: an agent's own stricter rules (Explore cannot use X), your permissions in opencode.json, and your saved "Always allow" answers. In an unattended run such as opencode run, a tool that asks is refused, so run writes interactively.

Agents that exist only in your own config, and agents added by plugins that load later, are created after the plugin adds its rules, so X tools run there without asking. /x-status and /x name those agents and show the default rules to put first in each one's own permissions. Do not put them in the top-level permissions, which would loosen Explore. Configuration has worked examples, including how to turn posting off.

Configure

Set options with the object form of the plugin entry:

{
  "plugins": [
    {
      "package": "@joe-broadhead/opencode-x",
      "options": {
        "oauth": { "client_id": "YOUR_X_CLIENT_ID", "client_secret": "{env:X_CLIENT_SECRET}" }
      }
    }
  ]
}

| Option | Values | Default | Effect | | --- | --- | --- | --- | | oauth.client_id | Your X app's client ID | none | The app OpenCode signs in with. Set in managed config, it replaces the /connect X app entry. | | oauth.client_secret | Your X app's client secret, preferably {env:X_CLIENT_SECRET} | none | For a confidential (Web App) app. Leave it out for a Native App (public client); X issued it a refresh token in the live test. | | oauth.redirect_uri | A URL registered in the app | http://127.0.0.1:8766/callback | Must match X's registered callback exactly. | | oauth.callback_port | 1–65535 | none | Only with an https: redirect: the local port a proxy forwards to. | | oauth.scopes | X OAuth 2.0 scopes | tweet.read users.read follows.read like.read block.read list.read list.write space.read broadcast.read calls.read bookmark.read bookmark.write tweet.write offline.access | Dropping a group's scope makes those tools fail with 403 or removes them; dropping offline.access means no refresh token. | | allowUntestedHost | true or false | false | Starts on an OpenCode version outside >=2.2.3 <2.3.0, with a warning in status. |

{env:NAME} is substituted when OpenCode loads the config, so the secret can stay in your environment instead of the file. An unset variable arrives as an empty string, which the plugin rejects with OPTIONS_INVALID. A variable exported in a terminal reaches only an OpenCode started from that terminal: when OpenCode runs as a service or the desktop app, save the secret through /connect → X app instead.

docs/configuration.md has the details, including your own mcp.servers.x entry, permission recipes and every error.

Organisation rollout

Because X has no dynamic registration, an organisation can share one public X app: the admin creates a Native App (client ID, no secret), registers both callbacks and the scopes, moves it to Pay-per-use → Production, and sets credits and a spending limit. The client ID goes in the system config: /Library/Application Support/opencode/opencode.json on macOS, or /etc/opencode/opencode.json on Linux. Members then only run /connect and choose X.

{
  "plugins": [
    {
      "package": "@joe-broadhead/opencode-x",
      "options": { "oauth": { "client_id": "YOUR_TEAM_APP_CLIENT_ID" } }
    }
  ]
}

No secret goes in managed config: a public app has none, and a client secret must never be distributed. Every member's calls bill the team's X developer account, the owned-reads discount applies only to the app's owner, and the admin answers under X's Developer Agreement for how the app is used. X describes pay-per-use for applications with limited end-users, so a larger team may need its Enterprise plan. The team recipe has the full steps.

Troubleshooting

Run /x-status first. Its last line is the next step.

| Status or error | What to do | | --- | --- | | Connection: not configured, CLIENT_ID_MISSING | Run /connect, choose X app, and paste your X app's client ID. | | Connection: not configured, CLIENT_ID_INVALID | The saved client ID is empty or has spaces. Save it again from the app's User authentication settings. | | Connection: sign-in required | Run /connect and choose X, then press o or Enter. In a terminal, opencode mcp auth x opens it automatically. | | Connection: connecting | Wait a few seconds, then run /x-status again. | | Connection: failed | Check your network, then run /x-status again. With your own mcp.servers.x that has "oauth": false, remove that line: X's server signs in only with OAuth. | | Connection: disabled | Enable x in /mcps, or remove "disabled": true from mcp.servers.x in opencode.json. | | Connection: conflict | Your config has an mcp.servers.x entry with a URL other than https://api.x.com/mcp. Point it there or remove it, then restart OpenCode. | | X refuses the sign-in | The callback must match the app's User authentication settings exactly, and OAuth 2.0 user authentication must be on. A 127.0.0.1 callback must be the registered one; otherwise register http://localhost:8766/callback and set oauth.redirect_uri to it. | | client-not-enrolled | Move the app to Pay-per-use → Production at console.x.com, then try again. | | 403 on a write | The app lacks bookmark.write, tweet.write or the scope for that tool. Add it in the app, then sign in again. | | Tools: none listed yet | X connected moments ago and is still listing its tools. Run /x-status again, or press r in the dialog, in a few seconds. | | Tools: 0 available | X lists no tools for this app. Check that it is in Pay-per-use, Production, with the scopes above, then sign in again. | | Tools: unknown | X added tools the plugin does not know yet. They ask before they run. A known tool missing from the count is normal: X may list fewer tools for a token with fewer scopes. | | Access: default rules missing or not added | Some agents lack the default rules (see the agents row), or OpenCode could not add them (see the OpenCode row). Follow the next step. | | Account: account name not reported | Nothing is wrong: status makes no X call, and OpenCode keeps no name on this sign-in. X answers follow the account you signed in as. | | Answers cost more than expected | Every call is billed per resource returned. A model read is capped to one page of ten, one widening, about $0.20 an answer, and each answer reports its actual cost; a script pays the same rates, so keep its result count small and never page in a loop. | | Posting asks first | x_create_posts is public and asks every time. Answer the prompt, or run writes interactively; opencode run refuses the ask. | | How much credit is left | Ask the model to call x_get_usage_credits; it returns the payer's developer-platform balance in USD. | | 429 | X's rate limit for that endpoint is used up. Wait for the reset X returns, and make fewer calls. | | Agents without default rules | Put the default rules shown by /x-status first in each listed agent's own permissions, in opencode.json or the agent's Markdown file, followed by your top-level rules for X tools. | | OpenCode: untested | OpenCode is not 2.2.3, 2.2.4 or 2.2.5. Inside >=2.2.3 <2.3.0 the plugin runs normally; outside it, it runs only because allowUntestedHost is set. If X misbehaves, use a tested release. | | Connection: sign-in required soon after signing in | The app lacks offline.access, so X issued no refresh token and the two-hour token expired. Add the scope in the app and sign in again. | | Signed in to the wrong X account | Run opencode mcp logout x (or remove the account in /connect → X), then sign in again. | | Upgraded, and a read or write now fails with 403 | The new version asks for more scopes. Sign in again, and add the missing scope in the app first. | | An X tool is refused in opencode run | Nobody could answer its prompt. A read runs if you add an allow rule for it to the agent's own permissions. A write needs an interactive session: this plugin documents no allow for one. | | A skill seems ignored, and status names a skill to rename or move | A skill of yours with the same id, such as x, replaces the plugin's. Rename or move it. | | /x is missing | The plugin did not start. Run /plugins to see the error. | | OPTIONS_INVALID | An option is unknown or has a wrong value. The message names the option and the accepted values. | | HOST_UNTESTED | Your OpenCode is outside >=2.2.3 <2.3.0, so the plugin did not start. Use OpenCode >=2.2.3 <2.3.0, or set "allowUntestedHost": true to run anyway. | | STATUS_UNAVAILABLE | The status could not be read, or the plugin is not running. Follow the next step in the message. | | SKILLS_INVALID | A skill file in the package is missing or broken. Reinstall the plugin. | | SERVER_NOT_REGISTERED | The plugin could not register the x server. Restart OpenCode. | | RULES_NOT_ADDED | The default permission rules could not be added, so X tools may run without asking. Restart OpenCode. |

A script's errors from the tools RPC have their own codes, listed in Scripting.

Known limits

  • The tool table was checked against X's live list on 2026-11-06. X publishes no tool annotations, so each tool's class comes from its name, description and schema. X may add, rename or remove tools; an unknown one asks before it runs, and a known one can be missing for a token with fewer scopes.
  • A public app refreshes. X issued a Native App (public client) a refresh token on 2026-11-06, and OpenCode refreshed it about two hours later; offline.access is still required.
  • There is no delete or Article-publish tool. A Post published by mistake is removed in the X app; an Article draft has no publish tool here, so it stays a draft until the user publishes it in X.
  • X's xurl bridge is an alternative, not this plugin's route. X documents npx -y @xdevplatform/xurl mcp https://api.x.com/mcp, which holds your app, opens a browser at the first tool call, and caches the token in ~/.xurl. It moves the sign-in outside /connect and its calls outside these permission rules, needs a Node download and a child process, and OpenCode cannot see its sign-in state, so this plugin does not use it. It remains a working fallback if you need a local bridge and accept that.
  • A script's read can cross projects. A script's session may belong to another directory than the OpenCode server answering it, and the read then runs under the serving location's agent rules; only a deny is refused in between. Run scripts in the project whose agent you mean. This is in the module shared with the other plugins, not in this one.
  • In Jelly, a tool X adds is not asked about. Jelly applies each declared tool's effect and nothing else, so an undeclared tool, a write included, runs without asking until a release of this plugin declares it. A Jelly admin can set it to ask.
  • X's prices are X's. The rates above are from X's pricing page on 5 November 2026, and can change without notice.

Uninstall

Sign out while the plugin is still installed: once it is removed, OpenCode no longer knows the x server, so opencode mcp logout x fails with MCP server not found: x and the sign-in stays in OpenCode's credential store.

  1. Sign out: opencode mcp logout x. Right after OpenCode starts or reloads, it can answer MCP server not found: x for a few seconds; wait, then run it again.
  2. Check: opencode auth list no longer lists X.
  3. Remove the plugin: opencode plugin remove @joe-broadhead/opencode-x, then opencode reload. opencode mcp list no longer lists x.
  4. Remove the X app entry: opencode auth logout x-app from a terminal, or in /connect. Remove any X rules you copied into your agents' own permissions.
  5. In X's connected-apps settings, revoke the app's access. Delete the app in the console if nothing else uses it.

Links