@twitterapis/mcp 0.17.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 +28 -0
- package/README.md +7 -4
- package/package.json +1 -1
- package/src/tools.js +93 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
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
|
+
|
|
20
|
+
## 0.18.0 (2026-10-01)
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- **`twitter_user_about_batch`.** The About object (account country, how the account
|
|
25
|
+
was created, username-change history, verification) for up to 100 accounts in one
|
|
26
|
+
call, by `usernames` or `user_ids`. Results come back in request order, each with
|
|
27
|
+
an `about` object or an error code. Billed per account X answered for; items that
|
|
28
|
+
failed on our side are free and safe to retry. Takes `fields` and `compact` like
|
|
29
|
+
every other read.
|
|
30
|
+
|
|
3
31
|
## 0.17.0 (2026-09-30)
|
|
4
32
|
|
|
5
33
|
### 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
|
-
|
|
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
|
|
|
@@ -105,8 +105,11 @@ Public reads (search, profiles, tweets, followers, likes) work with just your AP
|
|
|
105
105
|
| `twitter_user_info_by_id` | Full profile by numeric user id |
|
|
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
|
+
| `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 |
|
|
108
110
|
| `twitter_user_affiliates` | Accounts affiliated with an organization profile |
|
|
109
|
-
| `twitter_check_follow_relationship` | Follow relationship between two
|
|
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) |
|
|
110
113
|
| `twitter_user_tweets` | A user's recent original tweets (replies excluded) |
|
|
111
114
|
| `twitter_user_tweets_and_replies` | A user's full timeline (tweets + replies) |
|
|
112
115
|
| `twitter_user_tweets_complete` | A large batch of a user's tweet history per call (see [paging note](#paging-twitter_user_tweets_complete)) |
|
|
@@ -329,7 +332,7 @@ count: 50
|
|
|
329
332
|
|
|
330
333
|
## Pricing
|
|
331
334
|
|
|
332
|
-
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).
|
|
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.
|
|
333
336
|
|
|
334
337
|
## Links
|
|
335
338
|
|
|
@@ -342,7 +345,7 @@ Calls are billed to your twitterapis.com account. Most endpoints are $0.0008/cal
|
|
|
342
345
|
|
|
343
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.
|
|
344
347
|
|
|
345
|
-
**Is it read-only?** No.
|
|
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.
|
|
346
349
|
|
|
347
350
|
**Which clients are supported?** Claude Desktop, Cursor, Windsurf, and VS Code (Copilot agent mode), or any Model Context Protocol client.
|
|
348
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.
|
|
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:
|
|
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
|
|
@@ -154,6 +154,26 @@ export const TOOLS = [
|
|
|
154
154
|
),
|
|
155
155
|
},
|
|
156
156
|
},
|
|
157
|
+
{
|
|
158
|
+
name: "twitter_user_about_batch",
|
|
159
|
+
path: "/twitter/user/user_about/batch",
|
|
160
|
+
description:
|
|
161
|
+
"Get the 'About' object (account country, how the account was created, username-change history, verification and the rest of twitter_user_about) for up to 100 accounts in ONE call. Provide usernames or user_ids as a comma-separated list, never both. Results come back in request order, each with an about object or an error code (not_found is billed; forbidden is free but X refuses that account to everyone; rate_limited and unavailable are free and safe to retry). Billed per account X answered for, so use this instead of looping twitter_user_about when vetting a list of accounts, e.g. checking where a creator's audience sample is based. One batch per API key runs at a time: a second concurrent call gets 429 batch_in_progress (not billed), so run batches one after another. fields/compact apply inside each item's about object (fields=account_based_in); a results.* path returns about: {}.",
|
|
162
|
+
shape: {
|
|
163
|
+
usernames: z.string().optional().describe(
|
|
164
|
+
"Comma-separated handles, with or without the leading @ (e.g. 'openai,naval,sama'). 1 to 100 after duplicates are removed.",
|
|
165
|
+
),
|
|
166
|
+
user_ids: z.string().optional().describe(
|
|
167
|
+
"Comma-separated numeric user ids (e.g. '44196397,745273'), as an alternative to usernames. 1 to 100.",
|
|
168
|
+
),
|
|
169
|
+
fields: z.string().optional().describe(
|
|
170
|
+
"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.",
|
|
171
|
+
),
|
|
172
|
+
compact: z.enum(["1","true"]).optional().describe(
|
|
173
|
+
"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).",
|
|
174
|
+
),
|
|
175
|
+
},
|
|
176
|
+
},
|
|
157
177
|
{
|
|
158
178
|
name: "twitter_user_affiliates",
|
|
159
179
|
path: "/twitter/user/affiliates",
|
|
@@ -187,13 +207,19 @@ export const TOOLS = [
|
|
|
187
207
|
name: "twitter_check_follow_relationship",
|
|
188
208
|
path: "/twitter/user/check_follow_relationship",
|
|
189
209
|
description:
|
|
190
|
-
"Check the follow relationship between two accounts
|
|
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.",
|
|
191
211
|
shape: {
|
|
192
|
-
source_user_id: z.string().describe(
|
|
193
|
-
"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
|
+
),
|
|
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.",
|
|
194
220
|
),
|
|
195
|
-
|
|
196
|
-
"
|
|
221
|
+
target_username: z.string().optional().describe(
|
|
222
|
+
"Handle of the TARGET account, as an alternative to target_user_id.",
|
|
197
223
|
),
|
|
198
224
|
fields: z.string().optional().describe(
|
|
199
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.",
|
|
@@ -203,6 +229,67 @@ export const TOOLS = [
|
|
|
203
229
|
),
|
|
204
230
|
},
|
|
205
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.",
|
|
261
|
+
),
|
|
262
|
+
fields: z.string().optional().describe(
|
|
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.",
|
|
264
|
+
),
|
|
265
|
+
compact: z.enum(["1","true"]).optional().describe(
|
|
266
|
+
"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).",
|
|
267
|
+
),
|
|
268
|
+
},
|
|
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
|
+
},
|
|
206
293
|
{
|
|
207
294
|
name: "twitter_user_tweets",
|
|
208
295
|
path: "/twitter/user/tweets",
|