Files
claude-plugin-nvim-agentic/skills/editor-introspect/SKILL.md
T
oleks 3c5851e95e v0.5.0: route the neovim MCP through MetaMCP
mcp-neovim-server no longer runs as a per-session npx stdio child.
It is now a MetaMCP namespace upstream on emmett (one persistent
process), reached over Streamable-HTTP via the mcp-session-pool:

  Claude Code -> pool.localhost:12010/p/neovim/mcp
    -> MetaMCP neovim namespace -> mcp-neovim-server -> nvim socket

- .mcp.json: stdio npx server -> streamable-http pool endpoint
- agents/companion.md + skills: tool names are now 3-segment
  (mcp__neovim__neovim__vim_*) — the MetaMCP aggregation shape; the
  surface description and unavailability runbook updated for the
  three-link path (nvim socket / MetaMCP / session)
- README: connection-path diagram

Host side (servers/emmett, deployed separately): neovim added to
services.mcp-session-pool.upstreams; metamcp.nix seeds the neovim
mcp_servers row + mapping and adds nodejs to the unit PATH.
2026-05-21 11:19:49 +03:00

5.3 KiB

name, description, disable-model-invocation, allowed-tools
name description disable-model-invocation allowed-tools
editor-introspect Read-only inspection of the user's running Neovim instance via `mcp__neovim__neovim__*` tools. Use whenever a question depends on the live editor state: what is mapped to a key, which buffers are open, what the cursor is on, what diagnostics exist, which plugins are loaded, what the messages buffer says. Returns concrete facts grounded in the running instance — never guesses from training data. Trigger on "what's bound to", "what's mapped to", "what buffers", "show me the keymaps", "is X loaded", "what plugins are active", "current selection", "what's the cursor on", "any diagnostics", "lualine theme actually applied". false Bash, Read, Glob, Grep, mcp__neovim__neovim__*

editor-introspect — read the live nvim instance

Owner: nvim-agentic-companion:companion. This skill is for reading the running editor. Never use it to mutate state — use editor-act for that.

Preconditions

The user's nvim must be running with an RPC socket reachable by the mcp-neovim-server MCP server. If mcp__neovim__neovim__* tools are not present in this session, stop and tell the user:

The neovim MCP server isn't connected to this Claude Code session. Start (or restart) nvim so it creates the socket at {{nvim_socket}}, then restart Claude Code.

Do not try to fake introspection from the config alone.

The tools you have

mcp-neovim-server exposes these (note the vim_ prefix, not nvim_):

  • mcp__neovim__neovim__vim_command — run any : command, including :lua. Returns the rendered command output. This is the workhorse for introspection: pass :verbose nmap ..., :lua print(...), etc.
  • mcp__neovim__neovim__vim_status — cursor, mode, marks, registers in one call. Use instead of three separate :lua calls.
  • mcp__neovim__neovim__vim_buffer — buffer contents with line numbers.
  • mcp__neovim__neovim__vim_health — check the nvim↔server connection; run this first if anything seems disconnected.

There is no eval tool. To get a lua value, call vim_command with :lua print(vim.inspect(<expr>)) and parse the printed output.

The introspection vocabulary

Queries to reach for first. Unless noted, run each as the command argument to mcp__neovim__neovim__vim_command.

Keymaps

  • verbose nmap <leader>ff — what (and where in lua) is <leader>ff?
  • verbose imap <C-Space> — same for insert mode.
  • nmap <leader> — all normal-mode mappings starting with leader.
  • For machine-readable form prefer: lua print(vim.inspect(vim.api.nvim_get_keymap("n"))) …then grep client-side. nvim_get_keymap returns the LHS, RHS, mode, desc, and the file/line set when known.

Buffers, windows, tabs

  • ls! — list buffers, including hidden.
  • lua print(vim.inspect(vim.api.nvim_list_bufs()))
  • lua print(vim.api.nvim_buf_get_name(0)) — current buffer path.
  • lua print(vim.inspect(vim.api.nvim_tabpage_list_wins(0)))
  • For the visible layout the user is staring at, prefer windows over buffers.

Cursor, selection, mode

  • lua print(vim.inspect(vim.api.nvim_win_get_cursor(0)))[row, col].

  • lua print(vim.fn.mode())n, i, v, V, ^V, etc.

  • For the current visual selection text:

    :lua local v, c = vim.fn.getpos("v"), vim.fn.getpos(".")
    :lua print(vim.fn.getregion(v, c, {type = vim.fn.mode()}))
    

    (Neovim ≥ 0.10).

Options

  • set option? — value of a single option (set number?).
  • verbose set option?and the file that last set it.
  • lua print(vim.bo.filetype) / vim.wo.wrap / vim.o.background.

Diagnostics

  • lua print(vim.inspect(vim.diagnostic.get(0))) — current buf diags.
  • lua print(vim.inspect(vim.diagnostic.count())) — counts by severity.
  • lua print(vim.lsp.get_active_clients() | vim.iter ... | ...) — which LSPs are attached.

Plugins / runtime

  • lua print(vim.inspect(package.loaded["lualine"]) ~= nil) — is X loaded?
  • messages — startup warnings, last few notifications.
  • checkhealth <module> — for a single module; the long-form output is huge, so prefer specific modules (e.g. :checkhealth lualine) over :checkhealth whole.
  • For the lualine theme actually in use: lua print(require("lualine").get_config().options.theme)

"Why is X happening?"

When the user reports a symptom, the introspection script is:

  1. messages — did something fail at startup?
  2. :checkhealth <suspected-module> — does the module itself complain?
  3. verbose set <option>? or verbose nmap <key> — is something overriding?
  4. vim.lsp.get_active_clients() / vim.diagnostic.get(0) — is LSP behaving?

Stop as soon as you have the answer. Don't sweep when one query suffices.

How to report findings

  • Quote the exact runtime output you got, not a paraphrase.
  • When citing config provenance, give file_path:line_number from {{config_path}} (the user's NixVim source).
  • If runtime disagrees with config, say so explicitly: "Config sets X but loaded value is Y — likely a stale build."
  • For long outputs, summarise and offer to drill in. Don't paste a 200-line :messages dump verbatim unless the user asks.