mcphub.nvim

repository·main·Indexed 23 days ago

https://github.com/ravitemer/mcphub.nvim

A Neovim client for the Model Context Protocol (MCP) that integrates MCP servers (tools, resources, and prompts) into AI-powered chat workflows. It supports transports like Streamable-HTTP, SSE, and STDIO, and integrates with chat plugins such as Avante.nvim, CodeCompanion.nvim, and CopilotChat.nvim. The plugin allows for native Lua MCP server implementation, workspace-local configurations, and VS Code compatible server settings.

Tokens
46K
Snippets
91
Records
152
Agent score
81%

What's inside mcphub.nvim

  1. What is MCP Hub

    main
    MCP Hub is a Model Context Protocol (MCP) client for Neovim. It integrates MCP servers into your Neovim editing workflow, allowing you to manage, test, and use MCP servers with popular chat plugins. It supports tools, resources, and prompts, providing real-time updates when server capabilities change.
  2. How MCPHub.nvim manages the server lifecycle

    main

    MCPHub.nvim operates using an Express server that manages MCP servers and handles client requests. The lifecycle is designed for multi-instance support, allowing multiple Neovim instances to connect to a single shared hub.

    Initialization Sequence

    When setup() is called, the following occurs:

    1. Pre-flight checks: The plugin checks for the mcp-hub command installation, verifies version compatibility, and checks if a server is already running.
    2. Server Startup: If no server is running, it starts mcp-hub with config file watching enabled.
    3. Server Binding: An Express server is created at http://localhost:[config.port] or the specified config.server_url.
    4. Ready Callback: Once setup is successful, the on_ready callback is triggered, providing access to the hub instance and its REST API.

    Multi-instance and Shutdown Behavior

    • Shared State: Multiple Neovim instances can connect to the same running server. New instances register as clients.
    • Automatic Cleanup: When a Neovim instance closes, it unregisters as a client. The server remains active as long as there is at least one client connected. When the last client unregisters, a shutdown timer is triggered. If a new client connects before the timer expires, the shutdown is canceled.
  3. Implement a Tool Handler

    main

    Tool handlers are functions that receive a ToolRequest and a ToolResponse builder.

    ToolRequest contains:

    • params: Validated tool arguments.
    • tool: The complete MCPTool definition.
    • server: The NativeServer instance.
    • caller: Context about the caller (e.g., which LLM plugin is calling the tool).
    • editor_info: Current Neovim editor state (e.g., active buffer info).

    ToolResponse (Chainable Builder):

    • text(string): Add text content.
    • image(data, mime): Add image data.
    • audio(data, mime): Add audio data.
    • resource(MCPResourceContent): Add a resource.
    • error(message, details?): Send an error response.
    • send(result?): Finalize and send the response.
  4. Compare Plugin-Specific Tools vs Native MCP Servers

    main

    When building tools for AI chat plugins in Neovim, you can either use plugin-specific tool implementations (like those provided by Avante or CodeCompanion) or use MCPHub's Native MCP Servers.

    Native MCP Servers offer several advantages:

    • Write Once, Use Everywhere: Tools are written once using the standard MCP protocol and work across all compatible chat plugins.
    • Rich Response Types: Supports text, images, and blobs, whereas plugin-specific tools are often limited to text.
    • Resource System: Provides a URI-based system for accessing data.
    • Deep Editor Integration: Direct access to Neovim APIs and editor state.
    • Plugin-Aware Context: The ability to detect which plugin is calling the tool and adapt behavior accordingly.
    • Standard Protocol: Ensures consistent behavior and future compatibility.
  5. Chat Plugin Integrations

    main

    MCP Hub integrates with several Neovim chat plugins to expose MCP capabilities:

    • Avante.nvim: Supports Tools, resources, resourceTemplates, and prompts (exposed as slash_commands).
    • CodeCompanion.nvim: Supports Tools, resources, templates, prompts (exposed as slash_commands), and image responses.
    • CopilotChat.nvim: Supports Tools, resources, and function calling.
  6. Access Editor State via EditorInfo

    main

    The req.editor_info field provides a consistent view of the current Neovim editor state, including the last_active buffer and a list of all open buffers. This allows tools to interact with the user's workspace regardless of which plugin triggered the tool.

    ---@class EditorInfo
    ---@field last_active BufferInfo # Currently active buffer
    ---@field buffers BufferInfo[] # List of all buffers
    
    ---@class BufferInfo
    ---@field name string # Buffer name
    ---@field filename string # Full file path
    ---@field windows number[] # Window IDs showing this buffer
    ---@field winnr number # Primary window number
    ---@field cursor_pos number[] # Cursor position [row, col]
    ---@field filetype string # Buffer filetype
    ---@field line_count number # Total lines
    ---@field is_visible boolean # Whether buffer is visible
    ---@field is_modified boolean # Whether buffer is modified
    ---@field is_loaded boolean # Whether buffer is loaded
    ---@field lastused number # Last used timestamp
    ---@field bufnr number # Buffer number
    
    -- Example usage:
    mcphub.add_tool("buffer", {
        name = "get_info",
        description = "Get buffer information",
        handler = function(req, res)
        local info = req.editor_info
        local active = info.last_active
    
        -- Access current buffer state
        local details = {
        name = active.filename,
        type = active.filetype,
        lines = active.line_count,
        cursor = active.cursor_pos,
        modified = active.is_modified,
        visible = active.is_visible
        }
    
        -- List all open buffers
        local buffers = {}
        for _, buf in ipairs(info.buffers) do
        table.insert(buffers, buf.filename)
          end
    
          return res:text(vim.inspect({
                active = details,
                open_buffers = buffers
                })):send()
                    end
    })
  7. How MCP Hub works

    main

    MCP Hub acts as a central manager for Model Context Protocol (MCP) servers. When Neovim starts and setup() is called, it launches a background mcp-hub nodejs binary that reads your configuration file and manages the lifecycle of your MCP servers.

    It provides two primary interfaces:

    1. Management API (default: http://localhost:37373/api): Used by the Neovim plugin to start/stop servers, execute tools, access resources, and handle real-time events.
    2. Unified MCP Endpoint (http://localhost:37373/mcp): A single MCP server that aggregates the capabilities of ALL managed servers. You can point other MCP clients (like Claude Desktop or Cursor) to this single endpoint to gain access to all your configured tools and resources without individual configuration.
  8. What are Native MCP Servers in MCPHub.nvim

    main

    Native MCP Servers implement the Model Context Protocol (MCP) directly within Neovim using Lua. Unlike external MCP servers that run as separate processes, Native Servers run within Neovim's runtime, providing direct access to Neovim APIs, buffer/window management, and editor state.

    Key benefits include:

    • Full MCP Support: Implements tools, resources, and prompts using standard request/response formats.
    • Write Once, Run Anywhere: A single Lua implementation works across all chat plugins compatible with MCPHub.
    • Rich Response System: Supports standard response types like text, images, and blobs with MIME type support.
    • Deep Integration: Native Lua performance and direct access to Neovim's internal APIs.
  9. Define an MCP Tool

    main

    An MCPTool is a function that an LLM can call. You register tools using mcphub.add_tool(namespace, tool_definition).

    A tool definition requires a name, a description (or a function returning a string), and a handler function. You can optionally provide an inputSchema (JSON Schema) to validate the arguments passed by the LLM.

    ---@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
  10. Access Neovim context via URI resources

    main

    You can retrieve structured context about your environment using specific URI patterns:

    • neovim://buffer: Provides metadata (name, bufnr, line count), full buffer content with line numbers, cursor position, marks, and quickfix entries.
    • neovim://workspace: Provides system info (OS, hostname, memory), workspace details (current directory, git status), file structure, and lists of visible/loaded files.
    • neovim://diagnostics/buffer: Provides LSP diagnostics (error, warning, info, hint) for the active buffer, including line/column and source info.
    • neovim://diagnostics/workspace: Provides LSP diagnostics across all open buffers, grouped by file.
  11. Access Caller Context in Handlers

    main

    When a tool or resource is called from a chat plugin, the req.caller field provides context about the calling system. You can use this to adapt behavior (e.g., retrieving the correct buffer from avante or codecompanion).

    Supported type values:

    • avante
    • codecompanion
    • hubui
    mcphub.add_tool("workspace", {
        name = "analyze_buffer",
        description = "Analyze current buffer",
        handler = function(req, res)
        -- Get correct buffer based on caller
        local bufnr
        if req.caller.type == "codecompanion" then
        -- Get buffer from CodeCompanion chat context
        local chat = req.caller.codecompanion.chat
        bufnr = chat.context.bufnr
        local is_var = req.caller.meta.is_within_variable -- true if called from #variable
    
        elseif req.caller.type == "avante" then
        -- Get buffer from Avante code context
        bufnr = req.caller.avante.code.bufnr
    
        elseif req.caller.type == "hubui" then
        -- Using hub UI context
        bufnr = req.caller.hubui.context.bufnr or 0
        end
    
        -- Use the buffer number
        local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
        return res:text(#lines .. " lines found"):send()
        end
    })
  12. How Workspaces work in MCP Hub

    main

    Workspaces allow for isolated MCP server setups per project by using project-local configuration files. This enables different server configurations (like filesystem access or LSP settings) for different projects without manually editing a global configuration.

    Project Detection

    When you open Neovim, MCP Hub searches upward from the current directory for the first available configuration file:

    • .mcphub/servers.json (MCP Hub specific)
    • .vscode/mcp.json (VS Code compatibility)
    • .cursor/mcp.json (Cursor compatibility)

    Hub Instance Lifecycle

    Each detected workspace triggers the creation of a unique hub instance with:

    • Unique port: Generated from a hash of the project path.
    • Isolated processes: A separate mcp-hub instance running its own servers.
    • Merged configuration: Project-specific configurations override the global configuration.
    • Project context: The hub starts with its cwd set to the project directory.

    Automatic Switching

    If reload_on_dir_changed is enabled, MCP Hub automatically detects new workspaces and switches to the appropriate hub instance when you change directories using :cd.