@twitterapis/mcp 0.7.5 → 0.7.7
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 +13 -0
- package/README.md +6 -3
- package/package.json +3 -2
- package/src/tools.js +60 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.7 (2026-08-16)
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`twitter_spaces_info`** (GET /twitter/spaces/info): metadata and the participant roster for one X Space, live or ended. Returns title, lifecycle state (`Scheduled` / `NotStarted` / `Running` / `Ended`), content type (`audio`, or `visual_audio` when the host enabled video), the host profile, topics, the wrapper tweet, scheduled and actual start/end times, peak live listener count, replay view count, and the admin, speaker and listener rosters. Takes the Space id from a `x.com/i/spaces/<id>` URL. Two things worth knowing before you read a response as wrong: X does NOT retain the per-person listener roster once a Space ends, so `listeners` comes back empty for an ended Space while `total_live_listeners` (peak concurrent) and `total_replay_watched` still reflect the real audience, and `admins` and `speakers` do survive; and every timestamp is a millisecond-epoch number, because X sends `started_at` as a number and `ended_at` as a string in the same payload and both are normalised so you can subtract them directly. Returns metadata only, not the Space audio.
|
|
8
|
+
- **`twitter_article_update_cover_media`** (POST /twitter/article/update_cover_media): attach an already-uploaded image as a draft or published article's cover, completing the article write set. This attaches, it does not upload: call `twitter_media_upload` first and pass the `media_id` it returns. `media_category` defaults to `DraftTweetImage`, which is what X's own article editor sends. 78 tools now: 48 reads and 30 writes.
|
|
9
|
+
|
|
10
|
+
## 0.7.6 (2026-08-16)
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`twitter_user_status`** (GET /twitter/user/status): check whether a Twitter/X account is alive, suspended, or deleted. Returns `status` as one of `alive` / `suspended` / `not_found` / `unavailable`, plus the numeric `id` when the account is alive and X's own `reason` when it gives one. Use it instead of `twitter_user_info` when the question is whether an account still exists: user info answers a suspended account, a deleted account, and a handle that never existed all the same way, so it cannot tell a ban from a typo. Every outcome is a successful response, so read the `status` field rather than treating a suspension as an error. A protected (private) account counts as alive. 76 tools now: 47 reads and 29 writes.
|
|
15
|
+
|
|
3
16
|
## 0.7.5 (2026-08-16)
|
|
4
17
|
|
|
5
18
|
### Fixed
|
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
|
+
78 tools: 48 reads and 30 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. Two of the reads are free account/billing lookups (`twitter_account_me`, `twitter_account_payments`); the 14 monitoring tools 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** tools (see below) are the one 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
|
|
|
@@ -103,6 +103,7 @@ Public reads (search, profiles, tweets, followers, likes) work with just your AP
|
|
|
103
103
|
| `twitter_user_search` | Find user accounts by name or keyword |
|
|
104
104
|
| `twitter_user_info` | Full profile by handle (bio, counts, verification, location) |
|
|
105
105
|
| `twitter_user_info_by_id` | Full profile by numeric user id |
|
|
106
|
+
| `twitter_user_status` | Is an account alive, suspended, or deleted |
|
|
106
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) |
|
|
107
108
|
| `twitter_user_affiliates` | Accounts affiliated with an organization profile |
|
|
108
109
|
| `twitter_check_follow_relationship` | Follow relationship between two user ids (who follows whom) |
|
|
@@ -132,6 +133,7 @@ Public reads (search, profiles, tweets, followers, likes) work with just your AP
|
|
|
132
133
|
| `twitter_bookmark_folder_timeline` | Tweets inside one of your bookmark folders, by `folder_id` _(session)_ |
|
|
133
134
|
| `twitter_dm_list` | Your DM conversations (inbox), read-only _(session)_ |
|
|
134
135
|
| `twitter_dm_conversation` | Messages in one DM conversation, read-only _(session)_ |
|
|
136
|
+
| `twitter_spaces_info` | Metadata and participant roster for one X Space, live or ended (by Space `id`) |
|
|
135
137
|
| `twitter_trends` | Current top trends for a location (by `country` or `woeid`) |
|
|
136
138
|
| `twitter_trends_locations` | Every location X has trends for, each with its WOEID |
|
|
137
139
|
| `twitter_account_me` | Your twitterapis.com account: credits, usage, email (free) |
|
|
@@ -159,6 +161,7 @@ Public reads (search, profiles, tweets, followers, likes) work with just your AP
|
|
|
159
161
|
|---|---|
|
|
160
162
|
| `twitter_article_create` | Start a new draft article, returns its `id` |
|
|
161
163
|
| `twitter_article_update_title` | Set a draft or published article's title |
|
|
164
|
+
| `twitter_article_update_cover_media` | Attach an already-uploaded image as an article's cover (`media_id` from `twitter_media_upload`) |
|
|
162
165
|
| `twitter_article_update_content` | Replace a draft or published article's body (Draft.js `content_state` you build) |
|
|
163
166
|
| `twitter_article_publish` | Publish a draft, posting a **real public announcement tweet** (not fully reversible) |
|
|
164
167
|
| `twitter_article_unpublish` | Revert a published article to draft (leaves the announcement tweet up) |
|
|
@@ -264,7 +267,7 @@ count: 50
|
|
|
264
267
|
|
|
265
268
|
## Pricing
|
|
266
269
|
|
|
267
|
-
Calls are billed to your twitterapis.com account. Almost every endpoint is $0.0008/call: all reads (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`) at $0.004/call, and the article-editing writes (`twitter_article_create`, `twitter_article_update_title`, `twitter_article_update_content`, `twitter_article_publish`, `twitter_article_unpublish`) at $0.0016/call (`twitter_article_get`, `twitter_article_list`, and `twitter_article_delete` stay at the standard $0.0008/call). Your first $0.50 is free. See [twitterapis.com/pricing](https://www.twitterapis.com/pricing).
|
|
270
|
+
Calls are billed to your twitterapis.com account. Almost every endpoint is $0.0008/call: all reads (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`) 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/call (`twitter_article_get`, `twitter_article_list`, and `twitter_article_delete` stay at the standard $0.0008/call). Your first $0.50 is free. See [twitterapis.com/pricing](https://www.twitterapis.com/pricing).
|
|
268
271
|
|
|
269
272
|
## Links
|
|
270
273
|
|
|
@@ -277,7 +280,7 @@ Calls are billed to your twitterapis.com account. Almost every endpoint is $0.00
|
|
|
277
280
|
|
|
278
281
|
**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.
|
|
279
282
|
|
|
280
|
-
**Is it read-only?** No.
|
|
283
|
+
**Is it read-only?** No. 48 read tools work with just your API key; 30 write actions (post, like, retweet, follow, DM, media upload, article create/edit/publish/delete, monitor/webhook create/update/delete) act as a linked X account or per-call inline credentials, except monitor/webhook CRUD, which is account administration and needs only your API key.
|
|
281
284
|
|
|
282
285
|
**Which clients are supported?** Claude Desktop, Cursor, Windsurf, and VS Code (Copilot agent mode), or any Model Context Protocol client.
|
|
283
286
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twitterapis/mcp",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.7",
|
|
4
4
|
"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.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,9 +26,10 @@
|
|
|
26
26
|
"build": "node scripts/gen-tools.mjs --write",
|
|
27
27
|
"build:check": "node scripts/gen-tools.mjs --check",
|
|
28
28
|
"openapi:refresh": "node scripts/openapi-refresh.mjs",
|
|
29
|
-
"test": "node scripts/gen-tools.mjs --check && node test/gen-tools-endpoints.mjs && node test/catalog-identity.mjs && node test/tools.test.mjs && node test/smoke.mjs && node test/openapi-parity.mjs && node test/readme-parity.mjs && node test/firewall.mjs && node test/registry-manifests.mjs",
|
|
29
|
+
"test": "node scripts/gen-tools.mjs --check && node test/gen-tools-endpoints.mjs && node test/catalog-identity.mjs && node test/tools.test.mjs && node test/smoke.mjs && node test/openapi-parity.mjs && node test/body-mode-parity.mjs && node test/readme-parity.mjs && node test/firewall.mjs && node test/registry-manifests.mjs",
|
|
30
30
|
"prepublishOnly": "npm test && node test/publish-provenance.mjs",
|
|
31
31
|
"check:openapi-parity": "node test/openapi-parity.mjs",
|
|
32
|
+
"check:body-mode-parity": "node test/body-mode-parity.mjs",
|
|
32
33
|
"check:firewall": "node test/firewall.mjs",
|
|
33
34
|
"check:registry": "node test/registry-manifests.mjs",
|
|
34
35
|
"check:publish-provenance": "node test/publish-provenance.mjs"
|
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: 78 tools (48 reads, 30 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
|
|
@@ -86,6 +86,17 @@ export const TOOLS = [
|
|
|
86
86
|
),
|
|
87
87
|
},
|
|
88
88
|
},
|
|
89
|
+
{
|
|
90
|
+
name: "twitter_user_status",
|
|
91
|
+
path: "/twitter/user/status",
|
|
92
|
+
description:
|
|
93
|
+
"Check whether a Twitter/X account is alive, suspended, or deleted. Returns a status field that is one of 'alive', 'suspended', 'not_found', or 'unavailable', plus the numeric id when the account is alive and X's own reason when it gives one. Use this instead of twitter_user_info when the QUESTION is whether the account still exists: user info answers a suspended account, a deleted account, and a handle that never existed all the same way, so it cannot tell a ban from a typo. Every outcome here is a successful response, so read the status field rather than treating a suspension as an error. A protected (private) account counts as alive, since protection is a visibility setting and not an account state.",
|
|
94
|
+
shape: {
|
|
95
|
+
userName: z.string().describe(
|
|
96
|
+
"Twitter/X handle WITHOUT the leading @ (e.g. 'elonmusk', 'openai', 'sama').",
|
|
97
|
+
),
|
|
98
|
+
},
|
|
99
|
+
},
|
|
89
100
|
{
|
|
90
101
|
name: "twitter_user_about",
|
|
91
102
|
path: "/twitter/user/user_about",
|
|
@@ -456,6 +467,23 @@ export const TOOLS = [
|
|
|
456
467
|
),
|
|
457
468
|
},
|
|
458
469
|
},
|
|
470
|
+
{
|
|
471
|
+
name: "twitter_spaces_info",
|
|
472
|
+
path: "/twitter/spaces/info",
|
|
473
|
+
description:
|
|
474
|
+
"Get metadata and the participant roster for one X Space by id, live or ended: title, lifecycle state (Scheduled, NotStarted, Running or Ended), host, topics, scheduled and actual start/end times, peak live listener count, replay view count, and the admin, speaker and listener rosters. Returns metadata only, NOT the Space audio. Note that X does not retain the per-person listener roster once a Space ends, so listeners comes back empty for an ended Space while total_live_listeners and total_replay_watched still reflect the real audience. All timestamps are millisecond-epoch numbers.",
|
|
475
|
+
shape: {
|
|
476
|
+
id: z.string().describe(
|
|
477
|
+
"The Space id: the trailing token of a x.com/i/spaces/<id> URL, e.g. '1RKZzjkoYRAKB'. A '/peek' suffix on the URL is not part of the id.",
|
|
478
|
+
),
|
|
479
|
+
with_listeners: z.string().optional().describe(
|
|
480
|
+
"Optional. Include the listener roster. Defaults to true. X drops this roster once a Space ends, so it is empty for an ended Space regardless of this flag.",
|
|
481
|
+
),
|
|
482
|
+
with_replays: z.string().optional().describe(
|
|
483
|
+
"Optional. Include replay availability and related metadata. Defaults to true.",
|
|
484
|
+
),
|
|
485
|
+
},
|
|
486
|
+
},
|
|
459
487
|
{
|
|
460
488
|
name: "twitter_trends",
|
|
461
489
|
path: "/twitter/trends",
|
|
@@ -1158,6 +1186,37 @@ export const TOOLS = [
|
|
|
1158
1186
|
),
|
|
1159
1187
|
},
|
|
1160
1188
|
},
|
|
1189
|
+
{
|
|
1190
|
+
name: "twitter_article_update_cover_media",
|
|
1191
|
+
path: "/twitter/article/update_cover_media",
|
|
1192
|
+
method: "POST",
|
|
1193
|
+
write: true,
|
|
1194
|
+
description:
|
|
1195
|
+
"Attach an ALREADY-UPLOADED image as the cover of a DRAFT or PUBLISHED article, AS your authenticated account. This does NOT upload: call twitter_media_upload first and pass the media_id it returns. Provide the article's id (from twitter_article_create or twitter_article_list). Requires an authenticated session with write capability behind your key. Returns the updated article object with cover_media populated.",
|
|
1196
|
+
shape: {
|
|
1197
|
+
id: z.string().describe(
|
|
1198
|
+
"The article's entity id, from twitter_article_create or twitter_article_list (e.g. 'ArticleEntity:1234567890123456789').",
|
|
1199
|
+
),
|
|
1200
|
+
media_id: z.string().describe(
|
|
1201
|
+
"The media id returned by twitter_media_upload for the image to use as the cover.",
|
|
1202
|
+
),
|
|
1203
|
+
media_category: z.string().optional().describe(
|
|
1204
|
+
"Optional. X's media category for the upload. Defaults to 'DraftTweetImage', which is what X's own article editor sends for a cover image. Only set this if you know X expects a different category.",
|
|
1205
|
+
),
|
|
1206
|
+
auth_token: z.string().optional().describe(
|
|
1207
|
+
"Optional. The account's auth_token cookie, to act AS that account for this call (must be paired with ct0). Sent as the x-auth-token header; never placed in the URL.",
|
|
1208
|
+
),
|
|
1209
|
+
ct0: z.string().optional().describe(
|
|
1210
|
+
"Optional. The account's ct0 cookie, paired with auth_token. Sent as the x-ct0 header.",
|
|
1211
|
+
),
|
|
1212
|
+
proxy_url: z.string().optional().describe(
|
|
1213
|
+
"Optional. Residential proxy URL to egress this call through. Recommended for writes: X soft-blocks writes from datacenter IPs as automated. Sent as the x-proxy-url header.",
|
|
1214
|
+
),
|
|
1215
|
+
user_agent: z.string().optional().describe(
|
|
1216
|
+
"Optional. User-Agent string to send for this session. Sent as the x-user-agent header.",
|
|
1217
|
+
),
|
|
1218
|
+
},
|
|
1219
|
+
},
|
|
1161
1220
|
{
|
|
1162
1221
|
name: "twitter_article_update_title",
|
|
1163
1222
|
path: "/twitter/article/update_title",
|