MCP Tools Reference
Technical reference for all Model Context Protocol (MCP) tools provided by the noBGP MCP server. This documentation is intended for developers building integrations or users wanting to understand the underlying capabilities.
MCP Endpoint
https://mcp.nobgp.com/mcp
Transport adapters
The same canonical tools registry is reachable over three adapters. Parameters and response shapes are identical — only framing, auth carrier, and streaming delivery differ.
MCP — /mcp
JSON-RPC 2.0 over Streamable HTTP. Tools advertised via tools/list; invoked via tools/call. Streaming tools deliver chunks as notifications/progress messages; one-shot tools return their full response in the tools/call result.
REST — POST /api/v1/tools/{name}
Request struct as JSON body. Success envelope:
{ "data": { "...typed response..." }, "message": "human-readable summary" }
Error envelope:
{ "error": { "code": "not_found", "message": "..." } }
Streaming tools (fs_grep) negotiate via Accept: text/event-stream and respond with SSE events: chunk, progress, done, error.
OpenAPI — GET /api/v1/openapi.json
OpenAPI 3.1 spec served live from any router replica. Use it as a build input for SDK generation. Every tool appears under /api/v1/tools/{name} with full request/response schemas.
Tool index — GET /api/v1/tools
Returns a brief index (name, description, mode, capabilities) for quick introspection.
Execution modes
one_shot— request in, response out. Used by every read tool and most write tools.server_stream— long-running tool that emits incremental progress events (SSE on REST, MCP progress on MCP). Currently onlyfs_grep.
A few tools (command, file read/write) are technically one_shot but expose a session-based workflow: the first call returns a session id, subsequent calls pass that id to continue. See Cross-replica session forwarding.
Authentication
All endpoints require a Bearer token.
- MCP (
https://mcp.nobgp.com/mcp): OAuth 2.0 Authorization Code Flow with PKCE. Supported providers: Google, GitHub, Facebook, Discord, LinkedIn, and more. Token refresh is automatic; session management is per-conversation. - REST / OpenAPI: supply the same Bearer token via the
Authorizationheader.
Scopes
Scopes gate access to tool families. Missing scope returns forbidden (HTTP 403).
| Scope | Tools |
|---|---|
| (none) | whoami |
network.read | network_directory |
network.write | network_create, network_delete |
node.register | register_node |
provisioning.write | provision_node, deprovision_node |
service.write | service_publish, service_update, service_delete, service_share |
shell.exec | command |
fs.read | file (read/list/stat), fs_read, fs_list, fs_stat, fs_glob, fs_grep |
fs.write | file (write/edit/delete/mkdir), fs_write, fs_edit, fs_delete, fs_mkdir |
net.read | net_peers, net_interfaces, net_metrics, net_routes, net_dns |
Available Tools
noBGP provides the following MCP tools organized by function. provision_node and deprovision_node only appear for callers with provisioning access.
Network Management:
- network_directory - Discovery and listing
- network_create - Create networks
- network_delete - Delete networks
Node Management:
- provision_node - Create compute resources
- deprovision_node - Remove compute resources
- register_node - Generate install commands for existing machines
Service Publishing:
- service_publish - Expose services publicly
- service_update - Modify service settings
- service_delete - Remove services
- service_share - Manage authorized email lists
Command Execution:
- command - Run commands and manage interactive shell sessions
Filesystem:
- file - Unified file super-tool (read, write, edit, list, stat, delete, mkdir)
- fs_read - Stream a file from the agent
- fs_write - Stream a file to the agent
- fs_edit - Atomic diff-style edits
- fs_list - Directory listing
- fs_stat - Single-entry metadata
- fs_delete - Remove a file or directory
- fs_mkdir - Create a directory
Network Diagnostics:
- net_peers - Agent's peer directory
- net_interfaces - Host network interfaces
- net_metrics - Go runtime metrics (uptime, goroutines, heap, GC)
- net_routes - Kernel routing table (Linux)
- net_dns - Resolver configuration (Unix)
User Management:
- whoami - User identity and metadata
fs_glob (doublestar pattern match) and fs_grep (RE2 regex search) are available via the versioned REST surface at POST /api/v1/tools/{fs_glob|fs_grep} but are not registered over MCP yet — the MCP adapter does not speak the progress-notification protocol used for server-stream tools.
network_directory
List networks, nodes, and published services accessible to the authenticated user.
Purpose
Primary discovery tool - shows the current state of all infrastructure.
Input Schema
{
"network_name": "string (optional)",
"online": "boolean (optional)",
"node_name_glob": "string (optional)",
"hostname_glob": "string (optional)",
"platform_glob": "string (optional)",
"brief": "boolean (optional)"
}
Parameters:
All filters compose with AND — supply any combination to narrow the result.
network_name- Specific network name to filter results. If omitted, returns all networks.online- When set, restrict to nodes with this online state.true= currently connected only;false= disconnected only. Omit for all nodes.node_name_glob- Shell-glob (path.Matchsyntax) matched against each node's name. Malformed patterns returninvalid_args.hostname_glob- Shell-glob matched against each node's reported hostname.platform_glob- Shell-glob matched against each node's platform string (e.g.raspbian,ubuntu,darwin,openwrt).brief- Whentrue, omit each node'sservicesarray. Shrinks responses sharply for "is X online?" probes.
Output Schema
{
"networks": [
{
"id": "string",
"name": "string",
"files_url": "string",
"nodes": [
{
"id": "string",
"name": "string",
"online": "boolean",
"info": {
"hostname": "string",
"agent_version": "string",
"version_status": "string (current | behind | ahead | empty)",
"channel_version": "string",
"platform": "string",
"platform_version": "string",
"kernel_arch": "string",
"virtualization": "string"
},
"task_id": "string (provisioning UUID, if applicable)",
"services": [
{
"id": "string",
"title": "string",
"url": "string",
"public_url": "string",
"command": "string"
}
]
}
]
}
]
}
The info.virtualization field is emitted as system/role (e.g. docker/guest, kvm/host) and is omitted on bare-metal nodes. The info block is absent for nodes that have never connected.
info.version_status compares agent_version against the node's effective release-channel target:
| Value | Meaning |
|---|---|
current | Agent matches the channel target — no action. |
behind | Agent is older than the channel target — surface as a nudge to run nobgp upgrade. |
ahead | Agent is newer than the channel target — happens when a node was on a faster channel that has since been slowed mid-rollout. No user action needed. |
"" (empty) | Comparison couldn't be made (no channel target, channel disabled, missing agent metadata, or unparseable version) — fall back to agent_version. |
info.channel_version is the effective channel's target version, echoed alongside so callers don't need a second lookup to see what behind means in concrete terms.
Example Usage
Request:
{
"network_name": "production"
}
Response:
{
"networks": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "production",
"files_url": "https://files.nobgp.com/production/",
"nodes": [
{
"id": "8a1f2d5c-7b3a-4e9f-9c1b-2e5a0d8f4c12",
"name": "web-server-1",
"online": true,
"info": {
"hostname": "web-server-1",
"agent_version": "0.3.45",
"version_status": "current",
"channel_version": "0.3.45",
"platform": "debian",
"platform_version": "12",
"kernel_arch": "x86_64"
},
"services": [
{
"id": "svc_xyz789",
"title": "staging-app",
"public_url": "https://abc123.nobgp.com"
}
]
}
]
}
]
}
Filtered probe ("is any Raspberry Pi online?"):
{
"online": true,
"platform_glob": "raspbian",
"brief": true
}
The response omits the services array on each node so the payload stays small.
Use Cases
- Initial discovery when starting a conversation
- Checking current infrastructure state
- Finding available nodes for operations
- Listing all published services
- Compact online/offline probes via
online+briefwithout pulling the full directory
network_create
Create a new isolated network.
Purpose
Provision a new network namespace for connecting nodes.
Input Schema
{
"name": "string (required)"
}
Parameters:
name- Unique name for the network (DNS-compatible, unique per user)
Output Schema
{
"network_id": "string",
"name": "string",
"index": "number",
"created_at": "timestamp"
}
Example Usage
Request:
{
"name": "staging"
}
Response:
{
"network_id": "net_def456",
"name": "staging",
"index": 2,
"created_at": "2025-11-04T14:35:00Z"
}
Use Cases
- Creating isolated environments (dev, staging, prod)
- Separating different projects
- Multi-tenancy within a single account
network_delete
Delete an existing network.
Purpose
Remove an empty network.
Destructive operation. All nodes must be removed before the network can be deleted.
Input Schema
{
"network_name": "string (optional)"
}
Parameters:
network_name- Name of the network to delete. If omitted and only one network exists, it will be selected automatically.
A legacy force field is accepted for backwards compatibility but deprecated and ignored — networks with remaining nodes are always refused. Clean up via deprovision_node (or agent-side register_node removal) first.
Output Schema
{
"network_id": "string",
"name": "string",
"nodes_removed": "number",
"message": "string"
}
Example Usage
Request:
{
"network_name": "old-testing"
}
Response:
{
"network_id": "net_abc123",
"name": "old-testing",
"nodes_removed": 0,
"message": "Deleted network \"old-testing\" (id: net_abc123)"
}
Safety Features
- AI assistants typically ask user for confirmation first
- Deletion is rejected if any nodes remain — remove or deprovision all nodes first
- Cannot delete network with active provisioned nodes (must deprovision first)
provision_node
Provision a new compute instance on a cloud provider.
Requires provisioning permissions. Contact support if you don't have access.
Purpose
Create on-demand compute resources with automatic noBGP agent installation.
Input Schema
{
"node_name": "string (required)",
"network_name": "string (optional)",
"provider": "string (optional)",
"cpu": "number (optional)",
"memory": "number (optional)"
}
Parameters:
node_name- Name for the provisioned nodenetwork_name- Target network. If omitted, the default network is used.provider- Cloud provider:"aws"(default) or"cachengo"cpu- CPU units (1024 = 1 vCPU). Default: 256 (0.25 vCPU). Range: 256–4096. AWS only — Cachengo is a fixed 4 vCPU slot and ignores this parameter.memory- Memory in MiB. Default: 512. Range: 512–16384. AWS only — Cachengo is a fixed 4 GiB slot and ignores this parameter.
Output Schema
{
"task_id": "string",
"provider": "string",
"resource_summary": "string",
"notes": ["string"]
}
Example Usage
Request:
{
"node_name": "api-server-2",
"network_name": "production",
"cpu": 2048,
"memory": 4096
}
Response:
{
"task_id": "task_provisioning_123",
"provider": "aws",
"resource_summary": "2 vCPU, 4 GiB RAM",
"notes": ["Node will appear in network_directory once online."]
}
Provisioning Flow
- Request received and validated
- Container instance created on cloud provider
- noBGP agent installed and configured automatically
- Agent registers with your network
- Node appears as "online" in
network_directory
Typical duration: 1-2 minutes
deprovision_node
Terminate and remove a provisioned compute instance.
Destructive operation. All data on the node will be permanently lost.
Purpose
Remove provisioned infrastructure and stop billing.
Input Schema
{
"task_id": "string (required)"
}
Parameters:
task_id- Identifier of the provisioning task to deprovision (the UUID returned byprovision_node)
Output Schema
{
"task_id": "string",
"reference": "string",
"provider": "string",
"notes": ["string"]
}
Example Usage
Request:
{
"task_id": "task_provisioning_123"
}
Response:
{
"task_id": "task_provisioning_123",
"reference": "arn:aws:ecs:...",
"provider": "aws",
"notes": ["Task ran for 2h 15m before deprovisioning."]
}
Safety Features
- AI assistants typically ask user for confirmation first
- Creates audit log entry
service_publish
Publish a proxy or terminal service with a public HTTPS URL.
Purpose
Expose HTTP applications or terminal access through secure public endpoints.
Service Mode
The service mode is determined by which parameter is provided:
- Proxy mode: Provide
proxy_target_url(plain URL by default, e.g.http://127.0.0.1:8080). - Terminal mode: Provide
terminal_command(command to run in the terminal) - Shell mode: Omit both to create a terminal running the default user shell
Input Schema
{
"node_id": "string (optional)",
"network_name": "string (required unless node_id provided)",
"node_name": "string (required unless node_id provided)",
"title": "string (optional)",
"proxy_target_url": "string (optional, plain URL by default)",
"proxy_target_encoding": "utf8|base64 (optional, default: utf8)",
"terminal_command": "string (optional)",
"username": "string (optional)",
"workdir": "string (optional)",
"enabled": "boolean (optional, default: true)",
"auth_required": "boolean (optional, default: true)",
"authorized_emails": ["string (optional)"]
}
Parameters:
node_id- Node UUID. Alternative tonetwork_name+node_name.network_name- Network where the agent residesnode_name- Agent name where the service will be publishedtitle- Human-readable service nameproxy_target_url- Host/URI/port to proxy. Plain URL by default (e.g.http://127.0.0.1:8080). Setproxy_target_encoding: "base64"to pass a base64-encoded value (legacy form). Mutually exclusive withterminal_command.proxy_target_encoding- Encoding ofproxy_target_url:"utf8"(default — value is a plain URL) or"base64"(legacy — value is decoded before use). Omit unless you have a specific reason to encode.terminal_command- Command to expose via a web terminal. Mutually exclusive withproxy_target_url.username- OS user for command execution (terminal/shell services only)workdir- Working directory for command execution (terminal/shell services only)enabled- Whether the service starts enabled (default: true)auth_required- Whether OAuth is required to access (default: true). Only set to false if the user explicitly asks for unauthenticated access.authorized_emails- Email addresses or patterns to authorize for access (e.g.,user@example.com,*@company.com). Impliesauth_required: true.
Output Schema
{
"service_id": "string",
"public_url": "string",
"mode": "proxy|terminal"
}
Example Usage
Proxy Service:
Request:
{
"network_name": "production",
"node_name": "web-server-1",
"proxy_target_url": "http://localhost:8080",
"title": "staging-app",
"auth_required": true
}
Response:
{
"service_id": "svc_abc123",
"public_url": "https://a1b2c3d4.nobgp.com",
"mode": "proxy"
}
Terminal Service:
Request:
{
"network_name": "production",
"node_name": "raspberry-pi",
"terminal_command": "bash",
"username": "pi",
"auth_required": true
}
Response:
{
"service_id": "svc_def456",
"public_url": "https://x9y8z7w6.nobgp.com",
"mode": "terminal"
}
Default Shell Service:
Request:
{
"network_name": "production",
"node_name": "my-server",
"auth_required": true
}
Response:
{
"service_id": "svc_ghi789",
"public_url": "https://p1q2r3s4.nobgp.com",
"mode": "terminal"
}
service_update
Modify an existing service's configuration.
Purpose
Change service settings without deleting and recreating.
Input Schema
{
"service_id": "string (required)",
"auth_required": "boolean (optional)",
"enabled": "boolean (optional)",
"title": "string (optional)",
"proxy_target_url": "string (optional, plain URL by default)",
"proxy_target_encoding": "utf8|base64 (optional, default: utf8)",
"terminal_command": "string (optional)",
"username": "string (optional)",
"workdir": "string (optional)"
}
Parameters:
service_id- ID of the service to updateauth_required- Change authentication requirementenabled- Enable or disable servicetitle- Update service display nameproxy_target_url- Update proxy target. Plain URL by default (e.g.http://127.0.0.1:8080). Setproxy_target_encoding: "base64"to pass a legacy base64-encoded value. Switches service to proxy mode.proxy_target_encoding- Encoding ofproxy_target_url:"utf8"(default) or"base64". Ignored whenproxy_target_urlis omitted.terminal_command- Update terminal command. Switches service to terminal mode.username- Update OS user for command execution (terminal/shell services only)workdir- Update working directory (terminal/shell services only)
Output Schema
{
"service_id": "string",
"public_url": "string",
"mode": "proxy|terminal",
"title": "string",
"enabled": "boolean",
"auth_required": "boolean"
}
Example Usage
Request:
{
"service_id": "svc_abc123",
"auth_required": false,
"enabled": false
}
Response:
{
"service_id": "svc_abc123",
"public_url": "https://a1b2c3d4.nobgp.com",
"mode": "proxy",
"title": "staging-app",
"enabled": false,
"auth_required": false
}
service_delete
Delete a published service.
Purpose
Remove a service and free up its public URL.
Input Schema
{
"service_id": "string (required)"
}
Parameters:
service_id- ID of the service to delete
Output Schema
{
"service_id": "string",
"mode": "string",
"public_url": "string"
}
Example Usage
Request:
{
"service_id": "svc_abc123"
}
Response:
{
"service_id": "svc_abc123",
"mode": "proxy",
"public_url": "https://a1b2c3d4.nobgp.com"
}
service_share
Manage the authorized email list for a published service.
Purpose
Add, remove, list, or revoke email addresses that are authorized to access a service. Use this instead of recreating the service when you need to change who has access.
Input Schema
{
"service_id": "string (required)",
"action": "string (required)",
"emails": ["string (conditional)"]
}
Parameters:
service_id- Unique identifier for the serviceaction- Action to perform:list,add,remove, orrevokeemails- Email addresses (required foraddandremoveactions)
Output Schema
{
"service_id": "string",
"authorized_emails": ["string"]
}
Example Usage
List authorized emails:
Request:
{
"service_id": "svc_abc123",
"action": "list"
}
Response:
{
"service_id": "svc_abc123",
"authorized_emails": ["alice@example.com", "*@company.com"]
}
Add emails:
Request:
{
"service_id": "svc_abc123",
"action": "add",
"emails": ["bob@example.com", "*@partner.com"]
}
Revoke all access:
Request:
{
"service_id": "svc_abc123",
"action": "revoke"
}
Response:
{
"service_id": "svc_abc123",
"authorized_emails": []
}
Actions
| Action | Description | Requires emails |
|---|---|---|
list | Show current authorized emails | No |
add | Grant access to specified emails | Yes |
remove | Revoke access for specified emails | Yes |
revoke | Clear all authorized emails | No |
Cross-replica session forwarding
Sessions (command, file read/write) live on whichever router replica handled the first call. When a load-balanced subsequent call lands on a peer replica, the dispatcher transparently forwards the call via the mesh:
The forwarded call carries the caller's user_id so the owning replica can verify the session belongs to the caller — sessions cannot be hijacked by another authenticated user even if they guess the id.
A genuinely-expired session returns not_found. A forwarding failure returns target_unreachable (retryable).
command
Execute commands and manage interactive shell sessions on remote nodes.
Purpose
Run one-shot commands or manage stateful interactive sessions. Use the session parameter to start a new command, then use the returned command_id for subsequent interactions (sending input, reading more output, or sending signals).
Input Schema
{
"command_id": "string (optional)",
"session": {
"network_name": "string (required)",
"node_name": "string (required)",
"command": "string (required)",
"shell": "string (optional)",
"username": "string (optional)",
"workdir": "string (optional)",
"env": "object (optional)"
},
"input": "string (optional)",
"raw": "boolean (optional)",
"signal": "string (optional)",
"idle_timeout": "number (optional)"
}
Parameters:
command_id- Session identifier from a previous call. Omit to create a new session.session- Session creation parameters. Required whencommand_idis omitted.network_name- Network where the node residesnode_name- Agent name to connect tocommand- Command to execute (e.g.,"bash","df -h","python3 script.py")shell- Optional interpreter wrapper applied tocommandbefore dispatch. Omit (or"auto") for verbatim pass-through to the agent's native shell (sh -con Linux/macOS,cmd.exe /con Windows). Set to"powershell"on Windows to wrap aspowershell.exe -NoProfile -EncodedCommand <UTF16LE-base64>so cmd.exe quoting can't mangle the script — required for non-trivial PowerShell (pipelines, embedded quotes,$_,$ / & / | / %inside quotes). Unknown values returninvalid_args.username- OS user to run as (defaults to agent's configured user)workdir- Working directory (defaults to user's home directory)env- Additional environment variables (e.g.,{"DEBUG": "1"})
input- Text to send to stdin. C-style escape sequences (\n,\t,\xHH) are resolved by default. Setraw: trueto disable.raw- Send input bytes as-is without resolving escape sequences.signal- Signal to send to the process:SIGINT,SIGTERM,SIGHUP,SIGQUIT, orSIGKILL.idle_timeout- Seconds to wait with no output before returning (default: 5). Use0for non-blocking. Use higher values (e.g.,30or60) for slow commands. The command keeps running regardless.
Output Schema
{
"command_id": "string",
"output": "string (omitted if empty)",
"exit_code": "number (absent if still running)",
"state": "running | exited",
"duration_ms": "number"
}
Fields:
command_id- Use this in subsequent calls to send more input or read more outputoutput- Captured stdout/stderr since the last call. Omitted if no output was produced.exit_code- Present only when the command has exited (0 = success, non-zero = failure, -1 = killed by signal on Linux/macOS; on Windows a killed process returns 1). Absence means the process is still running.state- Always present."running"while the command is still executing (poll again withcommand_id);"exited"once the agent reports exit. Prefer branching onstaterather than inferring fromexit_code's nil-ness.duration_ms- Wall-clock duration of this tool call in milliseconds.
Session Lifecycle
Each command call runs one specific command. The session closes automatically when the command exits. A 30-second grace period allows reading the final exit code. For one-shot commands (ls, df -h, etc.), the session closes as soon as the command finishes.
Usage Patterns
One-Shot Commands
Start a command and read output in a single call:
Request:
{
"session": {
"network_name": "production",
"node_name": "web-server-1",
"command": "df -h"
}
}
Response:
{
"command_id": "cmd_abc123",
"output": "Filesystem Size Used Avail Use% Mounted on\n/dev/sda1 50G 12G 36G 25% /\n",
"exit_code": 0,
"state": "exited",
"duration_ms": 312
}
Interactive Shell Sessions
Start bash as the command to get an interactive shell, then send commands via input:
Start shell:
{
"session": {
"network_name": "production",
"node_name": "web-server-1",
"command": "bash"
}
}
Response:
{
"command_id": "cmd_xyz789",
"output": "user@web-server-1:~$ ",
"state": "running",
"duration_ms": 145
}
Send a command:
{
"command_id": "cmd_xyz789",
"input": "cd /var/log && ls -lt | head -5\n"
}
Response:
{
"command_id": "cmd_xyz789",
"output": "total 1024\n-rw-r--r-- 1 root root 45234 Nov 4 14:32 syslog\n...\nuser@web-server-1:/var/log$ ",
"state": "running",
"duration_ms": 89
}
Close the shell:
{
"command_id": "cmd_xyz789",
"input": "exit\n"
}
Polling Long-Running Commands
For commands that take a while, poll with command_id until exit_code appears:
Start:
{
"session": {
"network_name": "production",
"node_name": "build-server",
"command": "make build"
},
"idle_timeout": 30
}
Poll:
{
"command_id": "cmd_build123",
"idle_timeout": 30
}
Sending Signals
Interrupt a running process:
{
"command_id": "cmd_xyz789",
"signal": "SIGINT"
}
Shell semantics by platform
Linux / macOS: the command string runs under sh -c <command>. POSIX-style.
Windows: the command runs under cmd.exe /c <command> — not PowerShell. Use dir instead of ls, type instead of cat, set instead of printenv. Append .exe when calling executables by name (nobgp.exe, not nobgp). Backslashes in paths are fine (dir C:\Windows); quote paths with spaces.
For non-trivial PowerShell on Windows (pipelines, ForEach-Object blocks, $_ references, embedded quotes, anything with $ / & / | / % inside quotes), set session.shell: "powershell" instead of putting powershell -Command "…" directly in command. The router lowers the script to powershell.exe -NoProfile -EncodedCommand <UTF16LE-base64> so cmd.exe sees only base64 characters and has nothing to mangle. Pass the script as-is — do not pre-escape quotes. Trivial one-liners (Get-Date, Get-Process) work fine without shell.
Signals by platform
| Signal | Linux / macOS | Windows |
|---|---|---|
SIGINT | sent to process group | written as Ctrl+C (0x03) to stdin — only interrupts processes that read stdin |
SIGTERM | sent to process group | mapped to graceful close where possible |
SIGKILL | sent to process group | terminates reliably |
SIGHUP | sent to process group | not supported — returns invalid_args |
SIGQUIT | sent to process group | not supported — returns invalid_args |
Cross-replica behaviour
Subsequent calls with a command_id may land on a peer replica. The dispatcher detects this and forwards via the mesh — see Cross-replica session forwarding. The response shape is identical regardless of which replica handled it.
file
Unified super-tool for reading, writing, editing, and managing files on a remote agent's local filesystem.
Purpose
A single tool that dispatches to ten filesystem operations via the op field. Use this when you want one multiplexed handle; otherwise prefer the narrower per-op fs_* tools — their argument schemas evolve independently.
Operations
op | Purpose |
|---|---|
read | Stream a file from the agent (supports encoding=utf8 or base64, and offset resume) |
write | Stream a file to the agent (atomic via tmp+rename on done=true) |
edit | Single old_string → new_string replacement (atomic) |
multi_edit | Ordered batch of replacements (atomic, all-or-nothing) |
list | Directory listing (recursive=true walks subdirs) |
stat | Single-entry metadata (size, mode, mtime, is_dir) |
delete | Remove a file or directory (recursive=true for dirs) |
mkdir | Create a directory (recursive=true for mkdir -p) |
batch | Run N one-shot ops against the same target in one round-trip (best-effort; not transactional) |
fetch_url | Agent does an HTTP(S) GET and atomic-writes the bytes to path — router never sees the bytes |
Input Schema
{
"file_id": "string (optional, continues a read/write session)",
"target": {
"node_id": "string (optional)",
"network_name": "string",
"node_name": "string"
},
"op": "read|write|edit|multi_edit|list|stat|delete|mkdir|batch|fetch_url",
"path": "string (absolute path on the agent)",
"encoding": "base64|utf8 (read/write only; base64 default)",
"offset": "number (read only; resume offset)",
"max_bytes": "number (read only; default 262144, max 1048576)",
"chunk_b64": "string (write, encoding=base64)",
"chunk": "string (write, encoding=utf8)",
"done": "boolean (write only; finalize + fsync + rename)",
"mode": "string (write/mkdir; octal e.g. \"0644\")",
"recursive": "boolean (list/delete/mkdir)",
"old_string": "string (edit)",
"new_string": "string (edit)",
"replace_all": "boolean (edit)",
"edits": [{"old_string": "string", "new_string": "string", "replace_all": "boolean", "expected_sha256": "string"}],
"expected_sha256": "string (edit/multi_edit/fetch_url; sha-256 guard)",
"ops": "array (batch only; ordered list of sub-ops)",
"url": "string (fetch_url only; http or https)",
"headers": "object (fetch_url only; HTTP headers — Host / Content-Length filtered)",
"fetch_max_bytes": "number (fetch_url only; default 256 MiB, hard cap 1 GiB)"
}
Path format. path must be absolute. Both POSIX form (/etc/nginx.conf) and Windows drive form (C:\Users\Public\config.json or C:/Users/Public/config.json) are accepted; the router doesn't see the agent's OS at validation time so either is fine on either side.
Key behaviors:
- Provide
targeton the first call (vianode_idORnetwork_name+node_name). Continuations pass onlyfile_id. - Read/write are streaming: first call returns
file_id; poll with the samefile_iduntildone=true. - Write lands in a sibling
.nobgp-tmpfile and is atomically renamed on finalize — aborted writes leave the destination untouched. - Router-computed SHA-256 is cumulative and returned on every response.
- Use
edit/multi_edit(not read+write) when changing a few lines of a text file — it sends ~200 bytes instead of the whole file. expected_sha256guards edits against lost updates. A mismatch fails with HTTP 412 (precondition_mismatch) and returns the observed hash indetails.observed_sha256.op=batchruns N one-shot sub-ops against the same target in one round-trip. Each entry inops[]carries its ownop+ per-op fields (no nestedtarget, nofile_id). Sub-ops run serially and are best-effort — a failing sub-op doesn't abort siblings; per-op outcome lands inbatch_results[i]. Allowed sub-ops:mkdir,stat,list,delete,edit,multi_edit,write(implicitdone=true),fetch_url.readand nestedbatchare rejected.op=fetch_urlmakes the agent fetch an HTTP(S) URL directly from its own network position and atomic-write the bytes topath— the router never sees the body. Useful for internal LAN mirrors / authenticated proxies the router can't reach. Optionalexpected_sha256verifies the fetched bytes pre-rename; a mismatch leaves the destination untouched and surfaces asprecondition_mismatchwithdetails.observed_sha256.
Use Cases
- Binary-safe file transfer (uploads/downloads) with resume and integrity verification
- Surgical config edits (
edit/multi_edit) — no read-modify-write round-trip - Directory walks and metadata queries (
list/stat)
file (not command) for byte movementThe command tool description explicitly points here for file transfers. Don't pipe cat | base64 through a shell — file is atomic, checksummed, and binary-safe.
fs_read
Stream a file from a remote agent to the client.
Input Schema
{
"file_id": "string (optional)",
"target": {"node_id": "string", "network_name": "string", "node_name": "string"},
"path": "string (absolute)",
"offset": "number (optional, resume)",
"encoding": "base64|utf8 (default: base64)",
"max_bytes": "number (default: 262144, max: 1048576)"
}
Behavior:
- First call returns a
file_id. Pass it back on subsequent calls untildone=true. encoding=utf8returnschunkas a plain string; defaultbase64returnschunk_b64.- UTF-8 mode does not support offset-based resume (partial-codepoint state is per-session).
- Small files fit in a single response with
done=true.
Output Schema
{
"file_id": "string",
"path": "string",
"chunk_b64": "string (base64 mode)",
"chunk": "string (utf8 mode)",
"offset": "number",
"size": "number",
"total": "number",
"sha256": "string (cumulative, router-computed)",
"done": "boolean",
"duration_ms": "number"
}
fs_write
Stream a file from the client to a remote agent.
Input Schema
{
"file_id": "string (optional)",
"target": {"node_id": "string", "network_name": "string", "node_name": "string"},
"path": "string (absolute)",
"mode": "string (octal, default: \"0644\")",
"encoding": "base64|utf8 (default: base64)",
"chunk_b64": "string (base64 mode)",
"chunk": "string (utf8 mode)",
"done": "boolean (finalize)"
}
Behavior:
- First call opens a session and returns a
file_id. Stream chunks and call withdone=trueto finalize. - Finalize triggers
fsync+chmod+ atomic rename on the agent. Aborted writes leave the destination untouched. chunkandchunk_b64are mutually exclusive — pick one encoding per session.
Output Schema
{
"file_id": "string",
"path": "string",
"offset": "number",
"total": "number",
"sha256": "string",
"done": "boolean",
"duration_ms": "number"
}
fs_edit
Apply atomic old_string → new_string replacements to a file on a remote agent.
Input Schema
{
"target": {"node_id": "string", "network_name": "string", "node_name": "string"},
"path": "string (absolute)",
"old_string": "string (flat form)",
"new_string": "string (flat form)",
"replace_all": "boolean (flat form)",
"edits": [
{"old_string": "string", "new_string": "string", "replace_all": "boolean", "expected_sha256": "string"}
],
"expected_sha256": "string (pre-edit guard)"
}
Behavior:
- Provide either
{old_string, new_string, replace_all}(single edit) ORedits[](ordered batch). Mixing them is an error. - When
edits[]has more than one element, the tool dispatches tomulti_editsemantics: later edits see earlier edits' results. - All-or-nothing: if any edit fails (
old_stringnot found, or non-unique withoutreplace_all), no edits land. expected_sha256fails with HTTP 412CodePreconditionMismatchwhen the agent-observed file hash differs; the observed hash is returned inold_sha256so callers can rebase and retry.
Output Schema
{
"path": "string",
"edits_applied": "number",
"total": "number (file size after)",
"sha256": "string (after)",
"old_size": "number (before)",
"old_sha256": "string (before)",
"duration_ms": "number"
}
fs_list
List a directory on a remote agent.
Input Schema
{
"target": {"node_id": "string", "network_name": "string", "node_name": "string"},
"path": "string (absolute directory)",
"recursive": "boolean (walk subtrees)"
}
Unreadable subentries surface as entries with the error field set rather than failing the whole call.
Output Schema
{
"path": "string",
"entries": [
{
"name": "string",
"path": "string (absolute, recursive only)",
"size": "number",
"mode": "string (octal)",
"mtime": "number (unix timestamp)",
"is_dir": "boolean",
"error": "string (walk error, if any)"
}
],
"count": "number",
"duration_ms": "number"
}
fs_stat
Return metadata for a single path on a remote agent.
Input Schema
{
"target": {"node_id": "string", "network_name": "string", "node_name": "string"},
"path": "string (absolute)"
}
Symlinks are not followed — the link's own metadata is returned.
Output Schema
{
"path": "string",
"entry": {
"name": "string",
"size": "number",
"mode": "string (octal)",
"mtime": "number (unix timestamp)",
"is_dir": "boolean"
},
"duration_ms": "number"
}
fs_delete
Remove a file or directory on a remote agent.
Input Schema
{
"target": {"node_id": "string", "network_name": "string", "node_name": "string"},
"path": "string (absolute)",
"recursive": "boolean (required for non-empty directories)"
}
Deleting an already-missing path returns CodeNotFound. Callers wanting ensure-absent semantics should treat that as success.
recursive=true on a path with fewer than 3 segments is refused unless force=true. This catches catastrophic deletes like /, /etc, /home/user. Segments are counted using / and \, so the guard works for Windows agents — C:\Windows\System32 (depth 3) passes; C:\ (depth 1) is refused. Single-file deletes are unaffected.
Output Schema
{
"path": "string",
"duration_ms": "number"
}
fs_mkdir
Create a directory on a remote agent.
Input Schema
{
"target": {"node_id": "string", "network_name": "string", "node_name": "string"},
"path": "string (absolute)",
"mode": "string (octal, default: \"0755\")",
"recursive": "boolean (mkdir -p semantics)"
}
Without recursive, an existing path returns CodeAlreadyExists. With recursive, the call is idempotent.
Output Schema
{
"path": "string",
"duration_ms": "number"
}
net_peers
Read the directory of peers the agent currently knows about.
Input Schema
{
"target": {"node_id": "string", "network_name": "string", "node_name": "string"}
}
All net_* tools share the same input shape — just a target pointing at the node to read from.
Output Schema
{
"peers": [
{
"name": "string",
"internal_ip": "string",
"dst_ip": "string (local NAT alias)",
"remote_id": "string (node UUID)",
"local": "boolean"
}
],
"count": "number",
"duration_ms": "number"
}
net_interfaces
Read the agent host's network interfaces (equivalent to ip -j link / ifconfig).
Output Schema
{
"interfaces": [
{
"name": "string",
"index": "number",
"mtu": "number",
"hardware_addr": "string",
"flags": ["string"],
"addrs": ["string (CIDR)"]
}
],
"count": "number",
"duration_ms": "number"
}
net_metrics
Read the agent's Go runtime metrics. Triggers a brief stop-the-world pause on the agent — fine for occasional diagnostics, not a replacement for a metrics pipeline.
Output Schema
{
"uptime_secs": "number",
"goroutines": "number",
"heap_alloc_bytes": "number",
"heap_sys_bytes": "number",
"num_gc": "number",
"gc_pause_total_ms": "number",
"num_cpu": "number",
"gomaxprocs": "number",
"version": "string (Go version)",
"os": "string (GOOS)",
"arch": "string (GOARCH)",
"duration_ms": "number"
}
net_routes
Read the agent host's kernel routing table (IPv4 + IPv6). Currently Linux-only; macOS/Windows return HTTP 501 unsupported.
Output Schema
{
"routes": [
{
"family": "ip4|ip6",
"destination": "string (CIDR or \"default\")",
"gateway": "string",
"source": "string",
"interface": "string",
"metric": "number",
"scope": "string",
"protocol": "string"
}
],
"count": "number",
"duration_ms": "number"
}
net_dns
Read the agent host's DNS resolver configuration from /etc/resolv.conf. Unix-only; Windows returns HTTP 501 unsupported.
When a loopback nameserver is detected (systemd-resolved, dnsmasq, nscd), stub=true signals that the real upstream servers are hidden behind a local forwarder.
Output Schema
{
"nameservers": ["string"],
"search": ["string"],
"options": ["string"],
"source": "string (e.g. /etc/resolv.conf)",
"stub": "boolean",
"note": "string (hint when snapshot is incomplete)",
"duration_ms": "number"
}
register_node
Generate install commands to connect an existing machine to a noBGP network.
Purpose
Return ready-to-run shell commands for installing and configuring the noBGP agent on Linux, macOS, or Windows machines.
Input Schema
{
"network_name": "string (optional)",
"node_name": "string (optional)"
}
Parameters:
network_name- Network to register the node into. If omitted, uses the default network.node_name- Name for the node. If omitted, the machine's hostname is used.
Output Schema
{
"shell_install_command": "string",
"power_shell_install_command": "string",
"cmd_install_command": "string"
}
Fields:
shell_install_command— Shell command for Linux/macOS. Run this withsudoon the target machine to install and register the agent.power_shell_install_command— PowerShell command for Windows. Run this in an elevated PowerShell session.cmd_install_command— Command Prompt command for Windows CMD. Usescurl.exe(built-in since Windows 10 1803+). Run in an elevated Command Prompt.
Example Usage
Request:
{
"network_name": "production",
"node_name": "web-server-1"
}
Response:
{
"shell_install_command": "curl -fsSL https://downloads.nobgp.com/agent/install.sh | sudo NOBGP_KEY=VSH8vKlciUXLyFcnKptFBoQmhYQvq4PqC3/l0FrG/qQ= NOBGP_NAME=web-server-1 sh",
"power_shell_install_command": "$env:NOBGP_KEY=\"VSH8vKlciUXLyFcnKptFBoQmhYQvq4PqC3/l0FrG/qQ=\"; $env:NOBGP_NAME=\"web-server-1\"; irm https://downloads.nobgp.com/agent/install.ps1 | iex",
"cmd_install_command": "set \"NOBGP_KEY=VSH8vKlciUXLyFcnKptFBoQmhYQvq4PqC3/l0FrG/qQ=\" && set \"NOBGP_NAME=web-server-1\" && curl -fsSL https://downloads.nobgp.com/agent/install.cmd -o install.cmd && install.cmd"
}
Use Cases
- Connect an existing server, VM, or Raspberry Pi to noBGP
- Onboard Windows machines via PowerShell (use
power_shell_install_command) or Command Prompt (usecmd_install_command) - Automate agent installation in provisioning scripts
whoami
Get information about the currently authenticated user.
Purpose
Identity verification and session metadata.
Input Schema
{}
No input parameters required.
Output Schema
{
"user_id": "string",
"email": "string"
}
Example Usage
Request:
{}
Response:
{
"user_id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com"
}
Error Handling
All tools return errors in a consistent format:
{
"error": {
"code": "string",
"message": "string",
"details": {}
}
}
Common Error Codes
Tool errors use stable lowercase codes. Each code maps to a fixed HTTP status on the REST surface.
| Code | Description | Resolution |
|---|---|---|
unauthorized | Not authenticated | Sign in via OAuth |
forbidden | Authenticated but not allowed | Check account permissions |
permission_denied | Authenticated but blocked by policy | Check account permissions |
not_found | Resource doesn't exist | Verify resource name/ID |
invalid_args | Bad request parameters | Check input schema |
target_not_found | Node target can't be resolved | Verify network/node names |
target_unreachable | Node is offline or unreachable | Check agent connection |
already_exists | Resource already present | Use update or pick a new name |
too_large | Payload exceeds limits | Split the request |
timeout | Operation timed out | Retry with a longer timeout |
canceled | Operation was canceled | Retry if desired |
rate_limited | Rate limit exceeded | Honor Retry-After; back off |
unsupported | Operation not supported on this platform | Check platform requirements |
method_not_allowed | Wrong HTTP verb / tool mode | Check tool documentation |
failed_precondition | Guard failed (e.g. expected_sha256 mismatch) | Rebase on current state and retry |
internal | Server-side error | Retry; contact support if persistent |
Errors include a retryable flag. retryable=true codes (e.g. timeout, target_unreachable, rate_limited, resource_exhausted, internal) are safe to retry with backoff; others should be surfaced to the user.
Example Error
{
"error": {
"code": "forbidden",
"message": "Node provisioning requires special account permissions",
"retryable": false,
"details": {
"required_permission": "provisioning.write",
"contact": "support@nobgp.com"
}
}
}
Rate Limiting
Tool dispatch supports token-bucket rate limiting across three independent axes: caller (per user), target (per agent node), and tool (per tool name). Each axis has separate per_min and burst knobs, configured via environment variables on the router. When an axis exceeds its budget, dispatch returns rate_limited with a Retry-After hint.
Rate limits are disabled by default — production traffic is not silently throttled until an operator opts in. See the router's NOBGP_RATE_CALLER_*, NOBGP_RATE_TARGET_*, and NOBGP_RATE_TOOL_* environment variables for configuration.
A rate_limited error includes details.retry_after_ms. Streaming tools (fs_grep) also count against a global in-flight slot pool (default 8 concurrent streams) — exhaustion surfaces as resource_exhausted (HTTP 429, retryable) rather than rate_limited.
Platform-specific behaviour
Windows agents
| Tool | Behaviour |
|---|---|
command | cmd.exe /c, not PowerShell. .exe suffix recommended. Killed processes return exit code 1 (not -1). Set session.shell: "powershell" for non-trivial PowerShell scripts |
command signals | SIGHUP and SIGQUIT return invalid_args. SIGINT writes Ctrl+C (0x03) to stdin — works only on processes that read stdin. Use SIGKILL for guaranteed termination |
file / fs_* paths | Drive-letter form (C:\Users\... or C:/Users/...) is accepted as absolute; bare C:foo (no separator) is drive-relative and rejected |
file / fs_delete depth-guard | Counts / and \ segments. C:\Windows\System32 passes (depth 3); C:\ is refused (depth 1, needs force=true) |
file / fs_write / fs_mkdir mode | Only the read-only bit is honoured; full Unix mode bits are ignored |
net_routes | unsupported / HTTP 501 |
net_dns | unsupported / HTTP 501 |
macOS agents
| Tool | Behaviour |
|---|---|
net_routes | unsupported / HTTP 501 |
Provisioning targets
provider | CPU / memory params honoured? |
|---|---|
aws (default) | yes |
cachengo | no — fixed 4 vCPU / 4 GiB slot |
Versioning
The router and the agent are versioned independently. Tool schemas are forward-compatible: response struct field additions land without a major bump and are absorbed by the loose-output-schema in the MCP adapter, so cached clients don't break on new fields.
Field renames or type changes are breaking and require a coordinated agent + router release. CI diffs a committed OpenAPI snapshot on every change to catch unintended drift.
For the authoritative machine-readable schemas, fetch /api/v1/openapi.json live from any router replica.
Best Practices
1. Cache Directory Results
Don't repeatedly call network_directory - cache results and refresh periodically.
2. Sessions Auto-Close
Command sessions close automatically when the command exits — no need to explicitly close them. For interactive shell sessions (where you started bash), send exit\n via input or use signal: "SIGTERM" to end cleanly.
3. Use Descriptive Names
Give networks, nodes, and services meaningful names for easier management.
4. Handle Errors Gracefully
Check for error responses and handle them appropriately.
5. Respect Rate Limits
Batch operations where possible and implement exponential backoff.
Next Steps
- Core Concepts - Understand the underlying architecture
- Use Cases & Examples - See tools in action
- Claude Setup - Use via Claude MCP client
- ChatGPT Setup - Use via ChatGPT Actions API
Additional Resources
- MCP Specification - Learn about the protocol
- noBGP Web Dashboard - Alternative management interface
- OpenAPI Schema - For Actions API