Skip to main content
Glama

linkedin-mcp

実際の Chromium ブラウザ上で、あなた自身がログインした LinkedIn セッションを操作し、すべての書き込み操作の前に明示的な確認ステップを挟む Model Context Protocol サーバーです。


お️ まず読んでください

これは通常の API クライアントではありません。インストールする前に、以全を読んでください。

  • あなたに代わり、実際にログイン済みのブラウザセッションを操作します。 サーバーは Chromium を起動し、保存されている LinkedIn クキーを復元し、人間がクリックするのと同じボタンをクリックします。

  • 正規の LinkedIn API は使っていません。 その背にOAuthアプリもパートナー契約も、サポートされた連携もありません。これは公開 Web サイトのブラウザ自動操作です。

  • この方法での LinkedIn の自動化は、LinkedIn のユーザー規約の外で行われます。 LinkedIn の規約は、スクレイピングと自動アクセスを禁止しています。

  • あなたのアカウントは、レート制限・制限または永久的な停止を受ける可能性があります。 そのリスクは現実的であり、仮定の話ではありません。LinkedIn は自動化を検知し、執行は予告なく、不服申し立てもできないまま行われ得ますます。

  • あなたのセションクキーはこのマシン上にあります。 ログインが成功すると、Playwright の storageState ファイルがローカルディスクに書き込まれます。このファイルを読める人は誰でも、あなたとして LinkedIn を操作できます。

  • 利用は完全にアカウント所有者自身の責任で行われます。 ここに明示保証も黙示保証も一切ありませんし、アカウントが停止された場合の救済手段もありません。

以上の3つの設計上の判断は上記の事実から導かれており、設定では変更できません。

  1. 単一のアカウント、単一のユーザー。 サーバーは保存した一つのセッションを保持する一つの状態ディレクトリを持ちます。マルチアカウントのサポートも「ユーザー」の概念もありません。キーボードの前の人のためのローカルツールです。

  2. すべての書き込み操作には明示的な確認が必須です。 投稿、接続、メッセージ、求人への応募は、すベて2回の呼び出しによるハンドシェイクです。1回のツール呼びだけでLinkedInに何かを送信することはありません。次のセクションを参照してください。

  3. サーバーは CAPCHA を絶対に解決しません。 パスワードも打、ちません。2FAのプロンプトを消ませんし、セキュリティチェックポイトを回避しません。LinkedInがチャレンジを出すとき、サーバーは停止し、通常のブラウザで自分で処理するよう求めます。

上記のいずれかがアカウントにとって受け入れられない場合、インストールしないでください。


Related MCP server: remote-mcp-linkedin

確認してから実行す仕組み

書き込み系のツール(linkedin_create_post、linkedin_send_connt_request、linkedin_send_messge、linkedin_apply_to_ob)はすべて、2回呼び出しのハンドシェイクです。

呼び出し1 — プレビュー。 実際の引数で、confirm をなしでツールを呼び出します。サーバーはページを調査し、実際に何が起こるかを正確に特定して、プレビューエンベロープを返します。何も送盾されず、日割のクォータも消費されません。

{
  "profileUrl": "https://www.linkedin.com/in/dana-whitfield-example/",
  "note": "Hi Dana — we both worked on the OpenTelemetry collector. Would like to connect."
}

呼び出し2 — 確認。 同じ呼び出しに confirm: true を含めてもう一度実行します。任意で、受け取た previewToken を返してもよいです。しておくと、サーバーはプレビュー時から引数が変っていないこを検証し、変わっていれば confirmation_mismatch で失敗します。

{
  "profileUrl": "https://www.linkedin.com/in/dana-whitfield-example/",
  "note": "Hi Dana — we both worked on the OpenTelemetry collector. Would like to connect.",
  "previewToken": "9f2c41ab77e05d13",
  "confirm": true
}

プレビューエンベロープは次のようになります(実際の linkedin_send_connection_request プレビュー):

{
  "status": "preview",
  "action": "linkedin_send_connection_request",
  "executed": false,
  "confirmationRequired": true,
  "summary": {
    "profileUrl": "https://www.linkedin.com/in/dana-whitfield-example/",
    "name": "Dana Whitfield",
    "headline": "Staff Engineer, Observability",
    "note": "Hi Dana — we both worked on the OpenTelemetry collector. Would like to connect.",
    "noteLength": 88,
    "notePreview": "Hi Dana — we both worked on the OpenTelemetry collector. Would like to connect.",
    "connectionDegree": 2,
    "connectionDegreeLabel": "2nd",
    "connectPathway": "direct",
    "alreadyPending": false,
    "wouldSucceed": true
  },
  "previewToken": "9f2c41ab77e05d13",
  "quota": {
    "action": "connectionRequests",
    "used": 3,
    "cap": 20,
    "remaining": 17,
    "resetsAt": "2026-08-25T07:00:00.000Z",
    "allowed": true
  },
  "warnings": [],
  "howToConfirm": "Nothing has been sent to LinkedIn yet. To execute, re-issue the exact same `linkedin_send_connection_request` call with `confirm: true` (optionally echoing `previewToken: \"9f2c41ab77e05d13\"` so the arguments are verified as unchanged)."
}

プレビューの読み方:

  • executed: false と confirmationRequired: true は、プレビューでは常に存在します。実行済みの結果には代わりに "status": "executed"、executed: true、result オブジェクトが含まれます。

  • warnings は、サせーが問題を感じているとこを伝える場所です。トランケーとされた投稿、長すぎる接続のメ、消費しきったクォータ、dry-runート、など。

  • プレビューで呼び出しが成功しない(すでに1次つながりの関係、すでにペンディングの招待が存在、Connect コントローがない、1次つながではないなど)と示されている場合、howToConfirm にはその旨が明記され、sure 認 を指定しても invalid_input で拒否され、クォータは消費されません。

  • previewToken は、アクション名と正規格化されたペイロードに対する短いダイジェストです。これは不一致防止のためのガドであって、セキュリティトークンではありません。


必要環節

  • Node.js 20以上 ("engines": { "node": ">=20" })。

  • 可視なブラウザウィンドウを開けるデスクトップ環境。インタラクティブなログインにはこれが必要です。

  • Playright の Chromeium ビルド(下記参照)。

セットアップ

依存関係をインストールします:

npm install

Playwright が使う Chromium ビルドをダウンロードします:

npx playwright install chromium

環境フaイルを作成します(中の変数はすべてオプションで、認証情報は含まれません):

cp .env.example .env

設定フファイルを作成します(日々の上限とタイムアウトのみ):

cp config.example.json config.json

TypeScriptを dist/ にコンパイルします:

npm run build

初回時の手順

この手順により、最後のステップまではLinkedInアカウントに一切触れません。

  1. npm install

  2. npx playwright install chromium

  3. cp .env.example .env と cp config.example.json config.json — どちらもオプションで、クレデンシャルは含まれません

  4. npm run build

  5. npm test — 75件の本体ユニットテスト。ブラウザもネットワークも不要です

  6. npm run verify:dry — ローカルのフィクスチャーに対し、すべての11ツールを動かし、アカウントを向けた前に配線が正しいことを確認します (詳細

  7. args にまず --dry-run を含め、MCP クライアントにサーバーを登録します (詳細。クライアントに 11ツールが表示され、書き込みツールがプレビューを返すことを確認します。

  8. args から --dry-run を外し、クライアントを再起動して linkedin_login を呼びます ([詳細](detail #details?))。これが、じめて linkedin.com にアセスするステップです。可視の Chromium ウンドウが開き、あなた自身が手でサインインします。

  9. linkedin_session_status を呼び出し、保存したセションが正しく動くことを確認します。

ステップ 8 が境界です。それ以前はすべて、ディレクトリを削除することで元に戻せます。


ログイン

このプロジェクトには、認証情報の設定はどこにもありません。それ意図的です。実際のブラウザウインドウで、一度だけ手動でサインインします。

  1. linkedin_login ツールを呼びます。引数はありません。

  2. 実際の可視な Chromium ウインドウが LinkedIn のログインページに開きます。 headless: true を設定しても表示されます。インラクティブなログインでは常にヘッド付きブラウザが強制されます。

  3. あなたがメールアドレスとパスワードを入力し、あなたが LinkedIn が次に求るもの(SMSコード、ユチケータコー、メール PIN、デバイス続認、CAPTCHA)を解除します。サーバーは認証情報を入力しませ型、パフィールドを読み取りません。チャレンジのウィジェトに触れることもありません。ただ2秒ごとにポールし、LinkedIn のナビゲションにサインイン済みの本人要素が現れるのを待ちます。

  4. 急がなくて大丈夫です。待機時間は loginTimeoutMs で制限され、デフォルトは 300000(5分)です。今回必要なら config.json で上げてください。

  5. LinkedIn がサインイン済みフィードを表示したら、ブラウザセションは状態ディレクトリの storageState.json に書込まれます(ファイル権限は 0600=所有者の読み書きのみ)。このファイルは gitignore されます。

  6. 以降のツール呼び出しはその保存済みセションを再利用します。数週間は再度ングイン不要です。

linkedin_session_status(引数なし)でいつでもセションを確認できます。フィードを1回読み込んで、次を報告します:

{
  "valid": true,
  "lastVerified": "2026-08-24T18:42:10.114Z",
  "sessionSavedAt": "2026-08-11T09:03:55.002Z"
}

valid: false の場合、reason も返します(例えば LinkedIn がフィードをサインインページにリダレクトしたこと)。クキーやセションの内容が、このツールまたは他のいかなるツールによって返されることはありません。

セションには有効期限があります。 期限が切れると、ツールは session_expired で失敗します。対策は常に同じです: linkedin_login をもう一度実行し、手操作でサインインします。


レート制限・ペーシング

サーバーは独自の日次上限を適用し、ブラウザ操作の前にランダムな遅延を入れます。これはアカウントを守る上で最も重要な保護であり、デフォルトは意図的に控えめです。

config.example.json を config.json にコピーして編集してください:

{
  "dailyCaps": {
    "connectionRequests": 20,
    "messages": 30,
    "posts": 5,
    "jobApplications": 10
  },
  "delayRangeMs": {
    "min": 1500,
    "max": 6000
  },
  "headless": false,
  "navigationTimeoutMs": 30000,
  "actionTimeoutMs": 15000,
  "loginTimeoutMs": 300000
}

四つの日次上限

上限

制限内容

デフォルト

connectionRequests

linkedin_send_conction_request による招待・送信

20 / 日

messages

linkedin_send_message によるメッセージ送信

30 / 日

posts

linked_in_create_post による投稿

5 / 日

jobApplications

linkedin_appry_to_ob に送信された応募

10 / 日

動作は次のとおりです:

  • 上限は、確認済みの実行時のみ消費できます。プレビューは現在のクォータを報告しますが、消費はしません。サーバーが完全に拒否した呼び出しは消費しません。

  • カウントは状態ディレクトリの counters.json に保存されるため、サーバーを再起動してもリセ不是れません。

  • 上限に達したときは、実行されずに rate_limited で失敗します。上限を 0 にすると、そのアクション自体を無効にします。

  • すべてのプレビューと実行済エンべロープには、used・cap・remaining・resetsAt・allowed を持つ quota ブロックが含まれます。

ランダム間延

delayRangeMs は、ブラウザのアクションの前にサーバーが均一にランダムに待機する範囲です — デフォルトは 1500 ms から 6000 ms。ランダム化は重要です。固定の間隔は機械の指紋になります。min は max と等しくても構い(固定遅延)ず、min が max を超えると configロードが config_invalid で失敗します。

上限はローカルの深夜にリセット

カウンタはローカルのカレンダー日付でキー付けされるため、四つの上限はあなたのタイムゾーンの深夜にリセットされます — UTC の深夜ではなく、ローリング 24 時間ではありません。quota の resetsAt は、次のローカル深夜を UTC ISO タイムスタンプとして表したものです。

グデフォルトより低い上限から始める

デフォルトで出荷される値は上限であって、推奨ではありません。アカウントが新しい、コネクションがまだ少ない、またはオートメーションを使うのが初めてなら、十分に低い値から始めてください — 例えば connectionRequests: 5、messages: 5、posts: 1、jobApplications: 2 — そして、LinkedIn の警告に注意しながら数週間かけてゆっくり上げます。


ツール・リファレンス

11のツールを、サーバーが登録する順に示します。

これらの4つのスクレーピング/求人ツール(linkedin_scrape_profile、linkedin_scrape_feed、linkedin_search_jobs、linkedin_apply_to_job)は、src/types.ts と src/selectors.ts の共通コントラクトから説明されています。使用しているクライアントの tools/list 出力がここでの引数名と異なる場合は、tools/list が正です — 実際のスキーマをサーバーが報告します。

linkedin_login

実表示の Chromium ウィンドウを開き、2FAやCAPTCHAを含めて、あなたが自分でサインインするのを待機します。成功するとセッションをローカル・ディスクに保存します。引数はありません。--dry-run では使用できません。

{}

linkedin_session_status

保存セッションがまだ機能するか、いつ保存されたか、いつ実行確認されたかを報告します。1回フィードを読んでチェックします。読み取りのみ。--dry-run では常に valid を報告します。引数はありません。

{}

linkedin_create_службы (書き込み — 確認が必要)

自分フィードにテキストを投稿します。posts 上限を使いいます。

プレビュー:

{
  "text": "Spent the week reading Playwright's tracing internals. Notes soon.",
  "visibility": "connections"
}

確認:

{
  "text": "Spent the week reading Playwright's tracing internals. Notes soon.",
  "visibility": "connections",
  "confirm": true
}
  • text — 必須、1〜3000文字。約1300字を超えると LinkedIn は投稿を「see more」で折りたたみます。プレビューで警告します。

  • visibility — 任意、"public" または "connections"。デフォルトは "public"。

  • mediaUrl — 任意の URL です。サーバーはリンク先のメディアを取り込むことはありません。 指定するとプレビューで警告し、事項確認では invalid_input で拒否します。

  • previewToken — 任意。送信すると、引数がユーザーの内容と変わっていないかを検証します。

  • confirm — 任意のブーアン。実行するには完全に eu である必要があります。

linkedin_send_conton_request(書き込み — 確認が必要)

接続の招待を、オプションで note を添付して送信します。connectionRequests の制限を消費します。

本ビュー:

{
  "profileUrl": "https://www.linkedin.com/in/dana-whitfield-example/",
  "note": "Hi Dana — we both worked on the OpenTelemetry collector. Would like to connect."
}

確認:

{
  "profileUrl": "https://www.linkedin.com/in/dana-whitfield-example/",
  "note": "Hi Dana — we both worked on the OpenTelemetry collector. Would like to connect.",
  "previewToken": "9f2c41ab77e05d13",
  "confirm": true
}
  • profileUrl — 必須、空外をしません。完全なプロフィル URL またはベアなバニティスラグです。

  • note — 任意、最大300字。200字を超えるノートはの多くのアカウントで LinkedIn のプレミアムが必要になります。200超え場合はプレビューが警告します。

  • previewToken / confirm — 上と同。

すでに1次つながりの関係がある場、招待が進行中である場、そのページーに Connect コントロールがない場合は、クォータを消費す前に invalid_input で拒否されます。

linkedin_send_message(書き込み — 確認が必要)

Sends a direct message. Consumes the messages cap.

ダイレクトメッセージを送信します。messages キャップを消費します。

プレビュー:

{
  "profileUrlOrConversationId": "https://www.linkedin.com/in/dana-whitfield-example/",
  "text": "Thanks for the pointer to the collector RFC — that answered my question."
}

確認:

{
  "profileUrlOrConversationId": "https://www.linkedin.com/in/dana-whitfield-example/",
  "text": "Thanks for the pointer to the collector RFC — that answered my question.",
  "confirm": true
}
  • profileUrlOrConversationId — 必須、空にできません。プロフィールURL/スラッグ、または既存の会話スレッドID(/messaging/thread/<id>/ 形式)のいずれか。

  • text — 必須、1〜8000文字。

  • previewToken、confirm — 上記と同じ。

プロフィールモードでは、受信者は1次のつながりである必要があります。それ以外の相手は、割り当てを消費する前に not_connected で拒否されます。このツールはInMailを送信せず、作成ボックスでEnterキーを押すこともありません — Sendボタンをクリックします。

linkedin_scrape_profile

1つのプロフィールを読み取り、構造化された Profile を返します:名前、見出し、概要、場所、つながりの次数、自分のプロフィールかどうか、経験エントリ、学歴エントリ、スキル。読み取り専用。

{
  "profileUrl": "https://www.linkedin.com/in/dana-whitfield-example/"
}

linkedin_scrape_feed

フィードから最近の投稿を読み取り、FeedPost エントリを返します:著者名と見出し、テキスト、投稿URL、いいね数とコメント数、投稿日時。読み取り専用。

{
  "count": 20
}

linkedin_search_jobs

LinkedInの求人検索を実行し、JobListing エントリを返します:jobId、タイトル、会社、場所、Easy Applyかどうか、求人URL。読み取り専用。

{
  "keywords": "site reliability engineer",
  "location": "Berlin, Germany",
  "easyApplyOnly": true,
  "count": 25
}

keywords は必須です。その他はすべてオプション:

  • location — 自由記述の場所名。LinkedIn自身の location パラメータにマッピングされます。

  • easyApplyOnly — 結果をEasy Applyの求人に限定します(LinkedInの f_AL ファセット)。linkedin_apply_to_job はEasy Applyのみを処理するため、このサーバーを通じて応募する予定がある場合は常に設定する価値があります。

  • datePosted — "past24h"、"pastWeek"、または "pastMonth"。

  • experienceLevel — "internship"、"entry"、"associate"、"midSenior"、"director"、または "executive"。

  • remote — true にすると、結果がリモート勤務の職種に限定されます。

  • count — 返す求人の数(デフォルト25、最大100)。結果は遅延読み込みされるため、count が大きいと、それぞれの間にランダムな一時停止を挟んだスクロールが繰り返され、時間がかかることがあります。

これらの4つのファセットは、上記のようにトップレベルで渡すか、filters オブジェクト内にグループ化して渡すことができます — どちらも受け付けられ、同じキーを2回渡した場合は filters が優先されます:

{
  "keywords": "site reliability engineer",
  "filters": { "easyApplyOnly": true, "datePosted": "pastWeek", "remote": true },
  "count": 50
}

LinkedInが使用可能なジョブIDなしでレンダリングするカード(プロモーション枠、プレースホルダー)は、不完全な状態で返されるのではなくスキップされ、レスポンスの skipped フィールドにカウントされます。レスポンスは使用した searchUrl もエコーバックするため、同じクエリを自分のブラウザで開くことができます。

linkedin_apply_to_job(書き込み — 確認が必要)

LinkedInのEasy Apply申請を送信します。jobApplications キャップを消費します。

プレビュー:

{
  "jobId": "3912847561",
  "resumePath": "/Users/parthbansal/Documents/resume.pdf"
}

確認:

{
  "jobId": "3912847561",
  "resumePath": "/Users/parthbansal/Documents/resume.pdf",
  "confirm": true
}
  • jobId — 必須。生のジョブID、/jobs/view/<id>/ URL、?currentJobId= を含む検索URL、またはジョブURNはすべて同じIDに解決されます。

  • resumePath — このマシン上の履歴書ファイルへのオプションの絶対パス。ファイルがない場合は file_not_found で失敗します。ファイルの内容がログに記録されることはありません。

  • 外部の応募者追跡システムに引き渡す求人は external_application で失敗します。Easy Applyコントロールのない求人は not_easy_apply で失敗します。確認する前にプレビューを読んでください — フォームがあなたに代わって回答しようとしている質問を確認できる唯一の機会です。

  • previewToken、confirm — 上記と同じ。

linkedin_list_pending_invites

保留中の招待を PendingInvite エントリとして一覧表示します:名前、見出し、プロフィールURL、送信日時、方向。読み取り専用。

{
  "direction": "received",
  "count": 25
}
  • direction — オプション。"received"(誰かがあなたを招待)または "sent"(あなたが相手を招待)。デフォルトは "received"。

  • count — オプションの整数、1〜100。デフォルトは25。

linkedin_list_connections

自分のつながりを ConnectionSummary エントリとして一覧表示します:名前、見出し、プロフィールURL、つながった日時。読み取り専用。

{
  "count": 50,
  "query": "observability"
}
  • count — オプションの整数、1〜200。デフォルトは50。

  • query — オプション。サーバーがすでに読み取ったつながりに適用されるローカルな大文字小文字を区別しない部分文字列フィルターです。検索としてLinkedInに送信されることはありません。


MCPクライアント設定

サーバーはstdio上でJSON-RPCを使用し、手動ではなくMCPクライアントによって起動されることを想定しています。まずビルドし(npm run build)、その後クライアントを dist/server.js に向けます。

サーバーは config.json、状態ディレクトリ、fixtures/ をその作業ディレクトリからの相対パスで解決するため — そしてMCPクライアントの作業ディレクトリは通常このプロジェクトではないため — env ブロックで絶対パスを明示的に設定する価値があります。

Claude Code / Claude Desktop

claude_desktop_config.json 内:

{
  "mcpServers": {
    "linkedin": {
      "command": "node",
      "args": ["/Users/parthbansal/Desktop/claude code/linkedin-mcp/dist/server.js"],
      "env": {
        "LINKEDIN_MCP_CONFIG": "/Users/parthbansal/Desktop/claude code/linkedin-mcp/config.json",
        "LINKEDIN_MCP_STATE_DIR": "/Users/parthbansal/Desktop/claude code/linkedin-mcp/.linkedin-mcp",
        "LINKEDIN_MCP_LOG_LEVEL": "info"
      }
    }
  }
}

Cursor

.cursor/mcp.json 内:

{
  "mcpServers": {
    "linkedin": {
      "command": "node",
      "args": ["/Users/parthbansal/Desktop/claude code/linkedin-mcp/dist/server.js"],
      "env": {
        "LINKEDIN_MCP_CONFIG": "/Users/parthbansal/Desktop/claude code/linkedin-mcp/config.json",
        "LINKEDIN_MCP_STATE_DIR": "/Users/parthbansal/Desktop/claude code/linkedin-mcp/.linkedin-mcp",
        "LINKEDIN_MCP_LOG_LEVEL": "info"
      }
    }
  }
}

汎用stdioクライアント(例:Codex CLI)

stdio MCPサーバーを起動するクライアントには、同じ3つのものが必要です — コマンド、その引数、環境:

{
  "name": "linkedin",
  "command": "node",
  "args": ["/Users/parthbansal/Desktop/claude code/linkedin-mcp/dist/server.js"],
  "env": {
    "LINKEDIN_MCP_CONFIG": "/Users/parthbansal/Desktop/claude code/linkedin-mcp/config.json",
    "LINKEDIN_MCP_STATE_DIR": "/Users/parthbansal/Desktop/claude code/linkedin-mcp/.linkedin-mcp",
    "LINKEDIN_MCP_LOG_LEVEL": "info"
  }
}

Codex CLIはJSONではなくTOMLを使用しますが、マッピングは一対一です:command = "node"、args = ["/Users/parthbansal/Desktop/claude code/linkedin-mcp/dist/server.js"]、そして [env] テーブル。

安全な実験

args に "--dry-run" を追加すると、アカウントに触れることができない完全に機能するサーバーを登録できます:

"args": [
  "/Users/parthbansal/Desktop/claude code/linkedin-mcp/dist/server.js",
  "--dry-run"
]

ドライランモードでは、リクエストがlinkedin.comに到達することはなく、投稿、招待、メッセージ、申請が送信されることもありません。これは、エージェントに初めてツールの表面を探索させる正しい方法です。

サーバーが受け付ける他のフラグ:--headless、--config <path>、--log-level <debug|info|warn|error>、および -h / --help(stdoutがプロトコルを運ぶため、stderrに出力します)。不明なフラグはハードエラーです — タイプミスの --dry-runn によって、実際にはドライランモードでないのにドライランモードだと誤認させてはなりません。全体を通しての優先順位はコマンドラインフラグ > 環境変数 > config.json > デフォルトです。

すべての環境変数は .env.example に文書化されています。すべてオプションであり、どれも認証情報を保持しません。


開発

出力なしで型チェック:

npm run typecheck

ユニットテストを実行:

npm test

保存時に再コンパイル:

npm run dev

--dry-run

npm run dry-run は、ビルドされたサーバーを --dry-run で起動します。このモードでは、ブラウザはlinkedin.comではなく fixtures/ 内のローカルHTMLフィクスチャ を指し、すべての書き込みフローの最終送信クリックがスキップされるため、確認されたアクションはコードパス全体(検証、割り当てチェック、ダイアログ操作)を進み、何も実行せずに停止します。実行された結果には dryRun: true が含まれ、プレビューはドライランが有効であることを警告します。linkedin_login は使用できず、linkedin_session_status は常に有効を報告します。

フィクスチャは、プロフィールページ(3つのバリエーション — 直接Connectボタンがある2次のプロフィール、「More」メニューの背後にConnectがある2次のプロフィール、動作するメッセージ作成ボックスがある1次のつながり)、フィード、求人検索、Easy Applyと外部の応募者追跡システムの両方の求人詳細ページ、メッセージング、招待、つながりをカバーしています。ここで開発してください。 このサーバーでの日々の作業で実際のアカウントに触れる理由はありません。

URLがどのフィクスチャに解決されるかは、src/fixtures.ts 内の部分文字列ルールの順序付きリストによって決定されます。この順序は構造上重要です — /mynetwork/invite-connect/connections/ は /mynetwork/invit を含み、/in/tomas-eriksen は /in/ を含むため — 特定のルールは一般的なルールの上に配置され、tests/fixtures.test.ts がその順序を固定しています。

LinkedInなしでの検証

npm run verify:dry

これはコンパイルされたサーバーを --dry-run で起動し、実際のMCPクライアントがするようにstdio上でJSON-RPCを送受信し、23のケースにわたってすべての11ツールを実行します — すべての読み取りツールを対応するフィクスチャに対して、すべての書き込みツールを完全なプレビュー → 確認のハンドシェイクを通して、そして重要な拒否(古い previewToken、長すぎる投稿、2次のプロフィールへのメッセージ、外部の求人、ドライラン中の linkedin_login)を実行します。エンベロープの契約も検証します:プレビューは executed: false を報告しなければならず、確認は executed: true を報告しなければならず、stdoutはJSON-RPCのみを運ばなければなりません。

サーバー自身の起動行が dryRun: true を確認しない限り、実行を拒否するため、誤ってアカウントに作用することはありません。動作するChromiumが必要です — session_status 以外はすべてブラウザページを開きます — そして、いずれかの失敗で非ゼロの終了コードで終了し、失敗を セレクタ/ロジック の失敗(サーバーが間違っている)と ハーネス の失敗(スイープが間違っている)に分類します。

テスト

テストスイートはVitest(tests/**/*.test.ts)によって実行され、現在 レートリミッター(src/rateLimiter.ts)、設定ローダー(src/config.ts)、フィクスチャルーティング(src/fixtures.ts) をカバーしています — 3つのファイルで75ケース。3つすべては構造上隔離されています:各ケースは新しい mkdtemp ディレクトリに対して実行され、環境は process.env から読み取られるのではなく明示的に渡され、レートリミッターのクロック、スリープ、ランダム性は RateLimiterDeps を通じて注入されます。ネットワーク呼び出しを行わず、ブラウザも起動しません。

ブラウザが必要なものはすべて上記のドライランスイープにあり、ユニットスイートにはありません — そのため npm test は1秒未満で完了し、node_modules 以外に何もインストールする必要がありません。

編集前に知っておくべき規約

  • stdoutに書き込んではいけません。 stdoutはJSON-RPCチャネルです。1バイトの余分な出力がフレーミングを非同期化し、クライアントが接続を切断します。すべての診断情報は Logger を通じてstderrに出力されます。

  • プロジェクトは moduleResolution: "NodeNext" のESMであるため、相対インポートはTypeScriptソースでも .js で終わる必要があります。

  • src/types.ts と src/errors.ts は共有の契約です。src/selectors.ts は文字列と純粋関数のみです。


トラブルシューティング

「起動したのに何も起こらない」 — それは成功です

npm start を手動で実行すると、stderrに1行表示され、その後は見かけ上沈黙します:

{"ts":"...","level":"info","msg":"linkedin-mcp ready on stdio","version":"0.1.0","tools":11,"dryRun":false,"headless":false}

それは正常なサーバーであり、ハングではありません。 MCP stdioサーバーは、ポートを持つデーモンでも、結果を出力して終了するCLIでもありません。stdin からJSON-RPCリクエストを読み取り、stdout にレスポンスを書き込むため、自己宣言後はクライアントが何かを言うのを待ってブロックします。クライアントが接続されていない場合、言うべきことは何もなく、正しいサーバーはstdoutに何も出力せずに静かにしています — そこに1バイトの余分な出力があると、プロトコルのフレーミングが非同期化されます。

つまり:npm start はこれの使い方ではありません。サーバーをMCPクライアントに登録し(下記)、クライアントに起動させます。待機中に応答することを確認したい場合は、リクエストをstdinに貼り付けてReturnキーを押します — tools/list の応答がすぐに返ってくるはずです:

printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | node dist/server.js --dry-run

手動で起動したサーバーを停止するには Ctrl-C を押します。SIGINT/SIGTERM でシャットダウンし、shutting down をログに記録します。

関連する2つの症状:

  • クライアントに0個のツールが表示される、または「サーバーの起動に失敗しました」。 ほとんどの場合、args のパスが間違っているか、dist/ がビルドされていません。dist/server.js への絶対パスを使用し、npm run build を実行し、クライアント自身のMCPログを確認してください — サーバーのstderrはそこに出力され、config_invalid またはNodeのモジュール解決エラーがそこにあります。

  • 準備完了の行が表示されるが、すべてのツールが失敗する。 その行が何と言っているか確認してください。dryRun:true はフィクスチャのみを意味し、何をしてもLinkedInに到達しません。すべての呼び出しで not_authenticated が返る場合は、保存されたセッションがまだないことを意味するので、linkedin_login を実行してください。

エラーコード

失敗は、安定した code を持つMCPツールエラーとして返されます。最もよく見られるもの:

verification_required — LinkedInがCAPTCHA、チェックポイント、または認証壁を表示しました。サーバーは意図的にここで停止し、それを解決しようとは決してしません。通常のブラウザでlinkedin.comを開き、ご自身でチャレンジをクリアしてから、もう一度 linkedin_login を実行してください。これが頻繁に発生する場合は、毎日のキャップを下げるべき信号として扱ってください。

session_expired — 保存されたセッションがもはや認証されないか、LinkedInがフィードをサインインページにリダイレクトしました。linkedin_login を実行して、もう一度手動でサインインしてください。linkedin_session_status が具体的な reason を表示します。

selector_not_found — サーバーが必要な要素を見つけられませんでした。これはほとんど常に、あなたが何かを間違えたのではなく、LinkedInがマークアップを変更したことを意味します。プロジェクト内のすべの DOM セレクターは src/selectors.ts にあります。そこが唯一のメンテナンスポイントです。該当する候補リストを見つけ、その先頭に新しいセレクターを追加して再ビルドしてください。候補は順序付きリストのため、新しいセレクターを追加しても既存のものは壊れません。--log-level debug で実行すると、どのルックアップの探索が失敗したかが分かります。

rate_limited — 毎日のキャップに達しました。エラーと quota ブロックが、どのキャップに達し、それがいつリセットされるか(次回のローカル時での深夜0時)を教えてくれます。リセットを待つか、config.json でそのキャップを挙げてください。ただし、キャップの引き上は、アカウントの制限を引く起こす正にその明の行動なので、意図して行ってください。

external_application — 求人ページは応募幣を、LinkedIn の Easy Apply フォームではなく外部の応募者追跡システムに委ねます。サーバーはサードパーティ・サイトへの入力はしません。ブラウザで求人を開き、その場で応募してください。

**not_apply…。 Wait, careful. Let me redo.

not_connected — 1次接続ではない相手にメッセージを送ろとのしました。まずつながりリクエストを送り、承されるのを待ってからメッセージしてください。サーバーはこの ACanMail で回避することはありません。

他に遭遇する可能のあるコードは次の通りです: not_ауthenticated( ディスイス上にセッションがない — linkedin_login を実行)、not_easy_apply、invalid_input、confirmation_mismatch(プレビューと確認の間で引数が変更された — もう一度プレビューする)、navigation_failed、browser_error、dry_run_unsupported、config_invalid(不正な config.json。サーバー起動前に報告される)、file_not_found。


このツールがやらないこ

  • InMail は使いません。 メッセージは1次接続と既存の会話のみです。

  • 外部 ATS への応募もしません。 Easy Applyのみ。LinkedIn の外に出る操作はすべて拒否されます。

  • CAPTCHA は解析しません。 2FA の自動機、チェックポイントの回避、その類一切をしません。

  • マルチアカウントには対応しません。 保存したセッションは1つ、アカウントは1つ、キーボードの前にいるのは1人だけです。

  • リモートメディアのアップロードはしません。 サーバーが URL を取得して投稿に添付することはありません。

セキュリティとプラバシー

保存されるものと、その場所。 すべてはこのマシン上の状態ディレクトリ以下に保存されます。既定は <cwd>/.linkedin-mcp/ で、LINKEDIN_MCP_STATE_DIR(またはLINKEDIN_MCP_STORAGE_STATE、LINKEDIN_MCP_USER_DATA_DIR、LINKEDIN_MCP_SCREENSHOT_DIR、LINKEDIN_MCP_COUNTERS でパスごとに上書きできます。

パス

内容

storageState.json

あなたの LinkedInCookie とオリジンストレージ。モード 0600 で書き込まれる

chromium-profile/

永続的な Chromium プロファイルディレクトリ

counters.json

今日のローカル日付と4つのアクションカウント

screenshots/

診断用スクリーンショット。ローカルにのみ書き込まれる

何も外部に送信されません。 テレメトリも、アナリティクスも、クラッシュリポートも、外部への接続も一切ありません。唯一のネットワーク接続先は linkedin.com で、あなた自身のブラウザセッションを通じてのみ行われます。--dry-run ではそれすら行われません。

スクリーンショットはローカル専用です。 自身がデバッグするために sports デのスクリーンショットディレクトリ to write; それらはアップロードも、ツールの出力に含まれることもありません。

ログは意図的に最小限です。 診断情報は stderr に出力されます。クッキー、storageState の内容、レジュメファイルの内容がログやシリアライズにされることは一切ありません。ツールの失敗時には、実行後のログではなくエラーコードだけが記録されるため、メッセージ本文やレジュメのパスがクライアントによってキャプチャされた stderr に漏れることはありません。redactConfig は、起動時にサーバーがログする有効設定行から絶対パス(とホームディレクトリ)を取り除きます。

gitignoreの保証。 .gitignore は、.env と .env.*(.env.example と残る)、.linkedin-mcp/ 状態ディレクトリ全体、任意の深さの storageState.json、chromium-profile/、counters.json、screenshots/、*.log、さらに node_modules/、dist/、coverage/ を無視します。追跡されるのは config.example.json とあなたの config.json のみで、その中にはキャップとタイムアウトしかありません。

storageState.json をコミットしないでください。 これは生の認証情報です。状態ディレクトリを機密として扱ってください。 storageState.json を読める人は、パスワードや2FAコードなしであなたの LinkedIn アカウントとして普通の操作ができてしまいます。漏洩した可を性があれば、LinkedIn のセキュリティ設定からすべてのセッションをログアウトし、そのファイルを削除し、もう一度ログインしてください。

Available Tools

11 tools
linkedin_apply_to_jobApply to a LinkedIn job (Easy Apply only)A
Destructive

Submits a LinkedIn Easy Apply application. jobId accepts a bare id ("3812345678"), a job URL, a ?currentJobId= URL or a urn:li:jobPosting: URN. resumePath is optional — an absolute path to a local .pdf, .doc or .docx file (never uploaded anywhere except LinkedIn itself); omit it to use whatever resume is already attached to your LinkedIn profile. answers maps question text to the answer to give; keys are matched case-insensitively and by substring, so "years of experience" answers "How many years of experience do you have?". CONFIRMATION IS REQUIRED: called without confirm: true this opens the Easy Apply modal read-only, lists every question it will be asked, flags the required ones it cannot answer in unansweredRequired, closes the modal without submitting, and returns a previewToken. Re-issue the identical call with confirm: true to actually submit. Postings that apply through an employer's own site are refused with external_application and their URL handed back for you to use by hand; postings with no Easy Apply button are refused with not_easy_apply. If a required question still cannot be answered at submit time the application is abandoned with invalid_input rather than sent incomplete — this tool never submits a partial application. Counts against the daily jobApplications cap.

NameRequiredDescriptionDefault
jobIdYes
answersNo
confirmNo
resumePathNo
previewTokenNo

TDQS

A4.8/5.0
Behavior4/5

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

Annotations indicate readOnlyHint=false, destructiveHint=true, idempotentHint=false, openWorldHint=true. The description goes beyond annotations by disclosing the two-phase confirmation requirement, the refusal reasons, and that it never submits partial applications. It also clarifies that resumePath is never uploaded anywhere except LinkedIn. While it doesn't detail every failure mode, it covers essential behavioral traits beyond the annotations.

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 comprehensive yet well-structured. It front-loads the core action, then details each parameter, then explains the confirmation flow and failure modes. Every sentence adds value, and it's written in a logical order. It's long but justified given the complexity.

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 complexity (5 params, nested objects, validation logic) and lack of output schema, the description provides complete guidance: parameter formats, confirmation flow, refusal cases, failure handling, and quota impact. No critical information missing for an agent to call it correctly.

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

Parameters5/5

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

Schema coverage is 0%, so the description must fully explain each parameter. It does: jobId formats, resumePath optionality and accepted file types, answers matching semantics (case-insensitive, substring), confirm flag behavior, and previewToken usage. This adds significant meaning beyond the bare schema properties.

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: submitting a LinkedIn Easy Apply application. It specifies the verb (submits), resource (LinkedIn Easy Apply application), and key details like jobId formats and confirmation requirement. It distinguishes itself from siblings like linkedin_search_jobs by focusing on the application action.

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?

Explicitly explains when to use: for Easy Apply postings, and when NOT to use: external applications and non-Easy Apply postings are refused. It also clarifies the confirm flag flow (preview vs submit). Mentions alternatives implicitly (handling external URL manually). Provides clear context for when this tool is appropriate versus others.

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

linkedin_create_postCreate a LinkedIn postA
Destructive

Publishes a text post to the signed-in member’s LinkedIn feed. This is a two-step tool: called without confirm: true it only returns a preview (character count, line count, audience, and a previewToken) and touches nothing on LinkedIn; call it again with the same arguments plus confirm: true to actually publish. Counts against the daily posts cap. visibility defaults to "public" (Anyone); "connections" restricts the post to 1st-degree connections. LIMITATION: mediaUrl is accepted for validation but images and video are NOT supported — this server never performs its own network fetches, so it cannot download remote media, and confirming a call that sets mediaUrl is refused with invalid_input rather than silently publishing text only. Post without media, or attach the image by hand in LinkedIn afterwards. Posts over roughly 1300 characters are collapsed by LinkedIn behind "…see more" (a warning, not an error). Under --dry-run the composer is driven against a local HTML fixture and the final "Post" click is skipped.

NameRequiredDescriptionDefault
textYes
confirmNo
mediaUrlNo
visibilityNo
previewTokenNo

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already mark destructive=true, but the description adds substantial context: the two-step preview/publish mechanism, daily posts cap, visibility default, media refusal with invalid_input, 1300-character collapse warning, and dry-run behavior. This goes well beyond what annotations provide and contains no contradictions.

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?

While long, every sentence adds operational value: purpose first, then invocation pattern, then limitations and edge cases. There is no filler or redundant phrasing; the structure logically leads the agent through what to expect.

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

Completeness4/5

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

Covers the preview response fields, error handling for media, warning threshold, and dry-run behavior. The only notable gap is that it doesn't describe what the final publish call returns (e.g., post ID or confirmation response), which would be useful since there is no output schema. Still, this is minor relative to the volume of useful context.

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

Parameters5/5

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

Schema has zero descriptions, so the description carries the full burden. It explains text (content), confirm (publish gate), mediaUrl (accepted but unsupported, refused on confirm), visibility (default 'public', meaning of 'connections'), and previewToken (returned from preview). All parameters are meaningfully clarified.

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: 'Publishes a text post to the signed-in member’s LinkedIn feed.' This clearly distinguishes it from siblings like send_message or scrape_profile and leaves 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 Guidelines4/5

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

It explains the two-step invocation pattern (preview without confirm, publish with confirm) and the media limitation as a clear 'not supported' case with manual workaround. It doesn't explicitly mention sibling alternatives, but the usage context and when-to-publish guidance are strong.

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

linkedin_list_connectionsList LinkedIn connectionsA
Read-onlyIdempotent

Lists the signed-in member’s 1st-degree connections, most recently added first (LinkedIn’s default sort). count caps how many are returned (default 50, max 200) and the list is scroll-paginated until that many are loaded, the list stops growing, or an internal scroll cap is reached. query is an optional case-insensitive substring filter applied LOCALLY to the name and headline of the connections that were loaded — it is not LinkedIn’s own search, so a match that sits far down a very long list may not be reached. Read-only: takes no confirm and changes nothing. Each entry carries name, headline, canonical profile URL, and a "connected at" timestamp when LinkedIn exposes one. total is the account-wide count from the page header when it can be read (totalSource: "page-header"), otherwise the number of cards that were loaded.

NameRequiredDescriptionDefault
countNo
queryNo

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already declare readOnlyHint, openWorldHint, idempotentHint, and destructiveHint, so the bar is lower. The description adds significant behavioral detail beyond that: pagination mechanics (scroll-paginated until count met, stops growing, internal cap), how the query filter is applied locally, the total count source behavior, and the per-entry fields including 'connected at' timestamp. This goes well beyond the structured hints.

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 dense but efficient—about 5 sentences covering all important aspects. It is front-loaded with the core purpose and sorted order, then details parameters and output. Could be slightly more concise (e.g., merging some clauses) but every sentence adds value without fluff.

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 only 2 optional parameters and no output schema, the description covers all necessary context: purpose, sorting, pagination, parameter semantics, output fields, total count source, and read-only nature. An agent can confidently call it and interpret results without needing additional external knowledge.

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

Parameters5/5

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

Schema coverage is 0%, so the description must fully explain both parameters. It does: count is given a default (50), maximum (200), and describes pagination; query is defined as a case-insensitive substring filter applied locally to name and headline. This fully compensates for the lack of schema descriptions.

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 lists the signed-in member's 1st-degree connections with a specific sort order (most recently added first). This distinguishes it from siblings like linkedin_send_connection_request (mutating) and linkedin_scrape_profile (single profile), leaving no ambiguity about what it 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?

The description explains when to use it (listing connections) and clarifies that the query filter is local, not LinkedIn's search, which warns against misuse for large lists. However, it does not explicitly point to alternative tools for actions like sending requests or scraping profiles, though the sibling names make that implicit. Slight gap in explicit exclusions.

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

linkedin_list_pending_invitesList pending LinkedIn invitationsA
Read-onlyIdempotent

Lists invitations that are still awaiting a decision. direction: "received" (the default) returns invitations other people sent you; direction: "sent" returns invitations you sent that have not been accepted yet. count caps how many are returned (default 25, max 100). Read-only: this never accepts, ignores or withdraws anything, and takes no confirm. Each entry carries name, headline, canonical profile URL, and a timestamp (the datetime attribute when LinkedIn provides one, otherwise the relative text such as "3 days ago"). An empty list is a normal result, not an error. total is how many invitation cards were found on the page, which can exceed returned when count is smaller.

NameRequiredDescriptionDefault
countNo
directionNo

TDQS

A4.4/5.0
Behavior5/5

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

The description explicitly states the tool never accepts, ignores, or withdraws anything, and takes no `confirm`, adding real behavioral context beyond the readOnlyHint annotation. It also discloses edge cases such as empty lists being normal and the difference between `total` and `returned`.

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 dense but every sentence earns its place: purpose, parameter semantics, read-only guarantee, output format, and edge-case handling. It is front-loaded with the core action and then efficiently covers nuances without redundancy.

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?

With no output schema, the description fully explains what entries contain, how timestamps are represented, that an empty list is valid, and how `total` relates to `returned`. Combined with parameter coverage and side-effect disclosure, nothing critical is missing for calling this tool correctly.

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

Parameters5/5

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

Schema description coverage is 0%, so the description carries full responsibility for explaining parameters. It thoroughly documents the `direction` enum values with defaults, `count` default and maximum, and even explains result-field meanings that relate to the parameters, fully compensating for the schema gap.

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 clearly states the tool lists 'invitations that are still awaiting a decision' and specifies the resource precisely. It also distinguishes sent vs received directions, but does not explicitly differentiate from the similar sibling tool `linkedin_list_connections`, so it falls just short of a 5.

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 gives strong operational guidance: direction defaults, count limits, and read-only behavior. However, it never mentions when to prefer this tool over alternatives like `linkedin_list_connections` or `linkedin_send_connection_request`, leaving selection context implied rather than explicit.

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

linkedin_loginLog in to LinkedIn (interactive)A
Idempotent

Opens a visible Chromium window on LinkedIn’s login page and waits for the human to sign in themselves, including any 2FA or CAPTCHA step. This tool never types credentials and never reads the password field. When LinkedIn shows a signed-in feed, the browser session is saved to local disk (permissions 0600) and reused by every other tool. Takes no arguments. Not available under --dry-run.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already indicate readOnlyHint=false and idempotentHint=true, but the description adds valuable context: it opens a visible browser, waits for human input, never types credentials, saves the session to local disk with permissions 0600, and is interactive. This goes beyond annotations and provides security-relevant details that affect agent behavior.

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 key action (opens browser, waits for human) and then covers important caveats (never types, session saved, not under dry-run) in a compact set of sentences. Every sentence contributes value, and the structure is 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?

Given the tool's interactive nature and reliance on human input, the description covers the essential flow: what it does, how it completes (when feed appears, session saved), and constraints (no credential handling, file permissions). It doesn't specify failure handling, but for a login tool this is acceptable and the description is complete for an agent to invoke it 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 takes no arguments, and the schema confirms an empty properties object. The description redundantly states 'Takes no arguments.' Since there are no parameters to document, the baseline of 4 applies, and the description adds no misleading information.

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: opens a visible Chromium window on LinkedIn's login page and waits for the human to sign in. It specifies the verb (opens, waits), resource (LinkedIn login page), and explicitly distinguishes itself from siblings by emphasizing it requires human interaction and never types credentials.

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 communicates when to use it: when a human must authenticate interactively, including 2FA/CAPTCHA. It also notes the session is saved and reused by other tools, implying usage before authenticated operations, and states it's unavailable under --dry-run. However, it doesn't explicitly name alternatives like linkedin_session_status for checking login status, or mention when not to use it.

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

linkedin_scrape_feedScrape the LinkedIn feedA
Read-onlyIdempotent

Reads the signed-in member's home feed and returns the posts as structured data: author name and headline, post text, permalink, reaction and comment counts, and a timestamp. count is how many posts to return (default 10, max 50). The feed is lazy-loaded, so reaching a high count means scrolling, and this tool pauses a randomized human-like interval between scrolls — a large count can therefore take a minute or more. Read-only: takes no confirm, consumes no daily quota, and never likes, comments or reposts; the only clicks are "…see more" expanders so post text is captured at full length. Posts are de-duplicated by permalink (LinkedIn recycles cards while scrolling) and sponsored or suggestion cards with no readable author are skipped. Fewer posts than requested is a normal result, not an error.

NameRequiredDescriptionDefault
countNo

TDQS

A4.8/5.0
Behavior5/5

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

The description substantially exceeds the annotations' coverage. It discloses the read-only nature (never likes/comments/reposts, no confirm, no quota), explains the lazy-loading with human-like pauses and the time implication for large counts, de-duplication by permalink, skipping of sponsored cards, and that fewer-than-requested results is normal. This rich behavioral detail helps the agent anticipate side effects and performance without contradicting the annotations.

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 longer than average, every sentence earns its place. The opening sentence states the core purpose, then parameter semantics, behavioral safety, de-dup/skipping logic, and the normal-result caveat are each addressed succinctly. The structure is front-loaded and contains no filler or repetition.

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 one parameter and no output schema, the description covers everything an agent needs: what it returns, how it behaves, potential pitfalls (time, fewer results), and safety guarantees. There are no meaningful gaps that would prevent correct invocation.

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

Parameters5/5

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

Schema description coverage is 0%, so the description must fully compensate. It does: it explains that count is how many posts to return, defaults to 10, has a max of 50, and that a larger count involves scrolling and can take a minute or more. This goes far beyond the schema's bare integer min/max, giving the agent the context needed to choose an appropriate value.

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 a specific verb ('reads') and resource ('signed-in member's home feed'), and enumerates the exact output fields (author name/headline, post text, permalink, reaction/comment counts, timestamp). This clearly distinguishes it from sibling tools like linkedin_scrape_profile, which targets profiles, and linkedin_create_post, which writes.

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 unambiguously communicates that this tool is for reading the home feed, which sets it apart from siblings (profiles, jobs, messaging). However, it does not explicitly name alternative tools or state conditions when this tool should be avoided (e.g., 'if you need profile data, use linkedin_scrape_profile'), leaving that inference to the agent.

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

linkedin_scrape_profileScrape a LinkedIn profileA
Read-onlyIdempotent

Reads a LinkedIn member profile and returns it as structured data: name, headline, about, location, connection degree, experience, education and skills. profileUrl accepts a full URL ("https://www.linkedin.com/in/john-doe"), a path ("/in/john-doe") or a bare slug ("john-doe"); omit it entirely to read the signed-in member's own profile. Read-only: takes no confirm, consumes no daily quota, and the only thing it clicks is the "…see more" expander, which reveals text already on the page. Profile sections LinkedIn hides from the viewer come back as null or [] rather than as an error — only a profile with no readable name fails (selector_not_found), because that means the page is not a profile. connectionDegree is 1, 2 or 3, and null when no badge is shown (which includes your own profile). skills is capped at 50 entries, experience at 25, education at 15.

NameRequiredDescriptionDefault
profileUrlNo

TDQS

A4.8/5.0
Behavior5/5

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

Even though the annotations already declare readOnlyHint and idempotentHint, the description adds substantial behavioral detail: it consumes no daily quota, only clicks the '…see more' expander, returns null/[] for hidden sections, and specifies the sole error condition (selector_not_found). It also clarifies connectionDegree semantics and result caps, going well beyond the annotations.

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 long but densely informative, with the core purpose and return fields front-loaded followed by parameter formats, behavioral notes, and edge cases. Every sentence adds operational value; no filler or tautology is present.

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?

With one optional parameter, strong annotations, no output schema, and no nested objects, this description covers all necessary invocation details: input formats, return fields, error behavior, and limits. An agent can correctly select and call this tool without needing additional documentation.

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

Parameters5/5

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

The schema only defines profileUrl as a string with no description, giving 0% coverage. The description fully compensates by explaining all accepted input formats, the default when omitted, and what the tool reads from that URL. This gives an agent everything needed to pass the parameter correctly.

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 a specific verb and resource: 'Reads a LinkedIn member profile and returns it as structured data,' and enumerates the exact fields returned. It is clearly distinguishable from sibling tools like linkedin_scrape_feed and linkedin_search_jobs, which target different data sources.

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 gives concrete usage context: accepted profileUrl formats (full URL, path, bare slug) and the behavior when omitted (reads the signed-in member's own profile). It does not explicitly name alternatives or when not to use this tool, but the profile-scoped purpose and sibling names make the choice reasonably clear.

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

linkedin_search_jobsSearch LinkedIn jobsA
Read-onlyIdempotent

Searches LinkedIn job postings and returns them as structured data: job id, title, company, location, whether the posting supports Easy Apply, and a URL. keywords is required; location is a free-text place name ("London", "Remote", "New York, NY"). Four optional facets narrow the search — easyApplyOnly, datePosted ("past24h" | "pastWeek" | "pastMonth"), experienceLevel ("internship" | "entry" | "associate" | "midSenior" | "director" | "executive") and remote — each accepted either at the top level or nested inside a filters object; filters wins if you somehow pass both. count is how many postings to return (default 25, max 100); results are lazy-loaded, so a high count means scrolling with a randomized human-like pause between scrolls and can take a while. Read-only: takes no confirm and consumes no daily quota. Cards LinkedIn renders without a usable job id are skipped and counted in skipped. Pass a returned jobId to linkedin_apply_to_job, which handles Easy Apply postings only — so filter on easyApply before trying to apply.

NameRequiredDescriptionDefault
countNo
remoteNo
filtersNo
keywordsYes
locationNo
datePostedNo
easyApplyOnlyNo
experienceLevelNo

TDQS

A4.8/5.0
Behavior5/5

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

Annotations already mark read-only and idempotent, but description adds substantial behavior: lazy-loading with randomized scroll pauses, scoring and skipping of cards without job IDs (counted in `skipped`), no confirm or daily quota, and the filter precedence rule (filters wins if both present). This exceeds annotation coverage and is genuinely useful.

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 lengthy, every sentence adds value: purpose, parameters, behavior, and downstream workflow. Front-loads the core action and returns, then details. No filler; structure is logical.

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 complex tool with 8 params, nested objects, and no output schema, the description covers return fields, skipped count, lazy-loading performance, and the apply workflow. It also clarifies default/max count and filter precedence. No critical missing information for an agent to call it correctly.

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

Parameters5/5

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

Schema description coverage is 0%, yet the description compensates by explaining each major parameter: required keywords, free-text location examples, all four facets with exact enum values (datePosted and experienceLevel), count default/max, and the nested-versus-top-level filter hierarchy. This is more than the schema provides.

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?

Clear verb+resource: 'Searches LinkedIn job postings' and explicitly lists returned fields (job id, title, company, location, Easy Apply support, URL). Distinguishes from siblings because no other sibling handles job search; it's the obvious entry point for discovery.

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?

Provides clear context: explains how to use filters and count, and explicitly routes results to linkedin_apply_to_job for Easy Apply postings. Does not explicitly state exclusions (e.g., when not to use) but the workflow guidance is strong. Lacks a direct 'use instead of X' but that's not needed given sibling separation.

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

linkedin_send_connection_requestSend a LinkedIn connection requestA
Destructive

Sends one connection invitation to a member, optionally with a note. Two-call handshake: without confirm: true this only reads the profile (name, headline, connection degree, and whether a Connect control exists at all) and returns a preview plus a previewToken — nothing is clicked and no invitation is sent. Re-issue the same call with confirm: true to send. profileUrl accepts a slug ("john-doe") or any LinkedIn profile URL. note is capped at 300 characters; free accounts are limited to 200 and LinkedIn silently truncates past its own limit, so notes over 200 characters are flagged. The request is REFUSED (with invalid_input, before any quota is spent) when the member is already a 1st-degree connection, when an invitation is already pending, or when LinkedIn offers no Connect control for that profile; the preview says so up front in that case. Counts against the daily connectionRequests cap. Under --dry-run the invitation dialog is driven against a local HTML fixture and the final Send click is skipped.

NameRequiredDescriptionDefault
noteNo
confirmNo
profileUrlYes
previewTokenNo

TDQS

A5/5.0
Behavior5/5

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

The description goes far beyond the annotations (readOnlyHint=false, destructiveHint=true) by detailing concrete side effects: no click or send without confirm, quota consumption, note truncation limits per account type, and refusal before quota is spent. This level of transparency 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.

Conciseness5/5

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

The description is dense but every sentence adds critical information. It is front-loaded with the core purpose and handshake, then layers on parameter details, refusal conditions, and quotas. No redundancy or 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?

Given the tool's complexity (two-call handshake, refusals, dry-run, quotas, note limits), the description covers every operational aspect. Without an output schema, it explains what the preview returns and what confirm does. An agent can safely and correctly invoke this tool without further context.

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

Parameters5/5

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

Schema coverage is 0% (no descriptions in the schema), so the description carries full burden. It explains profileUrl accepts slug or URL, note has 300-char cap (200 for free accounts with silent truncation), confirm triggers the actual send, and previewToken is part of the handshake. All four parameters are clarified.

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 sends a connection invitation to a member, optionally with a note, and explicitly describes the two-call handshake. It distinguishes itself from siblings like send_message or create_post by focusing on the connection request action and its preview/confirm flow.

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?

Provides explicit usage instructions: the two-call pattern (preview then confirm), when requests are refused (already 1st-degree, pending invite, no Connect control), and the dry-run behavior. It clarifies when not to call with confirm:true immediately, covering usage conditions thoroughly.

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

linkedin_send_messageSend a LinkedIn direct messageA
Destructive

Sends one direct message, either to a 1st-degree connection (pass a profile URL or slug) or into a conversation that already exists (pass the id from its /messaging/thread// URL). Two-call handshake: without confirm: true this only reads the profile or thread and returns a preview plus a previewToken, and nothing is typed or sent. Messaging is limited to 1st-degree connections — a profile at 2nd/3rd degree (or one whose degree cannot be read) is refused with not_connected, and InMail is never attempted. Counts against the daily messages cap. Under --dry-run everything runs against local fixtures and the final Send click is skipped.

NameRequiredDescriptionDefault
textYes
confirmNo
previewTokenNo
profileUrlOrConversationIdYes

TDQS

A4.7/5.0
Behavior5/5

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

Beyond annotations (readOnlyHint false, destructiveHint true), the description discloses the two-call handshake, that confirm:true is required to actually send, that it counts against a daily messages cap, the dry-run behavior, and that InMail is never attempted. It also mentions the refusal condition. This is rich, actionable behavioral context that adds real value over the annotations.

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 dense yet efficient, front-loading the core action and then packing critical details (handshake, restrictions, dry-run) without fluff. Every sentence adds necessary information and the structure guides the reader through the flow logically.

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 complexity (two-call handshake, degree restrictions, dry-run mode), the description covers all essential aspects an agent needs to call it correctly: return of previewToken, refusal with not_connected, daily cap effect, and dry-run behavior. Without an output schema, it adequately explains the expected response shape.

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?

With 0% schema description coverage, the description carries the burden of explaining parameters. It explains profileUrlOrConversationId (URL/slug or thread ID), confirm (triggers the send in the second call), and previewToken (returned from the first call). It does not explicitly describe the text parameter, but its meaning is obvious as the message body. This nearly compensates for the lack of schema descriptions.

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: 'Sends one direct message' and specifies the two distinct target types (1st-degree connection via profile URL/slug, or an existing conversation via thread ID). This unambiguously distinguishes it from siblings like linkedin_create_post or linkedin_send_connection_request.

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?

Provides explicit usage conditions: only 1st-degree connections, refusal with not_connected, and the two-call handshake required for sending. It explains when the tool will refuse and how confirmation works, though it does not explicitly name an alternative tool for other messaging scenarios. Still, the context is strong enough for an agent to decide when to use it.

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

linkedin_session_statusCheck the saved LinkedIn sessionA
Read-onlyIdempotent

Reports whether the locally saved LinkedIn session still works, when it was saved, and when it was last verified. Loads the feed once to check; no cookie or session content is ever returned. Under --dry-run this always reports valid. Takes no arguments.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.7/5.0
Behavior5/5

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

Beyond the readOnlyHint annotation, the description reveals that it 'loads the feed once to check', which implies a network call despite being read-only. It also assures that 'no cookie or session content is ever returned' and clarifies the --dry-run behavior. These details give the agent important expectations beyond annotations.

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 two sentences with no waste. The core purpose is front-loaded, followed by behavioral notes and the no-arguments statement. Every clause adds necessary 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?

For a zero-parameter, no-output-schema tool, the description fully covers what the tool does, how it checks (loads feed), what it returns (status, save time, verification time), and edge case behavior (dry-run). Nothing an agent needs to invoke it correctly 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 description explicitly states 'Takes no arguments.' This meets the baseline for no-parameter tools, and the statement removes any ambiguity, so a 4 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 description states a specific verb ('reports') and resource ('saved LinkedIn session'), and specifies the exact outputs: whether it still works, when saved, last verified. This clearly differentiates it from action-oriented siblings like linkedin_login or linkedin_create_post.

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 makes it clear this is a status check tool by stating what it reports. While no explicit alternatives or when-not-to-use are mentioned, the context of sibling tool names (all performing actions) makes the intended usage obvious. A slight deduction for not explicitly saying 'use before other LinkedIn operations'.

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. 11 tool updatesv0.1.0
    • First observedlinkedin_apply_to_job
    • First observedlinkedin_create_post
    • First observedlinkedin_list_connections
    • First observedlinkedin_list_pending_invites
    • First observedlinkedin_login
    • First observedlinkedin_scrape_feed
    • First observedlinkedin_scrape_profile
    • First observedlinkedin_search_jobs
    • First observedlinkedin_send_connection_request
    • First observedlinkedin_send_message
    • First observedlinkedin_session_status

TDQS

A4.7/5.0

Scored across 11 tools

Disambiguation5/5

Each tool targets a distinct LinkedIn action (login, session check, create post, send connection, send message, scrape profile, scrape feed, search jobs, apply to job, list invites, list connections). Even similar functions like scraping profile vs. sending connection request are clearly separated by purpose, with no two tools overlapping in behavior.

Naming Consistency5/5

All tools follow the 'linkedin_' prefix plus a snake_case verb_noun pattern (e.g., linkedin_create_post, linkedin_list_connections). Even the outlier 'linkedin_session_status' uses a consistent noun-like phrase, but the naming is uniform and predictable across the entire set.

Tool Count5/5

With 11 tools, the server covers a broad set of LinkedIn capabilities without being bloated. Each tool serves a clear, distinct purpose, from authentication and session management to content creation, messaging, scraping, job search, and application. The count is well within the ideal 3-15 range and feels appropriately scoped for a LinkedIn assistant.

Completeness4/5

The surface covers core workflows: posting, messaging, connection requests, profile/feed reading, job search, and Easy Apply. Minor gaps exist such as accepting/rejecting invitations, editing/deleting posts, or reacting to content, but the available tools handle the most common personal LinkedIn tasks without dead ends.

Related MCP Connectors

Related MCP Servers