Skip to main content
Glama

nvim-ui-mcp

Playwright for Neovim agents.

An MCP server that lets AI agents see, act on, and verify the real, rendered Neovim UI — not just buffer text and API state.

License: MIT Node Neovim npm


Table of Contents

Requirements

Installation

npm install -g nvim-ui-mcp

MCP Client Configuration

claude mcp add nvim-ui -- npx -y nvim-ui-mcp
{
  "mcpServers": {
    "nvim-ui": {
      "command": "npx",
      "args": ["-y", "nvim-ui-mcp"]
    }
  }
}

Tools

Exactly 8 tools — deliberately not a generic nvim_* API wrapper.

Usage

The loop is observe → act → wait → observe:

// 1. Start an isolated instance
nvim_launch  { "clean": true, "rows": 24, "cols": 80 }
//          → { "sessionId": "nvim-1", ... }

// 2. Look at the real screen
nvim_observe { "sessionId": "nvim-1" }
//          → { "screen": "…24 rows of text…", "cursor": {...}, "mode": {...} }

// 3. Act
nvim_input   { "sessionId": "nvim-1", "keys": "ihello world<Esc>" }

// 4. Synchronize before looking again — see Gotchas below
nvim_wait    { "sessionId": "nvim-1", "condition": "contains", "text": "hello world" }

// 5. Verify, cheaply
nvim_observe_diff { "sessionId": "nvim-1" }
//          → { "changed": true, "rowChanges": [{ "row": 0, "after": "hello world…" }] }

nvim_close   { "sessionId": "nvim-1" }

Gotchas

nvim_input and nvim_command return as soon as Neovim accepts the request — not when the screen has repainted. Observing immediately races the redraw.

contains / not-contains test current screen content, so they work whether the redraw lands before or after the wait started. redraw, screen-change, and idle are edge-triggered and can miss a repaint that already happened — use idle only when you cannot predict the resulting text.

Neovim composites floats into a single grid, so popup and float content is already in the rendered text. The floats array from nvim_observe reports each float's row, col, width, and height — how you tell a completion popup from buffer text at the same coordinates.

Attach Mode

nvim --listen /tmp/nvim.sock
nvim_attach { "address": "/tmp/nvim.sock" }

The instance belongs to you, so nvim_close refuses it. Neovim sizes the screen to the smallest attached UI, so requesting a size can shrink your own view.

Development

npm install
npm test          # unit + integration, needs a real nvim binary
npm run typecheck
npm run lint
npm run build

See DESIGN.md for the architecture, the UI event pipeline, and the known gaps.

License

MIT