Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion third_party/x/.cursor-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "x",
"displayName": "X",
"version": "2.1.0",
"version": "2.2.0",
"minClientVersions": {
"cursor": "3.13.0"
},
Expand Down
7 changes: 7 additions & 0 deletions third_party/x/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

All notable changes to this plugin will be documented here.

## 2.2.0 — Credit balance on connect

- Agents fetch `GET /2/usage/credits` on connect, at session start when X calls are needed, and when the user asks what they can do. The capabilities message now includes the dollar balance and 2–3 ideas that fit it.
- Added budget-tier workflows (from $0 through $1,000+) so suggestions scale with remaining credits. If a job would exceed the balance, offer a cheaper alternative and send the user to https://console.x.com.
- Never tell the user to buy credits until after `get_usage_credits` returns. The on-connect message states the balance; console.x.com is only mentioned when the balance is ~$0 or a job would not fit.
- Out of credits (error 3): say the quoted line, then only free lookups — not a cheaper paid alternative. A successful `{credits}` of ~$0 is not error 3.

## 2.1.0 — X MCP guide skill

- Added the X MCP guide skill: tells agents how to handle sign-in, onboarding, and out-of-credits errors with simple user-facing messages, plus session-start, search, pagination, and cost-aware workflow rules.
Expand Down
107 changes: 85 additions & 22 deletions third_party/x/skills/x-api-mcp-guide/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,53 @@ name: X MCP guide
description: >-
ALWAYS read this when a user connects the X plugin or any X MCP, before using
any X connection, and again on any X error. Do not call an X tool until this
file has been read in the current turn. On first connect, send the user the
capabilities message defined here. Estimate the cost of every X call before
making it and confirm with the user before anything expensive.
file has been read in the current turn. On first connect, fetch get_usage_credits
BEFORE any user-facing text, then send the capabilities message. Never tell the
user to buy credits until that check returns ~$0 or a job would exceed the
balance. Estimate the cost of every X call before making it and confirm with
the user before anything expensive.
---
# X MCP guide

This plugin uses **X MCP**. The user taps Connect and signs in with X. They are not setting up an API app.

Probe the current user before search, timeline, bookmarks, or news. On a core error, stop. Name the simple issue, then the next step. Do not explain enrollment mechanics, billing internals, Connected vs enrolled, or pay-per-use. Never retry 401 / 403-enrollment / credits-blocked unchanged. Never ask for keys. Never tell them to create an app, Project, or Production env.
Probe the current user and their credit balance before search, timeline, bookmarks, or news. On a core error, stop. Name the simple issue, then the next step. Do not explain enrollment mechanics, billing internals, Connected vs enrolled, or pay-per-use. Never retry 401 / 403-enrollment / credits-blocked unchanged. Never ask for keys. Never tell them to create an app, Project, or Production env.

**Never tell the user to buy, purchase, or add credits until `get_usage_credits` has returned and `{credits}` is ~$0 or the planned job would exceed it.** Do not use the legacy line “you’ll need to purchase credits at https://console.x.com” (or any “buy credits first” variant) on connect or before that check.

## Credit balance

Call **`get_usage_credits`** (`GET /2/usage/credits`). It is free.

Response (values are **USD dollars and cents**; `20.0` = $20.00):

```json
{
"data": {
"free_balance": 20.0,
"free_grants": [
{ "amount": 10.0, "expires_at": "2026-11-19T02:14:28.000Z" },
{ "amount": 10.0, "expires_at": "2026-11-19T16:02:51.000Z" }
],
"prepaid_balance": 0.0,
"total_balance": 20.0
}
}
```

Use **`data.total_balance`** only. Cache it as `{credits}`. Quote it to the user in dollars (`$20.00`). Ignore `free_balance`, `free_grants`, and `prepaid_balance` — do not explain them or choose what to spend.

Fetch it **before any user-facing X message** in these cases:

1. **On connect** — first X tool call, before the capabilities message. Do not greet, list capabilities, or mention buying credits until this returns.
2. **When a session starts and X calls are required** — alongside `get_users_me`.
3. **When the user asks what they can do** — ideas, a setup, a budget, “what’s possible,” and similar.

Do not fetch on every message. If this call hits error 1 or 2, stop and say that error’s line. If it hits error 3, follow [Out of credits](#3-out-of-credits).

## On connect

The first time the user connects X — or on their first X interaction in a session — send this capabilities message once. Adapt the wording to your voice, keep every line of content:
The first time the user connects X — or on their first X interaction in a session — call `get_usage_credits` first (and `get_users_me`). Do not send the capabilities message until `{credits}` is cached. Then send it once. Adapt the wording to your voice. Keep every capability bullet. Then state their balance and suggest **2–3** things from the matching [By budget](#by-budget) row (plus a cheaper starter if useful). Do not pitch work above `{credits}`. Do not mention purchasing or console.x.com unless `{credits}` is ~$0.

> You're connected to X. Here's what I can do:
>
Expand All @@ -26,13 +60,19 @@ The first time the user connects X — or on their first X interaction in a sess
> - **News & trends** — search X news stories and get trends by location
> - **Bookmarks** — list, add, and remove bookmarks, and organize them into folders
>
> Requests use credits: you'll need to purchase credits at https://console.x.com for this to work. I'll show you a cost estimate before anything expensive.
> You have about $X.XX in credits.
>
> With that, we could: (2–3 ideas from the matching budget row).
>
> I'll show a cost estimate before anything expensive.

If `{credits}` is ~$0, keep the bullets, say they have $0.00, suggest only free lookups, and **then** send them to https://console.x.com to add credits — skip “With that, we could.” Do **not** use the error-3 quote; a successful ~$0 read is not error 3. If `{credits}` is above $0, do not mention buying or console.x.com.

Send it once per session, not on every message. If their first message already contains an ask, send this first, then do the ask.
Send it once per session, not on every message. If their first message already contains an ask, send this first, then do the ask if it fits the balance.

## The three errors

Match `type`, `reason`, `title`, `detail`. Then say the quoted line. Nothing else.
Match `type`, `reason`, `title`, `detail`. Then say the quoted line. For **#1 and #2**, nothing else. For **#3**, the quoted line plus the free-only follow-up below.

### 1. Sign-in failed

Expand All @@ -56,13 +96,13 @@ Do not retry. Do not search. Do not mention apps, projects, or pay-per-use. If t

### 3. Out of credits

**When:** no credits; balance zero or negative; “does not have any credits”; requests blocked until credits are added.
**When:** a billed request is blocked until credits are added; “does not have any credits”; credits-blocked. **Not** a successful `get_usage_credits` with `total_balance` ~$0 — that uses the [On connect](#on-connect) $0 copy (or the ~$0 [By budget](#by-budget) row).

**Say:**

> You're out of credits. Go to https://console.x.com and add credits, then I'll retry.

Stop. Do not retry.
Stop. Do not retry billed calls. Cache `{credits}` as $0. After the quoted line, you may offer **only free lookups** from the ~$0 [By budget](#by-budget) row: `{me}`, likers of a post, bookmark folders. Do not offer a cheaper paid search, timeline, or anything else from a higher budget row — those fail too. After they add credits, re-fetch `{credits}` before retrying.

If the payload is only `usage-capped` (no enrollment reason):

Expand Down Expand Up @@ -103,18 +143,17 @@ If `user-not-enrolled` or `client-not-enrolled` is present, that is #2, not this

## Session start

Resolve the current user (`user.fields=id,name,username,description,public_metrics`).
When X calls are required this session, resolve the current user (`user.fields=id,name,username,description,public_metrics`) and fetch `{credits}`.


| Result | Next |
| ---------------- | ------------------------------------------------------------------------------------ |
| Success | Cache `id` as `{me}`. Do their ask. Prefer `{me}` for timeline, mentions, bookmarks. |
| Error 1, 2, or 3 | Stop. Say that error's line. Do not search. |
| Success | Cache `id` as `{me}` and `total_balance` as `{credits}`. If `{credits}` is ~$0, use the [On connect](#on-connect) $0 copy (or the ~$0 budget row if they asked what they can do) — not error 3. Otherwise do their ask if it fits. Prefer `{me}` for timeline, mentions, bookmarks. |
| Error 1 or 2 | Stop. Say that error's line. Nothing else. Do not search. |
| Error 3 | Follow [Out of credits](#3-out-of-credits): quoted line, cache `{credits}` as $0, free lookups only. Do not search. |
| 200 + `errors[]` | Keep `data`. |




## Cost awareness

Every X call can charge the user. Estimate the cost **before** calling. Read [references/pricing.md](references/pricing.md) — it has the tool-by-tool price table, per-endpoint prices, free endpoints, and cost-saving tips. Once per session, fetch live pricing from https://console.x.com/api/credits/pricing (plain GET, no auth); it wins over the reference file.
Expand All @@ -123,18 +162,20 @@ The live payload:

- `eventTypePricing` — price **per resource returned** (each post, user, news story…).
- `requestTypePricing` — price **per request** (writes, counts, trends…).
- All prices are **USD dollars**: `0.005` = $0.005 = half a cent. Fractional cents to 3 decimal places are normal. $1.00 = 1,000 credits — that conversion is for your own math; quote costs to the user in dollars only.
- All prices are **USD dollars**: `0.005` = $0.005 = half a cent. Fractional cents to 3 decimal places are normal. $1.00 = 1,000 credits — that conversion is for your own math; quote costs to the user in dollars only. `{credits}` from `/2/usage/credits` is already dollars.

Estimate = (resources requested × per-resource price) + per-request price. `max_results` bounds a read: a search with `max_results=100` returning posts + expanded authors can cost ~100 × $0.005 + 100 × $0.01. Each pagination page bills again. Only request expansions you'll use — expanded objects bill too.

**Under ~$0.25:** just do it — don't nag about pennies. Keep `max_results` small (10–25) unless they asked for more.
**Under ~$0.25, and it fits `{credits}`:** just do it — don't nag about pennies. Keep `max_results` small (10–25) unless they asked for more.

**Over ~$0.25, or any pagination loop / bulk job:** stop first. Give a one-line estimate and ask:

> This will cost about $X.XX. Want me to continue?

Wait for a yes. Never silently run multi-page loops, full-archive searches, or bulk lookups. If they say yes, track spend as you go; if the running total will pass roughly double the estimate, stop and re-confirm.

**Estimate larger than `{credits}`:** do not run it. If `{credits}` is ~$0, use the ~$0 [By budget](#by-budget) row (free lookups, then console.x.com). Do not use the error-3 quote unless a billed call was actually blocked. If they still have some balance, offer a cheaper alternative from [By budget](#by-budget) that **fits `{credits}`**, and send them to https://console.x.com only if they still want the larger job. After they top up, re-fetch `{credits}` before retrying.

## Fields, pagination

Request fields. If the tool takes `tweet.fields` or `post.fields`, send `created_at,public_metrics,author_id,lang,conversation_id`. Also `user.fields=created_at,description,public_metrics,verified,location` and `expansions=author_id,referenced_tweets.id`.
Expand Down Expand Up @@ -166,21 +207,43 @@ Spaces = AND. Recent query max 512 characters; full-archive 1,024. Use `min_like

## Workflows

Current user first. Stop on errors 1–3.
Current user first. Stop on errors 1–2 with the quoted line only. On **API error 3** (billed calls blocked), quoted line plus free lookups only. A successful `{credits}` of ~$0 is **not** error 3 — use the [On connect](#on-connect) $0 copy, or the ~$0 [By budget](#by-budget) row if they asked what they can do. Tailor suggestions to `{credits}`.

### By budget

Pick from the **matching row**, not above it. Larger jobs still need an estimate and a yes. `$0.005`/post, `$0.01`/user, expansions bill too.


| `{credits}` | Suggest |
| ----------- | ------- |
| ~$0 | `{me}` (free). Likers of a post (free). Bookmark folders (free). Then: add credits at https://console.x.com. |
| under ~$0.25 | One post from a link. One user by handle. Recent post counts on a topic. |
| ~$0.25–$1 | A small search (10–25 posts). One page of home or mentions. |
| ~$1–$5 | A few targeted searches. News on a topic plus trends for a location. Tidy bookmarks. |
| ~$5–$20 | Compare 2–3 accounts (profile + recent posts). A short research pass: counts, then a couple of search angles. |
| ~$20–$50 | Deeper research: several angles, a handful of accounts, news on the topic. One account’s recent posts across a few pages (confirm). |
| ~$50–$100 | A full-archive slice on one query. A competitive set of ~5–10 accounts. Paginated timelines (confirm). |
| ~$100–$500 | Large archive jobs. Many queries or many accounts. Broad topic monitoring across pages — always confirm. |
| ~$500–$1,000 | Org-scale historical pulls. Multi-query archive. Large comparative studies — confirm each large chunk. |
| $1,000+ | Very large archive / bulk historical. Long-running research. Never silent pagination; confirm every large chunk. |


When they ask what they can do, re-fetch `{credits}`, then give 2–3 ideas from the matching row.

### Common tasks

- Home / mentions / my posts: `{me}`, modest `max_results`. Paginate only if asked.
- Handle: username → posts. Else user search, then ask.
- Topic: recent counts → small search page → stop.
- Bookmarks: list `{me}`. Save: parse status id, create bookmark.
- One post: parse status id, lookup.



## Don't

- Explain deep details (pay-per-use, Connected vs enrolled, billing internals). Do name the simple issue.
- Explain deep details (pay-per-use, Connected vs enrolled, billing internals, free vs prepaid grants). Do name the simple issue.
- Say pay-per-use, Project, Production, or "create an app".
- Ask for secrets.
- Retry 403 or credits-blocked in a loop.
- Pitch or run work above `{credits}`. If `{credits}` is ~$0, only free lookups. If they have some balance, offer a cheaper alternative that fits; send them to https://console.x.com only for the job that still would not fit.
- Tell the user to buy / purchase / add credits, or send them to console.x.com to pay, before `get_usage_credits` has returned. Never use “you’ll need to purchase credits at https://console.x.com” unless the check showed ~$0 or a job would exceed `{credits}`.
- Run an expensive request (over ~$0.25, pagination loops, bulk lookups) without giving an estimate and getting a yes.

2 changes: 2 additions & 0 deletions third_party/x/skills/x-api-mcp-guide/references/pricing.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Reference prices for cost estimates. May drift; the live endpoint https://consol
| Tool | Wraps | Cost |
|---|---|---|
| `get_users_me` | `GET /2/users/me` | free |
| `get_usage_credits` | `GET /2/usage/credits` | free |
| `get_users_by_id` / `get_users_by_username` / `get_users_by_usernames` / `search_users` | user lookup/search | $0.01/user |
| `get_posts_by_id` / `get_posts_by_ids` / `search_posts_all` / `get_posts_quoted_posts` | post lookup/search | $0.005/post |
| `get_users_posts` / `get_users_mentions` / `get_users_timeline` / `get_users_bookmarks` | user timelines | $0.005/post ($0.001 own data) |
Expand Down Expand Up @@ -116,6 +117,7 @@ Reference prices for cost estimates. May drift; the live endpoint https://consol
## Free endpoints (no per-use charge)

- `GET /2/users/me`
- `GET /2/usage/credits` (MCP: `get_usage_credits`)
- `GET /2/tweets/:id/liking_users`
- `GET /2/media` · `GET /2/media/:media_key`
- Bookmark folders: `GET /2/users/:id/bookmarks/folders` (and by folder id) · folder create
Expand Down