Skip to main content
Glama
danilobrando

publish-linkedin

by danilobrando

publish-linkedin

Servidor MCP mínimo para publicar en LinkedIn desde tu Second Brain.

Siete herramientas. ~1.100 líneas que puedes leer completas. API oficial de LinkedIn (OAuth 2.0, scope w_member_social) — sin cookies del navegador, que es la zona gris de los términos de servicio.

tú: "publica esto en LinkedIn"
Claude: [ensayo] esto es lo que saldría. ¿Confirmas?
tú: "sí"
Claude: ✓ Publicado. linkedin.com/feed/update/urn:li:share:…

El post sale en tu perfil personal, con tu nombre y tu foto.

La forma más fácil de instalarlo

Abre Claude Code en una carpeta vacía y pídele esto:

Instala https://github.com/danilobrando/publish-linkedin siguiendo su README
y guíame hasta que pueda publicar.

Él lee las instrucciones de este archivo, hace lo que le toca a él y te dice exactamente qué te toca a ti. Si prefieres a mano, están los cuatro pasos manuales más abajo.

Related MCP server: linkedin-mcp-server

Por qué existe

Existe southleft/linkedin-mcp: 87 herramientas, MIT, buen trabajo. Se descartó por tres razones concretas:

Mantenimiento

Último commit 25-mar-2026 · Development Status :: 3 - Alpha

Superficie

87 herramientas para usar 3

Riesgo de ToS

Depende de linkedin-api (scraping de cookies) para media API

La regla es la de siempre: si necesitas tres herramientas, escribe tres herramientas. Un repo que no puedes leer completo es un repo en el que confías a ciegas.


🤖 Si eres Claude Code (o cualquier agente): lee esto primero

El usuario te pidió instalar este repo. Guíalo hasta que publique, no solo hasta que instale. Sigue estos pasos en orden y no te saltes ninguna verificación.

Reglas que no puedes romper

Nunca corras auth.py credenciales

Usa input() y getpass(). Se te cuelga la terminal. Lo corre el usuario.

Nunca corras auth.py login

Abre el navegador y espera un clic humano. Lo corre el usuario.

Nunca pidas ni recibas el Client Secret por el chat

Va tecleado directo en la terminal del usuario. Si te lo pega, dile que lo rote.

✅ Sí corres

git clone, crear el entorno, instalar dependencias, claude mcp add, auth.py estado

Cuando llegues a un paso del usuario: dale el comando exacto con ruta absoluta, dile que lo corra en su propia terminal, y espera a que te diga que terminó. No sigas de largo.

Antes de empezar, avísale: "te voy a pedir permiso para correr git y pip; apruébalos o no puedo instalar nada." Sin eso, el primer comando se bloquea y el usuario no entiende por qué.

Paso 1 — Instalar (lo haces tú)

Detecta el sistema operativo y usa la rama correcta.

macOS / Linux:

git clone https://github.com/danilobrando/publish-linkedin.git
cd publish-linkedin
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt

Windows:

git clone https://github.com/danilobrando/publish-linkedin.git
cd publish-linkedin
py -m venv .venv
.venv\Scripts\pip install -r requirements.txt

⚠️ El entorno tiene que llamarse .venv y estar dentro del repo. El paso 4 depende de esa ruta exacta. No uses uv, conda, ni un entorno global aunque estén disponibles.

⚠️ Clona dentro de la carpeta donde el usuario te abrió, no en /tmp ni en el home. Apenas termines, corre pwd dentro del repo y dile al usuario la ruta absoluta. Desde ahí, todo comando que le entregues al usuario lleva la ruta absoluta completa — él va a correrlos en otra terminal, parada en otro lado. Un cd publish-linkedin relativo es la causa #1 de que esto falle.

Verifica antes de seguir (macOS/Linux · en Windows .venv\Scripts\python):

.venv/bin/python -c "import mcp, requests; print('OK')"

Si no imprime OK, arregla eso antes de continuar.

Paso 2 — Credenciales (lo hace el usuario)

Lo normal y lo recomendado es que el usuario cree su propia app. Es suya, no depende de nadie, y no se le vence cuando alguien más rote un secreto. Empieza siempre por ahí.

Dile:

Vas a crear tu propia aplicación de LinkedIn. Toma unos 10 minutos y te queda tuya para siempre. Necesitas administrar una Página de LinkedIn — si no tienes una, la creas gratis en el momento; sirve una página de tu empresa, de tu marca personal, o una de prueba.

Llévalo paso a paso por la sección Crear tu propia app de este mismo archivo. No la resumas: acompáñalo por cada pestaña y espera confirmación en cada una. Los dos errores que más pasan son pedir el producto equivocado y escribir mal la Redirect URL.

Cuando tenga Client ID y Client Secret, sigue abajo.

Plan B — credenciales prestadas

Solo si el usuario no puede crear la app ahora (no administra ninguna página, LinkedIn no le deja, o se quedó sin tiempo en una clase en vivo). Dile que le pida al instructor su Client ID y Client Secret, y explícale por qué no es riesgoso:

Son las credenciales de una empresa de prueba, creada justamente para esto — no de una compañía real. La app solo sirve para pedir permiso: el token que se genera es tuyo y solo publica en tu perfil. Nadie puede publicar por ti, ni tú por nadie.

Adviértele también que es prestado: si el instructor rota el secreto, tiene que volver a hacer el paso 2 con credenciales propias. Por eso el camino bueno es el de arriba.

2c · Guardar las credenciales — los DOS caminos pasan por aquí

Cuando el usuario ya tenga Client ID y Client Secret —propias o prestadas—, dile que corra en su terminal, dentro de la carpeta del repo:

.venv/bin/python auth.py credenciales

Le va a pedir Client ID (se ve al escribir) y Client Secret (no se ve, es normal).

Espera a que te confirme. Después verifica tú:

.venv/bin/python auth.py estado

Lo que imprime

Qué significa

✗ Todavía no hay credenciales

El paso 2c no se completó. No sigas.

✓ Credenciales de la app guardadas + ✗ Falta autenticarte

Correcto. Sigue al paso 3.

El código de salida también sirve: 2 si falta algo, 0 si todo está listo.

Paso 3 — Autorizar (lo hace el usuario)

Dile que corra en su terminal:

.venv/bin/python auth.py login

Adviértele antes de que lo corra:

  • Se le abre el navegador con su sesión de LinkedIn. Tiene que darle Allow.

  • Tiene 2 minutos. Si se pasa, simplemente lo vuelve a correr.

Espera. Después verifica tú:

.venv/bin/python auth.py estado

Tiene que salir el nombre del usuario y una fecha de vencimiento (~60 días). Si sale un error, búscalo en la tabla Cuando algo falle de este archivo y resuélvelo antes de seguir.

Paso 4 — Conectar el servidor (lo haces tú)

Desde la carpeta del repo:

claude mcp add publish-linkedin --scope user \
  -e PYTHONPATH=$PWD -- $PWD/.venv/bin/python $PWD/server.py

En Windows usa rutas absolutas explícitas en vez de $PWD (PowerShell: $PWD sirve; cmd no). Verifica con claude mcp list que aparezca publish-linkedin ✔ Connected.

Después dile al usuario, con estas palabras:

Cierra y vuelve a abrir Claude Code. Los servidores MCP se cargan al arrancar. Si no reinicias, las herramientas no aparecen y vas a creer que algo salió mal.

Paso 5 — Primera publicación (después del reinicio)

Ya reiniciado, verifica con la herramienta linkedin_quien_soy. Debe devolver el nombre del usuario.

Después dile:

Pídeme que publique algo. La primera vez no voy a publicar: te muestro un ensayo con el texto exacto que saldría. Solo publico cuando tú confirmes.

Cuando te pida publicar:

  1. Llama linkedin_publicar sin confirmar (o con confirmar=False). Muéstrale el ensayo completo.

  2. Espera su confirmación explícita. No la asumas, no la interpretes de un «dale» ambiguo.

  3. Solo entonces vuelve a llamar con confirmar=True y dale la URL.

Si se arrepiente, linkedin_borrar con el URN que devolviste.

Si algo falla en cualquier paso

Busca el síntoma en la tabla Cuando algo falle de este archivo antes de improvisar. Si no está ahí, dile al usuario qué falló en una frase, sin jerga, y qué vas a intentar.


Instalación (manual, si prefieres no usar el agente)

Cuatro pasos. Solo necesitas Python 3.11 o más nuevo.

1 · Clonar e instalar

git clone https://github.com/danilobrando/publish-linkedin.git
cd publish-linkedin
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt

En Windows, las dos últimas líneas son:

py -m venv .venv
.venv\Scripts\pip install -r requirements.txt

Si ya tienes uv, sirve igual y es más rápido: uv venv --python 3.12 && uv pip install -r requirements.txt. No hace falta instalarlo solo para esto — probado con venv y pip normales.

2 · Guardar las credenciales de la app

.venv/bin/python auth.py credenciales

(en Windows: .venv\Scripts\python auth.py credenciales — y así en todos los que siguen)

Te pide un Client ID y un Client Secret, que identifican a la aplicación que pide permiso — no a ti.

🎓 Lo recomendado es crear tu propia app — ver Crear tu propia app. Toma ~10 minutos y queda tuya.

Si estás en un curso y no alcanzas: pide esas dos credenciales a quien lo dicta. Una misma aplicación puede servir a todo el salón: cada persona autoriza por su lado y publica en su propio perfil. No van en este repo.

Si vas por tu cuenta: necesitas crear tu propia app. Ver Crear tu propia app abajo.

Se piden por teclado a propósito: pasarlas como argumento las dejaría en el historial del shell.

  • macOS → van al Keychain.

  • Windows y Linux → a ~/.config/publish-linkedin/, con permisos 0600.

3 · Autenticarte

.venv/bin/python auth.py login

Se abre el navegador con tu sesión de LinkedIn. Le das Allow. Eso crea un token que es tuyo y solo publica en tu perfil.

.venv/bin/python auth.py estado

Debe decir tu nombre y hasta cuándo dura el token (~60 días).

⚠️ El token vence y no avisa. Cuando falle dentro de dos meses, el mensaje te va a decir la fecha exacta y qué correr. No es un bug.

4 · Conectarlo a Claude Code

claude mcp add publish-linkedin --scope user \
  -e PYTHONPATH=$PWD -- $PWD/.venv/bin/python $PWD/server.py

⚠️ Cierra y vuelve a abrir Claude Code. Los servidores MCP se cargan al arrancar; si no reinicias, no aparecen y vas a creer que te equivocaste.

Verifica pidiéndole a Claude: "¿cuál es mi estado de LinkedIn?"


Las cuatro herramientas

Herramienta

Qué hace

linkedin_estado

¿Hay token? ¿Hasta cuándo? ¿Qué permisos?

linkedin_quien_soy

Confirma contra LinkedIn en qué cuenta se publicaría

linkedin_publicar

Publica, con o sin imagen o PDF — solo si confirmar=True

linkedin_borrar

Borra un post — solo si confirmar=True

linkedin_doctor

Diagnostica todo. Pásale reparar=True para que arregle lo que pueda

linkedin_menciones

A quién puedes etiquetar por nombre

linkedin_mencion_guardar

Agrega a alguien al directorio, verificando su URN

Adjuntar una imagen o un PDF

tú: "publica esto con la imagen ~/graficos/resultados.png"

Un solo adjunto por post. Sirven .jpg .png .gif (hasta 10 MB) y .pdf .pptx .docx (hasta 100 MB). El PDF sale en el feed como documento navegable, no como enlace.

El parámetro titulo cambia de significado según el tipo, porque LinkedIn los usa distinto:

Adjunto

titulo es

Por qué importa

Imagen

el texto alternativo

Accesibilidad: lo lee un lector de pantalla

Documento

el nombre visible

Es lo que la gente ve en el feed antes de abrirlo

El archivo se valida antes de subir nada —que exista, que la extensión sirva, que no esté vacío, que no pase del límite— y el ensayo te dice qué adjunto saldría:

⏸  ENSAYO — no se publicó nada.
Con adjunto → documento: informe.pdf (2.400 KB, application/pdf)

Y el adjunto entra en la huella de idempotencia: el mismo texto con otra imagen es otro post, y bloquearlo sería un falso positivo.

Mencionar a alguien (etiquetar)

tú: "publica esto agradeciéndole a @Andrés Caicedo"

Escribes @Nombre y el sistema lo convierte en una etiqueta real, que notifica a la persona. Funciona igual con páginas de empresa.

Pero hay un paso manual la primera vez, y no se puede evitar.

LinkedIn no le deja a esta app averiguar la URN de nadie. Probado contra la API:

Endpoint

Respuesta

/v2/people?q=search

404 — no existe para esta app

/v2/people/(vanityName:…)

403 ACCESS_DENIED, partner API

/v2/connections

403

/rest/organizations

403

/v2/userinfo (tú mismo)

200 ✓ — lo único accesible

Resolver URNs ajenas exige el Marketing Developer Platform, que es una solicitud de negocio con revisión humana. No es un permiso que se active.

Así que la URN se captura a mano, una vez por persona:

  1. Abre el perfil de la persona en LinkedIn, con tu sesión iniciada.

  2. Abre las herramientas de desarrollador del navegador (⌥⌘I en Mac) y pega esto en la consola:

    document.body.innerHTML.match(/urn:li:fsd_profile:([A-Za-z0-9_-]+)/)[1]
  3. Eso devuelve algo como ACoAAB1cD…. La URN es urn:li:person: + eso.

  4. Guárdala — y el sistema la verifica contra LinkedIn antes de aceptarla:

tú: "guarda a Andrés Caicedo con urn:li:person:ACoAAB1cD..."
→ ✓ Guardado (verificada con LinkedIn). Ahora escribe «@Andrés Caicedo».

La verificación usa un borrador que nunca sale al feed: LinkedIn valida las menciones del lado del servidor incluso en estado DRAFT, así que se crea uno, se lee la respuesta y se borra. Una URN inventada responde 400 INVALID_MENTION_PERSON_URN_ID; una real, 201.

Es tedioso la primera vez y gratis todas las siguientes: siempre mencionas a las mismas veinte personas. El directorio vive en ~/.config/publish-linkedin/menciones.json (permisos 0600).

El ensayo te dice a quién vas a etiquetar y a quién no:

Menciona a → Andrés Caicedo (ACoAAB1cD), Tribu iA (99887)
⚠️  Con @ pero SIN etiquetar (no están en tu directorio): Pedro Perez
    Van a salir como texto plano.

El freno de mano

linkedin_publicar no publica por defecto. Sin confirmar=True hace un ensayo y devuelve el texto exacto que saldría, el conteo de caracteres y cómo queda tras escapar los reservados del formato little text de LinkedIn.

Publicar es irreversible y es tu nombre. Un sistema autónomo sin freno no es autonomía — es un accidente esperando su turno.

Cuando algo no funcione: el doctor

.venv/bin/python doctor.py          # 10 chequeos, cada fallo dice qué hacer
.venv/bin/python doctor.py fix      # repara locks rancios y permisos
.venv/bin/python doctor.py --quiet  # solo el veredicto (para scripts)

Códigos de salida: 0 todo bien · 1 hay un aviso · 2 algo roto · 64 mal uso.

También está como herramienta MCP: pídele a Claude "diagnostica mi LinkedIn".

Que te avise antes de que se venza el token

El token dura ~60 días y LinkedIn no avisa. Esta app no recibe refresh_token (solo se los dan a apps aprobadas), así que hay que volver a autorizar. Para no enterarte el día que necesitas publicar:

./instalar-vigilante.sh        # corre el doctor a diario a las 9:15 (macOS)
./instalar-vigilante.sh quitar

En Linux, la misma idea con cron: 15 9 * * * /ruta/al/repo/vigilante.sh

Probar sin publicar

PUBLISH_LINKEDIN_SIMULACRO=1 ...

Con esa variable, publicar y borrar nunca tocan la red. La suite la activa sola:

.venv/bin/python prueba.py               # 46 pruebas, ninguna contacta a LinkedIn
.venv/bin/python prueba_concurrencia.py  # 8 procesos reales peleando por el lock

La segunda va aparte porque necesita procesos de verdad. Verifica que el lock sea atómico — y está calibrada para fallar con la versión anterior, que dejaba entrar hasta 8 publicadores a la vez.

Existe por un incidente real: el 31-ago-2026, dos corridas automatizadas publicaron de verdad en un perfil real. Redirigir el directorio de datos no basta — el token vive en el Keychain y sigue siendo válido.

Cómo se evita publicar dos veces

Es el fallo más caro de un publicador: no fallar, sino duplicar en público. Hay cuatro controles, y los cuatro fueron necesarios:

Control

Qué previene

Huella del contenido, ventana 24h

Republicar el mismo texto

El chequeo corre dentro del lock

Que dos procesos lo pasen a la vez

Lock atómico (O_CREAT|O_EXCL)

Que dos procesos tomen el lock a la vez

Se registra INTENTO antes de llamar

Que un timeout deje el post vivo sin rastro

Y ante un 5xx de LinkedIn no se reintenta a propósito: un 5xx no dice si el post se creó o no. El mensaje te lo dice y te manda a revisar tu perfil.

Si borras un post, su huella se libera y puedes volver a publicarlo corregido.

Seguridad

  • Secretos y token en el Keychain (macOS) o en archivos 0600 (Windows/Linux). Nunca en un archivo de configuración legible.

  • El state de OAuth se valida al volver del navegador (anti-CSRF).

  • Toda publicación —ensayo, fallo o real— queda en el diario de auditoría: ~/.config/publish-linkedin/publicaciones.log (override: PUBLISH_LINKEDIN_LOG).

Si compartes una app con varias personas

Es válido y es como funciona OAuth: la app pide permiso, cada persona lo concede por separado, y cada token solo sirve para quien lo autorizó. Nadie puede publicar en el perfil de otro. Dicho eso, dos cosas que conviene saber:

  • El Client Secret es una contraseña de la app. Quien lo administra debe poder rotarlo (pestaña Auth del portal, un clic) y hacerlo cuando el grupo deje de necesitarlo.

  • LinkedIn limita por app además de por persona. Si mucha gente publica en el mismo minuto, van a ver un 429. El mensaje lo explica y dice cuánto esperar.

Cuando algo falle

Síntoma

Qué pasó

Todavía no hay credenciales

Falta el paso 2

No hay token / El token venció

Corre auth.py login otra vez

El navegador dice redirect_uri inválido

La app no tiene registrada http://localhost:8765/callback

429

Límite de LinkedIn. Espera lo que diga el mensaje

401 / 403 al publicar

Token vencido, o falta el producto Share on LinkedIn en la app

426 NONEXISTENT_VERSION

LinkedIn retiró la versión de API. El código la renegocia solo y reintenta una vez

Claude no ve las herramientas

No reiniciaste Claude Code después del paso 4


Crear tu propia app

Este es el camino recomendado. Toma ~10 minutos y la app queda tuya.

Primero: la Página

LinkedIn no deja crear una app sin asociarle una Página. Si no administras ninguna, créala gratis en linkedin.com/company/setup/new. Sirve tu empresa, tu marca personal, o una página de prueba — da igual: la página no es donde sale el post. El post sale en tu perfil personal. La página solo figura como "editor" de la app en el portal. Es papeleo.

Después: la app

Ve a linkedin.com/developers/appsCreate app. Asóciala a tu página. Un super admin de esa página tiene que aprobar la verificación con un enlace — si la página es tuya, eres tú y es un clic.

Pestaña Products — pide estos dos. Ambos son aprobación instantánea:

Producto

Para qué

Share on LinkedIn

Publicar. Es el que importa.

Sign In with LinkedIn using OpenID Connect

Saber quién eres al autenticar.

No pidas Community Management API ni Ad Library: esos sí requieren revisión humana de LinkedIn y tardan días. No los necesitas para publicar.

Pestaña Auth:

  1. Copia el Client ID y el Client Secret.

  2. En OAuth 2.0 settings → Redirect URLs, agrega exactamente:

    http://localhost:8765/callback

    Sin https, sin barra al final, con ese puerto. Un carácter de más y el login falla con un error que no explica nada.

Archivos

Archivo

Qué es

linkedin.py

Cliente HTTP + almacén de secretos + escapado

medios.py

Subida de imágenes y documentos (validar → subir → adjuntar)

menciones.py

Directorio nombre→URN, expansión de @Nombre y verificación

auth.py

Flujo OAuth (credenciales · login · estado)

server.py

Servidor MCP con las cuatro herramientas

Estado y pendientes

Versión 0.1.0. Ver HARDENING.md para el detalle.

Lo que falta y conviene saber antes de confiar en esto:

  • Windows y Linux están escritos pero sin probar. El código los contempla; nadie los ha corrido de verdad.

  • No hay refresh_token: reautorizar cada ~60 días es obligatorio.

  • Publicar sin humano no está soportado a propósito. confirmar=True es siempre de una persona.

MIT.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

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
    214
    2
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Enables AI agents to publish posts, images, comments, and reactions to LinkedIn as the authenticated user, with built-in safety features like daily budgets and deduplication.
    9
    29
    Apache 2.0

Latest Blog Posts

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/danilobrando/publish-linkedin'

If you have feedback or need assistance with the MCP directory API, please join our Discord server