Local-first, read-only MCP server for a LinkedIn "Get a copy of your data" export.
linkedin-network-mcp ingests a LinkedIn export directory or ZIP into one SQLite database, then exposes typed MCP tools for relationship search, message search, relationship summaries, and dormant connection discovery. It makes no network calls and does not use an LLM internally.
- A local SQLite database at
~/.linkedin-network-mcp/linkedin.dbby default. - A stdio MCP server for Claude Desktop, Claude Code, Cursor, Continue, or another MCP client.
- Eight read-only tools:
find_personget_personlist_connectionssearch_messageslist_conversationsget_conversationsummarize_relationshipdormant_connections
- Python 3.11 or newer.
- A LinkedIn data export directory or ZIP from LinkedIn's data export settings.
- For MCP client usage, an MCP-aware client such as Claude Desktop or Claude Code.
The project currently runs directly from source. PyPI/uvx usage is the intended distribution path, but local development is the supported path in this repo today.
Download your LinkedIn data from LinkedIn's data/privacy export page. LinkedIn may move or rename the page, but the export is usually called something like "Get a copy of your data" or "Download your data."
For the v0.1 relationship tools, the useful files are:
Profile.csvEmail Addresses.csvPhoneNumbers.csvPositions.csvEducation.csvSkills.csvProjects.csvConnections.csvInvitations.csvmessages.csvEndorsement_Given_Info.csvEndorsement_Received_Info.csv
You can pass either the unzipped export directory or the original ZIP to linkedin-network-mcp ingest.
From the repo root:
pip install -e .If you do not want to install the package, run commands with PYTHONPATH=src:
PYTHONPATH=src python3 -m linkedin_mcp --helpInstalled package:
linkedin-network-mcp ingest /path/to/linkedin-export --db ~/.linkedin-network-mcp/linkedin.dbWithout installing:
PYTHONPATH=src python3 -m linkedin_mcp ingest /path/to/linkedin-export --db ~/.linkedin-network-mcp/linkedin.dbThe ingest is a full replace. Re-running it drops and rebuilds the generated tables from the export snapshot.
Expected output shape:
{
"db_path": "/Users/you/.linkedin-network-mcp/linkedin.db",
"source_path": "/path/to/linkedin-export",
"source_rows": 1937,
"people": 1035,
"messages": 908,
"conversations": 325,
"warnings": []
}Warnings usually mean LinkedIn changed a CSV shape or a row had an unparseable date. The ingest warns on optional issues and fails only for required v0.1 columns.
linkedin-network-mcp doctor --db ~/.linkedin-network-mcp/linkedin.db --redact-previewWithout installing:
PYTHONPATH=src python3 -m linkedin_mcp doctor --db ~/.linkedin-network-mcp/linkedin.db --redact-previewdoctor reports schema version, row counts, message coverage, unresolved message senders, and a masked sample of tool output when --redact-preview is set.
The query command calls the same Python implementation used by the MCP tools.
linkedin-network-mcp query find_person '{"query":"alex","limit":5}' --db ~/.linkedin-network-mcp/linkedin.db
linkedin-network-mcp query search_messages '{"query":"roadmap","limit":5}' --db ~/.linkedin-network-mcp/linkedin.db
linkedin-network-mcp query dormant_connections '{"min_silent_days":180,"min_active_tie_strength":0.3,"limit":10}' --db ~/.linkedin-network-mcp/linkedin.dbWithout installing:
PYTHONPATH=src python3 -m linkedin_mcp query find_person '{"query":"alex","limit":5}' --db ~/.linkedin-network-mcp/linkedin.dblinkedin-network-mcp serve --db ~/.linkedin-network-mcp/linkedin.dbThe server uses stdio, so it is normally launched by your MCP client rather than run manually in a terminal.
Add this to your Claude Desktop MCP config.
macOS path:
~/Library/Application Support/Claude/claude_desktop_config.json
Local source checkout:
{
"mcpServers": {
"linkedin": {
"command": "python3",
"args": [
"-m",
"linkedin_mcp",
"serve",
"--db",
"/Users/<you>/.linkedin-network-mcp/linkedin.db"
],
"env": {
"PYTHONPATH": "/absolute/path/to/linkedin-network-mcp/src"
}
}
}
}Installed package:
{
"mcpServers": {
"linkedin": {
"command": "linkedin-network-mcp",
"args": [
"serve",
"--db",
"/Users/<you>/.linkedin-network-mcp/linkedin.db"
]
}
}
}Restart Claude Desktop after editing the config.
From this repo, after ingesting the database:
claude mcp add linkedin -- python3 -m linkedin_mcp serve --db ~/.linkedin-network-mcp/linkedin.dbIf using the source checkout without installing, make sure Claude Code launches the server with PYTHONPATH pointing at this repo's src directory.
find_person(query: str, limit: int = 10) -> list[Person]Search people by name, company, title, or LinkedIn public ID.
get_person(person_id: int) -> PersonDetailReturn one person with profile fields, tie strength, last interaction dates, and explainable signals.
list_connections(
company: str | None = None,
title_contains: str | None = None,
sort: "tie_strength" | "connected_on" | "last_message" | "last_touch" = "tie_strength",
limit: int = 50,
) -> list[Person]List first-degree connections, optionally filtered by company or title.
search_messages(
query: str,
person_id: int | None = None,
since: str | None = None,
until: str | None = None,
advanced_fts: bool = False,
limit: int = 20,
) -> list[MessageHit]Search DMs. By default, the query is treated as plain text and escaped for SQLite FTS5. Set advanced_fts=true only when you want to pass raw FTS5 syntax.
list_conversations(person_id: int, limit: int = 20) -> list[Conversation]List conversations involving a person.
get_conversation(conversation_id: str, limit: int = 100) -> list[Message]Return messages in one conversation in chronological order.
summarize_relationship(person_id: int) -> RelationshipSignalsReturn aggregated relationship signals: conversation count, first/last message dates, inbound/outbound counts, last subjects, and unanswered outbound state.
dormant_connections(
min_silent_days: int = 180,
min_active_tie_strength: float = 0.3,
limit: int = 30,
) -> list[DormantTie]List first-degree connections with meaningful historical signal and stale message activity.
After connecting the server, try:
- "Find people in my LinkedIn network who worked at Acme."
- "Search my LinkedIn messages for roadmap and summarize the relevant conversations."
- "List dormant connections I have not messaged in 180 days but had a strong prior relationship with."
- "Show the last conversation with person id 42."
The server returns structured data only. Any prose summary is generated by your MCP client.
- The server is local-first and read-only.
- It makes no outbound network calls.
- It does not send messages, post, scrape LinkedIn, or modify the export.
- Generated databases are ignored by
.gitignore. - Real LinkedIn export directories and ZIPs are ignored by
.gitignore. - v0.1 does not ingest high-sensitivity optional mirrors such as ad targeting, account logins, jobs, receipts, or imported phone-book contacts.
Do not commit real exports or generated SQLite databases.
Install the package:
pip install -e .Or run with:
PYTHONPATH=src python3 -m linkedin_mcp --helpLinkedIn may have changed the export format, or the export is missing a required file. Run ingest against the top-level export directory or ZIP, not an inner folder unless that inner folder contains the CSVs.
Check that the parent directory exists and is writable. The default path ~/.linkedin-network-mcp/linkedin.db is created automatically by the ingest command.
Run doctor first to confirm that messages were ingested:
linkedin-network-mcp doctor --db ~/.linkedin-network-mcp/linkedin.dbThen try a broader query or omit person_id.
Check that:
- The JSON config is valid.
- The
PYTHONPATHvalue is an absolute path when running from source. - The
--dbpath points to a database created byingest. - Claude Desktop was restarted after editing the config.
Run tests:
PYTHONPATH=src python3 -m pytest -qRun syntax checks:
PYTHONPATH=src python3 -m compileall -q src tests