Workspaces
MCP Hub supports project-local configuration files that enable isolated MCP server setups per project. This solves the key problem of needing different server configurations for different projects.
Why Workspaces?
Consider these common scenarios:
Filesystem Access: mcp-server-filesystem
needs access to specific project directories:
{
"mcpServers": {
"filesystem": {
"command": "uvx",
"args": ["mcp-server-filesystem", "${CWD}"]
}
}
}
Language Server Integration: mcp-language-server
requires project-specific workspace and LSP configurations:
{
"mcpServers": {
"lsp": {
"command": "mcp-language-server",
"args": [
"--workspace", "${CWD}",
"--lsp", "typescript-language-server",
"--", "--stdio"
]
}
}
}
Without workspaces, you'd need to manually edit the global config for every project. With workspaces, each project gets its own configuration automatically.
How It Works
1. Project Detection
When you open Neovim in a directory, MCP Hub searches upward from the current directory for:
.mcphub/servers.json
(MCP Hub specific).vscode/mcp.json
(VS Code compatibility).cursor/mcp.json
(Cursor compatibility)
The first file found defines the project boundary.
2. Hub Instance Creation
Each detected workspace gets:
- Unique port: Generated from project path hash (e.g.,
40380
) - Isolated processes: Separate
mcp-hub
instance with its own servers - Merged configuration: Project config overrides global config
- Project context: Hub starts with
cwd
set to project directory
3. Automatic Switching
When you change directories (cd
), MCP Hub automatically:
- Detects the new workspace
- Connects to the appropriate hub instance
- Switches to the correct server configuration
Configuration
Enable workspaces in your MCP Hub setup:
require("mcphub").setup({
workspace = {
enabled = true, -- Default: true
look_for = { ".mcphub/servers.json", ".vscode/mcp.json", ".cursor/mcp.json" },
reload_on_dir_changed = true, -- Auto-switch on directory change
port_range = { min = 40000, max = 41000 }, -- Port range for workspace hubs
get_port = nil, -- Optional function for custom port assignment
}
})
Example Setup
Global Config (~/.config/mcphub/servers.json
)
{
"mcpServers": {
"memory": {
"command": "uvx",
"args": ["@modelcontextprotocol/server-memory"]
}
}
}
Project Config (.mcphub/servers.json
)
{
"mcpServers": {
"filesystem": {
"command": "uvx",
"args": ["mcp-server-filesystem", "${CWD}"]
},
"lsp": {
"command": "mcp-language-server",
"args": [
"--workspace", "${CWD}",
"--lsp", "lua-language-server",
"--", "--stdio"
]
}
}
}
Environment Variables
CWD
will be set to the working directory of the current running mcp-hub
. Basic environment variables (HOME
, USER
, TERM
, SHELL
, etc.) are always available.
Additional variables can be set via:
- global_env configuration in MCP Hub setup
- Process environment when the hub starts
require("mcphub").setup({
global_env = function(context)
return {
DBUS_SESSION_BUS_ADDRESS = os.getenv("DBUS_SESSION_BUS_ADDRESS") or "",
}
end
})
⚠️ Note: Environment variables are captured when the hub starts. To refresh environment, restart the hub with R
in the MCP Hub UI.
Custom Port Assignment
By default, MCP Hub generates ports by hashing the project path. You can override this with a custom function:
require("mcphub").setup({
workspace = {
get_port = function()
local project_name = vim.fn.fnamemodify(vim.fn.getcwd(), ":t")
-- Use fixed ports for specific projects
if project_name == "critical-project" then
return 45000
elseif project_name == "test-project" then
return 46000
end
-- Return nil to use default hash-based port generation
return nil
end
}
})
This is useful when you need consistent, predictable port numbers for specific projects.
UI Overview
The MCP Hub interface shows clear workspace organization:
Active Hubs
Config View
The Config view (C
) shows all configuration files with tabs for easy switching between global and project configs.
Workspace Actions
<l>
: Expand/collapse workspace details<d>
: Kill workspace process (with confirmation)<gc>
: Change directory to workspace<h>
: Collapse expanded workspace
Troubleshooting
Hub Not Switching
- Ensure
reload_on_dir_changed = true
- Check that config file exists in project root
- Use
:cd
instead of shell directory changes
Port Conflicts
- Check "Active Hubs" section for conflicts
- Kill stale processes with
<d>
- Restart with
R
Environment Variables Not Updated
- Environment is captured when hub starts
- Restart hub with
R
to get latest environment - Use
global_env
for dynamic values