mcpField
S
Substack MCP Server

Substack MCP Server

ローカル
implementing.substack.comソーシャルメディア928 週間前に更新

Substack MCP Server について

Substack MCP Server は、LLM クライアントから Substack の発行物を操作できる MCP サーバーです。下書き、記事、購読者、分析、リーダーフィード、コメント、画像を管理できます。Substack には公開 API がないため、認証済みのウェブセッションを利用し、セッショントークンを秘匿して扱う必要があります。

主な機能

  1. 01出版と購読者管理をカバーする 27 のツール
  2. 02複数条件のフィルターとページングに対応した購読者一覧
  3. 03エンゲージメント指標を含む購読者のエクスポート
  4. 04下書き本文への構造化ドキュメントの書き込み
  5. 05URL またはローカルファイルからの Substack への画像アップロード

活用シーン

  1. 01LLM クライアントから下書き記事を作成・編集する
  2. 02セグメントで購読者を絞り込み、エンゲージメントデータを書き出す
  3. 03画像をアップロードして記事に挿入する

MCP 対応

本プロジェクトは、Substack の発行、購読者、分析、画像に関するツールを MCP クライアントに公開する MCP サーバーです。

設定

ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。

mcp.json · 共通設定
{
"mcpServers": {
"marcomoauro-substack": {
"command": "npx",
"args": [
"-y",
"substack-mcp"
],
"env": {
"SUBSTACK_PUBLICATION_URL": "<SUBSTACK_PUBLICATION_URL>",
"SUBSTACK_SESSION_TOKEN": "<SUBSTACK_SESSION_TOKEN>",
"SUBSTACK_USER_ID": "<SUBSTACK_USER_ID>"
}
}
}
}
起動コマンドnpx -y substack-mcp
設定が必要な環境変数
SUBSTACK_PUBLICATION_URLFull URL of your Substack publication, including https://
SUBSTACK_SESSION_TOKENシークレットValue of the substack.sid or connect.sid cookie from your authenticated Substack session
SUBSTACK_USER_IDNumeric user ID returned by Substack's publication_user request

ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。

パッケージ
npmsubstack-mcp1.3.2
ocidocker.io/marcomoauro/substack-mcp:v1.3.2

ツール

27

掲載時のツール一覧 · 2 週間前に更新

パラメーター型必須説明
bodystringはい投稿の本文です。プレーンテキストは行ごとに段落になります。Markdown は解釈されないため、`## Heading` はそのまま表示されます。Substack ドキュメントの JSON 文字列も使用でき、set_post_body が公開に使うものと同じスキーマセットで検証されます。認識されないノード名は、投稿が崩れた状態で作成されるのではなく、エラーになります。構造化コンテンツには、スキーマが公開され、ノードの種類を確認できる set_post_body を使用してください。
titlestringはい作成する投稿のタイトル。
subtitlestringはい作成する投稿のサブタイトル。

詳細

Substack MCP サーバー

Substack 用の Model Context Protocol(MCP)サーバーです。LLM クライアントから Substack の API を利用し、投稿の作成や下書きの管理などを自動化できます。

Docker Pulls npm downloads

投稿の作成と公開、購読者や分析データの管理、読者フィードの閲覧、タグやコメントの管理、画像のアップロードができます。1 つの MCP サーバーで 27 個のツールを提供します。

重要

Substack は、これらの操作向けの公開 API を提供していません。このサーバーは、認証済みのウェブセッションを使用します。セッショントークンはパスワードと同様に扱ってください。ローカルに保管し、コミットせず、バグ報告にトークンや Cookie ヘッダー全体を含めないでください。

クイックスタート

最も簡単なインストール方法は、Node.js 22 以降と npx を使用することです。

1. Substack の認証情報を取得する

ブラウザーで Substack にサインインし、出版物のダッシュボードを開きます。次の 3 つの値が必要です。

  • 出版物の URL — 出版物の完全なベース URL。例: https://your-publication.substack.com。
  • セッショントークン — ブラウザーの開発者ツールを開き、Network を選択して Fetch/XHR で絞り込み、ダッシュボードを再読み込みします。出版物への認証済みリクエストを開きます。Request Headers で Cookie ヘッダーを見つけ、substack.sid または connect.sid という名前のセッションクッキーを探します。クッキー名やヘッダーの残りの部分を含めず、値だけをコピーします。両方の名前が異なる値で表示される場合は、それぞれを個別に試してください。ステップ 3 の読み取り専用の方法で、ローカル環境で確認できます。どちらの値も課題に貼り付けないでください。
  • ユーザー ID — 同じ Network パネルで、成功した publication_user リクエストを検索します。その JSON レスポンスの user オブジェクト内にある数値の id をコピーします。

ブラウザーの UI が異なる場合は、図解付きの認証情報ガイドに同じリクエストが掲載されています。認証が機能しなくなった場合は、再度サインインしてこれらの手順を繰り返し、最新のトークンを取得してください。

2. MCP クライアントにサーバーを追加する

MCP の JSON 設定に対応しているクライアントでは、次を追加してください。

{
  "mcpServers": {
    "substack": {
      "command": "npx",
      "args": ["-y", "substack-mcp@latest"],
      "env": {
        "SUBSTACK_PUBLICATION_URL": "https://your-publication.substack.com",
        "SUBSTACK_SESSION_TOKEN": "your-session-token",
        "SUBSTACK_USER_ID": "your-user-id"
      }
    }
  }
}

3 つのサンプル値を置き換えて設定を保存し、MCP クライアントを再起動します。クライアントで異なる設定形式を使用している場合は、クライアントのドキュメントを参照してください。

3. 接続を確認する

クライアントに次のように尋ねてください。

Substack の最新の下書きを 5 件表示してください。

クライアントは status: "drafts" を指定して list_posts を呼び出すはずです。失敗した場合は、課題を報告する前に、クライアントの MCP ログと以下のログのセクションを確認してください。

Docker クイックスタート

Node.js の代わりに公開済みの Docker イメージを使用するには、次のサーバー設定を追加してください。

{
  "mcpServers": {
    "substack": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-e", "SUBSTACK_PUBLICATION_URL",
        "-e", "SUBSTACK_SESSION_TOKEN",
        "-e", "SUBSTACK_USER_ID",
        "marcomoauro/substack-mcp:latest"
      ],
      "env": {
        "SUBSTACK_PUBLICATION_URL": "https://your-publication.substack.com",
        "SUBSTACK_SESSION_TOKEN": "your-session-token",
        "SUBSTACK_USER_ID": "your-user-id"
      }
    }
  }
}

🛠 利用可能なツール

create_draft_post - 下書きの投稿を作成

入力:

  • title(文字列):投稿のタイトル
  • subtitle(文字列):投稿のサブタイトル
  • body(文字列):投稿の本文。プレーンテキストは 1 行につき 1 つの段落になります。Markdown は解釈されないため、## Heading はそのまま文字列として扱われます。Substack ドキュメントの JSON 文字列も使用できます。この場合、set_post_body が公開するものと同じスキーマに照らして検証されます。そのため、認識されないノード名は、投稿が意図せず改変されるのではなく、エラーになります。

戻り値:{draft_id, is_published}。下書きを読み返すには、draft_id を get_draft に渡してください。

見出し、リスト、リンク、コード、画像、ペイウォールなど、構造化された内容を扱う場合は、下書きを作成した後に set_post_body を使用してください。スキーマはそこで公開されているため、呼び出し元のモデルはノードの種類を推測せず、語彙を確認できます。

list_subscribers - 購読者の一覧表示と絞り込み Subscribers ダッシュボードと同じフィルタリング機能を提供します。48 個の列、18 個の演算子、フリーテキスト検索、並べ替え、ページネーションに対応しています。

入力:

  • filters(配列、任意):AND で結合する条件。それぞれ {column, operator, value}
  • search(文字列、任意):購読者の名前とメールアドレスに対して照合するフリーテキスト
  • sort_by(文字列、任意):フィルタリング可能な任意の列
  • sort_direction(asc | desc、任意):デフォルトは desc
  • limit(数値、任意):1~100、デフォルトは 25
  • offset(数値、任意):ページングに使用

列で受け付ける演算子は、列の型によって異なります。

型演算子
Intis is_not gt gte lt lte
Stringis is_not is_any_of contains starts_with ends_with includes_none
DateTimeis_on is_after is_on_or_after is_before is_on_or_before
Array(tag_ids、emails_enabled)includes_any includes_all includes_none
subscription_type、group_membershipis is_not is_any_of

列は、購読者の識別情報(名前、メールアドレス、国、州、グループの所属状況)、購読情報(種別、開始日/有効期限/解約日、収益、Stripe プラン、アトリビューション)、メールのエンゲージメント(7 日間/30 日間/6 か月間の開封数とユニーク開封数、クリックされたリンク、セクション)、サイトのエンゲージメント(投稿の閲覧数、閲覧したユニークな投稿数、コメント、シェア、アクティブ日数、アクティビティ評価)を網羅しています。型を含む全列の一覧はツールの JSON Schema を通じてクライアントに渡されるため、モデルが列名を推測する必要はありません。

戻り値:{count, returned, limit, offset, subscribers}。count は limit にかかわらず、フィルターに一致する件数の合計です。そのため、limit: 1 を指定すれば、セグメントの規模を手軽に確認できます。

注:ここではエンゲージメント列をフィルタリングできますが、このツールが返すレコードには含まれません。Substack は返すフィールドを公開の保存済み表示設定から取得し、リクエストごとの列リストは無視します。値を取得するには export_subscribers を使用してください。

OR 条件とネストには対応していません。OR が必要な場合は、個別に呼び出してください。

export_subscribers - すべての列の値を含めて購読者をエクスポート

list_subscribers でフィルタリングのみ可能なエンゲージメント指標、つまり 7 日間/30 日間/6 か月間のメール開封数、ユニークなメールの閲覧数、投稿の閲覧数、閲覧したユニークな投稿数、コメント、シェア、クリックされたリンク、アクティブ日数、アクティビティ評価を実際に読み取る方法です。

入力:

  • filters(配列、任意):list_subscribers と同じ条件。AND で結合されます
  • search(文字列、任意):購読者の名前とメールアドレスに対して照合するフリーテキスト
  • columns(配列、任意):含める列。デフォルトではすべてが対象です
  • max_wait_seconds(数値、任意):1~600、デフォルトは 120

戻り値:{count, columns, missing_columns, unmapped_columns, export_id, subscribers}。各購読者の情報は列名をキーとして格納されます。

Substack はファイルを非同期で生成するため、このツールは購読者セットを作成し、エクスポートをリクエストして、準備ができるまでポーリングした後、ダウンロードします。小規模なエクスポートは数秒で完了します。待機時間の上限に達した場合、ツールはその旨と export_id を示し、処理をブロックしません。

注意点が 2 つあります。どちらも実際の API で確認済みです。

  • tag_ids と group_membership はエクスポートできません。Substack はエラーを返す代わりに、これらを通知なく除外します。そのため missing_columns に報告されます。48 列すべてを要求しても、返されるのは 46 列です。
  • 値は生データではなく、表示用に書式設定された形式で返されます。ここでは収益は "€50.00" ですが、list_subscribers では数値の 50 が返されます。日付は ISO 形式の文字列です。

ページネーションには対応していません。エクスポートには、一致するデータセット全体が含まれます。

list_posts - 下書き、公開済み、または予約済みの投稿を一覧表示 **入力**: - `status`(`drafts` | `published` | `scheduled`):読み込むリスト - `search`(文字列、任意):タイトルと本文に対して照合するフリーテキスト - `limit`(数値、任意):1~100。既定値は25 - `offset`(数値、任意):ページング用 - `sort_direction`(`asc` | `desc`、任意):下書きと公開済みの投稿は新しい順、予約済みの投稿は予定日時が近い順です

戻り値:{status, total, returned, limit, offset, posts}。各投稿は要約されています。未公開の投稿の全文を取得するには、get_draftを使用してください。

get_draft - 下書き1件の全文を読み込む

入力:

  • draft_id(数値):list_postsまたはcreate_draft_postが返すID

戻り値:Substackに保存されている形式の下書き。本文とオーディエンス/メールの設定が含まれます。

set_post_body - 下書きの本文を構造化ドキュメントで置き換える

見出し、リスト、リンク、コードブロック、引用、画像、ボタン、ペイウォールなどの構造化コンテンツを書き込む唯一の方法です。create_draft_postはプレーンテキストを受け取りますが、こちらはSubstackが実際に保存するドキュメントを受け取ります。そのスキーマはtools/listで公開されているため、呼び出し元のモデルは推測せずにノードの種類を確認できます。

入力:

  • draft_id(数値):list_postsまたはcreate_draft_postが返すID
  • body(オブジェクト):Substack ProseMirrorドキュメント — {type: 'doc', content: [...]}

15種類のノードを受け付けます:paragraph、heading、bullet_list、ordered_list、list_item、blockquote、highlighted_code_block、code_block、horizontal_rule、captionedImage、button、paywall、youtube2に加え、get_draftで読み込んだドキュメントを書き戻せるよう、digestPostEmbed、substack_mentions、directMessageは変更せずに渡されます。マーク:strong、em、code、strikethrough、link。

戻り値:{draft_id, nodes}。nodesは種類ごとに保存されたノードの数を示すため、ペイウォールを指定した呼び出し元は、実際に1つあることを確認できます。送信されていないノードについては、検証で報告できません。

知っておくべきことが3つあります:

  • 画像は事前にSubstack上でホストされている必要があります。 外部URLを指定したimage2.srcは保存されますが、表示されません。upload_imageを使ってホストし直し、使用可能なsrcを取得してください。
  • 1つのドキュメントに含められるpaywallは最大1つです。 Substackは2つでも受け付けて両方を表示しますが、どちらが投稿を区切るのか不定になるため、このツールは2つ目を拒否します。
  • ordered_listはattrs.startではなくattrs.orderに基づいて番号を付けます。 startだけを指定したリストは、エラーなく1から表示されます。
upload_image - URLまたはローカルファイルから画像をSubstack上でホストする

Substackのエディターは、画像をbase64データURIとしてPOST /api/v1/imageにアップロードし、Substack上でホストされたURLを返します。set_post_bodyのimage2.srcとupdate_draftのcover_imageにはこのようなURLを指定しないと表示されないため、このツールが橋渡しをします。Substack自体は独自のストレージ内にあるURLしか再取得しないため、画像は引き渡すのではなく、ここでエンコードされます。

入力 — urlまたはpathのいずれか一方のみ:

  • url(文字列):ダウンロードしてホストし直す画像のhttp(s) URL
  • path(文字列):このサーバーを実行しているマシン上にある画像ファイルの絶対パス。ダウンロードせず、ディスクから直接読み込みます
  • post_id(数値、任意):画像が属する投稿。影響は未確認です

戻り値:{id, url, content_type, bytes, width, height} — set_post_bodyの呼び出し時にurlをimage2.srcへ、またはupdate_draftの呼び出し時にcover_imageへ指定してください。

ダウンロードにはガードが適用されます:http/httpsのみが許可され、DNS解決後にプライベートホストとループバックホストは拒否されます(リダイレクトは各ホップで再確認されます)。コンテンツタイプは画像である必要があり、HEICは変換を促す注記とともに拒否されます。また、画像サイズは10 MB以下でなければなりません。 ローカルファイルは、信頼できる Content-Type ヘッダーがないため、別の方法で保護されます。パスは絶対パスでなければなりません。相対パスは呼び出し元のクライアントではなく、このサーバーの作業ディレクトリを基準に解決されるためです。また、拡張子ではなくファイルのマジックバイトから種類を判定するため、画像拡張子が付いた画像以外のファイルは Substack に渡る前にここで検出されます。PNG、JPEG、GIF、WebP は使用できますが、HEIC と SVG は使用できません。同じ 10 MB の上限が適用され、ファイルを読み込む前にサイズが確認されます。path は指定された絶対パスのファイルを読み込みます。この点が環境にとって問題になる場合は、ファイルシステムを信頼できないクライアントにこのサーバーを公開しないでください。

update_draft - 下書きのタイトル、サブタイトル、または投稿設定を変更します

更新は部分的です。渡したフィールドだけが変更され、本文はそのまま残ります。

入力:

  • draft_id(number):list_posts または create_draft_post が返す ID
  • draft_title(string、任意)
  • draft_subtitle(string、任意)
  • audience(everyone | only_paid | only_free | founding、任意)
  • write_comment_permissions(everyone | subscribers | only_paid | none、任意):コメントを許可する対象
  • default_comment_sort(best_first | most_recent_first | oldest_first、任意)
  • cover_image(string、任意):ソーシャルプレビュー画像。substack-post-media.s3.amazonaws.com または substackcdn.com 上にある URL はそのまま使用されます。それ以外はダウンロードされ、upload_image と同じ保護措置のもとで、まず Substack に再ホストされます
  • social_title(string、任意):投稿を別の場所で共有するときに使われるタイトル
  • description(string、任意):ソーシャルプレビューの説明文。サブタイトルではありません
  • search_engine_title(string、任意)
  • search_engine_description(string、任意)
  • slug(string、任意):投稿の URL スラッグ

戻り値:{draft_id, updated_fields, draft_title, draft_subtitle, audience, is_published, cover_image, cover_image_rehosted_from}。 変更するフィールドがない場合、何もしないリクエストとして送信するのではなく、呼び出しは拒否されます。cover_image は実際に設定された URL であり、再ホストされた場合は渡された URL と異なります。

publish_draft - 下書きを公開します

入力:

  • draft_id(number):list_posts または create_draft_post が返す ID
  • send(boolean、任意):投稿を購読者にメールで送信します。デフォルトは false です。これは Substack API 自体のデフォルトとは異なります。いずれの場合も投稿はウェブ上で公開されますが、メールは取り消せないため、明示的に指定する必要があります。

戻り値:{status, draft_id, post_id, title, slug, canonical_url, emailed, email_sent_at}。 emailed はメール送信の要求内容を示し、email_sent_at はメールを送信したかどうかをサーバーが記録したものです。

メール送信の意図は、公開前に下書きの should_send_email に書き込まれ、公開リクエストにも渡されます。このフィールドはダッシュボードで設定が保持される場所で、デフォルト値は true です。そのため、どちらか一方だけを設定すると、エンドポイントがリクエスト本文ではなく下書きを参照した場合に、リスト全体へメールを送ってしまうおそれがあります。

公開を取り消すツールはありません。このサーバーから公開を取り消すことはできません。

delete_draft - 未公開の下書きを削除します

入力:

  • draft_id(number):list_posts または create_draft_post が返す ID

戻り値:{status, draft_id, draft_title}。

Substack は下書きと公開済み投稿を同じエンドポイントで削除するため、このツールは対象を先に確認し、公開済みの場合は拒否します。公開中の投稿の削除は取り消せないため、ダッシュボードで行ってください。

get_publication - パブリケーションの設定を読み込みます

入力:

  • full(boolean、任意):項目を絞った結果ではなく、111 個すべてのフィールド(約 24 KB)を返します。 デフォルトは false です。 戻り値:デフォルトでは、name、subdomain、custom domain、hero text、copyright、sender name、logo、plans、payment state、community/podcast フラグの投影に加え、除外されたフィールド数を示す _meta を返します。完全なペイロードの大部分は、通知の切り替え設定とウェルカムメール、利用規約ページ、プライバシーページの生の HTML です。
get_user_profile - セッションの背後にあるアカウントを読み取る

入力:

  • full (boolean, optional):完全な subscriptions 配列を含めます。デフォルトは false です。

戻り値:{id, name, handle, bio, photo_url, publications, primary_publication_id, subscription_count}。publications には、セッションが何らかのロールを持つすべての publication が含まれます。これにより、SUBSTACK_PUBLICATION_URL が接続先になり得る唯一の publication ではないことを確認できます。

list_publication_tags - publication に定義されているタグを一覧表示する

入力:

  • include_hidden (boolean, optional):ナビゲーションに表示されないタグも含めます。デフォルトは true です。

戻り値:{total, returned, tags}。各タグは {id, name, slug, hidden} です。タグ ID は整数ではなく UUID です。この API のほかのすべての ID とは異なります。

get_post_tags - 1 件の投稿のタグを一覧表示する

入力:

  • post_id (number):list_posts の ID。下書きでも機能します。

戻り値:{post_id, count, tags}。各タグは {post_tag_id, name, slug, hidden, association_id} です。

基盤となるエンドポイントが返すのは UUID のみなので、publication のタグ一覧と照合して名前を解決します。get_draft も list_posts もタグを含まないため、タグを読み取る方法はこれだけです。

add_tag_to_post - 投稿にタグを付ける

入力:

  • post_id (number):list_posts の ID。下書きでも機能します。
  • tag_name (string):既存のタグと大文字・小文字を区別せずに照合します。
  • create_if_missing (boolean, optional):名前が一致するタグがない場合にタグを作成します。デフォルトは true です。入力ミスを新しいタグにせず、エラーとして報告するには false に設定します。

戻り値:{status, post_id, tag, tag_created, association_id}。status は tagged または already_tagged です。投稿にすでに付いているタグを再追加すると、上流では単に 400 が返されるため、事前に確認します。

ID は UUID で、呼び出し側が合理的に保持できるものではないため、ID ではなく名前を指定します。

get_post_comments - 自分の投稿に付いたコメントを読み取る

入力:

  • post_id (number):list_posts の ID
  • limit (number, optional):1~100。デフォルトは 50 です。

戻り値:{post_id, returned, automod_hidden_count, comments}。各コメントには、投稿者、プレーンテキストの本文、リアクション数と返信数、スレッド内の位置(parent_comment_id、depth)が含まれます。Substack の自動モデレーションによって保留されたコメントは、数としてカウントされますが、統合はされません。上流では別の配列で届くため、黙って除外すると「保留中」が「コメントなし」になってしまいます。

comment_on_post - 自分の投稿にコメントする

入力:

  • post_id (number):list_posts の ID
  • body (string):プレーンテキスト。Substack がサーバー側で変換します。

戻り値:{status, post_id, comment}。

コメントはあなたの名前で公開されます。何が書き込まれたかを記録する唯一の手段がログであるため、リクエスト前に全文を info に記録します。このサーバーでは削除機能を提供していませんが、Substack UI からコメントを削除できます。リスタックとは異なり、コメントにはそれ自体の ID があります。

以下の 7 つのツールは、publication ではなく substack.com を読み取ります。アカウントの読者としての情報、つまり何を購読しているか、受信トレイやフィードに何があるかを扱います。これは、上記の publisher 向けインターフェイスとはホストも ID 空間も異なります。

list_subscriptions - このアカウントが購読しているものを一覧表示する **入力**: - `limit`(数値、任意):1~500、デフォルトは100 - `active_only`(ブール値、任意):一時停止中および期限切れのサブスクリプションを除外します。デフォルトは`true`です。

戻り値:{returned, pages_fetched, subscriptions}。各サブスクリプションには、プラン、membership_state、 is_founding、is_favorite、メールがオフかどうかが含まれます。内部で最大20回リクエストを行い、その上限に達して取得が止まった場合は truncated: trueを返します。

list_subscribers(あなたを購読している人の一覧)とは異なります。

list_reader_posts - リーダーの受信トレイ

入力:

  • limit(数値、任意):1~100、デフォルトは20
  • after(文字列、任意):前回のレスポンスのnext_after。不透明なカーソルではなくタイムスタンプです。このエンドポイントのcursorフィールドは常にnullです。

戻り値:{returned, more, next_after, posts}。各投稿は、閲読状態(is_read、read_progress、is_saved)とともに要約されます。受信トレイは各投稿の本文全体を送信しますが、ここでは省略されるため、投稿を読むにはget_reader_postを使用してください。

get_reader_post - 任意の投稿を全文読む

入力:

  • post_id(数値):list_reader_postsまたはget_reader_feedから取得
  • include_body(ブール値、任意):デフォルトはtrue

戻り値:投稿のメタデータとbody_html。body_truncated: trueは、このセッションでは解除できないペイウォールによって本文が非表示になっていることを意味します。preview_textには引き続きプレビューが含まれます。

本文はHTMLのままです。変換するには新たな依存関係を追加するか、マークアップに正規表現を適用する必要があり、正規表現によるHTML変換では、入れ子になったリストや埋め込みコンテンツが気付かないうちに壊れてしまうためです。

get_reader_feed - Notesフィード

入力:

  • tab(文字列、任意):タブのID — for-you(デフォルト)またはsubscribed。表示名はローカライズされているため、指定しないでください。
  • limit(数値、任意):1~50、デフォルトは20
  • cursor(文字列、任意):前回のレスポンスのnext_cursor
  • include_tabs(ブール値、任意):利用可能なタブIDも返します

戻り値:{tab, returned, next_cursor, items}。各項目はnoteまたはpostです。 non_content_items_skippedは、Substackが配列に混在させる「フォローする人」ブロックの数を示します。これらにはコンテンツが含まれていません。

get_profile_feed - あるアカウントが公開した内容

入力:

  • user_id(数値、任意):デフォルトはSUBSTACK_USER_ID — あなた自身のアカウント
  • type(all | notes | posts、任意):デフォルトはall
  • limit(数値、任意):1~50、デフォルトは20
  • cursor(文字列、任意)

戻り値:{user_id, type, returned, next_cursor, items}。フィルタリング時は、read_from_profileがページに実際に含まれていた項目数を示します。そうでない場合、「20件を読み込んでノートが3件」という表示が、「このアカウントが書いたノートは3件」と誤解される可能性があります。

get_comment_thread - ノートとその返信を読む

入力:

  • comment_id(数値):SubstackのURLで使われるc-プレフィックスを除いたもの
  • include_replies(ブール値、任意):デフォルトはtrue

戻り値:{comment, branch_count, replies_returned, more_branches, next_cursor, branches}。各ブランチには直接の返信とその子孫が含まれ、parent_comment_idとdepthが解決されています。

restack_item - ノートをリスタックする

入力:

  • comment_id(数値):リスタックするノート。get_reader_feedまたはget_profile_feedから取得
  • tab_id(文字列、任意):デフォルトはfor-you

戻り値:{status, comment_id, restack_id, note}。

これは公開され、あなたのプロフィールに表示されます。また、ここから元に戻すことはできません。リスタック自体のIDはなく、元のノートがcontext: comment_restackとともに表示されるため、このサーバーで削除する対象がありません。SubstackのUIから削除してください。

注記のみです。*投稿*の再スタックは提供されていません。この呼び出しは、自分のPublicationで公開済みの投稿に対しても`404`を返すため、`post_id`パラメーターを指定すると、ツールの誤りではなく投稿が存在しないことを示すエラーになります。
get_publication_stats - 主要な統計を確認する

入力:なし。

戻り値:購読者の総数と最近の購読者数、メールとアプリの購読者数、ARR、サイトの閲覧数、過去30日間のメール開封率。それぞれ、Substackが変化量を報告している場合は、その変化量も含まれます。基盤となるエンドポイントのいずれかが失敗しても、残りのデータは返され、失敗したエンドポイントはerrorsに示されます。

さらに詳しく調べるには、get_analyticsを使用してください。

get_post_stats - 43種類の指標で投稿を順位付けする

実際に読者リストを増やした投稿、最も価値があった投稿、購読者を減らした投稿を確認できます。ダッシュボードの「投稿」タブと同様に、並べ替えとページ分割ができます。

入力:

  • order_by(文字列、任意):43種類の指標のいずれか。デフォルトはpost_date
  • order_direction(asc | desc、任意):デフォルトはdesc
  • limit(数値、任意):1~100。デフォルトは25
  • offset(数値、任意):アーカイブのページ分割に使用

特に役立つ指標は次のとおりです。

グループフィールド
コンバージョンsignups subscribes founding_subscribes annual_subscribes monthly_subscribes free_trials free_to_paid_upgrades signups_within_1_day estimated_value
解約unsubscribes
閲覧opens open_rate clicks click_through_rate views subscribers_finished_post
ソーシャルlikes shares restacks engagement_rate unique_engagements
配信queued sent delivered dropped
動画/ポッドキャストvideo_views video_minutes_watched downloads downloads_day30 …

戻り値:{total, returned, limit, offset, order_by, order_direction, posts}。totalはページ内の件数ではなく、アーカイブ全体の件数です。order_byとorder_directionも返されるため、どの条件で作成された順位なのかが分からないまま順位を読むことはありません。

注意点が2つあります。どちらも検証済みです。

  • **日付フィルターはありません。**このエンドポイントではfrom_date/to_dateは無視され、totalも変わらないため、スキーマにはこれらを用意していません。代わりに、並べ替えとページ分割で範囲を絞り込んでください。
  • 率(open_rate、engagement_rate、click_through_rate)で降順に並べ替えると、データのない投稿が先頭に表示されます。nullは数値より前に並ぶためです。別の質問に暗黙のうちに答えることにならないよう、ツールではこれらを除外しません。

order_byを列挙型にしているのは意図的です。APIは認識できないフィールドに対しても200を返し、任意の順序で結果を返すため、タイプミスがあると、信頼できそうに見える順位が生成されてしまいます。

get_analytics - Publicationレベルの16種類のレポートから1つを確認する

ダッシュボードの「統計」タブにあるすべての内容を、互いによく似た17個のツールではなく、report列挙型を使う1つのツールで確認できます。

入力:

  • report(文字列):確認するレポート。下の表を参照してください
  • from_date、to_date(文字列、任意):YYYY-MM-DD。期間を対象とするレポートでのみ使用され、デフォルトでは過去30日間が対象です
  • limit(数値、任意):1~100。audience_overlapとsubscriber_notesでのみ使用されます | レポート | 内容 | |---|---| | retention | コホート別の継続率。各登録コホートが数か月後も購読を続けている割合です | | retention_summary | 1、6、12 か月時点の主要な継続率 | | unsubscribes / unsubscribes_timeseries | 解約数と、解約時に記された理由 | | growth_sources | 新規購読者の流入元を順位付けして表示します | | growth_events | 指定期間内に発生した個々の成長イベント | | referrals_leaderboard / referrals_summary | 紹介数の多いユーザー、送信・承認・成約したギフト | | audience_overlap | オーディエンスが重複する他の Substack。コラボレーション候補の一覧です | | audience_locations | 購読者がいる国と米国の州の数 | | subscriber_notes | 購読者が最近書いた Notes | | paid_subscriber_growth | 有料購読者の成長率、新規購読、期限切れ | | subscribers_timeseries、followers_timeseries、arr_timeseries | 時系列での購読者数、フォロワー数、収益 | | network_attribution | Substack ネットワーク経由で登録した購読者の割合 |

戻り値:{report, params, ignored_params, data}。params には、デフォルト値を含め、実際に送信されたパラメーターが入ります。同じレポートでも対象期間が異なると結果は大きく変わるため、期間の情報なしでは数値の意味を把握しにくくなります。ignored_params には、指定したレポートが受け付けないパラメーターが記載され、黙って破棄されることはありません。

隣接するエンドポイントのうち、意図的に公開していないものが2つあります。audience_insights/location(購読者マップ)と visitor_sources は、Substack 自身のダッシュボードからでも 400 を返すため、呼び出し方の誤りではなく、上流側で機能していません。

🏗 ソースから実行する

サーバー自体を開発したい場合に使用してください。ビルド手順は不要です。ソースはプレーンな ESM で、そのまま実行できます。

Node.js
git clone https://github.com/marcomoauro/substack-mcp.git
cd substack-mcp
npm ci

次に、MCP 設定に追加します。

{
  "mcpServers": {
    "substack-api": {
      "command": "node",
      "args": ["<FULL_PATH_TO_PROJECT>/src/index.js"],
      "env": {
        "SUBSTACK_PUBLICATION_URL": "<YOUR_PUBLICATION_URL>",
        "SUBSTACK_SESSION_TOKEN": "<YOUR_SESSION_TOKEN>",
        "SUBSTACK_USER_ID": "<YOUR_USER_ID>"
      }
    }
  }
}
Docker(ソースからビルド)
git clone https://github.com/marcomoauro/substack-mcp.git
cd substack-mcp
docker build -t substack-mcp .

次に、MCP 設定に追加します。

{
  "mcpServers": {
    "substack-api": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-e", "SUBSTACK_PUBLICATION_URL",
        "-e", "SUBSTACK_SESSION_TOKEN",
        "-e", "SUBSTACK_USER_ID",
        "substack-mcp"
      ],
      "env": {
        "SUBSTACK_PUBLICATION_URL": "<YOUR_PUBLICATION_URL>",
        "SUBSTACK_SESSION_TOKEN": "<YOUR_SESSION_TOKEN>",
        "SUBSTACK_USER_ID": "<YOUR_USER_ID>"
      }
    }
  }
}

🪵 ログ

サーバーは、実行内容を1行につき1つの JSON オブジェクトとして stderr に記録します。MCP クライアントは、そのログを独自のログファイルに収集します(macOS では、Claude Desktop が ~/Library/Logs/Claude/mcp-server-substack-api.log に記録します)。呼び出しが期待どおりに動作しない場合、LLM が実際に何を送信したかを確認する最も手早い方法です。

{"ts":"2026-08-07T10:12:03.114Z","level":"info","msg":"tool.call.start","tool":"create_draft_post","args":{"title":"My title","subtitle":"My subtitle","body":"…"}}
{"ts":"2026-08-07T10:12:03.402Z","level":"info","msg":"substack.response","status":200,"duration_ms":287}
{"ts":"2026-08-07T10:12:03.403Z","level":"info","msg":"create_draft_post.created","draft_id":167712345}

認証情報と合わせてオプションの SUBSTACK_MCP_LOG_LEVEL 環境変数を設定すると、ログに記録する情報量を変更できます。

値内容
silent何も記録しません
error失敗した呼び出しのみ
warn上記に加えて、クライアントがエラーとして受け取ったすべての応答。実行前に不正な引数を理由に拒否された呼び出しも含みます
info (デフォルト)上記に加えて、すべてのツール呼び出し、リクエスト、レスポンス
debug上記に加えて、完全なペイロードとすべての JSON-RPC メッセージ

どのレベルでも、セッショントークンがログに記録されることはありません。

💻 人気の MCP クライアント

MCP クライアントとその機能サポートの完全な一覧は、公式 MCP クライアントページをご覧ください。

クライアント説明
Claude DesktopClaude AI のデスクトップアプリケーション
CursorAI を第一に考えたコードエディター
Cline for VS CodeAI アシスタンスのための VS Code 拡張機能
GitHub Copilot MCPGitHub Copilot の MCP 統合のための VS Code 拡張機能
WindsurfAI を活用したコードエディターおよび開発環境

🆘 サポート

  • この MCP サーバーに関する問題: GitHub でイシューを作成してください

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。