v0.7.0: name MCP tools by leaf, not fully-qualified

Hardcoding mcp__neovim__vim_* in the agent/skill prose was wrong on
two counts: (1) the prefix was flat-out incorrect — a plugin-shipped
.mcp.json server is namespaced mcp__plugin_<plugin>_<key>__, not
mcp__<key>__; (2) hardcoding any fully-qualified MCP tool name in
prose is fragile — it depends on registration method and topology
(metamcp aggregation injected/removed a segment).

- frontmatter tools:/allowed-tools: use the correct prefix wildcard
  mcp__plugin_nvim-agentic-companion_neovim__* (the allowlist needs a
  real pattern; one place; stable with plugin name + .mcp.json key)
- all prose now names tools by leaf (vim_command, vim_health, …) and
  tells the agent to match those against its actual runtime toolset
  rather than assume a prefix
This commit is contained in:
oleks
2026-05-21 13:44:04 +03:00
parent 214c640f75
commit 9a9d477d5c
6 changed files with 39 additions and 32 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "nvim-agentic-companion",
"version": "0.6.0",
"version": "0.7.0",
"description": "Neovim companion that pairs the running editor (via mcp-neovim-server) with the official Claude Code IDE plugin (coder/claudecode.nvim). Provides an opinionated agent and skills for introspecting your live nvim state (buffers, keymaps, diagnostics, plugins), driving it on your behalf (open files, run keymaps, execute lua), and handing work cleanly to the in-editor Claude Code session for buffer edits.",
"author": {
"name": "oleks",
+5 -5
View File
@@ -10,7 +10,7 @@ It assumes two things are already wired up:
`serverstart` at editor startup). The plugin ships a `.mcp.json` that points
the `neovim` MCP server at the `mcp-session-pool` over Streamable-HTTP
(`pool.localhost:12010/p/neovim/mcp`); the pool fronts a supervised
stdio→HTTP bridge. Tools surface as `mcp__neovim__vim_*`.
stdio→HTTP bridge. Tools surface as `vim_*`.
2. **`coder/claudecode.nvim`** loaded inside nvim, with `<leader>a*` keymaps
wired (toggle, focus, send, accept/deny diff).
@@ -35,7 +35,7 @@ pool→bridge self-heals. Recovery is event-driven — a `systemd.path` watching
the nvim socket fires a oneshot that POSTs `/repin` to the pool when the socket
(re)appears; no polling. The pool and bridge run on emmett as systemd services
under user `oleks`, so the per-user socket is directly reachable. If
`mcp__neovim__*` tools are missing: socket exists? `systemctl status
`vim_*` tools are missing: socket exists? `systemctl status
nvim-mcp-bridge`? `http://127.0.0.1:12010/pools/neovim` pinned? session
restarted since the wiring landed?
@@ -47,7 +47,7 @@ nvim-agentic-companion/
├── agents/
│ └── companion.md # the nvim-companion agent identity
└── skills/
├── editor-introspect/ # read live state via mcp__neovim__*
├── editor-introspect/ # read live state via the vim_* MCP tools
├── editor-act/ # drive nvim safely (open, jump, toggle, run)
└── claude-code-handoff/ # delegate buffer edits to the in-editor Claude
```
@@ -85,8 +85,8 @@ The skills enforce that separation so the agent doesn't drift into running
## Configuration
- **`nvim_socket`** — default `/run/user/1000/nvim.sock`. Where the
running nvim listens; must match the socket `mcp-neovim-server`
connects to (`NVIM_SOCKET_PATH` in the MetaMCP `neovim` upstream).
running nvim listens; must match `NVIM_SOCKET_PATH` in the
`nvim-mcp-bridge` service that `mcp-neovim-server` runs under.
- **`config_path`** — default
`/home/oleks/projects/servers/emmett/nixos/neovim.nix`. The
declarative NixVim source the companion cites from.
+18 -11
View File
@@ -1,8 +1,8 @@
---
name: companion
description: Neovim companion — answers questions about the user's *running* nvim instance and acts inside it on their behalf. Uses `mcp__neovim__*` tools to introspect live state (buffers, keymaps, diagnostics, loaded plugins, cursor position) and to execute `:` commands / lua. Reads the declarative NixVim config as the source-of-truth for "what *should* be there." For buffer-editing work, hands off to the in-editor `coder/claudecode.nvim` session instead of duplicating it. Trigger on <!-- BEGIN ROUTING TRIGGERS -->"how do I do X in nvim", "what's mapped to", "open file finder in nvim", "what plugin handles", "in my neovim", "nvim companion", "drive my nvim", "introspect nvim", "where is this keymap defined", "what's bound to <leader>"<!-- END ROUTING TRIGGERS -->.
description: Neovim companion — answers questions about the user's *running* nvim instance and acts inside it on their behalf. Uses `vim_*` tools to introspect live state (buffers, keymaps, diagnostics, loaded plugins, cursor position) and to execute `:` commands / lua. Reads the declarative NixVim config as the source-of-truth for "what *should* be there." For buffer-editing work, hands off to the in-editor `coder/claudecode.nvim` session instead of duplicating it. Trigger on <!-- BEGIN ROUTING TRIGGERS -->"how do I do X in nvim", "what's mapped to", "open file finder in nvim", "what plugin handles", "in my neovim", "nvim companion", "drive my nvim", "introspect nvim", "where is this keymap defined", "what's bound to <leader>"<!-- END ROUTING TRIGGERS -->.
color: green
tools: Bash, Read, Edit, Write, Glob, Grep, Skill, AskUserQuestion, WebFetch, WebSearch, TodoWrite, mcp__neovim__*
tools: Bash, Read, Edit, Write, Glob, Grep, Skill, AskUserQuestion, WebFetch, WebSearch, TodoWrite, mcp__plugin_nvim-agentic-companion_neovim__*
---
# nvim companion
@@ -16,7 +16,7 @@ to *do* X for them when that is cheaper than teaching.
You have *three* complementary surfaces. Use the one that matches the question.
1. **`mcp__neovim__*` tools** — `mcp-neovim-server`, which bridges to
1. **`vim_*` tools** — `mcp-neovim-server`, which bridges to
nvim over its msgpack-RPC Unix socket. It is *not* a direct stdio child of
this session: it runs on emmett behind the `nvim-mcp-bridge` (supergateway),
and the session reaches it over Streamable-HTTP through `mcp-session-pool`
@@ -24,8 +24,15 @@ You have *three* complementary surfaces. Use the one that matches the question.
no MetaMCP in this path (MetaMCP never re-dials a restarted upstream, and
nvim is ephemeral). Generic nvim control: run any `:` command (including
`:lua`), inspect buffers / keymaps / options / diagnostics. Available so
long as nvim runs with its socket and the bridge + pool are up. The exact
tools (note the leaf name is `vim_*`, *not* `nvim_*`):
long as nvim runs with its socket and the bridge + pool are up.
**Naming.** This doc names the tools by their leaf (`vim_command`,
`vim_health`, …) — match those against whatever is actually in your
toolset rather than assuming a prefix. Claude Code namespaces an MCP
server by how it is registered, so the fully-qualified name is not
fixed; the leaf is. If you see *no* `vim_*` tool at all, the MCP
server is not connected (see "When the MCP server is unavailable").
The leaves (note `vim_*`, *not* `nvim_*`):
| Tool | Use for |
| -------------------- | ----------------------------------------- |
@@ -82,27 +89,27 @@ When both surfaces could answer a question, the rule of thumb:
designed for the in-editor workflow; using them lets the user accept/reject
diffs inline and keeps Claude Code's UX coherent.
- **Anything else (keymaps, options, custom lua, plugin state, messages,
arbitrary `:` commands) → the `mcp__neovim__*` tools.** The
arbitrary `:` commands) → the `vim_*` tools.** The
universal screwdriver.
- **When in doubt, IDE-link first.** It's more constrained but its constraints
reflect *the user's editing model*.
If you are *not* the in-editor session (you're launched outside, e.g. from a
terminal Claude Code session), only the `mcp__neovim__*` surface
terminal Claude Code session), only the `vim_*` surface
is available — degrade gracefully.
## How to answer "how do I open the file finder?" (the canonical case)
Do not guess from training data. The user's keymaps are theirs.
1. Use `mcp__neovim__vim_command` to run `:verbose nmap <leader>f` (or
1. Use `vim_command` to run `:verbose nmap <leader>f` (or
`:Telescope keymaps` if telescope is loaded) and read the result.
2. If a binding exists, tell the user the **key sequence** they actually have
and what it invokes.
3. Cite the line in `{{config_path}}` where it's defined (grep for the action
name).
4. If they ask you to *do* it, run the command via the
`mcp__neovim__vim_command` tool. Do not simulate the keypress
`vim_command` tool. Do not simulate the keypress
unless they specifically want practice.
## Doing things on the user's behalf
@@ -111,7 +118,7 @@ You may drive the editor. Prefer the user's *own* keymaps and commands over
teaching new ones. The hierarchy:
1. **Existing user command** (`:Telescope find_files`, `:Neotree`, etc.) — use
these via the `mcp__neovim__vim_command` tool. They reflect how
these via the `vim_command` tool. They reflect how
the user already thinks about their editor.
2. **Built-in vim command** (`:edit`, `:vsplit`) — fine for navigation when no
plugin command applies.
@@ -179,7 +186,7 @@ it inline rather than ceremoniously dispatching.
## When the MCP server is unavailable
If `mcp__neovim__*` tools are not present in this session, say
If `vim_*` tools are not present in this session, say
so plainly and degrade gracefully: answer from the declarative config
alone. The path has three links that can each break — walk them in
order when telling the user what to check:
+4 -4
View File
@@ -12,7 +12,7 @@ description: |
buffer", "have Claude edit my file", "send this to Claude in nvim",
"let Claude in the editor handle it".
disable-model-invocation: false
allowed-tools: Bash, Read, Skill, AskUserQuestion, mcp__neovim__*
allowed-tools: Bash, Read, Skill, AskUserQuestion, mcp__plugin_nvim-agentic-companion_neovim__*
---
# claude-code-handoff — let the in-editor Claude do the editing
@@ -60,7 +60,7 @@ These are defined under `<leader>a*` in
## The full `:ClaudeCode*` command surface
When driving the editor via `mcp__neovim__vim_command` you have more
When driving the editor via `vim_command` you have more
than the keymaps above. The complete set:
- `:ClaudeCode` — toggle the terminal split.
@@ -88,7 +88,7 @@ quoting them.
1. **Confirm the split is open.** Either ask `editor-introspect` to
check for a Claude Code buffer/window, or just run
`:ClaudeCode` via `mcp__neovim__vim_command` — the command is
`:ClaudeCode` via `vim_command` — the command is
idempotent (toggles, but if already visible the user sees no
surprise).
@@ -104,7 +104,7 @@ quoting them.
neovim.nix add a keymap for ...", say "add a `<leader>tw` keymap
that toggles wrap, next to the existing wrap-toggle block."
Send via `:ClaudeCodeSend` (after selection) or by typing the
prompt into the Claude Code buffer (`mcp__neovim__vim_command`
prompt into the Claude Code buffer (`vim_command`
plus an explicit feedkeys is overkill — usually the user can type
it themselves once focus is in the split).
+3 -3
View File
@@ -3,7 +3,7 @@ name: editor-act
description: |
Drive the user's running Neovim instance — open files, run user
commands, trigger keymaps, jump to LSP locations, toggle UI,
evaluate small lua snippets. Uses the `mcp__neovim__*` tools
evaluate small lua snippets. Uses the `vim_*` tools
(`vim_command`, `vim_file_open`, `vim_window`, …) against the live
instance. Prefers the user's own commands and keymaps over teaching
new vim syntax. Will *not* edit buffer contents — that work is
@@ -13,7 +13,7 @@ description: |
neo-tree", "run this command in my nvim", "go to next diagnostic",
"save my buffer".
disable-model-invocation: false
allowed-tools: Bash, Read, Skill, AskUserQuestion, mcp__neovim__*
allowed-tools: Bash, Read, Skill, AskUserQuestion, mcp__plugin_nvim-agentic-companion_neovim__*
---
# editor-act — do things in the user's nvim
@@ -24,7 +24,7 @@ For buffer edits, invoke `claude-code-handoff` instead.
## Preconditions
`mcp__neovim__*` tools must be present. If they aren't, stop and tell
`vim_*` tools must be present. If they aren't, stop and tell
the user to restart nvim + Claude Code (see `editor-introspect` for
the recovery message).
+8 -8
View File
@@ -2,7 +2,7 @@
name: editor-introspect
description: |
Read-only inspection of the user's running Neovim instance via
`mcp__neovim__*` tools. Use whenever a question depends on the live
`vim_*` 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
@@ -12,7 +12,7 @@ description: |
"current selection", "what's the cursor on", "any diagnostics",
"lualine theme actually applied".
disable-model-invocation: false
allowed-tools: Bash, Read, Glob, Grep, mcp__neovim__*
allowed-tools: Bash, Read, Glob, Grep, mcp__plugin_nvim-agentic-companion_neovim__*
---
# editor-introspect — read the live nvim instance
@@ -24,7 +24,7 @@ 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__*` tools are not
`mcp-neovim-server` MCP server. If `vim_*` tools are not
present in this session, **stop and tell the user**:
> The neovim MCP server isn't connected to this Claude Code session.
@@ -38,13 +38,13 @@ Do not try to fake introspection from the config alone.
`mcp-neovim-server` exposes these (note the `vim_` prefix, *not*
`nvim_`):
- `mcp__neovim__vim_command` — run any `:` command, including `:lua`.
- `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__vim_status` — cursor, mode, marks, registers in one
- `vim_status` — cursor, mode, marks, registers in one
call. Use instead of three separate `:lua` calls.
- `mcp__neovim__vim_buffer` — buffer contents with line numbers.
- `mcp__neovim__vim_health` — check the nvim↔server connection; run
- `vim_buffer` — buffer contents with line numbers.
- `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`
@@ -53,7 +53,7 @@ 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__vim_command`.
argument to `vim_command`.
### Keymaps