@realaman90/x-mcp 1.0.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/README.md +103 -0
- package/index.js +126 -0
- package/package.json +16 -0
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# x-mcp
|
|
2
|
+
|
|
3
|
+
MCP server for X/Twitter API — give Claude (or any MCP client) the ability to search tweets, read profiles, get timelines, and read replies.
|
|
4
|
+
|
|
5
|
+
**Read-only. No posting, liking, or following. Safe to use with AI agents.**
|
|
6
|
+
|
|
7
|
+
## Why
|
|
8
|
+
|
|
9
|
+
- Find engagement opportunities on X without leaving Claude
|
|
10
|
+
- Study reply patterns and conversations around any topic
|
|
11
|
+
- Research accounts, their content, and audience reactions
|
|
12
|
+
- All read-only — no risk of accidental tweets or follows
|
|
13
|
+
|
|
14
|
+
## Tools
|
|
15
|
+
|
|
16
|
+
| Tool | Description | X API Endpoint |
|
|
17
|
+
|------|-------------|----------------|
|
|
18
|
+
| `search_tweets` | Search recent tweets (last 7 days) with full query operators | `GET /2/tweets/search/recent` |
|
|
19
|
+
| `get_user_profile` | Get user profile by username (bio, followers, etc.) | `GET /2/users/by/username/{username}` |
|
|
20
|
+
| `get_user_tweets` | Get a user's recent tweets by user ID | `GET /2/users/{id}/tweets` |
|
|
21
|
+
| `get_tweet_replies` | Get replies to a specific tweet | Search `conversation_id:{id}` |
|
|
22
|
+
| `get_tweet` | Get a single tweet with full details and metrics | `GET /2/tweets/{id}` |
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### 1. Get an X API Bearer Token
|
|
27
|
+
|
|
28
|
+
1. Go to [developer.x.com](https://developer.x.com) and sign in
|
|
29
|
+
2. Click **Developer Portal** → **Projects & Apps**
|
|
30
|
+
3. Create a new **Project** (any name)
|
|
31
|
+
4. Create an **App** inside the project
|
|
32
|
+
5. Go to **Keys and Tokens** → generate a **Bearer Token**
|
|
33
|
+
6. Copy the token — you'll need it in step 2
|
|
34
|
+
|
|
35
|
+
> The **Basic** tier ($200/mo pay-per-usage) is sufficient. Free tier works too but has lower rate limits.
|
|
36
|
+
|
|
37
|
+
### 2. Add to Claude Code
|
|
38
|
+
|
|
39
|
+
Add to `~/.claude/settings.json`:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"mcpServers": {
|
|
44
|
+
"x": {
|
|
45
|
+
"type": "stdio",
|
|
46
|
+
"command": "npx",
|
|
47
|
+
"args": ["-y", "x-mcp"],
|
|
48
|
+
"env": {
|
|
49
|
+
"X_BEARER_TOKEN": "your-bearer-token"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Or add to Claude Desktop
|
|
57
|
+
|
|
58
|
+
Open **Settings** → **Developer** → **Edit Config** and add the same block above.
|
|
59
|
+
|
|
60
|
+
### 3. Restart Claude and test
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
Search X for "AI agents" in English, no retweets
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Claude will use the `search_tweets` tool automatically.
|
|
67
|
+
|
|
68
|
+
## Usage Examples
|
|
69
|
+
|
|
70
|
+
### Search tweets
|
|
71
|
+
```
|
|
72
|
+
"AI video" lang:en -is:retweet # English tweets about AI video, no RTs
|
|
73
|
+
from:elonmusk has:media # Elon's tweets with media
|
|
74
|
+
#buildinpublic -is:reply # Hashtag, original tweets only
|
|
75
|
+
"machine learning" has:links min_faves:10 # ML tweets with links, 10+ likes
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Chain tools together
|
|
79
|
+
1. `get_user_profile` → get user ID from username
|
|
80
|
+
2. `get_user_tweets` → get their recent tweets
|
|
81
|
+
3. `get_tweet_replies` → see replies on a specific tweet
|
|
82
|
+
|
|
83
|
+
### Run directly (without Claude)
|
|
84
|
+
```bash
|
|
85
|
+
X_BEARER_TOKEN="your-token" npx x-mcp
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Requirements
|
|
89
|
+
|
|
90
|
+
- **Node.js 18+** (for native `fetch`)
|
|
91
|
+
- **X API Bearer Token** — [Get one here](https://developer.x.com)
|
|
92
|
+
|
|
93
|
+
## How It Works
|
|
94
|
+
|
|
95
|
+
Single-file MCP server (~120 lines). No build step, no config files. Reads `X_BEARER_TOKEN` from environment, connects via stdio.
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
Claude ↔ stdio ↔ x-mcp ↔ X API v2
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
MIT
|
package/index.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
|
|
7
|
+
const BEARER_TOKEN = decodeURIComponent(process.env.X_BEARER_TOKEN || "");
|
|
8
|
+
if (!BEARER_TOKEN) {
|
|
9
|
+
console.error("X_BEARER_TOKEN environment variable is required");
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const TWEET_FIELDS = "created_at,public_metrics,author_id,conversation_id,in_reply_to_user_id,lang";
|
|
14
|
+
const USER_FIELDS = "created_at,description,public_metrics,profile_image_url,url,verified";
|
|
15
|
+
|
|
16
|
+
async function xapi(endpoint, params = {}) {
|
|
17
|
+
const url = new URL(`https://api.x.com/2${endpoint}`);
|
|
18
|
+
for (const [k, v] of Object.entries(params)) {
|
|
19
|
+
if (v !== undefined) url.searchParams.set(k, String(v));
|
|
20
|
+
}
|
|
21
|
+
const res = await fetch(url, {
|
|
22
|
+
headers: { Authorization: `Bearer ${BEARER_TOKEN}` },
|
|
23
|
+
});
|
|
24
|
+
if (!res.ok) {
|
|
25
|
+
const body = await res.text();
|
|
26
|
+
throw new Error(`X API ${res.status}: ${body}`);
|
|
27
|
+
}
|
|
28
|
+
return res.json();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const server = new McpServer({
|
|
32
|
+
name: "x-mcp",
|
|
33
|
+
version: "1.0.0",
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// 1. Search tweets
|
|
37
|
+
server.tool(
|
|
38
|
+
"search_tweets",
|
|
39
|
+
"Search recent tweets by keywords, hashtags, or advanced operators. Returns up to 100 tweets from the last 7 days.",
|
|
40
|
+
{
|
|
41
|
+
query: z.string().describe("Search query (supports X search operators like lang:en, -is:retweet, from:username)"),
|
|
42
|
+
max_results: z.number().min(10).max(100).default(10).describe("Number of results (10-100)"),
|
|
43
|
+
},
|
|
44
|
+
async ({ query, max_results }) => {
|
|
45
|
+
const data = await xapi("/tweets/search/recent", {
|
|
46
|
+
query,
|
|
47
|
+
max_results,
|
|
48
|
+
"tweet.fields": TWEET_FIELDS,
|
|
49
|
+
expansions: "author_id",
|
|
50
|
+
"user.fields": "username,name",
|
|
51
|
+
});
|
|
52
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// 2. Get user profile
|
|
57
|
+
server.tool(
|
|
58
|
+
"get_user_profile",
|
|
59
|
+
"Get an X/Twitter user's profile by username. Returns bio, follower counts, and account details.",
|
|
60
|
+
{
|
|
61
|
+
username: z.string().describe("X username (without @)"),
|
|
62
|
+
},
|
|
63
|
+
async ({ username }) => {
|
|
64
|
+
const data = await xapi(`/users/by/username/${username}`, {
|
|
65
|
+
"user.fields": USER_FIELDS,
|
|
66
|
+
});
|
|
67
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
// 3. Get user's tweets
|
|
72
|
+
server.tool(
|
|
73
|
+
"get_user_tweets",
|
|
74
|
+
"Get recent tweets from a specific user by their user ID. Use get_user_profile first to get the ID from a username.",
|
|
75
|
+
{
|
|
76
|
+
user_id: z.string().describe("X user ID (numeric string — get this from get_user_profile)"),
|
|
77
|
+
max_results: z.number().min(5).max(100).default(10).describe("Number of results (5-100)"),
|
|
78
|
+
},
|
|
79
|
+
async ({ user_id, max_results }) => {
|
|
80
|
+
const data = await xapi(`/users/${user_id}/tweets`, {
|
|
81
|
+
max_results,
|
|
82
|
+
"tweet.fields": TWEET_FIELDS,
|
|
83
|
+
});
|
|
84
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
// 4. Get tweet replies
|
|
89
|
+
server.tool(
|
|
90
|
+
"get_tweet_replies",
|
|
91
|
+
"Get replies to a specific tweet using its conversation ID. Returns recent replies from the last 7 days.",
|
|
92
|
+
{
|
|
93
|
+
tweet_id: z.string().describe("Tweet ID to find replies for (used as conversation_id)"),
|
|
94
|
+
max_results: z.number().min(10).max(100).default(10).describe("Number of results (10-100)"),
|
|
95
|
+
},
|
|
96
|
+
async ({ tweet_id, max_results }) => {
|
|
97
|
+
const data = await xapi("/tweets/search/recent", {
|
|
98
|
+
query: `conversation_id:${tweet_id}`,
|
|
99
|
+
max_results,
|
|
100
|
+
"tweet.fields": TWEET_FIELDS,
|
|
101
|
+
expansions: "author_id",
|
|
102
|
+
"user.fields": "username,name",
|
|
103
|
+
});
|
|
104
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
// 5. Get single tweet
|
|
109
|
+
server.tool(
|
|
110
|
+
"get_tweet",
|
|
111
|
+
"Get a single tweet by ID with full details including metrics, author info, and conversation context.",
|
|
112
|
+
{
|
|
113
|
+
tweet_id: z.string().describe("Tweet ID"),
|
|
114
|
+
},
|
|
115
|
+
async ({ tweet_id }) => {
|
|
116
|
+
const data = await xapi(`/tweets/${tweet_id}`, {
|
|
117
|
+
"tweet.fields": TWEET_FIELDS,
|
|
118
|
+
expansions: "author_id",
|
|
119
|
+
"user.fields": "username,name",
|
|
120
|
+
});
|
|
121
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const transport = new StdioServerTransport();
|
|
126
|
+
await server.connect(transport);
|
package/package.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@realaman90/x-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for X/Twitter API — search tweets, read profiles, get timelines and replies",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"x-mcp": "index.js"
|
|
9
|
+
},
|
|
10
|
+
"keywords": ["mcp", "twitter", "x", "claude", "ai"],
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
14
|
+
"zod": "^3.24.4"
|
|
15
|
+
}
|
|
16
|
+
}
|