Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Opening multiple Neovim instances simultaneously results in an error #19

Open
rwjblue opened this issue Mar 15, 2025 · 4 comments
Open
Assignees

Comments

@rwjblue
Copy link

rwjblue commented Mar 15, 2025

Firstly, thank you for the awesome plugin! It has really made my life better.


I realize this is probably not a super standard setup, but I have utility functions that launch tmux with a bunch of sessions (e.g. one for each project that I have going). When I do that (and ~ 10 nvim instances open basically all at once) in many of them I get an error (EADDRINUSE) because they are all trying to start up mcp-hub (since it wasn't running before).

I realize this is basically my problem, but I'm curious if you've thought about it and if you have any advice on how best to handle it.

@rwjblue
Copy link
Author

rwjblue commented Mar 15, 2025

I'm not 100% sure why the plugin is being evaluated eagerly either. Maybe the solution here for me is to figure out why and make it lazier which would almost certainly avoid the race condition, since I'm only going to be directly triggering a single nvim instance at a time.

I think I'm just following the setup steps in the README:

https://github.com/rwjblue/dotfiles/blob/master/packages/nvim/lua/plugins/mcp-hub.lua

@ravitemer
Copy link
Owner

Glad it is helpful!

I thought about this while implementing the mcp-hub server to have some kind of lock file system to handle this scenario. But wanted to first make a working server for a wide audience.

~ 10 nvim instances open basically all at once

This is hard-core. 😄

I will work on this ASAP.

In the meantime, I have an idea that works.

The neovim plugin first checks if the server at the port is running or not. If it's running already, it will just use that rather than trying to start mcp-hub job. So If we make sure that the hub is started somewhere outside of neovim, any number of nvim instances will use that single server.

Start hub in another tmux session like
mcp-hub -p 3000 -c ~/mcpservers.json --shutdown-delay 100000

Normally when all the registered clients to the server are disconnected, it usually shuts down to avoid running forever if forgotten. The shutdown-delay in ms tells the server that even if all the registered clients are disconnected (closing all the neovim instances) wait for this number of milliseconds before shutting down. You can start the hub in another tmux session or even on device startup. This way we can connect any number of clients and get instant access to all the servers.

Note: Currently neovim plugin listens to tool_list and resource_list changes based on the server stdout rather than connecting to /events endpoint. So for now, with this approach we might have to manually refresh "r" the hub to get latest resources or tools if they are changed. (e.g when using puppeteer mcp server, calling screenshot tool takes the screenshot and returns the image. At the sametime It also adds a new resource and notify that resource_list has changed). This resource_list and tool_list changed notifications are scarcely implemented and are of no use with the chat plugins. So, you can ignore this for now.

Please let me know your thoughts and suggestions. Thanks

@ravitemer
Copy link
Owner

ravitemer commented Mar 15, 2025

make it lazier which would almost certainly avoid the race condition,

Yes! You can do this with

 {
    "ravitemer/mcphub.nvim",
    dependencies = {
      "nvim-lua/plenary.nvim",
    },
    cmd = "MCPHub"
    build = "mise up mcp-hub@latest",
    --rest stays same

in you mcphub.lua file. This way the mcp-hub job will only be started when MCPHub is called or require(mcphub...) is called. If you are using codecompanion chat plguin and using mcp tool as

callback = require("mcphub.extensions.codecompanion")

this might make it eager even with cmd = MCPHub.

You can change it to the following to lazy load the extension

  require("codecompanion").setup({
    strategies = {
        chat = {
            tools = {
                ["mcp"] = {
                    callback = function() return require("mcphub.extensions.codecompanion") end,
                    description = "Call tools and resources from the MCP Servers",
                    opts = {
                      -- user_approval = true,
                      requires_approval = true,
                    }
                }
            }
        }
    }
  })

The default way is not set as lazy because I wanted to make sure the mcphub.nvim is ready by the time user uses the @mcp tool in their chat

@rwjblue
Copy link
Author

rwjblue commented Mar 15, 2025

Thanks for the suggestions! For now, I've gone the lazy route (cmd = "MCPHub" + putting the CodeCompanion callback call into a closure instead of invoking it eagerly) as opposed to eagerly starting the CLI (but that totally would have worked as well).

The default way is not set as lazy because I wanted to make sure the mcphub.nvim is ready by the time user uses the @mcp tool in their chat

I also added this to try to help kick things off quicker (e.g. while I'm typing out my prompt or whatever, mcphub has a chance to get going...):

local group = vim.api.nvim_create_augroup("CodeCompanionHooks_MCPHub", {})

vim.api.nvim_create_autocmd({ "User" }, {
  pattern = "CodeCompanionChatCreated",
  group = group,
  callback = function(request)
    require("mcphub")
  end,
})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants