mcpField
X
x-article-publisher

x-article-publisher

github.com/wshuyiマーケティング・グロース872318 回インストール40 週間前に更新
リポジトリ

x-article-publisher について

x-article-publisherは、Markdownの記事をX Articlesエディター向けに整えるスキルです。書式や画像、区切り線を配置し、自動公開せず下書きとして保存します。

主な機能

  1. 01Markdownのタイトル、画像、区切り線の解析
  2. 02MarkdownからリッチテキストHTMLへの変換
  3. 03クリップボード経由のテキストと画像の貼り付け
  4. 04表とMermaid図の画像化
  5. 05指定位置への画像と区切り線の挿入
  6. 06下書き保存と書式のプレビュー

活用シーン

  1. 01Markdown記事のX Articles向け準備
  2. 02記事内の表やMermaid図の変換
  3. 03カバー画像や本文画像の適切な位置への挿入

MCP 対応

Playwright MCPを使い、X Articlesエディターをブラウザー操作します。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/wshuyi/x-article-publisher-skill --skill x-article-publisher

インストール後、Claude Code で/x-article-publisherを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

scripts/
copy_to_clipboard.py
parse_markdown.py
table_to_image.py

詳細

X 記事パブリッシャー

Markdown のコンテンツを X(Twitter)の記事エディターに公開します。リッチテキスト変換により書式を保持します。

前提条件

  • ブラウザー自動化用の Playwright MCP
  • Premium Plus サブスクリプションで X にログインしていること
  • 依存関係を含む Python 3.9 以降:
    • macOS:pip install Pillow pyobjc-framework-Cocoa
    • Windows:pip install Pillow pywin32 clip-util
  • Mermaid 図の場合:npm install -g @mermaid-js/mermaid-cli

スクリプト

~/.claude/skills/x-article-publisher/scripts/ にあります:

parse_markdown.py

Markdown を解析し、構造化データを抽出します:

python parse_markdown.py <markdown_file> [--output json|html] [--html-only]

title、cover_image、content_images、dividers(配置用の block_index を含む)、html、total_blocks を含む JSON を返します。

copy_to_clipboard.py

画像または HTML をシステムクリップボードにコピーします(クロスプラットフォーム対応):

# Copy image (with optional compression)
python copy_to_clipboard.py image /path/to/image.jpg [--quality 80]

# Copy HTML for rich text paste
python copy_to_clipboard.py html --file /path/to/content.html
table_to_image.py

Markdown の表を PNG 画像に変換します:

python table_to_image.py <input.md> <output.png> [--scale 2]

X 記事が表のネイティブ表示に対応していない場合や、一貫したスタイルにする場合に使用します。

前処理(任意)

公開前に Markdown をスキャンし、変換が必要な要素を確認します:

表 → PNG
# Extract table to temp file, then convert
python ~/.claude/skills/x-article-publisher/scripts/table_to_image.py /tmp/table.md /tmp/table.png
# Replace table in markdown with: ![Table](/tmp/table.png)
Mermaid 図 → PNG
# Extract mermaid block to .mmd file, then convert
mmdc -i /tmp/diagram.mmd -o /tmp/diagram.png -b white -s 2
# Replace mermaid block with: ![Diagram](/tmp/diagram.png)
区切り線(---)

区切り線は parse_markdown.py によって自動検出され、dividers 配列に出力されます。X では HTML の <hr> タグが無視されるため、X 記事の Insert > Divider メニューから挿入する必要があります。

ワークフロー

方針:「テキスト、画像、区切り線の順」

画像と区切り線を含む記事では、まずテキストコンテンツをすべて貼り付け、その後、block index を使って正しい位置に画像と区切り線を挿入します。

  1. (任意) 前処理:表や Mermaid 図を画像に変換
  2. Python スクリプトで Markdown を解析 → タイトル、画像、block_index 付きの dividers、HTML を取得
  3. X 記事エディターに移動
  4. カバー画像(最初の画像)をアップロード
  5. タイトルを入力
  6. HTML をクリップボードにコピー(Python)→ Cmd+V で貼り付け
  7. block_index で指定された位置にコンテンツ画像を挿入
  8. block_index で指定された位置に区切り線を挿入(Insert > Divider メニューから)
  9. 下書きとして保存(自動公開は絶対にしない)

効率的な実行原則

目標:操作間の待ち時間を最小限に抑え、スムーズな自動化を実現します。

1. 不要な browser_snapshot を避ける

ほとんどのブラウザー操作(click、type、press_key など)では、戻り値にページの状態が含まれます。各操作の後に個別に browser_snapshot を呼び出さないでください。操作の戻り値に含まれるページの状態をそのまま使用します。

❌ 错误做法:
browser_click → browser_snapshot → 分析 → browser_click → browser_snapshot → ...

✅ 正确做法:
browser_click → 从返回结果中获取页面状态 → browser_click → ...
2. 不要な browser_wait_for を避ける

browser_wait_for は、次の場合にのみ使用します:

  • 画像のアップロード完了を待つ(textGone="正在上传媒体")
  • ページの初回読み込みを待つ(ごくまれな場合)

ボタンや入力欄が表示されるのを待つために browser_wait_for を使用しないでください。ページの読み込みが完了すれば、すぐに使用できます。

3. 独立した操作を並列実行する

2 つの操作に依存関係がない場合は、同じメッセージ内で複数のツールを並列に呼び出せます:

✅ 可以并行:
- 填写标题 (browser_type) + 复制HTML到剪贴板 (Bash)
- 解析Markdown生成JSON + 生成HTML文件

❌ 不能并行(有依赖):
- 必须先点击create才能上传封面图
- 必须先粘贴内容才能插入图片
4. ブラウザー操作を連続して実行する

各ブラウザー操作の戻り値に含まれるページの状態には、必要な要素の参照がすべて含まれています。これらの参照をそのまま次の操作に使用します:

# 理想流程(每步直接执行,不额外等待):
browser_navigate → 从返回状态找create按钮 → browser_click(create)
→ 从返回状态找上传按钮 → browser_click(上传) → browser_file_upload
→ 从返回状态找应用按钮 → browser_click(应用)
→ 从返回状态找标题框 → browser_type(标题)
→ 点击编辑器 → browser_press_key(Meta+v)
→ ...
5. 準備を先に済ませる

ブラウザー操作を始める前に、準備作業をすべて完了してください:

  1. Markdown を解析して JSON データを取得
  2. HTML ファイルを /tmp/ に生成
  3. title、cover_image、content_images などの情報を記録

こうすることで、ブラウザー操作の段階を中断せずに連続して実行できます。

ステップ 1:Markdown を解析する(Python)

parse_markdown.py を使用して、すべての構造化データを抽出します:

python ~/.claude/skills/x-article-publisher/scripts/parse_markdown.py /path/to/article.md

JSON 出力:

{
  "title": "Article Title",
  "cover_image": "/path/to/first-image.jpg",
  "cover_exists": true,
  "content_images": [
    {"path": "/path/to/img2.jpg", "original_path": "/md/dir/assets/img2.jpg", "exists": true, "block_index": 5, "after_text": "context..."},
    {"path": "/path/to/img3.jpg", "original_path": "/md/dir/assets/img3.jpg", "exists": true, "block_index": 12, "after_text": "another..."}
  ],
  "html": "<p>Content...</p><h2>Section</h2>...",
  "total_blocks": 45,
  "missing_images": 0
}

主なフィールド:

  • block_index:このインデックス(0 始まり)のブロック要素の後に画像を挿入します
  • total_blocks:HTML 内のブロック要素の合計数
  • after_text:参照/デバッグ用にのみ保持されます。配置には使用しません
  • exists:画像ファイルが見つかったかどうか(false の場合、アップロードは失敗します)
  • original_path:Markdown から解決されたパス(自動検索の前)
  • path:使用する実際のパス(自動検索された場合、original_path と異なることがあります)
  • missing_images:どこにも見つからなかった画像の数

クリップボード用に HTML を一時ファイルへ保存します:

python parse_markdown.py article.md --html-only > /tmp/article_html.html

ステップ 2:X 記事エディターを開く

ブラウザーエラーの処理

Error: Browser is already in use エラーが発生した場合:

# 方案1:先关闭浏览器再重新打开
browser_close
browser_navigate: https://x.com/compose/articles

# 方案2:如果 browser_close 无效(锁定),提示用户手动关闭 Chrome

# 方案3:使用已有标签页,直接导航
browser_tabs action=list  # 查看现有标签
browser_navigate: https://x.com/compose/articles  # 在当前标签导航

ベストプラクティス:余分な空白タブが作成されないよう、毎回開始前に browser_tabs action=list で状態を確認します。

エディターに移動する
browser_navigate: https://x.com/compose/articles

重要:ページの読み込み後に表示されるのは下書き一覧で、エディターではありません。次の手順が必要です:

  1. ページの読み込み完了を待つ:browser_snapshot を使ってページの状態を確認します
  2. すぐに "create" ボタンをクリックする:「タイトルを追加」などのエディター要素が表示されるまで待たないでください。これらは create をクリックした後にのみ表示されます
  3. エディターの読み込みを待つ:create をクリックした後、エディター要素が表示されるのを待ちます
# 1. 导航到页面
browser_navigate: https://x.com/compose/articles

# 2. 获取页面快照,找到 create 按钮
browser_snapshot

# 3. 点击 create 按钮(通常 ref 类似 "create" 或带有 create 标签)
browser_click: element="create button", ref=<create_button_ref>

# 4. 现在编辑器应该打开了,可以继续上传封面图等操作

注意:ページの読み込みを待つために browser_wait_for text="添加标题" を使用しないでください。このテキストは create をクリックした後にのみ表示されるため、タイムアウトします。

ログインが必要な場合は、手動でログインするようユーザーに依頼します。

ステップ 3:カバー画像をアップロードする

  1. 「写真または動画を追加」ボタンをクリックします
  2. カバー画像のパス(JSON 出力から取得)を指定して browser_file_upload を使用します
  3. 画像がアップロードされたことを確認します

ステップ 4:タイトルを入力する

  • プレースホルダーが「タイトルを追加」のテキストボックスを探します
  • browser_type を使用してタイトル(JSON 出力から取得)を入力します

ステップ 5:テキストコンテンツを貼り付ける(Python クリップボード)

Python を使って HTML をシステムクリップボードにコピーしてから、貼り付けます:

# Copy HTML to clipboard
python ~/.claude/skills/x-article-publisher/scripts/copy_to_clipboard.py html --file /tmp/article_html.html

次に、ブラウザーで:

browser_click on editor textbox
browser_press_key: Meta+v

これにより、すべてのリッチテキスト書式(H2、太字、リンク、リスト)が保持されます。

ステップ 6:コンテンツ画像を挿入する(テキスト検索による位置指定)

推奨方法:block_index より直感的で信頼性の高い、after_text によるテキスト検索で位置を指定します。

位置指定の仕組み

各画像の after_text フィールドには、その直前の段落の末尾のテキスト(最大80文字)が記録されています。エディターでそのテキストを含む段落を検索し、クリックしてから画像を挿入します。

操作手順

各コンテンツ画像(content_images 配列から)について、block_index の大きい順に:

# 1. Copy image to clipboard (with compression)
python ~/.claude/skills/x-article-publisher/scripts/copy_to_clipboard.py image /path/to/img.jpg --quality 85
# 2. 在 browser_snapshot 中搜索包含 after_text 的段落
#    找到该段落的 ref

# 3. Click the paragraph containing after_text
browser_click: element="paragraph with target text", ref=<paragraph_ref>

# 4. **关键步骤**: 按 End 键移动光标到行尾
#    这一步非常重要!避免点击到段落中的链接导致位置偏移
browser_press_key: End

# 5. Paste image
browser_press_key: Meta+v

# 6. Wait for upload (only use textGone, no time parameter)
browser_wait_for textGone="正在上传媒体"
End キーを押す必要がある理由

問題:段落にリンクが含まれている場合(例:[リンクテキスト](url))、段落をクリックすると次のようなことが起きる場合があります:

  • リンク編集ポップアップが表示される
  • カーソルが段落の末尾ではなくリンク内に配置される

解決策:段落をクリックしたら、すぐに End キーを押します:

  • カーソルが段落の末尾に移動するようにします
  • リンクによる干渉を防ぎます
  • 画像がその段落の後に正しく挿入されます
位置指定の方法

browser_snapshot が返す構造の中から、after_text のキーワードを検索します:

textbox [ref=editor]:
  generic [ref=p1]:
    - StaticText: "元旦假期我在家里翻手机相册..."  # 如果 after_text 包含这段文字,点击 p1
  heading [ref=h1]:
    - StaticText: "演示"
  generic [ref=p2]:
    - StaticText: "这东西到底有多省事儿?"
    - link [ref=link1]: "Claude Code"  # 注意:段落可能包含链接
  ...
逆順で挿入する例

画像が3枚あり、それぞれの block_index が 5、12、27 の場合:

  1. 最初に block_index=27 の画像を挿入します(after_text で検索 + End + 貼り付け)
  2. 次に block_index=12 の画像を挿入します
  3. 最後に block_index=5 の画像を挿入します

大きい順に挿入することで、位置がずれる問題を防げます。

ステップ 6.5:区切り線を挿入する(メニューを使用)

重要:Markdown の --- 区切り線は、HTML の <hr> タグを貼り付けても挿入できません(X Articles はこれを無視します)。X Articles の Insert メニューから挿入する必要があります。

操作手順

各区切り線(dividers 配列から)について、block_index の逆順に:

# 1. Click the block element at block_index position
browser_click on the element at position block_index in the editor

# 2. Open Insert menu (Add Media button)
browser_click on "Insert" or "添加媒体" button

# 3. Click Divider menu item
browser_click on "Divider" or "分割线" menuitem

# Divider is inserted at cursor position
画像との挿入順序

最初にすべての画像を挿入し、次にすべての区切り線を挿入することをおすすめします。どちらも block_index の大きい順に挿入します:

  1. すべての画像を挿入します(block_index が最大のものから開始)
  2. すべての区切り線を挿入します(block_index が最大のものから開始)

ステップ 7:下書きを保存する

  1. コンテンツが貼り付けられたことを確認します(文字数インジケーターを確認)
  2. 下書きは自動保存されます。必要に応じて保存ボタンをクリックします
  3. 「プレビュー」をクリックして書式を確認します
  4. 次のように報告します:「下書きを保存しました。内容を確認し、手動で公開してください。」

重要なルール

  1. 絶対に公開しない — 下書きの保存のみ行います
  2. 最初の画像 = カバー — 最初の画像をカバー画像としてアップロードします
  3. リッチテキストへの変換 — 貼り付ける前に、必ず Markdown を HTML に変換します
  4. クリップボード API を使用する — 適切な書式を保持するため、クリップボード経由で貼り付けます
  5. block_index による位置指定 — 画像や区切り線を正確な位置に配置するには block_index を使用します
  6. 逆順で挿入する — 画像と区切り線は、block_index の大きい順に挿入します
  7. H1 タイトルの扱い — H1 はタイトルとしてのみ使用し、本文には含めません
  8. メニューから区切り線を挿入する — Markdown の --- は Insert > Divider メニューから挿入する必要があります(HTML の <hr> は無視されます)

対応している書式

要素対応状況備考
H2 (##)ネイティブセクション見出し
太字 (**)ネイティブ強調
斜体 (*)ネイティブ強調
リンク ([](url))ネイティブハイパーリンク
順序付きリストネイティブ1. 2. 3.
順序なしリストネイティブ- 箇条書き
引用 (>)ネイティブ引用テキスト
コードブロック変換→ 引用
テーブル変換→ PNG 画像(table_to_image.py を使用)
Mermaid変換→ PNG 画像(mmdc を使用)
区切り線 (---)メニューから挿入→ Insert > Divider

操作例

ユーザー:「/path/to/article.md を X に公開して」

# Step 1: Parse Markdown
python ~/.claude/skills/x-article-publisher/scripts/parse_markdown.py /path/to/article.md > /tmp/article.json
python ~/.claude/skills/x-article-publisher/scripts/parse_markdown.py /path/to/article.md --html-only > /tmp/article_html.html
  1. https://x.com/compose/articles に移動します
  2. カバー画像をアップロードします(カバーのみ browser_file_upload を使用)
  3. タイトルを入力します(JSON の title から取得)
  4. HTML をコピーして貼り付けます:
    python ~/.claude/skills/x-article-publisher/scripts/copy_to_clipboard.py html --file /tmp/article_html.html
    
    次に:browser_press_key Meta+v
  5. 各コンテンツ画像について、block_index の逆順に:
    python copy_to_clipboard.py image /path/to/img.jpg --quality 85
    
    • block_index の位置にあるブロック要素をクリックします
    • browser_press_key Meta+v
    • アップロードが完了するまで待ちます
  6. プレビューで確認します
  7. 「下書きを保存しました。確認して、手動で公開してください。」

ベストプラクティス

なぜテキスト検索ではなく block_index を使うのですか?
  1. 正確な位置指定:テキストの内容に依存しないため、似たテキストが複数箇所にあっても正しく位置を指定できます
  2. 信頼性:インデックスは確定的なため、似たテキストによって混同されることがありません
  3. デバッグが容易:after_text は人による確認用に引き続き保持されます
なぜブラウザー内の JavaScript ではなく Python を使うのですか?
  1. ローカルでの処理のほうが信頼性が高い:Python はシステムクリップボードを直接操作するため、ブラウザーのサンドボックスによる制限を受けません
  2. 画像の圧縮:アップロード前に画像を圧縮(--quality 85)し、アップロード時間を短縮します
  3. コードの再利用:スクリプトは固定されており、変換ロジックを毎回書き直す必要がありません
  4. デバッグが容易:スクリプトを個別にテストできるため、問題の特定が容易です
等待策略

重要发现: Playwright MCP 的 browser_wait_for 实际行为是 先等待 time 秒,再检查条件,而非轮询!

// 实际执行的代码:
await new Promise(f => setTimeout(f, time * 1000));  // 先固定等待
await page.getByText("xxx").waitFor({ state: 'hidden' });  // 再检查

正确用法:

  • ✅ 只用 textGone,不设 time:让 Playwright 自己轮询等待
  • ✅ 只用 time:固定等待指定秒数
  • ❌ 同时用 textGone + time:会先等 time 秒再检查,浪费时间
# 推荐:只用 textGone,让它自动等待条件满足
browser_wait_for textGone="正在上传媒体"

# 或者:用 browser_snapshot 轮询检查状态
# 每次操作后检查返回的页面状态,无需额外等待
图片插入效率

每张图片的浏览器操作从5步减少到2步:

  • 旧: 点击 → 添加媒体 → 媒体 → 添加照片 → file_upload
  • 新: 点击段落 → Meta+v
封面图 vs 内容图
  • 封面图: 使用 browser_file_upload(因为有专门的上传按钮)
  • 内容图: 使用 Python 剪贴板 + 粘贴(更高效)

故障排除

MCP 连接问题

如果 Playwright MCP 工具不可用(报错 No such tool available 或 Not connected):

方案1:重新连接 MCP(推荐)

执行 /mcp 命令,选择 playwright,选择 Restart

方案2:清理残留进程后重连

# 杀掉所有残留的 playwright 进程
pkill -f "mcp-server-playwright"
pkill -f "@playwright/mcp"

# 然后执行 /mcp 重新连接

配置文件位置: ~/.claude/mcp_servers.json

浏览器错误处理

如果遇到 Error: Browser is already in use 错误:

# 方案1:先关闭浏览器再重新打开
browser_close
browser_navigate: https://x.com/compose/articles

# 方案2:杀掉 Chrome 进程
pkill -f "Chrome.*--remote-debugging"
# 然后重新 navigate
图片位置偏移

如果图片插入位置不正确(特别是点击含链接的段落时):

原因: 点击段落时可能误触链接,导致光标位置错误

解决方案: 点击后必须按 End 键移动光标到行尾

# 正确流程
1. browser_click 点击目标段落
2. browser_press_key: End        # 关键步骤!
3. browser_press_key: Meta+v     # 粘贴图片
4. browser_wait_for textGone="正在上传媒体"
图片路径找不到

如果 Markdown 中的相对路径图片找不到(如 ./assets/image.png 实际在其他位置):

自动搜索: parse_markdown.py 会自动在以下目录搜索同名文件:

  • ~/Downloads
  • ~/Desktop
  • ~/Pictures

stderr 输出示例:

[parse_markdown] Image not found at '/path/to/assets/img.png', using '/Users/xxx/Downloads/img.png' instead

JSON 字段说明:

  • original_path: Markdown 中指定的路径(解析后的绝对路径)
  • path: 实际使用的路径(如果自动搜索成功,会不同于 original_path)
  • exists: true 表示找到文件,false 表示未找到(上传会失败)

如果仍然找不到:

  1. 检查 JSON 输出中的 missing_images 字段
  2. 手动将图片复制到 Markdown 文件同目录的 assets/ 子目录
  3. 或修改 Markdown 中的图片路径为绝对路径

コメント