spectre-mcp 1.0.0__tar.gz

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.
@@ -0,0 +1,30 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ *.egg
8
+
9
+ # Virtual env
10
+ .venv/
11
+ venv/
12
+
13
+ # Data files (user-specific)
14
+ *.db
15
+ accounts.db
16
+ spectre.db
17
+
18
+ # IDE
19
+ .idea/
20
+ .vscode/
21
+ *.swp
22
+ *.swo
23
+
24
+ # OS
25
+ .DS_Store
26
+ Thumbs.db
27
+
28
+ # Env
29
+ .env
30
+ .env.local
@@ -0,0 +1 @@
1
+ 3.11
@@ -0,0 +1,437 @@
1
+ Metadata-Version: 2.4
2
+ Name: spectre-mcp
3
+ Version: 1.0.0
4
+ Summary: 104 tools for X/Twitter automation — direct GraphQL/REST API, no browser, no API keys, free.
5
+ Author: Pranjal Richhariya
6
+ License-Expression: MIT
7
+ Keywords: agent,ai-agent,automation,mcp,scraper,social-media,twitter,x
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Software Development :: Libraries
13
+ Requires-Python: >=3.11
14
+ Requires-Dist: curl-cffi>=0.15.0
15
+ Requires-Dist: fastmcp>=3.4.2
16
+ Requires-Dist: loguru>=0.7.3
17
+ Requires-Dist: pydantic>=2.13.4
18
+ Requires-Dist: twscrape>=0.19.0
19
+ Description-Content-Type: text/markdown
20
+
21
+ <div align="center">
22
+
23
+ # Spectre MCP
24
+
25
+ **104 tools for X/Twitter automation. Direct GraphQL/REST API. No browser. No API keys. Free.**
26
+
27
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-3776AB?style=flat-square&logo=python&logoColor=white)](https://python.org)
28
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green?style=flat-square)](LICENSE)
29
+ [![MCP Compatible](https://img.shields.io/badge/MCP-compatible-8B5CF6?style=flat-square)](https://modelcontextprotocol.io)
30
+ [![PyPI](https://img.shields.io/pypi/v/spectre-mcp?style=flat-square&color=cb3837&label=pypi)](https://pypi.org/project/spectre-mcp/)
31
+
32
+ [Quick Start](#-quick-start) · [All 104 Tools](#-all-104-tools) · [Why Spectre?](#-why-spectre) · [Safety Guide](#️-safety-guide)
33
+
34
+ </div>
35
+
36
+ ---
37
+
38
+ ## Why Spectre?
39
+
40
+ Spectre makes **direct HTTP calls** to X's internal GraphQL and REST APIs. No browser, no Puppeteer, no DOM scraping. It's the fastest and lightest way for AI agents to automate X/Twitter.
41
+
42
+ | | **Spectre** | **XActions** | **twikit** |
43
+ |--|:-:|:-:|:-:|
44
+ | **Architecture** | Direct GraphQL/REST API | Puppeteer (headless browser) | Python API wrapper |
45
+ | **Speed** | <1s per request | 3-10s (browser startup) | ~1s per request |
46
+ | **Memory** | ~10MB | ~200MB+ (Chromium) | ~10MB |
47
+ | **MCP Server** | ✅ 104 tools | ⚠️ ~50 API tools + wrappers | ❌ None |
48
+ | **Account Pool** | ✅ Multi-account auto-rotation | ❌ Single auth_token | ❌ Single session |
49
+ | **Browser Required** | ❌ Never | ✅ Always | ❌ Never |
50
+ | **DOM Breakage** | ❌ None | ⚠️ Breaks on UI changes | ❌ None |
51
+ | **Media Upload** | ✅ Cookie-based, no API keys | ⚠️ Requires API keys | ❌ |
52
+ | **Lists** | ✅ Full CRUD + members + subscribe | ❌ | ❌ |
53
+ | **Drafts & Scheduled** | ✅ Full CRUD | ⚠️ Basic | ❌ |
54
+ | **Bookmark Folders** | ✅ Full CRUD + timeline | ❌ | ❌ |
55
+ | **DM Search** | ✅ All/groups/people | ❌ | ❌ |
56
+ | **Community Notes** | ✅ Read + rate | ❌ | ❌ |
57
+ | **Highlights** | ✅ Create, delete, get | ❌ | ❌ |
58
+ | **Notifications** | ✅ Full timeline | ❌ | ❌ |
59
+ | **Language** | Python | JavaScript | Python |
60
+
61
+ ---
62
+
63
+ ## 🚀 Quick Start
64
+
65
+ ### Installation
66
+
67
+ ```bash
68
+ # Recommended — isolated, auto-updates
69
+ pipx install spectre-mcp
70
+
71
+ # Alternative
72
+ uv tool install spectre-mcp
73
+
74
+ # Fallback
75
+ pip install spectre-mcp
76
+ ```
77
+
78
+ ### Add Your X Account (one-time)
79
+
80
+ Get your cookies: **x.com → DevTools (F12) → Application → Cookies → copy `auth_token` and `ct0`**
81
+
82
+ ```bash
83
+ spectre add myaccount "auth_token=xxx; ct0=yyy"
84
+ ```
85
+
86
+ > `ct0` is 160 hex characters. Double-check you copied the full value.
87
+
88
+ ### Configure Your MCP Client
89
+
90
+ **Hermes Agent** (`~/.hermes/config.yaml`):
91
+ ```yaml
92
+ mcp_servers:
93
+ spectre:
94
+ command: "uvx"
95
+ args: ["spectre-mcp"]
96
+ timeout: 120
97
+ ```
98
+
99
+ **Claude Desktop / Cursor**:
100
+ ```json
101
+ {
102
+ "mcpServers": {
103
+ "spectre": {
104
+ "command": "uvx",
105
+ "args": ["spectre-mcp"]
106
+ }
107
+ }
108
+ }
109
+ ```
110
+
111
+ > No `uv`? Install it: `curl -LsSf https://astral.sh/uv/install.sh | sh`
112
+ > Or use `python3 -m spectre.server` instead of `uvx spectre-mcp`.
113
+
114
+ ### Updating
115
+
116
+ The MCP server updates automatically via `uvx` (always pulls latest from PyPI). For the CLI:
117
+
118
+ ```bash
119
+ pipx upgrade spectre-mcp # if installed via pipx
120
+ uv tool upgrade spectre-mcp # if installed via uv
121
+ ```
122
+
123
+ ---
124
+
125
+ ## 📦 All 104 Tools
126
+
127
+ ### Account Pool (5)
128
+
129
+ Manage multiple X accounts with automatic rotation on rate limits.
130
+
131
+ | Tool | Description |
132
+ |------|-------------|
133
+ | `list_accounts()` | List all accounts with status |
134
+ | `pool_status()` | Pool health stats |
135
+ | `set_active_account(username)` | Set primary account |
136
+ | `set_auto_rotate(enabled)` | Enable/disable auto-rotation |
137
+ | `remove_account(username)` | ⚠️ Remove account from pool |
138
+
139
+ ### Search (2)
140
+
141
+ | Tool | Description |
142
+ |------|-------------|
143
+ | `search(query, limit, mode)` | Search tweets. Supports `from:username`, `since:2026-01-01`, `#hashtag`, `filter:media` |
144
+ | `search_users(query, limit)` | Search users by name/keyword |
145
+
146
+ ### Users (12)
147
+
148
+ | Tool | Description |
149
+ |------|-------------|
150
+ | `get_user(username)` | User profile by @handle |
151
+ | `get_user_tweets(username, limit)` | User's recent tweets |
152
+ | `get_user_tweets_and_replies(username, limit)` | User's tweets and replies |
153
+ | `get_user_media(username, limit)` | User's photos/videos/GIFs |
154
+ | `get_followers(username, limit)` | User's followers |
155
+ | `get_following(username, limit)` | Who a user follows |
156
+ | `get_user_likes(user_id, limit)` | Get a user's liked tweets |
157
+ | `get_user_highlights(user_id, limit)` | Get user's highlighted tweets |
158
+ | `get_verified_followers(user_id, limit)` | Get Blue-verified followers |
159
+ | `get_followers_you_know(user_id, limit)` | Get mutual followers |
160
+ | `get_list_memberships(user_id, limit)` | Lists a user is a member of |
161
+ | `get_list_ownerships(user_id, limit)` | Lists owned by a user |
162
+
163
+ > **Note:** `get_list_memberships` and `get_list_ownerships` may return empty due to a known X API server-side issue.
164
+
165
+ ### Tweets (8)
166
+
167
+ | Tool | Description |
168
+ |------|-------------|
169
+ | `get_tweet(tweet_id)` | Single tweet by ID |
170
+ | `get_tweet_replies(tweet_id, limit)` | Replies to a tweet |
171
+ | `get_thread(tweet_id, limit)` | Full conversation thread |
172
+ | `get_retweeters(tweet_id, limit)` | Users who retweeted |
173
+ | `get_favoriters(tweet_id, limit)` | Users who liked a tweet |
174
+ | `get_tweet_edit_history(tweet_id)` | Edit history of a tweet |
175
+ | `get_similar_posts(tweet_id, limit)` | Get similar/related posts |
176
+ | `get_community_notes(tweet_id)` | Get community notes on a tweet |
177
+
178
+ ### Timeline & Notifications (3)
179
+
180
+ | Tool | Description |
181
+ |------|-------------|
182
+ | `get_trends(category, limit)` | Trending topics |
183
+ | `get_home_timeline(limit)` | Home feed |
184
+ | `get_notifications(limit)` | Notifications timeline |
185
+
186
+ ### Post & Media (3)
187
+
188
+ | Tool | Description |
189
+ |------|-------------|
190
+ | `post_tweet(text, reply_to?, quote_tweet?, media_ids?)` | Post a tweet with optional media |
191
+ | `upload_media(file_path)` | Upload image or video (jpg, png, gif, mp4, mov) — no API keys needed |
192
+ | `delete_tweet(tweet_id)` | ⚠️ Delete a tweet |
193
+
194
+ ### Engagement (6)
195
+
196
+ | Tool | Description |
197
+ |------|-------------|
198
+ | `like_tweet(tweet_id)` | Like a tweet |
199
+ | `unlike_tweet(tweet_id)` | Unlike a tweet |
200
+ | `retweet(tweet_id)` | Retweet |
201
+ | `unretweet(tweet_id)` | Undo retweet |
202
+ | `bookmark_tweet(tweet_id)` | Bookmark a tweet |
203
+ | `unbookmark_tweet(tweet_id)` | Remove bookmark |
204
+
205
+ ### Social (6)
206
+
207
+ | Tool | Description |
208
+ |------|-------------|
209
+ | `follow_user(user_id)` | ⚠️ Follow a user |
210
+ | `unfollow_user(user_id)` | ⚠️ Unfollow a user |
211
+ | `mute_user(user_id)` | Mute a user |
212
+ | `unmute_user(user_id)` | Unmute a user |
213
+ | `block_user(user_id)` | ⚠️ Block a user |
214
+ | `unblock_user(user_id)` | ⚠️ Unblock a user |
215
+
216
+ ### Social Extended (3)
217
+
218
+ | Tool | Description |
219
+ |------|-------------|
220
+ | `remove_follower(user_id)` | ⚠️ Remove a follower |
221
+ | `pin_reply(tweet_id)` | Pin a reply to a conversation |
222
+ | `unpin_reply(tweet_id)` | Unpin a reply |
223
+
224
+ ### DMs (9)
225
+
226
+ | Tool | Description |
227
+ |------|-------------|
228
+ | `send_dm(user_id, text)` | ⚠️ Send a direct message |
229
+ | `get_dm_inbox(limit)` | Get recent DM conversations |
230
+ | `get_dm_conversation(conversation_id, limit)` | Get messages in a conversation |
231
+ | `search_dm(query, limit)` | Search all DMs by text |
232
+ | `search_dm_groups(query, limit)` | Search DMs in group conversations |
233
+ | `search_dm_people(query, limit)` | Search for people in DMs |
234
+ | `get_dm_muted(limit)` | Get muted DM conversations |
235
+ | `dm_block_user(user_id)` | Block a user in DMs |
236
+ | `dm_unblock_user(user_id)` | Unblock a user in DMs |
237
+
238
+ ### Lists (10)
239
+
240
+ Full lifecycle: create → manage members → subscribe → delete.
241
+
242
+ | Tool | Description |
243
+ |------|-------------|
244
+ | `create_list(name, description)` | Create a new list |
245
+ | `update_list(list_id, name, description)` | Update list details |
246
+ | `delete_list(list_id)` | ⚠️ Delete a list |
247
+ | `add_list_member(list_id, user_id)` | Add user to list |
248
+ | `remove_list_member(list_id, user_id)` | Remove user from list |
249
+ | `get_list_timeline(list_id, limit)` | Tweets from a list |
250
+ | `get_list_members(list_id, limit)` | Members of a list |
251
+ | `get_list_subscribers(list_id, limit)` | List subscribers |
252
+ | `subscribe_list(list_id)` | Subscribe to a list |
253
+ | `unsubscribe_list(list_id)` | Unsubscribe from a list |
254
+
255
+ ### Bookmark Folders (8)
256
+
257
+ | Tool | Description |
258
+ |------|-------------|
259
+ | `get_bookmark_folders()` | List all bookmark folders |
260
+ | `create_bookmark_folder(name)` | Create a folder |
261
+ | `edit_bookmark_folder(folder_id, name)` | Rename a folder |
262
+ | `delete_bookmark_folder(folder_id)` | ⚠️ Delete a folder |
263
+ | `add_tweet_to_folder(folder_id, tweet_id)` | Add tweet to folder |
264
+ | `remove_tweet_from_folder(folder_id, tweet_id)` | Remove tweet from folder |
265
+ | `get_bookmark_folder_timeline(folder_id, limit)` | Get tweets in a folder |
266
+ | `search_bookmarks(query, limit)` | Search bookmarks by text |
267
+
268
+ > **Note:** Bookmark folders require a bookmark to exist first. Use `bookmark_tweet()` before testing folder operations.
269
+
270
+ ### Bookmarks (2)
271
+
272
+ | Tool | Description |
273
+ |------|-------------|
274
+ | `get_bookmarks(limit)` | Your bookmarked tweets |
275
+ | `clear_all_bookmarks()` | ⚠️ Delete ALL bookmarks |
276
+
277
+ ### Scheduled Tweets (4)
278
+
279
+ | Tool | Description |
280
+ |------|-------------|
281
+ | `schedule_tweet(text, execute_at, reply_to?)` | Schedule a tweet for future posting |
282
+ | `get_scheduled_tweets()` | List all scheduled tweets |
283
+ | `edit_scheduled_tweet(id, text, execute_at)` | Edit a scheduled tweet |
284
+ | `delete_scheduled_tweet(tweet_id)` | ⚠️ Delete a scheduled tweet |
285
+
286
+ ### Draft Tweets (4)
287
+
288
+ | Tool | Description |
289
+ |------|-------------|
290
+ | `create_draft(text)` | Save a tweet draft |
291
+ | `get_drafts()` | List all drafts |
292
+ | `edit_draft(draft_id, text)` | Edit a draft |
293
+ | `delete_draft(tweet_id)` | ⚠️ Delete a draft |
294
+
295
+ ### Communities (4)
296
+
297
+ | Tool | Description |
298
+ |------|-------------|
299
+ | `get_community_info(community_id)` | Community details |
300
+ | `get_community_tweets(community_id, limit)` | Community feed |
301
+ | `join_community(community_id)` | Join a community |
302
+ | `leave_community(community_id)` | ⚠️ Leave a community |
303
+
304
+ ### Topics (3)
305
+
306
+ | Tool | Description |
307
+ |------|-------------|
308
+ | `get_topic_info(topic_id)` | Get topic details |
309
+ | `follow_topic(topic_id)` | Follow a topic |
310
+ | `unfollow_topic(topic_id)` | Unfollow a topic |
311
+
312
+ ### Community Notes (2)
313
+
314
+ | Tool | Description |
315
+ |------|-------------|
316
+ | `get_community_notes(tweet_id)` | Get community notes on a tweet |
317
+ | `rate_community_note(note_id, rating)` | Rate a note as helpful/not_helpful |
318
+
319
+ > **Note:** Community Notes creation requires an eligible account (6+ months old, verified phone, enrolled in the program). This tool is not included in the current release.
320
+
321
+ ### Account Settings & Profile (5)
322
+
323
+ | Tool | Description |
324
+ |------|-------------|
325
+ | `get_account_settings()` | View account settings |
326
+ | `update_profile(name?, bio?, location?, website?)` | Update profile info |
327
+ | `update_profile_image(file_path)` | ⚠️ Change avatar image |
328
+ | `update_profile_banner(file_path)` | ⚠️ Change banner image |
329
+ | `delete_profile_banner()` | ⚠️ Remove banner |
330
+
331
+ ### Pin & Highlights (5)
332
+
333
+ | Tool | Description |
334
+ |------|-------------|
335
+ | `pin_tweet(tweet_id)` | ⚠️ Pin tweet to profile (replaces existing) |
336
+ | `unpin_tweet(tweet_id)` | Unpin tweet |
337
+ | `create_highlight(tweet_ids)` | ⚠️ Add tweets to profile highlights |
338
+ | `delete_highlight(highlight_id)` | ⚠️ Remove a highlight |
339
+ | `get_user_highlights(user_id, limit)` | Get user's highlighted tweets |
340
+
341
+ ### Safety & Privacy (4)
342
+
343
+ | Tool | Description |
344
+ |------|-------------|
345
+ | `get_muted_accounts(limit)` | List all muted accounts |
346
+ | `get_blocked_accounts(limit)` | List all blocked accounts |
347
+ | `get_verified_followers(user_id, limit)` | Get Blue-verified followers |
348
+ | `get_user_likes(user_id, limit)` | Get a user's liked tweets |
349
+
350
+ ---
351
+
352
+ ## ⚠️ Safety Guide
353
+
354
+ Spectre marks every destructive tool with **⚠️**. Agents should respect these warnings.
355
+
356
+ ### Always Confirm Before Calling
357
+
358
+ | Tool | Why |
359
+ |------|-----|
360
+ | `post_tweet` | Public, live immediately |
361
+ | `delete_tweet` | Permanent |
362
+ | `pin_tweet` | Replaces existing pin silently |
363
+ | `update_profile*` | Changes your public profile |
364
+ | `follow_user` / `unfollow_user` | Visible to the other user |
365
+ | `block_user` | Blocked user loses access to your content |
366
+ | `send_dm` | Sent immediately, no recall |
367
+ | `create_highlight` | Adds to your public profile |
368
+
369
+ ### Safe to Use Freely (Read-Only)
370
+
371
+ All `get_*`, `search_*`, `list_*` tools. `pool_status`, `get_muted_accounts`, `get_blocked_accounts`, `get_scheduled_tweets`, `get_drafts`, `get_bookmarks`, `get_trends`, `get_home_timeline`, `get_notifications`.
372
+
373
+ ### Rate Limits
374
+
375
+ - ~300 requests/hour/account
376
+ - Spectre auto-rotates to the next account on rate limits
377
+ - Use `set_auto_rotate(false)` to lock to a single account
378
+ - Rate limit errors include `retry_after_seconds`
379
+
380
+ ---
381
+
382
+ ## X Query Operators
383
+
384
+ | Operator | Example | Description |
385
+ |----------|---------|-------------|
386
+ | `from:username` | `from:elonmusk` | Tweets by a user |
387
+ | `since:YYYY-MM-DD` | `since:2026-01-01` | After date |
388
+ | `until:YYYY-MM-DD` | `until:2026-06-01` | Before date |
389
+ | `#hashtag` | `#python` | Hashtag search |
390
+ | `filter:media` | `AI filter:media` | Only media tweets |
391
+ | `filter:links` | `AI filter:links` | Only tweets with links |
392
+ | `lang:en` | `AI lang:en` | Language filter |
393
+ | `min_retweets:N` | `AI min_retweets:100` | Minimum retweets |
394
+ | `min_faves:N` | `AI min_faves:50` | Minimum likes |
395
+ | `-"term"` | `AI -"GPT"` | Exclude term |
396
+ | `OR` | `python OR rust` | Either term |
397
+
398
+ ---
399
+
400
+ ## CLI
401
+
402
+ ```bash
403
+ spectre add <username> <cookies> Add account via browser cookies
404
+ spectre remove <username> Remove an account
405
+ spectre list List all accounts
406
+ spectre status Pool health check
407
+ spectre serve Start MCP server (default)
408
+ spectre help Show help
409
+ ```
410
+
411
+ ---
412
+
413
+ ## Environment Variables
414
+
415
+ | Variable | Default | Description |
416
+ |----------|---------|-------------|
417
+ | `SPECTRE_DB` | `~/.spectre/accounts.db` | Account pool path |
418
+ | `SPECTRE_PROXY` | none | Global proxy (`socks5://user:pass@host:port`) |
419
+ | `TWS_HTTP_BACKEND` | `httpx` | Set to `curl` for TLS fingerprinting |
420
+ | `TWS_TELEMETRY` | `0` | Disable telemetry |
421
+ | `SPECTRE_OP_*` | built-in | Override GraphQL operation IDs (when they rotate) |
422
+
423
+ ---
424
+
425
+ ## Limitations
426
+
427
+ - **Authenticated accounts required** — X blocks unauthenticated access
428
+ - **Rate limited** — ~300 requests/hour/account (rotation handles this)
429
+ - **~3200 tweet cap** on user timelines (X's own limit)
430
+ - **ToS risk** — automated access violates X's terms
431
+ - **Cookies expire** — re-add every ~2 weeks when searches return empty
432
+
433
+ ---
434
+
435
+ ## License
436
+
437
+ MIT