MCP Bridge for Zotero

Servidor MCP que permite a asistentes de IA construir, probar y depurar plugins de Zotero mediante 26 herramientas para inspección de interfaz, ejecución de JS, registro y más.

Documentación

Servidor MCP Zotero Dev

Dale a tu asistente de IA superpoderes para el desarrollo de plugins de Zotero

License: MIT Zotero 7+

Arquitectura · Primeros pasos · Herramientas disponibles

MCP Server Zotero Dev in action

Un servidor de Protocolo de Contexto de Modelo (MCP) que permite a asistentes de IA como Claude, Cursor y Windsurf crear, probar y depurar plugins de Zotero 7, 8, 9 y 10. Capturas de pantalla, estado del DOM, registros de depuración y ejecución de JavaScript le dan a la IA un contexto rico para entender lo que está sucediendo, además de herramientas para ayudarte a solucionarlo.

✨ Características

CategoríaCapacidades
🎯 Inspección de UICapturas de pantalla, árbol DOM, búsqueda de elementos, estilos calculados
🖱️ Interacción con UIHacer clic en elementos y escribir texto (compatible con shadow DOM)
💻 Ejecución de JSEjecutar código en el contexto de Zotero, inspeccionar APIs, probar fragmentos
🔧 Herramientas de compilaciónIntegración de andamiaje para compilar, servir, recarga en caliente
📋 Registros y erroresTransmitir salida de depuración, consola de errores, monitorear problemas
🗃️ Base de datosAcceso de solo lectura a zotero.sqlite para depuración
🔌 Gestión de pluginsInstalar, recargar, listar plugins

🚀 Inicio rápido

Requisitos previos

  • Node.js 20+ y npm
  • Zotero 7+ — Funciona en todas las compilaciones de Zotero 7, 8, 9 y 10 (release, beta, dev)
  • Para desarrollo de plugins: zotero-plugin-scaffold

1. Instalar el servidor MCP

Usa install-mcp para agregar el servidor a tu asistente de IA:

npx -y install-mcp @introfini/mcp-server-zotero-dev --client claude-code

Clientes compatibles: claude-code, cursor, windsurf, vscode, cline, roo-cline, claude, zed, goose, warp, codex

Claude Code
npx -y install-mcp @introfini/mcp-server-zotero-dev --client claude-code
Cursor
npx -y install-mcp @introfini/mcp-server-zotero-dev --client cursor
VS Code / Copilot
npx -y install-mcp @introfini/mcp-server-zotero-dev --client vscode
Windsurf
npx -y install-mcp @introfini/mcp-server-zotero-dev --client windsurf
Configuración manual

Agrega a la configuración de tu cliente MCP:

{
  "mcpServers": {
    "zotero-dev": {
      "command": "npx",
      "args": ["-y", "@introfini/mcp-server-zotero-dev@1.1.1"],
      "env": {
        "ZOTERO_RDP_PORT": "6100"
      }
    }
  }
}

Versión y actualizaciones: fija una versión exacta como se muestra arriba. Un npx <pkg> sin versión (sin número de versión) sigue ejecutando lo que npx tenga en caché y no recogerá nuevas versiones, así que siempre incluye una versión y -y (sin -y, npx se queda esperando un aviso de instalación). Aumenta la versión fijada para actualizar, o usa @latest para obtener siempre la más reciente al iniciar (actualizaciones automáticas, pero una versión defectuosa se ejecutaría automáticamente y agrega una verificación de registro en cada inicio). Ten en cuenta que install-mcp puede escribir una configuración sin -y o una versión, por lo que la configuración manual anterior es la ruta más robusta.

Reinicia tu asistente de IA después de agregar la configuración.

2. Instalar el plugin MCP Bridge en Zotero

Descarga zotero-mcp-bridge.xpi e instálalo:

  1. En Zotero: Herramientas → Plugins
  2. Haz clic en ⚙️ → Instalar plugin desde archivo
  3. Selecciona el archivo .xpi descargado
  4. Reinicia Zotero

Este plugin ligero habilita el Protocolo de Depuración Remota cuando Zotero se inicia. Solo necesita instalarse una vez y funciona en todas las compilaciones de Zotero 7+ (release, beta y dev).

3. ¡Comienza a desarrollar!

Simplemente abre Zotero normalmente y pregúntale a tu asistente de IA:

"Toma una captura de pantalla de Zotero y lista los plugins instalados"

¡Eso es todo! Sin banderas de inicio especiales, sin configuración. 🎉


🧰 Herramientas disponibles (28 en total)

Inspección de UI — Capturas de pantalla, DOM, estilos
HerramientaDescripción
zotero_screenshotCapturar capturas de pantalla de ventana, elemento o región
zotero_inspect_elementEncontrar elementos por selector CSS
zotero_get_dom_treeObtener estructura DOM de una ventana/panel
zotero_get_stylesObtener estilos CSS calculados para un elemento
zotero_list_windowsListar todas las ventanas abiertas de Zotero

Objetivos de captura: Ventana principal, preferencias, lector de PDF, diálogos o cualquier elemento por selector. Usa highlightSelector para agregar un borde rojo antes de la captura.

Interacción con UI — Hacer clic y escribir en la interfaz de Zotero
HerramientaDescripción
zotero_click_elementHacer clic en un elemento por selector CSS (botón de barra/menú, control de preferencias, fila de lista). Atraviesa shadow DOM; index elige entre múltiples coincidencias; mouseEvents sintetiza una secuencia completa de mouse.
zotero_send_keysEscribir texto en un input/textarea/contenteditable (lo enfoca primero, dispara input/change). Opcional clear y pressEnter.

La resolución intenta primero con el DOM claro, luego atraviesa raíces shadow abiertas (los elementos personalizados XUL de Zotero mantienen los internos en shadow DOM). Limitación: no puede descartar un diálogo modal nativo bloqueante (Services.prompt.confirmEx) — su bucle modal anidado bloquea el hilo de evaluación en el que se ejecutan estas herramientas.

Ejecución de JavaScript — Ejecutar código en el contexto de Zotero
HerramientaDescripción
zotero_execute_jsEjecutar JavaScript en el contexto privilegiado de Zotero. Envuelve automáticamente el código con declaraciones return de nivel superior en IIFE.
zotero_inspect_objectExplorar APIs de Zotero — listar métodos y propiedades de cualquier objeto (por ejemplo, Zotero.Items)
zotero_open_preferencesAbrir la ventana de configuración de Zotero, opcionalmente en un panel específico (integrado o de plugin)
zotero_search_prefsBuscar/descubrir preferencias por patrón (por ejemplo, encontrar todas las preferencias que contengan "debug")
zotero_get_prefObtener un valor de preferencia
zotero_set_prefEstablecer un valor de preferencia

Ejemplos: Zotero.Items.getAll(1), Zotero.Prefs.get('export.quickCopy.setting'), ZoteroPane.getSelectedItems()

Consejo: Usa zotero_inspect_object para explorar APIs antes de escribir código. Usa zotero_search_prefs para descubrir claves de preferencias.

Compilación y andamiaje — Integración con zotero-plugin-scaffold
HerramientaDescripción
zotero_scaffold_buildCompilar plugin (modo dev o producción)
zotero_scaffold_serveIniciar servidor de desarrollo con recarga en caliente
zotero_scaffold_lintEjecutar ESLint en el código fuente del plugin
zotero_scaffold_typecheckEjecutar verificación de tipos de TypeScript
Registros y depuración — Consola de errores y salida de depuración
HerramientaDescripción
zotero_read_logsLeer salida de depuración (Zotero.debug)
zotero_read_errorsLeer entradas de la consola de errores
zotero_watch_logsTransmitir registros en tiempo real
zotero_clear_logsLimpiar el búfer de registros
Gestión de plugins — Instalar, recargar, inspeccionar
HerramientaDescripción
zotero_plugin_reloadRecargar en caliente tu plugin de desarrollo
zotero_plugin_installInstalar plugin desde ruta XPI
zotero_plugin_listListar plugins instalados con versión/estado
Acceso a base de datos — Acceso SQLite de solo lectura
HerramientaDescripción
zotero_db_queryEjecutar consulta SELECT en zotero.sqlite
zotero_db_schemaObtener información del esquema de tablas
zotero_db_statsObtener estadísticas de la base de datos (elementos, adjuntos, colecciones, tamaño)

Nota: El acceso a la base de datos es de solo lectura y requiere que Zotero esté cerrado, o usa una copia de la base de datos.


🏗️ Arquitectura

┌─────────────────────────────────────────────────────────────────┐
│                        AI Assistant                             │
│                  (Claude, Cursor, Windsurf)                     │
└─────────────────────────┬───────────────────────────────────────┘
                          │ MCP Protocol (stdio)
                          ▼
┌─────────────────────────────────────────────────────────────────┐
│                  MCP Server (Node.js/TypeScript)                │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────────┐   │
│  │   Scaffold   │  │     RDP      │  │      Database        │   │
│  │  Integration │  │    Client    │  │      Reader          │   │
│  └──────────────┘  └──────┬───────┘  └──────────────────────┘   │
└─────────────────────────────┼───────────────────────────────────┘
                              │ Firefox RDP (port 6100)
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                      Zotero Application                         │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │            MCP Bridge for Zotero                         │   │
│  │         Starts DevToolsServer on launch                  │   │
│  └──────────────────────────────────────────────────────────┘   │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │              Firefox DevTools Server (built-in)          │   │
│  │           JS Execution • DOM • Console • Screenshots     │   │
│  └──────────────────────────────────────────────────────────┘   │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │                   Your Plugin (dev)                      │   │
│  └──────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────┘

¿Por qué este enfoque?

  • ✅ Plugin ligero — Solo habilita RDP, Firefox DevTools hace el resto
  • ✅ Cero configuración después de instalar — Solo abre Zotero normalmente, sin banderas especiales
  • ✅ Contexto rico para IA — Capturas de pantalla, DOM y registros ayudan a la IA a entender el estado de tu plugin
  • ✅ Recarga en caliente — Se integra con zotero-plugin-scaffold para retroalimentación instantánea
  • ✅ Acceso completo a Zotero — Ejecuta cualquier API de Zotero en el contexto privilegiado
  • ✅ Multiplataforma — Funciona en Linux, Windows, macOS

🔧 Variables de entorno

VariableDescripciónPredeterminado
ZOTERO_RDP_PORTPuerto de depuración remota6100
ZOTERO_RDP_HOSTHost de depuración127.0.0.1
ZOTERO_DATA_DIRRuta al directorio de datos de ZoteroDetección automática
ZOTERO_PROFILE_PATHRuta al perfil de ZoteroDetección automática

🔌 Cambiar el puerto RDP

El puente escucha en el puerto 6100 por defecto. Solo necesitas cambiarlo si ejecutas dos instancias de Zotero al mismo tiempo (un perfil normal y uno de desarrollo, por ejemplo), o si otro proceso ya tiene el 6100.

El puerto vive en ambos lados del puente, y ambos deben estar de acuerdo.

1. Lado de Zotero — establece la preferencia del plugin:

  1. Configuración → Avanzado → Editor de configuración, y acepta la advertencia
  2. Busca extensions.mcp-rdp.port
  3. Si no existe, créala: selecciona Número, nómbrala extensions.mcp-rdp.port, e ingresa tu puerto
  4. Reinicia Zotero — el listener solo se abre al inicio

Cuidado con el tipo. El Editor de configuración preselecciona Booleano. Crear la preferencia sin cambiar a Número almacena true en lugar de un puerto, y Zotero entonces abre el puente en una tubería local en lugar de un puerto TCP — el registro de depuración informa éxito mientras ningún cliente MCP puede conectarse.

2. Lado del cliente — establece ZOTERO_RDP_PORT al mismo valor en la configuración de tu cliente MCP:

{
  "mcpServers": {
    "zotero-dev": {
      "command": "npx",
      "args": ["-y", "@introfini/mcp-server-zotero-dev@1.1.2"],
      "env": {
        "ZOTERO_RDP_PORT": "6101"
      }
    }
  }
}

Cambia ambos o ninguno. Mover solo un lado desconecta el puente: Zotero escucha en un puerto mientras el cliente sigue marcando el otro.

Ejecutar dos instancias realmente

Iniciar Zotero por segunda vez te entrega la ventana que ya tienes — como Firefox, reenvía a la instancia en ejecución en lugar de iniciar otra. Una segunda instancia necesita su propio perfil y -no-remote:

# macOS; adjust the binary path on Windows/Linux
MOZ_NO_REMOTE=1 "/Applications/Zotero.app/Contents/MacOS/zotero" -P <profile-name> -no-remote

Dale a ese perfil su propio extensions.mcp-rdp.port y los dos puentes no se interpondrán entre sí. Verificado con 9.0.6 en 6100 y 10.0-beta.22 en 6101 al mismo tiempo.

Requiere el plugin MCP Bridge 1.0.5 o posterior. En 1.0.4 y anteriores, extensions.mcp-rdp.port se leía bajo la rama de preferencias incorrecta y se ignoraba silenciosamente, por lo que el puente permanecía en 6100 sin importar lo que configuraras. Si configuraste un puerto personalizado contra una compilación anterior, se almacena como extensions.zotero.extensions.mcp-rdp.port — ese nombre aún funciona, pero prefiere el anterior.

Deshabilitar el puente

Establece extensions.mcp-rdp.enabled a false (Booleano) en el Editor de configuración y reinicia Zotero. El plugin permanece instalado pero no abre ningún listener, y ningún cliente MCP puede alcanzar Zotero hasta que lo vuelvas a establecer en true.

Verificar qué hizo el puente

El plugin agrega una línea por cada transición del ciclo de vida a mcp-rdp-events.log en tu directorio de perfil de Zotero: inicio, listener abierto, listener caído, listener recuperado, listener fluctuante, apagado. Sobrevive a los reinicios y es legible sin que Zotero esté ejecutándose, lo que lo convierte en el primer lugar para mirar cuando un cliente MCP reporta Cannot connect to Zotero RDP:

2026-09-17T07:52:36.201Z startup v1.0.5 reason=1
2026-09-17T07:52:37.914Z listener DOWN on port 6177 - failed to open at startup: port 6177 does not answer (held by another process?)
2026-09-17T07:53:46.552Z listener RECOVERED on port 6177 after 6 failed checks, 69s down
2026-09-17T08:01:12.083Z shutdown v1.0.5 reason=2

Qué leer de él:

  • listener DOWN … failed to open at startup — algo más mantiene el puerto: otra instancia de Zotero, una anterior que no lo ha liberado, o un proceso que responde en el puerto sin hablar RDP. La razón después de los dos puntos indica cuál de las dos últimas es.
  • listener DOWN … stopped answering — el listener estaba activo y luego murió. La comprobación de salud lo reabre; la siguiente línea te dice cuándo funcionó y cuánto duró la brecha.
  • listener RECOVERED … after N failed checks — el puente volvió por sí solo. Una N grande significa que el puerto estuvo ocupado durante mucho tiempo; no se registra nada por intento, por lo que el archivo permanece corto sin importar cuánto dure la interrupción.
  • listener FLAPPING, luego cerrado por listener STEADY … after N flaps — el listener sigue muriendo y volviendo directamente en la primera reapertura. Eso es una falla diferente a una interrupción: el puerto es tuyo, algo está derribando el listener. Toda la ejecución cuesta estas dos líneas sin importar cuánto continúe, así que N es el número a reportar si abres un problema.
  • Un startup sin shutdown antes — Zotero fue eliminado o se bloqueó en lugar de salir limpiamente. Generalmente, la respuesta a "el puente dejó de funcionar" es simplemente que Zotero no está en ejecución.
  • Sin líneas nuevas en absoluto — el plugin nunca se inició: deshabilitado por preferencia, o no instalado en el perfil que realmente estás ejecutando.

La salida de log() va a dump() (se pierde a menos que Zotero se inicie desde una consola) y Zotero.debug() (una operación no operativa a menos que la salida de depuración esté habilitada), por lo que este archivo es el único registro duradero de una falla al arrancar. Una sesión saludable agrega tres líneas (inicio, apertura, cierre); una interrupción agrega dos más, y una serie de aleteos dos más, sin importar su duración. Ningún modo de falla escribe por tick.


📸 Ejemplos de capturas de pantalla

// Capture main Zotero window
await zotero_screenshot({ target: 'main-window' });

// Capture your plugin's panel with highlight
await zotero_screenshot({
  target: 'element',
  selector: '#my-plugin-panel',
  highlightSelector: '#my-plugin-button'
});

// Capture a specific window by ID (use zotero_list_windows to find IDs)
await zotero_screenshot({
  target: 'window',
  windowId: 12345
});

// Capture element after triggering UI action
await zotero_execute_js({ code: 'document.querySelector("#menu").click()' });
await zotero_screenshot({ target: 'element', selector: 'menupopup[state="open"]' });

🧑‍💻 Desarrollo

# Clone and install
git clone https://github.com/introfini/mcp-server-zotero-dev.git
cd mcp-server-zotero-dev
npm install

# Build everything
npm run build

# Build individual packages
npm run build:server
npm run build:plugin

# Run tests
npm test

# Development mode (watch)
npm run dev
Estructura del proyecto
mcp-server-zotero-dev/
├── packages/
│   ├── mcp-server/               # MCP server (npm package)
│   │   ├── src/
│   │   │   ├── index.ts          # MCP server entry
│   │   │   ├── rdp/              # RDP client
│   │   │   ├── tools/            # Tool implementations
│   │   │   └── prompts/          # Slash commands
│   │   └── package.json
│   │
│   └── zotero-plugin-mcp-rdp/    # Tiny Zotero plugin (.xpi)
│       ├── src/
│       │   └── bootstrap.js      # Starts RDP server (shipped verbatim)
│       ├── addon/
│       │   └── manifest.json
│       └── package.json
│
├── docs/                         # Documentation
└── package.json                  # Monorepo root

📚 Recursos


🤝 Contribuciones

Las contribuciones son bienvenidas. Consulta CONTRIBUTING.md para la configuración, las convenciones de prueba y las reglas específicas del código que vale la pena conocer antes de comenzar.

La versión corta:

  1. Sigue los patrones de código existentes
  2. Agrega pruebas para nuevas funciones, y omite en lugar de fallar cuando Zotero no está en ejecución
  3. Actualiza la documentación
  4. No hay CI, así que ejecuta npm run build, npm run typecheck, npm run lint y npm test tú mismo, y menciona en el PR qué versión de Zotero verificaste

📄 Licencia

MIT © introfini


Agradecimientos