Local MCP Server
A registered node can run an MCP server on 127.0.0.1 — its local server. An operator grant starts it, and nobgp mcp install points Claude Desktop or Claude Code at it — so the assistant on that machine gets the machine itself, and the rest of its noBGP network too.
This is the counterpart to the public server at https://mcp.nobgp.com/mcp. The public server speaks for you across every network you belong to; a node's local server speaks for that machine, from inside its network, with no round trip to the router for the things the node already knows.
This page is the guide — how to turn the endpoint on and get Claude talking to it. For the tool-by-tool catalogue — every tool marked with the surfaces that expose it — plus the concepts behind them (tiers, node references, selectors, execution identity) and the error codes, see the MCP Reference.
Why use it
- It works when the router doesn't.
statusandresolveare answered on the box, so they still work while the control channel is down — the first thing to check when the hosted tools start failing. - It reaches every node, not just the ones that answer. The other nodes in the network sit behind NAT, CGNAT and firewalls.
ssh,scpandcurlreach only the ones you have a route to, a key for and an open port on, and one that answers says nothing about the next. The proxied tools reach all of them, over each node's own connection to the router. - Getting files onto one of them is a thing to reach for, not to plan around. Code, configs, build artifacts, a whole repo:
fs_writeaddresses the node itself and needs no route from the machine you are working on, andfs_fetch_urlhas the node fetch a URL from its own network position without the bytes passing through the assistant. Since agent 0.4.74 the server's handshake says this in the same words the public server uses, so an assistant reaches for it instead of proposing a second machine on the same LAN, a copy by hand, or making a private repository public. - Events arrive by push. Subscribe once and the server tells the client when events land, instead of the client polling. See Waiting without polling.
Connect Claude to a node
The server is switch-driven: someone who controls the node — its owner, or an Owner or Admin of its organization — turns it on with node_grant, and the agent starts the local endpoint within seconds. Until then there is nothing on 127.0.0.1 to register — so turn it on first, install second:
Turn on studio-mac's local MCP server, scoped to the production network
There is no longer an observe / manage choice. node_grant takes no role value: the endpoint is on or off, and when it is on it acts for the node's owner with the owner's current rights, re-checked on every call. A node that held the old read-only observe tier is off until someone turns it on, and role: "observe" is refused by name.
Then run this on the node, from your own user account:
nobgp mcp install
The install needs root, because the endpoint and token it copies out live in the root-owned profile config. On Linux and macOS it re-executes itself under sudo, so sudo nobgp mcp install works too. On Windows there's no auto-elevation — run it from an Administrator terminal.
Root privileges required, re-executing with sudo...
Installing the noBGP MCP server for user "alice" (profile "default"):
Claude Desktop /Users/alice/Library/Application Support/Claude/claude_desktop_config.json
Claude Code /Users/alice/.claude.json
Continue? [Y/n] y
registered with Claude Desktop (/Users/alice/Library/Application Support/Claude/claude_desktop_config.json)
registered with Claude Code (/Users/alice/.claude.json)
MCP server ready at http://127.0.0.1:51843/mcp
This node holds an operator-granted role — 21 network tools available through the proxy.
Restart Claude to pick this up — it reads its server list only at launch.
Restart Claude and ask it something only that machine knows:
What's the noBGP status of this machine?
Claude's configuration is a per-user file, so getting the account wrong succeeds silently. Under sudo the command resolves your real account (SUDO_USER), writes that account's configuration and hands the file back to you afterwards. Run as root with no SUDO_USER at all — a root login shell, a provisioning script — and it configures root's Claude: it warns and defaults the prompt to no.
Before it registers anything, nobgp mcp install makes sure there is a server to register. It flips no switch — the grant is what starts the server — it just checks and waits:
- Starts the agent service if it isn't running.
- Names the reason an install can never succeed, rather than waiting out the timeout for it:
this node has no MCP role yet — a network operator must grant one first (node_grant, or the app), then retry(the node's local MCP is off; someone who controls the node turns it on)this node's owner turned the local MCP server off (local-mcp: off) — turn it on at the machine first: sudo nobgp config --local-mcp=on(agent 0.4.153+; no grant starts the server while that switch is set)
- Waits up to 30 seconds for an endpoint that actually answers.
A server that never comes up fails the install rather than registering a port nobody holds.
Once the registrations are written it opens one throwaway session against the live endpoint and counts the router tools the endpoint actually exposes. With the switch on that is 34 from router 0.4.195 (29 before it: file left and six tools arrived, three in its place and three from the split of command), plus the node's own two local tools. A zero count is reported as could not verify right now, not as a switch that is off: a serving server already proves it is on. ⚠ The example output above was captured when tiers still existed and shows 21 network tools; a current node reports 34.
Options:
| Option | Short | Description |
|---|---|---|
--yes | -y | Skip the confirmation prompt (for scripts) |
--profile | Which agent profile to bridge to (default: default) |
What gets written
nobgp mcp install adds one entry to each Claude configuration that already exists on the machine. It never creates a configuration for a Claude that isn't installed, and a file it cannot parse is left untouched and reported rather than replaced.
| Client | Configuration file |
|---|---|
| Claude Desktop (macOS) | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Claude Desktop (Windows) | %APPDATA%\Claude\claude_desktop_config.json |
| Claude Desktop (Linux) | ~/.config/Claude/claude_desktop_config.json |
| Claude Code | ~/.claude.json |
The registration takes one of two shapes, because the clients differ.
Claude Code speaks streamable HTTP itself, so it talks to the agent directly — no bridge process at all:
{
"mcpServers": {
"nobgp": {
"type": "http",
"url": "http://127.0.0.1:51843/mcp",
"headers": { "Authorization": "Bearer <token>" }
}
}
}
Claude Desktop launches STDIO servers only, so it gets nobgp mcp stdio as a bridge, with the same two values in the entry's environment:
{
"mcpServers": {
"nobgp": {
"command": "/usr/bin/nobgp",
"args": ["mcp", "stdio", "--profile", "default"],
"env": {
"NOBGP_MCP_URL": "http://127.0.0.1:51843/mcp",
"NOBGP_MCP_TOKEN": "<token>"
}
}
}
}
Either way the credential lands in the client's own configuration, owned by the account that runs Claude. That's the point of the sudo step: the token reaches an account because a sudoer ran one command for that account, not because a file was left readable — nothing user-land ever opens the agent's config directory.
What is per-account is the distribution, not the credential. mcp-token belongs to the profile, so every account the install was run for holds the same token and the endpoint cannot tell them apart. Two consequences worth planning for:
- There is no way to revoke one account's access on its own. The token changes only when the profile loses it, and that invalidates every registration on the machine — each account then has to re-run
nobgp mcp install.nobgp mcp uninstallremoves registrations, not the token; the switches that take the endpoint away from every local account at once arenode_revokeat the router andlocal-mcp: offat the machine. - On a machine with local logins you don't fully trust, install only for accounts that should have the node's MCP reach. What that reach covers is decided by the
node_grantat the router, not by anything on the box. - ⚠ From agent 0.4.153 the install says who can use the token before it asks. Every process running as the account you install for can read it and call this node's MCP server, and so can root. When that account is also the node's configured
user, that includes the unelevatedcommand, terminal and file calls anyone withuseof this node runs on it — so give someoneuseof this machine only when they may also use its MCP server. - Each Claude configuration is written
0600— readable by its owner and root, nobody else. Claude rewrites its own config at launch and a person canchmodit, so bothnobgp mcp installandnobgp mcp statuscheck the file as it actually is and name thechmod 600when its mode opens it to other accounts on the machine. (On Windows the mode bits say nothing, and a profile's%APPDATA%is private to its account, so there is nothing to check.)
The cost of freezing those values into a client config is that they have to stay true. The port only moves if something else takes it, and the token only changes if the profile loses it; either way nobgp mcp status reports the endpoint as not responding, and re-running the install repairs it.
There is a single nobgp entry per Claude configuration. Installing a second profile replaces the first — the command says so before it does it, rather than silently unhooking the profile you set up last week.
nobgp is also the name these docs give the public server at https://mcp.nobgp.com/mcp, so the two can want the same slot. From agent 0.4.126 an entry that is not a local node's is left strictly alone:
nobgp mcp installstops before it writes anything, naming the file and what the entry points at, and telling you to rename that entry —nobgp-public, say — and run the install again. It stops before the confirmation prompt, so--yescannot skip it.nobgp mcp uninstallleaves the entry in place and says so, rather than removing a registration this node never wrote.nobgp mcp statusreports it as "not registered" and names it, instead of probing it and calling the public server this node's endpoint. (Probing it with an empty token answers, so it used to reportresponding.)
An entry counts as this machine's when its url host is loopback — 127.0.0.1, localhost or ::1 — or its args begin mcp stdio. Anything else is yours. Only the scheme and host of a URL entry, or the command name of a command entry, are ever printed, never a path, query, arguments or environment: another server's registration can carry a credential in its path, arguments or environment.
Check it
nobgp mcp status
server: http://127.0.0.1:51843/mcp (responding, per the Claude Code registration)
Claude Desktop: registered (/Users/alice/Library/Application Support/Claude/claude_desktop_config.json)
Claude Code: registered (/Users/alice/.claude.json)
status needs no root: it reads the endpoint out of the registration — your own Claude configuration — and only falls back to the agent's root-owned state, which is why the line names its source. The server line is a live probe, not a file read, so an endpoint that has moved reports — NOT responding instead of a confident lie. With nothing registered yet and no readable agent state it reports unknown.
For the other half — whether the router has granted this node a role, and whether the machine's owner has left the switch on — use nobgp status, whose mcp block reports granted, local_mcp, running and port separately.
Remove it
nobgp mcp uninstall
This removes the nobgp entry from every Claude configuration on the machine, leaving everything else in those files alone. Restart Claude afterwards.
It needs no root, because it touches only your own account's Claude configuration. It deliberately leaves the agent side alone:
- The server keeps running — stopping it is the grant's job (
node_revoke). mcp-portandmcp-tokenare left in place. They are the running server's state, and other accounts on the machine may still be registered against them.
What the node exposes
The local server answers two tools itself. Both are pure reads of on-box state, and both advertise that (readOnlyHint, idempotentHint, closed world), so a client that auto-approves on hints doesn't stop to ask about them:
| Tool | What it does |
|---|---|
status | This node's agent status — identity, network and overlay address, transport, DNS, live sessions. Answered locally, so it works while the router is unreachable. |
resolve | Resolve a peer name to the overlay address this node would use, straight from the directory the router pushed. No round trip. |
Both hand back a node_id beside the address — each targets[] entry in status, and the result of resolve. That is the identifier every proxied tool takes as its target, so a name seen here can be acted on directly, with no round trip to network_directory. The address cannot: overlay addresses are minted per node, so the same peer has a different one on every machine and an address carried elsewhere points at the wrong host. Names and node ids travel; addresses do not.
Their full response fields are in the MCP Reference.
Everything else — commands, files, diagnostics, the event bus on the node's peers — is the router's surface, reached through the node. The tool catalogue lists every one of them with its parameters and the tier it needs.
One control turns all of it on, and it lives at the router: the node_grant someone who controls the node writes. It both starts the node's local MCP server and scopes what its proxied calls may do to peers. Nothing on the machine has to be set to make the endpoint appear — the one thing that can be set there is the machine owner's off switch, local-mcp: off from agent 0.4.153, which the grant cannot override.
A node that is off serves nothing at all on 127.0.0.1 — not even its own two tools. Turn it on and the server starts within seconds; turn it off and the server stops. It does not time out on its own. The port and token stay in the profile config either way, so turning it on again rebinds the same endpoint and already-registered Claude clients resume without a re-install.
From router 0.4.179 a node's local MCP acts for the node's owner with the owner's current rights: it reaches the peers the owner may use, runs as root only on a peer where the owner holds root, and stops working when the owner leaves the organization or loses a right. A change of the node's owner turns it off.
⚠ Whoever can run on the node can call this endpoint — it is a loopback port with a bearer token, and nobgp mcp install hands that token to the installing user on purpose. So a person with use of this machine can reach, through it, what the machine's owner may reach. Turn it on with that in mind, and use targets to narrow it.
To take the endpoint off a machine from the router, node_revoke it. That reaches the fleet with no on-box action, so a machine whose operator is not its owner is governed the same way as one you run yourself. The owner's own answer, at the machine, is local-mcp: off (agent 0.4.153), which no grant overrides. And what the node will actually serve once granted is still the owner's say: the capability keys (allow-tools, allow-roots, allow-root) refuse work on the box regardless of who asked. Since agent 0.4.33 they cover a published terminal service too, so turning the tools off closes that door as well — plan on-device access before narrowing a node you cannot reach physically.
With a grant in place, tools/list on the local endpoint returns the node's own tools plus the router tools a node context may reach, already filtered to what will actually be allowed. Local names win on a collision, and a proxy failure degrades to the local list rather than failing the session. A grant that changes mid-session pushes notifications/tools/list_changed, so a connected client refetches the list instead of holding a stale one.
Whether a call may ask for a peer's superuser identity is the node owner's root on that peer — its owner, an Owner/Admin of the peer's organization, or a root entry on the peer's access list — and the peer's own allow-root still caps it. It is not the organization role of whoever turned the switch on, and it is not a property of this node. See Execution identity.
A node that is on can operate on the machines in its own network that its owner may use. Two things ask for more than use: node_logs (from router 0.4.183) and a peer's storage area need the node's owner to control that peer, so a peer the owner merely uses answers forbidden; and root: true needs the owner's root on that peer. It cannot create, destroy, bill or delegate: no provisioning, no network or organization management, no service publishing, no access-list changes, and no labelling, renaming or transferring — labelling would otherwise let a node widen its own reach over its peers, and renaming would let it re-address one. Nodes in other networks need the public server. Call tools/list for the authoritative inventory, and see the MCP Reference for the whole allowlist.
Which identity a call gets, from here
Execution identity works the same on this endpoint as on the public one — one root field (its old name admin is no longer published to an MCP client from router 0.4.195). It is a boolean from router 0.4.201, with three states and no fourth: omit it, or send true or false when the work needs one identity. The string spellings "true" and "false" that earlier releases published are still decoded. Two things about reading it are specific to a node's local server, and since agent 0.4.42 the server states both in its MCP instructions, so an assistant has them before it picks a tool rather than after a refusal.
Whether root is available is a question about the TARGET, not about this node. Since router 0.4.179 there is no tier to read it off: the endpoint's whole tool list is present whenever it is on, and root: true succeeds on a peer where this node's owner holds root and fails with forbidden on one where they do not. An omitted root returns forbidden on the same peers when it lands on the ambient identity. Through router 0.4.178 the answer was the node's tier — command present meant manage and root was available, command absent meant observe and root was refused everywhere.
A peer's identity is visible from here for the nodes the grant reaches. From router 0.4.201 node_list with view: "full" reports each such node's user and allow_root on this endpoint too. For a peer it does not report, choose by what the work needs and let the refusal name the retry. failed_precondition: the call ran unelevated (false, or root omitted where the router resolved the peer as unelevated) on a peer whose unelevated identity would be root; retry with root: true. forbidden after root: true: retry with root omitted or false. forbidden after an omitted root: the peer reports no unelevated account and this node may not elevate. Retry with root: false only if the work need not run as root; a root-running peer refuses it with failed_precondition, and no retry from here succeeds there.
For this node, status answers it. Its allow block reports without_root — the account a root: false call actually runs as, resolved by the gate rather than read from the config key — or without_root_refusal when work without root cannot run here at all, which means every call on the box needs to run as root: send root: true. allow.root: false is the other half; with both refused, nothing runs remotely on this node. Since agent 0.4.46 the block also carries without_root_is_administrator when that account is itself administrative, which is what a peer's info.user_is_admin says in the directory. ⚠ Those three were unelevated, unelevated_refusal and unelevated_is_admin through agent 0.4.152, and allow.root was allow.admin; every old name is still reported. See The fourth line is not a veto.
Waiting without polling
This is the reason to prefer a node's local endpoint over the public one for monitoring. Each subscription is exposed as an MCP resource — nobgp://events/<subscription_id> — so a client subscribes once and is told when events land:
- Create a subscription with
fs_subscribe,presence_subscribe,fs_grep_subscribeorcommand_subscribe— all four are available whenever the endpoint is on, and each node of the cohort is still checked against what this node's owner may do there. - Call
resources/subscribeonnobgp://events/<subscription_id>. - The server sends
notifications/resources/updatedas events arrive;resources/readreturns them and advances the cursor.
No polling loop, and it keeps working between turns — which is what makes it the right way to wait for a long command to finish or a file to change. Reading still keeps the subscription alive, and event_tail still works as the fallback for a client that doesn't consume notifications.
A read also returns the dispatch's progress counts from agent 0.4.121, so outstanding: 0 tells you a command_subscribe or fs_grep_subscribe has finished — including the case where the last node went quiet without producing a final event, which the push path could not see before.
An agent below 0.4.121 delivers nothing over this path: resources/read returns events as null and the notification never fires, so waiting this way waits forever while the events sit readable at event_tail. Check with nobgp version on the node, upgrade with sudo nobgp upgrade, and poll event_tail in the meantime.
Event push goes through the router's queue, so it needs the proxy path to be usable. Where it isn't, resources/subscribe is refused with a reason rather than silently never firing.
See Monitoring & Events for the subscription workflow itself, and Events and subscriptions in the reference for the event shape, lifetime rules and the exact resources/read payload.
Configuration
| Config File Key | Description | Default |
|---|---|---|
local-mcp | The machine owner's off switch — on or off. off and the server never runs, whatever the router grants. Agent 0.4.153+ | on |
mcp-port | Loopback port to prefer. The agent binds this port when it's free, otherwise asks the OS for one and writes that back here, so a restart usually lands on the same port | (remembered) |
mcp-token | Bearer token for the server, minted by the agent on first start and written back here. One token per profile, shared by every account it is installed for. Durable on purpose — a token that changed on every restart would invalidate every registration nobgp mcp install wrote | (remembered) |
api-socket-group | Group (name or numeric gid) granted access to the agent's local API socket. Unix only | (unset) |
api-socket-mode | Octal mode for that socket, e.g. "0660". Only meaningful together with api-socket-group | 0600 |
mcp-port and mcp-token are written by the agent itself, into the root-owned 0600 profile config. You don't set them by hand — nobgp mcp install reads them as root and copies them into the client configuration for you.
The server is loopback-only and enforces that bearer token on every request. Those two config values are the endpoint — there is no discovery file to read or keep in step. (Older agents wrote one at <config-dir>/<profile>-mcp.json; a leftover is deleted at agent start.)
The machine's owner can refuse it
From agent 0.4.153 the grant is no longer the only control. local-mcp is the node owner's off switch, set at the machine:
# This machine never serves MCP on 127.0.0.1
sudo nobgp config --local-mcp=off
- The router's grant cannot override it.
offwith a live grant is granted, refused by the owner:nobgp statusreportsmcp.granted: truebesidemcp.local_mcp: off, so it is distinguishable from a listener that failed to bind. - It is set on the machine and only there, like the capability keys:
node_config_setrefuses it. A remote path to an off switch is not an off switch. onandoffare the values (trueandfalseare read too, for a hand edit).nobgp configrefuses anything else, and a value already in the file that the agent cannot parse reads asoff— a typo in an off switch must not leave the server on.- The switch leaves
mcp-portandmcp-tokenalone, so turning it back on rebinds the same endpoint and registered clients resume with no re-install.
The grant remains the control for a fleet, and it is the one an operator uses: the router filters every proxied call against it anyway, and a loopback listener is strictly less reach than the operator holding that grant already has. That is what lets a fleet be provisioned from the app with nothing to run on each box. local-mcp is the other half — the answer for a machine whose owner is not its operator.
Troubleshooting
"this node's MCP server is not running"
Either the agent isn't up, the node holds no node_grant — nothing starts the server without one — or the machine's owner set local-mcp: off. nobgp status separates the three: its mcp block reports granted, local_mcp and running. nobgp mcp install names a missing grant, and from agent 0.4.153 an owner's off, before it waits; nobgp service status covers the agent itself.
Claude shows the server but every call fails
Claude Desktop's bridge answers the handshake locally when the agent is unreachable, so a server that appears connected but can't do anything usually means the agent is down or restarting. It recovers on its own once the agent is back — no need to restart Claude.
nobgp mcp status says the endpoint is NOT responding
The registered port or token no longer matches what the agent is serving — the port was taken by something else while the agent was down, or the profile lost its token. Re-run nobgp mcp install to repair the registration, then restart Claude.
Another MCP client connects but lists no tools
A client that states its protocol version on every request — goose does — gets nothing back from an agent below 0.4.179: the session opens, tools/list fails, and the client shows an empty server. Check the node with nobgp version and upgrade it with sudo nobgp upgrade. A client that doesn't send that field was never affected.
Claude doesn't see the new server
Claude reads its server list only at launch — restart it. Verify the entry landed with nobgp mcp status.
Peer tools are missing from tools/list
The node's node_grant decides which router tools appear, and the list is filtered to what a call would actually be allowed to do — a narrower role means a shorter list. Revoking the grant stops the server outright. Grant changes take effect on the node's next call, and a connected client is told to refetch the list; there's no session to restart.
Next Steps
- MCP Reference - The full tool catalogue for both surfaces, with concepts and error codes
- Monitoring & Events - Subscribe to file changes, fleet commands, and node presence
- node_grant - Grant and revoke a node's reach over its peers
- CLI Reference - Full
nobgp mcpcommand reference - Public MCP Server - The other surface, for anything a node cannot reach