@twitterapis/mcp 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.19.0 (2026-10-01)
4
+
5
+ ### Added
6
+
7
+ - **`twitter_check_follow_relationship_batch`.** One account against up to 100 others
8
+ in one call, in either direction: fix a source and list targets, or fix a target and
9
+ list sources (which of these accounts follow the brand). Relationship is always from
10
+ the source's side. Billed per pair answered with a relationship.
11
+ - **`twitter_audience_summary`.** Samples up to 100 followers or retweeters and returns
12
+ a country histogram from the About data plus a likely-bot share from documented
13
+ profile signals.
14
+
15
+ ### Changed
16
+
17
+ - **`twitter_check_follow_relationship` takes usernames too** (`source_username`,
18
+ `target_username`), as the API already did.
19
+
3
20
  ## 0.18.0 (2026-10-01)
4
21
 
5
22
  ### Added
package/README.md CHANGED
@@ -91,7 +91,7 @@ Restart Claude Desktop. The `twitter_*` tools appear in the tool picker.
91
91
 
92
92
  ## Tools
93
93
 
94
- 110 tools: 66 reads and 44 write actions. Most user endpoints accept `username` (handle without @) **or** `user_id` (`twitter_user_likes` and `twitter_user_tweets_complete` require `user_id`); tweet endpoints accept `id` **or** `url`; paginated endpoints return a `cursor` you pass back to get the next page. Every data read also takes `fields` (comma-separated dotted paths to keep, e.g. `id,text,author.username`; pagination and envelope keys always survive) and `compact` (`"1"` for a built-in preset of ids, text, counts and author basics), so a model paying per token can trim a page to what it will read. Four of the reads are free account lookups (`twitter_account_me`, `twitter_account_payments`, `twitter_feedback_get`, `twitter_feedback_list`); the 14 monitoring tools and `twitter_feedback_send` are also free (account administration, not metered reads).
94
+ 112 tools: 68 reads and 44 write actions. Most user endpoints accept `username` (handle without @) **or** `user_id` (`twitter_user_likes` and `twitter_user_tweets_complete` require `user_id`); tweet endpoints accept `id` **or** `url`; paginated endpoints return a `cursor` you pass back to get the next page. Every data read (except `twitter_audience_summary`, which always returns its full summary) also takes `fields` (comma-separated dotted paths to keep, e.g. `id,text,author.username`; pagination and envelope keys always survive) and `compact` (`"1"` for a built-in preset of ids, text, counts and author basics), so a model paying per token can trim a page to what it will read. Four of the reads are free account lookups (`twitter_account_me`, `twitter_account_payments`, `twitter_feedback_get`, `twitter_feedback_list`); the 14 monitoring tools and `twitter_feedback_send` are also free (account administration, not metered reads).
95
95
 
96
96
  Public reads (search, profiles, tweets, followers, likes) work with just your API key. The **account-only** reads (bookmarks, DMs, home timeline, followers-you-know) and **most write actions** act AS an authenticated X account, so they need a session linked to your key first (returns HTTP 409 until then). Link a session either by registering your x.com cookies (`twitter_customer_session`) or by logging in with a username/password (`twitter_user_login`). Alternatively, pass **per-call inline credentials** on any of those tools (`auth_token` + `ct0`, with optional `proxy_url` / `user_agent`) to act AS that account for a single call without pre-registering a session, so one API key can act as many accounts. For write actions, set `proxy_url` to a residential proxy, since X soft-blocks writes that egress from datacenter IPs. Each write tool is annotated `readOnlyHint: false`; reversing actions (delete, unfollow, unlike, unretweet, unbookmark, monitor/webhook delete) are annotated `destructiveHint: true` so MCP clients can prompt before running them. The **monitoring** and **feedback** tools (see below) are the exception: they administer your twitterapis.com account, not an X session, so they need only your API key, no linked session and no inline credentials.
97
97
 
@@ -106,8 +106,10 @@ Public reads (search, profiles, tweets, followers, likes) work with just your AP
106
106
  | `twitter_user_status` | Is an account alive, suspended, or deleted |
107
107
  | `twitter_user_about` | A user's structured About object (category, professional/business labels, verification + identity-verification flags, joined date, and X's 'About this account' transparency panel) |
108
108
  | `twitter_user_about_batch` | The same About object for up to 100 accounts in one call (usernames or user_ids), billed per account answered; for vetting a list by country or account history |
109
+ | `twitter_audience_summary` | Who an audience is: a country histogram and likely-bot share over a sample of up to 100 followers or retweeters |
109
110
  | `twitter_user_affiliates` | Accounts affiliated with an organization profile |
110
- | `twitter_check_follow_relationship` | Follow relationship between two user ids (who follows whom) |
111
+ | `twitter_check_follow_relationship` | Follow relationship between two accounts, by id or username (who follows whom) |
112
+ | `twitter_check_follow_relationship_batch` | One account against up to 100 others in one call, either direction (which of these accounts follow the brand) |
111
113
  | `twitter_user_tweets` | A user's recent original tweets (replies excluded) |
112
114
  | `twitter_user_tweets_and_replies` | A user's full timeline (tweets + replies) |
113
115
  | `twitter_user_tweets_complete` | A large batch of a user's tweet history per call (see [paging note](#paging-twitter_user_tweets_complete)) |
@@ -330,7 +332,7 @@ count: 50
330
332
 
331
333
  ## Pricing
332
334
 
333
- Calls are billed to your twitterapis.com account. Most endpoints are $0.0008/call: nearly every read (search, profiles, tweets, followers, likes) plus the simple write actions (like, retweet, bookmark, follow and their undos, delete). At the read rate that works out to $0.04 per 1,000 tweets, since each call returns about 20 tweets. The premium endpoints cost a little more: tweet creation, sending a DM (`twitter_dm_send`), and DM reads (`twitter_dm_list`, `twitter_dm_conversation`) at $0.0016/call, full tweet history (`twitter_user_tweets_complete`) at $0.0024/call, a full tweet thread (`twitter_tweet_thread`) and a Grok answer (`twitter_grok_chat`) at $0.004/call, and the article-editing writes (`twitter_article_create`, `twitter_article_update_title`, `twitter_article_update_cover_media`, `twitter_article_update_content`, `twitter_article_publish`, `twitter_article_unpublish`) at $0.0016. The compose surface is $0.0016 for `twitter_draft_create`, `twitter_draft_edit`, `twitter_scheduled_create` and, note, BOTH LIST READS (`twitter_draft_list`, `twitter_scheduled_list`), which are the two reads that are not at the read rate; `twitter_draft_delete` and `twitter_scheduled_delete` are $0.0008/call, while `twitter_article_get`, `twitter_article_list` and `twitter_article_delete` stay at the standard $0.0008/call. The three profile writes (`twitter_update_profile`, `twitter_update_avatar`, `twitter_update_banner`) are $0.0016/call each. Your first $0.50 is free. See [twitterapis.com/pricing](https://www.twitterapis.com/pricing). `twitter_user_about_batch` is billed per account answered, $0.0008 each, so one 100-account call costs up to $0.08.
335
+ Calls are billed to your twitterapis.com account. Most endpoints are $0.0008/call: nearly every read (search, profiles, tweets, followers, likes) plus the simple write actions (like, retweet, bookmark, follow and their undos, delete). At the read rate that works out to $0.04 per 1,000 tweets, since each call returns about 20 tweets. The premium endpoints cost a little more: tweet creation, sending a DM (`twitter_dm_send`), and DM reads (`twitter_dm_list`, `twitter_dm_conversation`) at $0.0016/call, full tweet history (`twitter_user_tweets_complete`) at $0.0024/call, a full tweet thread (`twitter_tweet_thread`) and a Grok answer (`twitter_grok_chat`) at $0.004/call, and the article-editing writes (`twitter_article_create`, `twitter_article_update_title`, `twitter_article_update_cover_media`, `twitter_article_update_content`, `twitter_article_publish`, `twitter_article_unpublish`) at $0.0016. The compose surface is $0.0016 for `twitter_draft_create`, `twitter_draft_edit`, `twitter_scheduled_create` and, note, BOTH LIST READS (`twitter_draft_list`, `twitter_scheduled_list`), which are the two reads that are not at the read rate; `twitter_draft_delete` and `twitter_scheduled_delete` are $0.0008/call, while `twitter_article_get`, `twitter_article_list` and `twitter_article_delete` stay at the standard $0.0008/call. The three profile writes (`twitter_update_profile`, `twitter_update_avatar`, `twitter_update_banner`) are $0.0016/call each. Your first $0.50 is free. See [twitterapis.com/pricing](https://www.twitterapis.com/pricing). `twitter_user_about_batch` is billed per account answered, $0.0008 each, so one 100-account call costs up to $0.08. `twitter_check_follow_relationship_batch` bills $0.0008 per pair answered, and `twitter_audience_summary` bills $0.0008 per sample page plus per sampled account answered.
334
336
 
335
337
  ## Links
336
338
 
@@ -343,7 +345,7 @@ Calls are billed to your twitterapis.com account. Most endpoints are $0.0008/cal
343
345
 
344
346
  **Do I need an X (Twitter) developer account?** No. Get an API key at [twitterapis.com/signup](https://www.twitterapis.com/signup); there is no application or approval step.
345
347
 
346
- **Is it read-only?** No. 66 read tools work with just your API key; 44 write actions (post, save or schedule a post, like, retweet, follow, DM, media upload, profile name/bio/avatar/banner updates, List create/add member/remove member, article create/edit/publish/delete, monitor/webhook create/update/delete, feedback send) act as a linked X account or per-call inline credentials, except monitor/webhook CRUD and feedback, which are account administration and need only your API key.
348
+ **Is it read-only?** No. 68 read tools work with just your API key; 44 write actions (post, save or schedule a post, like, retweet, follow, DM, media upload, profile name/bio/avatar/banner updates, List create/add member/remove member, article create/edit/publish/delete, monitor/webhook create/update/delete, feedback send) act as a linked X account or per-call inline credentials, except monitor/webhook CRUD and feedback, which are account administration and need only your API key.
347
349
 
348
350
  **Which clients are supported?** Claude Desktop, Cursor, Windsurf, and VS Code (Copilot agent mode), or any Model Context Protocol client.
349
351
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@twitterapis/mcp",
3
3
  "mcpName": "io.github.TwitterAPIs/twitterapis-mcp",
4
- "version": "0.18.0",
4
+ "version": "0.19.0",
5
5
  "description": "Official MCP server for twitterapis.com, the Twitter/X API (search, users, followers, tweets, threads, lists, likes, bookmarks, DMs) plus write actions (post/like/retweet/follow) as native tools for Claude, Cursor, and any MCP client.",
6
6
  "repository": {
7
7
  "type": "git",
package/src/tools.js CHANGED
@@ -8,7 +8,7 @@
8
8
  // file in memory and fails if it does not match what is committed, so a hand edit
9
9
  // here is caught rather than shipped.
10
10
  //
11
- // Catalog: 110 tools (66 reads, 44 writes).
11
+ // Catalog: 112 tools (68 reads, 44 writes).
12
12
  //
13
13
  // Each tool maps 1:1 to a REST endpoint at https://api.twitterapis.com. Tool arg
14
14
  // names map 1:1 to endpoint query params (every endpoint, including the POST
@@ -207,13 +207,57 @@ export const TOOLS = [
207
207
  name: "twitter_check_follow_relationship",
208
208
  path: "/twitter/user/check_follow_relationship",
209
209
  description:
210
- "Check the follow relationship between two accounts by numeric user id: whether the source follows the target, whether the target follows the source, blocking/muting flags where available. Both ids are required. Use this to verify a follow before/after a follow action, or to detect mutuals.",
210
+ "Check the follow relationship between two accounts: whether the source follows the target, whether the target follows the source, blocking/muting flags where available. Give each side as a numeric user id or a username (an unknown username or id returns a not-found error, not billed). Use this to verify a follow before/after a follow action, or to detect mutuals. For one account against many, use twitter_check_follow_relationship_batch.",
211
211
  shape: {
212
- source_user_id: z.string().describe(
213
- "Numeric user id of the SOURCE account (the 'is this account following...' subject).",
212
+ source_user_id: z.string().optional().describe(
213
+ "Numeric user id of the SOURCE account (the 'is this account following...' subject). Or send source_username.",
214
214
  ),
215
- target_user_id: z.string().describe(
216
- "Numeric user id of the TARGET account (the '...the target?' object).",
215
+ source_username: z.string().optional().describe(
216
+ "Handle of the SOURCE account, as an alternative to source_user_id.",
217
+ ),
218
+ target_user_id: z.string().optional().describe(
219
+ "Numeric user id of the TARGET account (the '...the target?' object). Or send target_username.",
220
+ ),
221
+ target_username: z.string().optional().describe(
222
+ "Handle of the TARGET account, as an alternative to target_user_id.",
223
+ ),
224
+ fields: z.string().optional().describe(
225
+ "Optional. Comma-separated dotted field paths to KEEP in the response, applied to every object in the returned lists and to nested objects (e.g. \"id,text,author.username\"; a list name may prefix a path, \"tweets.id\"; a prefix that lands on an array applies to each element). Pagination and envelope keys (next_cursor, cursor, has_more, count, partial, error, message, reason) always survive; any other top-level key you do not name is dropped. Use it to cut a page down to the fields you will actually read.",
226
+ ),
227
+ compact: z.enum(["1","true"]).optional().describe(
228
+ "Optional. Set to \"1\" for the built-in compact preset: ids, url, text, created_at, lang, engagement counts, the is_retweet/is_reply/is_quote flags, conversation ids, the author's id/username/name/followers_count/verification, and the quoted or retweeted tweet's id/url/author username. Trims what it recognises and, on its own, never turns a body into {}. Combine with fields to keep extra paths (then only the named paths and the envelope keys survive).",
229
+ ),
230
+ },
231
+ },
232
+ {
233
+ name: "twitter_check_follow_relationship_batch",
234
+ path: "/twitter/user/check_follow_relationship/batch",
235
+ description:
236
+ "Check one account against up to 100 others in ONE call. Fix one source (source_user_id or source_username) and list up to 100 targets (target_usernames or target_user_ids), OR fix one target and list up to 100 sources (source_usernames or source_user_ids): a list on one side only. Each result carries the relationship from the SOURCE's side (following = source follows target, followed_by = target follows source), in request order. Use it to find which of a shortlist of accounts already follow a brand: fix the brand as target_username and list the accounts as source_usernames. Billed per pair answered with a relationship; not_found, forbidden, rate_limited and unavailable pairs are free. One batch per API key runs at a time (a concurrent call gets 429 batch_in_progress, not billed). fields/compact apply inside each item's relationship object.",
237
+ shape: {
238
+ source_user_id: z.string().optional().describe(
239
+ "Numeric id of the single SOURCE account, when the list is targets.",
240
+ ),
241
+ source_username: z.string().optional().describe(
242
+ "Handle of the single SOURCE account, when the list is targets.",
243
+ ),
244
+ target_user_id: z.string().optional().describe(
245
+ "Numeric id of the single TARGET account, when the list is sources.",
246
+ ),
247
+ target_username: z.string().optional().describe(
248
+ "Handle of the single TARGET account, when the list is sources.",
249
+ ),
250
+ target_usernames: z.string().optional().describe(
251
+ "Comma-separated target handles (with or without @), 1 to 100, when the source is fixed.",
252
+ ),
253
+ target_user_ids: z.string().optional().describe(
254
+ "Comma-separated numeric target ids, 1 to 100, when the source is fixed.",
255
+ ),
256
+ source_usernames: z.string().optional().describe(
257
+ "Comma-separated source handles (with or without @), 1 to 100, when the target is fixed.",
258
+ ),
259
+ source_user_ids: z.string().optional().describe(
260
+ "Comma-separated numeric source ids, 1 to 100, when the target is fixed.",
217
261
  ),
218
262
  fields: z.string().optional().describe(
219
263
  "Optional. Comma-separated dotted field paths to KEEP in the response, applied to every object in the returned lists and to nested objects (e.g. \"id,text,author.username\"; a list name may prefix a path, \"tweets.id\"; a prefix that lands on an array applies to each element). Pagination and envelope keys (next_cursor, cursor, has_more, count, partial, error, message, reason) always survive; any other top-level key you do not name is dropped. Use it to cut a page down to the fields you will actually read.",
@@ -223,6 +267,29 @@ export const TOOLS = [
223
267
  ),
224
268
  },
225
269
  },
270
+ {
271
+ name: "twitter_audience_summary",
272
+ path: "/twitter/user/audience_summary",
273
+ description:
274
+ "Summarise who an audience is in ONE call: samples up to 100 followers of an account (username or user_id) or retweeters of a tweet (tweet_id), reads each sampled account's About country, and returns a country histogram (shares over accounts with a known country) plus a likely-bot share from documented profile signals (default avatar, no bio, under 5 followers, extreme follow ratio, never posted, created in the last 30 days, digit-suffix handle; 3 or more signals = likely automated, a heuristic, not a verdict). Billed per item: each sample page that added accounts plus each sampled account X answered About for, so sample=100 costs at most $0.08 plus up to 3 pages. Shares the one-batch-per-key slot with the batch tools.",
275
+ shape: {
276
+ username: z.string().optional().describe(
277
+ "Handle whose FOLLOWERS to sample (or send user_id).",
278
+ ),
279
+ user_id: z.string().optional().describe(
280
+ "Numeric id whose FOLLOWERS to sample (or send username).",
281
+ ),
282
+ tweet_id: z.string().optional().describe(
283
+ "Tweet whose RETWEETERS to sample, instead of a user's followers.",
284
+ ),
285
+ source: z.string().optional().describe(
286
+ "Optional: 'followers' (with username/user_id) or 'retweeters' (with tweet_id); must agree with what you send.",
287
+ ),
288
+ sample: z.number().int().optional().describe(
289
+ "How many accounts to sample, 10 to 100 (default 50).",
290
+ ),
291
+ },
292
+ },
226
293
  {
227
294
  name: "twitter_user_tweets",
228
295
  path: "/twitter/user/tweets",