linkedin-mcp
linkedin-mcp
실제 Chromium 브라우저에서 로그인된 여러분의 LinkedIn 세션을 직접 구동하고, 모든 쓰기 작업 앞에 명시적인 확인 단계를 두는 Model Context Protocol 서버입니다.
⚠️ 먼저 읽어주세요
이것은 일반적인 API 클라이언트가 아닙니다. 설치하기 전에 아래 내용을 모두 읽으십시오.
실제 로그인된 브라우저 세션을 사용자를 대신해 구동합니다. 서버는 Chromium을 실행하고 저장된 LinkedIn 쿠키를 복원하며, 사람이 클릭하는 것과 같은 버튼을 클릭합니다.
공식 LinkedIn API를 사용하지 않습니다. 이 뒤에는 OAuth 앱도, 파트너 계약도, 지원되는 통합도 없습니다. 공개 웹사이트에 대한 브라우저 자동화입니다.
이렇게 LinkedIn을 자동화하는 것은 LinkedIn 사용자 약관(LinkedIn User Agreement) 밖의 행위입니다. LinkedIn의 약관은 사이트 스크래핑과 자동 접근을 금지합니다.
계정이 속도 제한, 이용 제한, 또는 영구 정지를 당할 수 있습니다. 그 위험은 가상적인 것이 아니라 실제입니다. LinkedIn은 자동화를 탐지하며, 제재는 경고 없이, 그리고 항소의 여지 없이 수행될 수 있습니다.
세션 쿠키는 이 기계에 보관됩니다. 로그인에 성공하면 Playwright
storageState파일이 로컬 디스크에 기록됩니다. 그 파일을 읽을 수 있는 사람은 누구나 LinkedIn에서 여러분을 대신해 행동할 수 있습니다.사용은 전적으로 계정 소유자의 책임입니다. 여기에는 명시적이든 묵시적이든 보증이 없으며, 정지된 계정에 대한 구제도 없습니다.
위 내용에서 비롯된 세 가지 설계상의 결정 사항은 설정으로 변경할 수 없습니다.
단일 계정, 단일 사용자. 서버에는 하나의 저장된 세션을 담는 상태 디렉터리가 하나만 있습니다. 멀티 계정 지원도, "사용자"라는 개념도 없습니다. 키보드 앞의 사람을 위한 로컬 도구입니다.
모든 쓰기 작업은 명시적 확인을 요구합니다. 게시, 연결 요청, 메시지 전송, 채용 지원은 모두 두 번에 호출하는 핸드셰이크입니다. 단 한 번의 도구 호출로는 LinkedIn에 아무것도 전송될 수 없습니다. 자세한 내용은 다음 섹션을 참조하세요.
서버는 결코 CAPTCHA를 해결하지 않습니다. 비밀번호를 입력하지 않고, 2FA 요청을 해제하지도 않으며, 보안 확인 구간을 우회하지 않습니다. LinkedIn이 태잤을 때 서버는 멈추고 일반 브라우저에서 직접 처리하도록 알려줍니다.
이 중 하나라도 계정에 부적절하다면 설치하지 마십시오.
Related MCP server: LinkedIn MCP Server
확인 후 실행(confirm-before-execute) 작동 방식
모든 쓰기 도구(linkedin_create_post, linkedin_send_connection_request, linkedin_send_message, linkedin_apply_to_job)는 두 번 호출 핸드셰이크 입니다.
호출 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 모드 등—이 담겨있습니다.미리보기에서 호출이 성공할 수 없다고 뭐(이미 연결됨, 이미 초대 처리 중임, Connect는 없음, 1-stage 연결 상태가 아님 등)라고 알리면
howToConfirm이 해석을 알려주고, 확정을 시도해도 할당량을 소모하지 않고invalid_input로 거절됩니다.previewToken은 작업(describe) 이름과 정규화된 페이로드의 부호에 대한 짧은 다이제스트입니다. 일치 확인용 보호 세서이며, 보안 토큰이 아닙니다.
요구 사항
Node.js 20 이상 (
"engines": { "node": ">=20" }).보이는 브라우저 창을 열 수 있는 데스크톱 환경 — 대화형 로그인에는 이 기능이 필요합니다.
Playwright의 Chromium 빌드(아래에서 설치).
설정
의존성을 설치합니다:
npm installPlaywright가 구동하는 Chromium 빌드를 다운로드합니다:
npx playwright install chromium환경 파일을 생성합니다(파일 안의 모든 변수는 선택 사항이고 자격 증명을 포함하지 않습니다):
cp .env.example .env구성 파일을 생성합니다(일일 상한과 타임아웃만 포함):
cp config.example.json config.jsonTypeScript를 dist/로 컴파일합니다:
npm run build첫 실행, 순서대로
여기 있는 어떤 것도 체한 마지막 단계까지 LinkedIn 계정에 접하지 않습니다.
npm installnpx playwright install chromiumcp .env.example .env머시cp config.example.json config.json— 둘 다 선택적이고 자격 증명을 포함하지 않습니다.npm run buildnpm test— 브라우저도 네트워크도 사용하지 않는 힐미적(hermetic) 단위 테스트 75개npm run verify:dry— 지역 fixtures로 11개 도구 모두를 구동해, 계정에 연결하기 전에 배선이 제대로 동작하는지 확인합니다 (자세히)“MCP 클라이언트에
args에--dry-run을 포함해서 먼저 서버를 등록합니다 (자세히). 클라이언트가 11개 도구를 켜고 쓰기 도구가 미리보기를 반환하는지 확인하세요.args에서--dry-run을 제거하고 클라이언트를 다시 시작한 다음linkedin_login을 호출합니다 (자세히). 이것이 linkedin.com에 도달하는 첫 단계입니다. 실재하는 Chromium 창이 열리고 여러분이 직접 로그인합니다.linkedin_session_status를 호출하여 저장된 세션이 동작하는지 확인합니다.
8단계가 경계입니다. 그 이전의 모든 단계는 디렉터리를 삭제하면 원상태로 되돌릴 수 있습니다.
로그인
이 프로젝트에는 어디에도 자격 증명 설정이 없습니다. 그것은 의도적인 설계입니다. 여러분은 한 번, 실제 브라우저 창에서 직접 로그인합니다.
linkedin_login도구를 호출합니다. 인수를 가지고하지 않습니다.LinkedIn 로그인 페이지에 실제로 보이는 Chromium 창이 열립니다.
headless: true로 설정해도 이때에는 보이는 창이 나타납니다. 대화형 로그인은 항상 창(head) 브라우저를 강제합니다.이메일과 비밀번호는 직접 입력하고, LinkedIn이 요청하는 다음 단계 — SMS 또는 인증기 코드, 이메일 PIN, 기기 확인, CAPTCHA — 도 직접 해결합니다. 서버는 자격 증명을 입력하지 않니다, 비밀번호 필드를 읽지 않으며, 보안 확인 위젯을 건드리지 않습니다. 그저 2초마다 폴링하면서 LinkedIn 내비게이션에 로그인된 사용자 요소가 나타났는지 기다릴 뿐입니다.
천천히 하세요. 기다리는 시간은
loginTimeoutMs로 제한되어 있으며 기본값은300000입니다(5분). 더 필요하면config.json에서 높이세요.LinkedIn에 로그인된 피드가 나타나면, 브라우저 세션은 상태 디렉터리 안의
storageState.json에 저장됩니다. 파일 권한은 **0600**입니다(소유자만 읽기/쓰기). 이 파일은 gitignore 처리되어 있습니다.이후 모든 도구 호출은 저장된 세션을 재사용합니다. 앞으로 몇 주 동안은 다시 로그인할 필요가 없어야 합니다.
언제든 linkedin_session_status (인자 없음) 으 세션을 확인하십시오. 피드릴 한 번 로드하여 다음을 보고합니다:
{
"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
}네 가지 일일 상한
상한 | 제한 대상 | 기본값 |
|
| 20 / 일 |
|
| 30 / 일 |
|
| 5 / 일 |
|
| 10 / 일 |
동작 방식은 다음과 같습니다:
상한은 확인된 실행에서만 소비됩니다. 미리보기는 현재 할당량을 알려줄 뿐 소비하지 않으며, 서버가 애당초 거절하는 호출도 할당량을 소비하지 않습니다.
카운터는 상태 디렉터리의
counters.json에 저장되므로 서버를 다시 시작해도 초기화되지 않습니다.상한에 도달하면 도구는 실행 대신
rate_limited로 실패합니다. 상한을0으로 설정하면 해당 작업을 완전히 비활성화합니다.모든 미리보기 및 실행 결과에는
used,cap,remaining,resetsAt,allowed를 담는quota블록이 포함됩니다.
무작위 지연
delayRangeMs는 서버가 브라우저 작업에 앞서 균등하게 무작위로 딜십 구위입니다. — 기본값은 1500 ms에서 6000 ms입니다. 무작위화가 중요합니다: 고정된 간격은 기계 알고리즘의 시그니처이기 때문입니다. min은 max와 같을 수 있지만(고정 지연), min은 max를 초과할 수 없습니다. 그렇지 않으면 구성 로드가 config_invalid로 실패합니다.
상한은 자정(로컬 시간)에 초기화됩니다
카운터는 로컬 달력 날짜를 키로 사용합니다. 따라서 네 가지 상한은 UTC 자정도 아니고 오랫동안 흐르는 24시간 창도 아닌, 사용자의 시간대의 자정에 재설정됩니다. quota 블록의 resetsAt은 해당 다음 로컬 자정을 UTC ISO 타임스탬프로 직렬화한 값입니다.
기본값보다 낮춰서 시작하세요
제공된 기본값은 상한이지 권장값이 아닙니다. 계정이 새로 계정이고 연결 이력이 별로 없거나 자동화를 사용한 적이 없다면, 기본값보다 훨씬 낮춰서 시작하세요. 예: connectionRequests: 5, messages: 5, posts: 1, jobApplications: 2 — 그리고 LinkedIn 경고를 관찰하면서 수 주 동안 천천히 올려보세요. 평소 사용과 상관없어 보이는 활동의 치발증은 정확히 계정이 제한되는 원인이 됩니다.
도구 목록
서버가 등록하는 순서대로 11개 도구입니다.
다음에 있는 스크랩/채용 도구 4개(
probe_linkedin_scrape_profile,linkedin_scrape_feed,linkedin_search_jobs,linkedin_apply_to_job)는src/types.ts와src/selectors.ts의 접촉 계약(shared contract)을 기준으로 문서화되었습니다. 클라이언트의tools/list출력이 여기의 인수 이름과 다르면tools/list가 권위를 가집니다 — 서버는 항상 실제 스키마를 보고합니다.
linkedin_login
실제 가시되는 Chromium 창을 열고 사용자가 직접 로그인하기 를 기다립니다. 2FA나 CAPTCHA 단계도 포함됩니다. 성공하면 로컬 디스크의 세션 저장합니다. 인수가 없으며, --dry-run에서는 사용할 수 없습니다.
{}linkedin_session_status
저장된 세션이 여전히 유효한지, 언제 저장되었는지, 언제 마지막으로 검증되었는지 보고합니다. 피드를 한 번 로드하여 확인합니다. 읽기 전용이며, --dry-run에서는 항상 유효 상태를 보고합니다. 인수가 없습니다.
{}linkedin_create_post (쓰기 — 확인 필요)
텍스트 글을 자신의 피드에 게시합니다. 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이 게시물을 "더 보기" 뒤로 접습니다. 미리보기에서 경고를 안내합니다.visibility— 선택,"public"또는"connections". 기본값은"public"입니다.mediaUrl— 선택 URL. 서버는 원격 미디어를 일체 가져오지 않습니다. 이 값을 넘기면 미리보기에서 경고가 표시되고 confirm 시invalid_input으로 확정이 거절됩니다.보기
previewToken— 선택 사항입니다. 그대로 발신하면 인수가 변경되지 않았는지 검증됩니다.이기본
confirm— 선택 불리언. 실행되려면 정확히true여야 합니다.
linkedin_send_connection_request (쓰기 — 확인 필요)
연결 초대를 보내며 선택적으로 쪽지를 포함할 수 있습니다. 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
}profile_URL— 필수, 비어 있지 않아야 합니다. 전체 프로필 URL 또는 밴니티 슬러그(vanity slug)만 넣어도 됩니다.note— 선택, 최대 300자. 200자를 넘는 쪽지는 많은 계정에서 LinkedIn Premium이 필요하므로, 미리보기에서 200자가 넘으면 경고합니다.previewToken,confirm— 위와 동일합니다.
상대방이 이미 1촌 관계이거나, 초대가 이미 전송 중이거나, 페이지에 Connect 컨트롤이 없으면 할당량에 대한 누락을 소모하기 전에 invalid_input으로 거절됩니다.
linkedin_send_message (쓰기 — 확인 필요)
직접 메시지를 보냅니다. 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촌 연결 상태여야 합니다. 그 외 사람은 할당량(quota)을 소비하기 전에 not_connected 오류로 거부됩니다. 이 도구는 InMail을 보내지 않으며, 작성 모드에서 Enter 키를 누르지 않고 Send 버튼을 클릭합니다.
linkedin_scrape_profile
프로필 하나를 읽고 구조화된 Profile을 반환합니다: name, headline, about, location, 연결 관계(connection degree), 본인 프로필 여부, 경력 항목, 학력 항목, 기술 항목입니다. 읽기 전용입니다.
{
"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_ALfacet).linkedin_apply_to_job은 Easy Apply만 처리하므로, 이 서버를 통해 지원할 계획이라면 설정할 가치가 있습니다.datePosted—"past24h","pastWeek"또는"pastMonth".experienceLevel—"internship","entry","associate","midSenior","director"또는"executive".remote—true로 설정하면 원격 근무 공고로 결과가 제한됩니다.count— 반환할 공고 수(기본값 25, 최대 100)입니다. 결과는 lazy-load 방식이라count가 크면 각 항목 사이에 무작위 지연을 두고 반복 스크롤해야 하므로 시간이 걸릴 수 있습니다.
이 네 가지 facet은 위처럼 최상위 레벨에서 전달하거나, filters 객체 안에 묶어 전달할 수 있습니다. 두 방식 모두 허용되며, filters에 같은 키를 다시 전달하면 filters가 우선합니다.
{
"keywords": "site reliability engineer",
"filters": { "easyApplyOnly": true, "datePosted": "pastWeek", "remote": true },
"count": 50
}LinkedIn이 사용 가능한 job id 없이 렌더링하는 카드(프로모션 슬롯, placeholl드)는 반쯤 채워진 상태로 반환하지 않고 건너뛰며, 그 개수는 응답의 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— 필수입니다. 단순 job id,/jobs/view/<id>/URL,?currentJobId=를 포함하는 검색 URL 또는 job커블 모두 동일한 id로 해석됩니다.resumePath— 이 컴퓨터에 있는 이력서 파일의 절대 경로(선택 사항). 파일이 없으면file_not_found오류로 실패합니다. 파일 내용은 결고 로그에 남지 않습니다.외부 지원자 추적 시스템(applicant-tracking system)으로 넘어가는공고는
external_application오류, Easy Apply 컨트롤이 없는 공고는not_easy_apply오류로 실패합니다. 확인 전에 미리보기를 읽으세요 — 당신을 대신해 폼이 어떤 질문에 답할지 확인할 수 있는 유일한 기회입니다.raw,confirm— 위와 동일합니다.
linkedin_list_pending_invites
대기 중인 초대를 PendingInvite 항목으로 나열합니다: name, headline, 프로필 URL, 보낸 시각(sent-at), 방향(direction)입니다. 읽기 전용입니다.
{
"direction": "received",
"count": 25
}direction— 선택 사항."received"(누군가 나에게) 또는"sent"(내가 누군가에게)입니다. 기본값은"received"입니다.count— 선택 사항, 1에서 100까지의 정수. 기본값은 25입니다.
linkedin_list_connections
내 연결을 ConnectionSummary 항목으로 나열합니다: name, headline, 프로필 URL, 연결 시각(connected-at)입니다. 읽기 전용입니다.
{
"count": 50,
"query": "observability"
}count— 선택 사항, 1에서 200까지의 정수. 기본값은 50입니다.query— 선택 사항. 서버가 이미 읽은 연결 목록에 적용되는 로컬, 대소문자를 구분하지 않는 부분 문자열 필터입니다. LinkedIn 검색으로 전송되지 않습니다.
MCP 클라이언트 설정
서버는 stdio에서 JSON-RPC를 사용하며, 수동이 아니라 MCP 클라이언트가 실행하도록 설계되었습니다. 먼저 build(npm run build)한 다음 클라이언트가 dist/server.js를 가리키게 하세요.
서버는 config.json, 상태 디렉터리, 그리고 fixtures/를 staffing디렉토리relative as path 작업 디렉터리 기준으로 해석하므로 — 그리고 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 서버를 실행하는 모든 클라이언트는 명령어(command), 인자(args), 환경(env) 이렇게 같은 세 가지가 필요합니다:
{
"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/code/linkedin-mcp/dist/server.js"], 그리고 [env] 테이블입니다.
안전한 실험
계정에 아무런 변경을 가하지 않는 완전히 작동하는 서버를 등록하려면 args에 "--dry-run"을 추가하세요:
"args": [
"/Users/parthbansal/Desktop/claude code/linkedin-mcp/dist/server.js",
"--dry-run"
]dry-run 모드에서는 어떤 요청도 linkedin.com에 도달하지 않으며 어떤 게시물, 초대, 메시지, 지원서도 제출되지 않습니다. 즉 에이전트가 처음으로 도구 표면을 탐색하도록 할 때 가장 적자한 방법입니다.
서버가 허용하는 다른 플래그: --headless, --config <path>, --log-level <debug|info|warn|error>, -h / --help(--help는 stdout이 프로토콜을 전달하므로 stderr로 출력됩니다). 알 수 없는 플래그는 하드 오류입니다. 철자가 틀린 --dry-runn으로 인해 실제로 dry-run 모드가 아닌데 dry-run인 것으로 설마 믿게 되어서는 안 됩니다. 우선 순위는 항상 명령줄 플래그 > 환경 변수 > config.json > 기본값 순서입니다.
모든 환경 변수는 .env.example에 문서화되어 있으며, 모두 선택 사항이고 어떤 것도 자격 증명을 보관하지 않습니다.
개발
출력(emit) 없이 타입 검사만 수행:
npm run typecheck유닛 테스트 실행:
npm test저장 시 다시 컴파일:
npm run dev--dry-run
npm run dry-run은 빌드된 서버를 --dry-run으로 시작합니다. 이 모드에서는 브라우저가 linkedin.com 대신 fixtures/에 있는 로컬 HTML 픽스처를 가리키며, 모든 쓰기 흐름의 마지막 제출 클릭은 건너뛰기 때문에 확인된 작업이 유효성 검사, 할당량(quota) 검사, 대화형 상호작용 등 전체 코드 경로를 지난 뒤 실제 실행 직전에 멈춥니다. 실행 결과에는 dryRun: true가 포함되며, 미리보기에서도dry-run모드임을 경고합니다. linkedin_login은 사용할 수 없으며, linkedin_session_status는 항상 유효하다고 보고합니다.
픽스처는 프로필 페이지(연결 요청 버튼이 직접 있는 2촌 프로필, Connect 버튼이 "More" 메뉴 뒤에 있는 2촌 프로필, 메시저가 작동하는 1촌 연결의 세 가지 변형)와 메시지 보기, job search 채용 정보, Easy Apply와 외부 채용 추적 시스템 모두에 대한 직업 상세 페이지, 메시징, 초대, 연결을 다룹니다. 이것을 사용하여 개발하세요. 이 서버를 일상적으로 작업하면서 실제 계정을 만질 이유가 없습니다.
어떤 URL이 어떤 픽스처로 연결되는지는 src/fixtures.ts에 있는 정렬된 부분 문자열 규칙이 결정합니다. 이 순서는 중요합니다(load-bearing). /mynetwork/invite-connect/connections/는 /mynetwork/invit를 포함하고, /in/tomas-eriksen은 /untapped — so 광범위한 규칙보다는 구체적인 규칙이 위에 있어야 하며, tests/fixtures.test.ts가 그 순서를 고정합니다.
LinkedIn 없이 검증하기
npm run verify:dry이 검증은 컴파일된 서버를 --dry-run으로 시작한 뒤, 실제 MCP 클라이언트처럼 stdio로 JSON-RPC를 주고받으며 23개 케이스에서 모든 11개 도구를 실행합니다 — 모든 읽기 도구를 자iers 고정장치(fixture) 상대로, 모든 쓰기 도구를 전체 미리보기 → 확인 handshake로, 그리고 유효한 경우 거부사항(오래된 previewToken, 너무 긴 게시물, 2촌 프로필로 보낸 메시지, 외부 직원공고, dry-run 상태에서의 linkedin_login)을 검증합니다. 또한 레포지토 contract 검증: preview는 executed: false를, confirm은 executed: true를 보고해야 하며, stdout에는 JSON-RPC만 포함해야 합니다.
서버 스스로의 시작 줄에 dryRun: true가 확인되지 않으면 실행거부하므로 우연한 계정 대한 실수를 방지합니다. 정상 동작하는 Chromium이 필요하고 — session_status를 제외한 모든 것에서 브라우저 페이지를 엽니다 — 어떤 실패 시에도 non-zero 종료를 하며, 실패를 selector/logic 실패(서버가 잘못된 경우)와 harness 실패(검증 루프가 잘못된 경우)로 나눕니다.
테스트
이 테스트 스위트는 Vitest(tests/**/*.test.ts)로 실행되며 현재 비율 제한기(src/rateLimiter.ts), Config 로더(src/config.ts), 픽스처 라우팅(src/fixtures.ts) 를 3개 파일에서 75개 케이스로 다루고 있습니다. 세 렉트 모두 설계적으로 차단되어 있습니다(불렁 격리). 각 사례는 새 mkdtemp 디렉터리에서 실행되며, 환경은 process.env primaryIterator가 아니라 명시적으로 전달되고, ratelimiter의 클록, sleep, 난수는 RateLimiterDeps 통해 주입됩니다. 네트워크 호출 없이와 브라우저 로 marq가 없습니다.
Any browser is needed is in the above dry-run sweep, not in unit tests — that is why npm test finishes in under second and nothing needs to be installed other than node_modules.
편집하기 전에 알아 두면 좋은 규칙
**stdout에 무언가를 쓰면 안 됩니다. 평소 stdout is JSON-RPC channel, a stray byte breaks the framing and the client disconnects. All diagnostics use
Loggeron stderr.프로젝트는 ESM with
moduleResolution: "NodeNext"이므로, TypeScript source에서도 상대적 imports must exit.jsfor the**.
Example: The source says "* src/types.ts and src/errors.ts are the shared contract. src/selectors.ts contains only strings and pure functions." This means "** src/types.ts와 src/errors.ts는 공통 계약이며, src/selectors.ts는 문자열과 순수 함수만 가능합니다."
Troubleshooting
"diúl uh, and what's the next" — 그런 갈은 정상입니다
npm start을 직접 실행하면 stderr에 한 줄이 출력된 후 아무 소리 없이 대기합니다:
{"ts":"...","level":"info","msg":"linkedin-mcp ready on stdio","version":"0.1.0","tools":11,"dryRun":false,"headless":false}이는 정상 서버이 mari, hang이 아닙니다. MCP stdio 서버는 포트가 있는 데몬도 아니고 결과를 출력한 뒤 종료하는 CLI도 아닙니다. stdin에서 JSON-RPC 요청을 읽고 stdout으로 응답을 씁니다. 따라서 자체 소개 후에는 클라이언트가 말하기를 기다리며 대기합니다. 어떤 클라이언트도 접붙지 않았으면 할 말이 없습니다. 정상 서버는 stdout에 아무것도 출력하지 않는 편안을 유지 — 어떤 한 바이트라도 흘러들어가면 프로토콜 framing이 be desynchronized입니다.
So npm start is not how you use it. 서버를 MCP client에 등록()아래 하고 클라이언트가 시작하게 하세요. 서버가 대기 중에 응답하는 것을 검증하고 싶다면 요청 하나를 stdin에 붙여 넣고 Return 키를 눌러 보세요 — tools/list댓페이지가 바로 돌아옵니다:
printf '%s\n' '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | node dist/server.js --dry-runCtrl-C를 누르면 수동으로 시작한 서버가 중지됩니다. SIGINT/SIGTERM을 처리하고 shutting down로 레 응답을 기록합니다.
두 가지 관련 증상:
"Client shows 0 tools, or "server failed to start"." Almost all the time the path under
argsis wrong ordist/was not built.dist/server.js에 상대 경로가 아니라 절대 경로를 사용하고,npm run build를 실행하고, MCP 로그를 살펴보세요 — 서버의 stderr의 특반이 거기 들어오며,config_invalid또는 Node 모듈 해석 오류가 대기합니다.라인의 표시는 보이지만 호출마다 오류가 떨어집니다. 라인 내용을 확인하세요.
dryRun:true는 픽스처만 사용하며 실제 LinkedIn에 어떤 동작도 적용되지 않는다는 뜻입니다. 호출마다not_authenticated는 저장된 세션이 아직 없다는 뜻이므로linkedin_login실행하세요.
오류 코드
실패는 고윳값 code를 가진 MCP 도구 오류로 반환됩니다. 실제로 흔한 것:
verification_required — LinkedIn이 CAPTCHA, checkpoint 또는 인 supported wall을 띄웠습니다. 서버는 의도적으로 여기서 멈추며 절대 해결하지 않아야 합니다. 일반 브라우저에서 linkedin.com을 열어 직접 챌린지를 해결한 다음, linkedin_login을 다시 실행하세요. 이게 반복된다면 일일 한도(cap)를 낮춰야 하는 신호로 받아들이십시오.
session_expired — 저장된 세션이 더 이상 인증되지 않거나, LinkedIn이 피드를 로그인 페이지로 redirect했습니다. linkedin_login을 실행하고 직접 다시 로그인하세요. linkedin_session_status에 specific reason이 표시됩니다.
selector_not_found — 서버가 필요한 요소를 찾을 수 없었습니다. 이는 거의 항상 LinkedIn이 마크업을 변경했음을 뜻하며, 사용자가 잘못한 것이 아닙니다. 프로젝트의 모든 DOM 선택자는 **src/selectors.ts**에 모여 있으며, 이것이 유일한 유지보수 지점입니다. 관련 후보 목록을 찾아 새 선택자를 그 앞에 추가하고 다시 빌드하면 됩니다. 후보는 순서가 있는 목록이므로 추가된 선택자가 기존 선택자를 깨뜨리지 않습니다. --log-level debug로 실행하면 어떤 조회가 실패했는지 알 수 있습니다.
rate_limited — 일일 상한에 도달했습니다. 오류와 quota 블록에서 어느 상한인지, 언제 초기화되는지(다음 로컬 자정)를 확인할 수 있습니다. 기다리거나, config.json에서 그 상한을 올리면 됩니다. 다만 상한을 올리는 것은 계정이 제한되는 원인이 되는 행동이므로 신중하게 올리십시오.
external_application — 채용 공고가 지원자를 LinkedIn의 Easy Apply 양식 대신 외부 지원자 추적 시스템으로 넘기는 경우입니다. 서버는 제3자 사이트를 작성하지 않습니다. 브라우저에서 채용 공고를 열어 그쪽에서 지원하세요.
not_connected — 1촌 연결이 아닌 사람에게 메시지를 보내려고 했습니다. 먼저 연결 요청을 보내고 수락될 때까지 기다린 후 메시지를 보내세요. 서버는 InMail로 이 문제를 우회하지 않습니다.
만날 수 있는 다른 코드: not_authenticated(아직 디스크에 세션이 없음 — 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 자동화 또는 체크포인트 우회는 절대 없습니다.
다중 계정 지원 없음. 저장된 세션 하나, 계정 하나, 키보드 앞의 사용자 한 명만 지원합니다.
원격 미디어 업로드 없음. 서버는 게시물에 첨부할 URL을 가져오지 않습니다.
보안 및 개인정보 보호
저장되는 내용과 위치 모든 것은 이 머신의상태 디렉터리 아래에 저장됩니다. 기본값은 <cwd>/.linkedin-mcp/이며, LINKEDIN_MCP_STATE_DIR(또는 경로별로 LINKEDIN_MCP_STORAGE_STATE, LINKEDIN_MCP_USER_DATA_DIR, LINKEDIN_MCP_SCREENSHOT_DIR, LINKEDIN_MCP_COUNTERS)로 재정의할 수 있습니다:
경로 | 내용 |
| 사용자의 LinkedIn 쿠키 및 원본 저장내용 — **모드 |
| 영구 Chromium 프로필 디렉터리 |
| 오늘 로컬 날짜와 네 가지 작업 개수 |
| 진단 스크린샷, 로컬에만 기록됨 |
아무 곳에도 전송되지 않습니다. 텔레메트리, 애널리틱스, 크래시 리포팅 및 어떠한 종류의 외부 연락 전화도 없습니다. 유일한 네트워크 대상은 linkedin.com이며, 가 사용자 브라우저 세션을 통해서만 연결됩니다. --dry-run에서는 그 통신조차 일어나지 않습니다.
스크린샷은 로컬 전용입니다. 자신의 디버깅을 위해 스크린샷 디렉터리에 기록되며, 업로드되거나 도구 출력에 포함되지 않습니다.
로깅은 의도적으로 얇게 유지됩니다. 진단 정보는 stderr로 출력됩니다. 쿠키, storageState 내용, 이력서 파일 내용은 절대 로그에 기록되거나 직렬화되지 않습니다. 도구가 실패하면 인자가 아니라 오류 코드만 기록되므로, 메시지 본문이나 이력서 경로가 사용자의 클라이언트가 캡처한 stderr에 유출될 수 없습니다. redactConfig는 시작 시 서버가 기록하는 적용 구성(effective-configuration) 줄에서 절대 경로(및 홈셰어디렉터리)를 제거합니다.
.gitignore 보장 .gitignore는 .env 및 .env.*를 제외합니다(желая .env.example 을 유지), 전체 .mcp-linkedin/ 상태 디렉터리, 어느 경로에 있든 수준의 storageState.json, chromium-profile/, counters.json, screenshots/, *.log, 그리고 node_modules/, dist/, coverage/를 제외합니다. 추적되는 파일은 config.example.json와 사용자의 config.json뿐이며, 이들에는 상한값과 시간 초과 값 외에 다른 것이 포함되지 않습니다. storageState.json을 커밋하지 마십시오 — 이것은 실제 자격 증명입니다.
상태 디렉터리는 비밀처럼 취급하십시오. storageState.json을 읽을 수 있는 사람은 비밀번호나 2FA 코드 없이 LinkedIn에서 사용자처럼 활동할 수 있습니다. 유출된 것으로 의심되면 LinkedIn의 보안 설정에서 모든 세션 로그아웃한 후 파일을 삭제하고 다시 로그인하십시오.
Need examine output for translation errors and Markdown formatting. Let's test formatting.
We have table with header. Need ensure rows/columns same. Good.
Potential issue: "어떤 조회가 실패" vs "which lookup failed" - "어떤조회" okay.
Need "state directory" as "상태디렉터리" consistent.
Maybe "등 each element appears same number of times" is fine.
Please produce final with no commentary**selector_not_found** — 서버가 필요한 요소를 찾을 수 없었습니다. 이는 거의 항상 LinkedIn이 마크업을 변경했음을 뜻하며, 사용자가 잘못한 것이 아닙니다. 프로젝트의 모든 DOM 선택자는 **src/selectors.ts**에 모여 있으며, 이것이 유일한 유지보수 지점입니다. 관련 후보 목록을 찾아 새 선택자를 그 앞에 추가하고 다시 빌드하면 됩니다. 후보는 순서가 있는 목록이므로 추가된 선택자가 기존 선택자를 깨뜨리지 않습니다. --log-level debug로 실행하면 어떤 조회가 실패했는지 알 수 있습니다.
rate_limited — 일일 상한에 도달했습니다. 오류와 quota 블록을 통해 어느 상한인지와 언제 초기화되는지(다음 로컬 자정)를 알 수 있습니다. 기다리거나 config.json에서 그 상한을 올리면 됩니다. 다만 상한을 올리는 것은 계정이 제한될 수 있는 행동이므로, 신중하게 올리십시오.
external_application — 채용 공고가 지원자를 LinkedIn의 Easy Apply 양식 대신 외부 지원자 추적 시스템에게 넘기는 경우입니다. 서버는 제3자 사이트를 작성하지 않습니다. 브라우저에서 해당 채용 공고를 열고 그곳에서 선택하세요.
not_connected — 1촌 연결이 아닌 사람에게 메시지를 보내려고 했습니다. 먼저 연결 요청을 보내고 수락될 때까지 기다린 후 메시지를 보내세요. 서버는 InMail로 이 문제를 우회하지 않습니다.
다른 코드가 발생할 수 있습니다: not_authenticated (아직 디스크에 세션이 없음 — 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 자동화와 체크포인트 우회도 결코 없습니다.
다중 계정 지원 없음. 저장된 세션 하나, 계정 하나, 키보드 앞의 한 사람만 사용할 수 있습니다.
원격 미디어 업로드 없음. 서버는 게시물에 첨부할 URL을 가져오지 않습니다.
보안 및 개인정보 보호
저장되는 내용과 위치. 모든 것은 이 컴퓨터의 상태 디렉터리 아래에 저장됩니다. 기본값은 <cwd>/.linkedin-mcp/이며, LINKEDIN_MCP_STATE_DIR (또는 경로별로 LINKEDIN_MCP_STORAGE_STATE, LINKEDIN_MCP_USER_DATA_DIR, LINKEDIN_MCP_SCREENSHOT_DIR, LINKEDIN_MCP_COUNTERS)로 변경할 수 있습니다:
경로 | 내용 |
| 사용자의 LinkedIn 쿠키 및 원본 저장 데이터, **모드 |
| 영구 Chromium 프로필 디렉터리 |
| 오늘의 로컬 날짜와 4가지 작업 횟수 |
| 진단용 스크린샷, 로컬에만 기록 |
어디에도 전송되지 않습니다. 텔레메트리, 분석, 크래시 리포트, 어떤 종류의 연락도 없습니다. 유일한 네트워크 대상은 linkedin.com이며, 사용자의 브라우저 세션을 통해서만 연결됩니다. --dry-run에서는 그러한 연결조차 없습니다.
스크린샷은 로컬 전용입니다. 스크린샷은 사용자 자신의 디버깅을 위해 스크린샷 디렉터리에 기록되며 절대 업로드되거나 도구 출력에 포함되지 않습니다.
로깅은 의도적으로 최소화되어 있습니다. 진단 정보는 stderr로 전송됩니다. 쿠키, storageState 내용, 이력서 파일 내용은 절대 기록하거나 직렬화하지 않습니다. 도구 실패시 실제 인자 대신 오류 코드를 기록하므로, 메시지 본문이나 이력서 경로가 클라이언트가 캡처한 stderr에 유출될 수 없습니다. redactConfig는 서버의 시작 시 기록되는 구성 줄에서 절대 경로(및 홈 디렌터리)를 제거합니다.
Gitignore 보장. .gitignore는 .env 및 .env.* (.env.example는 유지), 전체 .linkedin-mcp/ 상태 디렴터删除 state.json, chromium-profile/, counters.json, screenshots/, *.log, 추가로 node_modules/, dist/, coverage/를 제외하십니다. 오직 config.example.json과 사용자의 config.json만 추적되며, 이 파일들은 한도와 타임아웃 외에는 아무것도 포함하지 않습니다. storageState.json은 절대로 커밋하지 마세요. — 이것은 실인증 정보입니다.
상태 디렉터리를 비밀처럼 취급하세요. storageState.json을 읽을 수 있는 사람은 비밀번호나 2FA 코드 없이 LinkedIn에서 사용자를 대신할 수 있습니다. 노출되었가 걱정되면 LinkedIn 자체 보안 설정에서 모든 세션을 로그아웃하고, 이 파일을 삭제한 후 다시 로그인하세요.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseBqualityBmaintenanceEnables full control over LinkedIn profiles through browser automation, allowing reading, editing, adding, removing entries, and publishing posts directly from conversations.41294MIT
- AlicenseNot gradedqualityDmaintenanceEnables fetching detailed LinkedIn profile data by automating a browser session with your LinkedIn cookie to access full profiles.214MIT
- AlicenseBqualityCmaintenanceEnables read-only extraction of LinkedIn profile data via MCP tools, using a local browser bridge for secure, authenticated access without exposing browser credentials.2MIT
- AlicenseAqualityBmaintenanceLets an AI assistant operate LinkedIn through an authenticated browser session, enabling profile management, posting, networking, messaging, job search, and automated applications.1003831MIT
Related MCP Connectors
Give AI agents the LinkedIn tools to find, qualify, engage, and follow up with prospects.
Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.
Let AI tools securely access your LinkedIn network and DMs
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/bansalsahab/linkdin-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server