Skip to main content
Glama
Sundeepg98

linkedin-mcp

by Sundeepg98

linkedin

あなた自身の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 のフラグをひとつだけ渡します -- --disable-blink-features=AutomationControlled。これはBlinkが navigator.webdriver を公開するのを止めるもの。LinkedIn はサインイン時にこれをチェックし、それを取り込まないブラウザは拒否するからです。それだけのことです。この制約は起動時に readonly.assert_launch_flags_permitted が強制し、tests/test_launch_boundary.py が3つ目の追加フラグが現れたらビルドを失敗させます。

自分のデータのみ

あなたのプロフィールビュー、あなたの応募、保存した求人、あなたのプロフィール、あなたの通知。ほかのメンバーを列挙したり収集したりすることはありません。

読み取りのみ(指定された書き込み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

何ができますか

ツール

読み取り内容

linkedin_who_viewed_me

あなたのプロフィールを閲覧した人。アカウントがPremium Careerを利用している場合、365日前まで遡ります。これは求職活動の中で最も意図が明確なシグナルです。

linkedin_my_applications

応募した求人と、LinkedInが表示するステータス。

linkedin_saved_jobs

ブックマークした求人。

linkedin_search_jobs

キーワード、勤務地、リモート、掲載日、経験レベルによる求人検索。

linkedin_job_detail

1件の掲載情報を全文表示します。給与範囲、LinkedInの応募者数、勤務タイプと雇用形態、募集ステータス、そして説明文です。これらはいずれも検索結果や保存済み求人カードにはありません。また、apply_path——この掲載が使う2つの応募経路のどちらか、そしてオフサイト経路の場合、どの雇用主の応募者管理システムに送られるかを示します。

linkedin_followed_companies

フォローしている会社ページと、そのそれぞれの数値ID。linkedin_unfollow_company はこのIDを宛先にします。LinkedInはフォローしている数のうち約20行分しか表示できず、残りをページ送りする手段がないため、このツールは自分がカバーした範囲を報告するもので、全体をカバーしたことを示すものではありません。

linkedin_my_profile

自分自身のプロフィール——見出し、概要、スキル、そしてレンダリングされたセクション。職務経験・学歴・スキルは、ページがスクロールされるまでLinkedInが遅延させるため、ゼロではなくUNKNOWNとして読み取られます。

linkedin_notifications

あなたの通知リスト。

linkedin_auth_status

ライブセッションが存在するかどうか。認証済みリクエストで測定します。

linkedin_login_browser

あなた自身がサインインするためのウィンドウを開きます。

linkedin_session_info

セッションが生きているかどうかと、いつ有効期限が切れるのかを、ブラウザ自身のクッキージャーから読み取ります。資格情報、それを支えるcsrfクッキー、持続性、そして、ここでサイレント再認証が存在しない理由を報告します。renewal.session_lapses_at は、それ以降は更新しても意味がなく、手動でサインインしなければならない日付です——サーバー間で比較するためのフィールドで、LinkedInではクッキー自身の有効期限と一致します。なぜなら、この環境にはセッションをその期限を超えて運ぶ仕組みが何もないからです。

linkedin_logout

このマシンのクッキージャーを消してローカルのサインインを終了します。ここで唯一の破壊的ツールです。confirm=False(デフォルト)は何も実行せず、行われようとしていることをプレビューします。リクエストは発行しないため、LinkedInに知らされることはありません。

linkedin_cdp_status

復旧の診断——このサーバーが接続できるChromeが存在するかどうか。LinkedInには何も触れません。

linkedin_server_info

境界、レート設定、起動フラグを、ソースを読むことなく表示します。

書き込みを行う3つのツール

ツール

動作

linkedin_save_job

1件の掲載をブックマークします。confirm_token を付けずに呼び出すと何も実行しません。その場合は、掲載と保存済みリストをライブで読み込み、タイトルと雇用元で求人を特定し、トグルがどちらに動くか、各事実がどこから来たか、そして元に戻す方法を示すブロックを返します。そのブロックのトークンを付けてもう一度呼び出すと実行します。

linkedin_unsave_job

同じ構造、同じゲートを持ちますが、拒否します。後述を参照してください。

linkedin_unfollow_company

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——それは返してくれません。これは実際の回答であり、重要なり答えです。後述を参照。

それが役に立つ半分で、追加のページロードが不要で、純粋な読み取りです。

それは送信しません。 応募することがこのサーバーの管轄の下になるからないではなく、測定された理由によります。

  1. 応募(apply)フローは、一度もキャプチャされたことがない。 13件の求人キャプチャすべてを通しても、フォームはゼロ、ファイル入力はゼロ、ダイアログはゼロ、スクリーニング質問はゼロ、何かを送信するコントロールもゼロである。ここにあるものは、何が入力され、何が押されることになるのかを、何一つ見ていない。これは unsave_job に適用されるのと同じ基準であり、その基準を最も適用すべきアクションに適用したものである。

  2. 応募はここから取り消すことができない。 あらゆる確認レベルで、あらゆる状況で。取り消しは恒久的に禁止されている。

  3. サイトオフの半分は、もともとこのサーバーの管轄ではない。 どれほど良いキャプチャが得られたとしても、他人のフォームを、他人のドメインの上で、相手の利用規約の下で操作することは、別のソフトウェアの仕事である。

したがって 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 の項目として名前を挙げである。

二つの副作用 — 隠されてではなくとしてではなく、明示され

読み取りによって何かが変えられれば、それを明言する必要がある:

  1. 通知ページを開くと LinkedIn の未読バッジが消去される — 自分でページを開いたのとまったく同じである。検出では実測ではなく、測定結果であり、2026-08-21の一回の呼び出してバッジが 1→0 になり、それは後にも戻ってこない。避けるのは不可能: LinkedIn はページが提供される時点でサーバー側にリストを見たことをマークするので、このサーフェス上のどのよう読み取りでもバッジを変更しろも残さないことはない。クリックも、スクロールも、アイテムを個別に開くこともなく、パッケージ内に mark-as-read して呼び出しは一つ存在しない。バッジを消さない唯一の方法は、linkedin_notifications を呼ばないことです。 いずれにせよバッジは消えるため、各行には読み取り時点で LinkedIn が保持していた unread を載せて enumする — ページロードが破壊されてしまう、唯一の獲得はである。

  2. ジョブ検索を一回実行すると、あなた自身の「最近の検索履歴」に追加される — サイトで自分でクエリをタイプした場合とと同じです。

どちらもツールの 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つのメカニズムがあり、テストは、それらがそれぞれ 植えられた違反で失敗すること を、実際のパッケージに適用する前に示す。失敗できないチェックは、何かを保証するものではありません。

  1. ナビゲーション許可リスト 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.

  2. ソーススキャナー
    パッケージ内を、状態を変更する可能性があるメソッド — 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 はビルドを失敗させる。

  3. ツール表面のチェック
    ツール名に書き込みを表す動詞を含むツールはなく、ツールの docstring にも、肯定的な書き込みの主張はありません。ただし、ツールが「できない」ことを docstring が言っても良いです。 — むしろ「何も追加や削除する手段はない」こそ、読み取り専用ツールが含むべき文です。このため、このチェックは単語を禁止するのではなく、否定の有無をチェックします。

  4. 起動時の境界
    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 が現れた瞬間に成功を報告したのです。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つのものが静かに負けています。どちらもこのマシンで実測しています:

  1. タスクバーから開いた Chrome には DevTools ポートがありません。 「マイブラウザは開いている」では不十分です。--remote-debugging-port 付きで起動されたものである必要があります。

  2. 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の出力しなくなったマークアップ、またはページのレンダリング状況に依存するそれ、に支えられていたのです。

ツール

変更前

変更後

linkedin_my_profile

エラー: 名前を読み取れなかった

h1 がゼロのページから、名前、見出し、場所、About、写真を read。セクションは、見出しをちょうど1つ持つ、見出しの最大の先祖 -- 整合性、行ウォークと同じ規則 -- ハイドレーション前後で同一の出力ということを確認。ライブで検証済み。

linkedin_saved_jobs, linkedin_my_applications

リダイレクトでエラーになった

/jobs-tracker/?stage=saved と ?stage=applied を読み取る。両リストはは実際に空で、空結果は明示的にそう表示されます。LinkedIn自身のタブ数と、空状態の文言と一緒に表示されます。ページが裏付けたゼロ以外は、エラーです。ライブ検証済み。

linkedin_notifications

行が、ノイズがある

スクリーンリーダーのテキストはフレーズではなく数単位で除去され、when はカード自体の time 要素に基づいています。各行は読み取り時点の unread を保持しています。ライブページの固定スナップショットに対して検証済み。

skills(my_profile 内)

All、Industry Knowledge, Tools & Technologies を返していた

実際のリストを返す -- ライブアカウントは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がまだ出荷していない装飾を、固定したすべての行のすべての位置に注入し、答えが動かないことを要求しています。また、アンカーを取り除くと、同じ注入がフィールドを壊すことを示す対照実験も備えています。

Available Tools

17 tools
linkedin_auth_statusA

Report whether there is a live LinkedIn session, measured not guessed.

The verdict comes from an authenticated request: GET /voyager/api/me, the same identity call LinkedIn's own web app makes on page load. A session cookie sitting in the profile proves nothing and is never treated as an answer -- LinkedIn hands cookies to signed-out visitors too.

Three outcomes, deliberately:

  • authenticated true -- the endpoint returned an identity.

  • authenticated false -- the endpoint refused, or the feed redirected to LinkedIn's signed-out wall.

  • authenticated null -- neither could be established. Unknown is reported as unknown rather than collapsed into "signed out".

Costs up to two requests: the identity call, plus one feed load used only to turn an inconclusive answer into a definite "false".

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden and does so excellently. It discloses the internal behavior: makes up to two requests, uses the identity endpoint, treats cookies as unreliable, and explicitly defines the null case as 'unknown' rather than 'signed out'. This level of detail is transparent and goes well beyond basic expectations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is structured with a clear opening statement, followed by detail and bullet-point outcomes. Every sentence adds value: it explains the measurement method, the meaning of each outcome, and the cost. It is thorough without being verbose, and the front-loaded core statement makes it easy to grasp quickly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given that the tool has no parameters, an output schema exists, and the description thoroughly explains the semantics of each outcome, nothing is missing. It also covers edge cases (e.g., cookies from signed-out users) and costs, making it complete for an agent to use correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so the description correctly does not discuss any. Per the calibration rules, a baseline of 4 is appropriate since there are no parameters to explain and the schema coverage is 100% (empty schema). The description doesn't need to add anything beyond that.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: to report whether there is a live LinkedIn session, and it specifies the exact method (authenticated GET /voyager/api/me). It distinguishes itself from siblings like linkedin_session_info by emphasizing it measures actual authentication status, not guessed from cookies.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description effectively communicates when to use this tool—whenever an agent needs to determine authentication status. It explains the methodology and the three possible outcomes, making it obvious how to interpret results. However, it does not explicitly name alternative tools or state when not to use it, so it misses the highest bar for usage guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

linkedin_cdp_statusA

Is there a browser this server could attach to? A recovery diagnostic.

NOT the normal way to run this server, and not something to reach for first. The daily path is the persistent Chrome profile: sign in once and it holds for as long as LinkedIn honours it. This exists for the day that profile's session dies and an automated sign-in is being refused -- then the operator can run his own Chrome with a DevTools port open and let this server read through that instead.

It needs a Chrome that is ALREADY RUNNING and that was started with --remote-debugging-port. A browser opened from the taskbar has no such port, so "my browser is open" is not enough. Worse, when a Chrome is already running, a second one started with the flag silently hands its arguments to the first and no port opens at all -- so either quit Chrome completely first, or give the new one its own --user-data-dir.

This touches nothing on LinkedIn. It asks the local port what is there and reports the answer, or the exact command to run when nothing answered.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It states the tool touches nothing on LinkedIn, only queries the local port, and reports the answer or the exact command to run when nothing answers. This fully discloses side effects (none) and expected outcomes, making behavior transparent and safe to invoke.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Although the description is relatively long, every sentence earns its place. It front-loads the purpose and the 'not the normal way' nuance, then details prerequisites and pitfalls, and finishes with the safety guarantee and output behavior. The structure is logical and efficient, with no filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description is complete for a no-parameter diagnostic tool with an output schema. It explains the tool's role, when to use it, prerequisites, common failure modes, side effects, and how the result is reported. Nothing an agent needs to decide whether and how to call it is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, and the schema confirms an empty object. The description does not need to explain parameters, and the baseline for no parameters is 4. It adds nothing beyond that, but nothing is needed—the schema already fully covers the parameter surface.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: checking for an attachable browser, explicitly framing it as a recovery diagnostic. It distinguishes this from the normal persistent-Chrome-profile path and names the alternative clearly, so an agent can tell this tool apart from its siblings like linkedin_auth_status or linkedin_server_info.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly explains when to use this tool (when the profile session dies and automated sign-in is refused) and when not to use it (not the first choice, not the daily path). It also provides operational prerequisites (Chrome already running with --remote-debugging-port) and warns about the second-instance pitfall, giving concrete guidance that leaves no ambiguity.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

linkedin_followed_companiesA

The company Pages LinkedIn records you as following.

A read, and the exact counterpart of linkedin_saved_jobs: it answers "is this Page already on the list?". This server cannot follow or unfollow anything and ships no tool that could.

THE ONE THING TO READ BEFORE TRUSTING AN ANSWER. LinkedIn draws only the first rows of this list and fetches the rest on scroll; this server opens one page and reads whatever had drawn. So complete is normally false -- measured 2026-08-23, twenty rows under a heading saying 58 Pages -- and a Page missing from pages comes back as UNKNOWN, never as not-followed. Three-valued on purpose: "absent from the rows I was shown" and "you are not following them" are different facts, and reporting the first as the second is how a confirm gate ends up pointing the wrong way.

For a SINGLE employer whose posting you already have, linkedin_job_detail is the better read: it reports company_follow_state off the posting page itself, at no extra page load, and that answer is never partial.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNohow many rows to return.
companyNoa Page name or numeric Page id to ask about. Leave it out to get the whole rendered list.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description fully carries the burden and does so excellently. It discloses the incomplete list due to scrolling, that 'complete' is normally false, and explains the three-valued semantics (UNKNOWN vs not followed) to avoid misinterpretation. It also notes the server's read-only nature.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Although long, every section earns its place. The critical caveat is front-loaded in a bolded warning, and the structure guides the reader from core purpose to alternatives. Nothing is redundant; it's dense but efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers purpose, limitations, alternatives, and parameter usage thoroughly. Since an output schema exists to explain the return shape, nothing essential is missing. An agent can confidently decide when and how to use this tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Both parameters are already well-documented in the schema (100% coverage). The description adds a slight extra: 'Leave it out to get the whole rendered list' for the company parameter, which provides context beyond the schema. Given the high schema coverage, this is adequate but not extensive.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the resource (company Pages you follow) and the action (list/read). It explicitly identifies itself as the counterpart of linkedin_saved_jobs and answers 'is this Page already on the list?', which precisely distinguishes it from mutation siblings like linkedin_unfollow_company.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly names linkedin_job_detail as the better alternative for a single employer, explains why (reports company_follow_state directly from the posting page, never partial), and clarifies that this server cannot follow or unfollow anything. This gives clear when-to-use guidance and prevents misuse.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

linkedin_job_detailA

Read one job posting in full, including the description and the pay.

Every list tool here returns CARDS -- title, company, location, link. This reads the posting behind one of them, which is where the facts that settle a decision actually live: the pay range, LinkedIn's own applicant count, the workplace and employment type, the hiring status, and the description itself. None of those is on any card.

One page load, one posting. There is no sweep and no paging.

A read in both directions: this does not apply to the job, does not save it, and has no way to change anything about the posting. Nobody else on the page is collected either -- LinkedIn draws a hiring team and a "people also viewed" rail beside a job, and neither is read here.

A field the page did not carry comes back null rather than blank. If the page did not render the posting at all the call FAILS instead of returning an empty one: LinkedIn serves the document title before the body, so a title with nothing behind it is never treated as an answer.

ParametersJSON Schema
NameRequiredDescriptionDefault
job_idYesthe numeric job id, or a job url to take it from -- linkedin_search_jobs and linkedin_saved_jobs both return each.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

There are no annotations provided (readOnlyHint, destructiveHint are absent), so the description carries the full burden of disclosing behavioral traits. It does this well: it states that this tool does not apply to the job, does not save it, and has no way to change anything about the posting; it also clarifies it does not collect other people on the page. It describes null vs. FAIL behavior for missing fields and unrenderable pages. The only small gap is that it doesn't explicitly mention auth requirements or rate limits, but for a read-only tool with one page load, the disclosure is thorough and covers the key side effects and failure modes.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured and front-loaded: the first sentence states the core purpose, then it builds context about what cards lack, confirms the one-page behavior, clarifies non-effects, and explains null/fail semantics. It is a bit longer than strictly necessary (around 150 words) but every paragraph earns its place by addressing a distinct aspect: what this gives, what it does not give, and the specific edge case. The structure is clear and uses paragraphs to separate logical blocks. Slightly verbose in places (repeats 'this does not apply...' phrasing), but overall effective.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity is simple (one parameter, no nested objects) and there is an output schema (has_output_schema=true) that presumably describes the returned fields, the description covers the critical runtime behaviors an agent needs: what data is available, the distinction from sibling list tools, the null vs. FAIL semantics, and the explicit side-effect-free nature. The description fully equips an agent to decide when to call this tool and what to expect. The only potential missing detail is the exact output fields, but that is covered by the output schema, so the description is complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has a single parameter, job_id, with 100% description coverage (the schema already describes it as the numeric job id or a job URL from two sibling tools). The description reinforces this by mentioning that both linkedin_search_jobs and linkedin_saved_jobs return it, but does not add new meaning beyond what the schema gives. Since coverage is 100% and the description adds a small hint about which tools return the id, a score of 4 is appropriate—slightly above baseline because the description confirms the source of the parameter value, which is useful for an agent.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a direct, specific statement of what the tool does: 'Read one job posting in full, including the description and the pay.' It clearly identifies the resource (one job posting) and the verb (read). It goes further to distinguish itself from sibling list tools, explaining that this tool retrieves the full posting behind a card, and explicitly lists the facts available only here (pay range, applicant count, etc.). This fully separates it from the list/collection siblings (linkedin_search_jobs, linkedin_saved_jobs) and from action tools (linkedin_save_job, linkedin_unsave_job).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description is explicit about when to use this tool vs. the sibling list tools: it explains that list tools return only cards with title, company, location, link, while this tool is where the decision-settling facts live. It states there is one page load, no pagination, and explicitly mentions it does not read the 'people also viewed' rail or hiring team. It also provides clear behavioral conditions: the call FAILS if the page did not render the posting, and it treats a title-only page as an error. This gives an agent unambiguous selection criteria.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

linkedin_login_browserA

Open LinkedIn's sign-in page and wait for you to sign in yourself.

This server never sees, types, stores or transmits a password. It opens a browser window at linkedin.com/login; you type into that window; the persistent Chrome profile keeps the session afterwards, so this is a one-time step until LinkedIn expires it.

The window stays open until the identity endpoint confirms a real session, the window is closed, or wait_seconds runs out. A cookie appearing does not end the wait -- it only causes the endpoint to be asked again. On timeout the result is authenticated false with a reason, never an optimistic success.

ParametersJSON Schema
NameRequiredDescriptionDefault
wait_secondsNohow long to leave the window open for you. Default 300.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden — and it excels. It discloses that the server 'never sees, types, stores or transmits a password,' that the persistent Chrome profile retains the session, and crucially the non-obvious cookie nuance: 'A cookie appearing does not end the wait — it only causes the endpoint to be asked again.' It also states the no-optimistic-success guarantee on timeout. This prevents a common agent failure mode of assuming cookie presence means logged in.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the purpose in the first sentence, and every subsequent sentence earns its place: the password privacy guarantee, the persistent-profile behavior, the termination conditions, and the cookie/timeout nuance. Nothing is filler; the multi-paragraph length is justified by the density of non-obvious behavioral information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's interactive-browser complexity and the presence of an output schema, the description covers everything an agent needs: how the interactive flow works, the three termination conditions, the meaning of wait_seconds on timeout, and the return semantics. The one-time-step framing sets expectations correctly. Nothing essential is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the schema already documents wait_seconds with a default. The description adds value beyond the schema by tying the parameter to observable behavior: 'On timeout the result is authenticated false with a reason, never an optimistic success.' This connects the parameter to the wait-termination and result semantics, which is genuine value added over the schema text.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb+resource+behavior: 'Open LinkedIn's sign-in page and wait for you to sign in yourself.' This clearly distinguishes it from siblings like linkedin_auth_status (checking status) and linkedin_session_info (session info). An agent immediately knows this tool initiates interactive login rather than inspecting or acting on the session.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description conveys the key usage fact that this is 'a one-time step until LinkedIn expires it,' which tells the agent not to expect repeated calls. However, it does not explicitly route to alternatives such as checking linkedin_auth_status first to determine whether login is even needed. The context is clear but lacks explicit when-not/alternative guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

linkedin_logoutA

End the local LinkedIn session by erasing this profile's cookie jar.

THE ONE DESTRUCTIVE TOOL IN THIS SERVER, and the most expensive thing it can do to you. The sign-in it throws away took a full day to establish, and there is no automated way to put it back: linkedin_login_browser opens a window and you type into it yourself, exactly as you did the first time.

So confirm is False by default and an unconfirmed call performs NOTHING AT ALL -- no file is opened, no file is even stat-ed, no browser starts, the profile is not read. It hands back a preview naming the exact files a confirmed call would erase, what the sign-in cost, and how you get back in. Read that first; nothing about this is reversible afterwards.

Nothing here reaches LinkedIn. This server stays read-only towards the platform: no request goes out, no session is ended on LinkedIn's side, your account is untouched, and any other browser signed in to it stays signed in. What lapses is purely local -- the cookie jar on this machine.

A profile another process is using is never touched. If the cross-process lock is held, the answer is cleared false naming the holder's PID, because erasing a jar out from under a live Chromium is how a profile gets corrupted -- which costs the same day this tool is asking about.

ParametersJSON Schema
NameRequiredDescriptionDefault
confirmNoFalse -- the default -- previews and performs nothing. True erases the jar.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full behavioral burden. It thoroughly discloses the destructive nature, the confirm-gated behavior (including that unconfirmed calls do nothing), the local-only scope (no remote impact), and the cross-process lock protection. It is transparent about irreversibility and side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is lengthy but every paragraph serves a safety-critical purpose: purpose, consequences, confirm behavior, local-only scope, and lock protection. It is front-loaded with the core action, and structure is logical. While it could be trimmed, the verbosity is justified for a destructive tool.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's destructive complexity and absence of annotations, the description covers all essential aspects: the action, the confirm gate, preview behavior, irreversibility, recovery path, remote non-effect, and cross-process lock. The output schema exists separately, so return value details are not required here.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema already provides a description for 'confirm' that exactly matches the text in the tool description ('False -- the default -- previews and performs nothing. True erases the jar.'). Since schema coverage is 100%, the description adds no additional semantic value beyond reiteration; the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'End the local LinkedIn session by erasing this profile's cookie jar.' It clearly distinguishes itself as the sole destructive tool among siblings, making selection unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly warns that this is the one destructive tool and explains the cost of recovery, pointing to linkedin_login_browser as the alternative for re-establishing a session. The description implies when to use it (when you intend to end session) and strongly cautions against casual use, providing clear usage context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

linkedin_my_applicationsA

List the jobs you have applied to on LinkedIn, with their status.

Reads the Applied tab of your own job tracker (the page My Items > Applied became). Each row carries title, company, location, the status LinkedIn shows (applied, application viewed, resume downloaded, no longer accepting applications) and how long ago, plus the job id and link.

Status is whatever LinkedIn displays; this server does not infer, score or chase anything, and it cannot see applications you made anywhere else.

An empty result says so explicitly and carries LinkedIn's own count for the tab, so "you have applied to nothing" and "this could not be read" are never the same answer. If the two disagree -- no rows, but a non-zero count -- you get an error rather than an empty list.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNomaximum rows to return (default 25, max 100).

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It discloses that status is whatever LinkedIn displays, that the server does not infer or chase, and explicitly explains edge cases: empty results carry LinkedIn's count, and a mismatch between rows and count yields an error. This is exceptionally transparent about behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is multi-sentence and a bit longer than strictly needed, but every sentence adds value: scope, row contents, status behavior, and edge cases. It could be tightened slightly, but it is well-structured and front-loads the primary action.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description explains row contents (title, company, location, status, how long ago, job id, link), clarifies that statuses mirror LinkedIn's display, and covers empty-result and error disambiguation. Given the simple schema and the presence of an output schema, this is complete enough for an agent to call correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% for the only parameter (limit), and the schema already documents its behavior fully. The description adds no further parameter details, so the baseline score of 3 is appropriate per the scoring rules.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states exactly what the tool does: 'List the jobs you have applied to on LinkedIn, with their status.' It names a specific resource (own job tracker's Applied tab) and the action (list), and clarifies it reads only your own applications. This clearly distinguishes it from siblings like saved_jobs or search_jobs.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

While it doesn't explicitly name alternative tools or say when not to use it, the description makes the scope obvious: it reads only the Applied tab and cannot see applications made elsewhere. This is sufficient guidance for an agent to know when to invoke it versus other job-related tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

linkedin_my_profileA

Read your own LinkedIn profile as LinkedIn currently stores it.

Returns name, headline, location, the About text, and which sections were on the page when it was read.

On completeness: LinkedIn's own profile-strength meter is not exposed here, so this server does not report one. What it reports is derived and labelled as such.

One honest limitation, stated because its absence would otherwise read as data: LinkedIn now defers Experience, Education and Skills until the page is SCROLLED, and this server does not scroll. Those sections are therefore usually absent from the render, and absent means UNKNOWN here, never zero. sections_not_rendered names them, and details_urls gives you the page for each one if you want to look yourself.

ParametersJSON Schema
NameRequiredDescriptionDefault
include_skillsNoalso load the full skills page, which is where a real skills list can be read. That is a second page load, reported as pages_loaded: 2. Pass false to stay at one.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full responsibility for behavioral disclosure. It is exceptionally transparent: it explains that LinkedIn defers certain sections until scrolling and that this server does not scroll, so those sections are usually absent and reported as UNKNOWN, not zero. It also admits the profile-strength meter is not exposed and that derived data is labelled as such.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is longer than average but every sentence adds information: it states the purpose, enumerates outputs, discloses limitations, and clarifies how to interpret absence. It is well-paragraphed and front-loads the core purpose before diving into nuance. No filler exists.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only profile tool with an output schema, the description covers all necessary aspects: what is returned, the caveat about missing sections, how to interpret missing data, and a hint about the details_urls for further investigation. The 'include_skills' parameter behavior is also disclosed through the schema. Nothing an agent needs to call and interpret the tool correctly is omitted.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

There is a single parameter, include_skills, and its schema description is already very informative, explaining the effect (additional page load, pages_loaded: 2) and the trade-off. The description itself does not elaborate on the parameter, but because the schema coverage is 100% and the schema text is detailed, the parameter semantics are clear. A score of 4 reflects the extra value of the schema description beyond a bare type.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb and resource ('Read your own LinkedIn profile') and immediately lists the concrete output fields (name, headline, location, About text, sections). It clearly distinguishes itself from siblings by focusing on the user's own profile and explicitly notes what is not included (profile-strength meter).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains when the tool is appropriate (reading your own profile) and provides guidance on handling missing sections by pointing to details_urls. It does not explicitly contrast with sibling tools, but the tool name and the content give enough context for correct selection. The explanation of the 'include_skills' parameter also adds usage context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

linkedin_notificationsA

List your LinkedIn notifications as they appear on the notifications page.

====================== SIDE EFFECT -- READ FIRST ====================== THIS TOOL CHANGES SOMETHING ON LINKEDIN. Loading the notifications page CLEARS YOUR UNREAD BADGE -- every notification LinkedIn was still counting as unread stops being counted, exactly as if you had opened the page yourself. MEASURED, not theorised: one call on 2026-08-21 took the badge from 1 to 0, and it does not come back.

It cannot be avoided. LinkedIn marks the list seen on the server when the page is served, so there is no read of this surface that leaves the badge alone: no click, no scroll and no per-item open is involved, and there is no mark-as-read call anywhere in this package. The only way not to clear the badge is not to call this tool.

It is the ONE server-side change any tool here causes. Everything else in this package leaves LinkedIn exactly as it found it.

Partial compensation, since the badge is going either way: each row carries "unread": true/false as LinkedIn had it AT THE MOMENT OF READING -- which is the fact the page load is about to destroy. Read it here or lose it.

Rows carry the notification text, how long ago it arrived, whether it was unread, and the link LinkedIn attaches. Screen-reader-only text ("Unread notification.", "Status is reachable") is stripped, so the body is what you would read on screen.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNomaximum notifications to return (default 20, max 50).

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations are absent, so the description carries the full burden — and it delivers exceptionally. It discloses the badge-clearing side effect with a dated measured example (badge 1→0), states it is unavoidable, confirms this is the only server-side change in the package, and describes the partial compensation (unread flags captured at read time) plus input stripping behavior. This is model behavioral disclosure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long (~300 words), but the length is earned: the side-effect disclosure requires specific, verifiable detail. The purpose is front-loaded before the warning block, and the block formatting makes the critical information visually distinct. Minor deduction for verbosity in the compensation paragraph, which could be tightened.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter list tool with full schema coverage, an output schema, and a thoroughly disclosed side-effect profile, nothing an agent needs to call it correctly is missing. Return format is described in prose (notification text, time, unread flag, link), complementing the output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% — the single 'limit' parameter (default 20, max 50) is fully documented in the schema. The description adds no parameter-specific meaning beyond the schema, which the rubric sets as the baseline 3. Nothing beyond the baseline, but nothing missing either.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence states a specific verb (List) and resource (your LinkedIn notifications) with a clear frame of reference ('as they appear on the notifications page'). It is unmistakably distinct from the sibling tools, all of which cover auth, sessions, jobs, or profile surfaces — no other tool claims the notifications surface.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description does not explicitly name an alternative, but none of the siblings overlap with notifications, so exclusion guidance is unnecessary. It instead provides strong context on when NOT to call: the side-effect block makes the cost explicit ('The only way not to clear the badge is not to call this tool'), which functions as clear usage guidance. Slight deduction for not naming an alternative, though no natural one exists.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

linkedin_saved_jobsA

List the jobs you have bookmarked on LinkedIn.

Reads the Saved tab of your own job tracker: title, company, location, when it was posted where LinkedIn shows it, and the job link.

Read-only in both directions -- this lists what you saved and has no way to add to or remove from the list.

An empty result says so explicitly and carries LinkedIn's own count for the tab, so an empty list can never be mistaken for a read that failed.

The tracker also holds In Progress, Interview and Archived tabs. They are not exposed as tools: this reads the two lists it names and nothing else.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNomaximum rows to return (default 25, max 100).

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations at all, the description carries the full disclosure burden and handles it well. It explicitly declares read-only behavior in both directions (covering what destructiveHint would normally signal) and gives a strong edge-case guarantee: an empty result is stated explicitly and carries LinkedIn's own count, so an empty list can never be misread as a failed call. This is the kind of behavioral detail agents genuinely need.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The opening line is profitably front-loaded, but the body is somewhat verbose for a single-parameter list tool and contains an internal inconsistency: the last sentence says 'this reads the two lists it names' when only one list (the Saved tab) is actually named. Phrases like 'when it was posted where LinkedIn shows it' are clunky. The redundancy around read-only scope could be tightened.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (one optional parameter, no required inputs, an output schema present), the description is more than sufficient. It covers the safety profile, the exact scope, and the empty-result edge case, leaving no gap an agent needs to fill before calling it. The 'two lists' wording is a minor editorial slip, not a completeness failure.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema covers 100% of the single parameter (limit) with a complete description ('maximum rows to return (default 25, max 100)'). The tool description adds no parameter-level detail, which is acceptable since the schema already documents the limit fully and there are no other parameters to explain. Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first line uses a specific verb and resource ('List the jobs you have bookmarked on LinkedIn') that clearly identifies this as the read counterpart to the save/unsave siblings. It names exactly what is returned (title, company, location, post date, link) and scopes itself to the Saved tab, distinguishing it from search and job-detail tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description states the tool reads only the Saved tab and explicitly notes that In Progress, Interview and Archived tabs are 'not exposed as tools.' The read-only framing ('no way to add to or remove from the list') implicitly routes agents away from linkedin_save_job/linkedin_unsave_job, though it does not name those alternatives directly or give an explicit when-to-use statement.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

linkedin_save_jobA

Bookmark one job posting on LinkedIn. Two steps, and the first is free.

THIS TOOL CHANGES SOMETHING ON LINKEDIN, which no other tool in this server does. It is the reason linkedin_server_info no longer reports read_only: true.

CALL IT WITHOUT confirm_token FIRST. Nothing is done: the posting and your own saved list are read live, and you get back a block naming the job by title and employer, saying which way the toggle would move, where each fact was read from, and how the action can be undone. Read it, then call again with the confirm_token it hands you.

The token works ONCE, only for this posting, only for this verb, and it expires in two minutes -- so a scheduled or unattended caller can never hold a live one. That is the intended consequence and not a side effect.

After the click the result is confirmed from a DIFFERENT surface: your saved list, with LinkedIn's own per-tab count, rather than from the button that was just pressed. performed comes back true, false, or "unknown"; on "unknown" do not retry, because a retry on a toggle that did land performs the opposite action -- look at your saved jobs instead.

ParametersJSON Schema
NameRequiredDescriptionDefault
job_idYesthe numeric LinkedIn job id, as it appears in /jobs/view/<id>.
confirm_tokenNoleave empty to preview. Pass the token from that preview to actually save.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure, and it excels: it states the tool mutates LinkedIn state, explains the preview/confirm two-step mechanism, describes the token's single-use, per-post, per-verb, two-minute expiry, and discloses that confirmation is read from a different surface (the saved list) rather than the button. It also explains the 'performed' result values and the risk of opposite actions on retry. This is exhaustive and transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is longer than typical, but it is well-structured and front-loaded: the opening sentence captures the core action and two-step nature. Subsequent paragraphs each add necessary detail (token lifecycle, confirmation source, retry warning) without redundancy. Every sentence serves a purpose; while verbose, it respects the complexity of the operation.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's state-changing complexity and two-step flow, the description is exceptionally complete. It covers the preview, confirmation, token expiry, result semantics, retry avoidance, and even a fallback suggestion ('look at your saved jobs instead'). An output schema exists, but the description still clarifies the meaning of 'performed' and the confirmation pathway, leaving no critical operational detail missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already documents both parameters. The description adds meaningful context for confirm_token—its lifecycle, the preview/confirm pattern, and that an empty value means preview—which goes beyond the schema. For job_id, it mostly repeats the schema's description but confirms it as a numeric ID. The added semantic depth for confirm_token raises the score above the baseline of 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description explicitly states the verb 'Bookmark' and the resource 'job posting on LinkedIn', and immediately distinguishes this tool from all read-only siblings by noting it is the only one that changes something on LinkedIn. It also implicitly contrasts with the sibling 'linkedin_unsave_job' by describing the save action. No ambiguity about what the tool does.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives a precise two-step procedure: call without confirm_token to preview, then call again with the token to save. It clearly states when to use the tool (when you want to save a job) and provides explicit warnings about token expiration, single-use, and when not to retry ('on "unknown" do not retry'). It also mentions the intended consequence of preventing unattended callers, further clarifying appropriate usage.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

linkedin_search_jobsA

Search LinkedIn jobs with filters, returning one page of results.

Runs the search LinkedIn's own jobs page runs and reads the rendered results: title, company, location, job id and link.

One page load per call, no scrolling and no auto-paging -- LinkedIn puts roughly 25 results on a page, so ask for the next page deliberately with start=25, start=50 and so on. capped in the result tells you the limit trimmed the rows, and page_had tells you how many the page actually held.

Note that LinkedIn records searches in your own recent-search history, exactly as it would if you typed the query on the site. That is the only trace a search leaves, and it is on your account, not anyone else's.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNomaximum rows to return (default 25, max 50).
startNoresult offset for manual paging (0, 25, 50 ...).
remoteNoany | on_site | remote | hybrid.any
sort_byNorelevance | date.relevance
keywordsYeswhat to search for, e.g. "senior node.js engineer".
locationNocity, region or country. Empty means LinkedIn's default.
date_postedNoany | past_24h | past_week | past_month.any
experience_levelNocomma-separated from internship, entry, associate, mid_senior, director, executive. Empty means no filter.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full disclosure burden. It exceeds expectations by detailing that each call is a single page load with no scrolling or auto-paging, that results are roughly 25 per page, and that capped/page_had indicate row trimming. Most notably, it discloses a non-obvious side effect: searches are recorded in the user's own LinkedIn recent-search history, leaving a trace on the account. This level of transparency about side effects and pagination mechanics is exemplary.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is structured into three focused paragraphs: purpose, pagination mechanics, and privacy note. It is front-loaded with the core purpose in the first sentence. Every sentence contributes either operational details (single page, start offsets) or behavioral transparency (capped, page_had, search history). It is concise relative to the amount of critical information it conveys, though slightly longer than the bare minimum. The structure makes the key usage guidance easy to find.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a search tool with 8 parameters and an output schema, the description covers all necessary aspects: what the tool does, how it works, how to page for more results, what the returned fields include (title, company, location, job id, link), and the side effect on search history. The presence of an output schema means the return format needn't be detailed, and the description still names the key fields. Nothing essential is missing for an agent to call this correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The description does not add significant meaning beyond the schema: it mentions start for paging and limit indirectly through the pagination discussion, but the schema already documents defaults, enums, and descriptions for every parameter (keywords, start, limit, remote, etc.). The description's pagination explanation adds marginal value over the schema's 'result offset for manual paging' line, but not enough to raise the score above baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a crisp statement of function: 'Search LinkedIn jobs with filters, returning one page of results.' It then specifics the returned fields (title, company, location, job id, link) and indicates the search runs on LinkedIn's own jobs page. This clearly distinguishes it from sibling tools like linkedin_job_detail (which likely retrieves a single job) and linkedin_saved_jobs (which lists saved jobs). The purpose is unambiguous and specific.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit operational guidance: it explains the single-page behavior and instructs the agent to page deliberately using start=25, start=50, etc. It also explains the meaning of capped and page_had fields for interpreting results. However, it does not explicitly say 'use this instead of X' for any sibling tool, though the distinct search function is implied. The guidance is clear on how to use the tool for repeated paginated searches, which is the core usage pattern.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

linkedin_server_infoA

Describe this server: what it can do, what it deliberately cannot.

Useful for confirming the read-only boundary and the rate settings without reading the source.

version and build.code.commit are two different facts and both are reported. version is a HAND-MAINTAINED label: it says what this server calls itself, and it keeps saying it whether or not anybody remembered to bump it. build.code.commit is MEASURED -- it is the commit this process was imported from, read once at import and frozen.

WHAT TO DO WITH IT. A fix committed to disk changes nothing for a server that is already running. To tell "the fix is not loaded" from "the fix is wrong", compare build.code.commit against git rev-parse HEAD in the checkout::

git -C <this checkout> rev-parse --short=12 HEAD

They MATCH -> the running process holds that commit, so a bug you can still reproduce is a real bug. They DIFFER -> the process is STALE and no further committing will change its behaviour until the MCP client restarts it. build.code.dirty says whether the working tree had uncommitted changes when this process started, so a matching commit with dirty: true means the commit is necessary but not sufficient to describe what is loaded. build.process.started_at dates the answer.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully carries the burden. It discloses that version is a hand-maintained label while build.code.commit is measured and frozen at import, distinguishes between stale and current processes, explains how to compare against git rev-parse HEAD, and covers the dirty flag and started_at field. This goes well beyond what annotations would provide and gives the agent complete behavioral expectations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the main purpose and then dives into detailed interpretation of version/build fields. It is well-structured with a clear flow, but it is somewhat verbose, especially the code block and extended explanation of staleness. Every sentence adds value, so it is not padded, but it could be tightened without losing essential meaning.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description is extraordinarily complete for a no-parameter tool. It explains the meaning of each output field (version, build.code.commit, dirty, started_at), how to interpret them together, and even provides a practical procedure (git rev-parse HEAD) for diagnosing stale servers. Nothing an agent needs to call or interpret this tool is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so the schema is trivially complete (100% coverage). Per the rubric, a baseline of 4 applies, and the description adds no parameter detail (none is needed). No deduction is warranted.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with "Describe this server: what it can do, what it deliberately cannot," a specific verb/resource pair that clearly distinguishes this tool from siblings like linkedin_auth_status or linkedin_session_info. It states the exact scope (server capabilities, read-only boundary, rate settings) and the primary output (version and build information). No ambiguity about what this tool does.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly states when to use it ("Useful for confirming the read-only boundary and the rate settings without reading the source"). It stops short of naming alternatives or explicit when-not-to-use conditions, but the context of the sibling list makes the intended use clear. A 5 would require an explicit comparison to alternatives, which is missing.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

linkedin_session_infoA

Report whether the session is live and how long it has left.

This is the question that comes up after a week away: is the sign-in still good, and when does it lapse? The verdict is the same measured one linkedin_auth_status gives -- an authenticated call to the identity endpoint, never a cookie's presence -- and alongside it comes the expiry date read from the browser profile's own cookie jar.

The sign-in lives in an on-disk Chrome profile rather than in this process, so it survives this server restarting and the machine rebooting. What ends it is LinkedIn expiring it, a sign-out, or the profile directory going away.

When no browser can be started at all -- Chromium missing, another process holding the profile -- this tool does not die with it. It falls back to reading the expiry dates out of that profile's cookie jar on disk, which is precisely the moment you most want to know whether the login survived. Then 'authenticated' is null and the live_check block says why in plain words: a cookie in the jar is not a session, and reporting one as the other is a lie this server refuses to tell. Two labelled fields, never one blurred one.

What comes back, block by block, so a caller is never guessing:

  • credential -- li_at, the one cookie that authenticates here. Its name, whether it is there, its expiry, and expiry_source naming which route produced that date: the live browser's jar, or the on-disk jar read with no browser.

  • supporting -- JSESSIONID, role csrf. Not a second credential: it cannot sign anything in, it only governs whether the identity call can be made at all. It dies with the browser and a fresh one arrives on the next page load, so it having lapsed means nothing on its own.

  • renewal -- silent_renew_available is false here, and why says what the four servers in this family were ruled on: there is one credential layer, so a linkedin_reauth would be linkedin_login_browser wearing a different name and it is deliberately not shipped. It also carries session_lapses_at / _in_days: the date past which no silent renew can help and you sign in by hand. THAT is the number to compare against a sibling server, not credential.expires_at -- a server that re-mints its own credential shows hours there while its session holds for months. On LinkedIn the two coincide, because nothing here can carry the session past the cookie, and session_lapses_source says so. uses_browser is null rather than false -- there is no renewal here to characterise, and a false would claim one exists and happens to need no browser -- while mechanism spells out what recovery actually costs: a real window and your own hands, never a background refresh.

  • durability -- where the sign-in is kept and what it survives.

Cookie values are never returned. Only the name, whether it is there, and when it lapses. When it has lapsed every read tool says so with a reason rather than handing back nothing, and linkedin_login_browser is the way back.

(Everything below this point is dropped from the description a caller sees: FastMCP cuts a docstring at Args: and renders the rest into the argument schema. Prose that has to reach a caller goes ABOVE it.)

ParametersJSON Schema
NameRequiredDescriptionDefault
verify_liveNoput the question to the identity endpoint for a real verdict, which requires a working browser. Pass False for the free, browserless answer -- jar facts only, 'authenticated' null. Default True.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden and delivers heavily: it discloses the measurement method (identity endpoint, never cookie presence), the persistence of the session in an on-disk Chrome profile, the fallback to reading the jar without a browser and the resulting null 'authenticated', the fact that cookie values are never returned, and the semantic of renewal (silent_renew_available false, costs manual sign-in). This is extensive behavioral disclosure beyond the basics.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely verbose. It includes narrative prose ('This is the question that comes up after a week away...'), extensive block-by-block explanations, and a meta-note about FastMCP cutting the docstring. While structured with headings, the length is excessive for an AI caller; many sentences could be trimmed without losing essential information. The front-loaded purpose is clear, but the overall bulk detracts from usability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with no annotations and a fairly complex behavior (fallback, multiple output fields, nuanced semantics), the description is extremely thorough. It explains each output block, the meaning of null, the reasons behind renewal limitations, and the durability of the session. An agent would have all necessary context to call it correctly, even without seeing the output schema (which exists but is not needed to understand the tool).

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema already covers the only parameter (verify_live) with a clear description, so the baseline is 3. The description adds value by explaining the trade-offs in context: it details what True vs False means for the live_check block, the fallback when no browser is available, and the null semantics. This enriches the parameter's meaning beyond the schema's concise text.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence clearly states the tool's purpose: 'Report whether the session is live and how long it has left.' It goes further to differentiate from linkedin_auth_status by specifying that it uses the same identity-endpoint verdict but also includes the expiry date from the cookie jar, and it names linkedin_login_browser as the recovery path. This makes the resource and scope unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly references alternatives and when to choose them: it says the verdict is the same as linkedin_auth_status, points out that linkedin_login_browser is the way back after lapse, and explains the fallback behavior when no browser can be started. It also tells when to set verify_live True vs False. This gives clear decision rules for an agent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

linkedin_unfollow_companyA

Stop following one company Page. Two steps, and the first is free.

Same two-step shape and the same five gates as linkedin_save_job. What differs is worth reading before you use it, because two of the differences change what an answer from this tool means.

IT IS ADDRESSED BY THE NUMERIC COMPANY ID, NOT BY NAME. Call linkedin_followed_companies first: it prints the id beside each Page. A name is refused outright -- names collide, they change, and they belong to somebody else -- and the click is anchored to the row carrying the id, so the thing you name and the thing that gets pressed are the same row by construction. The preview still prints the NAME, because an id is not something a person can check.

THE LIST IS NEVER COMPLETE, AND THAT IS THE IMPORTANT ONE. LinkedIn renders about twenty rows of however many you follow, and offers no way to page through the rest. So a Page that is not in the rendered rows comes back "unknown" rather than "not followed", and the preview refuses rather than guessing. If the company you want is not reachable, this tool will say so instead of doing nothing quietly.

Confirmation after the click is read by RELOADING the same list -- there is no second surface that lists followed Pages -- and the verdict rests on LinkedIn's own stated total dropping by one, not on the row having vanished. On a partial list an absent row is not evidence.

THE PAIR IS ASYMMETRIC ON PURPOSE: this server can stop a follow and cannot start one. linkedin_follow_company is specced and is not performed.

ParametersJSON Schema
NameRequiredDescriptionDefault
company_idYesthe numeric LinkedIn company id, as printed by ``linkedin_followed_companies``.
confirm_tokenNoleave empty to preview. Pass the token from that preview to actually unfollow.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden and excels at it. It discloses the two-step confirmation flow, the lack of pagination in the followed-companies list and its consequences, the confirmation method (reloading the list and checking the total count drop), and the 'unknown' vs 'not followed' distinction. It even explains why a partial list makes absent rows unreliable evidence. This is exemplary behavioral disclosure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Though long, the description is tightly structured and every sentence earns its place. The key warnings are front-loaded and emphasized with caps, and the paragraphs each address a distinct behavioral aspect. It is verbose only where necessary; the complexity of the tool justifies the length, and the structure makes it scannable.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with an output schema and non-trivial behavioral caveats, this description is complete. It covers prerequisites (get the ID), limitations (list is partial), verification (count drop), and side-effect asymmetry. Nothing an agent needs to call it correctly or interpret results is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the parameters are already documented. The description adds contextual meaning: company_id is clarified as the numeric id printed by the followed list (not the name), and confirm_token is explained as the preview vs final confirmation flow, with an empty value meaning preview. This goes beyond the schema's basic 'leave empty to preview' by tying it into the overall two-step process, justifying a 4 over the baseline 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a precise verb+resource ('Stop following one company Page') and immediately differentiates from siblings by referencing the shared two-step shape and five gates, and by naming the asymmetry with linkedin_follow_company. It leaves no ambiguity about what the tool does and how it differs from related tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit when-to-use guidance: call linkedin_followed_companies first to obtain the numeric ID, explains that names are refused, and details the list incompleteness that makes a 'unknown' verdict possible. It also states the asymmetry (can unfollow but not follow) and directs to the unimplemented follow tool for the inverse, leaving no room for misuse.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

linkedin_unsave_jobA

Remove one job posting from your saved list. Built, gated, and refusing.

Same two-step shape as linkedin_save_job and the same gates, with ONE honest difference that this docstring will not bury: THIS TOOL CANNOT PERFORM ANYTHING TODAY, and it is not because the code is missing.

LinkedIn labels the save control by its accessible name, and the name it wears when a posting IS saved has never been observed on this account -- there has been nothing saved on it to observe. Every capture this repo holds shows the unsaved state. So the selector an unsave would click is unknown, and this server will not guess one: "Saved" and "Unsave the job" are both plausible spellings and it has seen neither.

THE FIX IS ONE MEASURED LINE, and the first supervised save produces it -- linkedin_save_job reads back the label the control changes into and reports it. Until that label is written down, this refuses with that explanation rather than clicking something it hopes is the right button.

A preview may also be unrenderable for a second and unrelated reason: an unsave is only valid on a posting that is currently saved, so with an empty saved list there is nothing to preview it against. The preview says so.

ParametersJSON Schema
NameRequiredDescriptionDefault
job_idYesthe numeric LinkedIn job id, as it appears in /jobs/view/<id>.
confirm_tokenNoleave empty to preview. A token is accepted but the action behind it will refuse until its anchor has been measured.

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A3.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations supplied, the description carries the full burden of behavioral disclosure. It is exceptionally transparent: it confesses 'THIS TOOL CANNOT PERFORM ANYTHING TODAY,' explains the technical reason (unknown accessible name), details the preview failure condition, and states it will refuse rather than guess. This level of honest disclosure is exemplary.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a multi-paragraph essay covering the tool's broken state, future fix, and preview behavior. While informative, it is verbose and could be condensed into a few sentences. An MCP description should be concise; this one isn't.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool has an output schema and no annotations, the description must convey behavioral context. It thoroughly covers why the tool refuses, how preview behaves, and the condition for it to work, leaving no ambiguity about what will happen when called. It is complete for an agent to handle correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% – both parameters (job_id and confirm_token) are fully described in the input schema, including the preview vs. token behavior. The description adds no additional parameter-level information, which is acceptable given the schema already covers semantics. Baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with 'Remove one job posting from your saved list,' which is a clear verb+resource statement. It implicitly differentiates from linkedin_save_job by being the inverse operation, though it doesn't explicitly compare with siblings. The core purpose is unambiguous despite the lengthy caveats about the tool being non-functional.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage when a user wants to unsave a job and explicitly states the tool cannot currently perform the action and will refuse until a label is measured. It also mentions the preview limitation based on having a saved job. However, it doesn't explicitly state when not to use it or mention alternatives, though the refusal condition is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

linkedin_who_viewed_meA

List the people who viewed your profile, most recent first.

The highest-intent signal in a job search: someone who opened your profile has already spent attention on you. Where the account has Premium Career, this list reaches back 365 days rather than the free tier's five viewers.

Rows carry name, headline, when the view happened, and a profile link.

Viewers browsing with limited visibility appear exactly as LinkedIn shows them to you and no more: "Someone at Acme", "Recruiter at Acme", with a date and no link, flagged "anonymous": true. They are the majority of a typical list and often the most useful part of it -- a recruiter's view is a recruiter's view whether or not it comes with a name. This server makes no attempt to work out who they are, and there is no code here that could: nothing is fetched about any viewer, and no viewer's profile is ever opened. What you get is the row LinkedIn already put on your screen.

Reads the Premium analytics page. The older /me/profile-views/ address now redirects to that same page, so the second attempt is a re-load for a page that had not finished rendering rather than a different surface; it still reports pages_loaded: 2 when it happens.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNomaximum rows to return (default 25, max 100).

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description takes full responsibility for behavior. It discloses that some viewers appear as anonymous with limited info, states the server makes no attempt to identify them and fetches nothing about viewers, mentions the data source (Premium analytics page), and explains the redirect behavior and resulting pages_loaded count. This is exceptionally transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is longer than average, but it is front-loaded with the core purpose and organized into informative paragraphs. Each segment adds value: the high-intent context, anonymous viewer behavior, technical data source, and the redirect edge case. It is structured logically and avoids redundancy, though it could be tightened without losing clarity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (one parameter, no nested objects) and the presence of an output schema, the description still covers all essential behavioral context: the kind of rows returned (name, headline, when, link), anonymous flagging, the data source, and the redirect quirk. It even explains what the server does not do. This is complete for an agent to use correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3. The sole parameter 'limit' is already fully described in the schema with default and max values; the tool description adds no additional semantic meaning or usage nuance beyond that. Thus it meets the baseline without needing extra explanation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with 'List the people who viewed your profile, most recent first' — a precise verb and resource. It clearly distinguishes from sibling tools like linkedin_search_jobs or linkedin_my_applications by specifying the unique action of listing profile viewers, and provides additional context about the 'highest-intent signal in a job search.'

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides strong contextual guidance on when this tool is valuable ('the highest-intent signal in a job search' and notes that anonymous viewers are often the most useful part), implying usage in recruiter outreach scenarios. However, it does not explicitly exclude or compare against sibling tools, so it falls short of a 5 but is above a 3 due to the clear situational context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 17 tool updatesv0.1.0
    • First observedlinkedin_auth_status
    • First observedlinkedin_cdp_status
    • First observedlinkedin_followed_companies
    • First observedlinkedin_job_detail
    • First observedlinkedin_login_browser
    • First observedlinkedin_logout
    • First observedlinkedin_my_applications
    • First observedlinkedin_my_profile
    • First observedlinkedin_notifications
    • First observedlinkedin_save_job
    • First observedlinkedin_saved_jobs
    • First observedlinkedin_search_jobs
    • First observedlinkedin_server_info
    • First observedlinkedin_session_info
    • First observedlinkedin_unfollow_company
    • First observedlinkedin_unsave_job
    • First observedlinkedin_who_viewed_me

TDQS

A4.2/5.0

Scored across 17 tools

Disambiguation5/5

Each tool targets a distinct action or query: auth status, session details, login, logout, CDP diagnostics, profile viewers, applications, saved jobs, job search, job detail, followed companies, own profile, notifications, save job, unsave job, unfollow company, server info. Even related tools like auth_status and session_info are clearly differentiated by scope.

Naming Consistency4/5

All tools begin with 'linkedin_' and use snake_case, but the pattern mixes noun phrases (linkedin_my_profile, linkedin_notifications) with verb-noun (linkedin_search_jobs, linkedin_save_job) and state queries (linkedin_auth_status). While consistent in prefix, the verb usage is not uniform, though still readable.

Tool Count4/5

17 tools is slightly above the ideal 3-15 range but each serves a distinct purpose, from session management to job and company interactions. The count is justified by the breadth of LinkedIn functionality covered, though a few are diagnostic or advisory.

Completeness3/5

The server covers the main job-search workflow (search, detail, saved, applications) and profile viewing, but has notable gaps: no way to follow a company (only unfollow), and the unsave_job tool is explicitly non-functional. Additionally, there is no tool for applying to jobs or editing profile, leaving some expected actions absent.

Maintenance

ActivityActive
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    C
    maintenance
    Enables AI agents to manage LinkedIn profiles, posts, connections, skills, education, and certifications through the LinkedIn API.
    18
    176 npm
    64
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables AI assistants to connect to LinkedIn, accessing profiles and companies, searching for jobs and people, managing saved jobs, updating job-search profile settings, and inspecting analytics.
    1
    Apache 2.0
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables natural language interaction with LinkedIn, including profile retrieval, job searching, messaging, and post engagement.
    3
    Apache 2.0