medium-ops について
medium-opsは、Mediumの記事、返信、拍手、フィード、プロフィールを読み書きするPython製コマンドラインツール兼MCPサーバーです。IDEホストのLLMで返信案を作成でき、別途AI APIキーは不要です。書き込みは重複防止と監査ログを備え、確認を経て実行できます。
主な機能
- 01記事、フィード、プロフィールの閲覧
- 02記事と返信の投稿
- 03返信案の作成と承認
- 04書き込みのドライランと重複防止
- 05操作の監査と状態確認
- 06MCPホスト設定のインストール
活用シーン
- 01未返信のコメントを探す
- 02投稿前に返信案を作成・確認
- 03Mediumの記事を公開
- 04記事の統計と拍手数を確認
MCP 対応
stdio MCPサーバーは22個のツールでMediumの操作を提供し、対応ホストの設定をインストールできます。返信案はホストのLLMで作成し、投稿前に承認用としてステージできます。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
uvx medium-opsMEDIUM_INTEGRATION_TOKENシークレットLegacy Medium Integration Token (api.medium.com/v1/*). Optional. Generate at medium.com/me/settings → 'Integration tokens' (note: Medium stopped issuing new tokens in 2023).MEDIUM_SIDシークレットMedium 'sid' cookie value from medium.com (Application → Cookies). Used for authenticated reads + dashboard GraphQL.MEDIUM_UIDMedium 'uid' cookie value. Required alongside sid for some authenticated endpoints.MEDIUM_XSRFシークレットMedium 'xsrf' cookie value. Required for any dashboard write (post_response, publish_post, delete_post, draft updates).MEDIUM_CF_CLEARANCEシークレットCloudflare 'cf_clearance' cookie. Only required when Cloudflare challenges your IP for dashboard write calls.MEDIUM_USERNAMEYour Medium handle (without the @). Required for the public RSS read path so the client knows whose feed to fetch.MEDIUM_OPS_MCP_PATHOverride path to the mcp.json file the auth layer reads. Defaults to ~/.cursor/mcp.json.MEDIUM_OPS_LLM_CMDOptional: override the host CLI used by the unattended daemon path (default: auto-detect claude / cursor-agent / codex on PATH). Use {prompt} placeholder if your CLI takes the prompt as an arg.ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
medium-ops
Cursor MCP、Claude MCP、OpenCode MCP、およびあらゆる stdio MCP ホスト向けの、スタンドアロンの Medium CLI と 22 個のツールを備えた MCP サーバーです。返信の下書きは IDE が作成します。AI API キーは不要です。
ストーリー、返信、拍手、フィード、プロフィール、統計、返信エンジン、MCP サーバー。 Python のインストールは 1 回、バイナリは 1 つ、MIT ライセンスです。 Substack-ops の姉妹プロジェクトです。
TL;DR — MCP ネイティブ(API キー不要、コマンド 1 つ)
uvx medium-ops mcp install cursor # or claude-desktop, claude-code, opencode, print
# Restart your host. Then in chat:
# "list unanswered responses on post abc123def456"
# "draft a warm reply to response r1"
# "post that draft"
下書きは、ホストの LLM(Cursor や Claude)が propose_reply/confirm_reply ツールを使って作成します。ANTHROPIC_API_KEY/OPENAI_API_KEY は不要です。
インストール先が違いますか?
このプロジェクトは PyPI の medium-ops です。関連のない「Medium MCP」検索結果に出てくる npx パッケージではなく、uv/uvx でインストールしてください。正式な掲載先:Glama — 06ketan/medium-ops。
対応ホスト(MCP)
以下の表は、検索キーワードによる発見に役立つものです。アップグレードする前に、各ホストの最新の MCP ドキュメントを確認してください。
オープンソース志向のホスト
| ホスト | ドキュメント | 一般的な接続方法 |
|---|---|---|
| OpenCode | OpenCode MCP サーバー | uvx medium-ops mcp install opencode |
| Continue | Continue | uvx medium-ops mcp install print — スニペットを Continue の MCP 設定に貼り付けます |
| Zed | Zed | Zed のドキュメントに従って stdio MCP を設定します |
| Cline | Cline | 拡張機能/マーケットプレイスのドキュメントに従って MCP を設定します |
| Goose | Goose | Goose のドキュメントに従って MCP 拡張機能を設定します |
大規模な商用スタック
| ホスト | ドキュメント | 一般的な接続方法 |
|---|---|---|
| Cursor | Cursor MCP | uvx medium-ops mcp install cursor |
| Claude(Desktop/Code) | Claude Desktop, Claude Code | mcp install claude-desktop/claude-code |
| GitHub Copilot | Copilot | 対応している場合は VS Code で MCP を使用します。print とホストのドキュメントを参照してください |
| ChatGPT | OpenAI | コネクター経由の接続。MCP 非対応のツールでは REST が使われることがよくあります |
| Google Gemini | Gemini | Google のドキュメントに従って Gemini CLI/IDE 機能を設定します |
OpenCode(コピーして貼り付け)
自動インストール:
uvx medium-ops mcp install opencode
手動(~/.config/opencode/opencode.json):
{
"mcp": {
"medium-ops": {
"type": "local",
"command": ["uvx", "medium-ops", "mcp", "serve"],
"enabled": true
}
}
}
任意のバージョン固定:["uvx", "medium-ops==0.1.2", "mcp", "serve"](現在の PyPI リリースに置き換えてください)。
ハイブリッド構成の理由
Medium には利用可能なインターフェースが3つあり、すべてを使用しています。
- 公開 RSS(読み取り、認証不要)。
medium.com/feed/@{user}は、body_html、pubDate、tags、ヒーロー画像、dc:creatorを含む、著者の最新ストーリー約10件を返します。認証情報は不要で、GraphQL より高速かつ安定しています。デフォルトではlist_posts/get_post/get_post_contentに使用されます。Portfolio_V2 のブログページに着想を得ています。 - ダッシュボード GraphQL(認証済みの読み取り)。
sidCookie を使用してmedium.com/_/graphql+medium.com/_/api/*にアクセスします。10件を超える投稿を要求した場合、投稿が RSS の対象期間に含まれない場合、または RSS では取得できない情報(返信、拍手、フィード、統計、検索)を取得する場合のフォールバックとして使用されます。 - 公式 REST(書き込み)。 Integration Token を使用して
api.medium.com/v1/*にアクセスします。createPost、createPostInPublication、getUser、getPublicationsをサポートしています。それだけです。
posts list、posts show、posts content で --source rss|graphql|auto を指定すると、使用する通信方式を強制できます。ダッシュボードと GraphQL のエンドポイントはドキュメント化されておらず、Medium はいつでも変更する可能性があります。既知の制限を参照してください。
セットアップ(開発/ソースから)
git clone https://github.com/06ketan/medium-ops && cd medium-ops
uv sync
uv sync --extra mcp # mcp SDK for the MCP server (recommended)
uv sync --extra tui # textual for the TUI
認証情報は ~/.cursor/mcp.json の mcpServers.medium-ops.env(または
medium-api/medium)から読み込まれます。環境変数または .env で上書きできます。
uv run medium-ops auth verify
uv run medium-ops quickstart
コマンド一覧
すべての書き込み操作はデフォルトで --dry-run になります。--no-dry-run で切り替えます。すべての書き込み操作は .cache/audit.jsonl に記録され、.cache/actions.db と照合して重複がチェックされます。
認証(3)
| コマンド | 内容 |
|---|---|
auth verify | Integration Token(/me)と sid Cookie(GraphQL Viewer)の両方を確認します。 |
auth test | 同じ確認を行い、失敗した場合はゼロ以外の終了コードを返します(CI に適しています)。 |
auth setup | 対話形式でトークン/sid/uid/ユーザー名を .env に貼り付けます。 |
読み取り — ストーリー(5)
| コマンド | 内容 |
|---|---|
posts list [--user] [--limit] | ユーザーの最新ストーリーを表示します(デフォルトは自分)。 |
posts show <id_or_url> | ストーリーのメタデータ(タイトル、拍手数、返信数)を表示します。 |
posts content <id> [--md] | 本文の HTML を表示します(--md を指定すると Markdown)。 |
posts search <query> [--limit] | Medium 側で全文検索します。 |
| `posts publish -t "..." -f body.md [--pub] [--status draft | public |
読み取り+書き込み — 返信(3)
| コマンド | 内容 |
|---|---|
responses list <post_id> [--limit] | トップレベルの返信一覧を表示します。 |
responses tree <post_id> [--out file.json] | 返信とその応答をすべて含むツリーを JSON 形式で出力します。 |
responses add <post_id> "body" [--parent <r_id>] [--no-dry-run] | 返信または応答を投稿します。 |
読み取り+書き込み — 拍手(2)
| コマンド | 内容 |
|---|---|
claps count <post_id> | 拍手の合計数を表示します。 |
claps give <post_id> [--claps N] [--no-dry-run] | 1~50回拍手します。重複は防止されます。 |
読み取り — 探索+プロフィール(5)
| コマンド | 内容 |
|---|---|
feed list [--tab home|following|tag-{slug}] [--limit] | リーダーフィードを表示します。 |
profile me | 自分のプロフィール全体を表示します(GraphQL)。 |
profile get <username> | 任意のユーザーの公開プロフィールを表示します。 |
profile stats [--days N] | 投稿ごとの閲覧数/読了数/ファン数を表示します(ダッシュボードのスクレイピング)。 |
profile publications | 投稿可能なパブリケーションを表示します(Integration Token)。 |
返信エンジン(3)
| コマンド | 内容 |
|---|---|
reply template <post_id> --template thanks | ルールベースの返信を作成します(LLM 不使用)。 |
reply bulk <post_id> --out drafts.json | すべての返信をファイルに下書きします。 |
reply bulk-send drafts.json [--no-dry-run] | action=approved の行のみ投稿します。重複はチェックされます。 |
運用+安全性(2)
| コマンド | 内容 |
|---|---|
audit search [--kind] [--target] [--status] [--since 7d] | JSONL 監査ログを検索します。 |
audit dedup-status | 重複排除用 SQLite DB の件数を表示します。 |
MCP サーバー(3)
| コマンド | 説明 |
|---|---|
mcp install <cursor|claude-desktop|claude-code|opencode|print> [--dry-run] | ホストの設定に自動でマージします。 |
mcp serve | stdio MCP サーバー(22 個のツール)。 |
mcp list-tools | ツールレジストリを表示します。 |
その他(1)
| コマンド | 説明 |
|---|---|
quickstart | クイックスタートのチェックリストを表示します。 |
返信モード
| モード | 説明 | 安全性 |
|---|---|---|
template | src/medium_ops/templates/*.yaml にある YAML キーワードルール | デフォルトでドライラン |
bulk | LLM がすべての返信を drafts.json に下書きします。編集して、action: "approved" を設定してください | オフラインでレビュー。送信時に重複をチェック |
bulk-send | action: "approved" の項目のみを投稿します | デフォルトでドライラン。重複排除 DB が返信の重複を防止 |
MCP propose_reply → confirm_reply | ホストの LLM が下書きを作成し、項目ごとに承認します。トークンで制御されます | トークンの有効期間は 5 分。冪等。API キー不要 |
MCP サーバー
medium-ops mcp install opencode # ~/.config/opencode/opencode.json
medium-ops mcp install cursor # auto-add to ~/.cursor/mcp.json
medium-ops mcp install claude-desktop # auto-add to claude_desktop_config.json
medium-ops mcp install claude-code # uses `claude mcp add`
medium-ops mcp install print # print the snippet only
medium-ops mcp serve # stdio server
medium-ops mcp list-tools # 22 tools
手動設定のスニペット:
{
"mcpServers": {
"medium-ops": {
"command": "medium-ops",
"args": ["mcp", "serve"]
}
}
}
mcp SDK がインストールされていない場合、サーバーは最小限の
stdin/stdout JSON ラインディスパッチャーにフォールバックします:
echo '{"tool":"list_posts","args":{"limit":3}}' | medium-ops mcp serve
MCP ネイティブの下書きループ(API キー不要)
無人モードを安全にする、安全性と下書き作成のスタック:
| ツール | 説明 |
|---|---|
get_unanswered_responses | 作業リスト — まだ返信していないレスポンスです。 |
propose_reply | ドライランのみ。token とペイロードのプレビューを返します。 |
confirm_reply | トークンを使って準備済みの返信を投稿します。重複排除 DB によって冪等性が保たれます。トークンの有効期間は 5 分です。 |
bulk_draft_replies / send_approved_drafts | ファイルベースのオフラインレビューループです。 |
audit_search / dedup_status | 監査ログと重複排除の件数を読み取ります。 |
LLM 戦略
どちらも無料の 2 つのレイヤーがあります:
- MCP ネイティブ(デフォルト)。 ホストの LLM が
propose_reply/confirm_replyを通じて下書きを作成します。環境変数も API キーも不要です。対話形式の返信に使用してください。 - サブプロセス CLI(デーモン経路)。 人が介在しない
reply bulk用です。PATH 上のclaude(Claude Code)、cursor-agent、またはcodexを自動検出します。MEDIUM_OPS_LLM_CMDで上書きできます。
有料 API キーを使う経路はありません。
認証の設定
Medium には、異なる機能に対応する 2 つの認証レイヤーがあります:
- Integration Token —
Authorization: Bearer <token>。api.medium.com/v1/*に対して使用します。利用できる機能:publish_post、list_own_publications。トークンは https://medium.com/me/settings → 「Integration tokens」で生成できます。 注:Medium は 2023 年に新規トークンの発行を停止しました。 トークンを生成したことがない場合、書き込み経路は 401 エラーになり、書き込みには sid Cookie を使うレスポンス経路を使用する必要があります。 - sid Cookie —
medium.comから取得します(Application → Cookies →sid)。medium.com/_/graphqlおよびmedium.com/_/api/*に対して使用します。利用できる機能:すべての読み取り(ストーリー、レスポンス、拍手、フィード、統計、プロフィール)に加え、clap_postとpost_response(不安定 — ドキュメント化されていません)。
medium-ops auth verify
medium-ops auth test
medium-ops auth setup
medium-ops auth har ./medium.har # ingest a Chrome devtools HAR export
HAR から認証情報を更新する
Cookie がローテーションされた場合や、Medium が GraphQL スキーマを変更した場合に、最も手早く解決する方法は次のとおりです:
- Chrome で
medium.comを開き、開発者ツールの[Network]パネルを表示します。 - 失敗する操作(下書きの公開、レスポンスの投稿など)を再現します。
- 任意のリクエストを右クリックし、[Save all as HAR with content]を選択します。
medium-ops auth har ./medium.har
これにより:
- 新しい
sid、uid、xsrf、cf_clearanceCookie が.envにマージされます (それ以外はすべて保持されます)。 .cache/har-snapshot.jsonに、秘匿化されたスナップショットが書き込まれます。観測されたすべての Medium GraphQL オペレーションと、そのリクエスト変数/レスポンスデータの キー構造が記録されます。これは、client.pyにハードコードされたクエリとの差分を取り、ユーザーが問題に遭遇する前にスキーマの変更を検出するのに役立ちます。
まだ準備できていませんか? docs/AUTH-SETUP.md にある、ブラウザーの開発者ツールを使った 5 分間の手順をご覧ください。Medium Integration Token API は 2023 年から非推奨になっています。現在はほとんどのユーザーが
MEDIUM_SIDを使った Cookie ベースの認証を利用しています。
環境変数(または ~/.cursor/mcp.json → mcpServers.medium-ops.env):
MEDIUM_INTEGRATION_TOKEN=2fb00... # optional, for writes
MEDIUM_SID=1:... # optional, for reads
MEDIUM_UID=... # optional
MEDIUM_USERNAME=yourhandle # optional but recommended
アーキテクチャ
mcp.json | env → auth.py
│
MediumConfig (token? sid? uid? username?)
│
MediumClient (httpx)
┌───────┼──────────┐
▼ ▼ ▼
api.medium.com medium.com/ medium.com/_/
/v1/* (REST) _/graphql api/* (dashboard)
│ │ │
Bearer token sid cookie sid cookie
│
┌──────┬──────┬────────────┬──────┬────────┬──────────┐
▼ ▼ ▼ ▼ ▼ ▼ ▼
posts responses claps profile stats feed reply_engine
│
┌───────────────────┼───────────────┐
▼ ▼ ▼
template ai_bulk MCP propose/confirm
└───────────────────┬───────────────┘
▼
base.post_response
│
┌─────────┼─────────┐
▼ ▼ ▼
dedup audit dry_run
(SQLite) (jsonl)
mcp/server.py ──── 22 tools ─── all share MediumClient
使用するエンドポイント
| 操作 | メソッド+URL |
|---|---|
| 認証:インテグレーショントークン | GET https://api.medium.com/v1/me |
| 認証:sid Cookie | POST https://medium.com/_/graphql (Viewer) |
| ユーザープロフィール | POST /_/graphql (UserProfileQuery) |
| ストーリー一覧 | POST /_/graphql (UserStreamOverview) |
| ストーリーのメタデータ | POST /_/graphql (PostViewer) |
| ストーリー本文 | POST /_/graphql (PostContent) |
| ストーリー検索 | POST /_/graphql (SearchPosts) |
| レスポンス | POST /_/graphql (PostResponses) |
| フィード | POST /_/graphql (HomeFeed / FollowingFeed / TagFeed) |
| ストーリーを公開 | POST https://api.medium.com/v1/users/{id}/posts |
| パブリケーションに公開 | POST https://api.medium.com/v1/publications/{pub_id}/posts |
| 自分のパブリケーション | GET https://api.medium.com/v1/users/{id}/publications |
| 拍手 | POST https://medium.com/_/api/posts/{id}/clap(非公開仕様) |
| 投稿へのレスポンス | POST https://medium.com/_/api/posts(非公開仕様) |
| 統計情報 | GET https://medium.com/@{username}/stats?count=...(非公開仕様) |
関連する MCP
- slideshot — HTML → スライド(PNG / PDF / PPTX);npm
slideshot-mcp。 - substack-ops — Substack の投稿、ノート、コメント+MCP(PyPI
substack-ops)。
テスト
uv run pytest -q
カバレッジ:認証の読み込み、クライアントのトランスポート+XSSI の除去、重複排除 DB、監査ログ検索、MCP ツールレジストリ+ディスパッチャー、MCP インストール時のホスト設定のマージ、提案/確認フロー+トークンの有効期限、返信エンジンのテンプレートマッチング+重複排除・監査フロー、サブプロセスでの LLM 検出。
既知の制限事項
- Medium は 2023 年にインテグレーショントークンの新規発行を停止しました。 取得したことがない場合、
publish_post/list_own_publicationsは 401 エラーになります。読み取り+レスポンス+拍手の処理は、引き続き sid 経由で動作します。RSS の読み取りには認証情報は必要ありません。 - RSS で取得できる投稿は約 10 件に制限されており、拍手/レスポンス数や統計情報もありません。より多く取得するには、
--source graphqlを指定するか(sid が必要)、--limit > 10を設定してください。クライアントは自動的に GraphQL にフォールバックします。 - GraphQL のオペレーション名とスキーマは予告なく変更されます。
client.pyのクエリは現在のダッシュボードの実装に合わせています。数か月ごとに動作しなくなることがあるため、このパッケージのバージョンを固定してください。 post_responseは GraphQL のsavePostResponse(deltas: [Delta!]!, inResponseToPostId: ID!)を使用します。Delta の形式は{type: 1, index: N, paragraph: {type: 1, text, markups: []}}です(type=1 は挿入、paragraph.type=1 は P を意味します)。エラーメッセージからリバースエンジニアリングしました。update_draft_contentはダッシュボードのPOST /p/{id}/deltasを{baseRev, rev, deltas}とともに使用します。新規ドラフトでは、baseRev=-1, rev=0です。 以降の編集では両方の値を増やしてください。clap_postは引き続き非公開仕様の/_/api/posts/{id}/clapの形式を使用します。 新しい GraphQL の仕組みに対してはまだ再検証していません。まずドライランを実行してください。- メンバー限定のストーリーは、
sidが有料会員のものでない場合、ペイウォールのプレビューを返します。 - 「リストアック」に相当する機能はありません。 Medium には再共有機能がありません。最も近い方法は拍手+レスポンスです。その場合は
clap_postとpost_responseを組み合わせて使用してください。 - ノート/短文投稿には対応していません。 Medium は 2018 年に短文投稿機能を終了しました。
- Chrome Cookie の自動取得(substack-ops の
auth_chromeフロー)はまだ実装されていません。現時点では.envにsidを貼り付けてください。 - TUI はまだ実装されていません。extras の固定は今後の開発に向けたものです。
ライセンス
MIT。詳しくは LICENSE をご覧ください。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。