@twitterapis/mcp 0.12.1 → 0.13.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,36 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.13.0 (2026-09-23)
4
+
5
+ - **Every data read takes `fields` and `compact`.** The API added response
6
+ projection on GET routes: `fields` keeps only the dotted paths you name
7
+ (applied to every object in the returned lists and to nested objects; the
8
+ pagination and envelope keys `next_cursor`, `cursor`, `has_more`, `count`,
9
+ `partial`, `error`, `message`, `reason` always survive, anything else at the
10
+ top level that you do not name is dropped), `compact` applies a built-in
11
+ preset (ids, url, text, created_at, lang, engagement counts, the
12
+ retweet/reply/quote flags, conversation ids, the author's id/username/name/
13
+ followers_count/verification, the quoted or retweeted tweet's id/url/author)
14
+ that, on its own, never turns a body into `{}`. 55 data-read tools gained the
15
+ pair (account, feedback, session-status, monitor and webhook reads return
16
+ their bodies unchanged); a model paying per token for a 40-tweet page is the
17
+ caller these were built for. Still 109 tools (65 reads, 44 writes), so a
18
+ MINOR for the new args rather than a new tool.
19
+ - **`twitter_dm_conversation` pages backwards.** New optional `max_id`: pass the
20
+ previous page's `min_entry_id` (now returned at the root beside
21
+ `max_entry_id`) to walk a thread back in time. It must be a numeric entry id;
22
+ the API answers 400 for anything else instead of quietly returning page one.
23
+ - **`twitter_follow_user` and `twitter_unfollow_user` take a `username`** as an
24
+ alternative to `user_id` (exactly one of the two). The API resolves the handle
25
+ on every call, never from a cache, so a renamed account is acted on by its
26
+ current handle.
27
+ - The catalog generator resolves `$ref` parameters. The published spec now
28
+ declares its shared read parameters once under `components.parameters` and
29
+ references them from every GET; the generator and the openapi-parity gate read
30
+ those as a param named `undefined` and refused every read tool. Both now share
31
+ one resolver that fails closed on a ref the vendored spec lacks. The frozen
32
+ catalog baseline was regenerated in the same commit for review.
33
+
3
34
  ## 0.12.0 (2026-09-13)
4
35
 
5
36
  - **Two new tools, 107 -> 109 (65 reads, 44 writes): `twitter_update_avatar` and
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
- 109 tools: 65 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. 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
+ 109 tools: 65 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).
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
 
@@ -136,7 +136,7 @@ Public reads (search, profiles, tweets, followers, likes) work with just your AP
136
136
  | `twitter_bookmark_folders` | Your authenticated account's bookmark folders _(session)_ |
137
137
  | `twitter_bookmark_folder_timeline` | Tweets inside one of your bookmark folders, by `folder_id` _(session)_ |
138
138
  | `twitter_dm_list` | Your DM conversations (inbox), read-only _(session)_ |
139
- | `twitter_dm_conversation` | Messages in one DM conversation, read-only _(session)_ |
139
+ | `twitter_dm_conversation` | Messages in one DM conversation, read-only; pass the previous page's `min_entry_id` as `max_id` to page back _(session)_ |
140
140
  | `twitter_spaces_info` | Metadata and participant roster for one X Space, live or ended (by Space `id`) |
141
141
  | `twitter_community_search` | Find X Communities by keyword; the discovery step that produces the numeric id the rest of the community family needs |
142
142
  | `twitter_community_info` | One X Community by numeric id: name, counts, join policy, rules, topic, banners, admin |
@@ -164,7 +164,7 @@ Public reads (search, profiles, tweets, followers, likes) work with just your AP
164
164
  | `twitter_favorite_tweet` / `twitter_unfavorite_tweet` | Like / unlike a tweet |
165
165
  | `twitter_retweet` / `twitter_unretweet` | Retweet / undo retweet |
166
166
  | `twitter_bookmark_tweet` / `twitter_unbookmark_tweet` | Bookmark / remove bookmark |
167
- | `twitter_follow_user` / `twitter_unfollow_user` | Follow / unfollow a user by id |
167
+ | `twitter_follow_user` / `twitter_unfollow_user` | Follow / unfollow a user by `user_id` or `username` (exactly one) |
168
168
  | `twitter_dm_send` | Send a Direct Message to a user by their numeric `recipient_id` |
169
169
  | `twitter_list_create` | Create a Twitter/X List owned by your session (`name`, optional `description` / `is_private`) |
170
170
  | `twitter_list_add_member` / `twitter_list_remove_member` | Add / remove one account on a List you own; `member_count` comes back as proof the write landed |
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.12.1",
4
+ "version": "0.13.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
@@ -49,6 +49,12 @@ export const TOOLS = [
49
49
  cursor: z.string().optional().describe(
50
50
  "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.",
51
51
  ),
52
+ fields: z.string().optional().describe(
53
+ "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.",
54
+ ),
55
+ compact: z.enum(["1","true"]).optional().describe(
56
+ "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).",
57
+ ),
52
58
  },
53
59
  },
54
60
  {
@@ -66,6 +72,12 @@ export const TOOLS = [
66
72
  cursor: z.string().optional().describe(
67
73
  "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.",
68
74
  ),
75
+ fields: z.string().optional().describe(
76
+ "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.",
77
+ ),
78
+ compact: z.enum(["1","true"]).optional().describe(
79
+ "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).",
80
+ ),
69
81
  },
70
82
  },
71
83
  {
@@ -77,6 +89,12 @@ export const TOOLS = [
77
89
  username: z.string().describe(
78
90
  "Twitter/X handle WITHOUT the leading @ (e.g. 'elonmusk', 'openai', 'sama').",
79
91
  ),
92
+ fields: z.string().optional().describe(
93
+ "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.",
94
+ ),
95
+ compact: z.enum(["1","true"]).optional().describe(
96
+ "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).",
97
+ ),
80
98
  },
81
99
  },
82
100
  {
@@ -88,6 +106,12 @@ export const TOOLS = [
88
106
  user_id: z.string().describe(
89
107
  "Numeric Twitter/X user id (e.g. '44196397' for @elonmusk). Found in responses from other tools as user_id or author_id.",
90
108
  ),
109
+ fields: z.string().optional().describe(
110
+ "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.",
111
+ ),
112
+ compact: z.enum(["1","true"]).optional().describe(
113
+ "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).",
114
+ ),
91
115
  },
92
116
  },
93
117
  {
@@ -99,6 +123,12 @@ export const TOOLS = [
99
123
  userName: z.string().describe(
100
124
  "Twitter/X handle WITHOUT the leading @ (e.g. 'elonmusk', 'openai', 'sama').",
101
125
  ),
126
+ fields: z.string().optional().describe(
127
+ "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.",
128
+ ),
129
+ compact: z.enum(["1","true"]).optional().describe(
130
+ "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).",
131
+ ),
102
132
  },
103
133
  },
104
134
  {
@@ -113,6 +143,12 @@ export const TOOLS = [
113
143
  user_id: z.string().optional().describe(
114
144
  "Numeric Twitter/X user id (e.g. \"44196397\"). Provide exactly one of username or user_id.",
115
145
  ),
146
+ fields: z.string().optional().describe(
147
+ "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.",
148
+ ),
149
+ compact: z.enum(["1","true"]).optional().describe(
150
+ "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).",
151
+ ),
116
152
  },
117
153
  },
118
154
  {
@@ -136,6 +172,12 @@ export const TOOLS = [
136
172
  cursor: z.string().optional().describe(
137
173
  "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.",
138
174
  ),
175
+ fields: z.string().optional().describe(
176
+ "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.",
177
+ ),
178
+ compact: z.enum(["1","true"]).optional().describe(
179
+ "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).",
180
+ ),
139
181
  },
140
182
  },
141
183
  {
@@ -150,6 +192,12 @@ export const TOOLS = [
150
192
  target_user_id: z.string().describe(
151
193
  "Numeric user id of the TARGET account (the '...the target?' object).",
152
194
  ),
195
+ fields: z.string().optional().describe(
196
+ "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.",
197
+ ),
198
+ compact: z.enum(["1","true"]).optional().describe(
199
+ "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).",
200
+ ),
153
201
  },
154
202
  },
155
203
  {
@@ -170,6 +218,12 @@ export const TOOLS = [
170
218
  cursor: z.string().optional().describe(
171
219
  "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.",
172
220
  ),
221
+ fields: z.string().optional().describe(
222
+ "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
+ ),
224
+ compact: z.enum(["1","true"]).optional().describe(
225
+ "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).",
226
+ ),
173
227
  },
174
228
  },
175
229
  {
@@ -190,6 +244,12 @@ export const TOOLS = [
190
244
  cursor: z.string().optional().describe(
191
245
  "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.",
192
246
  ),
247
+ fields: z.string().optional().describe(
248
+ "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.",
249
+ ),
250
+ compact: z.enum(["1","true"]).optional().describe(
251
+ "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).",
252
+ ),
193
253
  },
194
254
  },
195
255
  {
@@ -207,6 +267,12 @@ export const TOOLS = [
207
267
  cursor: z.string().optional().describe(
208
268
  "Resume point from a previous response's next_cursor. Omit on the first call. Pass it back to continue collecting where the last call stopped, and keep repeating while next_cursor is non-null.",
209
269
  ),
270
+ fields: z.string().optional().describe(
271
+ "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.",
272
+ ),
273
+ compact: z.enum(["1","true"]).optional().describe(
274
+ "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).",
275
+ ),
210
276
  },
211
277
  },
212
278
  {
@@ -227,6 +293,12 @@ export const TOOLS = [
227
293
  cursor: z.string().optional().describe(
228
294
  "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.",
229
295
  ),
296
+ fields: z.string().optional().describe(
297
+ "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.",
298
+ ),
299
+ compact: z.enum(["1","true"]).optional().describe(
300
+ "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).",
301
+ ),
230
302
  },
231
303
  },
232
304
  {
@@ -244,6 +316,12 @@ export const TOOLS = [
244
316
  cursor: z.string().optional().describe(
245
317
  "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.",
246
318
  ),
319
+ fields: z.string().optional().describe(
320
+ "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.",
321
+ ),
322
+ compact: z.enum(["1","true"]).optional().describe(
323
+ "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).",
324
+ ),
247
325
  },
248
326
  },
249
327
  {
@@ -261,6 +339,12 @@ export const TOOLS = [
261
339
  cursor: z.string().optional().describe(
262
340
  "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.",
263
341
  ),
342
+ fields: z.string().optional().describe(
343
+ "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.",
344
+ ),
345
+ compact: z.enum(["1","true"]).optional().describe(
346
+ "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).",
347
+ ),
264
348
  },
265
349
  },
266
350
  {
@@ -281,6 +365,12 @@ export const TOOLS = [
281
365
  cursor: z.string().optional().describe(
282
366
  "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.",
283
367
  ),
368
+ fields: z.string().optional().describe(
369
+ "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.",
370
+ ),
371
+ compact: z.enum(["1","true"]).optional().describe(
372
+ "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).",
373
+ ),
284
374
  },
285
375
  },
286
376
  {
@@ -301,6 +391,12 @@ export const TOOLS = [
301
391
  cursor: z.string().optional().describe(
302
392
  "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.",
303
393
  ),
394
+ fields: z.string().optional().describe(
395
+ "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.",
396
+ ),
397
+ compact: z.enum(["1","true"]).optional().describe(
398
+ "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).",
399
+ ),
304
400
  },
305
401
  },
306
402
  {
@@ -321,6 +417,12 @@ export const TOOLS = [
321
417
  cursor: z.string().optional().describe(
322
418
  "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.",
323
419
  ),
420
+ fields: z.string().optional().describe(
421
+ "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.",
422
+ ),
423
+ compact: z.enum(["1","true"]).optional().describe(
424
+ "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).",
425
+ ),
324
426
  },
325
427
  },
326
428
  {
@@ -341,6 +443,12 @@ export const TOOLS = [
341
443
  cursor: z.string().optional().describe(
342
444
  "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.",
343
445
  ),
446
+ fields: z.string().optional().describe(
447
+ "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.",
448
+ ),
449
+ compact: z.enum(["1","true"]).optional().describe(
450
+ "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).",
451
+ ),
344
452
  },
345
453
  },
346
454
  {
@@ -361,6 +469,12 @@ export const TOOLS = [
361
469
  cursor: z.string().optional().describe(
362
470
  "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.",
363
471
  ),
472
+ fields: z.string().optional().describe(
473
+ "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.",
474
+ ),
475
+ compact: z.enum(["1","true"]).optional().describe(
476
+ "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).",
477
+ ),
364
478
  },
365
479
  },
366
480
  {
@@ -390,6 +504,12 @@ export const TOOLS = [
390
504
  user_agent: z.string().optional().describe(
391
505
  "Optional. User-Agent string to send for this session. Sent as the x-user-agent request header, or in the JSON body on a body-taking tool.",
392
506
  ),
507
+ fields: z.string().optional().describe(
508
+ "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.",
509
+ ),
510
+ compact: z.enum(["1","true"]).optional().describe(
511
+ "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).",
512
+ ),
393
513
  },
394
514
  },
395
515
  {
@@ -404,6 +524,12 @@ export const TOOLS = [
404
524
  url: z.string().optional().describe(
405
525
  "Full tweet URL, e.g. \"https://x.com/elonmusk/status/1789012345678901234\". Provide exactly one of id or url.",
406
526
  ),
527
+ fields: z.string().optional().describe(
528
+ "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.",
529
+ ),
530
+ compact: z.enum(["1","true"]).optional().describe(
531
+ "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).",
532
+ ),
407
533
  },
408
534
  },
409
535
  {
@@ -421,6 +547,12 @@ export const TOOLS = [
421
547
  cursor: z.string().optional().describe(
422
548
  "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.",
423
549
  ),
550
+ fields: z.string().optional().describe(
551
+ "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.",
552
+ ),
553
+ compact: z.enum(["1","true"]).optional().describe(
554
+ "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).",
555
+ ),
424
556
  },
425
557
  },
426
558
  {
@@ -435,6 +567,12 @@ export const TOOLS = [
435
567
  url: z.string().optional().describe(
436
568
  "Full tweet URL, e.g. \"https://x.com/elonmusk/status/1789012345678901234\". Provide exactly one of id or url.",
437
569
  ),
570
+ fields: z.string().optional().describe(
571
+ "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.",
572
+ ),
573
+ compact: z.enum(["1","true"]).optional().describe(
574
+ "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).",
575
+ ),
438
576
  },
439
577
  },
440
578
  {
@@ -455,6 +593,12 @@ export const TOOLS = [
455
593
  cursor: z.string().optional().describe(
456
594
  "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.",
457
595
  ),
596
+ fields: z.string().optional().describe(
597
+ "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.",
598
+ ),
599
+ compact: z.enum(["1","true"]).optional().describe(
600
+ "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).",
601
+ ),
458
602
  },
459
603
  },
460
604
  {
@@ -481,6 +625,12 @@ export const TOOLS = [
481
625
  cursor: z.string().optional().describe(
482
626
  "Opaque pagination cursor from a previous response's next_cursor field. Omit on the first call.",
483
627
  ),
628
+ fields: z.string().optional().describe(
629
+ "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.",
630
+ ),
631
+ compact: z.enum(["1","true"]).optional().describe(
632
+ "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).",
633
+ ),
484
634
  },
485
635
  },
486
636
  {
@@ -498,6 +648,12 @@ export const TOOLS = [
498
648
  cursor: z.string().optional().describe(
499
649
  "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.",
500
650
  ),
651
+ fields: z.string().optional().describe(
652
+ "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.",
653
+ ),
654
+ compact: z.enum(["1","true"]).optional().describe(
655
+ "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).",
656
+ ),
501
657
  },
502
658
  },
503
659
  {
@@ -515,6 +671,12 @@ export const TOOLS = [
515
671
  cursor: z.string().optional().describe(
516
672
  "Opaque pagination cursor from a previous response's next_cursor field. Omit on the first call. next_cursor is null once X marks the follower list complete.",
517
673
  ),
674
+ fields: z.string().optional().describe(
675
+ "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.",
676
+ ),
677
+ compact: z.enum(["1","true"]).optional().describe(
678
+ "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).",
679
+ ),
518
680
  },
519
681
  },
520
682
  {
@@ -544,6 +706,12 @@ export const TOOLS = [
544
706
  cursor: z.string().optional().describe(
545
707
  "Opaque pagination cursor from a previous response's next_cursor field. Omit on the first call.",
546
708
  ),
709
+ fields: z.string().optional().describe(
710
+ "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.",
711
+ ),
712
+ compact: z.enum(["1","true"]).optional().describe(
713
+ "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).",
714
+ ),
547
715
  },
548
716
  },
549
717
  {
@@ -561,6 +729,12 @@ export const TOOLS = [
561
729
  cursor: z.string().optional().describe(
562
730
  "Opaque pagination cursor from a previous response's next_cursor field. Omit on the first call.",
563
731
  ),
732
+ fields: z.string().optional().describe(
733
+ "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.",
734
+ ),
735
+ compact: z.enum(["1","true"]).optional().describe(
736
+ "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).",
737
+ ),
564
738
  },
565
739
  },
566
740
  {
@@ -578,6 +752,12 @@ export const TOOLS = [
578
752
  with_replays: z.string().optional().describe(
579
753
  "Optional. Include replay availability and related metadata. Defaults to true.",
580
754
  ),
755
+ fields: z.string().optional().describe(
756
+ "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.",
757
+ ),
758
+ compact: z.enum(["1","true"]).optional().describe(
759
+ "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).",
760
+ ),
581
761
  },
582
762
  },
583
763
  {
@@ -592,6 +772,12 @@ export const TOOLS = [
592
772
  cursor: z.string().optional().describe(
593
773
  "Opaque pagination cursor from a previous response's next_cursor field. Omit on the first call.",
594
774
  ),
775
+ fields: z.string().optional().describe(
776
+ "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.",
777
+ ),
778
+ compact: z.enum(["1","true"]).optional().describe(
779
+ "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).",
780
+ ),
595
781
  },
596
782
  },
597
783
  {
@@ -603,6 +789,12 @@ export const TOOLS = [
603
789
  community_id: z.string().describe(
604
790
  "Numeric X community id, the digits in a x.com/i/communities/<id> URL, e.g. '1493446837214187523'. Digits only. This is NOT a Space id (those are base-62 tokens) and NOT a user id.",
605
791
  ),
792
+ fields: z.string().optional().describe(
793
+ "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.",
794
+ ),
795
+ compact: z.enum(["1","true"]).optional().describe(
796
+ "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).",
797
+ ),
606
798
  },
607
799
  },
608
800
  {
@@ -614,6 +806,12 @@ export const TOOLS = [
614
806
  community_id: z.string().describe(
615
807
  "Numeric X community id, the digits in a x.com/i/communities/<id> URL, e.g. '1493446837214187523'.",
616
808
  ),
809
+ fields: z.string().optional().describe(
810
+ "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.",
811
+ ),
812
+ compact: z.enum(["1","true"]).optional().describe(
813
+ "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).",
814
+ ),
617
815
  },
618
816
  },
619
817
  {
@@ -631,6 +829,12 @@ export const TOOLS = [
631
829
  cursor: z.string().optional().describe(
632
830
  "Opaque pagination cursor from a previous response's next_cursor field. Omit on the first call. Absence of next_cursor is the only end-of-list signal X gives on this operation.",
633
831
  ),
832
+ fields: z.string().optional().describe(
833
+ "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.",
834
+ ),
835
+ compact: z.enum(["1","true"]).optional().describe(
836
+ "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).",
837
+ ),
634
838
  },
635
839
  },
636
840
  {
@@ -648,6 +852,12 @@ export const TOOLS = [
648
852
  cursor: z.string().optional().describe(
649
853
  "Opaque pagination cursor from a previous response's next_cursor field. Omit on the first call.",
650
854
  ),
855
+ fields: z.string().optional().describe(
856
+ "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.",
857
+ ),
858
+ compact: z.enum(["1","true"]).optional().describe(
859
+ "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).",
860
+ ),
651
861
  },
652
862
  },
653
863
  {
@@ -668,6 +878,12 @@ export const TOOLS = [
668
878
  cursor: z.string().optional().describe(
669
879
  "Opaque pagination cursor from a previous response's next_cursor field. Omit on the first call.",
670
880
  ),
881
+ fields: z.string().optional().describe(
882
+ "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.",
883
+ ),
884
+ compact: z.enum(["1","true"]).optional().describe(
885
+ "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).",
886
+ ),
671
887
  },
672
888
  },
673
889
  {
@@ -685,6 +901,12 @@ export const TOOLS = [
685
901
  cursor: z.string().optional().describe(
686
902
  "Opaque pagination cursor from a previous response's next_cursor field. Omit on the first call.",
687
903
  ),
904
+ fields: z.string().optional().describe(
905
+ "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.",
906
+ ),
907
+ compact: z.enum(["1","true"]).optional().describe(
908
+ "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).",
909
+ ),
688
910
  },
689
911
  },
690
912
  {
@@ -742,6 +964,12 @@ export const TOOLS = [
742
964
  user_agent: z.string().optional().describe(
743
965
  "Optional. User-Agent string to send for this session. Sent as the x-user-agent request header, or in the JSON body on a body-taking tool.",
744
966
  ),
967
+ fields: z.string().optional().describe(
968
+ "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.",
969
+ ),
970
+ compact: z.enum(["1","true"]).optional().describe(
971
+ "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).",
972
+ ),
745
973
  },
746
974
  },
747
975
  {
@@ -759,14 +987,27 @@ export const TOOLS = [
759
987
  count: z.number().int().min(1).optional().describe(
760
988
  "Truncate the returned trends list to at most this many. Omit to return X's full list for the location.",
761
989
  ),
990
+ fields: z.string().optional().describe(
991
+ "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.",
992
+ ),
993
+ compact: z.enum(["1","true"]).optional().describe(
994
+ "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).",
995
+ ),
762
996
  },
763
997
  },
764
998
  {
765
999
  name: "twitter_trends_locations",
766
1000
  path: "/twitter/trends/locations",
767
1001
  description:
768
- "List every location X publishes trends for, each with the numeric WOEID to pass back to twitter_trends as woeid. Takes no parameters. Use this to resolve a country or city to its WOEID before requesting trends for that place.",
769
- shape: {},
1002
+ "List every location X publishes trends for, each with the numeric WOEID to pass back to twitter_trends as woeid. Takes no required parameters. Use this to resolve a country or city to its WOEID before requesting trends for that place.",
1003
+ shape: {
1004
+ fields: z.string().optional().describe(
1005
+ "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.",
1006
+ ),
1007
+ compact: z.enum(["1","true"]).optional().describe(
1008
+ "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).",
1009
+ ),
1010
+ },
770
1011
  },
771
1012
  {
772
1013
  name: "twitter_account_me",
@@ -872,6 +1113,12 @@ export const TOOLS = [
872
1113
  user_agent: z.string().optional().describe(
873
1114
  "Optional. User-Agent string to send for this session. Sent as the x-user-agent request header, or in the JSON body on a body-taking tool.",
874
1115
  ),
1116
+ fields: z.string().optional().describe(
1117
+ "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.",
1118
+ ),
1119
+ compact: z.enum(["1","true"]).optional().describe(
1120
+ "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).",
1121
+ ),
875
1122
  },
876
1123
  },
877
1124
  {
@@ -898,6 +1145,12 @@ export const TOOLS = [
898
1145
  user_agent: z.string().optional().describe(
899
1146
  "Optional. User-Agent string to send for this session. Sent as the x-user-agent request header, or in the JSON body on a body-taking tool.",
900
1147
  ),
1148
+ fields: z.string().optional().describe(
1149
+ "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.",
1150
+ ),
1151
+ compact: z.enum(["1","true"]).optional().describe(
1152
+ "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).",
1153
+ ),
901
1154
  },
902
1155
  },
903
1156
  {
@@ -924,6 +1177,12 @@ export const TOOLS = [
924
1177
  user_agent: z.string().optional().describe(
925
1178
  "Optional. User-Agent string to send for this session. Sent as the x-user-agent request header, or in the JSON body on a body-taking tool.",
926
1179
  ),
1180
+ fields: z.string().optional().describe(
1181
+ "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.",
1182
+ ),
1183
+ compact: z.enum(["1","true"]).optional().describe(
1184
+ "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).",
1185
+ ),
927
1186
  },
928
1187
  },
929
1188
  {
@@ -950,6 +1209,12 @@ export const TOOLS = [
950
1209
  user_agent: z.string().optional().describe(
951
1210
  "Optional. User-Agent string to send for this session. Sent as the x-user-agent request header, or in the JSON body on a body-taking tool.",
952
1211
  ),
1212
+ fields: z.string().optional().describe(
1213
+ "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.",
1214
+ ),
1215
+ compact: z.enum(["1","true"]).optional().describe(
1216
+ "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).",
1217
+ ),
953
1218
  },
954
1219
  },
955
1220
  {
@@ -979,6 +1244,12 @@ export const TOOLS = [
979
1244
  user_agent: z.string().optional().describe(
980
1245
  "Optional. User-Agent string to send for this session. Sent as the x-user-agent request header, or in the JSON body on a body-taking tool.",
981
1246
  ),
1247
+ fields: z.string().optional().describe(
1248
+ "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.",
1249
+ ),
1250
+ compact: z.enum(["1","true"]).optional().describe(
1251
+ "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).",
1252
+ ),
982
1253
  },
983
1254
  },
984
1255
  {
@@ -999,6 +1270,12 @@ export const TOOLS = [
999
1270
  user_agent: z.string().optional().describe(
1000
1271
  "Optional. User-Agent string to send for this session. Sent as the x-user-agent request header, or in the JSON body on a body-taking tool.",
1001
1272
  ),
1273
+ fields: z.string().optional().describe(
1274
+ "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.",
1275
+ ),
1276
+ compact: z.enum(["1","true"]).optional().describe(
1277
+ "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).",
1278
+ ),
1002
1279
  },
1003
1280
  },
1004
1281
  {
@@ -1025,6 +1302,12 @@ export const TOOLS = [
1025
1302
  user_agent: z.string().optional().describe(
1026
1303
  "Optional. User-Agent string to send for this session. Sent as the x-user-agent request header, or in the JSON body on a body-taking tool.",
1027
1304
  ),
1305
+ fields: z.string().optional().describe(
1306
+ "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.",
1307
+ ),
1308
+ compact: z.enum(["1","true"]).optional().describe(
1309
+ "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).",
1310
+ ),
1028
1311
  },
1029
1312
  },
1030
1313
  {
@@ -1045,17 +1328,26 @@ export const TOOLS = [
1045
1328
  user_agent: z.string().optional().describe(
1046
1329
  "Optional. User-Agent string to send for this session. Sent as the x-user-agent request header, or in the JSON body on a body-taking tool.",
1047
1330
  ),
1331
+ fields: z.string().optional().describe(
1332
+ "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.",
1333
+ ),
1334
+ compact: z.enum(["1","true"]).optional().describe(
1335
+ "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).",
1336
+ ),
1048
1337
  },
1049
1338
  },
1050
1339
  {
1051
1340
  name: "twitter_dm_conversation",
1052
1341
  path: "/twitter/dm/conversation",
1053
1342
  description:
1054
- "Get the messages in one Direct Message conversation by its conversation_id (from twitter_dm_list). Requires an authenticated session behind your key. Returns each message with sender id, time, and text. Read-only: this does not send DMs.",
1343
+ "Get the messages in one Direct Message conversation by its conversation_id (from twitter_dm_list). Requires an authenticated session behind your key. Returns each message with sender id, time, and text, plus min_entry_id and max_entry_id for the page; to walk the thread back in time, call again with max_id set to the previous page's min_entry_id. Read-only: this does not send DMs.",
1055
1344
  shape: {
1056
1345
  conversation_id: z.string().describe(
1057
1346
  "The conversation_id from a twitter_dm_list entry identifying which DM thread to read.",
1058
1347
  ),
1348
+ max_id: z.string().optional().describe(
1349
+ "Optional. Page backwards: return entries older than this numeric entry id. Pass the previous page's min_entry_id to walk a thread back in time; omit for the newest page. Must be a numeric entry id; any other value returns 400.",
1350
+ ),
1059
1351
  auth_token: z.string().optional().describe(
1060
1352
  "Optional. The account's auth_token cookie, to act AS that account for this call (must be paired with ct0). Travels out of band: as the x-auth-token request header on most tools, or inside the JSON request body on the tools that take one. Never a query parameter, so it never reaches a URL or an access log.",
1061
1353
  ),
@@ -1068,6 +1360,12 @@ export const TOOLS = [
1068
1360
  user_agent: z.string().optional().describe(
1069
1361
  "Optional. User-Agent string to send for this session. Sent as the x-user-agent request header, or in the JSON body on a body-taking tool.",
1070
1362
  ),
1363
+ fields: z.string().optional().describe(
1364
+ "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.",
1365
+ ),
1366
+ compact: z.enum(["1","true"]).optional().describe(
1367
+ "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).",
1368
+ ),
1071
1369
  },
1072
1370
  },
1073
1371
  {
@@ -1366,6 +1664,12 @@ export const TOOLS = [
1366
1664
  user_agent: z.string().optional().describe(
1367
1665
  "Optional. User-Agent string to send for this session. Sent as the x-user-agent request header, or in the JSON body on a body-taking tool.",
1368
1666
  ),
1667
+ fields: z.string().optional().describe(
1668
+ "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.",
1669
+ ),
1670
+ compact: z.enum(["1","true"]).optional().describe(
1671
+ "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).",
1672
+ ),
1369
1673
  },
1370
1674
  },
1371
1675
  {
@@ -1452,6 +1756,12 @@ export const TOOLS = [
1452
1756
  user_agent: z.string().optional().describe(
1453
1757
  "Optional. User-Agent string to send for this session. Sent as the x-user-agent request header, or in the JSON body on a body-taking tool.",
1454
1758
  ),
1759
+ fields: z.string().optional().describe(
1760
+ "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.",
1761
+ ),
1762
+ compact: z.enum(["1","true"]).optional().describe(
1763
+ "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).",
1764
+ ),
1455
1765
  },
1456
1766
  },
1457
1767
  {
@@ -1631,10 +1941,13 @@ export const TOOLS = [
1631
1941
  method: "POST",
1632
1942
  write: true,
1633
1943
  description:
1634
- "Follow a user AS your authenticated account, by numeric user_id. Requires write capability behind your key. Reverse with twitter_unfollow_user.",
1944
+ "Follow a user AS your authenticated account, by numeric user_id or by @handle (provide exactly one). Requires write capability behind your key. Reverse with twitter_unfollow_user.",
1635
1945
  shape: {
1636
- user_id: z.string().describe(
1637
- "Numeric user id of the account to follow. Resolve a handle to a user_id first with twitter_user_info.",
1946
+ user_id: z.string().optional().describe(
1947
+ "Numeric user id of the account to follow. Provide exactly one of user_id or username; user_id skips the handle lookup.",
1948
+ ),
1949
+ username: z.string().optional().describe(
1950
+ "The @handle WITHOUT the leading @ (e.g. \"elonmusk\") of the account to follow. Provide exactly one of user_id or username; the API resolves the handle to its id on every call, never from a cache, so a renamed account is followed by its current handle.",
1638
1951
  ),
1639
1952
  auth_token: z.string().optional().describe(
1640
1953
  "Optional. The account's auth_token cookie, to act AS that account for this call (must be paired with ct0). Travels out of band: as the x-auth-token request header on most tools, or inside the JSON request body on the tools that take one. Never a query parameter, so it never reaches a URL or an access log.",
@@ -1657,10 +1970,13 @@ export const TOOLS = [
1657
1970
  write: true,
1658
1971
  destructive: true,
1659
1972
  description:
1660
- "Unfollow a user AS your authenticated account, by numeric user_id. Requires write capability behind your key.",
1973
+ "Unfollow a user AS your authenticated account, by numeric user_id or by @handle (provide exactly one). Requires write capability behind your key.",
1661
1974
  shape: {
1662
- user_id: z.string().describe(
1663
- "Numeric user id of the account to unfollow.",
1975
+ user_id: z.string().optional().describe(
1976
+ "Numeric user id of the account to unfollow. Provide exactly one of user_id or username.",
1977
+ ),
1978
+ username: z.string().optional().describe(
1979
+ "The @handle WITHOUT the leading @ of the account to unfollow. Provide exactly one of user_id or username; resolved to its id on every call, never from a cache.",
1664
1980
  ),
1665
1981
  auth_token: z.string().optional().describe(
1666
1982
  "Optional. The account's auth_token cookie, to act AS that account for this call (must be paired with ct0). Travels out of band: as the x-auth-token request header on most tools, or inside the JSON request body on the tools that take one. Never a query parameter, so it never reaches a URL or an access log.",
@@ -1876,6 +2192,12 @@ export const TOOLS = [
1876
2192
  user_agent: z.string().optional().describe(
1877
2193
  "Optional. User-Agent string to send for this session. Sent as the x-user-agent request header, or in the JSON body on a body-taking tool.",
1878
2194
  ),
2195
+ fields: z.string().optional().describe(
2196
+ "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.",
2197
+ ),
2198
+ compact: z.enum(["1","true"]).optional().describe(
2199
+ "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).",
2200
+ ),
1879
2201
  },
1880
2202
  },
1881
2203
  {
@@ -2075,6 +2397,12 @@ export const TOOLS = [
2075
2397
  user_agent: z.string().optional().describe(
2076
2398
  "Optional. User-Agent string to send for this session. Sent as the x-user-agent request header, or in the JSON body on a body-taking tool.",
2077
2399
  ),
2400
+ fields: z.string().optional().describe(
2401
+ "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.",
2402
+ ),
2403
+ compact: z.enum(["1","true"]).optional().describe(
2404
+ "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).",
2405
+ ),
2078
2406
  },
2079
2407
  },
2080
2408
  {
@@ -2104,6 +2432,12 @@ export const TOOLS = [
2104
2432
  user_agent: z.string().optional().describe(
2105
2433
  "Optional. User-Agent string to send for this session. Sent as the x-user-agent request header, or in the JSON body on a body-taking tool.",
2106
2434
  ),
2435
+ fields: z.string().optional().describe(
2436
+ "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.",
2437
+ ),
2438
+ compact: z.enum(["1","true"]).optional().describe(
2439
+ "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).",
2440
+ ),
2107
2441
  },
2108
2442
  },
2109
2443
  {
@@ -2238,6 +2572,12 @@ export const TOOLS = [
2238
2572
  limit: z.number().int().min(1).max(200).optional().describe(
2239
2573
  "Max delivery events to return, 1 to 200. Defaults to 50 when omitted.",
2240
2574
  ),
2575
+ fields: z.string().optional().describe(
2576
+ "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.",
2577
+ ),
2578
+ compact: z.enum(["1","true"]).optional().describe(
2579
+ "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).",
2580
+ ),
2241
2581
  },
2242
2582
  },
2243
2583
  {