Skip to main content
Glama
madalincmc

Claude LinkedIn Studio

by madalincmc

Claude LinkedIn Studio

Write, polish and publish LinkedIn posts without leaving Claude.

Share an idea in a chat. Claude shapes it into a post in your voice, checks it against your real experience, and publishes it to your LinkedIn profile when you say so.

License: MIT Next.js MCP Deploy on Vercel

What you can do · How it works · Setup · Using it · Security · FAQ


What you can do

  • 💡 Share ideas straight from Claude. A rough thought, a few bullets, a voice-note transcript, a link you're reacting to. Drop it into a chat from your desktop or phone.

  • ✍️ Polish them with Claude. Get one focused draft, give feedback ("shorter", "less formal", "lead with the second point"), and iterate until it's right.

  • 🎙️ Sound like yourself. Your role, opinions, writing voice, real stories and boundaries live in five reference files plus Project instructions. Claude writes from them, and can interview you to fill them in.

  • 🛡️ Never post invented experience. Claude follows strict authenticity rules: no made-up anecdotes, results, numbers or opinions. When a story is missing, it asks for one instead of making it up.

  • 🔎 Research topics. Ask Claude to look into what's new in your field and draft a post from it, with sources listed and facts attributed.

  • 🗓️ Schedule and automate. Set up recurring tasks, for example "every Monday, research my topics and draft one post for review", or go fully hands-off.

  • 🚀 Publish in one sentence. Say "publish it" and the exact approved text goes to your LinkedIn feed. Delete a post the same way.

Related MCP server: linkedin-mcp-server

How it works

flowchart LR
    You(["You"]) -- "ideas, feedback,<br/>'publish it'" --> Claude
    subgraph Claude["Claude Project"]
        I["Instructions<br/>(authenticity rules)"]
        K["Your 5 reference files<br/>(identity, opinions, voice,<br/>stories, boundaries)"]
    end
    Claude -- "MCP tools<br/>publish_post · delete_post · linkedin_status" --> App
    subgraph App["Your deployment (Vercel)"]
        M["/api/mcp<br/>OAuth 2.1 or bearer token"]
        DB[("Neon Postgres<br/>encrypted LinkedIn token,<br/>publish log")]
        C["Console<br/>(Google sign-in)"]
    end
    App -- "Posts API" --> LI["LinkedIn"]

There are two halves:

  1. The Claude side is a Claude Project that holds your instructions and reference files. Drafting, feedback, research and scheduling all happen there. No extra AI API keys and no per-token bills from this project.

  2. The connector is a small Next.js app that you deploy to your own Vercel account. It holds your LinkedIn connection (encrypted) and exposes a remote MCP server with three tools:

Tool

What it does

linkedin_status

Shows whether LinkedIn is connected, which account, and when access expires.

publish_post

Publishes the exact approved text to your feed (public or connections only). Accidental double posts are blocked.

delete_post

Deletes a post that was published through the connector.

Everything runs on your own accounts: your Vercel project, your database, your Google sign-in, your LinkedIn app. Nobody else, including the author of this repo, can see your data or post for you.

Setup

Plan on about 45 minutes the first time. Every step is concrete. Where a value is specific to you, it's written as <like-this>.

What you need

Account / tool

Why

Cost

GitHub

To fork the repo and deploy from it

Free

Vercel

Hosts the connector

Hobby plan is free

Neon (through the Vercel Marketplace)

Postgres database

Free plan

Google Cloud

"Sign in with Google" for your console

Free

LinkedIn developer app

Permission to post as you

Free, but requires a LinkedIn Page (step 6)

Claude

Drafting and publishing. Projects and custom connectors are needed; see Anthropic's help center for current plan availability.

Your Claude plan

Node.js 22+ and the Vercel CLI

Local commands: npm i -g vercel

Free

1. Fork and clone

  1. Click Fork at the top of this page. Your copy can be private.

  2. Clone your fork and install dependencies:

    git clone https://github.com/<your-github-user>/claude-linkedin-studio.git
    cd claude-linkedin-studio
    npm install

2. Deploy to Vercel

You deploy first because you need your app's URL for the Google and LinkedIn setup.

  1. Go to vercel.com/new, import your fork and click Deploy. The defaults are correct for this Next.js app. The first deploy builds fine without any configuration.

  2. Note your production URL, for example https://<your-project>.vercel.app. The rest of this guide calls it <APP_URL>.

  3. Link your local folder to the project:

    vercel link

3. Add the database

  1. In the Vercel dashboard, open your project and go to Storage → Create Database → Neon (Serverless Postgres). Choose the free plan and a region near you, and connect it to all environments.

  2. Vercel adds DATABASE_URL (pooled) and DATABASE_URL_UNPOOLED (direct) to your project automatically.

  3. Pull them locally and create the tables:

    vercel env pull .env.local
    npm run db:migrate

    You should see migrations applied successfully.

4. Generate your secrets

Run these three commands and keep the output somewhere safe, like a password manager. You'll paste the values in step 7.

# SESSION_SECRET: signs your console session cookie
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"

# TOKEN_ENCRYPTION_KEY: encrypts your LinkedIn token in the database
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"

# MCP_TOKEN: optional bearer token for Claude Code
node -e "console.log(require('crypto').randomBytes(32).toString('base64url'))"

5. Create the Google sign-in client

The console is protected by Google sign-in and accepts exactly one account: yours.

  1. Open the Google Cloud console, click the project picker and choose New project. Name it, for example, claude-linkedin-studio.

  2. Go to Google Auth Platform. If you're asked to configure it, click Get started and fill in:

    • App name: anything, e.g. Claude LinkedIn Studio

    • User support email: your email

    • Audience: External

    • Contact email: your email

  3. Open Audience → Test users → Add users and add the Google account you'll sign in with. The app can stay in Testing mode. You don't need to publish it or get it verified.

  4. Open Clients → Create client:

    • Application type: Web application

    • Authorized redirect URIs:

      • <APP_URL>/api/auth/google/callback

      • http://localhost:3000/api/auth/google/callback (for local development)

  5. Click Create, then copy the Client ID and the Client secret. Google shows the secret only once.

6. Create the LinkedIn app

  1. LinkedIn requires every developer app to be associated with a LinkedIn Page. If you don't have one, create one at linkedin.com/company/setup/new. A simple page for yourself or a side project is enough.

  2. Go to linkedin.com/developers/apps → Create app. Enter a name, select your Page, upload a logo and accept the terms.

  3. If LinkedIn asks you to verify the app with your Page, open Settings → Verify and approve the request as the Page admin.

  4. Open the Products tab and request access to both of these. They're self-serve and usually approved instantly:

    • Sign In with LinkedIn using OpenID Connect

    • Share on LinkedIn

  5. Open the Auth tab:

    • Under Authorized redirect URLs for your app, add:

      • <APP_URL>/api/linkedin/callback

      • http://localhost:3000/api/linkedin/callback

    • Copy the Client ID and the Primary Client Secret.

    • Under OAuth 2.0 scopes, check that openid, profile and w_member_social are listed.

7. Configure environment variables

In the Vercel dashboard, open Project → Settings → Environment Variables. Add each variable below and tick both Production and Development, so local development uses the same values. Keep the default type: values are encrypted at rest. Vercel's Sensitive type can't be pulled for local development, so use it only for Production values you don't need locally.

Variable

Value

SESSION_SECRET

From step 4

TOKEN_ENCRYPTION_KEY

From step 4

MCP_TOKEN

From step 4 (optional; only for Claude Code)

GOOGLE_CLIENT_ID

From step 5

GOOGLE_CLIENT_SECRET

From step 5

OWNER_EMAIL

The Google account allowed to sign in, e.g. <you@gmail.com>

LINKEDIN_CLIENT_ID

From step 6

LINKEDIN_CLIENT_SECRET

From step 6

vercel env add SESSION_SECRET production
vercel env add SESSION_SECRET development
# …repeat for each variable. The CLI prompts for the value, so it doesn't end up in your shell history.

Then apply the changes:

  1. Redeploy: Deployments → ⋯ on the latest deployment → Redeploy.

  2. Refresh your local env file with vercel env pull .env.local.

8. Sign in and connect LinkedIn

  1. Open <APP_URL> and click Sign in with Google. Use the account from OWNER_EMAIL.

  2. Click Connect LinkedIn and approve the access request.

  3. The console now shows Connected as <your name> and when access expires.

9. Connect Claude

claude.ai (web, desktop and mobile)

  1. Open Settings → Connectors → Add custom connector.

  2. Enter any name, e.g. LinkedIn, and the URL <APP_URL>/api/mcp.

  3. Claude sends you to your console's consent screen. Sign in with Google if asked, then click Allow.

  4. In a new chat, open the tools menu and make sure the connector is enabled. Ask "What's my LinkedIn status?" to test it.

Claude Code (optional)

claude mcp add --transport http linkedin <APP_URL>/api/mcp \
  --header "Authorization: Bearer <MCP_TOKEN>"

Your console's Claude setup section shows both options with your real URL filled in.

10. Create your Claude Project and set your voice

  1. In claude.ai, go to Projects → New project, for example LinkedIn.

  2. Paste the full contents of claude-workspace/instructions.md into the Project Instructions.

  3. Upload the five files from claude-workspace/knowledge/ to the Project's knowledge (shown as Files or Context, depending on your Claude version).

  4. Start a chat in the Project and say "Interview me". Claude asks a few questions per file and gives you the filled-in content to replace each template. Or edit the files yourself; each one explains what goes where.

  5. Optional: run the checks in claude-workspace/test-prompts.md to confirm Claude doesn't invent anything about you.

11. Publish your first post

In a new chat in the Project:

You: Idea: most "AI transformation" projects fail on change management, not on the model. Short insight.

Claude: (a draft in your voice)

You: Shorter, and drop the last line.

Claude: (revised draft)

You: Publish it, connections only.

Claude: Published: https://www.linkedin.com/feed/update/urn:li:share:…

That's it. The post shows up in your console's Publish history.

Using it

Everyday flow

You say

Claude does

"Here's a thought: …"

Drafts one post around your point, in your voice and language

"Opinion, provocative but respectful"

Changes type and tone (types: Short Insight, Opinion, Educational, Lesson Learned, Question; lengths: Short, Medium, Long)

"Less formal, no emojis"

Revises, and asks whether to save it as a permanent preference

"Here's a story from last year: …"

Uses it, and offers to add it to your Story Bank

"Publish it" / "Publish to connections only"

Calls publish_post with the exact approved text and returns the link

"Delete that post"

Calls delete_post after confirming which one

Research

"Research what changed in AI coding assistants this month and draft an educational post."

Claude searches the web, lists its sources with links after the draft, attributes facts in the post, and keeps the opinion yours: it comes from your Point of View file or the chat, never from the research.

Scheduling and automation

Use your Claude Project's Scheduled tasks (or scheduled routines in Claude Code, via /schedule) to put the work on a schedule. Some ideas:

  • Weekly ideas: "Every Monday at 9:00: research what's new in my topics and draft one post for my review."

  • Story mining: "Every Friday: ask me one question about my week that could become a Lesson Learned post."

  • Fully automatic: "Every Tuesday and Thursday at 8:30: research, draft and publish one Short Insight."

By default, scheduled tasks stop at a draft for you to review. They publish only if the task explicitly says so, and never if the draft still has an unresolved [confirm: …] marker.

Keeping your voice up to date

The reference files are the source of truth. Keep them in your fork under claude-workspace/knowledge/, edit them as you grow, and re-upload a file to the Project whenever you change it.

Security

This app can post publicly as you, so it's locked down by default:

  • One owner. The console uses Google sign-in (OpenID Connect with PKCE, state and nonce). The ID token is verified against Google's keys, and only OWNER_EMAIL with a verified email gets in. There's no password to guess.

  • The MCP endpoint requires auth. claude.ai uses OAuth 2.1: discovery metadata, Dynamic Client Registration, authorization code with PKCE (S256), and rotating refresh tokens. Redirect URIs are limited to Claude's callback and localhost. Claude Code can use the static MCP_TOKEN instead, which is compared in constant time. Anything else gets 401 before a tool runs.

  • Secrets stay server-side. The LinkedIn token is encrypted at rest with AES-256-GCM. OAuth codes and tokens are stored only as SHA-256 hashes. No secret reaches the browser.

  • Minimal LinkedIn scopes: openid profile w_member_social.

  • Kill switches:

    • Disconnect deletes the LinkedIn token immediately.

    • Revoke under Authorized apps cuts off a claude.ai connection.

    • Rotating MCP_TOKEN cuts off Claude Code.

  • Every publish attempt is logged in publish_log. Identical text within 24 hours is refused unless forced, so retries can't double-post.

Secret

How to rotate

Side effect

SESSION_SECRET

Generate a new value (step 4), update it in Vercel, redeploy

Signs out all browser sessions

MCP_TOKEN

Same

Re-add the connector in Claude Code with the new token

TOKEN_ENCRYPTION_KEY

Same

The stored LinkedIn token becomes unreadable, so click Connect LinkedIn again

GOOGLE_CLIENT_SECRET

Add a new secret on the Google client, update Vercel, redeploy, then delete the old secret

None

LINKEDIN_CLIENT_SECRET

Generate a new secret on the LinkedIn Auth tab, update Vercel, redeploy

Reconnect LinkedIn

Configuration reference

Variable

Required

Description

DATABASE_URL

Yes

Pooled Postgres URL (added by the Neon integration)

DATABASE_URL_UNPOOLED

For migrations

Direct Postgres URL (added by the Neon integration)

SESSION_SECRET

Yes

At least 32 characters; signs the session cookie

TOKEN_ENCRYPTION_KEY

Yes

32 bytes, base64; encrypts the LinkedIn token

GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET

Yes

Google OAuth client for the console login

OWNER_EMAIL

Yes

The only Google account allowed to sign in

LINKEDIN_CLIENT_ID / LINKEDIN_CLIENT_SECRET

Yes

LinkedIn developer app credentials

MCP_TOKEN

No

Static bearer token for Claude Code

LINKEDIN_REDIRECT_URI

No

Overrides <origin>/api/linkedin/callback

LINKEDIN_API_VERSION

No

LinkedIn API version (YYYYMM); defaults to the version in src/lib/linkedin/posts.ts

Local development

vercel env pull .env.local   # Development values from step 7
npm run dev                  # http://localhost:3000
  • The localhost redirect URIs from steps 5 and 6 make Google sign-in and LinkedIn connect work locally.

  • Local development uses the same database as production unless you create a separate Neon branch and point DATABASE_URL* at it.

  • After changing src/db/schema.ts, run npm run db:generate and commit the new file in drizzle/, then npm run db:migrate.

  • Test the MCP server locally with the MCP Inspector against http://localhost:3000/api/mcp and your MCP_TOKEN.

claude-workspace/          Claude Project instructions, reference-file templates, test prompts
drizzle/                   SQL migrations
src/
  app/
    api/mcp/               Remote MCP server (linkedin_status, publish_post, delete_post)
    api/oauth/             OAuth 2.1: client registration and token endpoint
    oauth/authorize/       OAuth consent screen
    .well-known/           OAuth discovery metadata
    api/auth/google/       Google sign-in for the console
    api/linkedin/          LinkedIn connect flow
    login/, page.tsx       Sign-in page and console
  db/schema.ts             Tables: linkedin_connection, publish_log, oauth_*
  lib/
    auth/                  Session, Google OIDC, owner checks
    data/                  Data access: LinkedIn, publishing, OAuth
    linkedin/              LinkedIn OAuth and Posts API clients
  proxy.ts                 Route protection

Troubleshooting

Symptom

Fix

Google: redirect_uri_mismatch

The redirect URI in step 5 must be exactly <APP_URL>/api/auth/google/callback, with no trailing slash.

Google: "Access blocked" or "app not verified"

Add your account under Audience → Test users (step 5.3).

"That Google account isn't allowed"

OWNER_EMAIL must match the account you signed in with. Redeploy after changing it.

LinkedIn: "The redirect_uri does not match"

Add <APP_URL>/api/linkedin/callback on the LinkedIn Auth tab (step 6.5).

LinkedIn: unauthorized_scope_error

Both products from step 6.4 must be approved on the Products tab.

Claude: connector fails to connect

Check that <APP_URL>/.well-known/oauth-authorization-server returns JSON, and that you're using the production URL, not a preview URL.

publish_post: "token can't be read"

TOKEN_ENCRYPTION_KEY changed or differs between environments. Use the same value everywhere, then reconnect LinkedIn.

publish_post: "token has expired"

LinkedIn tokens last about 60 days. Click Reconnect in the console.

Database errors after deploying

Run npm run db:migrate with an up-to-date .env.local.

FAQ

Does this cost anything? On free tiers (Vercel Hobby, Neon free, Google Cloud, LinkedIn), the connector costs nothing to run. Drafting uses your existing Claude plan; there are no extra AI API keys.

Can it post to company Pages, or with images? Not yet. It posts text to your personal profile, which is what the self-serve "Share on LinkedIn" product allows. Pull requests are welcome.

How often do I need to reconnect LinkedIn? About every 60 days. LinkedIn doesn't issue refresh tokens to self-serve apps. The console warns you 7 days before expiry.

Can Claude post without asking me? Only if you set it up that way. Claude publishes when you explicitly ask, or when a scheduled task you created says to. Claude's own tool-permission prompts add another check unless you choose "always allow".

Where does my data live? In your own Neon database and your own Claude Project. This repo has no telemetry and no central server.

Contributing

Issues and pull requests are welcome. Before opening a PR, run:

npm run lint && npx tsc --noEmit && npm run build

License

MIT. Use it, fork it, adapt it.


Claude LinkedIn Studio is an independent open-source project. It isn't affiliated with, endorsed by or sponsored by LinkedIn, Microsoft, Anthropic, Google or Vercel. All trademarks belong to their owners. Follow LinkedIn's User Agreement and API Terms when using it.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Post to LinkedIn from Claude — create posts, upload images, edit/delete posts, and manage company pages via natural language. Uses the official LinkedIn REST API with OAuth 2.0.
    9
    49 npm
    2
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    Enables Claude Desktop to manage your LinkedIn profile and company pages, including posting, reading posts, and fetching profile information.
    7
    49 npm
    4
    MIT