Skip to main content

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 only fs_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 Authorization header.

Scopes

Scopes gate access to tool families. Missing scope returns forbidden (HTTP 403).

ScopeTools
(none)whoami
network.readnetwork_directory
network.writenetwork_create, network_delete
node.registerregister_node
provisioning.writeprovision_node, deprovision_node
service.writeservice_publish, service_update, service_delete, service_share
shell.execcommand
fs.readfile (read/list/stat), fs_read, fs_list, fs_stat, fs_glob, fs_grep
fs.writefile (write/edit/delete/mkdir), fs_write, fs_edit, fs_delete, fs_mkdir
net.readnet_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:

Node Management:

Service Publishing:

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:

User Management:

  • whoami - User identity and metadata
REST-only streaming tools

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.Match syntax) matched against each node's name. Malformed patterns return invalid_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 - When true, omit each node's services array. 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:

ValueMeaning
currentAgent matches the channel target — no action.
behindAgent is older than the channel target — surface as a nudge to run nobgp upgrade.
aheadAgent 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 + brief without 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.

warning

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.
note

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.

Access Required

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 node
  • network_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

  1. Request received and validated
  2. Container instance created on cloud provider
  3. noBGP agent installed and configured automatically
  4. Agent registers with your network
  5. Node appears as "online" in network_directory

Typical duration: 1-2 minutes


deprovision_node

Terminate and remove a provisioned compute instance.

warning

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 by provision_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 to network_name + node_name.
  • network_name - Network where the agent resides
  • node_name - Agent name where the service will be published
  • title - Human-readable service name
  • proxy_target_url - Host/URI/port to proxy. Plain URL by default (e.g. http://127.0.0.1:8080). Set proxy_target_encoding: "base64" to pass a base64-encoded value (legacy form). Mutually exclusive with terminal_command.
  • proxy_target_encoding - Encoding of proxy_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 with proxy_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). Implies auth_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 update
  • auth_required - Change authentication requirement
  • enabled - Enable or disable service
  • title - Update service display name
  • proxy_target_url - Update proxy target. Plain URL by default (e.g. http://127.0.0.1:8080). Set proxy_target_encoding: "base64" to pass a legacy base64-encoded value. Switches service to proxy mode.
  • proxy_target_encoding - Encoding of proxy_target_url: "utf8" (default) or "base64". Ignored when proxy_target_url is 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 service
  • action - Action to perform: list, add, remove, or revoke
  • emails - Email addresses (required for add and remove actions)

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

ActionDescriptionRequires emails
listShow current authorized emailsNo
addGrant access to specified emailsYes
removeRevoke access for specified emailsYes
revokeClear all authorized emailsNo

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 when command_id is omitted.
    • network_name - Network where the node resides
    • node_name - Agent name to connect to
    • command - Command to execute (e.g., "bash", "df -h", "python3 script.py")
    • shell - Optional interpreter wrapper applied to command before dispatch. Omit (or "auto") for verbatim pass-through to the agent's native shell (sh -c on Linux/macOS, cmd.exe /c on Windows). Set to "powershell" on Windows to wrap as powershell.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 return invalid_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. Set raw: true to disable.
  • raw - Send input bytes as-is without resolving escape sequences.
  • signal - Signal to send to the process: SIGINT, SIGTERM, SIGHUP, SIGQUIT, or SIGKILL.
  • idle_timeout - Seconds to wait with no output before returning (default: 5). Use 0 for non-blocking. Use higher values (e.g., 30 or 60) 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 output
  • output - 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 with command_id); "exited" once the agent reports exit. Prefer branching on state rather than inferring from exit_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

SignalLinux / macOSWindows
SIGINTsent to process groupwritten as Ctrl+C (0x03) to stdin — only interrupts processes that read stdin
SIGTERMsent to process groupmapped to graceful close where possible
SIGKILLsent to process groupterminates reliably
SIGHUPsent to process groupnot supported — returns invalid_args
SIGQUITsent to process groupnot 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

opPurpose
readStream a file from the agent (supports encoding=utf8 or base64, and offset resume)
writeStream a file to the agent (atomic via tmp+rename on done=true)
editSingle old_stringnew_string replacement (atomic)
multi_editOrdered batch of replacements (atomic, all-or-nothing)
listDirectory listing (recursive=true walks subdirs)
statSingle-entry metadata (size, mode, mtime, is_dir)
deleteRemove a file or directory (recursive=true for dirs)
mkdirCreate a directory (recursive=true for mkdir -p)
batchRun N one-shot ops against the same target in one round-trip (best-effort; not transactional)
fetch_urlAgent 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 target on the first call (via node_id OR network_name + node_name). Continuations pass only file_id.
  • Read/write are streaming: first call returns file_id; poll with the same file_id until done=true.
  • Write lands in a sibling .nobgp-tmp file 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_sha256 guards edits against lost updates. A mismatch fails with HTTP 412 (precondition_mismatch) and returns the observed hash in details.observed_sha256.
  • op=batch runs N one-shot sub-ops against the same target in one round-trip. Each entry in ops[] carries its own op + per-op fields (no nested target, no file_id). Sub-ops run serially and are best-effort — a failing sub-op doesn't abort siblings; per-op outcome lands in batch_results[i]. Allowed sub-ops: mkdir, stat, list, delete, edit, multi_edit, write (implicit done=true), fetch_url. read and nested batch are rejected.
  • op=fetch_url makes the agent fetch an HTTP(S) URL directly from its own network position and atomic-write the bytes to path — the router never sees the body. Useful for internal LAN mirrors / authenticated proxies the router can't reach. Optional expected_sha256 verifies the fetched bytes pre-rename; a mismatch leaves the destination untouched and surfaces as precondition_mismatch with details.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)
Use file (not command) for byte movement

The 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 until done=true.
  • encoding=utf8 returns chunk as a plain string; default base64 returns chunk_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 with done=true to finalize.
  • Finalize triggers fsync + chmod + atomic rename on the agent. Aborted writes leave the destination untouched.
  • chunk and chunk_b64 are 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_stringnew_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) OR edits[] (ordered batch). Mixing them is an error.
  • When edits[] has more than one element, the tool dispatches to multi_edit semantics: later edits see earlier edits' results.
  • All-or-nothing: if any edit fails (old_string not found, or non-unique without replace_all), no edits land.
  • expected_sha256 fails with HTTP 412 CodePreconditionMismatch when the agent-observed file hash differs; the observed hash is returned in old_sha256 so 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.

Depth-guard

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 with sudo on 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. Uses curl.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 (use cmd_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.

CodeDescriptionResolution
unauthorizedNot authenticatedSign in via OAuth
forbiddenAuthenticated but not allowedCheck account permissions
permission_deniedAuthenticated but blocked by policyCheck account permissions
not_foundResource doesn't existVerify resource name/ID
invalid_argsBad request parametersCheck input schema
target_not_foundNode target can't be resolvedVerify network/node names
target_unreachableNode is offline or unreachableCheck agent connection
already_existsResource already presentUse update or pick a new name
too_largePayload exceeds limitsSplit the request
timeoutOperation timed outRetry with a longer timeout
canceledOperation was canceledRetry if desired
rate_limitedRate limit exceededHonor Retry-After; back off
unsupportedOperation not supported on this platformCheck platform requirements
method_not_allowedWrong HTTP verb / tool modeCheck tool documentation
failed_preconditionGuard failed (e.g. expected_sha256 mismatch)Rebase on current state and retry
internalServer-side errorRetry; 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

ToolBehaviour
commandcmd.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 signalsSIGHUP 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_* pathsDrive-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-guardCounts / and \ segments. C:\Windows\System32 passes (depth 3); C:\ is refused (depth 1, needs force=true)
file / fs_write / fs_mkdir modeOnly the read-only bit is honoured; full Unix mode bits are ignored
net_routesunsupported / HTTP 501
net_dnsunsupported / HTTP 501

macOS agents

ToolBehaviour
net_routesunsupported / HTTP 501

Provisioning targets

providerCPU / memory params honoured?
aws (default)yes
cachengono — 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

Additional Resources