@twitterapis/mcp 0.5.0 → 0.6.1
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 +19 -0
- package/README.md +19 -3
- package/package.json +5 -3
- package/src/index.js +8 -2
- package/src/tools.js +19 -24
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.1 (2026-07-20)
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **The server advertised the wrong version.** `serverInfo.version` in the MCP handshake and the outbound `user-agent` header were both hardcoded to `0.3.0`, so every client since 0.4.0 was told it was talking to 0.3.0. Both now derive from `package.json`, so the literal cannot drift again.
|
|
8
|
+
- Corrected a factual error in the 0.6.0 changelog entry below: the endpoint that stopped advertising `count` alongside `twitter_tweet_replies` is `twitter_tweet_thread`, not `twitter_tweet_retweeters`. `twitter_tweet_retweeters` does accept `count` and is unchanged.
|
|
9
|
+
|
|
10
|
+
## 0.6.0 (2026-07-20)
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **Removed three phantom parameters from the published tool schemas.** Eleven write tools (`twitter_create_tweet`, `twitter_delete_tweet`, `twitter_favorite_tweet` / `twitter_unfavorite_tweet`, `twitter_retweet` / `twitter_unretweet`, `twitter_bookmark_tweet` / `twitter_unbookmark_tweet`, `twitter_follow_user` / `twitter_unfollow_user`, `twitter_dm_send`) advertised an optional `account` parameter that the API never accepted, so agents that passed it were silently ignored. `twitter_tweet_replies` and `twitter_tweet_thread` advertised the full pagination shape when the endpoint only accepts `cursor`.
|
|
15
|
+
- A fail-closed MCP-to-OpenAPI parity gate now runs on every `npm test`, so a tool schema can no longer drift from the live API contract unnoticed.
|
|
16
|
+
- README: corrected the Links section (the REST base URL is `https://api.twitterapis.com`; removed a link to a status page that does not exist) and added an FAQ covering signup, read-vs-write scope, supported clients, billing, and data handling.
|
|
17
|
+
|
|
18
|
+
### Breaking
|
|
19
|
+
|
|
20
|
+
- If your client explicitly passed `account` to a write tool, or `count` to `twitter_tweet_replies` / `twitter_tweet_thread`, those keys are no longer part of the schema. They were never honoured by the API, so behaviour is unchanged; only the advertised schema is now accurate.
|
|
21
|
+
|
|
3
22
|
## 0.5.0 (2026-07-06)
|
|
4
23
|
|
|
5
24
|
### Added
|
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# @twitterapis/mcp
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@twitterapis/mcp)
|
|
4
|
+
[](https://www.npmjs.com/package/@twitterapis/mcp)
|
|
5
|
+
[](./LICENSE)
|
|
6
|
+
|
|
3
7
|
Official **Model Context Protocol** server for [twitterapis.com](https://www.twitterapis.com), the Twitter / X API as native tools for Claude, Cursor, Windsurf, and any MCP client. Reads (search, profiles, timelines, followers, DMs) plus write actions (post, like, retweet, follow).
|
|
4
8
|
|
|
5
9
|
Ask your agent to search tweets, pull a user's profile or timeline, list followers/following, fetch thread context, or enumerate list members and it calls the API directly. Every tool maps to a REST endpoint at `https://api.twitterapis.com`; the server holds no state and forwards your API key on each call.
|
|
@@ -204,14 +208,26 @@ count: 50
|
|
|
204
208
|
|
|
205
209
|
## Pricing
|
|
206
210
|
|
|
207
|
-
Calls are billed to your twitterapis.com account
|
|
211
|
+
Calls are billed to your twitterapis.com account. Almost every endpoint is $0.0008/call: all reads (search, profiles, tweets, followers, likes) plus the simple write actions (like, retweet, bookmark, follow and their undos, delete). At the read rate that works out to $0.04 per 1,000 tweets, since each call returns about 20 tweets. The premium endpoints cost a little more: tweet creation, sending a DM (`twitter_dm_send`), and DM reads (`twitter_dm_list`, `twitter_dm_conversation`) at $0.0016/call, full tweet history (`twitter_user_tweets_complete`) at $0.0024/call, and a full tweet thread (`twitter_tweet_thread`) at $0.004/call. Your first $0.50 is free. See [twitterapis.com/pricing](https://www.twitterapis.com/pricing).
|
|
208
212
|
|
|
209
213
|
## Links
|
|
210
214
|
|
|
211
215
|
- Docs: [docs.twitterapis.com](https://docs.twitterapis.com)
|
|
212
216
|
- Dashboard / API keys: [twitterapis.com/dashboard](https://www.twitterapis.com/dashboard)
|
|
213
|
-
-
|
|
214
|
-
-
|
|
217
|
+
- Pricing: [twitterapis.com/pricing](https://www.twitterapis.com/pricing)
|
|
218
|
+
- REST API base URL (call it directly, without MCP): `https://api.twitterapis.com`
|
|
219
|
+
|
|
220
|
+
## FAQ
|
|
221
|
+
|
|
222
|
+
**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.
|
|
223
|
+
|
|
224
|
+
**Is it read-only?** No. 29 read tools work with just your API key; 11 write actions (post, like, retweet, follow, DM) act as a linked X account or per-call inline credentials.
|
|
225
|
+
|
|
226
|
+
**Which clients are supported?** Claude Desktop, Cursor, Windsurf, and VS Code (Copilot agent mode), or any Model Context Protocol client.
|
|
227
|
+
|
|
228
|
+
**How is it billed?** Per request. New keys start with $0.50 in free credits, no card required. See [pricing](https://www.twitterapis.com/pricing).
|
|
229
|
+
|
|
230
|
+
**Does it store my key or data?** No. The server holds no state and forwards your API key on each call.
|
|
215
231
|
|
|
216
232
|
## License
|
|
217
233
|
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@twitterapis/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
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",
|
|
7
|
-
"url": "https://github.com/TwitterAPIs/twitterapis-mcp.git"
|
|
7
|
+
"url": "git+https://github.com/TwitterAPIs/twitterapis-mcp.git"
|
|
8
8
|
},
|
|
9
9
|
"type": "module",
|
|
10
10
|
"bin": {
|
|
@@ -23,7 +23,9 @@
|
|
|
23
23
|
"scripts": {
|
|
24
24
|
"start": "node src/index.js",
|
|
25
25
|
"check": "node --check src/index.js && node --check src/tools.js",
|
|
26
|
-
"test": "node test/tools.test.mjs && node test/smoke.mjs"
|
|
26
|
+
"test": "node test/tools.test.mjs && node test/smoke.mjs && node test/openapi-parity.mjs && node test/firewall.mjs",
|
|
27
|
+
"check:openapi-parity": "node test/openapi-parity.mjs",
|
|
28
|
+
"check:firewall": "node test/firewall.mjs"
|
|
27
29
|
},
|
|
28
30
|
"dependencies": {
|
|
29
31
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
package/src/index.js
CHANGED
|
@@ -16,8 +16,14 @@
|
|
|
16
16
|
//
|
|
17
17
|
// Run: npx -y @twitterapis/mcp@latest (stdio transport)
|
|
18
18
|
|
|
19
|
+
import { createRequire } from "node:module";
|
|
19
20
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
20
21
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
22
|
+
|
|
23
|
+
// Single source of truth for the version. Previously this was a literal in two
|
|
24
|
+
// places and drifted: the package shipped 0.5.0 while the MCP handshake and the
|
|
25
|
+
// outbound user-agent both still advertised 0.3.0.
|
|
26
|
+
const VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
21
27
|
import { TOOLS, buildQuery } from "./tools.js";
|
|
22
28
|
|
|
23
29
|
const API_KEY = process.env.TWITTERAPIS_KEY;
|
|
@@ -52,7 +58,7 @@ async function callEndpoint(path, args, method = "GET") {
|
|
|
52
58
|
Authorization: `Bearer ${API_KEY}`,
|
|
53
59
|
"x-api-key": API_KEY,
|
|
54
60
|
accept: "application/json",
|
|
55
|
-
"user-agent":
|
|
61
|
+
"user-agent": `twitterapis-mcp/${VERSION}`,
|
|
56
62
|
};
|
|
57
63
|
if (auth_token && ct0) {
|
|
58
64
|
headers["x-auth-token"] = auth_token;
|
|
@@ -99,7 +105,7 @@ async function callEndpoint(path, args, method = "GET") {
|
|
|
99
105
|
}
|
|
100
106
|
|
|
101
107
|
// ── MCP server ───────────────────────────────────────────────────────────────
|
|
102
|
-
const server = new McpServer({ name: "twitterapis", version:
|
|
108
|
+
const server = new McpServer({ name: "twitterapis", version: VERSION });
|
|
103
109
|
|
|
104
110
|
for (const tool of TOOLS) {
|
|
105
111
|
const method = tool.method || "GET";
|
package/src/tools.js
CHANGED
|
@@ -10,13 +10,16 @@
|
|
|
10
10
|
import { z } from "zod";
|
|
11
11
|
|
|
12
12
|
// ── Shared Zod input-schema fragments ───────────────────────────────────────
|
|
13
|
+
const CURSOR = {
|
|
14
|
+
cursor: z.string().optional().describe(
|
|
15
|
+
"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.",
|
|
16
|
+
),
|
|
17
|
+
};
|
|
13
18
|
const PAGINATION = {
|
|
14
19
|
count: z.number().int().min(1).max(200).optional().describe(
|
|
15
20
|
"Max items to return for this page. Typical range 1 to 200; endpoint default (20) applies if omitted. To page through results, pass the cursor from the previous response.",
|
|
16
21
|
),
|
|
17
|
-
|
|
18
|
-
"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.",
|
|
19
|
-
),
|
|
22
|
+
...CURSOR,
|
|
20
23
|
};
|
|
21
24
|
const USER_REF = {
|
|
22
25
|
username: z.string().optional().describe(
|
|
@@ -34,14 +37,6 @@ const TWEET_REF = {
|
|
|
34
37
|
'Full tweet URL, e.g. "https://x.com/elonmusk/status/1789012345678901234". Provide exactly one of id or url.',
|
|
35
38
|
),
|
|
36
39
|
};
|
|
37
|
-
// Optional pooled-session selector, shared by every write action. A customer
|
|
38
|
-
// key maps to a default authenticated session; pass account only to target a
|
|
39
|
-
// specific handle in a multi-account pool.
|
|
40
|
-
const ACCOUNT = {
|
|
41
|
-
account: z.string().optional().describe(
|
|
42
|
-
"Optional. The @handle (without @) of the authenticated account to act AS, when your key manages more than one session. Omit to use your key's default session.",
|
|
43
|
-
),
|
|
44
|
-
};
|
|
45
40
|
// Per-call inline credentials. Pass an account's own X session cookies to act AS
|
|
46
41
|
// that account for this one call, without pre-registering a session, so a single
|
|
47
42
|
// API key can act as many accounts (e.g. polling several inboxes or posting from
|
|
@@ -272,14 +267,14 @@ export const TOOLS = [
|
|
|
272
267
|
path: "/twitter/tweet/replies",
|
|
273
268
|
description:
|
|
274
269
|
"Get replies to a specific tweet. Returns each reply tweet with author, text, and metrics. Paginate with cursor to load more. Use this to read the conversation under a tweet, gauge sentiment, or find notable responses.",
|
|
275
|
-
shape: { ...TWEET_REF, ...
|
|
270
|
+
shape: { ...TWEET_REF, ...CURSOR },
|
|
276
271
|
},
|
|
277
272
|
{
|
|
278
273
|
name: "twitter_tweet_thread",
|
|
279
274
|
path: "/twitter/tweet/thread",
|
|
280
275
|
description:
|
|
281
276
|
"Get all tweets in a thread: the connected chain of tweets posted by the SAME author in sequence (a tweetstorm or numbered thread). Pass any tweet id/url from the thread and the API returns the full ordered sequence. Paginate with cursor for long threads. Does NOT return replies from other users, use twitter_tweet_replies for that.",
|
|
282
|
-
shape: { ...TWEET_REF, ...
|
|
277
|
+
shape: { ...TWEET_REF, ...CURSOR },
|
|
283
278
|
},
|
|
284
279
|
{
|
|
285
280
|
name: "twitter_tweet_retweeters",
|
|
@@ -361,7 +356,7 @@ export const TOOLS = [
|
|
|
361
356
|
text: z.string().min(1).describe(
|
|
362
357
|
"The Direct Message body text to send (non-empty).",
|
|
363
358
|
),
|
|
364
|
-
...
|
|
359
|
+
...INLINE,
|
|
365
360
|
},
|
|
366
361
|
},
|
|
367
362
|
|
|
@@ -386,7 +381,7 @@ export const TOOLS = [
|
|
|
386
381
|
media_ids: z.string().optional().describe(
|
|
387
382
|
"Optional. Comma-separated media id(s) from a prior media upload to attach (images/video).",
|
|
388
383
|
),
|
|
389
|
-
...
|
|
384
|
+
...INLINE,
|
|
390
385
|
},
|
|
391
386
|
},
|
|
392
387
|
{
|
|
@@ -397,7 +392,7 @@ export const TOOLS = [
|
|
|
397
392
|
destructive: true,
|
|
398
393
|
description:
|
|
399
394
|
"Delete a tweet AS your authenticated account. Irreversible: the tweet is permanently removed. You can only delete tweets your authenticated account authored. Provide the tweet id or url. Requires write capability behind your key.",
|
|
400
|
-
shape: { ...TWEET_REF, ...
|
|
395
|
+
shape: { ...TWEET_REF, ...INLINE },
|
|
401
396
|
},
|
|
402
397
|
// ── Writes: engagement (favorite / retweet / bookmark) + inverses ──────────
|
|
403
398
|
{
|
|
@@ -407,7 +402,7 @@ export const TOOLS = [
|
|
|
407
402
|
write: true,
|
|
408
403
|
description:
|
|
409
404
|
"Like (favorite) a tweet AS your authenticated account. Provide the tweet id or url. Requires write capability behind your key. Reverse with twitter_unfavorite_tweet.",
|
|
410
|
-
shape: { ...TWEET_REF, ...
|
|
405
|
+
shape: { ...TWEET_REF, ...INLINE },
|
|
411
406
|
},
|
|
412
407
|
{
|
|
413
408
|
name: "twitter_unfavorite_tweet",
|
|
@@ -417,7 +412,7 @@ export const TOOLS = [
|
|
|
417
412
|
destructive: true,
|
|
418
413
|
description:
|
|
419
414
|
"Remove a like (unfavorite) from a tweet AS your authenticated account. Provide the tweet id or url. Requires write capability behind your key.",
|
|
420
|
-
shape: { ...TWEET_REF, ...
|
|
415
|
+
shape: { ...TWEET_REF, ...INLINE },
|
|
421
416
|
},
|
|
422
417
|
{
|
|
423
418
|
name: "twitter_retweet",
|
|
@@ -426,7 +421,7 @@ export const TOOLS = [
|
|
|
426
421
|
write: true,
|
|
427
422
|
description:
|
|
428
423
|
"Retweet a tweet AS your authenticated account. Provide the tweet id or url. Requires write capability behind your key. Reverse with twitter_unretweet.",
|
|
429
|
-
shape: { ...TWEET_REF, ...
|
|
424
|
+
shape: { ...TWEET_REF, ...INLINE },
|
|
430
425
|
},
|
|
431
426
|
{
|
|
432
427
|
name: "twitter_unretweet",
|
|
@@ -436,7 +431,7 @@ export const TOOLS = [
|
|
|
436
431
|
destructive: true,
|
|
437
432
|
description:
|
|
438
433
|
"Undo a retweet AS your authenticated account. Provide the tweet id or url. Requires write capability behind your key.",
|
|
439
|
-
shape: { ...TWEET_REF, ...
|
|
434
|
+
shape: { ...TWEET_REF, ...INLINE },
|
|
440
435
|
},
|
|
441
436
|
{
|
|
442
437
|
name: "twitter_bookmark_tweet",
|
|
@@ -445,7 +440,7 @@ export const TOOLS = [
|
|
|
445
440
|
write: true,
|
|
446
441
|
description:
|
|
447
442
|
"Bookmark a tweet to YOUR authenticated account's private bookmarks. Provide the tweet id or url. Requires write capability behind your key. Reverse with twitter_unbookmark_tweet.",
|
|
448
|
-
shape: { ...TWEET_REF, ...
|
|
443
|
+
shape: { ...TWEET_REF, ...INLINE },
|
|
449
444
|
},
|
|
450
445
|
{
|
|
451
446
|
name: "twitter_unbookmark_tweet",
|
|
@@ -455,7 +450,7 @@ export const TOOLS = [
|
|
|
455
450
|
destructive: true,
|
|
456
451
|
description:
|
|
457
452
|
"Remove a tweet from YOUR authenticated account's bookmarks. Provide the tweet id or url. Requires write capability behind your key.",
|
|
458
|
-
shape: { ...TWEET_REF, ...
|
|
453
|
+
shape: { ...TWEET_REF, ...INLINE },
|
|
459
454
|
},
|
|
460
455
|
// ── Writes: follow graph ───────────────────────────────────────────────────
|
|
461
456
|
{
|
|
@@ -469,7 +464,7 @@ export const TOOLS = [
|
|
|
469
464
|
user_id: z.string().describe(
|
|
470
465
|
"Numeric user id of the account to follow. Resolve a handle to a user_id first with twitter_user_info.",
|
|
471
466
|
),
|
|
472
|
-
...
|
|
467
|
+
...INLINE,
|
|
473
468
|
},
|
|
474
469
|
},
|
|
475
470
|
{
|
|
@@ -484,7 +479,7 @@ export const TOOLS = [
|
|
|
484
479
|
user_id: z.string().describe(
|
|
485
480
|
"Numeric user id of the account to unfollow.",
|
|
486
481
|
),
|
|
487
|
-
...
|
|
482
|
+
...INLINE,
|
|
488
483
|
},
|
|
489
484
|
},
|
|
490
485
|
];
|