Post X MCP
Servidor MCP para publicar en X (Twitter) con soporte para múltiples cuentas
Documentación
mcp-server-x
Un servidor MCP (Model Context Protocol) para X (Twitter). Construido en Rust usando OAuth 1.0a y la API v2 de X. Soporta múltiples cuentas.
Se comunica a través de stdio usando JSON-RPC 2.0.
Herramientas
| Herramienta | Descripción |
|---|---|
list_accounts | Lista las cuentas disponibles y cuál es la predeterminada |
post_tweet | Publica un tweet con medios opcionales (hasta 4 imágenes, 1 video o 1 GIF) |
post_thread | Publica un hilo de hasta 25 tweets, cada uno con medios opcionales |
delete_tweet | Elimina un tweet por ID o URL |
upload_media | Sube medios para adjuntarlos más tarde (devuelve un media_id) |
update_profile | Actualiza tu biografía/descripción, nombre mostrado, ubicación y/o URL del sitio web (endpoint heredado v1.1) |
update_profile_banner | Actualiza la imagen de cabecera/portada del perfil (endpoint heredado v1.1) |
search_tweets | Busca tweets recientes (últimos 7 días) con operadores de Twitter |
get_timeline | Obtén tu cronología de inicio en orden cronológico inverso |
get_bookmarks | Obtén tus tweets marcados (paginados) |
get_me | Obtén el perfil del usuario autenticado |
lookup_user | Busca cualquier usuario por @nombredeusuario o ID numérico |
get_followers | Lista tus seguidores (paginados) |
get_following | Lista a quién sigues (paginado) |
get_all_followers | Obtén TODOS tus seguidores en una sola llamada (auto-paginación) |
get_all_following | Obtén TODAS las cuentas que sigues en una sola llamada (auto-paginación) |
like_tweet | Da "Me gusta" a un tweet por ID o URL |
unlike_tweet | Quita "Me gusta" a un tweet por ID o URL |
retweet | Retuitea un tweet por ID o URL |
unretweet | Deshace un retweet por ID o URL |
bookmark_tweet | Marca un tweet por ID o URL |
unbookmark_tweet | Elimina un marcador por ID o URL |
get_trends | Obtén los temas de tendencia actuales para una ubicación WOEID (predeterminado: mundial) |
get_dm_events | Obtén mensajes directos recientes en todas las conversaciones |
send_dm | Envía un mensaje directo a una conversación |
follow_user | Sigue a un usuario por nombre de usuario o ID |
unfollow_user | Deja de seguir a un usuario por nombre de usuario o ID |
Todas las herramientas aceptan un parámetro opcional account para seleccionar qué cuenta de X usar. Omítelo para usar la cuenta predeterminada.
Inicio Rápido
1. Compilar
cargo build --release
Produce target/release/mcp-server-x (optimizado con LTO, sin símbolos).
2. Configurar credenciales
El servidor busca la configuración en la primera de estas ubicaciones existentes:
$XDG_CONFIG_HOME/mcp-server-x/config.toml~/.config/mcp-server-x/config.toml$XDG_CONFIG_HOME/mcp-server-post-x/config.toml(heredado)~/.config/mcp-server-post-x/config.toml(heredado)
También puedes ejecutarlo sin ningún archivo de configuración proporcionando credenciales mediante variables de entorno (ideal para contenedores/CI):
export X_API_KEY=...
export X_API_KEY_SECRET=...
export X_ACCESS_TOKEN=...
export X_ACCESS_TOKEN_SECRET=...
# Optional:
# export X_ACCOUNT_NAME=myaccount
POST_X_* / POST_X_ACCOUNT_NAME todavía se aceptan si las variables X_* no están definidas.
Crea el archivo de configuración (enfoque clásico):
mkdir -p ~/.config/mcp-server-x
Crea ~/.config/mcp-server-x/config.toml:
Cuenta única (no se necesita default_account):
[accounts.myaccount]
api_key = "your-api-key"
api_key_secret = "your-api-key-secret"
access_token = "your-access-token"
access_token_secret = "your-access-token-secret"
Múltiples cuentas:
default_account = "myaccount"
[accounts.myaccount]
api_key = "your-api-key"
api_key_secret = "your-api-key-secret"
access_token = "your-access-token"
access_token_secret = "your-access-token-secret"
[accounts.otheraccount]
api_key = "your-api-key"
api_key_secret = "your-api-key-secret"
access_token = "other-access-token"
access_token_secret = "other-access-token-secret"
Notas:
- Las claves de cuenta son nombres de usuario de X (ej.
[accounts.codechap]) - Si tienes múltiples cuentas,
default_accountes obligatorio - Si tienes una sola cuenta,
default_accountes opcional (se detecta automáticamente) - Múltiples cuentas pueden compartir el mismo
api_key/api_key_secret(misma app de X). Solo losaccess_token/access_token_secretdifieren por cuenta. bookmark_tweet/unbookmark_tweetrequieren Contexto de Usuario OAuth 2.0 (bookmark.write). Agregaoauth2_client_id,oauth2_client_secret,oauth2_access_tokenyoauth2_refresh_tokenopcionales en la cuenta que necesita marcadores. OAuth 1.0a sigue en uso para todas las demás herramientas. Genera el token de usuario en la Consola de Desarrolladores de X (App → Keys & Tokens → OAuth 2.0 Access Token) contweet.read,users.read,bookmark.read,bookmark.writeyoffline.access. Los tokens de acceso duran ~2 horas; el servidor los renueva conoauth2_refresh_token(y escribe los tokens rotados de vuelta enconfig.toml).
Asegúralo:
chmod 700 ~/.config/mcp-server-x
chmod 600 ~/.config/mcp-server-x/config.toml
Consulta Obtención de credenciales a continuación para saber cómo obtenerlas.
3. Agregar a tu cliente MCP
Claude Code (~/.claude.json):
{
"mcpServers": {
"x": {
"command": "/path/to/mcp-server-x"
}
}
}
Luego pídele a Claude cosas como:
- "Publica un tweet que diga hola mundo"
- "Publica un tweet como securechap que diga hola mundo"
- "Busca tweets sobre Rust"
- "Muéstrame mi cronología"
- "Dale Me gusta a este tweet: https://x.com/someone/status/123456"
- "¿Quiénes son mis seguidores?"
- "Busca @elonmusk"
- "Lista mis cuentas"
Referencia de Herramientas
list_accounts
Sin parámetros obligatorios. Devuelve los nombres de cuentas disponibles, cuál es la predeterminada y nombres de usuario en caché.
post_tweet
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
account | string | no | Cuenta a usar (omitir para la predeterminada) |
text | string | sí | Texto del tweet (máximo 280 caracteres) |
media | array | no | Medios a subir y adjuntar. Cada elemento: { path, alt_text? }. Máximo 4 imágenes, o 1 video, o 1 GIF. |
media_ids | array | no | IDs de medios previamente subidos para adjuntar (máximo 4). Mutuamente excluyente con media. |
reply_to | string | no | ID del tweet al que responder |
post_thread
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
account | string | no | Cuenta a usar (omitir para la predeterminada) |
tweets | array | sí | Array de tweets (máximo 25). Cada uno: { text, media? } |
delete_tweet / like_tweet / unlike_tweet / retweet / unretweet / bookmark_tweet / unbookmark_tweet
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
account | string | no | Cuenta a usar (omitir para la predeterminada) |
tweet_id | string | sí | ID del tweet o URL completa del tweet |
Todos aceptan URLs como https://x.com/user/status/123456 — el ID se extrae automáticamente.
upload_media
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
account | string | no | Cuenta a usar (omitir para la predeterminada) |
path | string | sí | Ruta del archivo local. Compatible: jpeg/png/webp (máx. 5MB), gif (máx. 15MB), mp4 (máx. 512MB) |
alt_text | string | no | Texto alternativo (solo imágenes y GIFs, no video) |
Devuelve un media_id para usar con el parámetro media_ids de post_tweet.
update_profile
Actualiza los campos de texto del perfil del usuario autenticado. Debe proporcionarse al menos un campo; solo se cambian los campos que pases, y pasar una cadena vacía borra ese campo.
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
account | string | no | Cuenta a usar (omitir para la predeterminada) |
description | string | no* | Nueva biografía/descripción (máx. 160 caracteres; cadena vacía la borra) |
name | string | no* | Nuevo nombre mostrado (1-50 caracteres) |
location | string | no* | Nueva ubicación (máx. 30 caracteres; cadena vacía la borra) |
url | string | no* | Nueva URL del sitio web mostrada en el perfil (máx. 100 caracteres; cadena vacía la borra) |
* Se requiere al menos uno de description, name, location o url.
Usa el endpoint heredado POST /1.1/account/update_profile.json (sin equivalente v2). Requiere que la app tenga permiso de Lectura y Escritura; sin él, el endpoint devuelve 403.
update_profile_banner
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
account | string | no | Cuenta a usar (omitir para la predeterminada) |
path | string | sí | Ruta del archivo local de la imagen de portada (solo JPEG/PNG/WebP, máx. 5MB). X recomienda 1500x500 píxeles. |
width | integer | no | Ancho de la imagen (para recorte) |
height | integer | no | Alto de la imagen (para recorte) |
offset_left | integer | no | Desplazamiento izquierdo (píxeles) para inicio del recorte |
offset_top | integer | no | Desplazamiento superior (píxeles) para inicio del recorte |
Usa el endpoint heredado POST /1.1/account/update_profile_banner.json (parámetro banner en base64; sin equivalente v2). El éxito devuelve HTTP 200 sin cuerpo.
search_tweets
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
account | string | no | Cuenta a usar (omitir para la predeterminada) |
query | string | sí | Consulta de búsqueda. Compatible con: from:user, #hashtag, @mention, "exact phrase", -exclude, lang:en |
max_results | integer | no | 10-100 (predeterminado 10) |
sort_order | string | no | recency o relevancy |
pagination_token | string | no | Token de página siguiente de la respuesta anterior |
get_trends
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
account | string | no | Cuenta a usar (omitir para la predeterminada; determina qué límite de tasa de la app se usa) |
woeid | integer | no | WOEID para la ubicación (predeterminado: 1 = Mundial). Consulta valores comunes en la descripción de la herramienta. |
Devuelve nombres de tendencias y volúmenes aproximados de publicaciones.
get_timeline
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
account | string | no | Cuenta a usar (omitir para la predeterminada) |
max_results | integer | no | 1-100 (predeterminado 20) |
exclude | string | no | replies, retweets o ambos separados por comas |
pagination_token | string | no | Token de página siguiente |
get_bookmarks
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
account | string | no | Cuenta a usar (omitir para la predeterminada) |
max_results | integer | no | 1-100 (predeterminado 20) |
pagination_token | string | no | Token de página siguiente |
lookup_user / follow_user / unfollow_user
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
account | string | no | Cuenta a usar (omitir para la predeterminada) |
user | string | sí | Nombre de usuario (con o sin @) o ID numérico de usuario |
get_followers / get_following
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
account | string | no | Cuenta a usar (omitir para la predeterminada) |
max_results | integer | no | 1-100 (predeterminado 20) |
pagination_token | string | no | Token de página siguiente |
get_all_followers / get_all_following
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
account | string | no | Cuenta a usar (omitir para la predeterminada) |
max_users | integer | no | Límite de seguridad (predeterminado 5000, máximo 10000). Evita respuestas enormes para cuentas con muchos seguidores. |
Auto-pagina a través de los resultados (100 por página) con un retraso de 200ms entre páginas. Para cuentas con decenas de miles de seguidores, prefiere las herramientas paginadas get_followers / get_following.
get_dm_events
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
account | string | no | Cuenta a usar (omitir para la predeterminada) |
max_results | integer | no | 1-100 (predeterminado 20) |
pagination_token | string | no | Token de página siguiente |
send_dm
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
account | string | no | Cuenta a usar (omitir para la predeterminada) |
conversation_id | string | sí | ID de la conversación de DM (obtener de get_dm_events) |
text | string | sí | Texto del mensaje |
get_me
| Parámetro | Tipo | Obligatorio | Descripción |
|---|---|---|---|
account | string | no | Cuenta a usar (omitir para la predeterminada) |
Devuelve tu ID de usuario, nombre mostrado y @nombredeusuario.
Agregar Cuentas Adicionales
Para agregar otra cuenta de X a una app existente (sin una cuenta de desarrollador separada), usa el script de autorización OAuth incluido:
export X_API_KEY="your-app-api-key"
export X_API_KEY_SECRET="your-app-api-key-secret"
./oauth-authorize.sh
Importante: El script ya no contiene credenciales codificadas. Debes proporcionar las Consumer Keys de tu propia app mediante las dos variables de entorno mostradas arriba. Esto ejecuta el flujo OAuth 1.0a de 3 patas basado en PIN:
- Abre una URL donde la nueva cuenta autoriza tu aplicación
- Pegas el PIN de vuelta en la terminal
- Genera el bloque de configuración
[accounts.username]para añadir a tuconfig.toml
Todas las cuentas que autorices comparten la misma App de X (y sus límites de tasa + facturación). Este es el patrón normal para uso multi-cuenta.
Obtención de Credenciales
- Ve a developer.x.com y regístrate para obtener una cuenta de desarrollador
- Crea un Proyecto y una App en la Consola de Desarrollador
- En la configuración de tu App, configura Autenticación de usuario:
- Permisos de la App: Lectura y escritura (y Mensajes Directos si quieres soporte de DM)
- Tipo: Aplicación Web, Aplicación Automatizada o Bot
- URL de callback:
https://example.com(no se usa, pero es obligatoria) - URL del sitio web: cualquier URL válida
- Ve a Claves y tokens y genera:
- Clave de API y Secreto de Clave de API (bajo Claves de Consumidor)
- Token de Acceso y Secreto de Token de Acceso (bajo Tokens de Autenticación)
- Copia los cuatro valores en tu
config.tomlbajo[accounts.yourusername]
El servidor valida las credenciales al inicio. Si recibes errores 401 persistentes, regenera tus tokens en developer.x.com.
Desarrollo
cargo build # debug build
cargo run # run in dev mode
RUST_LOG=debug cargo run # debug logging (credentials are redacted)
cargo test # run unit tests
cargo clippy -- -D warnings # strict lint check (must pass)
cargo build --release # optimized binary
Detalles Técnicos
- Autenticación: OAuth 1.0a con firmas HMAC-SHA1 (RFC 5849, codificación porcentual RFC 3986)
- Multi-cuenta: Múltiples cuentas de X por instancia del servidor, seleccionables por llamada de herramienta
- API de Tweets: X API v2 (
api.x.com/2/) - Subida de medios: Subida fragmentada v1.1 (
upload.twitter.com/1.1/media/upload.json) — flujo INIT/APPEND/FINALIZE/STATUS para video/GIF, multipart simple para imágenes - Límites de medios: JPEG/PNG/WebP hasta 5MB, GIF hasta 15MB, MP4 hasta 512MB
- Validación de medios: Máximo 4 imágenes O 1 video O 1 GIF por tweet (sin mezclar)
- Publicación de hilos: Retraso de 500ms entre tweets, encadenados vía
in_reply_to_tweet_id - Lógica de reintentos: Reintento automático con retroceso exponencial en errores 503
- Límites de tasa: Las respuestas 429 incluyen la marca de tiempo de reinicio en el mensaje de error (sin reintento automático — el llamador decide)
- Seguridad:
get_all_followers/get_all_followingestán limitados a 10k usuarios por defecto para evitar destruir las ventanas de contexto de LLM - Edición de Rust: 2021 (amplia compatibilidad)
Estructura del Proyecto
src/
main.rs — entry point, config loading, tracing, stdio transport
server.rs — MCP tool handlers, response formatting, multi-account routing
api.rs — X API client: OAuth signing, tweet/media/user/DM endpoints
params.rs — tool parameter types (serde + JSON Schema)