paragraph-cli について
paragraph-cli は、Web3 出版・ニュースレタープラットフォーム Paragraph を管理するコマンドラインスキルです。投稿、下書き、購読者、分析などを扱い、JSON 出力や公開・メール送信時の安全制御も備えています。
主な機能
- 01投稿とコンテンツの下書き管理
- 02出版物の設定
- 03購読者の管理
- 04投稿とブログの検索
- 05出版物の分析データ照会
- 06コインとユーザーの管理
活用シーン
- 01出版物の投稿を作成・確認
- 02SNSやニュースレターの下書きを準備
- 03購読者リストの取り込み・管理
- 04出版物のエンゲージメント指標を確認
MCP 対応
README は、MCP 対応クライアントに Paragraph MCP サーバーの利用を推奨しています。ホスト型のリモートサーバーとローカルサーバーがあり、投稿、コンテンツ、出版物、購読者、コイン、検索、分析などのツールを提供します。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ paragraph-xyz/ skill --skill paragraph-cliインストール後、Claude Code で/paragraph-cliを入力して読み込まれたか確認します。
詳細
Paragraph CLI
Paragraph の CLI — web3 の出版およびニュースレタープラットフォームです。投稿、下書きコンテンツ、出版物、購読者、コインを管理できます。
インストール不要で HTTP または SDK から直接アクセスする場合は、代わりに paragraph-api スキルをご覧ください。
MCP サーバー(MCP 対応クライアントに推奨)
お使いのクライアントが MCP に対応している場合は、より統合された環境を利用できる Paragraph MCP サーバーを、CLI の代わりにお使いください。
リモートサーバー(推奨)
ホスト型サーバー mcp.paragraph.com をお使いください。インストールや API キーの管理は不要です。ブラウザーで Paragraph アカウントを使って認証します。
claude mcp add paragraph --transport http https://mcp.paragraph.com/mcp
その他のクライアントでは、MCP 設定にサーバー URL として https://mcp.paragraph.com/mcp を追加してください。
ローカルサーバー
サーバーをローカルで実行する場合(Node.js 18+ が必要です):
claude mcp add paragraph -- npx @paragraph-com/mcp
ローカルモードでは、環境変数 PARAGRAPH_API_KEY を使うか、CLI から paragraph login を実行して API キーを設定する必要があります。
MCP サーバーは 29 個のツール(投稿、コンテンツ、出版物、購読者、コイン、検索、フィード、ユーザー、自分、分析、メール)を公開し、CLI と認証情報を共有します。全ドキュメントをご覧ください。
最近追加された主なツール:create-content / list-content / get-content / update-content / archive-content / restore-content(下書きの X 投稿、LinkedIn 投稿、ニュースレター、X Articles — 下書きのみを作成し、送信はしません)、send-custom-email(宛先リストへの Markdown メール一斉送信 — 出版物の承認が必要です)、update-publication(設定、注目の投稿、ピン留めされた投稿、メール通知の切り替え)、remove-subscriber(メールアドレスまたはウォレットによる完全削除)、update-post は過去の日付に設定する publishedAt を受け付けます。また、update-post は imageUrl(カバー画像を設定/置き換え)と clearImage(既存のカバー画像を削除)も受け付けます。
CLI のセットアップ
グローバルにインストールします:
npm install -g @paragraph-com/cli
認証します — ログインすると、キーが ~/.paragraph/config.json に保存されます:
paragraph login --token <api-key>
echo "$PARAGRAPH_API_KEY" | paragraph login --with-token
または、ログインを省略し、環境変数を使ってコマンドごとにキーを渡します:
PARAGRAPH_API_KEY=<api-key> paragraph post list --json
確認:paragraph whoami --json
作業上の取り決め
- 解析可能な出力には必ず
--jsonを使ってください。データは stdout に、ステータスやエラーは stderr に出力されます。 - 確認プロンプトを省略するため、
deleteでは必ず--yesを使ってください。 - 実行内容を事前確認するには、
delete、publish、archiveの前に--dry-runを使ってください。 - 位置引数だけでなく、フラグを使ってください。すべての識別子は
--idを受け付けるため、コマンドを連続して実行できます。 - ファイルから投稿を作成または更新する場合は、標準入力経由でコンテンツをパイプしてください:
cat draft.md | paragraph post create --title "My Post" --limitと--cursorを使ってページネーションしてください。JSON レスポンスにはpagination.cursorとpagination.hasMoreが含まれます。- 対話型ログインは使わないでください。非対話型認証には
--tokenまたは--with-tokenを使ってください。 - コマンドの実行前に認証を確認してください。
paragraph whoami --jsonを実行して、認証情報が有効であることを確認します。 - ユーザーの明示的な承認なしに公開しないでください。公開するとコンテンツが公開状態になり、購読者にメールが送信される場合があります。
- 既定では下書きにしてください。
post createは下書きを作成します。ユーザーが求めた場合にのみpost publishを呼び出してください。 contentコマンドは送信を行いません。出版物のライブラリに書き込むだけです。ライターは Paragraph アプリから記事を送信します。送信されたとユーザーに伝えないでください。- ユーザーの明示的な承認なしにカスタムメールを送信しないでください。
paragraph email sendは実際のメールを配信し、取り消せません。まず件名と本文を下書きし、実際に送信する前に--dry-runを使って対象の絞り込みを確認してください。403が返された場合は「この出版物では、まだカスタムメールが承認されていません」と伝えて停止し、再試行しないでください。 - レート制限を守ってください。
RATE_LIMITEDが発生した場合は、待ってから再試行してください。ページネーションされたリクエストを短い間隔で繰り返すことは避けてください。
コマンド
投稿
# Create a draft
paragraph post create --title "My Post" --file ./post.md --tags "web3,defi" --json
paragraph post create --title "My Post" --text "# Hello World" --subtitle "A subtitle" --json
cat content.md | paragraph post create --title "My Post" --json
# List
paragraph post list --json
paragraph post list --status draft --limit 20 --json
paragraph post list --status scheduled --json
paragraph post list --publication <slug-or-id> --json
# Get (by ID, URL, or @pub/slug)
paragraph post get --id <post-id> --json
paragraph post get @my-blog/post-slug --json
# Extract a single field
paragraph post get --id <post-id> --field markdown > post.md
paragraph post get --id <post-id> --field title
# Update
paragraph post update --id <id-or-slug> --title "New Title" --json
paragraph post update --id <id-or-slug> --text "Updated content" --subtitle "New subtitle" --json
paragraph post update --id <id-or-slug> --file ./updated.md --tags "new,tags" --json
# Backdate (publishedAt sticks across re-publishes — useful for imported content)
paragraph post update --id <id-or-slug> --published-at "2024-01-01T00:00:00Z" --json
# Set/replace cover image (URL is fetched + re-hosted on Paragraph's CDN)
paragraph post update --id <id-or-slug> --image-url https://example.com/cover.jpg --json
# Remove the existing cover image
paragraph post update --id <id-or-slug> --clear-image --json
# Publish
paragraph post publish --id <id-or-slug> --json
paragraph post publish --id <id-or-slug> --newsletter --json
# Revert to draft
paragraph post draft --id <id-or-slug> --json
# Archive
paragraph post archive --id <id-or-slug> --json
# Schedule a post for future publication
paragraph post schedule --id <id-or-slug> --at "2026-05-01T09:00:00Z" --json
paragraph post schedule --id <id-or-slug> --at "2026-05-01T09:00:00Z" --newsletter --json
# Cancel a scheduled publication
paragraph post unschedule --id <id-or-slug> --json
# Create and schedule in one step
paragraph post create --title "Launch Day" --file ./post.md --schedule-at "2026-05-01T09:00:00Z" --newsletter --json
# Preview destructive actions
paragraph post delete --id <id-or-slug> --dry-run --json
paragraph post publish --id <id-or-slug> --dry-run --json
# Delete
paragraph post delete --id <id-or-slug> --yes --json
# Send test newsletter email
paragraph post test-email --id <post-id> --json
# Browse
paragraph post feed --limit 10 --json
paragraph post by-tag --tag web3 --limit 20 --json
コンテンツ
短い形式のコンテンツを下書きします。X の投稿やスレッド、LinkedIn の投稿、単発メール、X Articles が対象です。これらのコマンドでは下書きだけを作成し、投稿、メール送信、予約は行いません。長文の投稿には paragraph post を使用します。
--kind は tweet、linkedin、newsletter、x_article のいずれかです。本文のフラグは種類によって異なります。tweet では --tweet(スレッド内の各ツイートに 1 つずつ指定し、複数回指定可能)、newsletter では --subject と --preheader、x_article では --headline と --canonical-url を使用します。長文のテキストは --text、--file、または標準入力から指定します。下書きはテキストのみです。メディアはアプリで追加する必要があります。
# Create
paragraph content create --kind tweet --title "Launch note" --text "We shipped it." --json
paragraph content create --kind tweet --title "Thread" --tweet "First." --tweet "Second." --json
paragraph content create --kind linkedin --title "Launch note" --file ./post.md --json
paragraph content create --kind newsletter --title "October update" --subject "What we shipped" --file ./body.md --json
paragraph content create --kind x_article --title "Editor rewrite" --headline "Why we rebuilt the editor" --file ./article.md --json
cat post.md | paragraph content create --kind linkedin --title "Launch note" --json
# List (--status defaults to all, which excludes archived pieces)
paragraph content list --json
paragraph content list --kind tweet --status draft --limit 50 --json
paragraph content list --status archived --json
# Get, with its body
paragraph content get <id> --json
paragraph content get <id> --field body
# Update — the body is replaced, not merged. Read it first.
paragraph content update <id> --title "Launch note, second pass" --json
paragraph content update <id> --text "Rewritten, and shorter." --json
# Archive and restore
paragraph content archive <id> --dry-run --json
paragraph content archive <id> --json
paragraph content restore <id> --json
送信が予約されているコンテンツはロックされます。本文の編集は拒否されます(予約はアプリでキャンセルする必要があります)が、名前はいつでも変更できます。JSON の lockedReason にはロックの理由が記載されます。編集可能な場合は null です。
コンテンツグループ
グループは、投稿と、そこから作成されたすべてのコンテンツ(投稿、投稿をもとにしたスレッド、LinkedIn 版、ニュースレター)をまとめる単位です。Content の下に 1 つの積み重なった行として表示されます。
投稿を再利用する場合は、それと一緒に作成する下書きもグループ化してください。そうしないと、互いに関連付けられていない下書きが作成者に表示されます。
# Get or create a post's group. Safe to repeat: the same ID comes back.
paragraph content bucket create <post-id> --json
# Draft into it
BUCKET=$(paragraph content bucket create <post-id> --json | jq -r .bucketId)
paragraph content create --kind tweet --title "Thread" --tweet "First." --bucket "$BUCKET" --json
# Group a draft made before the group existed
paragraph content update <id> --bucket "$BUCKET" --json
# Read a group and everything already made from its post
paragraph content bucket get <bucket-id> --json
# Find a post's group without creating one (bucketId is null when there is none)
paragraph content bucket for-post <post-id> --json
# List every group, most recently active first
paragraph content bucket list --limit 50 --json
下書きを作成する前にグループを読み込み、すでに存在するバージョンを作り直さないようにしてください。各メンバーの kind で読み込み先を確認できます。post は paragraph post get、content は paragraph content get で読み込みます。kind が other の場合、そのメンバーは Paragraph のグループに属しますが、CLI では取得できません。
別の投稿とすでにグループ化されているコンテンツは、移動されずに拒否されます。ユーザーにはアプリでグループ化を解除するよう伝えてください。グループからコンテンツを取り除く操作はアプリで行います。
Publications
paragraph publication get --id <slug-or-id-or-domain> --json
# Update settings (only provided fields change). The publication ID must
# match the publication that owns your API key — run `paragraph whoami` to
# look it up.
paragraph publication update <publication-id> --name "My Blog" --theme-color purple-600 --json
# Featured post: a post ID, or one of "latest" | "popular" | "disabled"
paragraph publication update <publication-id> --featured-post latest --json
# Pinned posts (replaces the existing list, max 50, IDs must belong to this publication)
paragraph publication update <publication-id> --pinned-post-ids id1,id2,id3 --json
# Owner email-notification toggles (key=value pairs).
# Allowed keys: newComment, newSubscriber, newPaidSubscriber, newContentCollected.
paragraph publication update <publication-id> --email-notifications newSubscriber=true,newComment=false --json
# Apply a full settings body from a JSON file. Explicit flags override file values.
paragraph publication update <publication-id> --from-json ./settings.json --json
検索
paragraph search post --query "ethereum" --json
paragraph search blog --query "web3" --json
購読者
paragraph subscriber list --limit 100 --json
paragraph subscriber count --publication <id> --json
paragraph subscriber add --email [email protected] --json
paragraph subscriber add --wallet 0x1234...abcd --json
paragraph subscriber import --csv ./subscribers.csv --json
# Remove (hard delete, irreversible — prompts for confirmation; use --yes to skip)
paragraph subscriber remove --email [email protected] --yes --json
paragraph subscriber remove --wallet 0x1234...abcd --yes --json
コイン
paragraph coin get --id <id-or-address> --json
paragraph coin popular --limit 10 --json
paragraph coin search --query "ethereum" --json
paragraph coin holders --id <id-or-address> --limit 50 --json
paragraph coin quote --id <id-or-address> --amount <wei> --json
ユーザー
paragraph user get --id <user-id-or-wallet> --json
アナリティクス
Publication のアナリティクススキーマに対して、読み取り専用の SQL を実行します。API キーを所有する Publication に自動的にスコープが設定されるため、WHERE 句にブログのフィルターを含めないでください。
# Discover tables and columns
paragraph analytics schema --json
paragraph analytics schema --table post_analytics_summary --json
# Run a query
paragraph analytics query --sql "SELECT title, total_views, open_rate FROM post_analytics_summary ORDER BY total_views DESC LIMIT 5" --json
# Read SQL from a file or stdin
paragraph analytics query --file ./query.sql --json
echo "SELECT active_subscriber_count FROM blog_subscriber_counts" | paragraph analytics query --json
生のテーブルより、事前集計済みのビュー(post_analytics_summary、subscriber_engagement_scores、blog_subscriber_counts)を優先してください。サブ秒で応答し、ほとんどのレポートに関する質問に対応できます。使用できるのは SELECT/WITH のみです。セミコロンは使用できません。最大行数は 10,000 行、ステートメントのタイムアウトは 30 秒です。
カスタムメール
指定した宛先リストに、出版物から単発のマークダウンメールを送信します。各受信者には個別にメールが送信され、必須の配信停止フッターが付きます。
次の用途に使用します:
- 対象を絞ったセグメントへの送信 — 「前回の投稿を開封した人全員にメールする」「エンゲージメントが最も高い購読者50人にフォローアップする」「厳選した読者20人に連絡する」などです。
paragraph analytics queryまたはparagraph subscriber listでセグメントを作成します。 - 執筆者自身への通知 — 「購読者が1,000人に達したらメールする」「週次の分析ダイジェスト」などです。必要に応じて
paragraph whoamiで執筆者のメールアドレスを確認します。 - 購読者以外のアドレスへの連絡 — カンファレンス参加者のCSV、報道関係者リスト、友人の友人への紹介などです。宛先はどこからでも指定でき、
paragraph subscriber listに含まれている必要はありません。(過去に配信停止した人は引き続きsuppressedとして扱われます。) - 非アクティブな購読者の再エンゲージメント — 「90日間開封していない人全員にメールする」などです。
subscriber_engagement_scoresまたはnewsletter_metricsを対象にparagraph analytics queryを実行してセグメントを特定します。 - 少数の共同作業者による下書きのレビュー — 「この企画書の下書きを共著者3人に送ってフィードバックをもらう」などです。出版物の所有者以外にメールを送る必要がある場合に使用します。
paragraph post test-emailは所有者にしか送信されません。
ニュースレターの一斉配信には使用しないでください。 投稿をすべての購読者にメールするには、paragraph post publish --newsletter(または paragraph post create ... --newsletter)を使用します。これはニュースレターの配信パイプラインです。email send は、指定した対象リストへの送信に使用します。
カスタムメールを利用するには、Paragraph による出版物の承認が必要です。対象外の出版物には 403 が返されます。1回の呼び出しで送信できる受信者は最大10,000人です。メールは実際に送信され、取り消せないため、送信前に必ずユーザーに確認してください。
# Send (prompts for confirmation; pass --yes to skip)
paragraph email send --subject "Hello" --body "# Hi" --to [email protected] --yes --json
# Read body from a file
paragraph email send --subject "Update" --body-file ./body.md --to [email protected] --to [email protected] --yes --json
# Comma-separated recipients (--to is repeatable)
paragraph email send --subject "Update" --body-file ./body.md --to "[email protected],[email protected]" --yes --json
# Pipe body via stdin
cat body.md | paragraph email send --subject "Update" --to [email protected] --yes --json
# Dry run — preview the accepted/skipped split without sending
paragraph email send --subject "Update" --body "# Hi" --to [email protected] --dry-run --json
JSON レスポンスには、配信待ちの accepted と、スキップされた宛先の skipped が含まれます。スキップされた宛先にはそれぞれ reason があり、invalid(形式が正しくないアドレス)、suppressed(過去に配信停止済み)、scheduling_failed(キューへの登録失敗。再試行しても安全な理由はこれだけです)のいずれかです。
認証
paragraph login --token <api-key>
echo "<api-key>" | paragraph login --with-token
paragraph whoami --json
paragraph logout
JSON レスポンスの形式
ページ分割されたリスト(注:CLI では項目が data の下に格納されます。REST API と SDK では代わりに items が使用されます):
{
"data": [{ "id": "...", "title": "..." }],
"pagination": { "cursor": "abc123", "hasMore": true }
}
単一項目:
{ "id": "...", "title": "...", "markdown": "..." }
変更:
{ "id": "...", "status": "published" }
エラー(標準エラー出力):
{ "error": "Not found.", "code": "NOT_FOUND", "status": 404 }
エラーコード:UNAUTHORIZED、FORBIDDEN、NOT_FOUND、RATE_LIMITED、SERVER_ERROR、REQUEST_FAILED、CLIENT_ERROR、UNKNOWN。
よくあるパターン
1つの手順で作成して公開する
ID=$(paragraph post create --title "My Post" --file ./post.md --json | jq -r '.id')
paragraph post publish --id "$ID" --newsletter --json
すべての投稿をマークダウンとしてエクスポートする
paragraph post list --limit 100 --json | jq -r '.data[].id' | while read id; do
SLUG=$(paragraph post get --id "$id" --json | jq -r '.slug')
paragraph post get --id "$id" --field markdown > "${SLUG}.md"
done
すべての購読者をページ分割して取得する
CURSOR=""
while true; do
RESULT=$(paragraph subscriber list --limit 100 ${CURSOR:+--cursor "$CURSOR"} --json)
echo "$RESULT" | jq '.data[]'
CURSOR=$(echo "$RESULT" | jq -r '.pagination.cursor // empty')
HAS_MORE=$(echo "$RESULT" | jq '.pagination.hasMore')
[ "$HAS_MORE" = "true" ] || break
done
環境変数
| 変数 | 用途 |
|---|---|
| PARAGRAPH_API_KEY | API キー(ログインを省略) |
| PARAGRAPH_API_URL | カスタム API ベース URL |
| PARAGRAPH_NON_INTERACTIVE | CLI モードを強制するには 1 を設定 |
| CI | CLI モードを強制するには true を設定 |
トラブルシューティング
認証エラー
コマンドが UNAUTHORIZED で失敗する場合:
# Check if logged in
paragraph whoami --json
# Re-authenticate
paragraph login --token <api-key>
CLI は 401 の発生時に、保存された認証情報を自動的に消去します。認証情報が無効にされた場合は、再度ログインしてください。
レート制限
RATE_LIMITED が返された場合は、待ってから再試行してください。エラーには 429 ステータスコードが含まれます。短い間隔での繰り返しを避け、ページ分割されたリクエストの間に遅延を入れてください。
コマンドが停止したように見える
コマンドが停止したように見える場合、標準入力を待っている可能性があります。--text、--file、または標準入力へのパイプでコンテンツを渡していることを確認してください。標準入力がパイプ接続されていてデータが届かない場合、CLI は30秒後にタイムアウトします。
インストール後に CLI が見つからない
npm install -g @paragraph-com/cli
# Verify
npx paragraph --version