linkedin-mcp
あなた自身のLinkedInアカウントデータを、クリックして進むだけのページではなく、構造化されたツールの結果として表示するMCPサーバー。
17のツールのうち14は読み取り専用で、何も変更しません。書き込みを行うのは3つだけです。
2026-08-23までは、この段落には 「読み取りのみ。それだけです。このリポジトリに書き込み経路は存在しない。無効化されているのでもなく、スタブでもなく、フラグの背後に隠されているのでもありません」 と書かれていました。それは本当であり、主張ではなく実行として強制されていました。しかし、それは linkedin_save_job がリリースされた日に、真実ではなくなりました。心地よい文を残したままのREADMEは、読者が最初に信じるものになり、また最初に読者を誤らせるものになります。
いま真実なのは、次のとおりです。
このパッケージには、LinkedIn上で何かを変更できる呼び出しがちょうどひとつだけあります。それは
writes.perform内の単一のアンカー付きクリックです。ソーススキャナは今もそれを報告しています(見つけるのをやめるようには作られていません)。また、その呼び出しはパス・関数・種別の形で1行の許可リストにのっており、その許可リストが広がるとテストが失敗します。書き込みは、オンにしない限りオフです。 プロセスごとに
LINKEDIN_ENABLE_WRITES=1。クローンしたばかりの状態では、LinkedInにいっさい書き込めません。全書き込みは2つの呼び出しから成ります。 最初の呼び出しは何も実行せず、読むためのブロックを渡します。2番目の呼び出しは、そのブロックから一度きりのトークンを使用します。トークンは1つの対象に対する1つの操作にバインドされ、120秒で失効するため、予約された書き込みや無人での書き込みは、単に推奨されないのではなく、構造的に不可能です。
linkedin_unsave_jobは構築され、ゲートされ、動作を拒否します。拒否するものを参照してください。応募を送信することはありません。それは肩上をすくめているからではありません。
linkedin_job_detailはapply_pathを報告します。求人がLinkedIn上で応募を受けているのか、それとも外部の応募者追跡システムへ送られるのかを伝え、そのシステムの名前も書いています。識別する半分は読み取りとして公開されています。送信する半分は、考え抜いた理由で拒否されています。応募:公開される半分と公開されない半分を参照してください。
これより先にまずお読みください
LinkedInの利用規約は、サイトへの自動アクセスを制限しています。 サーバーの作り方に関係なく、それは事実であり、以下のどこを見てもそれは変わりません。
この設計がしているのは、その事実を偶然しらずに済ませることではありません。外部に影響を与える部分を小さく抑えています。
この選択 | それによってリスクが減る理由 |
人間が直接操作するのみ | すべての呼び出しは、その場であなたが行ったものです。タイマーで動くものは何もなく、あなたが寝ている間に動くものもありません。 |
一度に1つのアクション | ツール呼び出し1回につきページの読み込みは1回。スクロールのループも、自動ページめくりも、並列書き出しのファンアウトもありません。 |
自分のセッション、自分のマシン、自分のIPアドレス | クッキーが第三者に輸出されることはありません。プロキシも、データセンターのIPも、ヘッドレスファームもありません。 |
普通のブラウザとフラグ1つ | ステルスプラグインなし。ブラウザの訪者が差し出す、 ユーザーエージェントやユーザーolatformを偽らない(ユーザーエージェントやプラットフォームの偽装も、fingerprint を偽装するチューニングも、プロキシも、人間を微妙にエミュレートするようなタイミング戦略もありません。Chromium のフラグをひとつだけ渡します -- |
自分のデータのみ | あなたのプロフィールビュー、あなたの応募、保存した求人、あなたのプロフィール、あなたの通知。ほかのメンバーを列挙したり収集したりすることはありません。 |
読み取りのみ(指定された書き込み3つは除く) | 応募、送信、投稿、エンドース、招待、編集は一切行われません。保存、保存解除、フォロー解除は例外です。それらはデオルトではオフで、1回に1つのみ、ライブの読み取りから作られたブロックをあなたが確認し、一度きりで、2分間で失効するトークンを使って行われます。この行は2026-08-23までは「読み取り専用」と書かれていましたが、静かに範囲を広げたのではなく、このとおりにはっきり修正されたものです。 |
これでリスクは減る。無くするのではありません。 自動アクセスはやはりレート制限、チャレンジ、アカウントへの措置などにつながる恐れがあり、そののはあなたが受けるのみです。サーバーを登録する前に、意図的にそのか決めましょう。
これは検出回避ツールではなく、その保証ではなく証拠をnこで示します
上記のChromiumのフラグは、repos本身的回避プロジェクトのように見せてしまうものです。ここで測定した事実を率直に述べる価値があります。なぜなら、この主張は確認可能であり、読者に「The tone source」で理解させるべきではないからです。
2026-08-24に、トラッキングされている全105ファイルを監査しました:
フィンガープリンティングの細工:ゼロ flo。ユーザーエージェント、platform、ロケール、タイムゾーン、地理的ロケール、ビューポートのなりすまし、デバイススケール、WebGL、Canvasのパッチングはありません。
page.routeの behind (中継) もありませんし、注入された init脚本も、ヘッダーの追加も、プロキシもありません。それぞれをパッケージあ中を名前で検索して、呼び出し箇所はゼロを確認しました。grep読者が誤らないように注意個が削除されています:add_init_scriptはreadonly.pyに2回現れますが、どちらの場合もスキャナ自身のこの呼び出しを検出するためのパターンです。スキャナが禁止しているものを名前でリストアップそのため、このパッケージの中には今回だけ、そのスキャナのみが実行できる形で符号が入れられます — それが、partition_mutation_hitsにより独立に確認でき、許可されたミューテーションの呼び出しはちょうど1、許可されていないのはほかにはありませんとなります。ステルス実装なし 依存4つのいずれも自動検出回避のライブラリはなく、パッケージ全体を
readonly.scan_source_for_evasionでスキャンしてもヒットはゼロ。そのヒット以外で唯一現れるのは、テストコード内に意図的に用意した素材です。タイミングは固定で、人間くさくしない すべてのdelayが固定値です。
import randomの出現回数は 0回。ページ間の3秒のgapはMIN_INTERVAL - elapsedをそのままとかスリープ、機械的に一定です。ランダマイズは理したんでいるかジャンタルは人間を模すためのもの。フラットな間隔こそがレートリミットの本来の姿です。フラグ自身ちは、良い意図ではなくゲートで縛られています。
readonly.assert_launch_flags_permitted起動のたびに実行され、第3のフラグが出現すると、tests/test_launch_boundary.pyがビルドを失敗させます。
このフラグは、navigator.webdriver をBlinkが設定するのを止めます。LinkedInはサインイン時にこのをチェックし、この値があるとなっています、アカウントの野生き主本人であっても自動ブラウザが使えないからです。それ以上の意味があります。自動ブラウザを動かすことと、検出されずにねぐずぐすることと異なります。ここに存在しているのは前者だけです。
ライセンスはそれに基づきいて、意図的に與許的にはしていません
このリポジトリはプロプライエタリです。全著作権は、参照のためのみに付与されており、またの利用・コピー・変更・配布の権利はありません。
これはうっかり欠いたのではなく、一時のFillerでもありません。このサーバーは、自動化を禁止する利用規約に入方が下で、認証済みLinkedInセssionを動かすものです。パーミップシブなライセンスあれば、やり方を教えたリポジトリに私の名前がたったまま、そのサーバーを自分の – あるいは他の誰のこと – アカウント منهاに向ける人が現れするでしょう。
これはポートフォリのためのものです。本番でデプロイしてむ用ではなく、読まれるためのものです。 設計、境界、ゲート、監査の証跡を読んでください。
Related MCP server: LinkedIn MCP Server
何ができますか
ツール | 読み取り内容 |
| あなたのプロフィールを閲覧した人。アカウントがPremium Careerを利用している場合、365日前まで遡ります。これは求職活動の中で最も意図が明確なシグナルです。 |
| 応募した求人と、LinkedInが表示するステータス。 |
| ブックマークした求人。 |
| キーワード、勤務地、リモート、掲載日、経験レベルによる求人検索。 |
| 1件の掲載情報を全文表示します。給与範囲、LinkedInの応募者数、勤務タイプと雇用形態、募集ステータス、そして説明文です。これらはいずれも検索結果や保存済み求人カードにはありません。また、 |
| フォローしている会社ページと、そのそれぞれの数値ID。 |
| 自分自身のプロフィール——見出し、概要、スキル、そしてレンダリングされたセクション。職務経験・学歴・スキルは、ページがスクロールされるまでLinkedInが遅延させるため、ゼロではなくUNKNOWNとして読み取られます。 |
| あなたの通知リスト。 |
| ライブセッションが存在するかどうか。認証済みリクエストで測定します。 |
| あなた自身がサインインするためのウィンドウを開きます。 |
| セッションが生きているかどうかと、いつ有効期限が切れるのかを、ブラウザ自身のクッキージャーから読み取ります。資格情報、それを支えるcsrfクッキー、持続性、そして、ここでサイレント再認証が存在しない理由を報告します。 |
| このマシンのクッキージャーを消してローカルのサインインを終了します。ここで唯一の破壊的ツールです。 |
| 復旧の診断——このサーバーが接続できるChromeが存在するかどうか。LinkedInには何も触れません。 |
| 境界、レート設定、起動フラグを、ソースを読むことなく表示します。 |
書き込みを行う3つのツール
ツール | 動作 |
| 1件の掲載をブックマークします。 |
| 同じ構造、同じゲートを持ちますが、拒否します。後述を参照してください。 |
| 1つの会社ページのフォローを停止します。同じ構造と同じ5つのゲート。指定は数値の会社IDで行い、名前に決してよりません——名前は衝突し、変化し、あてにできるものではありません。そしてクリックはそのIDが載っている行に固定されるため、あなたが名前で指定したものと、実際に押されるものは構造上で同じ行になります。 |
クリック後、結果は「押したボタン」ではなく別のサーフェス——保存済みリスト、それもLinkedIn自身のタブ別カウント付きで——から確認されます。performed は true、false、"unknown" のいずれかで返ります。"unknown" の場合は再試行しないでください。実際にトグルが動作した状態で再実行すると、逆の操作になってしまいます。
拒否するツール
LinkedInは保存コントロールをアクセシブルネームで識別します。このリポジトリが保持するすべてのキャプチャ——4つの掲載、両方のハイドレーション状態、異なる2日分——では、aria-label="Save the job"、つまり未保存状態でした。掲載が保存済みであるときにまとっての名前は観測されたことがなく、また読み取っても観測できません。というのも、アカウントに保存済みのものが存在せず、それを観測する対象がないからです。
そのため、linkedin_unsave_job はアンカーを持たず、このサーバーは推測もしません。"Saved" も "Unsave the job" もどちらも可能性がありますが、どちらも確認されていません。この問題は「not implemented」ではなく、その理由を明確にな表現して拒否します。「not implemented」とは言わないのは、誰かに文字列を選んで実装してしまうと誘ってしまうからです。
修正は、計測された1行です。 最初の管理下での保存がそれを作ります。perform は、コントロールが変わるラベルを読み返して報告します。その値を shape.SAVE_LABELS に書き込めば、unsave_job はアンカーを手に入れます。それはテーブルの1行であり、欠が存在しないコードパスではありません。
応募: 提供される半分と、提供されない半分
linkedin_job_detail は、掲載への応募方法を伝えます。 LinkedInは応募コントロールを、ボタンではなくリンクとして描画するため、そのリンク先を触らずに読み取れます。apply_path は次の3つのうち1つを報告します。
linkedin_apply——応募はLinkedIn上で入力・送信されます。offsite——LinkedInはあなたを、その雇用主自身の応募者管理システムに委ねます。送信先はLinkedInの外部ラッパーから文字列だけでデコードされます。リダイレクトは追わず、第三者のホストに接続することもありません。送信先のホストが分かるため、これから自分の情報を入力されるのがどの入社先のフォームかを把握できます。unknown——それは返してくれません。これは実際の回答であり、重要なり答えです。後述を参照。
それが役に立つ半分で、追加のページロードが不要で、純粋な読み取りです。
それは送信しません。 応募することがこのサーバーの管轄の下になるからないではなく、測定された理由によります。
応募(apply)フローは、一度もキャプチャされたことがない。 13件の求人キャプチャすべてを通しても、フォームはゼロ、ファイル入力はゼロ、ダイアログはゼロ、スクリーニング質問はゼロ、何かを送信するコントロールもゼロである。ここにあるものは、何が入力され、何が押されることになるのかを、何一つ見ていない。これは
unsave_jobに適用されるのと同じ基準であり、その基準を最も適用すべきアクションに適用したものである。応募はここから取り消すことができない。 あらゆる確認レベルで、あらゆる状況で。取り消しは恒久的に禁止されている。
サイトオフの半分は、もともとこのサーバーの管轄ではない。 どれほど良いキャプチャが得られたとしても、他人のフォームを、他人のドメインの上で、相手の利用規約の下で操作することは、別のソフトウェアの仕事である。
したがって apply_job は writes.py 内で完全に仕様化され、ゲートされており、ツールは登録されず、url も持たない。そのため、それに対する grant は、利用時ではなく発行段階で拒否される。
そして、このギャップには宛先(address)がある それが、恒久的ではなく「未測定のまま」であることの理由である。scripts/_probe_apply_flow.py は LinkedIn がホストしているフローをキャプチャし、既存のキャプチャがすべて欠落している管理対象 — フォーム、ファイル入力、ダイアログ、スクリーニング質問、送信を担うコントロール — を列挙する。このフローへは、クリックではなくナビゲーションで到達する(LinkedIn は apply コントロールをリンクとして描画する)。パッケージ自身のミューテーションスキャナーは、このスクリプトの中にゼロ個のミューテーション呼び出しを検出する。そして、ジョブ ID を必須引数として受け取るため、デフォルト値によってあなたの代わりに応募先が選択されることはない。また、前後で LinkedIn 自身の Applied タブの件数を読み取る。Easy Apply フローを開くとドラフトが作成される可能性があるためだ — これは誰も検証したことがない仮説であり、仮説であると明示した上で、推定ではなく測定されるだけで処理される。
このプローブはまだ実行されていない。 誰かが立ち会った状態で、apply_path が linkedin_apply と示す求人掲載を先頭に実行すること。
分類器が、一見明らかな1つのフィールドで十分そうなのに、複数のフィールドの一致を要求するのはなぜか。
それぞれの候補は測定され、各候補単独では失敗する:
data-view-name="job-apply-button" は13件のキャプチャのうち1件にしか存在せず、完全にハイドレーションされた外部ホスト求人掲載には存在しない。したがって、この属性の不在にはまったく情報がない。外部遷移用のラッパーは汎用的であり、ある1件のキャプチャにはそのようなラッパーが2つ含まれ、そのうち1つだけが apply コントロールである。アクセシブルネームは単一のフィールドとしては最も強力だが、それは LinkedIn がすでに変更してしまった部分である。「Easy Apply」という文字列はアクセシブルネームの中でゼロ件現れ、同じページの本文中には2回現れる。そのため、誰もが知っている機能名をキーにしたパーサーは何も一致しない。さらに、ハイドレーション前のペイロードは無役にすらならない — オフサイトの求人掲載が、同じジョブ ID に対して、オンサイトフローのマーカーそのものを保持した状態で計測されたことがある。LinkedIn は apply 状態機械全体を、送信先ごとのテンプレートとして配信するからである。
意図的にできないこと
メッセージング、InMail、コネクト招待。プロフィール編集。Open To Work。企業のフォロー。投稿、いいね、コメント、エンドース。通知の既読化。他のメンバーに関するデータの収集。以上は前述のとおり、応募の送信は禁止。
これらはまだ存在しない機能があるということではなく、それぞれが同種の「拒否」ではない。linkedin_server_info は各項目を POLICY、MEASURED、UNMEASURED のいずれかでラベル付けする。なぜなら、「原則として拒否する」と「調べた結果動かない」と「まだ誰も調べていない」は別物であり、それらを一つに平らなリスト化すると、未検証のギャップが設計上の決定であるかのように読まれてしまうからである。
面白いのはフォローの方である,その理由は2026-08-24に変更されたが、答えは変わっていない。以前は、unfollow が存在しないためにブロックされていた — このサーバーは、自身が後で晴れない状態を作り出せたから。今では unfollow は1つ存在する。それでもまだ実行可能ではない。取り消しは、組えないためである。求 posting は雇用主を slug で示すが、unfollow の側は数値の会社 ID によって行を識別する。このリポジトリ内のキャプチャで、どちらかのアクションが使用するサーフェス上で、ある社両方を持っているものはない。また、どのサーフェスも、ページネーション制御なしに 58 件中約20件しか描画しないため、リストの大部分は1回のページロードでは到達できない。その拒否はその2つの理由を挙げ、何が制約を解除するのかも明示している。
自身の受信トレイを読み取ることは、UNMEASURED(未判定)であり、拒否ではない。 読み取りの境界は /messaging をブロックし、そのブロックのための書かれた理由は全て 送受信 に対して説明している。読み取り自体が可能であるかどうかが、テストされたことはない。scripts/_probe_messaging.py は、それをテストするために存在し、そして、その問いが通常スキップするもう一つのこともテストする。仮説は — 未検証である、というのがポイントである — LinkedIn のデスクトップメッセージングビューは、表示された時点で会話文を開く。そのため、受信トレイの「読み取り」はスレッドを既読にしてしまってことになり、「通知」への抗議が、自分を「読み取り」と呼ぶツールを通じてやってくる、というものである。
そのプローブは、ロードが触れない面である /feed/ のナビバッジを前後で読み取ることで計測する。まだ実行されていない, そしてそれが実行されるまで、禁止リストは変わらない。境界というものは、まだ測定されていない主張では動かないものだ。
LinkedIn のサーバー上で何かを変更するその他の対応は全てスコープ外であり、tests/test_readonly.py は、パッケージ内のどこかの場所に2つ目の 変更操作(mutating call)が追加されて存在すれば、ビルドを failさせる。
1つのツールがこのマシン上の何かを変更する。: linkedin_logout(confirm=True) は ローカルの cookie jar を消去する。リクエストは一切発行されないため、LinkedIn に織り留められることはない。そして、linkedin_server_info はそれを、しかも read_only フィールドに enclosing して含めるのではなく、local_state_writes の項目として名前を挙げである。
二つの副作用 — 隠されてではなくとしてではなく、明示され
読み取りによって何かが変えられれば、それを明言する必要がある:
通知ページを開くと LinkedIn の未読バッジが消去される — 自分でページを開いたのとまったく同じである。検出では実測ではなく、測定結果であり、2026-08-21の一回の呼び出してバッジが 1→0 になり、それは後にも戻ってこない。避けるのは不可能: LinkedIn はページが提供される時点でサーバー側にリストを見たことをマークするので、このサーフェス上のどのよう読み取りでもバッジを変更しろも残さないことはない。クリックも、スクロールも、アイテムを個別に開くこともなく、パッケージ内に mark-as-read して呼び出しは一つ存在しない。バッジを消さない唯一の方法は、
linkedin_notificationsを呼ばないことです。 いずれにせよバッジは消えるため、各行には読み取り時点で LinkedIn が保持していたunreadを載せて enumする — ページロードが破壊されてしまう、唯一の獲得はである。ジョブ検索を一回実行すると、あなた自身の「最近の検索履歴」に追加される — サイトで自分でクエリをタイプした場合とと同じです。
どちらもツールの docstring と linkedin_server_info 内で利用されている。
セットアップ
cd D:\Sundeep\projects\job-hunting\mcp-servers\linkedin
pip install -r requirements.txt
playwright install chromium
python -m pytest # 986 passedそして、サーバーをクライアントに登録したあとは、最初に linkedin_login_browser を呼んでください。 linkedin.com/login のウィンドウが開きます。その画面に自分自身でサインインしなさい — このサーバーがパスワードを見たり、入力したり、保存したり、送信したりすることは決してありません。永続 Chrome プロファイルが、以後セッションを保持するので、これは LinkedIn がそれを失効するまで一度だけの手順です。
すべての読み取りを信じる前に、linkedin_auth_status で確認しましょう。
登録
stdio 転送、エントリーポイント linkedin.py:
{
"mcpServers": {
"linkedin": {
"command": "python",
"args": ["D:\\Sundeep\\projects\\job-hunting\\mcp-servers\\linkedin\\linkedin.py"]
}
}
}「読み取り専用」が持つものでなく、強制される方法
linkedin_server/readonly.py には 4つのメカニズムがあり、テストは、それらがそれぞれ 植えられた違反で失敗すること を、実際のパッケージに適用する前に示す。失敗できないチェックは、何かを保証するものではありません。
ナビゲーション許可リスト
assert_read_urlがpage.gotoへの唯一のドアである。許可される URL はすべてアンカーされたパターンです。タイプしたキーワードが、操作 URL へのナビゲーションになることはありません。ブロック対象には、/jobs/application/、/messaging/、招待、/edit/、open-to-work、action=を含む任意のもの、そしてwww.linkedin.com以外のすべてのホストが含まれる。求鼓のパターンがリストの中でもっとも厳限で形式て、数値 ID と、クエリ文字列を一切持たないことです。URL は整数から構成されているので、保存すべきクエリ文字列がもともと存在しません。LinkedIn が配信するのもお使いする slug 形式 (/jobs/view/senior-node-engineer-at-acme-4600000042) は、同じ理由で拒否されます — slug は job title であり、title は string であり Therefore.ソーススキャナー
パッケージ内を、状態を変更する可能性があるメソッド —click,fill、type、press、select_option、set_input_files、フォーム送信、およびGET以外のリクエスト — で grep します。検出されるのはちょうど1つだけです:writes.perform内のクリックです。スキャナーはこれを受け入れるために、緩めることはなされていない。依然としてすべての変更呼び出しを無条件に報告します。この一つの書き込みを許可しているのは、
readonly.SANCTIONED_MUTATIONSという独立した1行の許可リストであり、(path,function, kind)をキーとしています。その3つはすべて、実際の拒否に結びついています。つまりdom.py内の click、writes.pyの別の関数内の click、perform内のfillはすべて拒否される — そして、1スコープ下のクロージャに隠れた click も拒否されます。なぜなら属性は最も内側の関数に帰属するからです。これら5つのニアミスは、それぞれが失敗として示され ます。さらに、パッケージ内のミューテーション呼び出しがリストのエントリ数とちょうど一致することを別途断言し、これによって、3タプルだけでは区別できないperform内の2つ目の click を捕えています。evaluateも検出対象です: 読み取り専用DOM ハーベスターの3つは、末尾の# readonly-okでそれらのevaluateを免除しており、誰かがレビューできる diff において明示的に許可するまでは、新しいevaluateはビルドを失敗させる。ツール表面のチェック
ツール名に書き込みを表す動詞を含むツールはなく、ツールの docstring にも、肯定的な書き込みの主張はありません。ただし、ツールが「できない」ことを docstring が言っても良いです。 — むしろ「何も追加や削除する手段はない」こそ、読み取り専用ツールが含むべき文です。このため、このチェックは単語を禁止するのではなく、否定の有無をチェックします。起動時の境界
assert_launch_flags_permittedは、許可された 2つのフラグ以外の Chromium フラグを拒否し、また、--disable-blink-featuresがAutomationControlledをの値以外を持つことを拒否します — そのフラグは任意の Blink の動作を無効にできてしまうため、名前だけを許可するだけでは十分ではないからです。browser.pyはこのチェックを起動のたびに実行するので、CI内でだけでなく、実時間でも有効です。さらに、アンチ検出ライブラリ(playwright_stealth、undetected_chromedriver、キャプチャソルバー、TLSスプーフィングクライアント)が依存関係として入ってきた場合は、import 行のみにマッチするに合わせたスキャナーが拒否します。そのため、このファイルは引き続き散文で境界を説明することができます。
注入されたスクリプトは、ページを変更する可能性のあるもの(.click(、.value =、dispatchEvent、fetch( など) を対象に個別にスキャンされます。それらは DOM を検索してテキストを読み取ります。
そのスキャンは実際に動作しているものに縛られており、特定の方法で名前が付けられたものには縛られません。テストは最初にパッケージをパースし、すべての page.evaluate(...) 呼び出しの最初の引数を解決して、それをモジュールレベルの定数与え、その定数をスキャンします。これにより、スクリプトが読まれずに注入されることはできません。また、このチェックが解決できないもの(たとえば実行時に組み立てるもの)は、その場ビルドの失敗と総合的に扱われる。以前の実装は手書きの3つの名前(_JS で終わる)をスキャンしていました。ある冷静なレビューでは、localStorage.setItem と fetch( を持つ EVIL_INLINEという定数が既存のコールサイトに仕込まれ、テストが全てグリーンののまま出荷されてもいた。この穴は塞がれ、その攻撃は今ではテストです。
ログインの門: cookie がログインに見せる
このサーバーが構築される前日、兄弟サーバーがこれと正反対の成功を出荷した: セッション Cookie が現れた瞬間に成功を報告したのです。LinkedIn は、サインアウト状態の訪問者にも Cookie を渡すので、その成功報告は全く意味がありませんでした。
このサーバでは、判定は GET /voyager/api/me から得られます — LinkedIn の独自の Web アプリ本体が、ページロード時に行う identity 呼び出しです。 li_at Cookie が現れたとしても、それは単にエンドポイントに再度問い合わせるべき理由になるだけです。
結果は2ではなく3つの状態として報告されます:
authenticated: true— エンドポイントがアイデンティティを返しました。authenticated: false— エンドポイントが拒否したか、フィードがログアウト用の壁にリダイレ act した。authenticated: null— どちらも確認できなかった。「unknown」は「signed out」に collapse してしまわない。そうなると、セッションが正常であるのに もう一度サインインさせられる、事になってしまいます。
裏づけは、それが必要なのは「unknown を falseに変える」ことだけです。弱い証拠に基づいて true を生産することがを持つことは決して許されない。
Cookieの値は資格情報です。ログに記録されることも、このサーバーが永続化することも、ツールの結果に現れることもありません。報告されるのはその存在だけであり、それを2つのテストが検証しています。
サインインとその有効期限
linkedin_login_browser を呼び出します。 LinkedInのサインインページにChromeのウィンドウがが開き、そこに入力します。このサーバーがパスワードを見ることも、入力することも、保存することも、送信することもありません。それを行えるコードパスは存在しない、という理由です。ウィンドウは、identity endpoint が実際のセッションを確認するまで、あなたが閉じるまで、または wait_seconds が経過するまで開いたままです(デフォルトは300秒。もっと必要な場合は大きい数を渡してください)。
これは一度きりのステップであり、セッションごとのステップではありません。 セッションは、_state/chrome-profile/ の下にあるディスク上のChromeプロフィールに保存されています。つまり次のようになります:
出来事 | セッションは存続するか? | 理由 |
このサーバーの再起動 | はい | セッションはディスク上にあり、プロセス内にはありません。 |
マシンの再起動 | はい | 同上。 |
プロフィールディレクトリを削除 | いいえ | そのディレクトリがセッションそのもの。 |
ウィンドウ内でサインアウトする | いいえ | LinkedInが失効させるため。 |
LinkedInがcookieを失効 | いいえ | 後述参照。 |
LinkedInがどれくらい持たせてくれるか。 linkedin_session_info は、li_at cookieの有効期限日と残り日数を、ブラウザ自身のcookie jarからライブで読み取って報告します。つまり、推測する必要がなく、README の中の主張ではなく実測値なのでです。較正のためにいうと、このプロファイルにおけるLinkedIn独自の長期cookie(bcookie、bscookie)は 365日 の有効期限で発行されました。ログインを制御するのは li_at の値であり、本物のサインインだけがそれを生成します。
Cookieの値は認証情報です。ログに記録されることも、このサーバーが永続化することも、ツールの結果に載ることもありません。載るのは、名前と存在と有効期限だけです。
失効したときは、 すべての読み取りツールがその旨を報告します。{"error": "not_authenticated"、"message": "..."} と返し、復帰方法として linkedin_login_browser を指します。代わりに空のリストを返すことは決してありません。失効したセッションから得られる空のリストは、実際に何もないために返る空リストと見分けがつかないからです。
コールドスタートとその罠
li_at は persistent cookieです。JSESSIONID -- LinkedIn自身のWebアプリが csrf-token ヘッダーにコピーするもので、これがないとidentity endpoint は認証済みリクエストに応答しない-- は セッション cookie です(このプロフィールのcookie store では is_persistent=0)。そのため、ブラウザが起動するたび、cookie jar には完全に正常なログイン情報と、csrfトークンがない状態が入っています。
いきなりidentity endpoint に問い合わせるサーバー者は、トークンのないリクエストを送り、拒否され、セッションが正常なのにサインインを求められるでしょう。そこで、コールドジャーでは check_auth が最初にLinkedInページを1つ読み込みます。これによってLinkedInがきなちゃんを発行し、そのあとに尋ねます。その読み込みは裏書きの読み込みを兼ねるので、余分なリクエストはかかりません。
復旧パス: 自分のChrome にアタッチする
日常的に使うパスではありません。 上記の persistent プロフィールがありますが、答えです。これは、そのプロフィールのセッションが死亡する日、サインインインが拒否されている日のためのフォールバックです。LINKEDIN_CDP_ATTACH=1 で有効にすると、対象のサーバーは何も起動しません。あなたが起動したChrome にCDP経由で接続するだけです。
この奏力には2つのものが静かに負けています。どちらもこのマシンで実測しています:
タスクバーから開いた Chrome には DevTools ポートがありません。 「マイブラウザは開いている」では不十分です。
--remote-debugging-port付きで起動されたものである必要があります。Chromeのシングルトンがフラグを飲み込みます。 すでにChromeが起動している場合、フラグを付けて2つ目を起動すると、最初のプロセスに引数が渡されて終了します。ポートもなく、エラーもなく、終了コード ゼロで終わります。
そこでGit、まずChromeを完全に終了するか(ウィンドウ と バックグラウンドのインスタンスの両方)、するとあなたが実際のプロフィール、したがって実際のLinkedInセッションが維持されます:
"C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9224または、専用のプロフィールをそれに与えます。これは既存の実行中のChromeと並行して動作しますが、何にもログインしていないため、そのウィンドウ内で一度LinkedInにサインインします:
"C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9224 --user-data-dir="%LOCALAPPDATA%\linkedin-cdp"動作確認は http://127.0.0.1:9224/json/version を開けてください。JSON が返れば、ポートは稼働しています。または linkedin_cdp_status を呼ぶと、あなたの代わりにプローブして、何も鼻が返らないときはコマンドを報告します。アドレスは localhost ではなく 127.0.0.1 です。Chromeは IPv4 でしかポートにバインドしないため、ホスト名が [::1] を最初に解決してタイムアウトしてしまいます(実測 35 ms に対し 2085 ms)。
ポートは 9224 で、兄弟のNaukriサーバーの9223 とではないことを意図的に選んでいます。
アタッチモードでは、このサーバーはプロフィールロックを取得せず(自分のプロフィールを持っていないため)、あなたのタブを操作する代わりに、自分専用のタブで動作し、終了処理では ブラウザを閉じないで切断する だけです。CDP接続に対するPlaywright の close() は、クライアントを切断するだけで、実機実Chromeで計測したうえで採用しました。読み取り専用の許可リストは、両モードで同じです。
レート規律
ページロードの間隔は 最小3秒 に固定され、グローバルに導入されています。それは、変装ではなくスロットリングです。何かになりすますようなジッターをわざと入れていません。
1回のツール呼び出しにつき、ページロードは1回。 唯一の例外は
linkedin_my_profile(include_skills=True)で、2ページ目を読み込みpages_loaded: 2と報告します。自動ページングなし。 検索結果の次のページは、
start=25で意図的に取得します。すべてのリストの結果はcappedとpage_hadとlimitを持つであるため、「25件の結果」と「25件存在する」ことを混同するを得ありません。同時呼び出しは1つまで であり、プロセス内で直列化しています。また、プロセスも同時に1つまで で、Chromeプロフィール上のプロセス間ロックで直列化しています。1つのChromium ユーザーデータディレクトリを2つのプロセスが使うと破損し、セッションが消えます。それによって兄弟サーバーは37分使っていました。
ウィンドウは残りません。 ブラウザはアイドル5分間で閉じられ、ロックを解放します。
読み取れないとき
サーバーは、空のリストを返さないで例外を上げます。レンダリングに失敗したページからの空リストと、本当に何もないという空リストとを区別できないからです。この2つは決して混同していけません。失敗した読み取りは {"error": "extraction_failed"、"url": ...、"hint": ...} として返ってくるので、自分の手で同じページを開いて、サーバーが何を見たのかを確認できます。
唯一の例外は linkedin_search_jobs で、ゼロ件は有効な結果です。note 付きで results: [] を返します。
ページの読みられ方
LinkedInのクラス名は生成型で、GraphQL のクエリIDはデプロイごとに変わるため、どちらをアンカーにしても壊れやすいです。ローテーションしないのは、リンクの形です。人物は /in/<slug> の中、求人は /jobs/view/<id> の中にいます。すべてのリスト面では、そのリンクを見つけて、カードに周りのテキストを読み取って収集し、shape.py にある pure 関数でパースしています。そのために、パース部分はブラウザもネットワークもアカウントなしでテストされています。
通知をは、項目ごとで信頼できるリンクが唯一無い面です。この、代わりに構造をアンカーにしています。それは、あなたが最も修正が必要になる可能性が高い場所であり、見失った場合は空のリストではなく例外をスローします。
レイアウト
linkedin.py entry point (stdio)
linkedin_server/
config.py paths, timeouts, caps, the rate floor,
the two launch flags
readonly.py the allowlist, the scanners, the verb list,
the launch boundary
profile_lock.py cross-process lock on the Chrome profile
browser.py persistent context, single-flight, idle close
auth.py the login gate, session lifetime, cold start
cdp_bridge.py the recovery path: attach to a running Chrome
dom.py the read-only harvesters and the control readers
shape.py pure parsers and the result envelope
server.py the seventeen tools
errors.py
tests/ 1393 tests, no network, no account
fixtures/ frozen LinkedIn markup, scrubbedステータス
ビルドとテスト済み: 1393テスト,ネットワークなし・アカウントなし。 またguarda雛。ほとんどはブラウザなしで動作し、フィクスチャドリブンのモジュールはローランドのヘッドレスChromium を起動して、静止状態のマークアップ上で実際のリーダーを動かします。これらはマシンの外に触れることは一切ありません。
これらの数字は、このファイルが過去にも最もよく誤っていた数字です。スイートが1,000を超えた後も3回のリリースで986と書かれていました。それは単独では無害ですが、このサーバーが書けないという4つのドキュメントが言い続けていたのと同じ傾向です。今はこの数を引き継がず、各リリースごとに再測定しています。
初回ライブ実行: 2026-08-21。 サインインが成功し、セッションが維持できることを確認しました。上のフラグはこのマシンでは 十分であると検証済み です。また /voyager/api/me と li_at の有効期間(365 日)も確認しました。それ以降、実アカウントに対して読み取る各ツールを一度だけ実行しました。11個あるツールのうち4個が動作しました。その調査は ../_audit/2026-08-21-linkedin-parse-fix.md にまとめられていて、次のような結果でした。
linkedin_who_viewed_me は、名前ではないものを名前として返していました。 すべての行にページ見出し「Who's viewed your profile」があり、実在の人物のプロフィールリンクが付いていました。4行では、繰り返された名前が1つ、リンク数はすべて本物でした。修正は同じ日に行われました。行枠機能は、ハイドレーション後LinkedInが追加する属性に依存しなかではなくなり、プライバシー制限のある閲覧者(10件中6件)を黙って除外しのなくなりましたし、さらにタイムスタンプも読み取れるようにしました。ライブで検証済み: 10行、10つの異なる名前、欠落しているフィールドはありません。
第2回検証、2026-08-22。 前回わったままだった3つの面を修復し、ライブで検証しました。実は4件の欠陥をすべて同じもである。パーサがLinkedInの出力しなくなったマークアップ、またはページのレンダリング状況に依存するそれ、に支えられていたのです。
ツール | 変更前 | 変更後 |
| エラー: 名前を読み取れなかった |
|
| リダイレクトでエラーになった |
|
| 行が、ノイズがある | スクリーンリーダーのテキストはフレーズではなく数単位で除去され、 |
skills( |
| 実際のリストを返す -- ライブアカウントは20個だったが -- キーはページが提供するスキル単位の唯一のアンカー。 |
プロフィールの読み取りは、1点だけ行いません。 Experience と Education と Skills はプロフィールページにはありません。 LinkedIn はそれらをスクロールまで延期するのに、このサーバーはスクロールをしないからです。それらは 0 ではなく UNKNOWN と報告され、details_urls がその各ページを案内します。
第3回検証、2026-08-22。 最後に壊れていたのが linkedin_search_jobs でした。検証済みの雇用者の行には LinkedIn が「<title> with verification」という スクリーンリーダー行を追加します。これを位置的に読み取るとその行が company になり、実際の企業名が location に押し込まれました。2回のライブ検索で 14 行中 5 行です。
この修正は、その文字列に関するルールではありません。フィールドはもはや「1行目、2行目、3行目」としては読み取られません。LinkedInが1行ぶん挿入すると、それ以降のすべてのフィールドがずれるためです。しかも同じ2つのページには「Promoted」「Apply」「Viewed」「Actively reviewing applicants」に加えて、給与チップや卒業生の行までありました。各フィールドは現在、それを識別できる要素に固定されています。タイトルは、その行を求人行にするリンクのテキストに固定され、ページ自身のスクリーンリーダー用コピーは数えて差し引きます。会社は、LinkedInが雇用主のロゴに付与するアクセシブルネームに固定されます。ロゴは画像なので、行が挿入されても移動しません。勤務地は、entity lockup内のメタデータリストに固定されます。lockupは、クラス名を使わず、リンクもロゴも含む最小の祖先として見つけられます。これらのどれも提供しないサーフェス(ジョブトラッカーはどれも提供しない)は、従来どおりに行を順番に読むフォールバックになります。
同じクエリで実地検証済みです。7行中7行すべてが、この修正が意図的に使っていないLinkedIn自身のartdeco-entity-lockup要素と一致し、その7行のうち3行には検証用の装飾が付いていました。テストでは、LinkedInがまだ出荷していない装飾を、固定したすべての行のすべての位置に注入し、答えが動かないことを要求しています。また、アンカーを取り除くと、同じ注入がフィールドを壊すことを示す対照実験も備えています。
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityAmaintenanceEnables AI assistants to interact with LinkedIn by scraping profiles, companies, job postings, and getting personalized job recommendations using authenticated browser automation.173,204Apache 2.0
- FlicenseAqualityDmaintenanceEnables LLMs and agents to interact with LinkedIn's REST API for managing profiles, creating posts, viewing connections, and overseeing organizations.52
- AlicenseNot gradedqualityDmaintenanceEnables interaction with LinkedIn's Community Management API, allowing users to retrieve profile information and create posts via natural language.17738MIT
- AlicenseBqualityCmaintenanceEnables AI agents to manage LinkedIn profiles, posts, connections, skills, education, and certifications through the LinkedIn API.1817664MIT
Related MCP Connectors
Live LinkedIn data for AI agents: profiles, companies, jobs, posts, email finding. No account risk.
Let AI tools securely access your LinkedIn network and DMs
Search, label and export your LinkedIn saved posts, then draft, schedule and publish from them.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Sundeepg98/linkedin-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server