We use analytics cookies and similar tools to see how LinkMCP is used. You can accept or reject them, and change your choice at any time in Cookie settings. Privacy Policy

LinkMCP for builders: connect your own agent

Your own code cannot open a browser window for a sign-in, so it connects to LinkMCP with an API key. Create the key once, keep it in an environment variable, and send it as a Bearer header. The snippets below connect the most used agent SDKs to the LinkMCP server and its 35 LinkedIn tools.

1. Create an API key

  1. Sign in to LinkMCP. A new account starts with the 7-day free trial.
  2. Open API Keys and click New access key.
  3. Give the key a label, choose when it expires (30 days, 90 days or 1 year), and click Create key.
  4. Copy the key. LinkMCP shows it only once. Keep it in an environment variable such as LINKMCP_TOKEN or in your secret store.

The key acts for your LinkMCP account and the LinkedIn account that you connected. Revoke a key on the API Keys page when you no longer need it.

Send every request to this URL, with the header Authorization: Bearer <your API key>. The server speaks MCP over Streamable HTTP (JSON-RPC 2.0).

https://app.linkmcp.io/api/mcp?ref=builders-url

3. Connect your agent

The code reads the key from LINKMCP_TOKEN and asks for a profile summary, which works in the free trial.

Claude Agent SDK (Python)

The Agent SDK does not run a browser sign-in, so the key goes in headers. The snippet allows only the tools that read. With dontAsk, the agent may not call any other tool. To let it write, remove dontAsk and approve each call in can_use_tool.

# pip install claude-agent-sdk
import asyncio
import os

from claude_agent_sdk import ClaudeAgentOptions, ResultMessage, query

PROMPT = "Look up [profile URL] on LinkedIn and summarize the current role."

READ_TOOLS = [
    "mcp__linkmcp__linkedin_get_*",
    "mcp__linkmcp__linkedin_bulk_get_*",
    "mcp__linkmcp__linkedin_list_*",
    "mcp__linkmcp__linkedin_search_*",
    "mcp__linkmcp__linkedin_who_am_i",
    "mcp__linkmcp__find_email",
    "mcp__linkmcp__validate_email",
    "mcp__linkmcp__find_mobile",
]

options = ClaudeAgentOptions(
    mcp_servers={
        "linkmcp": {
            "type": "http",
            "url": "https://app.linkmcp.io/api/mcp?ref=builders-claude-agent-sdk-py",
            "headers": {"Authorization": f"Bearer {os.environ['LINKMCP_TOKEN']}"},
        }
    },
    # Never allow "mcp__linkmcp__*": it approves every write tool too.
    allowed_tools=READ_TOOLS,
    permission_mode="dontAsk",
)


async def main():
    async for message in query(prompt=PROMPT, options=options):
        if isinstance(message, ResultMessage) and message.subtype == "success":
            print(message.result)


asyncio.run(main())

Source: Claude Agent SDK docs.

Claude Agent SDK (TypeScript)

The same agent in TypeScript. To let it write, remove dontAsk and approve each call in canUseTool.

// npm install @anthropic-ai/claude-agent-sdk
import { query } from "@anthropic-ai/claude-agent-sdk";

for await (const message of query({
  prompt: "Look up [profile URL] on LinkedIn and summarize the current role.",
  options: {
    mcpServers: {
      linkmcp: {
        type: "http",
        url: "https://app.linkmcp.io/api/mcp?ref=builders-claude-agent-sdk-ts",
        headers: { Authorization: `Bearer ${process.env.LINKMCP_TOKEN}` },
      },
    },
    // Read tools only. Never allow "mcp__linkmcp__*".
    allowedTools: [
      "mcp__linkmcp__linkedin_get_*",
      "mcp__linkmcp__linkedin_bulk_get_*",
      "mcp__linkmcp__linkedin_list_*",
      "mcp__linkmcp__linkedin_search_*",
      "mcp__linkmcp__linkedin_who_am_i",
      "mcp__linkmcp__find_email",
      "mcp__linkmcp__validate_email",
      "mcp__linkmcp__find_mobile",
    ],
    permissionMode: "dontAsk",
  },
})) {
  if (message.type === "result" && message.subtype === "success") {
    console.log(message.result);
  }
}

Source: Claude Agent SDK docs.

OpenAI Agents SDK (Python)

MCPServerStreamableHttp sends the header with every request. Its session timeout is 5 seconds by default, and a LinkedIn call can take longer, so the snippet sets 60.

# pip install openai-agents
import asyncio
import os

from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp, create_static_tool_filter

PROMPT = "Look up [profile URL] on LinkedIn and summarize the current role."

# The LinkMCP tools that only read (MCP annotation readOnlyHint: true).
# The agent gets no tool that writes to LinkedIn.
READ_TOOLS = [
    "find_email", "find_mobile", "linkedin_bulk_get_companies",
    "linkedin_bulk_get_profiles", "linkedin_get_company",
    "linkedin_get_company_posts", "linkedin_get_connections",
    "linkedin_get_conversation_messages", "linkedin_get_event",
    "linkedin_get_job", "linkedin_get_my_engagement",
    "linkedin_get_my_saved_items", "linkedin_get_nested_comments",
    "linkedin_get_person_posts", "linkedin_get_post_analytics",
    "linkedin_get_post_comments", "linkedin_get_post_reactions",
    "linkedin_get_profile", "linkedin_get_profile_views",
    "linkedin_get_shared_connections", "linkedin_list_company_pages",
    "linkedin_list_connection_requests", "linkedin_list_conversations",
    "linkedin_list_events", "linkedin_list_scheduled_posts",
    "linkedin_search_jobs", "linkedin_search_people",
    "linkedin_search_sales_navigator", "linkedin_who_am_i", "validate_email",
]


async def main():
    async with MCPServerStreamableHttp(
        name="linkmcp",
        params={
            "url": "https://app.linkmcp.io/api/mcp?ref=builders-openai-agents-py",
            "headers": {"Authorization": f"Bearer {os.environ['LINKMCP_TOKEN']}"},
            "timeout": 30,
        },
        client_session_timeout_seconds=60,
        tool_filter=create_static_tool_filter(allowed_tool_names=READ_TOOLS),
    ) as linkmcp:
        agent = Agent(
            name="LinkedIn assistant",
            instructions="Use the LinkMCP tools to answer.",
            mcp_servers=[linkmcp],
        )
        result = await Runner.run(agent, PROMPT)
        print(result.final_output)


asyncio.run(main())

Source: OpenAI Agents SDK docs.

OpenAI Agents SDK (JavaScript)

In JavaScript, the header goes in requestInit, the fetch options of every request. Connect the server before the run and close it after.

// npm install @openai/agents
import {
  Agent,
  MCPServerStreamableHttp,
  createMCPToolStaticFilter,
  run,
} from "@openai/agents";

const PROMPT = "Look up [profile URL] on LinkedIn and summarize the current role.";

// The LinkMCP tools that only read (MCP annotation readOnlyHint: true).
// The agent gets no tool that writes to LinkedIn.
const READ_TOOLS = [
  "find_email", "find_mobile", "linkedin_bulk_get_companies",
  "linkedin_bulk_get_profiles", "linkedin_get_company",
  "linkedin_get_company_posts", "linkedin_get_connections",
  "linkedin_get_conversation_messages", "linkedin_get_event",
  "linkedin_get_job", "linkedin_get_my_engagement",
  "linkedin_get_my_saved_items", "linkedin_get_nested_comments",
  "linkedin_get_person_posts", "linkedin_get_post_analytics",
  "linkedin_get_post_comments", "linkedin_get_post_reactions",
  "linkedin_get_profile", "linkedin_get_profile_views",
  "linkedin_get_shared_connections", "linkedin_list_company_pages",
  "linkedin_list_connection_requests", "linkedin_list_conversations",
  "linkedin_list_events", "linkedin_list_scheduled_posts",
  "linkedin_search_jobs", "linkedin_search_people",
  "linkedin_search_sales_navigator", "linkedin_who_am_i", "validate_email",
];

async function main() {
  const linkmcp = new MCPServerStreamableHttp({
    name: "linkmcp",
    url: "https://app.linkmcp.io/api/mcp?ref=builders-openai-agents-js",
    requestInit: {
      headers: { Authorization: `Bearer ${process.env.LINKMCP_TOKEN}` },
    },
    toolFilter: createMCPToolStaticFilter({ allowed: READ_TOOLS }),
  });
  const agent = new Agent({
    name: "LinkedIn assistant",
    instructions: "Use the LinkMCP tools to answer.",
    mcpServers: [linkmcp],
  });
  try {
    await linkmcp.connect();
    const result = await run(agent, PROMPT);
    console.log(result.finalOutput);
  } finally {
    await linkmcp.close();
  }
}

main().catch(console.error);

Source: OpenAI Agents SDK docs.

Vercel AI SDK

createMCPClient comes from the @ai-sdk/mcp package. Without stopWhen, generateText stops after the first tool call and returns no answer. Close the client when you are done.

// npm install ai @ai-sdk/mcp
import { createMCPClient } from "@ai-sdk/mcp";
import { generateText, isStepCount } from "ai";

// The LinkMCP tools that only read (MCP annotation readOnlyHint: true).
// The agent gets no tool that writes to LinkedIn.
const READ_TOOLS = [
  "find_email", "find_mobile", "linkedin_bulk_get_companies",
  "linkedin_bulk_get_profiles", "linkedin_get_company",
  "linkedin_get_company_posts", "linkedin_get_connections",
  "linkedin_get_conversation_messages", "linkedin_get_event",
  "linkedin_get_job", "linkedin_get_my_engagement",
  "linkedin_get_my_saved_items", "linkedin_get_nested_comments",
  "linkedin_get_person_posts", "linkedin_get_post_analytics",
  "linkedin_get_post_comments", "linkedin_get_post_reactions",
  "linkedin_get_profile", "linkedin_get_profile_views",
  "linkedin_get_shared_connections", "linkedin_list_company_pages",
  "linkedin_list_connection_requests", "linkedin_list_conversations",
  "linkedin_list_events", "linkedin_list_scheduled_posts",
  "linkedin_search_jobs", "linkedin_search_people",
  "linkedin_search_sales_navigator", "linkedin_who_am_i", "validate_email",
];

const linkmcp = await createMCPClient({
  transport: {
    type: "http",
    url: "https://app.linkmcp.io/api/mcp?ref=builders-vercel-ai-sdk-ts",
    headers: { Authorization: `Bearer ${process.env.LINKMCP_TOKEN}` },
  },
});

try {
  const tools = Object.fromEntries(
    Object.entries(await linkmcp.tools()).filter(([name]) =>
      READ_TOOLS.includes(name),
    ),
  );
  const { text } = await generateText({
    model: "anthropic/claude-sonnet-5.5", // or any model the AI SDK supports
    tools,
    stopWhen: isStepCount(5),
    prompt: "Look up [profile URL] on LinkedIn and summarize the current role.",
  });
  console.log(text);
} finally {
  await linkmcp.close();
}

Source: AI SDK docs.

LangChain and LangGraph (Python)

MultiServerMCPClient from langchain-mcp-adapters turns the LinkMCP tools into LangChain tools. create_agent builds a LangGraph agent, and the same tools also work in your own graph.

# pip install langchain langchain-mcp-adapters langchain-anthropic
import asyncio
import os

from langchain.agents import create_agent
from langchain_mcp_adapters.client import MultiServerMCPClient

PROMPT = "Look up [profile URL] on LinkedIn and summarize the current role."

# The LinkMCP tools that only read (MCP annotation readOnlyHint: true).
# The agent gets no tool that writes to LinkedIn.
READ_TOOLS = [
    "find_email", "find_mobile", "linkedin_bulk_get_companies",
    "linkedin_bulk_get_profiles", "linkedin_get_company",
    "linkedin_get_company_posts", "linkedin_get_connections",
    "linkedin_get_conversation_messages", "linkedin_get_event",
    "linkedin_get_job", "linkedin_get_my_engagement",
    "linkedin_get_my_saved_items", "linkedin_get_nested_comments",
    "linkedin_get_person_posts", "linkedin_get_post_analytics",
    "linkedin_get_post_comments", "linkedin_get_post_reactions",
    "linkedin_get_profile", "linkedin_get_profile_views",
    "linkedin_get_shared_connections", "linkedin_list_company_pages",
    "linkedin_list_connection_requests", "linkedin_list_conversations",
    "linkedin_list_events", "linkedin_list_scheduled_posts",
    "linkedin_search_jobs", "linkedin_search_people",
    "linkedin_search_sales_navigator", "linkedin_who_am_i", "validate_email",
]


async def main():
    client = MultiServerMCPClient(
        {
            "linkmcp": {
                "transport": "http",
                "url": "https://app.linkmcp.io/api/mcp?ref=builders-langchain-py",
                "headers": {"Authorization": f"Bearer {os.environ['LINKMCP_TOKEN']}"},
            }
        }
    )
    tools = [t for t in await client.get_tools() if t.name in READ_TOOLS]
    agent = create_agent("claude-sonnet-5", tools)  # or any chat model LangChain supports
    result = await agent.ainvoke({"messages": [{"role": "user", "content": PROMPT}]})
    print(result["messages"][-1].content)


asyncio.run(main())

Source: LangChain docs.

Google ADK (Python)

McpToolset connects over Streamable HTTP and sends the header. ADK waits 5 seconds for the connection by default; the snippet sets 30.

# pip install google-adk
import os

from google.adk.agents import LlmAgent
from google.adk.tools.mcp_tool import McpToolset
from google.adk.tools.mcp_tool.mcp_session_manager import StreamableHTTPConnectionParams

# The LinkMCP tools that only read (MCP annotation readOnlyHint: true).
# The agent gets no tool that writes to LinkedIn.
READ_TOOLS = [
    "find_email", "find_mobile", "linkedin_bulk_get_companies",
    "linkedin_bulk_get_profiles", "linkedin_get_company",
    "linkedin_get_company_posts", "linkedin_get_connections",
    "linkedin_get_conversation_messages", "linkedin_get_event",
    "linkedin_get_job", "linkedin_get_my_engagement",
    "linkedin_get_my_saved_items", "linkedin_get_nested_comments",
    "linkedin_get_person_posts", "linkedin_get_post_analytics",
    "linkedin_get_post_comments", "linkedin_get_post_reactions",
    "linkedin_get_profile", "linkedin_get_profile_views",
    "linkedin_get_shared_connections", "linkedin_list_company_pages",
    "linkedin_list_connection_requests", "linkedin_list_conversations",
    "linkedin_list_events", "linkedin_list_scheduled_posts",
    "linkedin_search_jobs", "linkedin_search_people",
    "linkedin_search_sales_navigator", "linkedin_who_am_i", "validate_email",
]

linkmcp = McpToolset(
    connection_params=StreamableHTTPConnectionParams(
        url="https://app.linkmcp.io/api/mcp?ref=builders-google-adk-py",
        headers={"Authorization": f"Bearer {os.environ['LINKMCP_TOKEN']}"},
        timeout=30,
    ),
    tool_filter=READ_TOOLS,
)

root_agent = LlmAgent(
    model="gemini-flash-latest",
    name="linkedin_assistant",
    instruction="Use the LinkMCP tools for LinkedIn tasks.",
    tools=[linkmcp],
)

Source: ADK docs.

n8n (MCP Client Tool node)

Attach an MCP Client Tool node to an AI Agent node. Paste the URL into Endpoint, set Server Transport to HTTP Streamable and Authentication to Bearer Auth, and put your API key in the Bearer Token field of the credential. Set Tools to Include to Selected, and select only the tools that read (the names in READ_TOOLS in the snippets above). Then the agent gets no tool that writes to LinkedIn. How to Connect LinkedIn to n8n has the full steps.

https://app.linkmcp.io/api/mcp?ref=builders-n8n-url

Source: n8n docs.

Share the setup with your team (Claude Code)

Claude Code reads .mcp.json in the project folder and fills in ${LINKMCP_TOKEN} from each person's environment. Commit the file, and every teammate uses their own key. Claude Code asks once before it uses a server from .mcp.json.

Since version 2.1.283, Claude Code starts in auto mode, where a second model, not a person, approves tool calls. An ask rule still makes it ask you: add each LinkedIn write tool to permissions.ask in .claude/settings.json, for example mcp__linkmcp__linkedin_send_message.

{
  "mcpServers": {
    "linkmcp": {
      "type": "http",
      "url": "https://app.linkmcp.io/api/mcp?ref=builders-mcp-json",
      "headers": {
        "Authorization": "Bearer ${LINKMCP_TOKEN}"
      }
    }
  }
}

Add an approval step before any LinkedIn write

Nobody approves a tool call that your code makes, unless you add that step. These tools act on LinkedIn as you: linkedin_send_message, linkedin_mark_conversation_read, linkedin_manage_connection_request, linkedin_send_connection_request, linkedin_create_post, linkedin_comment_on_post, linkedin_react_to_post, and any other tool whose MCP annotation readOnlyHint is not true. Every LinkMCP tool has this annotation, and only the tools that read have it set to true. Before your agent calls a write tool, show the action to a person and wait for an OK, or give the agent only the read tools.

  • Claude Agent SDK: allow only the read tools (the snippets do) and approve the others in can_use_tool (Python) or canUseTool (TypeScript). Permissions
  • OpenAI Agents SDK (Python): pass require_approval={"always": {"tool_names": [...]}} to the MCP server, then approve or reject each item of result.interruptions. Human in the loop
  • OpenAI Agents SDK (JavaScript): a local MCP server has no approval option yet. Give the agent only the read tools with toolFilter: createMCPToolStaticFilter({ allowed: READ_TOOLS }), as the snippet does. MCP guide
  • Vercel AI SDK: set toolApproval on generateText or streamText, for example 'user-approval' for every tool whose readOnlyHint is not true. Tool approvals
  • LangChain and LangGraph: add HumanInTheLoopMiddleware(interrupt_on={...}) with a checkpointer. Human in the loop
  • Google ADK: pass require_confirmation=True to McpToolset (an experimental ADK feature), or put the write tools in a second toolset that asks. Tool confirmation

Limits

Every action through your LinkedIn account has a minimum gap, a cap per minute and a daily limit, and every plan has a hard monthly usage cap. The numbers are in Rate limits and pacing. When a limit stops a call, the tool answers with an error that says how long to wait, so do not retry in a loop. As with any tool that acts on LinkedIn for you, a restriction cannot be ruled out completely; these limits keep the risk low.

What it costs

LinkMCP starts with a 7-day free trial, and you do not need a credit card. In the trial, profile and company lookups, posts and post comments, and email and phone lookups work. Searches, messages, posting and Sales Navigator act through your own LinkedIn account, and connecting it needs a paid plan. Starter costs $19 per month. New trials get their first month of Starter for $9.50.

More

To use LinkMCP in an AI client instead, see Install LinkMCP in any AI client. Every tool and its inputs are in the tool reference.