Skip to main content
Glama
stepanic

linkedin-poster

by stepanic

linkedin-poster

Claude Code、シェル、cron ジョブから自分の LinkedIn フィードに投稿します。依存関係のない Cloudflare Worker が1つ、3つのサーフェスがあります。エージェントが直接公開できるMCP サーバー、スクリプト用のHTTP API、そして機械が書いた散文の兆候を含むテキストの公開を拒否するボイスリンターです。

良い投稿は自動化する価値があり、悪い emダッシュは公開する価値がないからです。この README にも emダッシュはありません。それがポイントです。

Claude Code ──MCP──┐
                   ├──> Worker ──> LinkedIn Posts API ──> your feed
curl / CI ──HTTP───┘      │
                          └──> KV (one 60-day token) + daily cron reminder

これは何ではないか

これは1人のメンバー、つまりあなたとして投稿します。スケジュール SaaS でも、マルチテナントツールでもなく、他の誰のアカウントにも触れません。必要なものはすべてセルフサービスで、LinkedIn パートナープログラムは関与しません。

Related MCP server: mcp-server-linkedin

すべてを形作る制約

LinkedIn のアクセストークンは60日間有効で、プログラムによるリフレッシュトークンは承認された Marketing Developer Platform パートナーのみに制限されています。セルフサービスアプリは決して取得できません。これを回避する方法はなく、設定して忘れる LinkedIn 自動化を約束するガイドはすべて間違っています。

実際にできること: 現在のトークンがまだ有効で LinkedIn にログインしている間、認証エンドポイントに再度アクセスすると同意画面がスキップされ、リダイレクトされるだけです。つまり、トークンが失効する前に実行すれば、7週間ごとに1クリックで更新できます。期間を逃すと同意画面が戻ってきて、1クリックではなく2クリックになります。

そのため、このサービスには cron トリガーが付属しています。残り日数をカウントダウンし、サイレント更新がまだ機能する14日、7日、3日、1日前に通知します。

セットアップ

1. LinkedIn 側(手動、約10分)

  1. LinkedIn ページが必要です。開発者ポータルでは、アプリをページに添付し、ページ管理者に確認してもらう必要があります。

  2. linkedin.com/developers でアプリを作成します。Client ID と Client Secret をメモします。

  3. Products タブで、両方をリクエストします(オープンパーミッションで即時承認されます):

    • Sign In with LinkedIn using OpenID Connect → openid, profile

    • Share on LinkedIn → w_member_social

  4. Auth タブ → リダイレクト URL を追加します。HTTPS で絶対 URL であり、REDIRECT_URI と文字単位で一致する必要があります: https://your-worker.example.com/auth/callback

2. Worker 側

git clone https://github.com/stepanic/linkedin-poster.git
cd linkedin-poster
npm install

# KV to hold the token
npx wrangler kv namespace create TOKENS
npx wrangler kv namespace create TOKENS --preview
# paste both ids into wrangler.jsonc

# edit wrangler.jsonc: name, routes, REDIRECT_URI, account_id

npx wrangler secret put LINKEDIN_CLIENT_ID
npx wrangler secret put LINKEDIN_CLIENT_SECRET
npx wrangler secret put API_TOKEN     # openssl rand -hex 32, guards /post and /mcp
npx wrangler secret put SETUP_KEY     # openssl rand -hex 32, guards /auth/start
npx wrangler secret put NOTIFY_WEBHOOK  # optional, any URL taking a JSON POST

npm run deploy

3. アカウントを接続する

ブラウザで https://your-worker.example.com/auth/start?key=<SETUP_KEY> を開き、一度承認すれば完了です。ページに有効期限が表示されます。

SETUP_KEY は飾りではありません。これがないと、URL を見つけた誰でも自分の LinkedIn アカウントをあなたの Worker にバインドし、保存されたトークンを上書きできてしまいます。

Claude Code から使う

claude mcp add --transport http linkedin https://your-worker.example.com/mcp \
  --header "Authorization: Bearer $API_TOKEN"

3つのツール:

Tool

機能

linkedin_post

リンターを実行してから公開します。force: true でない限り、エラーがあると拒否します。

linkedin_check

公開せずにリンターを実行します。下書き中に使用します。

linkedin_status

誰として投稿するか、トークンの残り日数。

セッションでは: 「この下書きをリンターでチェックしてから投稿して」

シェルから使う

# dry run: lint only, publish nothing
curl -s -X POST https://your-worker.example.com/post \
  -H "Authorization: Bearer $API_TOKEN" -H 'Content-Type: application/json' \
  -d '{"text":"…","dry_run":true}'

# publish
curl -s -X POST https://your-worker.example.com/post \
  -H "Authorization: Bearer $API_TOKEN" -H 'Content-Type: application/json' \
  -d '{"text":"…","visibility":"PUBLIC"}'

# token state
curl -s https://your-worker.example.com/status -H "Authorization: Bearer $API_TOKEN"

/post は、投稿 URN と公開 URL とともに 201 を返し、リンターがブロックした場合は 422、トークンがないか失効している場合は 503 を返します。

リンター

エラーは公開をブロックします。警告はブロックしません。

ルール

重大度

理由

no-em-dash, no-en-dash

エラー

生成された散文の最も明確な兆候。コンマやピリオドで常に代用できます。

max-length

エラー

LinkedIn は3000文字を超えるコメントを拒否します。

empty

エラー

明白ですが、ファイルからパイプするときによく発生します。

no-markdown-bold, no-markdown-link, no-markdown-heading, markdown-bullets

警告

LinkedIn はどれもレンダリングしません。アスタリスクやハッシュマークはそのまま表示されます。

ハイフンは決してフラグされません。 read-only tier、two-day sprint、per-client limits は正しく、そのまま残す必要があります。エラーとなるのは U+2014 と U+2013 のみです。

src/lint.ts を編集して、これを自分用にカスタマイズするのが意図された方法です。ルールは一人の書き方の慣習をエンコードしたものであり、普遍的な標準ではありません。

驚くであろうこと

  • Posts API は URL をスクレイピングしません。 テキスト内のリンクはそのままのリンクとして残ります。実際のプレビューカードには、Images API を通じてアップロードしたサムネイルと content.article ブロックが必要です。ここではまだ実装されていません。

  • LinkedIn-Version は必須で、バージョンは廃止されます。 202508 は2026年8月17日に廃止されました。wrangler.jsonc の値はメンテナンス項目として扱ってください。

  • /v2/ugcPosts は非推奨です。 これは、それに代わる /rest/posts を使用します。

  • 要求されたスコープを変更すると、既存のすべてのトークンが無効になります。 再認証が必要になります。

  • 1日あたりメンバーごとに150リクエスト。 アプリごとではなく、メンバーごとです。

レイアウト

ファイル

内容

src/index.ts

ルート、OAuth フロー、cron リマインダー

src/linkedin.ts

OAuth と Posts API 呼び出し、すべてのエンドポイントを1か所に

src/mcp.ts

MCP サーバー、手作りの JSON-RPC、ステートレス

src/lint.ts

ボイスリンター

src/tokens.ts

KV トークンレコード、有効期限の計算、タイミングセーフなシークレット比較

ローカル開発

cp .dev.vars.example .dev.vars   # fill in, never commit
npm run types                    # regenerate Env after editing wrangler.jsonc
npm run typecheck
npm run dev

wrangler dev --local はローカル KV を提供するため、実際の LinkedIn 呼び出し以外のフロー全体を資格情報なしで実行できます。

ライセンス

MIT。src/lint.ts のルールは私のものですが、メカニズムは自由に使えます。

Related MCP Connectors

Related MCP Servers