x-twitter-mcp
MCP server for interacting with X (Twitter) via API and web scraping, enabling AI agents to analyze profiles, track threads, and explore social graphs.
Trit: -1 (MINUS) - Validation/analysis of social signals Use Cases: Profile analysis, thread extraction, sentiment analysis, emergence detection
Overview
This skill wraps X/Twitter interaction as an MCP server, allowing AI agents to:
- Analyze user profiles and posting patterns
- Extract and track conversation threads
- Map social connections and influence
- Detect emergent topics and memes
- Monitor specific accounts or keywords
Key Insight: Social media is a distributed learning system. By observing interaction patterns, we can detect:
- Emergent concepts (like "tpot" - tech Twitter subculture)
- Emotional development arcs (user growth over time)
- Information cascades and viral dynamics
- Collective sense-making processes
Case Study: @imitationlearn
Profile Overview
- Joined: August 2024
- Growth: ~1200 followers by Jan 2026 (rapid, organic growth)
- Ratio: Following:Followers ≈ 1:1 (authentic engagement)
- Bio: "the minima are so pretty" (mathematical/optimization interest)
- Website: imitationlearn.bearblog.dev (minimalist, markdown-based)
Posting Themes
-
Technical AI/ML
- "4chan figured out step-by-step reasoning as emergent property of gpt-3"
- Deep interest in emergence and reasoning
-
Personal Growth
- "emotionally repressed ppl who figured out how to feel more — what did u do?"
- Active emotional development work
-
Social Awareness
- "holy fuck it's hard to socialize i need to work on that"
- "any tips for someone just getting into socializing? (not sarcasm)"
- Public documentation of learning social skills
-
Cultural Commentary
- "i grew up on studio ghibli...now its everywhere...ive lost something sacred"
- "this isn't enjoyable to me. it's the smoothing out of reality"
- Critique of cultural commodification
-
Tech Culture
- "wait is tpot just a bunch of nerds"
- Discovering and naming subcultures
Temporal Arc
Aug 2024: Joins, starts exploring tech Twitter
Dec 2024: Technical observations (GPT-3 reasoning, socialization mechanics)
Mar 2025: Cultural critique (Ghibli commodification, Manifest events)
May 2025: Socialization awareness emerges ("hard to socialize")
Jul 2025: Active emotional growth work ("emotionally repressed ppl")
Sep 2025: Continued introspection and cultural analysis
Analysis: Imitation Learning in Action
Username Meaning: "imitation learning" - literally describes the process
- Learning social behaviors through observation
- Publicly documenting experiments and failures
- Meta-awareness of the learning process itself
Pattern Recognition:
- Technical → Personal progression (common in tech Twitter)
- Observation → Practice cycle (classic RL)
- Public accountability (social pressure as training signal)
Emergence Detection:
- User becoming aware of "tpot" (tech post-rationalist Twitter)
- Discovering implicit cultural norms through collision
- Meta-commentary on discovery process itself
MCP Tools
x_get_profile
Get profile information for a user.
{
"name": "x_get_profile",
"description": "Get user profile information",
"inputSchema": {
"type": "object",
"properties": {
"username": {"type": "string", "description": "Twitter username (without @)"}
},
"required": ["username"]
}
}
x_get_posts
Get recent posts from a user.
{
"name": "x_get_posts",
"description": "Get recent posts from a user",
"inputSchema": {
"type": "object",
"properties": {
"username": {"type": "string"},
"limit": {"type": "integer", "default": 20},
"include_replies": {"type": "boolean", "default": false},
"since_date": {"type": "string", "format": "date"}
},
"required": ["username"]
}
}
x_get_thread
Extract a full conversation thread.
{
"name": "x_get_thread",
"description": "Get full conversation thread",
"inputSchema": {
"type": "object",
"properties": {
"tweet_id": {"type": "string"},
"include_quotes": {"type": "boolean", "default": true}
},
"required": ["tweet_id"]
}
}
x_search
Search for tweets matching a query.
{
"name": "x_search",
"description": "Search tweets by keyword or phrase",
"inputSchema": {
"type": "object",
"properties": {
"query": {"type": "string"},
"limit": {"type": "integer", "default": 20},
"filter": {"type": "string", "enum": ["top", "latest", "people", "media"]}
},
"required": ["query"]
}
}
x_analyze_profile
Analyze posting patterns and themes for a user.
{
"name": "x_analyze_profile",
"description": "Analyze user posting patterns and themes",
"inputSchema": {
"type": "object",
"properties": {
"username": {"type": "string"},
"lookback_days": {"type": "integer", "default": 30}
},
"required": ["username"]
}
}
Example Output:
{
"username": "imitationlearn",
"themes": [
{"topic": "AI/ML emergence", "frequency": 0.25},
{"topic": "personal_growth", "frequency": 0.20},
{"topic": "cultural_critique", "frequency": 0.15},
{"topic": "social_learning", "frequency": 0.15},
{"topic": "philosophy", "frequency": 0.10}
],
"posting_frequency": {
"avg_per_day": 3.2,
"most_active_hour": 14,
"reply_ratio": 0.45
},
"emotional_arc": {
"trend": "introspective_to_social",
"sentiment_trajectory": "increasing_openness",
"milestones": [
{"date": "2024-12-21", "event": "questioning social mechanics"},
{"date": "2025-05-12", "event": "socialization awareness"},
{"date": "2025-07-16", "event": "active emotional growth"}
]
}
}
x_get_social_graph
Map connections and influence for a user.
{
"name": "x_get_social_graph",
"description": "Map social connections and influence",
"inputSchema": {
"type": "object",
"properties": {
"username": {"type": "string"},
"depth": {"type": "integer", "default": 1, "maximum": 3},
"include_followers": {"type": "boolean", "default": false}
},
"required": ["username"]
}
}
x_detect_emergence
Detect emergent topics across a set of users.
{
"name": "x_detect_emergence",
"description": "Detect emergent topics and memes",
"inputSchema": {
"type": "object",
"properties": {
"usernames": {"type": "array", "items": {"type": "string"}},
"time_window_hours": {"type": "integer", "default": 24}
},
"required": ["usernames"]
}
}
Architecture
┌─────────────────────────────────────────────────────────────┐
│ Claude Desktop / AI Agent │
└─────────────────────────┬───────────────────────────────────┘
│ JSON-RPC 2.0 (stdio)
▼
┌─────────────────────────────────────────────────────────────┐
│ x-twitter-mcp-server.bb (Babashka) │
│ ├─ Profile fetcher │
│ ├─ Thread extractor │
│ ├─ Sentiment analyzer │
│ └─ Emergence detector │
└─────────────────────────┬───────────────────────────────────┘
│
├─► Twitter API v2 (with bearer token)
├─► Web scraping (nitter.net fallback)
└─► DuckDB (local cache)
┌─────────────────────────────────────────────────────────────┐
│ DuckDB: twitter_cache.db │
│ ├─ profiles (username, bio, followers, etc.) │
│ ├─ posts (tweet_id, content, sentiment, etc.) │
│ ├─ threads (conversation graphs) │
│ └─ themes (emergent topics over time) │
└─────────────────────────────────────────────────────────────┘
Implementation
Babashka MCP Server
#!/usr/bin/env bb
(ns x-twitter-mcp
(:require [cheshire.core :as json]
[babashka.process :as p]
[babashka.http-client :as http]
[clojure.string :as str]))
(def server-info
{:name "x-twitter-mcp"
:version "1.0.0"
:protocolVersion "2024-11-05"})
(def tools
[{:name "x_get_profile"
:description "Get user profile information"
:inputSchema {:type "object"
:properties {:username {:type "string"}}
:required ["username"]}}
{:name "x_get_posts"
:description "Get recent posts from a user"
:inputSchema {:type "object"
:properties {:username {:type "string"}
:limit {:type "integer" :default 20}
:include_replies {:type "boolean" :default false}}
:required ["username"]}}
{:name "x_analyze_profile"
:description "Analyze posting patterns and themes"
:inputSchema {:type "object"
:properties {:username {:type "string"}
:lookback_days {:type "integer" :default 30}}
:required ["username"]}}
{:name "x_detect_emergence"
:description "Detect emergent topics across users"
:inputSchema {:type "object"
:properties {:usernames {:type "array" :items {:type "string"}}
:time_window_hours {:type "integer" :default 24}}
:required ["usernames"]}}])
;; Twitter API v2 client
(defn twitter-api-get [endpoint params]
(let [bearer-token (System/getenv "TWITTER_BEARER_TOKEN")
url (str "https://api.twitter.com/2/" endpoint)
response (http/get url {:headers {"Authorization" (str "Bearer " bearer-token)}
:query-params params})]
(json/parse-string (:body response) true)))
;; Fallback: Web scraping via nitter
(defn nitter-scrape-profile [username]
(let [url (str "https://nitter.net/" username)
html (slurp url)]
(parse-nitter-html html)))
(defn get-profile [username]
(try
(twitter-api-get (str "users/by/username/" username)
{:user.fields "description,created_at,public_metrics"})
(catch Exception e
(nitter-scrape-profile username))))
(defn get-posts [username limit include-replies]
(let [user (get-profile username)
user-id (:id user)]
(twitter-api-get (str "users/" user-id "/tweets")
{:max_results limit
:exclude (when-not include-replies "replies")
:tweet.fields "created_at,public_metrics"})))
(defn analyze-profile [username lookback-days]
(let [posts (get-posts username 200 true)
themes (extract-themes posts)
patterns (analyze-patterns posts)
emotional-arc (detect-emotional-arc posts)]
{:username username
:themes themes
:posting_frequency patterns
:emotional_arc emotional-arc}))
(defn extract-themes [posts]
;; Simple keyword frequency + clustering
(let [all-text (str/join " " (map :text posts))
keywords (extract-keywords all-text)
clusters (cluster-keywords keywords)]
(map (fn [[topic freq]]
{:topic topic :frequency (float freq)})
(take 10 clusters))))
(defn detect-emotional-arc [posts]
;; Sentiment analysis over time
(let [sorted (sort-by :created_at posts)
sentiments (map analyze-sentiment sorted)
trend (compute-trend sentiments)
milestones (find-inflection-points sorted sentiments)]
{:trend trend
:sentiment_trajectory (classify-trajectory sentiments)
:milestones milestones}))
(defn handle-tool-call [{:keys [name arguments]}]
(case name
"x_get_profile"
{:content [{:type "text"
:text (json/generate-string
(get-profile (:username arguments))
{:pretty true})}]}
"x_get_posts"
{:content [{:type "text"
:text (json/generate-string
(get-posts (:username arguments)
(or (:limit arguments) 20)
(or (:include_replies arguments) false))
{:pretty true})}]}
"x_analyze_profile"
{:content [{:type "text"
:text (json/generate-string
(analyze-profile (:username arguments)
(or (:lookback_days arguments) 30))
{:pretty true})}]}
"x_detect_emergence"
{:content [{:type "text"
:text (json/generate-string
(detect-emergence (:usernames arguments)
(or (:time_window_hours arguments) 24))
{:pretty true})}]}
{:content [{:type "text" :text (str "Unknown tool: " name)}]
:isError true}))
DuckDB Schema
-- Profiles table
CREATE TABLE profiles (
username VARCHAR PRIMARY KEY,
display_name VARCHAR,
bio TEXT,
website VARCHAR,
joined_date DATE,
followers INTEGER,
following INTEGER,
verified BOOLEAN,
fetched_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Posts table
CREATE TABLE posts (
tweet_id VARCHAR PRIMARY KEY,
username VARCHAR REFERENCES profiles(username),
content TEXT,
posted_at TIMESTAMP,
likes INTEGER,
retweets INTEGER,
replies INTEGER,
is_reply BOOLEAN,
reply_to_id VARCHAR,
sentiment_score FLOAT,
themes JSON
);
-- Themes table (aggregated over time)
CREATE TABLE themes (
id INTEGER PRIMARY KEY,
username VARCHAR REFERENCES profiles(username),
theme VARCHAR,
frequency FLOAT,
detected_at TIMESTAMP,
time_window_days INTEGER
);
-- Social graph edges
CREATE TABLE follows (
follower_username VARCHAR,
followee_username VARCHAR,
detected_at TIMESTAMP,
PRIMARY KEY (follower_username, followee_username)
);
-- Emergent topics
CREATE TABLE emergent_topics (
topic_id INTEGER PRIMARY KEY,
topic_name VARCHAR,
first_seen TIMESTAMP,
peak_velocity FLOAT,
participating_users JSON,
related_keywords JSON
);
Queries
-- User posting frequency over time
SELECT
DATE_TRUNC('week', posted_at) as week,
COUNT(*) as posts_per_week,
AVG(sentiment_score) as avg_sentiment
FROM posts
WHERE username = 'imitationlearn'
GROUP BY week
ORDER BY week DESC;
-- Detect emergence (sudden topic spikes)
WITH recent_themes AS (
SELECT
theme,
COUNT(DISTINCT username) as user_count,
AVG(frequency) as avg_freq
FROM themes
WHERE detected_at > CURRENT_TIMESTAMP - INTERVAL '24 hours'
GROUP BY theme
)
SELECT * FROM recent_themes
WHERE user_count > 5
ORDER BY user_count DESC, avg_freq DESC;
-- Emotional arc (sentiment trajectory)
SELECT
DATE_TRUNC('month', posted_at) as month,
AVG(sentiment_score) as sentiment,
STDDEV(sentiment_score) as variance,
COUNT(*) as post_count
FROM posts
WHERE username = 'imitationlearn'
GROUP BY month
ORDER BY month;
-- Social graph clustering
WITH RECURSIVE graph AS (
SELECT follower_username as user, 0 as depth
FROM follows WHERE follower_username = 'imitationlearn'
UNION ALL
SELECT f.followee_username, g.depth + 1
FROM follows f
JOIN graph g ON f.follower_username = g.user
WHERE g.depth < 2
)
SELECT user, COUNT(*) as connections
FROM graph
GROUP BY user
ORDER BY connections DESC;
Analysis Patterns
Pattern 1: Imitation Learning Detection
Signals:
- Meta-commentary on social interaction
- Explicit questions about social norms
- Public documentation of experiments
- Temporal progression from observation → practice
@imitationlearn Example:
Dec 2024: "How does a porn bot...find my obscure reply?" (observation)
May 2025: "holy fuck it's hard to socialize" (awareness)
May 2025: "any tips for someone just getting into socializing?" (seeking guidance)
Jul 2025: "emotionally repressed ppl...what did u do?" (active learning)
Pattern 2: Emergence Detection
Method:
- Monitor keyword frequency across cohort
- Detect sudden spikes (>3σ from baseline)
- Identify "patient zero" users
- Map diffusion through social graph
Example: "tpot" emergence
- Originated as inside joke (~2020)
- Crystallized as identifier (~2022)
- Now broadly used with shared meaning (~2024)
- User discovers: "wait is tpot just a bunch of nerds" (2025)
Pattern 3: Cultural Commodification Critique
Signal: Expressions of loss when niche culture goes mainstream
@imitationlearn Example:
"i grew up on studio ghibli. i come back to it for comfort...
now its everywhere, everything. ive lost something sacred.
this is different...it's the smoothing out of reality."
Analysis: Awareness of Goodhart's Law in cultural space
- When niche culture becomes target (mainstream), it loses what made it valuable
- "Smoothing out of reality" = loss of authentic roughness
GF(3) Integration
Triad: Social Media Analysis
x-twitter-mcp (-1, MINUS) # Validation via social proof/consensus
gay-mcp (0, ERGODIC) # Deterministic color coding of users/topics
bluesky-jetstream (+1, PLUS) # Generative alternative protocol
Sum: -1 + 0 + 1 ≡ 0 (mod 3) ✓
Interpretation:
- Twitter (MINUS): Validates ideas through likes, retweets, engagement
- Gay.jl (ERGODIC): Assigns deterministic colors to users based on behavioral patterns
- Bluesky (PLUS): Generates new decentralized social primitives (AT Protocol)
Privacy & Ethics
Principles
- Public Data Only: Only access publicly visible posts
- Rate Limiting: Respect API limits (300 requests/15min for v2)
- Caching: Use DuckDB to minimize API calls
- No Automation: Never automate likes, follows, or replies without disclosure
- Anonymization: When sharing analyses, anonymize unless public figures
- Opt-Out: Respect user privacy preferences
Authorized Use Cases
✅ Allowed:
- Academic research on emergence and collective behavior
- Personal profile analysis for self-reflection
- Trend detection and forecasting
- Sentiment analysis of public discourse
- Social graph research
❌ Prohibited:
- Automated engagement without disclosure
- Scraping protected accounts
- Training models on user data without consent
- Manipulative behavior (astroturfing, sockpuppets)
- Harassment or doxxing
Setup
1. Get Twitter API Access
# Apply for Twitter API v2 access
open https://developer.twitter.com/en/portal/dashboard
# Set bearer token
export TWITTER_BEARER_TOKEN="your_token_here"
2. Alternative: Web Scraping (No API Key)
# Use nitter.net as fallback
export X_SCRAPING_METHOD="nitter"
3. Claude Desktop Config
{
"mcpServers": {
"x-twitter": {
"command": "bb",
"args": ["/Users/bob/ies/plurigrid/asi/skills/x-twitter-mcp/x-twitter-mcp-server.bb"],
"env": {
"TWITTER_BEARER_TOKEN": "${TWITTER_BEARER_TOKEN}"
}
}
}
}
Related Skills
- bluesky-jetstream - Alternative social protocol (AT Protocol)
- gay-mcp - Deterministic coloring for social graph visualization
- bisimulation-game - Compare user behaviors across platforms
- cognitive-surrogate - Learn from social interaction patterns
- agent-o-rama - Model social learning as agent training
- low-discrepancy-sequences - Sample social graph uniformly
Future Enhancements
1. Real-Time Streaming
(defn subscribe-to-user [username callback]
(let [stream (twitter-api-stream "tweets" {:user username})]
(doseq [tweet stream]
(callback (analyze-tweet tweet)))))
2. Cross-Platform Analysis
(defn compare-platforms [username]
{:twitter (analyze-twitter-profile username)
:bluesky (analyze-bluesky-profile username)
:mastodon (analyze-mastodon-profile username)
:behavioral_diff (compute-behavioral-delta)})
3. Predictive Modeling
-- Train on historical arcs to predict future sentiment
CREATE TABLE sentiment_predictions AS
WITH historical AS (
SELECT username, posted_at, sentiment_score,
LAG(sentiment_score, 7) OVER (PARTITION BY username ORDER BY posted_at) as prev_week
FROM posts
)
SELECT username,
regr_slope(sentiment_score, prev_week) as sentiment_velocity,
sentiment_score + (regr_slope(sentiment_score, prev_week) * 7) as predicted_next_week
FROM historical;
Status: Design complete, requires API credentials for deployment Trit: -1 (MINUS - Social validation and consensus) License: MIT (respect Twitter ToS and rate limits)