@twitterapis/mcp 0.6.7 → 0.6.9

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,12 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.9 (2026-08-10)
4
+
5
+ ### Added
6
+
7
+ - **2 new tools for X's bookmark folders** (task #9): `twitter_bookmark_folders` (list your own bookmark folders, X's internal name: collections) and `twitter_bookmark_folder_timeline` (read the tweets inside one specific folder, by `folder_id`). Both require an authenticated session, same auth model as `twitter_bookmarks` and `twitter_bookmark_search`. `twitter_bookmark_folder_timeline` is cursor-paginated only; there is no count/page-size argument for this op. The catalog is now **62 tools: 41 reads and 21 write actions**.
8
+
9
+ ## 0.6.8 (2026-08-10)
10
+
11
+ ### Added
12
+
13
+ - **`twitter_article_get` gains an owner-only `article_id` form** (task #12, competitor parity): pass `article_id` (the article's own entity id, from `twitter_article_create` or `twitter_article_list`) to read one of your own articles by id, including Drafts, which have no announcement tweet the existing public `id`/`url` form could resolve. Requires a registered session or per-call `auth_token`/`ct0`, same as the other authenticated article tools. Returns `article: null` when the id is not found or not owned by the calling account, X exposes no dedicated get-by-id op, so this scans the caller's own Draft then Published lists and matches client-side, same approach `article/delete` already used for lifecycle resolution.
14
+
3
15
  ## 0.6.7 (2026-08-10)
4
16
 
5
17
  ### Added
6
18
 
7
19
  - **8 new tools for X's long-form Articles/Notes feature** (#1096): `twitter_article_create`, `twitter_article_update_title`, `twitter_article_update_content`, `twitter_article_publish`, `twitter_article_unpublish`, `twitter_article_get`, `twitter_article_list`, `twitter_article_delete`. `twitter_article_get` is a public read (no session required, like `twitter_tweet_detail`); the other 7 require a customer session. The catalog is now **60 tools: 39 reads and 21 write actions**.
8
20
 
9
- ## Unreleased
21
+ ## 0.6.6 (2026-08-09)
22
+
23
+ ### Added
24
+
25
+ - **`twitter_customer_session_delete`**, the self-serve counterpart to `twitter_customer_session`: the backend has shipped this revoke endpoint since PR #188, but no published surface carried it, so an agent could link a session with no documented way to unlink it. Takes no body field and no header beyond the API key; the handler resolves the session to delete from the auth context, which is what makes cross-key deletion impossible. Placed next to `twitter_customer_session` so the way out sits beside the way in. The catalog is now **52 tools: 37 reads and 15 write actions**.
26
+
27
+ ### Fixed
28
+
29
+ - **`twitter_user_login` was missing `proxy_url` and `user_agent`**, which the backend handler reads and stores on the resulting session, governing that session's ongoing egress and fingerprint rather than just the one login call. Both were undocumented and therefore uncallable through the tool. Verified against `src/server/routes/user-login.ts`, not the spec.
30
+
31
+ ## 0.6.5 (2026-08-06)
10
32
 
11
33
  ### Changed
12
34
 
@@ -37,7 +59,7 @@
37
59
 
38
60
  - Release tooling hardened. No user-facing or API behaviour change.
39
61
 
40
- ## Unreleased
62
+ ## 0.6.2 (2026-07-21)
41
63
 
42
64
  ### Added
43
65
 
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
- 60 tools: 39 reads and 21 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`).
94
+ 62 tools: 41 reads and 21 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`).
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 **all 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) are annotated `destructiveHint: true` so MCP clients can prompt before running them.
97
97
 
@@ -129,6 +129,8 @@ Public reads (search, profiles, tweets, followers, likes) work with just your AP
129
129
  | `twitter_blocking` | Accounts your authenticated account has blocked (your own list only) _(session)_ |
130
130
  | `twitter_muting` | Accounts your authenticated account has muted (your own list only) _(session)_ |
131
131
  | `twitter_bookmark_search` | Full-text search within your bookmarks _(session)_ |
132
+ | `twitter_bookmark_folders` | Your authenticated account's bookmark folders _(session)_ |
133
+ | `twitter_bookmark_folder_timeline` | Tweets inside one of your bookmark folders, by `folder_id` _(session)_ |
132
134
  | `twitter_dm_list` | Your DM conversations (inbox), read-only _(session)_ |
133
135
  | `twitter_dm_conversation` | Messages in one DM conversation, read-only _(session)_ |
134
136
  | `twitter_trends` | Current top trends for a location (by `country` or `woeid`) |
@@ -255,7 +257,7 @@ Calls are billed to your twitterapis.com account. Almost every endpoint is $0.00
255
257
 
256
258
  **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.
257
259
 
258
- **Is it read-only?** No. 39 read tools work with just your API key; 21 write actions (post, like, retweet, follow, DM, media upload, article create/edit/publish/delete) act as a linked X account or per-call inline credentials.
260
+ **Is it read-only?** No. 41 read tools work with just your API key; 21 write actions (post, like, retweet, follow, DM, media upload, article create/edit/publish/delete) act as a linked X account or per-call inline credentials.
259
261
 
260
262
  **Which clients are supported?** Claude Desktop, Cursor, Windsurf, and VS Code (Copilot agent mode), or any Model Context Protocol client.
261
263
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twitterapis/mcp",
3
- "version": "0.6.7",
3
+ "version": "0.6.9",
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",
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: 60 tools (39 reads, 21 writes).
11
+ // Catalog: 62 tools (41 reads, 21 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
@@ -634,6 +634,52 @@ export const TOOLS = [
634
634
  ),
635
635
  },
636
636
  },
637
+ {
638
+ name: "twitter_bookmark_folders",
639
+ path: "/twitter/user/bookmark_folders",
640
+ description:
641
+ "List YOUR authenticated account's bookmark FOLDERS (X's internal name: collections), the named groups you can organize saved tweets into, separate from your flat bookmarks list (twitter_bookmarks). Requires an authenticated session behind your key. Returns each folder's id, name, and a cover image. Takes no arguments; your folders resolve from your session alone. Use twitter_bookmark_folder_timeline with a folder's id to read the tweets inside it.",
642
+ shape: {
643
+ auth_token: z.string().optional().describe(
644
+ "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.",
645
+ ),
646
+ ct0: z.string().optional().describe(
647
+ "Optional. The account's ct0 cookie, paired with auth_token. Sent as the x-ct0 header.",
648
+ ),
649
+ proxy_url: z.string().optional().describe(
650
+ "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.",
651
+ ),
652
+ user_agent: z.string().optional().describe(
653
+ "Optional. User-Agent string to send for this session. Sent as the x-user-agent header.",
654
+ ),
655
+ },
656
+ },
657
+ {
658
+ name: "twitter_bookmark_folder_timeline",
659
+ path: "/twitter/user/bookmark_folder_timeline",
660
+ description:
661
+ "Read the tweets inside ONE of your authenticated account's bookmark folders, identified by folder_id (from twitter_bookmark_folders). Requires an authenticated session behind your key. Cursor-paginated; there is no count/page-size argument for this op.",
662
+ shape: {
663
+ folder_id: z.string().describe(
664
+ "The bookmark folder's id, from twitter_bookmark_folders (e.g. '2073826456430592429').",
665
+ ),
666
+ cursor: z.string().optional().describe(
667
+ "Opaque pagination cursor from a previous response's next_cursor field. Omit on the first call; pass on subsequent calls to fetch the next page.",
668
+ ),
669
+ auth_token: z.string().optional().describe(
670
+ "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.",
671
+ ),
672
+ ct0: z.string().optional().describe(
673
+ "Optional. The account's ct0 cookie, paired with auth_token. Sent as the x-ct0 header.",
674
+ ),
675
+ proxy_url: z.string().optional().describe(
676
+ "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.",
677
+ ),
678
+ user_agent: z.string().optional().describe(
679
+ "Optional. User-Agent string to send for this session. Sent as the x-user-agent header.",
680
+ ),
681
+ },
682
+ },
637
683
  {
638
684
  name: "twitter_dm_list",
639
685
  path: "/twitter/dm/list",
@@ -1240,7 +1286,7 @@ export const TOOLS = [
1240
1286
  name: "twitter_article_get",
1241
1287
  path: "/twitter/article/get",
1242
1288
  description:
1243
- "Read a PUBLISHED article's full content (title, content_state, cover media, author, timestamps, public_url) via its announcement tweet. PUBLIC read: no registered session or per-call credentials needed, just your API key, same auth model as twitter_tweet_detail. Provide either id or url of the announcement tweet. Returns 404 (article null) if the tweet does not exist, is not visible, or is not an article announcement, for example a Draft that was never published, or a Published article that was later unpublished/deleted. Use twitter_article_list instead to read your OWN drafts.",
1289
+ "Read an article's full content (title, content_state, cover media, author, timestamps, public_url). Two mutually exclusive forms. PUBLIC: provide id or url of the article's announcement tweet, no registered session or per-call credentials needed, just your API key, same auth model as twitter_tweet_detail, works for PUBLISHED articles only. OWNER-ONLY: provide article_id (the article's own entity id, from twitter_article_create or twitter_article_list), requires an authenticated session, also reaches your own Drafts, which have no announcement tweet the public form could resolve. Returns 404 (article null) if not found, not visible, or (article_id form) not owned by the calling account.",
1244
1290
  shape: {
1245
1291
  id: z.string().optional().describe(
1246
1292
  "Tweet/post numeric id (e.g. \"1789012345678901234\"). Provide exactly one of id or url.",
@@ -1248,6 +1294,21 @@ export const TOOLS = [
1248
1294
  url: z.string().optional().describe(
1249
1295
  "Full tweet URL, e.g. \"https://x.com/elonmusk/status/1789012345678901234\". Provide exactly one of id or url.",
1250
1296
  ),
1297
+ article_id: z.string().optional().describe(
1298
+ "OWNER-ONLY form. The article's own entity id, from twitter_article_create or twitter_article_list (e.g. 'ArticleEntity:1234567890123456789', or the bare numeric rest_id). Requires an authenticated session. Provide exactly one of id, url, or article_id.",
1299
+ ),
1300
+ auth_token: z.string().optional().describe(
1301
+ "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.",
1302
+ ),
1303
+ ct0: z.string().optional().describe(
1304
+ "Optional. The account's ct0 cookie, paired with auth_token. Sent as the x-ct0 header.",
1305
+ ),
1306
+ proxy_url: z.string().optional().describe(
1307
+ "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.",
1308
+ ),
1309
+ user_agent: z.string().optional().describe(
1310
+ "Optional. User-Agent string to send for this session. Sent as the x-user-agent header.",
1311
+ ),
1251
1312
  },
1252
1313
  },
1253
1314
  {