# mcp-server-zotero-dev [Health: Active]

**Category:** 🧠 Knowledge & Memory  
**Repository:** https://github.com/introfini/mcp-server-zotero-dev  
**GitHub Stars:** 39  
**Views:** 0  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/mcp-server-zotero-dev

## Description
MCP server enabling AI assistants to build, test, and debug Zotero plugins

## Claude Desktop Quick Installation
Install path detected from listing signals. Uses `npx` (confidence: high):

```json
"mcpServers": {
  "mcp-server-zotero-dev": {
    "command": "npx",
    "args": ["-y","install-mcp"]
  }
}
```

## Documentation & README

<div align="center">

# MCP Server Zotero Dev

**Give your AI assistant superpowers for Zotero plugin development**

[![License: MIT](https://img.shields.io/badge/License-MIT-8b5cf6.svg?style=flat-square)](LICENSE)
[![Zotero 7+](https://img.shields.io/badge/Zotero-7%2B-CC2936?style=flat-square&logo=zotero&logoColor=white)](https://www.zotero.org)

[Architecture](https://github.com/introfini/mcp-server-zotero-dev/blob/HEAD/ARCHITECTURE.md) · [Getting Started](#quick-start) · [Available Tools](#available-tools)

<img src="https://raw.githubusercontent.com/introfini/mcp-server-zotero-dev/HEAD/docs/images/demo.png" alt="MCP Server Zotero Dev in action" width="800">

</div>

---

A **Model Context Protocol (MCP) server** that enables AI assistants like Claude, Cursor, and Windsurf to build, test, and debug [Zotero](https://www.zotero.org) 7, 8, 9, and 10 plugins. Screenshots, DOM state, debug logs, and JavaScript execution give the AI rich context to understand what's happening—and tools to help you fix it.

## ✨ Features

| Category | Capabilities |
|----------|-------------|
| 🎯 **UI Inspection** | Screenshots, DOM tree, element finding, computed styles |
| 🖱️ **UI Interaction** | Click elements and type text (shadow-DOM aware) |
| 💻 **JS Execution** | Run code in Zotero context, inspect APIs, test snippets |
| 🔧 **Build Tools** | Scaffold integration for build, serve, hot reload |
| 📋 **Logs & Errors** | Stream debug output, error console, watch for issues |
| 🗃️ **Database** | Read-only access to zotero.sqlite for debugging |
| 🔌 **Plugin Management** | Install, reload, list plugins |

---

## 🚀 Quick Start

### Prerequisites

- **Node.js** 20+ and npm
- **Zotero 7+** — Works on all Zotero 7, 8, 9, and 10 builds (release, beta, dev)
- For plugin development: [zotero-plugin-scaffold](https://github.com/windingwind/zotero-plugin-scaffold)

### 1. Install MCP Server

Use [install-mcp](https://www.npmjs.com/package/install-mcp) to add the server to your AI assistant:

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

Supported clients: `claude-code`, `cursor`, `windsurf`, `vscode`, `cline`, `roo-cline`, `claude`, `zed`, `goose`, `warp`, `codex`

<details>
<summary><strong>Claude Code</strong></summary>

```bash
npx -y install-mcp @introfini/mcp-server-zotero-dev --client claude-code
```
</details>

<details>
<summary><strong>Cursor</strong></summary>

```bash
npx -y install-mcp @introfini/mcp-server-zotero-dev --client cursor
```
</details>

<details>
<summary><strong>VS Code / Copilot</strong></summary>

```bash
npx -y install-mcp @introfini/mcp-server-zotero-dev --client vscode
```
</details>

<details>
<summary><strong>Windsurf</strong></summary>

```bash
npx -y install-mcp @introfini/mcp-server-zotero-dev --client windsurf
```
</details>

<details>
<summary><strong>Manual Configuration</strong></summary>

Add to your MCP client config:

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

> **Version & updates**: pin an exact version as shown above. A bare `npx <pkg>` (no version) keeps running whatever `npx` cached and won't pick up new releases, so always include a version and `-y` (without `-y`, `npx` hangs waiting for an install prompt). Bump the pinned version to upgrade, or use `@latest` to always fetch the newest at launch (auto-updates, but a bad release would run automatically and it adds a registry check on every start). Note that `install-mcp` may write a config without `-y` or a version, so the manual configuration above is the most robust path.

**Restart your AI assistant** after adding the configuration.

### 2. Install MCP Bridge Plugin in Zotero

Download [zotero-mcp-bridge.xpi](https://github.com/introfini/mcp-server-zotero-dev/releases/latest) and install:

1. In Zotero: **Tools → Plugins**
2. Click ⚙️ → **Install Plugin From File**
3. Select the downloaded `.xpi` file
4. **Restart Zotero**

> This lightweight plugin enables the Remote Debugging Protocol when Zotero starts. It only needs to be installed once and works on all Zotero 7+ builds (release, beta, and dev).

### 3. Start Developing!

Just open Zotero normally and ask your AI assistant:

> "Take a screenshot of Zotero and list installed plugins"

That's it! No special launch flags, no configuration. 🎉

---

## 🧰 Available Tools (28 total)

<details>
<summary><strong>UI Inspection</strong> — Screenshots, DOM, styles</summary>

| Tool | Description |
|------|-------------|
| `zotero_screenshot` | Capture window, element, or region screenshots |
| `zotero_inspect_element` | Find elements by CSS selector |
| `zotero_get_dom_tree` | Get DOM structure of a window/panel |
| `zotero_get_styles` | Get computed CSS styles for element |
| `zotero_list_windows` | List all open Zotero windows |

> **Screenshot Targets**: Main window, preferences, PDF reader, dialogs, or any element by selector. Use `highlightSelector` to add a red border before capture.

</details>

<details>
<summary><strong>UI Interaction</strong> — Click and type in the Zotero UI</summary>

| Tool | Description |
|------|-------------|
| `zotero_click_element` | Click an element by CSS selector (toolbar/menu button, preference control, list row). Pierces shadow DOM; `index` picks among multiple matches; `mouseEvents` synthesizes a full mouse sequence. |
| `zotero_send_keys` | Type text into an input/textarea/contenteditable (focuses it first, fires input/change). Optional `clear` and `pressEnter`. |

> Resolution tries light DOM first, then pierces open shadow roots (Zotero's XUL custom elements keep internals in shadow DOM). **Limitation**: cannot dismiss a *blocking* native modal dialog (`Services.prompt.confirmEx`) — its nested modal loop blocks the eval thread these tools run on.

</details>

<details>
<summary><strong>JavaScript Execution</strong> — Run code in Zotero context</summary>

| Tool | Description |
|------|-------------|
| `zotero_execute_js` | Execute JavaScript in Zotero's privileged context. Auto-wraps code with top-level `return` statements in IIFE. |
| `zotero_inspect_object` | Explore Zotero APIs - list methods and properties of any object (e.g., `Zotero.Items`) |
| `zotero_open_preferences` | Open Zotero's settings window, optionally to a specific pane (built-in or plugin) |
| `zotero_search_prefs` | Search/discover preferences by pattern (e.g., find all prefs containing "debug") |
| `zotero_get_pref` | Get a preference value |
| `zotero_set_pref` | Set a preference value |

> **Examples**: `Zotero.Items.getAll(1)`, `Zotero.Prefs.get('export.quickCopy.setting')`, `ZoteroPane.getSelectedItems()`
>
> **Tip**: Use `zotero_inspect_object` to explore APIs before writing code. Use `zotero_search_prefs` to discover preference keys.

</details>

<details>
<summary><strong>Build & Scaffold</strong> — Integration with zotero-plugin-scaffold</summary>

| Tool | Description |
|------|-------------|
| `zotero_scaffold_build` | Build plugin (dev or production mode) |
| `zotero_scaffold_serve` | Start dev server with hot reload |
| `zotero_scaffold_lint` | Run ESLint on plugin source |
| `zotero_scaffold_typecheck` | Run TypeScript type checking |

</details>

<details>
<summary><strong>Logs & Debugging</strong> — Error console and debug output</summary>

| Tool | Description |
|------|-------------|
| `zotero_read_logs` | Read debug output (Zotero.debug) |
| `zotero_read_errors` | Read error console entries |
| `zotero_watch_logs` | Stream logs in real-time |
| `zotero_clear_logs` | Clear log buffer |

</details>

<details>
<summary><strong>Plugin Management</strong> — Install, reload, inspect</summary>

| Tool | Description |
|------|-------------|
| `zotero_plugin_reload` | Hot reload your dev plugin |
| `zotero_plugin_install` | Install plugin from XPI path |
| `zotero_plugin_list` | List installed plugins with version/status |

</details>

<details>
<summary><strong>Database Access</strong> — Read-only SQLite access</summary>

| Tool | Description |
|------|-------------|
| `zotero_db_query` | Execute SELECT query on zotero.sqlite |
| `zotero_db_schema` | Get table schema information |
| `zotero_db_stats` | Get database statistics (items, attachments, collections, size) |

> **Note**: Database access is read-only and requires Zotero to be closed, or uses a copy of the database.

</details>

---

## 🏗️ Architecture

```
┌─────────────────────────────────────────────────────────────────┐
│                        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)                      │   │
│  └──────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────┘
```

**Why this approach?**

- ✅ **Lightweight plugin** — Just enables RDP, Firefox DevTools does the rest
- ✅ **Zero-config after install** — Just open Zotero normally, no special flags
- ✅ **Rich AI context** — Screenshots, DOM, and logs help the AI understand your plugin's state
- ✅ **Hot reload** — Integrates with zotero-plugin-scaffold for instant feedback
- ✅ **Full Zotero access** — Execute any Zotero API in the privileged context
- ✅ **Cross-platform** — Works on Linux, Windows, macOS

---

## 🔧 Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `ZOTERO_RDP_PORT` | Remote debugging port | `6100` |
| `ZOTERO_RDP_HOST` | Debugging host | `127.0.0.1` |
| `ZOTERO_DATA_DIR` | Path to Zotero data directory | Auto-detect |
| `ZOTERO_PROFILE_PATH` | Path to Zotero profile | Auto-detect |

---

## 🔌 Changing the RDP Port

The bridge listens on port `6100` by default. You only need to change it if you run **two Zotero instances at the same time** (a normal profile and a development one, say), or if another process already holds 6100.

The port lives on **both sides of the bridge**, and both have to agree on it.

**1. Zotero side** — set the plugin preference:

1. **Settings → Advanced → Config Editor**, and accept the warning
2. Search for `extensions.mcp-rdp.port`
3. If it doesn't exist, create it: select **Number**, name it `extensions.mcp-rdp.port`, and enter your port
4. **Restart Zotero** — the listener only opens at startup

> **Watch the type.** The Config Editor pre-selects **Boolean**. Creating the preference without switching to **Number** stores `true` instead of a port, and Zotero then opens the bridge on a local pipe rather than a TCP port — the debug log reports success while no MCP client can connect.

**2. Client side** — set `ZOTERO_RDP_PORT` to the same value in your MCP client config:

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

> **Change both or neither.** Moving only one side disconnects the bridge: Zotero listens on one port while the client keeps dialing the other.

### Actually running two instances

Launching Zotero a second time hands you the window you already have — like Firefox, it forwards to the running instance instead of starting another. A second instance needs its own profile *and* `-no-remote`:

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

Give that profile its own `extensions.mcp-rdp.port` and the two bridges stay out of each other's way. Verified with 9.0.6 on 6100 and 10.0-beta.22 on 6101 at the same time.

> **Requires MCP Bridge plugin 1.0.5 or later.** In 1.0.4 and earlier, `extensions.mcp-rdp.port` was read under the wrong preference branch and silently ignored, so the bridge stayed on 6100 no matter what you set. If you configured a custom port against an older build, it is stored as `extensions.zotero.extensions.mcp-rdp.port` — that name still works, but prefer the one above.

### Disabling the bridge

Set `extensions.mcp-rdp.enabled` to `false` (**Boolean**) in the Config Editor and restart Zotero. The plugin stays installed but opens no listener, and no MCP client can reach Zotero until you set it back to `true`.

---

## 📸 Screenshot Examples

```typescript
// 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"]' });
```

---

## 🧑‍💻 Development

```bash
# 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
```

<details>
<summary><strong>Project Structure</strong></summary>

```
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
```

</details>

---

## 📚 Resources

- **[Architecture & Technical Learnings](https://github.com/introfini/mcp-server-zotero-dev/blob/HEAD/ARCHITECTURE.md)** — Deep dive into RDP protocol, actor hierarchy, and common pitfalls
- **[Zotero Plugin Development](https://www.zotero.org/support/dev/client_coding/plugin_development)** — Official docs
- **[Zotero 10 for Developers](https://www.zotero.org/support/dev/zotero_10_for_developers)** — Migration guide for the latest major
- **[Zotero 7 for Developers](https://www.zotero.org/support/dev/zotero_7_for_developers)** — Migration guide
- **[zotero-plugin-scaffold](https://github.com/windingwind/zotero-plugin-scaffold)** — Build tooling
- **[zotero-plugin-template](https://github.com/windingwind/zotero-plugin-template)** — Starter template
- **[zotero-plugin-toolkit](https://github.com/windingwind/zotero-plugin-toolkit)** — API helpers
- **[Firefox RDP Protocol](https://firefox-source-docs.mozilla.org/devtools/backend/protocol.html)** — Protocol docs

---

## 🤝 Contributing

Contributions are welcome. See **[CONTRIBUTING.md](https://github.com/introfini/mcp-server-zotero-dev/blob/HEAD/CONTRIBUTING.md)** for setup, test conventions and the codebase-specific rules worth knowing before you start.

The short version:

1. Follow existing code patterns
2. Add tests for new features, and skip rather than fail when Zotero is not running
3. Update documentation
4. There is no CI, so run `npm run build`, `npm run typecheck`, `npm run lint` and `npm test` yourself, and say in the PR which Zotero version you verified against

---

## 📄 License

MIT © [introfini](https://github.com/introfini)

---

## Acknowledgments

- Built for the [Zotero](https://www.zotero.org) plugin developer community
- Integrates with [zotero-plugin-scaffold](https://github.com/windingwind/zotero-plugin-scaffold) by [@windingwind](https://github.com/windingwind)
- Leverages Firefox DevTools RDP for reliable communication

