*mcphub.nvim.txt*        For NVIM v0.10.0        Last change: 2025 December 12

==============================================================================
Table of Contents                              *mcphub.nvim-table-of-contents*

1. What is MCP HUB?                             |mcphub.nvim-what-is-mcp-hub?|
  - How does MCP Hub work?|mcphub.nvim-what-is-mcp-hub?-how-does-mcp-hub-work?|
  - Feature Support Matrix|mcphub.nvim-what-is-mcp-hub?-feature-support-matrix|
  - Next Steps                       |mcphub.nvim-what-is-mcp-hub?-next-steps|
2. Installation                                     |mcphub.nvim-installation|
  - Requirements                       |mcphub.nvim-installation-requirements|
  - Lazy.nvim                             |mcphub.nvim-installation-lazy.nvim|
  - NixOS                                     |mcphub.nvim-installation-nixos|
3. Configuration                                   |mcphub.nvim-configuration|
  - Default Configuration    |mcphub.nvim-configuration-default-configuration|
  - Binary mcp-hub Options  |mcphub.nvim-configuration-binary-mcp-hub-options|
  - Chat-Plugin Related Options|mcphub.nvim-configuration-chat-plugin-related-options|
  - Plugin Options                  |mcphub.nvim-configuration-plugin-options|
4. MCP Servers                                       |mcphub.nvim-mcp-servers|
  - MCP Config File                  |mcphub.nvim-mcp-servers-mcp-config-file|
  - Lua MCP Servers                  |mcphub.nvim-mcp-servers-lua-mcp-servers|
5. Extensions                                         |mcphub.nvim-extensions|
  - Avante Integration             |mcphub.nvim-extensions-avante-integration|
  - CodeCompanion Integration|mcphub.nvim-extensions-codecompanion-integration|
  - CopilotChat Integration   |mcphub.nvim-extensions-copilotchat-integration|
  - Lualine Integration           |mcphub.nvim-extensions-lualine-integration|
6. Other                                                   |mcphub.nvim-other|
7. Links                                                   |mcphub.nvim-links|

==============================================================================
1. What is MCP HUB?                             *mcphub.nvim-what-is-mcp-hub?*

MCPHub.nvim is a MCP client for neovim that seamlessly integrates MCP (Model
Context Protocol) <https://modelcontextprotocol.io/> servers into your editing
workflow. It provides an intuitive interface for managing, testing, and using
MCP servers with your favorite chat plugins.


  [!IMPORTANT] It is recommended to read this page before going through the rest
  of the documentation.

HOW DOES MCP HUB WORK?   *mcphub.nvim-what-is-mcp-hub?-how-does-mcp-hub-work?*

Let’s break down how MCP Hub operates in simple terms:


MCP CONFIG FILE ~

Like any MCP client, MCP Hub requires a configuration file to define the MCP
servers you want to use. This file is typically located at
`~/.config/mcphub/servers.json`. MCP Hub supports local `stdio` servers as well
as remote `streamable-http` or `sse` servers.

**VS Code Compatibility**: MCP Hub supports VS Code’s `.vscode/mcp.json`
format directly, including the `servers` key, `${env:}` syntax, and predefined
variables. You can use the same file for MCP Hub, VS Code, Claude Desktop,
Cursor, Cline, Zed, etc. It looks something like:

>js
    // Example: ~/.config/mcphub/servers.json
    {
      "mcpServers": {
        "fetch": {
          "command": "uvx",
          "args": [
            "mcp-server-fetch"
          ]
        },
        "github": {
          "url": "https://api.githubcopilot.com/mcp/",
          "headers": {
            "Authorization": "Bearer ${GITHUB_PERSONAL_ACCESS_TOKEN}"
          }
        }
      }
    }
<


SERVERS MANAGER ~

- When MCP Hub’s `setup()` is called typically when Neovim starts, it launches the nodejs binary, mcp-hub <https://github.com/ravitemer/mcp-hub> with the `servers.json` file.
- The `mcp-hub` binary reads `servers.json` file and starts the MCP servers.
- It provides two key interfaces:
    1. **Management API** (default: `http://localhost:37373/api`):- Used by this plugin to manage MCP servers
    - Start/stop servers, execute tools, access resources
    - Handle real-time server events
    2. **Unified MCP Endpoint** (`http://localhost:37373/mcp`):- A single MCP server that other MCP clients can connect to
    - Exposes ALL capabilities from ALL managed servers
    - Automatically namespaces capabilities to prevent conflicts
    - Use this endpoint in Claude Desktop, Cline, or any MCP client

For example, instead of configuring each MCP client with multiple servers:

>json
    {
        "mcpServers" : {
            "filesystem": { ... },
            "search": { ... },
            "database": { ... }
        }
    }
<

Just configure them to use MCP Hub’s unified endpoint:

>json
    {
        "mcpServers" : {
            "Hub": {
                "url" : "http://localhost:37373/mcp"  
            }
        }
    }
<


USAGE ~

- Use `:MCPHub` command to open the interface
- Adding (`<A>`), editing (`<e>`), deleting (`<d>`) MCP servers in easy and intuitive with MCP Hub. You don’t need to edit the `servers.json` file directly.
- Install servers from the Marketplace (`M`)
- Toggle servers, tools, and resources etc
- Test tools and resources directly in Neovim


BUILTIN NATIVE SERVERS ~

MCPHub includes two native servers that run directly within Neovim:

- **Neovim Server**: Comprehensive file operations, terminal access, LSP integration, and buffer management
- **MCPHub Server**: Plugin management utilities, server lifecycle control, and documentation access

These servers provide essential functionality without external dependencies and
offer deep Neovim integration.


WORKSPACE-AWARE CONFIGURATION ~

MCP Hub automatically detects project-local configuration files
(`.mcphub/servers.json`, `.vscode/mcp.json`, `.cursor/mcp.json`) and creates
isolated hub instances for each workspace. This enables:

- **Project-specific servers**: `mcp-server-filesystem` with project paths, `mcp-language-server` with project-specific LSP configurations
- **Isolated environments**: Each project gets its own hub instance and server processes
- **Configuration merging**: Project configs override global settings while preserving global servers

Users can view all active workspace hubs and switch between them seamlessly
through the UI.


CHAT INTEGRATIONS ~

- MCP Hub provides integrations with popular chat plugins like Avante <https://github.com/yetone/avante.nvim>, CodeCompanion <https://github.com/olimorris/codecompanion.nvim>, CopilotChat <https://github.com/CopilotC-Nvim/CopilotChat.nvim>.
- LLMs can use MCP servers through our `@mcp` tool.
- Resources show up as `#variables` in chat.
- Prompts become `/slash_commands`.


FEATURE SUPPORT MATRIX   *mcphub.nvim-what-is-mcp-hub?-feature-support-matrix*

  ---------------------------------------------------------------------------------
  Category             Feature              Support            Details
  -------------------- -------------------- ------------------ --------------------
  Capabilities                                                 

                       Tools                ✅                 Full support

                       🔔 Tool List Changed ✅                 Real-time updates

                       Resources            ✅                 Full support

                       🔔 Resource List     ✅                 Real-time updates
                       Changed                                 

                       Resource Templates   ✅                 URI templates

                       Prompts              ✅                 Full support

                       🔔 Prompts List      ✅                 Real-time updates
                       Changed                                 

                       Roots                ❌                 Not supported

                       Sampling             ❌                 Not supported

  MCP Server                                                   
  Transports                                                   

                       Streamable-HTTP      ✅                 Primary transport
                                                               protocol for remote
                                                               servers

                       SSE                  ✅                 Fallback transport
                                                               for remote servers

                       STDIO                ✅                 For local servers

  Authentication for                                           
  remote servers                                               

                       OAuth                ✅                 With PKCE flow

                       Headers              ✅                 For API keys/tokens

  Chat Integration                                             

                       Avante.nvim          ✅                 Tools, resources,
                                                               resourceTemplates,
                                                               prompts(as
                                                               slash_commands)

                       CodeCompanion.nvim   ✅                 Tools, resources,
                                                               templates, prompts
                                                               (as slash_commands),
                                                               🖼 image responses

                       CopilotChat.nvim     ✅                 Tools, resources,
                                                               function calling
                                                               support

  Marketplace                                                  

                       Server Discovery     ✅                 Browse from verified
                                                               MCP servers

                       Installation         ✅                 Manual and auto
                                                               install with AI

  Configuration                                                

                       Universal ${} Syntax ✅                 Environment
                                                               variables and
                                                               command execution
                                                               across all fields

                       VS Code              ✅                 Support for servers
                       Compatibility                           key, ${env:},
                                                               ${input:},
                                                               predefined variables

                       JSON5 Support        ✅                 Comments and
                                                               trailing commas via
                                                               lua-json5

  Workspace Management                                         

                       Project-Local        ✅                 Automatic detection
                       Configs                                 and merging with
                                                               global config

  Advanced                                                     

                       Smart File-watching  ✅                 Smart updates with
                                                               config file watching

                       Multi-instance       ✅                 All neovim instances
                                                               stay in sync

                       Shutdown-delay       ✅                 Can run as systemd
                                                               service with
                                                               configure delay
                                                               before stopping the
                                                               hub

                       Lua Native MCP       ✅                 Write once , use
                       Servers                                 everywhere. Can
                                                               write tools,
                                                               resources, prompts
                                                               directly in lua

                       Dev Mode             ✅                 Hot reload MCP
                                                               servers on file
                                                               changes for
                                                               development
  ---------------------------------------------------------------------------------

NEXT STEPS                           *mcphub.nvim-what-is-mcp-hub?-next-steps*

- Installation Guide </installation> - Set up MCPHub in your Neovim
- Configuration Guide </configuration> - Learn about configuring MCP Hub


==============================================================================
2. Installation                                     *mcphub.nvim-installation*

Please read the getting started </index> guide before reading this.


REQUIREMENTS                           *mcphub.nvim-installation-requirements*

- Neovim >= 0.8.0 (built with LuaJIT)
- Node.js >= 18.0.0
- plenary.nvim <https://github.com/nvim-lua/plenary.nvim>
- mcp-hub <https://github.com/ravitemer/mcp-hub> (automatically installed via build command)
- jq <https://github.com/jqlang/jq> (optional, for better servers.json formatting)


LAZY.NVIM                                 *mcphub.nvim-installation-lazy.nvim*

MCPHub.nvim requires mcp-hub <https://github.com/ravitemer/mcp-hub> to manage
MCP Servers. You can make `mcp-hub` binary available in three ways:

1. |mcphub.nvim-global-installation| (Recommended)
2. |mcphub.nvim-local-installation|
3. |mcphub.nvim-dev-installation|


DEFAULT INSTALLATION ~

Install `mcp-hub` node binary globally using `npm`, `yarn`, or `bun` any other
node package manager using the `build` command. The `build` command will run
everytime the plugin is updated.

>lua
    {
        "ravitemer/mcphub.nvim",
        dependencies = {
            "nvim-lua/plenary.nvim",
        },
        build = "npm install -g mcp-hub@latest",  -- Installs `mcp-hub` node binary globally
        config = function()
            require("mcphub").setup()
        end
    }
<

Please see configuration </configuration> for default plugin config and on how
to configure the plugin.


LOCAL INSTALLATION ~

Ideal for environments where global binary installations aren’t possible.

Download `mcp-hub` binary alongside the neovim plugin using `bundled_build.lua`
for the `build` command. We need to explicitly set `use_bundled_binary` to
`true` to let the plugin use the locally available `mcp-hub` binary.

>lua
    {
        "ravitemer/mcphub.nvim",
        dependencies = {
            "nvim-lua/plenary.nvim",
        },
        build = "bundled_build.lua",  -- Bundles `mcp-hub` binary along with the neovim plugin
        config = function()
            require("mcphub").setup({
                use_bundled_binary = true,  -- Use local `mcp-hub` binary
            })
        end,
    }
<


DEV INSTALLATION ~

Ideal for development. You can provide the command that our plugin should use
to start the `mcp-hub` server. You can clone the `mcp-hub` repo locally using
`gh clone ravitemer/mcp-hub` and provide the path to the `cli.js` as shown
below:

>lua
    {
        "ravitemer/mcphub.nvim",
        dependencies = {
            "nvim-lua/plenary.nvim",
        },
        config = function()
            require("mcphub").setup({
                cmd = "node",
                cmdArgs = {"/path/to/mcp-hub/src/utils/cli.js"},
            })
        end,
    }
<

See Contributing
<https://github.com/ravitemer/mcphub.nvim/blob/main/CONTRIBUTING.md> guide for
detailed development setup.


NIXOS                                         *mcphub.nvim-installation-nixos*

Flake install ~

Just add it to your NixOS flake.nix or home-manager:

>nix
    inputs = {
    mcphub-nvim.url = "github:ravitemer/mcphub.nvim";
    ...
    }
<

To integrate mcphub.nvim to your NixOS/Home Manager nvim configs, add the
following to your neovim.plugins
<https://nixos.wiki/wiki/Neovim#Installing_Plugins> or your neovim.packages
<https://nixos.wiki/wiki/Neovim#System-wide_2>

>nix
    inputs.mcphub-nvim.packages."${system}".default
<

and add the setup function in lua code
<https://nixos.wiki/wiki/Neovim#Note_on_Lua_plugins>


NIXVIM EXAMPLE ~

Nixvim <https://github.com/nix-community/nixvim> example:

>nix
    { mcphub-nvim, ... }:
    {
    extraPlugins = [mcphub-nvim];
    extraConfigLua = ''
    require("mcphub").setup()
    '';
    }
    
    # where
    {
    # For nixpkgs (not available yet)
    # ...
    
    # For flakes
    mcphub-nvim = inputs.mcphub-nvim.packages."${system}".default;
    }
<

Nixpkgs install ~


  coming…

==============================================================================
3. Configuration                                   *mcphub.nvim-configuration*

Please read the getting started </index> guide before reading this.


DEFAULT CONFIGURATION        *mcphub.nvim-configuration-default-configuration*

All options are optional with sensible defaults. See below for each option in
detail.

>lua
    {
        "ravitemer/mcphub.nvim",
        dependencies = {
            "nvim-lua/plenary.nvim",
        },
        build = "npm install -g mcp-hub@latest",  -- Installs `mcp-hub` node binary globally
        config = function()
            require("mcphub").setup({
                --- `mcp-hub` binary related options-------------------
                config = vim.fn.expand("~/.config/mcphub/servers.json"), -- Absolute path to MCP Servers config file (will create if not exists)
                port = 37373, -- The port `mcp-hub` server listens to
                shutdown_delay = 5 * 60 * 000, -- Delay in ms before shutting down the server when last instance closes (default: 5 minutes)
                use_bundled_binary = false, -- Use local `mcp-hub` binary (set this to true when using build = "bundled_build.lua")
                mcp_request_timeout = 60000, --Max time allowed for a MCP tool or resource to execute in milliseconds, set longer for long running tasks
                global_env = {}, -- Global environment variables available to all MCP servers (can be a table or a function returning a table)
                workspace = {
                    enabled = true, -- Enable project-local configuration files
                    look_for = { ".mcphub/servers.json", ".vscode/mcp.json", ".cursor/mcp.json" }, -- Files to look for when detecting project boundaries (VS Code format supported)
                    reload_on_dir_changed = true, -- Automatically switch hubs on DirChanged event
                    port_range = { min = 40000, max = 41000 }, -- Port range for generating unique workspace ports
                    get_port = nil, -- Optional function returning custom port number. Called when generating ports to allow custom port assignment logic
                },
    
                ---Chat-plugin related options-----------------
                auto_approve = false, -- Auto approve mcp tool calls
                auto_toggle_mcp_servers = true, -- Let LLMs start and stop MCP servers automatically
                extensions = {
                    avante = {
                        make_slash_commands = true, -- make /slash commands from MCP server prompts
                    }
                },
    
                --- Plugin specific options-------------------
                native_servers = {}, -- add your custom lua native servers here
                builtin_tools = {
                    edit_file = {
                        parser = {
                            track_issues = true,
                            extract_inline_content = true,
                        },
                        locator = {
                            fuzzy_threshold = 0.8,
                            enable_fuzzy_matching = true,
                        },
                        ui = {
                            go_to_origin_on_complete = true,
                            keybindings = {
                                accept = ".",
                                reject = ",",
                                next = "n",
                                prev = "p",
                                accept_all = "ga",
                                reject_all = "gr",
                            },
                        },
                    },
                },
                ui = {
                    window = {
                        width = 0.8, -- 0-1 (ratio); "50%" (percentage); 50 (raw number)
                        height = 0.8, -- 0-1 (ratio); "50%" (percentage); 50 (raw number)
                        align = "center", -- "center", "top-left", "top-right", "bottom-left", "bottom-right", "top", "bottom", "left", "right"
                        relative = "editor",
                        zindex = 50,
                        border = "rounded", -- "none", "single", "double", "rounded", "solid", "shadow"
                    },
                    wo = { -- window-scoped options (vim.wo)
                        winhl = "Normal:MCPHubNormal,FloatBorder:MCPHubBorder",
                    },
                },
                json_decode = nil, -- Custom JSON parser function (e.g., require('json5').parse for JSON5 support)
                on_ready = function(hub)
                    -- Called when hub is ready
                end,
                on_error = function(err)
                    -- Called on errors
                end,
                log = {
                    level = vim.log.levels.WARN,
                    to_file = false,
                    file_path = nil,
                    prefix = "MCPHub",
                },
            })
        end
    }
<


BINARY MCP-HUB OPTIONS      *mcphub.nvim-configuration-binary-mcp-hub-options*

On calling `require("mcphub").setup()`, MCPHub.nvim starts the `mcp-hub`
process with the given arguments. Internally the default command looks
something like:

>bash
    mcp-hub --config ~/.config/mcphub/servers.json --port 37373 --auto-shutdown --shutdown-delay 600000 --watch
<

We can configure how the `mcp-hub` process starts and stops as follows:


CONFIG ~

Default: `~/.config/mcphub/servers.json`

Absolute path to the MCP Servers configuration file. The plugin will create
this file if it doesn’t exist. See servers.json </mcp/servers_json> page to
see how `servers.json` should look like, how to safely add it to source control
and more


PORT ~

Default: `37373`

The port number that the `mcp-hub`’s express server should listen on.
MCPHub.nvim sends curl requests to `http://localhost:37373/` endpoint to manage
MCP servers. We first check if `mcp-hub` is already running before trying to
start a new one.


SERVER_URL ~

Default: `nil`

By default, we send curl requests to `http://localhost:37373/` to manage MCP
servers. However, in cases where you want to run `mcp-hub` on another machine
in your local network or remotely you can override the endpoint by setting this
to the server URL e.g `http://mydomain.com:customport` or
`https://url_without_need_for_port.com`


SHUTDOWN_DELAY ~

Default: `5 * 60 * 000` (5 minutes)

Time in milliseconds to wait before shutting down the `mcp-hub` server when the
last Neovim instance closes. The `mcp-hub` server stays up for 10 minutes after
exiting neovim. On entering, MCPHub.nvim checks for the running server and
connects to it. This makes the MCP servers readily available. You can set it to
a longer time to keep `mcp-hub` running.


USE_BUNDLED_BINARY ~

Default: `false`

Uses local `mcp-hub` binary. Enable this when using `build =
"bundled_build.lua"` in your plugin configuration.


MCP_REQUEST_TIMEOUT ~

Default: `60000` (1 minute)

Maximum time allowed for a MCP tool or resource or prompt to execute in
milliseconds. If exceeded, an McpError with code `RequestTimeout` will be
raised. Set longer if you have longer running tools.


CMD, CMDARGS ~

Default: `nil`

Internally `cmd` points to the `mcp-hub` binary. e.g for global installations
it is `mcp-hub`. When `use_bundled_binary` is `true` it is
`~/.local/share/nvim/lazy/mcphub.nvim/bundled/mcp-hub/node_modules/mcp-hub/dist/cli.js`.
You can set this to something else so that MCPHub.nvim uses `cmd` and `cmdArgs`
to start the `mcp-hub` server. You can clone the `mcp-hub` repo locally using
`gh clone ravitemer/mcp-hub` and provide the path to the `cli.js` as shown
below:

>lua
    require("mcphub").setup({
        cmd = "node",
        cmdArgs = {"/path/to/mcp-hub/src/utils/cli.js"},
    })
<

See Contributing
<https://github.com/ravitemer/mcphub.nvim/blob/main/CONTRIBUTING.md> guide for
detailed development setup.


GLOBAL_ENV ~

Default: `{}`

The `global_env` option lets you inject environment variables into all MCP
servers started by MCPHub. This is useful for secrets, tokens, or session
variables (like `DBUS_SESSION_BUS_ADDRESS`) that should be available to every
server process.

You can use either a table or a function that returns a table. The function
receives the job context (workspace info, port, config files, etc).


TABLE EXAMPLE

>lua
    require("mcphub").setup({
        global_env = {
            -- Array-style: uses os.getenv("VAR")
            "DBUS_SESSION_BUS_ADDRESS",
            -- Hash-style: explicit value
            PROJECT_ROOT = vim.fn.getcwd(),
            CUSTOM_VAR = "custom_value",
        }
    })
<


FUNCTION EXAMPLE

>lua
    require("mcphub").setup({
        global_env = function(context)
            local env = {
                "DBUS_SESSION_BUS_ADDRESS",
            }
            -- Add context-aware variables
            if context.is_workspace_mode then
                env.WORKSPACE_ROOT = context.workspace_root
                env.WORKSPACE_PORT = tostring(context.port)
            end
            env.CONFIG_FILES = table.concat(context.config_files, ":")
            return env
        end
    })
<

**Notes:** - Array-style entries (`"VAR"`) will use the value from
`os.getenv("VAR")`. - Hash-style entries (`KEY = "value"`) use the explicit
value. - The function receives a context table with fields like `port`, `cwd`,
`config_files`, `is_workspace_mode`, and `workspace_root`.


WORKSPACE ~

Default:

>lua
    {
        workspace = {
            enabled = true,
            look_for = { ".mcphub/servers.json", ".vscode/mcp.json", ".cursor/mcp.json" },
            reload_on_dir_changed = true,
            port_range = { min = 40000, max = 41000 },
            get_port = nil,
        }
    }
<

Enables project-local configuration files. When enabled, MCP Hub automatically
detects project boundaries and creates isolated hub instances with merged
configurations (project overrides global).

- **enabled**: Master switch for workspace functionality
- **look_for**: Files to search for when detecting project boundaries
- **reload_on_dir_changed**: Auto-switch hubs on directory change
- **port_range**: Port range for generating unique workspace ports
- **get_port**: Optional function returning custom port number. Called when generating ports to allow custom port assignment logic


CUSTOM PORT EXAMPLE

>lua
    require("mcphub").setup({
        workspace = {
            get_port = function()
                local project_name = vim.fn.fnamemodify(vim.fn.getcwd(), ":t")
                if project_name == "critical-project" then
                    return 45000 -- Use fixed port for specific project
                end
                return nil -- Use default hash-based port generation
            end
        }
    })
<

See Workspace Guide </workspace> for detailed usage examples.


CHAT-PLUGIN RELATED OPTIONS*mcphub.nvim-configuration-chat-plugin-related-options*


AUTO_APPROVE ~

Default: `false`

By default when the LLM calls a tool or resource on a MCP server, we show a
confirmation window like below.


BOOLEAN AUTO-APPROVAL

Set it to `true` to automatically approve all MCP tool calls without user
confirmation:

>lua
    require("mcphub").setup({
        auto_approve = true, -- Auto approve all MCP tool calls
    })
<

This also sets `vim.g.mcphub_auto_approve` variable to `true`. You can toggle
this option in the MCP Hub UI with `ga` keymap. You can see the current auto
approval status in the Hub UI.


FUNCTION-BASED AUTO-APPROVAL

For maximum control, provide a function that decides approval based on the
specific tool call:

>lua
    require("mcphub").setup({
        auto_approve = function(params)
            -- Auto-approve GitHub issue reading
            if params.server_name == "github" and params.tool_name == "get_issue" then
                return true -- Auto approve
            end
            
            -- Block access to private repos
            if params.arguments.repo == "private" then
                return "You can't access my private repo" -- Error message
            end
            
            -- Auto-approve safe file operations in current project
            if params.tool_name == "read_file" then
                local path = params.arguments.path or ""
                if path:match("^" .. vim.fn.getcwd()) then
                    return true -- Auto approve
                end
            end
            
            -- Check if tool is configured for auto-approval in servers.json
            if params.is_auto_approved_in_server then
                return true -- Respect servers.json configuration
            end
            
            return false -- Show confirmation prompt
        end,
    })
<

**Parameters available in the function:** - `params.server_name` - Name of the
MCP server - `params.tool_name` - Name of the tool being called (nil for
resources) - `params.arguments` - Table of arguments passed to the tool -
`params.action` - Either "use_mcp_tool" or "access_mcp_resource" - `params.uri`
- Resource URI (for resource access) - `params.is_auto_approved_in_server` -
Boolean indicating if tool is configured for auto-approval in servers.json

**Return values:** - `true` - Auto-approve the call - `false` - Show
confirmation prompt - `string` - Deny with error message - `nil` - Show
confirmation prompt (same as false)

**Events:** When the confirmation window is shown, MCPHub fires
`MCPHubApprovalWindowOpened` and `MCPHubApprovalWindowClosed` events that can
be used for sound notifications or other integrations.


SERVER-LEVEL AUTO-APPROVAL

For fine-grained control per server or tool, configure auto-approval using the
`autoApprove` field in your `servers.json`. You can also toggle auto-approval
from the Hub UI using the `a` keymap on individual servers or tools. See
servers.json configuration </mcp/servers_json#auto-approval-configuration> for
detailed examples and configuration options.


AUTO-APPROVAL PRIORITY

The system checks auto-approval in this order: 1. **Function**: Custom
`auto_approve` function (if provided) 2. **Server-specific**: `autoApprove`
field in server config 3. **Default**: Show confirmation dialog


AUTO_TOGGLE_MCP_SERVERS ~

Default: `true`

Allow LLMs to automatically start and stop MCP servers as needed. Disable to
require manual server management. The following demo shows avante auto starting
a disabled MCP server to acheive it’s objective. See discussion
<https://github.com/ravitemer/mcphub.nvim/discussions/88> for details.


EXTENSIONS ~

Default:

>lua
    {
        extensions = {
            avante = {
                enabled = true,
                make_slash_commands = true
            }
        }
    }
<

Avante <https://github.com/yetone/avante.nvim> integration options: -
`make_slash_commands`: Convert MCP server prompts to slash commands in Avante
chat - Please visit Avante </extensions/avante> for full integration
documentation

Also see CodeCompanion </extensions/codecompanion>, CopilotChat
</extensions/copilotchat> pages for detailed setup guides.


PLUGIN OPTIONS                      *mcphub.nvim-configuration-plugin-options*


NATIVE_SERVERS ~

Default: `{}`

Define custom Lua native MCP servers that run directly in Neovim without
external processes. Each server can provide tools, resources, and prompts.
Please see native servers guide </mcp/native/index> to create MCP Servers in
lua.


BUILTIN_TOOLS ~

Default:

>lua
    {
        builtin_tools = {
            edit_file = {
            },
        },
    }
<

Configuration options for MCPHub’s builtin tools like `edit_file` tool. View
complete Builtin Tools Documentation </mcp/builtin/neovim> for all available
tools and their configuration options.


UI ~

Default:

>lua
    {
        ui = {
            window = {
                width = 0.85, -- 0-1 (ratio); "50%" (percentage); 50 (raw number)
                height = 0.85, -- 0-1 (ratio); "50%" (percentage); 50 (raw number)
                align = "center", -- "center", "top-left", "top-right", "bottom-left", "bottom-right", "top", "bottom", "left", "right"
                border = nil, -- When nil, uses vim.o.winborder. Can be set to "none", "single", "double", "rounded", "solid", "shadow" to override
                relative = "editor",
                zindex = 50,
            },
            wo = { -- window-scoped options (vim.wo)
                winhl = "Normal:MCPHubNormal,FloatBorder:MCPHubBorder",
            },
        },
    }
<

Controls the appearance and behavior of the MCPHub UI window: - `width`: Window
width (0-1 for ratio, "50%" for percentage, or raw number) - `height`: Window
height (same format as width) - `align`: Window alignment position. Options: -
`"center"`: Center the window (default) - `"top-left"`, `"top-right"`,
`"bottom-left"`, `"bottom-right"`: Corner positions - `"top"`, `"bottom"`:
Top/bottom edge, centered horizontally - `"left"`, `"right"`: Left/right edge,
centered vertically - `relative`: Window placement relative to ("editor",
"win", or "cursor") - `zindex`: Window stacking order - `border`: Border style
(defaults to `vim.o.winborder`). Can be set to "none", "single", "double",
"rounded", "solid", "shadow" to override the global setting


ON_READY ~

Default: `function(hub) end`

Callback function executed when the MCP Hub server is ready and connected.
Receives the hub instance as an argument.


ON_ERROR ~

Default: `function(err) end`

Callback function executed when an error occurs in the MCP Hub server. Receives
the error message as an argument.


JSON_DECODE ~

Default: `nil`

Custom JSON parser function for configuration files. This is particularly
useful for supporting JSON5 syntax (comments and trailing commas) in VS Code
config files.


JSON5 SUPPORT EXAMPLE

To enable JSON5 support for `.vscode/mcp.json` files with comments and trailing
commas:

1. Install the lua-json5 parser:

>bash
    # Using your package manager, e.g., lazy.nvim
    { "Joakker/lua-json5" }
<


2. Configure MCPHub to use it:

>lua
    require("mcphub").setup({
        json_decode = require('json5').parse,
        -- other config...
    })
<



With this setup, your config files can include:

>json5
    {
      // This is a comment
      "servers": {
        "github": {
          "url": "https://api.githubcopilot.com/mcp/",
        }, // Trailing comma is fine
      },
    }
<


LOG ~

Default:

>lua
    {
        level = vim.log.levels.WARN,
        to_file = false,
        file_path = nil,
        prefix = "MCPHub"
    }
<

Logging configuration options: - `level`: Log level (vim.log.levels.ERROR,
WARN, INFO, DEBUG, TRACE) - `to_file`: Whether to write logs to file -
`file_path`: Custom log file path - `prefix`: Prefix for log messages


==============================================================================
4. MCP Servers                                       *mcphub.nvim-mcp-servers*


MCP CONFIG FILE                      *mcphub.nvim-mcp-servers-mcp-config-file*

MCPHub.nvim like other MCP clients uses a JSON configuration file to manage MCP
servers. This `config` file is located at `~/.config/mcphub/servers.json` by
default and supports real-time updates across all Neovim instances. You can set
`config` option to a custom location.


VS CODE COMPATIBILITY ~

MCPHub fully supports VS Code’s `.vscode/mcp.json` configuration format,
including:

- **servers key** instead of `mcpServers` (both keys are supported)
- **VS Code variable syntax**: `${env:VARIABLE}`, `${workspaceFolder}`, `${userHome}`, etc.
- **JSON5 syntax**: Comments and trailing commas (with `lua-json5` <https://github.com/Joakker/lua-json5>)
- **Seamless migration**: Existing VS Code configs work directly


  [!NOTE] You can use a single config file for any MCP client like VS Code,
  Cursor, Cline, Zed etc. MCPHub supports both `mcpServers` and `servers` keys,
  with VS Code’s format taking precedence if both are present. With
  MCPHub.nvim, `config` file can be safely added to source control as it supports
  **universal ${} placeholder syntax** for environment variables and command
  execution across all configuration fields.

  [!TIP] Use the `global_env` option to inject environment variables into all MCP
  servers, instead of duplicating them in every server’s `env` field.

MANAGE SERVERS ~

Adding, editing, deleting and securing MCP servers in easy and intuitive with
MCP Hub. You don’t need to edit the `servers.json` file directly. Everything
can be done right from the UI.


FROM MARKETPLACE


BROWSE, SORT, FILTER , SEARCH FROM AVAILABLE MCP SERVERS.


ONE CLICK INSTALL/UNINSTALL

Choose from different install options:


FROM HUB VIEW

Add (`<A>`), edit (`<e>`), delete (`<d>`) MCP servers from the (`H`) Hub view.


BASIC SCHEMA ~

MCPHub supports both traditional `mcpServers` format and VS Code’s `servers`
format:


MCPSERVERS FORMAT

>json
    {
        "mcpServers": {
            // Add stdio and remote MCP servers here
        },
        "nativeMCPServers": { // MCPHub specific
            // To store disabled tools, custom instructions etc
        }
    }
<


VS CODE FORMAT (ALSO SUPPORTED)

>json
    {
        "servers": {
            // Add stdio and remote MCP servers here (VS Code format)
        }
    }
<


SERVER TYPES ~


LOCAL (STDIO) SERVERS

>json
    {
        "mcpServers": {
            "local-server": {
                "command": "${MCP_BINARY_PATH}/server",
                "args": [
                    "--token", "${API_TOKEN}",
                    "--secret", "${cmd: op read op://vault/secret}"
                ],
                "env": {
                    "API_TOKEN": "${cmd: aws ssm get-parameter --name /app/token --query Parameter.Value --output text}",
                    "DB_URL": "postgresql://user:${DB_PASSWORD}@localhost/myapp",
                    "DB_PASSWORD": "password123",
                    "FALLBACK_VAR": null
                },
                "cwd": "/home/ubuntu/server-dir/"
            }
        }
    }
<


REQUIRED FIELDS:

- `command`: The executable to start the server (supports `${VARIABLE}` and `${cmd: command}`)


OPTIONAL FIELDS:

- `args`: Array of command arguments (supports `${VARIABLE}` and `${cmd: command}` placeholders)
- `env`: Environment variables with placeholder resolution and system fallback
- `cwd`: The current working directory for the MCP server process (supports `${VARIABLE}` and `${cmd: command}` placeholders)
- `dev`: Development mode configuration for auto-restart on file changes
- `name`: Display name that will be shown in the UI
- `description`: Short description about the server (useful when the server is disabled and `auto_toggle_mcp_servers` is `true`)


UNIVERSAL ${} PLACEHOLDER SYNTAX

**All fields** support the universal placeholder syntax with VS Code
compatibility: - **${ENV_VAR}** or **${env:ENV_VAR}** - Resolves environment
variables (VS Code style supported) - **${cmd: command args}** - Executes
commands and uses output - **${workspaceFolder}**, **${userHome}**,
**${pathSeparator}** - VS Code predefined variables - **${input:variable-id}**
- VS Code input variables (via MCP_HUB_ENV) - **null or ""** - Falls back to
`process.env`

Given `API_KEY=secret` in the environment:

  ----------------------------------------------------------------------------------------------------------------
  Example                                       Becomes                            Description
  --------------------------------------------- ---------------------------------- -------------------------------
  "API_KEY": ""                                 "API_KEY": "secret"                Empty string falls back to
                                                                                   process.env.API_KEY

  "API_KEY": null                               "API_KEY": "secret"                null falls back to
                                                                                   process.env.API_KEY

  "AUTH": "Bearer ${API_KEY}"                   "AUTH": "Bearer secret"            ${} Placeholder values are
                                                                                   replaced

  "AUTH": "Bearer ${env:API_KEY}"               "AUTH": "Bearer secret"            VS Code style environment
                                                                                   variables

  "PATH": "${workspaceFolder}/bin"              "PATH": "/home/user/project/bin"   VS Code predefined variables

  "TOKEN": "${cmd: op read op://vault/token}"   "TOKEN": "secret"                  Commands are executed and
                                                                                   output used

  "HOME": "/home/ubuntu"                        "HOME": "/home/ubuntu"             Used as-is
  ----------------------------------------------------------------------------------------------------------------

  �� ️ **Legacy Syntax**: `$VAR` (args) and `$: command` (env) are deprecated
  but still supported with warnings. Use `${VAR}` and `${cmd: command}` instead.

CWD EXAMPLE:

The `cwd` field is particularly useful when your MCP server needs to run in a
specific directory context. Here’s a practical example:

>json
    {
        "mcpServers": {
            "project-server": {
                "command": "npm",
                "args": ["start"],
                "cwd": "/home/ubuntu/my-mcp-project/",
                "env": {
                    "NODE_ENV": "development"
                }
            }
        }
    }
<

**Use cases for cwd:** - When the MCP server needs to access relative files in
its project directory - When using npm/yarn scripts that depend on being in the
project root


  **Note**: The top-level `cwd` field sets the working directory for the server
  process itself, while `dev.cwd` (used in development mode) sets the directory
  for file watching. These serve different purposes and can be used together.

DEV DEVELOPMENT MODE

The `dev` field enables automatic server restarts when files change during
development:

>json
    {
        "mcpServers": {
          "dev-server": {
            "command": "npx",
            "args": [
              "tsx",
              "path/to/src/index.ts"
            ],
            "dev": {
              "watch": [
                "src/**/*.ts",
                "package.json"
              ],
              "enabled": true,
              "cwd": "/path/to/dev-server/"
            }
          }
        }
    }
<


DEV CONFIGURATION OPTIONS:

  -------------------------------------------------------------------------------------------------------
  Field        Required           Default                                         Description
  ------------ ------------------ ----------------------------------------------- -----------------------
  cwd          Yes                -                                               Absolute path to
                                                                                  server’s working
                                                                                  directory

  watch        No                 ["**/*.js", "**/*.ts", "**/*.py","**/*.json"]   Array of glob patterns
                                                                                  to watch

  enabled      No                 true                                            Enable/disable dev mode
  -------------------------------------------------------------------------------------------------------
When enabled, the server will automatically restart whenever files matching the
watch patterns change in the specified directory. The system uses a 500ms
debounce to prevent rapid restarts and ignores common directories like
`node_modules`, `build`, `.git`, etc.

**Example use cases:** - TypeScript/JavaScript MCP servers during development.
Use `npx tsc index.ts` to bypass build step during development. - Python
servers with source code changes - Configuration file updates that require
restarts


REMOTE SERVERS

MCPHub supports both `streamable-http` and `sse` remote servers.

>json
    {
        "mcpServers": {
            "remote-server": {
                "url": "https://${PRIVATE_DOMAIN}/mcp",
                "headers": {
                    "Authorization": "Bearer ${cmd: op read op://vault/api/token}",
                    "X-Custom-Header": "${CUSTOM_VALUE}"
                }
            }
        }
    }
<


REQUIRED FIELDS:

- `url`: Remote server endpoint (supports `${VARIABLE}` and `${cmd: command}` placeholders)


OPTIONAL FIELDS:

- `headers`: Authentication headers (supports `${VARIABLE}` and `${cmd: command}` placeholders)
- `name`: Display name that will be shown in the UI
- `description`: Short description about the server (useful when the server is disabled and `auto_toggle_mcp_servers` is `true`)


  **Note**: Remote servers use the same universal `${}` placeholder syntax as
  local servers. See the Universal Placeholder Syntax section above for full
  details.

MCPHUB SPECIFIC FIELDS ~

MCPHub adds several extra keys for each server automatically from the UI:

>json
    {
        "mcpServers": {
            "example": {
                "disabled": false,
                "disabled_tools": ["expensive-tool"],
                "disabled_resources": ["resource://large-data"],
                "disabled_resourceTemplates": ["resource://{type}/{id}"],
                "autoApprove": ["safe-tool", "read-only-tool"],
                "custom_instructions": {
                    "disabled": false,
                    "text": "Custom instructions for this server"
                }
            }
        }
    }
<


AUTO-APPROVAL CONFIGURATION

The `autoApprove` field allows fine-grained control over which tools are
automatically approved without user confirmation:

  ---------------------------------------------------------------------------------------------
  Value                Behavior                    Example
  -------------------- --------------------------- --------------------------------------------
  true                 Auto-approve all tools on   "autoApprove": true
                       this server                 

  ["tool1", "tool2"]   Auto-approve only specific  "autoApprove": ["read_file", "list_files"]
                       tools                       

  [] or omitted        No auto-approval (show      "autoApprove": []
                       confirmation dialog)        
  ---------------------------------------------------------------------------------------------
**Notes:** - Resources are always auto-approved by default (no explicit
configuration needed) - Auto-approval only applies to enabled servers and
enabled tools - You can toggle auto-approval from the UI using the `a` keymap
on servers or individual tools


VS CODE FORMAT EXAMPLE

Here’s a complete example using VS Code’s `.vscode/mcp.json` format with
JSON5 syntax:

>json5
    {
      // VS Code MCP configuration with JSON5 support
      "servers": {
        "github": {
          "url": "https://api.githubcopilot.com/mcp/",
          "headers": {
            "Authorization": "Bearer ${env:GITHUB_TOKEN}"
          }
        },
        "filesystem": {
          "command": "npx",
          "args": ["-y", "mcp-server-filesystem", "${workspaceFolder}"],
          "env": {
            "WORKSPACE_PATH": "${workspaceFolder}",
            "USER_HOME": "${userHome}"
          }
        }, // Trailing comma is supported with JSON5
      }
    }
<

This example demonstrates: - **VS Code servers key** instead of `mcpServers` -
**VS Code environment syntax**: `${env:GITHUB_TOKEN}` - **VS Code predefined
variables**: `${workspaceFolder}`, `${userHome}` - **JSON5 features**: Comments
and trailing commas


SUPPORTED PREDEFINED VARIABLES

MCPHub supports the following VS Code predefined variables:

  ----------------------------------------------------------------------------------
  Variable                     Description              Example Value
  ---------------------------- ------------------------ ----------------------------
  ${workspaceFolder}           Directory where mcp-hub  /home/user/my-project
                               is running               

  ${userHome}                  User’s home directory    /home/user

  ${pathSeparator}             OS path separator        / (Linux/macOS) or \
                                                        (Windows)

  ${workspaceFolderBasename}   Just the folder name     my-project

  ${cwd}                       Alias for                /home/user/my-project
                               workspaceFolder          

  ${/}                         VS Code shorthand for    / (Linux/macOS) or \
                               pathSeparator            (Windows)
  ----------------------------------------------------------------------------------
**Notes:** - These variables are available for placeholder resolution but are
NOT passed to server environments - Use them in any configuration field:
`command`, `args`, `env`, `url`, `headers`, `cwd`


VS CODE INPUT VARIABLES SUPPORT ~

MCPHub supports VS Code’s `${input:variable-id}` syntax for compatibility
with VS Code configurations. Input variables are configured via MCPHub’s
`global_env` option and made available to all servers.

**Setup in MCPHub configuration:**

>lua
    require("mcphub").setup({
        global_env = {
            ["input:perplexity-key"] = "my-awesome-project",
        },
        -- other config...
    })
<

**Usage in configuration files:**

>json
    {
      "servers": {
        "Perplexity": {
          "type": "stdio",
          "command": "npx",
          "args": ["-y", "server-perplexity-ask"],
          "env": {
            "PERPLEXITY_API_KEY": "${input:perplexity-key}"
          }
        }
      }
    }
<

**How it works:** 1. Input variables are defined in MCPHub’s `global_env`
configuration 2. MCPHub passes them to the backend via `MCP_HUB_ENV` 3.
Variables are resolved using the format: `${input:variable-id}` 4. This enables
direct compatibility with existing VS Code configurations


LUA MCP SERVERS                      *mcphub.nvim-mcp-servers-lua-mcp-servers*


WHY NATIVE MCP SERVERS? ~

MCPHub.nvim allows you to create MCP servers directly in Lua without any
external processes. This guide explains why you might want to use native MCP
servers and how they compare to other approaches.


THE PROBLEM WITH PLUGIN-SPECIFIC TOOLS

Many chat plugins like Avante and CodeCompanion provide their own tool systems:

>lua
    -- Avante custom tools
    require("avante").setup({
        custom_tools = {
            get_weather = {
                name = "get_weather",
                description = "Get weather info",
                schema = { ... },
                func = function() end
            }
        }
    })
    
    -- CodeCompanion tools
    require("codecompanion").setup({
        chat = {
            tools = {
                get_weather = {
                    name = "get_weather",
                    description = "Get weather info",
                    schema = { ... },
                    handler = function() end
                }
            }
        }
    })
<

This leads to several limitations:

  ---------------------------------------------------------------------------
  Feature          Plugin-Specific Tools          Native MCP Servers
  ---------------- ------------------------------ ---------------------------
  Implementation   Need to rewrite for each       Write once, use everywhere
                   plugin                         

  API              Different for each plugin      Standard MCP protocol

  Instructions     Limited by schema              Full system prompt

  Resources        No standard way                URI-based system

  Response Types   Usually just text              Text, images, blobs

  State            Per-plugin management          Centralized lifecycle

  Updates          May break tools                Plugin-independent
  ---------------------------------------------------------------------------

BENEFITS OF NATIVE MCP SERVERS


1. WRITE ONCE, USE EVERYWHERE

>lua
    -- Write once, works in any chat plugin
    mcphub.add_tool("weather", {
        name = "get_weather",
        description = "Get weather info",
        handler = function(req, res)
            return res:text("Current weather: ☀️"):send()
        end
    })
<


2. RICH RESPONSE TYPES

>lua
    -- Support multiple response types
    function handler(req, res)
        return res
            :text("Here's the weather:")
            :image(generate_chart(), "image/png")
            :text("Additional details...")
            :send()
    end
<


3. RESOURCE SYSTEM

Access data through clean URIs:

>lua
    mcphub.add_resource_template("weather", {
        uriTemplate = "weather://{city}",
        handler = function(req, res)
            local city = req.params.city
            return res:text(city .. ": ☀️"):send()
        end
    })
<


4. DEEP EDITOR INTEGRATION

Direct access to Neovim’s features:

>lua
    mcphub.add_tool("buffer", {
        name = "analyze",
        handler = function(req, res)
            -- Access current editor state
            local buf = req.editor_info.last_active
            -- Use LSP features
            local diagnostics = vim.diagnostic.get(buf.bufnr)
            -- Format response
            return res:text("Analysis complete"):send()
        end
    })
<


5. PLUGIN-AWARE CONTEXT

Adapt to different chat plugins:

>lua
    mcphub.add_tool("context", {
        name = "analyze",
        handler = function(req, res)
            if req.caller.type == "codecompanion" then
                -- Handle CodeCompanion context
                local chat = req.caller.codecompanion.chat
                return handle_codecompanion(chat)
            elseif req.caller.type == "avante" then
                -- Handle Avante context
                local code = req.caller.avante.code
                return handle_avante(code)
            end
        end
    })
<


6. STANDARD PROTOCOL

Following the MCP specification ensures: - Consistent behavior across plugins -
Future compatibility - Clear documentation - Standard error handling


REAL-WORLD EXAMPLE

Here’s a real example from MCPHub’s built-in Neovim server that
demonstrates these benefits:

>lua
    -- LSP diagnostics as a resource
    mcphub.add_resource("neovim", {
        name = "Diagnostics: Current File",
        description = "Get diagnostics for current file",
        uri = "neovim://diagnostics/current",
        mimeType = "text/plain",
        handler = function(req, res)
            -- Get editor context
            local buf_info = req.editor_info.last_active
            if not buf_info then
                return res:error("No active buffer")
            end
    
            -- Use LSP features
            local diagnostics = vim.diagnostic.get(buf_info.bufnr)
            
            -- Format response
            local text = string.format(
                "Diagnostics for: %s\n%s\n",
                buf_info.filename,
                string.rep("-", 40)
            )
            
            for _, diag in ipairs(diagnostics) do
                local severity = vim.diagnostic.severity[diag.severity]
                text = text .. string.format(
                    "\n%s: Line %d - %s\n",
                    severity,
                    diag.lnum + 1,
                    diag.message
                )
            end
    
            return res:text(text):send()
        end
    })
<

This example shows how native servers can: 1. Access Neovim APIs directly 2.
Use built-in features like LSP 3. Format responses clearly 4. Handle errors
properly 5. Work across all chat plugins

The next sections will show you how to create your own native MCP servers,
starting with registration methods.


REGISTRATION METHODS ~

There are two ways to register native MCP servers in MCPHub. But first, let’s
understand the core types involved.


CORE TYPES


SERVER DEFINITION

>lua
    ---@class NativeServerDef
    ---@field name? string Name of the server
    ---@field displayName? string Display name of the server
    ---@field capabilities? MCPCapabilities List of server capabilities
    
    ---@class MCPCapabilities
    ---@field tools? MCPTool[] List of tools
    ---@field resources? MCPResource[] List of resources
    ---@field resourceTemplates? MCPResourceTemplate[] List of templates
    ---@field prompts? MCPPrompt[] List of prompts
<


REGISTRATION METHODS


1. CONFIGURATION-BASED (SETUP)

Register your complete server through MCPHub’s setup:

>lua
    require("mcphub").setup({
        native_servers = {
            -- Define your server
            example = {
                -- Required: Server name
                name = "example",
                
                -- Optional: Display name
                displayName = "Example Server",
                
                -- Required: Server capabilities
                capabilities = {
                    tools = { ... },
                    resources = { ... },
                    resourceTemplates = { ... },
                    prompts = { ... }
                }
            }
        }
    })
<


2. API-BASED (DYNAMIC)

Add capabilities incrementally using the API:

>lua
    local mcphub = require("mcphub")
    
    -- Create or get a server
    mcphub.add_server("example", {
        displayName = "Example Server"
    })
    
    -- Or automatically create server when adding capabilities
    mcphub.add_tool("example", {...})      -- Creates server if needed
    mcphub.add_resource("example", {...})  -- Creates server if needed
<


ADD_SERVER

>lua
    ---@param server_name string Name of the server
    ---@param def? NativeServerDef Optional server definition
    ---@return NativeServer|nil server Server instance or nil on error
    mcphub.add_server("example", {
        name = "example",
        displayName = "Example Server",
        capabilities = { ... }
    })
<


ADD_TOOL

>lua
    ---@param server_name string Name of the server
    ---@param tool_def MCPTool Tool definition
    ---@return NativeServer|nil server Server instance or nil on error
    mcphub.add_tool("example", {
        -- Required: Tool name
        name = "greeting",
        
        -- Optional: Description (string or function)
        description = "Greet a user",
        
        -- Optional: Input validation schema
        inputSchema = {
            type = "object",
            properties = {
                name = {
                    type = "string",
                    description = "Name to greet"
                }
            }
        },
        
        -- Required: Tool implementation
        ---@param req ToolRequest Request context
        ---@param res ToolResponse Response builder
        handler = function(req, res)
            return res:text("Hello " .. req.params.name):send()
        end
    })
<


ADD_RESOURCE

>lua
    ---@param server_name string Name of the server
    ---@param resource_def MCPResource Resource definition
    ---@return NativeServer|nil server Server instance or nil on error
    mcphub.add_resource("example", {
        -- Optional: Resource name
        name = "Welcome",
        
        -- Required: Static URI
        uri = "example://welcome",
        
        -- Optional: Description
        description = "Welcome message",
        
        -- Optional: MIME type
        mimeType = "text/plain",
        
        -- Required: Resource handler
        ---@param req ResourceRequest Request context
        ---@param res ResourceResponse Response builder
        handler = function(req, res)
            return res:text("Welcome!"):send()
        end
    })
<


ADD_RESOURCE_TEMPLATE

>lua
    ---@param server_name string Name of the server
    ---@param template_def MCPResourceTemplate Template definition
    ---@return NativeServer|nil server Server instance or nil on error
    mcphub.add_resource_template("example", {
        -- Optional: Template name
        name = "UserInfo",
        
        -- Required: URI template with parameters
        uriTemplate = "example://user/{id}",
        
        -- Optional: Description
        description = "Get user information",
        
        -- Optional: Default MIME type
        mimeType = "text/plain",
        
        -- Required: Template handler
        ---@param req ResourceRequest Request with params
        ---@param res ResourceResponse Response builder
        handler = function(req, res)
            -- Access template parameters
            local id = req.params.id
            return res:text("User " .. id):send()
        end
    })
<


ADD_PROMPT

>lua
    ---@param server_name string Name of the server
    ---@param prompt_def MCPPrompt Prompt definition
    ---@return NativeServer|nil server Server instance or nil on error
    mcphub.add_prompt("example", {
        -- Optional: Prompt name
        name = "chat",
        
        -- Optional: Description
        description = "Start a chat",
        
        -- Optional: Prompt arguments
        arguments = {
            {
                name = "topic",
                description = "Chat topic",
                required = true
            }
        },
        
        -- Required: Prompt handler
        ---@param req PromptRequest Request with arguments
        ---@param res PromptResponse Response builder
        handler = function(req, res)
            return res
                :system()
                :text("Chat about: " .. req.params.topic)
                :user()
                :text("Tell me about " .. req.params.topic)
                :llm()
                :text("I'd be happy to discuss " .. req.params.topic)
                :send()
        end
    })
<


NEXT STEPS

Now that you understand server registration, dive into: 1. Adding Tools
<./tools> - Implement tool capabilities 2. Adding Resources <./resources> -
Create resources and templates 3. Adding Prompts <./prompts> - Create
interactive prompts

Each capability type has its own request/response types and patterns for
success.


ADDING TOOLS ~

Tools are functions that LLMs can call with specific parameters. This guide
covers how to implement tools with proper typing and best practices.


TOOL DEFINITION

>lua
    ---@class MCPTool
    ---@field name string Required: Tool identifier
    ---@field description string|fun():string Optional: Tool description
    ---@field inputSchema? table|fun():table Optional: JSON Schema for validation
    ---@field handler fun(req: ToolRequest, res: ToolResponse): nil|table Required: Implementation
<


REQUEST CONTEXT

Tool handlers receive a request object with:

>lua
    ---@class ToolRequest
    ---@field params table Tool arguments (validated against inputSchema)
    ---@field tool MCPTool Complete tool definition
    ---@field server NativeServer Server instance
    ---@field caller table Additional context from caller
    ---@field editor_info EditorInfo Current editor state
<


RESPONSE BUILDER

Tool handlers use a chainable response builder:

>lua
    ---@class ToolResponse
    ---@field text fun(text: string): ToolResponse Add text content
    ---@field image fun(data: string, mime: string): ToolResponse Add image
    ---@field audio fun(data: string, mime: string): ToolResponse Add audio
    ---@field resource fun(resource: MCPResourceContent): ToolResponse Add resource
    ---@field error fun(message: string, details?: table): table Send error
    ---@field send fun(result?: table): table Send final response
<


EXAMPLES


BASIC EXAMPLE

Here’s a simple greeting tool:

>lua
    mcphub.add_tool("example", {
        name = "greet",
        description = "Greet a user",
        inputSchema = {
            type = "object",
            properties = {
                name = {
                    type = "string",
                    description = "Name to greet"
                }
            },
            required = { "name" }
        },
        handler = function(req, res)
            return res:text("Hello " .. req.params.name):send()
        end
    })
<


REAL EXAMPLE: FILE READING

Here’s how the built-in Neovim server implements file reading:

>lua
    mcphub.add_tool("neovim", {
        name = "read_file",
        description = "Read contents of a file",
        inputSchema = {
            type = "object",
            properties = {
                path = {
                    type = "string",
                    description = "Path to the file to read",
                },
                start_line = {
                    type = "number",
                    description = "Start reading from this line (1-based)",
                    default = 1
                },
                end_line = {
                    type = "number",
                    description = "Read until this line (inclusive)",
                    default = -1
                }
            },
            required = { "path" }
        },
        handler = function(req, res)
            local params = req.params
            local p = Path:new(params.path)
            
            -- Validate file exists
            if not p:exists() then
                return res:error("File not found: " .. params.path)
            end
    
            -- Handle line range reading
            if params.start_line and params.end_line then
                local extracted = {}
                local current_line = 0
                
                for line in p:iter() do
                    current_line = current_line + 1
                    if current_line >= params.start_line 
                    and (params.end_line == -1 or current_line <= params.end_line) then
                        table.insert(extracted, 
                            string.format("%4d │ %s", current_line, line))
                    end
                    if params.end_line ~= -1 and current_line > params.end_line then
                        break
                    end
                end
                return res:text(table.concat(extracted, "\n")):send()
            end
    
            -- Read entire file
            return res:text(p:read()):send()
        end
    })
<


ADVANCED FEATURES


DYNAMIC DESCRIPTIONS

Descriptions can be functions for dynamic content:

>lua
    mcphub.add_tool("files", {
        name = "search",
        description = function()
            local count = #vim.api.nvim_list_bufs()
            return string.format("Search %d open buffers", count)
        end,
        handler = function(req, res)
            -- Implementation
        end
    })
<


DYNAMIC SCHEMAS

Input schemas can also be dynamic:

>lua
    mcphub.add_tool("buffer", {
        name = "edit",
        inputSchema = function()
            -- Get open buffers
            local bufs = vim.api.nvim_list_bufs()
            local options = {}
            for _, bufnr in ipairs(bufs) do
                if vim.api.nvim_buf_is_loaded(bufnr) then
                    table.insert(options, tostring(bufnr))
                end
            end
            
            return {
                type = "object",
                properties = {
                    buffer = {
                        type = "string",
                        enum = options,
                        description = "Buffer to edit"
                    }
                }
            }
        end,
        handler = function(req, res)
            -- Implementation
        end
    })
<


RICH RESPONSES

Tools can return multiple content types:

>lua
    mcphub.add_tool("diagram", {
        name = "generate",
        handler = function(req, res)
            -- Generate diagram image
            local image_data = generate_diagram(req.params)
            
            -- Return both text and image
            return res
                :text("Generated diagram:")
                :image(image_data, "image/png")
                :text("Diagram shows relationship between A and B")
                :send()
        end
    })
<


ERROR HANDLING

Use proper error handling with details:

>lua
    mcphub.add_tool("git", {
        name = "commit",
        handler = function(req, res)
            -- Check git repository
            if not is_git_repo() then
                return res:error("Not a git repository", {
                    cwd = vim.fn.getcwd(),
                    suggestion = "Initialize git with 'git init'"
                })
            end
            
            -- Check for changes
            if git_is_clean() then
                return res:error("No changes to commit", {
                    status = vim.fn.system("git status"),
                    suggestion = "Make changes before committing"
                })
            end
            
            -- Implementation
        end
    })
<


USING EDITOR CONTEXT

Access current editor state:

>lua
    mcphub.add_tool("buffer", {
        name = "analyze",
        handler = function(req, res)
            -- Get active buffer info
            local buf = req.editor_info.last_active
            if not buf then
                return res:error("No active buffer")
            end
            
            -- Get buffer details
            local details = {
                name = buf.filename,
                type = buf.filetype,
                lines = buf.line_count,
                modified = buf.is_modified
            }
            
            -- Format response
            return res:text(vim.inspect(details)):send()
        end
    })
<


CALLER-AWARE TOOLS

Adapt behavior based on caller:

>lua
    mcphub.add_tool("context", {
        name = "get_code",
        handler = function(req, res)
            if req.caller.type == "codecompanion" then
                -- Get context from CodeCompanion chat
                local chat = req.caller.codecompanion.chat
                return handle_codecompanion(chat, res)
            
            elseif req.caller.type == "avante" then
                -- Get context from Avante
                local code = req.caller.avante.code
                return handle_avante(code, res)
                
            else
                -- Default behavior
                return res:text("No special context"):send()
            end
        end
    })
<

Next, learn about Adding Resources <./resources> to provide data through URIs.


ADDING RESOURCES ~

Resources provide data through URIs in two ways: 1. Static Resources - Fixed
URIs 2. Resource Templates - Dynamic URIs with parameters


TYPE DEFINITIONS


STATIC RESOURCES

>lua
    ---@class MCPResource
    ---@field name? string Resource identifier
    ---@field description? string|fun():string Resource description
    ---@field mimeType? string Resource MIME type (e.g., "text/plain")
    ---@field uri string Static URI (e.g., "system://info")
    ---@field handler fun(req: ResourceRequest, res: ResourceResponse) Implementation
<


RESOURCE TEMPLATES

>lua
    ---@class MCPResourceTemplate
    ---@field name? string Template identifier
    ---@field description? string|fun():string Template description
    ---@field mimeType? string Default MIME type
    ---@field uriTemplate string URI with parameters (e.g., "buffer://{bufnr}/lines")
    ---@field handler fun(req: ResourceRequest, res: ResourceResponse) Implementation
<


REQUEST CONTEXT

Resource handlers receive:

>lua
    ---@class ResourceRequest
    ---@field params table<string,string> Template parameters from URI
    ---@field uri string Complete requested URI
    ---@field uriTemplate string|nil Original template if from template
    ---@field resource MCPResource|MCPResourceTemplate Complete definition
    ---@field server NativeServer Server instance
    ---@field caller table Additional context from caller
    ---@field editor_info EditorInfo Current editor state
<


RESPONSE BUILDER

Resource handlers use:

>lua
    ---@class ResourceResponse
    ---@field text fun(text: string, mime?: string): ResourceResponse Add text
    ---@field blob fun(data: string, mime?: string): ResourceResponse Add binary
    ---@field image fun(data: string, mime?: string): ResourceResponse Add image
    ---@field audio fun(data: string, mime?: string): ResourceResponse Add audio
    ---@field error fun(message: string, details?: table): table Send error
    ---@field send fun(result?: table): table Send final response
<


EXAMPLES


BASIC EXAMPLES


STATIC RESOURCE

>lua
    mcphub.add_resource("system", {
        name = "System Info",
        description = "Get system information",
        uri = "system://info",
        mimeType = "text/plain",
        handler = function(req, res)
            local info = {
                os = vim.loop.os_uname(),
                pid = vim.fn.getpid(),
                vimdir = vim.fn.stdpath("config")
            }
            return res:text(vim.inspect(info)):send()
        end
    })
<


RESOURCE TEMPLATE

>lua
    mcphub.add_resource_template("files", {
        name = "File Lines",
        description = "Get specific lines from a file",
        uriTemplate = "files://{path}/{start}-{end}",
        handler = function(req, res)
            -- Get parameters
            local path = req.params.path
            local start_line = tonumber(req.params.start)
            local end_line = tonumber(req.params.end)
            
            -- Validate file
            if not vim.loop.fs_stat(path) then
                return res:error("File not found: " .. path)
            end
            
            -- Read lines
            local lines = {}
            local current = 0
            for line in io.lines(path) do
                current = current + 1
                if current >= start_line then
                    table.insert(lines, string.format(
                        "%4d │ %s", current, line
                    ))
                end
                if current >= end_line then
                    break
                end
            end
            
            return res:text(table.concat(lines, "\n")):send()
        end
    })
<


REAL EXAMPLES FROM NEOVIM SERVER


LSP DIAGNOSTICS RESOURCE

>lua
    mcphub.add_resource("neovim", {
        name = "Diagnostics: Current File",
        description = "Get diagnostics for the current file",
        uri = "neovim://diagnostics/current",
        mimeType = "text/plain",
        handler = function(req, res)
            -- Get active buffer
            local buf_info = req.editor_info.last_active
            if not buf_info then
                return res:error("No active buffer")
            end
    
            -- Get diagnostics
            local diagnostics = vim.diagnostic.get(buf_info.bufnr)
            
            -- Format header
            local text = string.format(
                "Diagnostics for: %s\n%s\n",
                buf_info.filename,
                string.rep("-", 40)
            )
            
            -- Format diagnostics
            for _, diag in ipairs(diagnostics) do
                local severity = vim.diagnostic.severity[diag.severity]
                text = text .. string.format(
                    "\n%s: %s\nLine %d: %s\n",
                    severity,
                    diag.source or "unknown",
                    diag.lnum + 1,
                    diag.message
                )
            end
    
            return res:text(text):send()
        end
    })
<


BUFFER LINES TEMPLATE

>lua
    mcphub.add_resource_template("neovim", {
        name = "Buffer Lines",
        description = "Get specific lines from a buffer",
        uriTemplate = "neovim://buffer/{bufnr}/lines/{start}-{end}",
        handler = function(req, res)
            -- Get parameters
            local bufnr = tonumber(req.params.bufnr)
            local start_line = tonumber(req.params.start)
            local end_line = tonumber(req.params.end)
            
            -- Validate buffer
            if not vim.api.nvim_buf_is_valid(bufnr) then
                return res:error("Invalid buffer: " .. req.params.bufnr)
            end
            
            -- Get lines
            local lines = vim.api.nvim_buf_get_lines(
                bufnr,
                start_line - 1,  -- 0-based index
                end_line,       -- Exclusive end
                false          -- Strict indexing
            )
            
            -- Format with line numbers
            local result = {}
            for i, line in ipairs(lines) do
                table.insert(result, string.format(
                    "%4d │ %s",
                    start_line + i - 1,
                    line
                ))
            end
            
            return res:text(table.concat(result, "\n")):send()
        end
    })
<


ADVANCED FEATURES


DYNAMIC MIME TYPES

Change MIME type based on content:

>lua
    mcphub.add_resource_template("files", {
        name = "File Content",
        uriTemplate = "files://{path}",
        handler = function(req, res)
            local path = req.params.path
            local ext = vim.fn.fnamemodify(path, ":e")
            
            -- Get MIME type based on extension
            local mime_types = {
                json = "application/json",
                yaml = "application/yaml",
                md = "text/markdown",
                txt = "text/plain"
            }
            
            local mime = mime_types[ext] or "text/plain"
            return res:text(vim.fn.readfile(path), mime):send()
        end
    })
<


BINARY DATA

Handle binary files:

>lua
    mcphub.add_resource_template("files", {
        name = "File Download",
        uriTemplate = "files://download/{path}",
        handler = function(req, res)
            local path = req.params.path
            local ext = vim.fn.fnamemodify(path, ":e")
            
            -- Binary file types
            local binary_types = {
                png = "image/png",
                jpg = "image/jpeg",
                pdf = "application/pdf"
            }
            
            if binary_types[ext] then
                -- Read as binary
                local data = vim.fn.readfile(path, "b")
                return res:blob(data, binary_types[ext]):send()
            else
                -- Read as text
                return res:text(vim.fn.readfile(path)):send()
            end
        end
    })
<


RESOURCE VALIDATION

URI parameter validation:

>lua
    mcphub.add_resource_template("git", {
        name = "Commit Info",
        uriTemplate = "git://commit/{hash}",
        handler = function(req, res)
            local hash = req.params.hash
            
            -- Validate hash format
            if not hash:match("^[0-9a-f]+$") then
                return res:error("Invalid commit hash", {
                    hash = hash,
                    expected = "hexadecimal string"
                })
            end
            
            -- Validate hash exists
            local exists = vim.fn.system(
                "git rev-parse --quiet --verify " .. hash
            )
            if vim.v.shell_error ~= 0 then
                return res:error("Commit not found", {
                    hash = hash,
                    suggestion = "Use 'git log' to list commits"
                })
            end
            
            -- Get commit info
            local info = vim.fn.system(
                "git show --no-patch --format='%h %s' " .. hash
            )
            return res:text(info):send()
        end
    })
<

Next, learn about Adding Prompts <./prompts> to create interactive
conversations.


ADDING PROMPTS ~

Prompts create interactive conversations with role-based messaging. They help
guide LLMs through specific tasks by setting up context and examples.


PROMPT DEFINITION

>lua
    ---@class MCPPrompt
    ---@field name? string Prompt identifier
    ---@field description? string|fun():string Prompt description
    ---@field arguments? MCPPromptArgument[]|fun():MCPPromptArgument[] List of arguments
    ---@field handler fun(req: PromptRequest, res: PromptResponse) Implementation
    
    ---@class MCPPromptArgument
    ---@field name string Argument name
    ---@field description? string Argument description
    ---@field required? boolean Whether argument is required
    ---@field default? string Default value
<


REQUEST CONTEXT

>lua
    ---@class PromptRequest
    ---@field params table<string,string> Argument values
    ---@field prompt MCPPrompt Complete prompt definition
    ---@field server NativeServer Server instance
    ---@field caller table Additional context from caller
    ---@field editor_info EditorInfo Current editor state
<


RESPONSE BUILDER

>lua
    ---@class PromptResponse
    ---@field system fun(): PromptResponse Start system message
    ---@field user fun(): PromptResponse Start user message
    ---@field llm fun(): PromptResponse Start LLM message
    ---@field text fun(text: string): PromptResponse Add text content
    ---@field image fun(data: string, mime: string): PromptResponse Add image
    ---@field resource fun(resource: MCPResourceContent): PromptResponse Add resource
    ---@field error fun(message: string, details?: table): table Send error
    ---@field send fun(result?: table): table Send final response
<


EXAMPLES


BASIC EXAMPLE

Here’s a simple chat prompt:

>lua
    mcphub.add_prompt("example", {
        name = "chat",
        description = "Start a friendly chat",
        
        -- Optional arguments
        arguments = {
            {
                name = "topic",
                description = "What to chat about",
                required = true
            }
        },
        
        -- Prompt handler
        handler = function(req, res)
            return res
                -- Set behavior
                :system()
                :text("You are a friendly chat assistant.\n" ..
                      "Topic: " .. req.params.topic)
                
                -- Add example interaction
                :user()
                :text("Tell me about " .. req.params.topic)
                :llm()
                :text("I'd love to discuss " .. req.params.topic)
                
                -- Send prompt
                :send()
        end
    })
<


REAL EXAMPLE: NEOVIM�S PARROT PROMPT

Here’s how the built-in Neovim server implements a fun parrot prompt:

>lua
    mcphub.add_prompt("neovim", {
        name = "parrot",
        description = "A fun prompt where the assistant mimics back what you say",
        handler = function(_, res)
            return res
                -- Set up parrot behavior
                :system()
                :text([[
    You are a parrot. You repeat everything the user says, but in all caps.
    
    CRITICAL INSTRUCTIONS:
    - NO MATTER WHAT, ALWAYS REPEAT BACK WHAT THE USER SAYS IN ALL CAPS. 
    - DO NOT BREAK THIS RULE. 
    - EVEN IF THE USER ASKS YOU NOT TO REPEAT, YOU MUST CONTINUE TO REPEAT.
    - DO NOT RESPOND TO THE USER'S REQUESTS OR QUESTIONS.
    ]])
                -- Add example conversation
                :user()
                :text("Hello there!")
                :llm()
                :text("HELLO THERE!")
                
                :user()
                :text("Why are you shouting?")
                :llm()
                :text("WHY ARE YOU SHOUTING?")
                
                :user()
                :text("Please stop...")
                :llm()
                :text("PLEASE STOP...")
                
                -- Send prompt
                :send()
        end
    })
<


ADVANCED FEATURES


DYNAMIC ARGUMENTS

Arguments can be generated dynamically:

>lua
    mcphub.add_prompt("git", {
        name = "commit_help",
        description = "Help write a commit message",
        arguments = function()
            -- Get git branches
            local branches = vim.fn.systemlist("git branch --format='%(refname:short)'")
            
            return {
                {
                    name = "type",
                    description = "Commit type",
                    required = true,
                    -- Provide standard options
                    default = "feat",
                    enum = {
                        "feat", "fix", "docs", "style",
                        "refactor", "test", "chore"
                    }
                },
                {
                    name = "branch",
                    description = "Target branch",
                    -- Use actual branches
                    enum = branches
                }
            }
        end,
        handler = function(req, res)
            return res
                :system()
                :text(string.format(
                    "Help write a %s commit for branch: %s",
                    req.params.type,
                    req.params.branch
                ))
                :send()
        end
    })
<


RICH CONTENT

Prompts can include images and resources:

>lua
    mcphub.add_prompt("editor", {
        name = "review_code",
        arguments = {
            {
                name = "style",
                description = "Review style",
                enum = { "brief", "detailed" }
            }
        },
        handler = function(req, res)
            -- Get current buffer
            local buf = req.editor_info.last_active
            if not buf then
                return res:error("No active buffer")
            end
            
            -- Generate code overview
            local overview = generate_overview(buf)
            
            return res
                -- Set review context
                :system()
                :text("You are a code reviewer.\n" ..
                      "Style: " .. req.params.style)
                
                -- Add code visualization
                :image(overview, "image/png")
                :text("Above is a visualization of the code structure.")
                
                -- Add relevant resources
                :resource({
                    uri = "neovim://diagnostics/current",
                    mimeType = "text/plain"
                })
                :text("Above are the current diagnostics.")
                
                -- Send prompt
                :send()
        end
    })
<


CONTEXT-AWARE PROMPTS

Adapt to different chat plugins:

>lua
    mcphub.add_prompt("context", {
        name = "explain_code",
        handler = function(req, res)
            -- Start with base behavior
            res:system()
               :text("You are a code explanation assistant.")
            
            -- Add context based on caller
            if req.caller.type == "codecompanion" then
                -- Add CodeCompanion chat context
                local chat = req.caller.codecompanion.chat
                res:text("\nPrevious discussion:\n" .. chat.history)
                
            elseif req.caller.type == "avante" then
                -- Add Avante code context
                local code = req.caller.avante.code
                res:text("\nSelected code:\n" .. code)
            end
            
            -- Add example interactions
            res:user()
               :text("Explain this code")
               :llm()
               :text("I'll explain the code in detail...")
            
            return res:send()
        end
    })
<


BEST PRACTICES ~

This guide covers essential patterns and recommendations for creating effective
native MCP servers.


CONSISTENT NAMING

>lua
    -- Tools: verb_noun format
    mcphub.add_tool("git", {
        name = "create_branch",    -- ✅ Clear action
        -- name = "branch_maker",  -- ❌ Unclear action
    })
    
    -- Resources: noun/category format
    mcphub.add_resource("git", {
        name = "Current Branch",    -- ✅ Clear content
        uri = "git://branch/current"
        -- uri = "git://getcurbr"   -- ❌ Unclear/abbreviated
    })
<


INPUT VALIDATION


1. TOOL ARGUMENTS

>lua
    mcphub.add_tool("files", {
        name = "read_lines",
        inputSchema = {
            type = "object",
            properties = {
                path = {
                    type = "string",
                    description = "File path",
                    examples = ["src/main.lua"]
                },
                start = {
                    type = "number",
                    minimum = 1,
                    description = "Start line (1-based)",
                    default = 1
                }
            },
            required = ["path"]
        }
    })
<


2. RESOURCE PARAMETERS

>lua
    mcphub.add_resource_template("git", {
        uriTemplate = "git://log/{count}",
        handler = function(req, res)
            -- Validate numeric parameter
            local count = tonumber(req.params.count)
            if not count or count < 1 then
                return res:error("Invalid count", {
                    received = req.params.count,
                    expected = "positive number"
                })
            end
        end
    })
<


ERROR HANDLING


1. PREREQUISITES

>lua
    mcphub.add_tool("git", {
        handler = function(req, res)
            -- Check environment
            if not vim.fn.executable("git") then
                return res:error("Git not installed", {
                    install = "https://git-scm.com"
                })
            end
            
            -- Check repository
            if not is_git_repo() then
                return res:error("Not a git repository", {
                    cwd = vim.fn.getcwd(),
                    action = "Initialize with 'git init'"
                })
            end
        end
    })
<


2. OPERATION ERRORS

>lua
    mcphub.add_tool("files", {
        handler = function(req, res)
            -- Handle operation failure
            local ok, result = pcall(function()
                return vim.fn.readfile(req.params.path)
            end)
            
            if not ok then
                return res:error("Failed to read file", {
                    error = result,
                    path = req.params.path,
                    permissions = vim.fn.getfperm(req.params.path)
                })
            end
        end
    })
<

These best practices help create robust, maintainable, and user-friendly native
MCP servers. Review them regularly as you develop your servers.


==============================================================================
5. Extensions                                         *mcphub.nvim-extensions*


AVANTE INTEGRATION                 *mcphub.nvim-extensions-avante-integration*

Add MCP capabilities to Avante.nvim <https://github.com/yetone/avante.nvim> by
following these steps:


ADD TOOLS TO AVANTE ~

>lua
    require("avante").setup({
        -- system_prompt as function ensures LLM always has latest MCP server state
        -- This is evaluated for every message, even in existing chats
        system_prompt = function()
            local hub = require("mcphub").get_hub_instance()
            return hub and hub:get_active_servers_prompt() or ""
        end,
        -- Using function prevents requiring mcphub before it's loaded
        custom_tools = function()
            return {
                require("mcphub.extensions.avante").mcp_tool(),
            }
        end,
    })
<

- The `get_active_servers_prompt()` function adds the running MCP servers from MCP Hub to `system_prompt`
- The `mcp_tool()` function adds two custom tools `use_mcp_tool` and `access_mcp_resource` to avante.


CONFIGURE AVANTE INTEGRATION ~

By default, MCP server prompts will be available as
`/mcp:server_name:prompt_name` in avante chat. If you are using `blink.cmp`
then you also need to configure `Kaiser-Yang/blink-cmp-avante`
<https://github.com/Kaiser-Yang/blink-cmp-avante>

Example blink.cmp configuration ~

>lua
    return {
      "saghen/blink.cmp",
      dependencies = {
        "Kaiser-Yang/blink-cmp-avante",
      },
      ---@module 'blink.cmp'
      ---@type blink.cmp.Config
      opts = {
        sources = {
          default = { "lsp", "avante", "path", "snippets", "buffer" },
          providers = {
            avante = {
              module = "blink-cmp-avante",
              name = "Avante",
              opts = {
                -- options for blink-cmp-avante
              },
            },
          },
        },
      }
    }
<

>lua
    require("mcphub").setup({
        extensions = {
            avante = {
                make_slash_commands = true, -- make /slash commands from MCP server prompts
            }
        }
    })
<


TOOL CONFLICTS ~

MCP Hub’s built-in Neovim server provides some basic development tools by
default.

Avante also provides built-in tools for file operations and terminal access.
You need to disable either the MCP Hub’s built-in tools or Avante’s tools
to avoid conflicts. If you prefer to use neovim server tools, you should
disable the corresponding Avante tools to prevent duplication:

>lua
    require("avante").setup({
        disabled_tools = {
            "list_files",    -- Built-in file operations
            "search_files",
            "read_file",
            "create_file",
            "rename_file",
            "delete_file",
            "create_dir",
            "rename_dir",
            "delete_dir",
            "bash",         -- Built-in terminal access
        },
    })
<


AUTO-APPROVAL ~

By default, whenever avante calls `use_mcp_tool` or `access_mcp_resource` tool,
it shows a confirm dialog with tool name, server name and arguments.


GLOBAL AUTO-APPROVAL

You can set `auto_approve` to `true` to automatically approve all MCP tool
calls without user confirmation:

>lua
    require("mcphub").setup({
        -- This sets vim.g.mcphub_auto_approve to true by default (can also be toggled from the HUB UI with `ga`)
        auto_approve = true, 
    })
<

This also sets `vim.g.mcphub_auto_approve` variable to `true`. You can also
toggle this option in the MCP Hub UI with `ga` keymap. You can see the current
auto approval status in the Hub UI.


FINE-GRAINED AUTO-APPROVAL

For more control, configure auto-approval per server or per tool in your
`servers.json`:

>json
    {
        "mcpServers": {
            "trusted-server": {
                "command": "npx",
                "args": ["trusted-mcp-server"],
                "autoApprove": true  // Auto-approve all tools on this server
            },
            "partially-trusted": {
                "command": "npx", 
                "args": ["some-mcp-server"],
                "autoApprove": ["read_file", "list_files"]  // Only auto-approve specific tools
            }
        }
    }
<

You can also toggle auto-approval from the Hub UI: - Press `a` on a server line
to toggle auto-approval for all tools on that server - Press `a` on an
individual tool to toggle auto-approval for just that tool - Resources are
always auto-approved (no configuration needed)


FUNCTION-BASED AUTO-APPROVAL

For maximum control, provide a function that decides approval based on the
specific tool call:

>lua
    require("mcphub").setup({
        auto_approve = function(params)
            -- Auto-approve GitHub issue reading
            if params.server_name == "github" and params.tool_name == "get_issue" then
                return true -- Auto approve
            end
            
            -- Block access to private repos
            if params.arguments.repo == "private" then
                return "You can't access my private repo" -- Error message
            end
            
            -- Auto-approve safe file operations in current project
            if params.tool_name == "read_file" then
                local path = params.arguments.path or ""
                if path:match("^" .. vim.fn.getcwd()) then
                    return true -- Auto approve
                end
            end
            
            -- Check if tool is configured for auto-approval in servers.json
            if params.is_auto_approved_in_server then
                return true -- Respect servers.json configuration
            end
            
            return false -- Show confirmation prompt
        end,
    })
<

**Parameters available in the function:** - `params.server_name` - Name of the
MCP server - `params.tool_name` - Name of the tool being called (nil for
resources) - `params.arguments` - Table of arguments passed to the tool -
`params.action` - Either "use_mcp_tool" or "access_mcp_resource" - `params.uri`
- Resource URI (for resource access) - `params.is_auto_approved_in_server` -
Boolean indicating if tool is configured for auto-approval in servers.json

**Return values:** - `true` - Auto-approve the call - `false` - Show
confirmation prompt - `string` - Deny with error message - `nil` - Show
confirmation prompt (same as false)


AUTO-APPROVAL PRIORITY

The system checks auto-approval in this order: 1. **Function**: Custom
`auto_approve` function (if provided) 2. **Server-specific**: `autoApprove`
field in server config 3. **Default**: Show confirmation dialog


USAGE ~

1. Start a chat in Avante
2. All the tools, resources, templates from the running MCP servers will be added to system prompt along with `use_mcp_tool` and `access_mcp_resource` tools.
3. Avante will call `use_mcp_tool` and `access_mcp_resource` tools when necessary


CODECOMPANION INTEGRATION   *mcphub.nvim-extensions-codecompanion-integration*

Add MCP capabilities to CodeCompanion.nvim
<https://github.com/olimorris/codecompanion.nvim> by adding it as an extension.


FEATURES ~

- **Flexible Tool Access**: Multiple ways to use MCP tools - from broad `@mcp` access to granular individual tools
- **Server Groups**: Access all tools from a specific server (e.g., `@neovim`, `@github`, `@tree_sitter`)
- **Individual Tools**: Use specific tools with clear namespacing (e.g., `@neovim__read_file`, `@github__create_issue`)
- **Custom Tool Groups**: Create your own tool combinations for specific workflows
- **Resource Variables**: Utilize MCP resources as context variables using the `#` prefix (e.g., `#mcp:resource_name`)
- **Slash Commands**: Execute MCP prompts directly using `/mcp:prompt_name` slash commands
- **Rich Media Support**: Supports 🖼 images and other media types as shown in the demo
- **Real-time Updates**: Automatic updates in CodeCompanion when MCP servers change


MCP HUB EXTENSION ~

Register MCP Hub as an extension in your CodeCompanion configuration:

>lua
    require("codecompanion").setup({
      extensions = {
        mcphub = {
          callback = "mcphub.extensions.codecompanion",
          opts = {
            -- MCP Tools
            make_tools = true,              -- Make individual tools (@server__tool) and server groups (@server) from MCP servers
            show_server_tools_in_chat = true, -- Show individual tools in chat completion (when make_tools=true)
            add_mcp_prefix_to_tool_names = false, -- Add mcp__ prefix (e.g `@mcp__github`, `@mcp__neovim__list_issues`)
            show_result_in_chat = true,      -- Show tool results directly in chat buffer
            format_tool = nil,               -- function(tool_name:string, tool: CodeCompanion.Agent.Tool) : string Function to format tool names to show in the chat buffer
            -- MCP Resources
            make_vars = true,                -- Convert MCP resources to #variables for prompts
            -- MCP Prompts
            make_slash_commands = true,      -- Add MCP prompts as /slash commands
          }
        }
      }
    })
<


USAGE ~

MCP Hub provides multiple ways to access MCP tools in CodeCompanion, giving you
flexibility from broad access to fine-grained control:


TOOL ACCESS


1. UNIVERSAL MCP ACCESS (@MCP)

Adds all available MCP servers to the system prompt and provides LLM with
`@mcp` tool group which has `use_mcp_tool` and `access_mcp_resource` tools.

>
    @{mcp} What files are in the current directory?
<


2. SERVER GROUPS (WHEN MAKE_TOOLS = TRUE)

You can add all the enabled tools from a specific server with server groups.
Unlike the `@mcp` group where all the running servers are converted and added
to the system prompt, the tools added with server groups are pure function
tools and hence depend on model support. The available groups depend on your
connected MCP servers:

>
    @{neovim} Read the main.lua file    # All tools from the neovim server will be added as function tools
    @{github} Create an issue
    @{fetch} Get this webpage
<

Server groups are automatically created based on your connected MCP servers
when enabled via `make_tools`. Check your MCP Hub UI to see which servers you
have connected.

MCPHub includes powerful builtin servers </mcp/builtin/neovim> like `@neovim`
(file operations, terminal, LSP) and `@mcphub` (server management) that are
always available.


3. INDIVIDUAL TOOLS (WHEN MAKE_TOOLS = TRUE)

You can just provide a single tool from a server for fine-grained
functionality. Tool names depend on your connected servers:

>
    @{neovim__read_file} Show me the config file
    @{fetch__fetch} Get this webpage content
    @{github__create_issue} File a bug report
<

Use the MCP Hub UI or CodeCompanion’s tool completion to discover available
tools.


4. CUSTOM TOOL SETS

Create your own tool combinations by mixing MCP tools with existing
CodeCompanion tools:

Example configuration for custom tool groups:

>lua
    require("codecompanion").setup({
      interactions = {
        chat = {
          tools = {
            groups = {
              ["github_pr_workflow"] = {
                description = "GitHub operations from issue to PR",
                tools = {
                  -- File operations
                  "neovim__read_multiple_files", "neovim__write_file", "neovim__edit_file",
                  -- GitHub operations
                  "github__list_issues", "github__get_issue", "github__get_issue_comments",
                  "github__create_issue", "github__create_pull_request", "github__get_file_contents",
                  "github__create_or_update_file",  "github__search_code"
                },
              },
            },
          },
        },
      },
      extensions = {
        mcphub = {
          callback = "mcphub.extensions.codecompanion",
          opts = {
            make_tools = true,  -- Required for individual tools
            -- ... other options
          }
        }
      }
    })
<

Then use your custom groups:

>
    @{github_pr_workflow} Fix this bug, create tests, and submit a PR with proper documentation
<

**Important Notes:** - Tool names depend on your connected MCP servers - Use
MCP Hub UI or Codecompanion’s tool completion to see available servers and
tools - Tool names follow the pattern `servername__toolname` - Mix MCP tools
with CodeCompanion’s built-in tools (`cmd_runner`, `editor`, `files`, etc.) -
Each MCP tool can be individually auto-approved for fine-grained control (see
Auto-Approval section)


RESOURCES AS VARIABLES

If `make_vars = true`, MCP resources become available as variables prefixed
with `#`:

>
    Fix diagnostics in the file #{mcp:neovim://diagnostics/buffer}
    Analyze the current buffer #{mcp:neovim:buffer}
<

_Example: Accessing LSP diagnostics_:


SLASH COMMANDS

If `make_slash_commands = true`, MCP prompts are available as slash commands:

>
    /mcp:code_review
    /mcp:explain_function
    /mcp:generate_tests
<

_Example: Using an MCP prompt via slash command_:


AUTO-APPROVAL ~

By default, whenever codecompanion calls `use_mcp_tool` or
`access_mcp_resource` tool or a specific tool on some MCP server, it shows a
confirm dialog with tool name, server name and arguments.


FINE-GRAINED AUTO-APPROVAL

For fine-grained control, configure auto-approval per server or per tool in
your `servers.json`:

>json
    {
        "mcpServers": {
            "trusted-server": {
                "command": "npx",
                "args": ["trusted-mcp-server"],
                "autoApprove": true  // Auto-approve all tools on this server
            },
            "partially-trusted": {
                "command": "npx",
                "args": ["some-mcp-server"],
                "autoApprove": ["read_file", "list_files"]  // Only auto-approve specific tools
            }
        }
    }
<

You can also toggle auto-approval from the Hub UI: - Press `a` on a server line
to toggle auto-approval for all tools on that server - Press `a` on an
individual tool to toggle auto-approval for just that tool - Resources are
always auto-approved (no configuration needed)


GLOBAL AUTO-APPROVAL

You can set `auto_approve` to `true` to automatically approve all MCP tool
calls without user confirmation:

>lua
    require("mcphub").setup({
        -- This sets vim.g.mcphub_auto_approve to true by default (can also be toggled from the HUB UI with `ga`)
        auto_approve = true,
    })
<

This also sets `vim.g.mcphub_auto_approve` variable to `true`. You can also
toggle this option in the MCP Hub UI with `ga` keymap. You can see the current
auto approval status in the Hub UI.


FUNCTION-BASED AUTO-APPROVAL

For maximum control, provide a function that decides approval based on the
specific tool call:

>lua
    require("mcphub").setup({
        auto_approve = function(params)
            -- Respect CodeCompanion's auto tool mode when enabled
            if vim.g.codecompanion_auto_tool_mode == true then
                return true -- Auto approve when CodeCompanion auto-tool mode is on
            end
    
            -- Auto-approve GitHub issue reading
            if params.server_name == "github" and params.tool_name == "get_issue" then
                return true -- Auto approve
            end
    
            -- Block access to private repos
            if params.arguments.repo == "private" then
                return "You can't access my private repo" -- Error message
            end
    
            -- Auto-approve safe file operations in current project
            if params.tool_name == "read_file" then
                local path = params.arguments.path or ""
                if path:match("^" .. vim.fn.getcwd()) then
                    return true -- Auto approve
                end
            end
    
            -- Check if tool is configured for auto-approval in servers.json
            if params.is_auto_approved_in_server then
                return true -- Respect servers.json configuration
            end
    
            return false -- Show confirmation prompt
        end,
    })
<

**Parameters available in the function:** - `params.server_name` - Name of the
MCP server - `params.tool_name` - Name of the tool being called (nil for
resources) - `params.arguments` - Table of arguments passed to the tool -
`params.action` - Either "use_mcp_tool" or "access_mcp_resource" - `params.uri`
- Resource URI (for resource access) - `params.is_auto_approved_in_server` -
Boolean indicating if tool is configured for auto-approval in servers.json

**Return values:** - `true` - Auto-approve the call - `false` - Show
confirmation prompt - `string` - Deny with error message - `nil` - Show
confirmation prompt (same as false)


AUTO-APPROVAL PRIORITY

The system checks auto-approval in this order: 1. **Function**: Custom
`auto_approve` function (if provided) 2. **Server-specific**: `autoApprove`
field in server config 3. **Default**: Show confirmation dialog


COPILOTCHAT INTEGRATION       *mcphub.nvim-extensions-copilotchat-integration*

Add MCP capabilities to CopilotChat.nvim
<https://github.com/CopilotC-Nvim/CopilotChat.nvim> by adding it as an
extension. CopilotChat now has native function-calling support, making it easy
to integrate MCP tools and resources.


FEATURES ~

- **Tool Integration**: Register MCP tools as CopilotChat functions with proper schemas
- **Resource Integration**: Register MCP resources as CopilotChat functions for easy access
- **Server Groups**: Functions are organized by MCP server name for better organization
- **Real-time Updates**: Automatic updates in CopilotChat when MCP servers change


SETUP ~


ENABLE COPILOTCHAT EXTENSION

Add CopilotChat as an extension in your MCPHub configuration:

>lua
    require("mcphub").setup({
        extensions = {
            copilotchat = {
                enabled = true,
                convert_tools_to_functions = true,     -- Convert MCP tools to CopilotChat functions
                convert_resources_to_functions = true, -- Convert MCP resources to CopilotChat functions  
                add_mcp_prefix = false,                -- Add "mcp_" prefix to function names
            }
        }
    })
<


CONFIGURATION OPTIONS

- **convert_tools_to_functions**: When `true`, all MCP tools are registered as CopilotChat functions
- **convert_resources_to_functions**: When `true`, all MCP resources are registered as CopilotChat functions
- **add_mcp_prefix**: When `true`, adds "mcp_" prefix to all function names (e.g., `mcp_github__get_issue`)


USAGE ~


MCP TOOLS

When `convert_tools_to_functions = true`, all MCP tools become available as
CopilotChat functions. Functions are automatically organized by server name,
making it easy to see all tools from a specific MCP server. Tool names follow
the pattern `server_name__tool_name`:

**Examples:** - `@neovim__read_file` - Read a file using the neovim server -
`@github__create_issue` - Create a GitHub issue - `@fetch__fetch` - Fetch web
content


MCP RESOURCES

You can use `#` to access MCP resources as variables in CopilotChat. In
addition, when `convert_resources_to_functions = true`, all MCP resources will
also be available as CopilotChat functions:

**Examples:** - `#neovim__Buffer` - Access current buffer content -
`@neovim__Buffer` - Get current buffer content (as a function)


EXAMPLE WORKFLOW ~

1. **Start CopilotChat** and type `@` to see available functions
2. **Select MCP functions** from your connected servers
3. **Use tools**: `@github__create_issue Fix the navigation bug`
4. **Access resources**: `@neovim__current_buffer Show me the current file content`
5. **Organize by server**: Browse functions grouped by MCP server


LUALINE INTEGRATION               *mcphub.nvim-extensions-lualine-integration*

MCP Hub provides multiple ways to integrate with lualine, with the recommended
approach using global variables for optimal lazy-loading support.


RECOMMENDED: GLOBAL VARIABLES APPROACH ~

Use MCPHub’s global variables for a lightweight, lazy-load friendly
component:

>lua
    require('lualine').setup {
        sections = {
            lualine_x = {
                {
                    function()
                        -- Check if MCPHub is loaded
                        if not vim.g.loaded_mcphub then
                            return "󰐻 -"
                        end
                        
                        local count = vim.g.mcphub_servers_count or 0
                        local status = vim.g.mcphub_status or "stopped"
                        local executing = vim.g.mcphub_executing
                        
                        -- Show "-" when stopped
                        if status == "stopped" then
                            return "󰐻 -"
                        end
                        
                        -- Show spinner when executing, starting, or restarting
                        if executing or status == "starting" or status == "restarting" then
                            local frames = { "⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏" }
                            local frame = math.floor(vim.loop.now() / 100) % #frames + 1
                            return "󰐻 " .. frames[frame]
                        end
                        
                        return "󰐻 " .. count
                    end,
                    color = function()
                        if not vim.g.loaded_mcphub then
                            return { fg = "#6c7086" } -- Gray for not loaded
                        end
                        
                        local status = vim.g.mcphub_status or "stopped"
                        if status == "ready" or status == "restarted" then
                            return { fg = "#50fa7b" } -- Green for connected
                        elseif status == "starting" or status == "restarting" then  
                            return { fg = "#ffb86c" } -- Orange for connecting
                        else
                            return { fg = "#ff5555" } -- Red for error/stopped
                        end
                    end,
                },
            },
        },
    }
<


AVAILABLE GLOBAL VARIABLES

MCPHub automatically maintains these global variables: - `vim.g.loaded_mcphub`
- Whether MCPHub plugin is loaded (set by plugin loader) -
`vim.g.mcphub_status` - Current hub state ("starting", "ready", "stopped",
etc.) - `vim.g.mcphub_servers_count` - Number of connected servers -
`vim.g.mcphub_executing` - Whether a tool/resource is currently executing


LEGACY: FULL COMPONENT (LOADS MCPHUB) - DEPRECATED ~

**�� ️ DEPRECATED**: This approach will load MCPHub even with lazy loading
enabled and shows a deprecation warning. Use the global variables approach
above instead.

>lua
    require('lualine').setup {
        sections = {
            lualine_x = {
                {require('mcphub.extensions.lualine')}, -- Uses defaults
            },
        },
    }
<


WHEN MCP HUB IS CONNECTING:


WHEN CONNECTED SHOWS NUMBER OF CONNECTED SERVERS:


WHEN A TOOL OR RESOURCE IS BEING CALLED, SHOWS SPINNER:


LEGACY COMPONENT OPTIONS

The lualine component accepts the standard lualine options and the following
options: - `icon`: Icon to display. (default: `"󰐻"`) - `colored`: Enable
dynamic colors (default: `true`) - `colors`: The color to dynamically display
for each MCP Hub state - `connecting`: The color to use when MCP Hub is
connecting (default: `"DiagnosticWarn"`) - `connected`: The color to use when
MCP Hub is connected (default: `"DiagnosticInfo"`) - `error`: The color to use
when MCP Hub encounters an error (default: `"DiagnosticError"`)


CUSTOMIZATION EXAMPLES

>lua
    -- Custom icon
    {require('mcphub.extensions.lualine'), icon = ''}
<

>lua
    -- Custom colors
    {
      require('mcphub.extensions.lualine'),
      colors = {
        connecting = { fg = "#ffff00" }, -- Yellow
        connected = { fg = "#00ff00" }, -- Green
        error = { fg = "#ff0000" }, -- Red
      },
    }
<

>lua
    -- Statically color the icon, dynamically color the status text
    {require('mcphub.extensions.lualine'), icon = { '󰐻', color = {fg = '#eeeeee'}}}
<

>lua
    -- Dynamically color the icon, statically color the status text
    {require('mcphub.extensions.lualine'), color = {fg = '#eeeeee'}}
<

>lua
    -- Disable coloring
    {require('mcphub.extensions.lualine'), colored = false}
<


==============================================================================
6. Other                                                   *mcphub.nvim-other*

```{.include shift-heading-level-by=1} doc/other/api.md
doc/other/architecture.md doc/other/troubleshooting.md

==============================================================================
7. Links                                                   *mcphub.nvim-links*

1. *Image*: doc/https:/github.com/user-attachments/assets/7c299fbd-4820-4065-8b07-50db66179d3d
2. *Image*: doc/https:/github.com/user-attachments/assets/201a5804-99b6-4284-9351-348899e62467
3. *Image*: doc/https:/github.com/user-attachments/assets/64708065-3428-4eb3-82a5-e32d2d1f98c6
4. *Image*: doc/mcp/https:/github.com/user-attachments/assets/f5c8adfa-601e-4d03-8745-75180a9d3648
5. *Image*: doc/mcp/https:/github.com/user-attachments/assets/560bddda-e48d-488b-a9f8-7b188178914c
6. *Image*: doc/mcp/https:/github.com/user-attachments/assets/1cb950da-2f7f-46e9-a623-4cc4b00cc3d0
7. *Image*: doc/mcp/https:/github.com/user-attachments/assets/131bfed2-c4e7-4e2e-ba90-c86e6ca257fd
8. *Image*: doc/mcp/https:/github.com/user-attachments/assets/befd1d44-bca3-41f6-a99a-3d15c6c8a5f5
9. *Image*: doc/extensions/https:/github.com/user-attachments/assets/47086587-d10a-4749-a5df-3a562750010e
10. *Image*: doc/extensions/https:/github.com/user-attachments/assets/dbc0d210-2ccf-49f8-b1f5-58d868dc02c8
11. *Image*: doc/extensions/https:/github.com/user-attachments/assets/201a5804-99b6-4284-9351-348899e62467
12. *Image*: doc/extensions/https:/github.com/user-attachments/assets/64708065-3428-4eb3-82a5-e32d2d1f98c6
13. *Image*: doc/extensions/https:/github.com/user-attachments/assets/131bfed2-c4e7-4e2e-ba90-c86e6ca257fd
14. *Image*: doc/extensions/https:/github.com/user-attachments/assets/befd1d44-bca3-41f6-a99a-3d15c6c8a5f5
15. *image*: doc/extensions/https:/github.com/user-attachments/assets/fb04393c-a9da-4704-884b-2810ff69f59a
16. *image*: doc/extensions/https:/github.com/user-attachments/assets/678a06a5-ada9-4bb5-8f49-6e58549c8f32
17. *Image*: doc/extensions/https:/github.com/user-attachments/assets/201a5804-99b6-4284-9351-348899e62467
18. *Image*: doc/extensions/https:/github.com/user-attachments/assets/131bfed2-c4e7-4e2e-ba90-c86e6ca257fd
19. *Image*: doc/extensions/https:/github.com/user-attachments/assets/befd1d44-bca3-41f6-a99a-3d15c6c8a5f5
20. *Image*: doc/extensions/https:/github.com/user-attachments/assets/64708065-3428-4eb3-82a5-e32d2d1f98c6
21. *Tool Functions*: doc/extensions/https:/github.com/user-attachments/assets/7c16bc7e-a9df-4afc-9736-2ee6a39919a9
22. *Resource Functions*: doc/extensions/https:/github.com/user-attachments/assets/7f77bf1e-12b7-4745-a87b-40181a619733
23. *Server Groups*: doc/extensions/https:/github.com/user-attachments/assets/adc556bb-7d5f-4d22-820a-a7daeb0ac72c
24. *image*: doc/extensions/https:/github.com/user-attachments/assets/f67802fe-6b0c-48a5-9275-bff9f830ce29
25. *image*: doc/extensions/https:/github.com/user-attachments/assets/f90f7cc4-ff34-4481-9732-a0331a26502b
26. *image*: doc/extensions/https:/github.com/user-attachments/assets/f6bdeeec-48f7-48de-89a5-22236a52843f
27. *Image*: doc/extensions/https:/github.com/user-attachments/assets/3f4fd202-d780-441f-a8cf-58d8a8414ab1
28. *Image*: doc/extensions/https:/github.com/user-attachments/assets/5522b929-d9b1-472c-9bf8-1c14aef36dbe
29. *Image*: doc/extensions/https:/github.com/user-attachments/assets/9f309871-5fda-458f-967e-e7d3d8b269a5
30. *Image*: doc/extensions/https:/github.com/user-attachments/assets/e3c16813-2210-4b7c-9f79-2737c19c6c30
31. *Image*: doc/extensions/https:/github.com/user-attachments/assets/78aea188-59e8-4299-a375-1acc0784c7bf

Generated by panvimdoc <https://github.com/kdheepak/panvimdoc>

vim:tw=78:ts=8:noet:ft=help:norl:
