| name | tweet-rl-tracker |
| description | Create and manage a Notion-based tweet performance tracking system for "poor man's reinforcement learning" |
| license | MIT |
| compatibility | opencode |
| metadata | [object Object] |
What I Do
Set up a Notion database to track tweet performance and enable a feedback loop for improving tweet quality over time. This is "poor man's reinforcement learning" - manually logging outcomes to discover what works.
NEW: Screenshot Capture - Automatically capture screenshots from tweet links (including video frames at 10 seconds) using Chrome DevTools MCP.
The RL Loop
1. WRITE -> Draft tweet with a hypothesis (hook type, topic, etc.)
2. POST -> Publish to Twitter/X
3. LOG -> Record in Notion after 24-48hrs with metrics
4. SCORE -> Mark "Worked?" based on engagement rate
5. REVIEW -> Weekly: compare winners vs losers, extract patterns
6. REPEAT -> Apply learnings to step 1
Database Schema
Core Fields (Outcomes)
| Property | Type | Purpose |
|---|---|---|
| Tweet | title | The tweet text |
| Likes | number | Primary engagement signal |
| Impressions | number | Reach/views |
| Score | formula | Likes / Impressions * 100 (engagement %) |
| Worked? | checkbox | Binary gut-check - was this a win? |
Input Features (What You Controlled)
| Property | Type | Options |
|---|---|---|
| Hook | select | Question, Bold Claim, Story, List, How-To, Contrarian, Data/Stats |
| Topic | select | (customize to your niche) |
| Posted | date | When you posted |
Optional Extensions
| Property | Type | Purpose |
|---|---|---|
| Replies | number | Conversation signal |
| Retweets | number | Amplification signal |
| Link | url | Link to original tweet |
| Notes | rich_text | Why did it work/fail? |
Setup Instructions
Step 1: Create the Page and Database
Use the Notion MCP tools to create:
notion_notion-create-pages with:
- title: "Tweet Lab" (or your preferred name)
- content: Brief intro about the system
Then inside that page, create a database with:
notion_notion-create-database with the schema above
Step 2: Example Notion Tool Calls
Create the container page:
{
"pages": [
{
"properties": { "title": "Tweet Lab" },
"content": "## Tweet Performance Tracker\n\nA simple system to track what works and improve over time.\n\n### The Loop\n1. Post tweet\n2. Log metrics after 24-48hrs\n3. Mark if it \"Worked\"\n4. Weekly: review patterns\n\n<database>Tweets</database>"
}
]
}
Create the database (after getting page_id):
{
"parent": { "page_id": "<page-id-from-above>" },
"title": [{ "type": "text", "text": { "content": "Tweets" } }],
"properties": {
"Tweet": { "type": "title", "title": {} },
"Likes": { "type": "number", "number": { "format": "number" } },
"Impressions": { "type": "number", "number": { "format": "number" } },
"Score": {
"type": "formula",
"formula": {
"expression": "if(prop(\"Impressions\") > 0, prop(\"Likes\") / prop(\"Impressions\") * 100, 0)"
}
},
"Worked?": { "type": "checkbox", "checkbox": {} },
"Hook": {
"type": "select",
"select": {
"options": [
{ "name": "Question", "color": "blue" },
{ "name": "Bold Claim", "color": "red" },
{ "name": "Story", "color": "green" },
{ "name": "List", "color": "yellow" },
{ "name": "How-To", "color": "purple" },
{ "name": "Contrarian", "color": "orange" },
{ "name": "Data/Stats", "color": "pink" }
]
}
},
"Topic": {
"type": "select",
"select": { "options": [] }
},
"Posted": { "type": "date", "date": {} }
}
}
Using the System
Daily: Log Tweets
After 24-48 hours, add a row:
- Copy tweet text
- Pull Likes and Impressions from Twitter analytics
- Select the Hook type you used
- Select or create a Topic tag
Weekly: Review Session
- Sort by Score (descending) - what performed best?
- Filter by "Worked?" = true - what patterns emerge?
- Group by Hook - which hook types win?
- Group by Topic - which topics resonate?
Monthly: Form Hypotheses
Based on patterns, create hypotheses to test:
- "Questions about [topic] get 2x engagement"
- "Bold claims underperform for my audience"
- "Threads outperform single tweets"
Then deliberately test these in the next month.
Key Insights
What Makes This Work
- Track inputs, not just outputs - Without knowing what you tried (hook, topic), you can't learn what caused success
- Binary "Worked?" is powerful - Forces a clear decision, easier to analyze than continuous scores
- Review regularly - Data without reflection is useless
- Test hypotheses deliberately - Don't just observe, experiment
Common Pitfalls
- Logging inconsistently (do it same time each day/week)
- Not tracking input features (hook, topic)
- Never reviewing the data
- Optimizing for wrong metric (likes vs. replies vs. followers)
Customization
Different Goals = Different Metrics
| Goal | Primary Metric | Formula |
|---|---|---|
| Virality | Retweets / Impressions | Amplification rate |
| Engagement | (Likes + Replies) / Impressions | Interaction rate |
| Growth | Profile clicks / Impressions | Curiosity rate |
| Community | Replies / Impressions | Conversation rate |
Add Your Topics
Update the Topic select options based on your niche. Examples:
- Tech founder: Product, Fundraising, Hiring, Lessons, Industry
- Creator: Process, Tools, Mindset, Results, Behind-the-scenes
Troubleshooting
"I forget to log tweets"
- Set a daily/weekly calendar reminder
- Add a "Logged?" checkbox to your posting workflow
- Batch log once per week instead of daily
"I don't know what hook I used"
- Decide BEFORE posting, not after
- Add hook type to your tweet drafting process
- When in doubt, pick the closest match
"My Score is always low"
- Engagement rates are typically 1-5% - this is normal
- Compare relative performance, not absolute numbers
- Consider using a different base metric for "Worked?"
Screenshot Capture with Chrome MCP
Prerequisites
- Chrome DevTools MCP configured in your MCP client
- Logged into Twitter/X in the Chrome profile
Database Field
The Tweets database includes a Screenshot field (type: files) to store captured images.
Data Source ID: a6913492-bfdc-4f6a-b539-ea98b57a2738
Workflow: Capture Tweet Screenshot
Given a tweet URL like https://x.com/username/status/1234567890:
Step 1: Navigate to Tweet
// Open new page with the tweet
new_page({ url: 'https://x.com/username/status/1234567890' });
// Wait for tweet to load
wait_for({ text: 'Reply' }); // or wait for specific tweet content
Step 2: Take Screenshot of Tweet
// Take full page screenshot
take_screenshot({ fullPage: false });
// Or screenshot specific element (the tweet article)
take_snapshot(); // Get element uids first
take_screenshot({ uid: 'tweet-article-uid' });
Step 3: For Video Tweets - Capture Frame at 10 Seconds
// 1. Find and click the video to start playing
take_snapshot();
click({ uid: 'video-player-uid' });
// 2. Wait 10 seconds for video to play
// (Use evaluate_script to seek if possible)
evaluate_script({
function: `() => {
const video = document.querySelector('video');
if (video) {
video.currentTime = 10;
video.pause();
return { success: true, duration: video.duration };
}
return { success: false };
}`,
});
// 3. Take screenshot of the video frame
take_screenshot({ fullPage: false });
Step 4: Extract Tweet Metadata
evaluate_script({
function: `() => {
// Extract tweet text
const tweetText = document.querySelector('[data-testid="tweetText"]')?.textContent || '';
// Extract author
const author = document.querySelector('[data-testid="User-Name"]')?.textContent || '';
// Extract metrics (likes, retweets, etc.)
const metrics = {};
document.querySelectorAll('[data-testid="like"], [data-testid="retweet"]').forEach(el => {
const label = el.getAttribute('aria-label') || '';
if (label.includes('like')) metrics.likes = parseInt(label) || 0;
if (label.includes('repost')) metrics.retweets = parseInt(label) || 0;
});
// Check if video exists
const hasVideo = !!document.querySelector('video');
const hasImage = !!document.querySelector('[data-testid="tweetPhoto"]');
return {
text: tweetText,
author,
metrics,
mediaType: hasVideo ? 'Video' : hasImage ? 'Image' : 'None'
};
}`,
});
Complete Example: Add Tweet with Screenshot
User: Add this tweet to the tracker: https://x.com/unvalley_/status/2005899617775542298
Agent workflow:
1. new_page({ url: 'https://x.com/unvalley_/status/2005899617775542298' })
2. wait_for({ text: 'Reply' })
3. take_snapshot() - to see page structure
4. evaluate_script() - extract tweet text, author, media type
5. If video: seek to 10s and pause
6. take_screenshot() - capture the visual
7. Save screenshot to local file (screenshot is returned as base64)
8. Create Notion page with extracted data + screenshot file
Saving Screenshot to Notion
Important: Notion's files property requires external URLs. The workflow is:
- Take screenshot with Chrome MCP (returns base64)
- Save to a publicly accessible location (e.g., upload to S3, Cloudinary, or use a temp file host)
- Add the URL to the Notion page's Screenshot field
// After taking screenshot, you'll get base64 data
// Save it locally first:
const fs = require('fs');
const screenshotPath = `/tmp/tweet-${Date.now()}.png`;
fs.writeFileSync(screenshotPath, Buffer.from(base64Data, 'base64'));
// Then upload to your preferred hosting and get URL
// Finally, create Notion page with the screenshot URL
Quick Reference: Chrome MCP Tools for Tweet Capture
| Tool | Purpose |
|---|---|
new_page |
Navigate to tweet URL |
wait_for |
Wait for tweet to load |
take_snapshot |
Get accessibility tree (find element uids) |
take_screenshot |
Capture visual screenshot |
evaluate_script |
Extract tweet data, control video playback |
click |
Click play button on video |
Troubleshooting Screenshots
"Tweet not loading"
- Check if logged into Twitter in Chrome profile
- Twitter may require login for some content
- Try
wait_forwith longer timeout
"Video won't seek"
- Some videos are streaming and don't support seeking
- Try clicking play first, then waiting 10 seconds naturally
- Use
setTimeoutin evaluate_script if needed
"Screenshot is blank"
- Wait longer for content to render
- Check if page has overlay/modal blocking content
- Try
fullPage: trueto capture everything
"Can't upload to Notion"
- Notion files require external URLs
- Use a file hosting service (S3, Cloudinary, imgur)
- Or embed screenshot as image in page content instead