X Reply Posting (Browser Automation)
Post replies to tweets on x.com using Chrome MCP browser tools. Uses the inline reply box with keyboard submit — the most reliable method.
Prerequisites
- Chrome browser open with X/Twitter logged in
- Chrome MCP extension connected (
mcp__claude-in-chrome__*tools available)
Verify Chrome MCP is available:
mcp__claude-in-chrome__tabs_context_mcp
If it errors: tell the user "Chrome MCP isn't connected. Open Chrome with the Claude extension active."
Posting Flow (Per Reply)
Step 1: Navigate to the Tweet
mcp__claude-in-chrome__navigate to https://x.com/{handle}/status/{tweet_id}
Wait for page load.
Step 2: Check for Duplicate (Optional)
Before posting, verify you haven't already replied. Replace 'YOUR_HANDLE' with the user's X handle:
[...document.querySelectorAll('[data-testid="tweet"]')].some(t =>
(t.querySelector('[data-testid="User-Name"]')?.textContent || '').toLowerCase().includes('YOUR_HANDLE')
);
If true, skip this tweet.
Step 3: Click the Inline Textarea
Get the textarea position dynamically, then click it:
const textarea = document.querySelector('[data-testid="tweetTextarea_0"]');
const rect = textarea.getBoundingClientRect();
JSON.stringify({ x: Math.round(rect.x + rect.width / 2), y: Math.round(rect.y + rect.height / 2) });
Use mcp__claude-in-chrome__computer to left_click at the returned (x, y) coordinates.
This physical click is required. JS focus() alone does not reliably activate the contenteditable div.
Step 4: Insert Text and Submit
const textarea = document.querySelector('[data-testid="tweetTextarea_0"]');
textarea?.focus();
document.execCommand('insertText', false, 'REPLY_TEXT_HERE');
document.querySelector('[data-testid="tweetTextarea_0"]')?.textContent || '';
Verify the returned text is non-empty. If empty, repeat Step 3 (click textarea) and retry once.
Then submit with keyboard shortcut:
mcp__claude-in-chrome__computer → key → cmd+Return
Step 5: Verify
const found = [...document.querySelectorAll('[data-testid="tweet"]')].filter(t => {
const text = t.querySelector('[data-testid="tweetText"]')?.textContent || '';
return text.includes('FIRST_FEW_WORDS_OF_REPLY');
});
found.length > 0;
If true, reply is confirmed. If false, retry from Step 3 once.
Critical Rules
Use the inline reply box. The inline reply box at the bottom of a tweet page works. No need to open the compose modal.
Never use computer type for reply text. Use execCommand('insertText') via javascript_tool. This properly triggers React's state updates on the contenteditable div.
Never use form_input tool. The tweet textarea is a contenteditable div, not a form input. form_input will not work.
Never rely on JS focus() alone. Always visually click the textarea with computer tool first (at coordinates from getBoundingClientRect), then use execCommand('insertText').
Always find textarea coordinates dynamically. The inline reply box position varies with tweet length and media. Use getBoundingClientRect() every time — never hardcode coordinates.
Submit with cmd+Return. This is the most reliable submit method. Works regardless of button position or React event handling.
Handling 404s
If a tweet URL returns "this page doesn't exist":
navigate to https://x.com/search?q=from:{handle} {first_few_words}&f=live
Find the tweet in search results, extract the URL from time?.closest('a')?.href, and retry.
Why Not Use the API?
X's CreateTweet GraphQL API requires an x-client-transaction-id header generated by an obfuscated client-side script (ondemand.s chunk) that uses DOM animation timing data. This is not practical to reverse-engineer. The browser automation method works reliably.
Troubleshooting
| Issue | Fix |
|---|---|
| Textarea not found | Page hasn't loaded — wait longer before extraction |
| Text insertion fails | Click didn't activate textarea — repeat Step 3 click |
| Submit doesn't work | Verify cmd+Return (macOS) or ctrl+Return (Windows/Linux) |
| Reply not appearing | Check Step 5 verification — may need page refresh |
| "Detached while handling" | Chrome MCP timeout — check if reply posted before retrying |
| 404 on tweet URL | Use rest_id not legacy.id_str if extracting from API (see x-list-extraction skill) |
Quick Reference: Full Flow
1. navigate → tweet URL
2. javascript_tool → get textarea coordinates (getBoundingClientRect)
3. computer → left_click at (x, y)
4. javascript_tool → execCommand('insertText', false, replyText)
5. computer → key → cmd+Return
6. javascript_tool → verify reply appears in DOM
Tested Feb 2026. X can change DOM structure — if posting fails, inspect the reply box in DevTools to find updated selectors.