Skip to content

Let an AI agent run DedNets

DedNets ships an MCP server: point an AI coding agent at it and the agent can enroll a machine, publish a service at a public HTTPS URL, and wire up a watchdog, using the same API you would. It runs locally, out of the dednetsctl binary, and talks only to your Console with a token you mint and can revoke. (Since v0.4.1.)

(Since v0.4.3. macOS and Linux.) One command does everything on this page:

Terminal window
curl -fsSL https://dednets.com/install-cli.sh | sh

It installs dednetsctl (sha256-verified against what your Console publishes), remembers your Console, offers to put it on your PATH, turns on a daily background update check, asks for an API token, and configures the AI agent hosts it finds - Claude Code (plugin plus marketplace auto-update) and Claude Desktop. Restart your agent afterwards and ask it to “list my DedNets hosts”.

Point it at a self-hosted Console, or answer nothing at all:

Terminal window
curl -fsSL https://dednets.com/install-cli.sh | sh -s -- --console https://console.example.com
curl -fsSL https://dednets.com/install-cli.sh | sh -s -- --yes --token dak_your_token_here

It is safe to re-run, and it is the fastest way to check a setup: every step reports ok, changed or skipped, and a step that is already correct writes nothing.

1. Console ok https://console.dednets.com (publishes 0.4.3)
2. PATH ok ~/.local/bin is on PATH
3. auto-update ok on (checked once a day; DEDNETS_NO_UPDATE_CHECK disables it)
4. login ok already logged in to https://console.dednets.com
5. agent hosts ok
Claude Code ok plugin dednets@dednets installed, marketplace auto-update on
Claude Desktop ok MCP server "dednets" -> ~/.local/bin/dednetsctl

Prefer to do it yourself, or on Windows? The manual steps are below and are not going away. If you already have the CLI, dednetsctl setup runs the same wizard without the download.

Enrolling a host (a server that exports apps) is a different, separate command that the Console’s “Connect a host” wizard mints for you. Do not confuse https://dednets.com/install-cli.sh (your workstation) with your Console’s /install.sh (a host).

The MCP server is a subcommand of dednetsctl. Let npx fetch it for you, download the CLI yourself, or install it with Homebrew.

Using Claude Code? Install the plugin in step 3 and skip this step entirely: it brings its own binary resolution.

You want Use Because
One command, everything wired up curl … install-cli.sh | sh (above) installs the CLI, logs you in, configures your agent hosts, keeps itself updated
Nothing installed system-wide npx -y @dednets/mcp (Option A) follows your Console automatically, verified by sha256
A real, upgradable CLI on your PATH brew install dednets/tap/dednetsctl (Option C) one command, brew upgrade later; pinned to a version you choose
No Node and no Homebrew, or Windows curl from /dist (Option B) works anywhere, and you manage upgrades

(Since v0.4.1. Needs Node 20.9 or newer, and a Console publishing v0.4.1 or newer.)

Terminal window
npx -y @dednets/mcp --probe-only

That downloads dednetsctl from your Console (DEDNETS_CONSOLE, default https://console.dednets.com), checks it against the sha256 that Console publishes, caches it under ~/.cache/dednets (%LOCALAPPDATA%\dednets\cache on Windows) and exits without starting anything. Nothing is installed system-wide, and the CLI always follows the Console you point at, including a self-hosted one.

Run it once before wiring up an agent so the first session does not wait for the download. Then use the npx configuration in step 3. If it prints dednets mcp failed: console_too_old, that Console has not been upgraded to v0.4.1 yet; use Option B against it, or ask whoever operates it to upgrade.

Claude Code users should prefer the plugin (step 3) and can skip this step.

Download it from your Console and put it on your PATH:

Terminal window
# macOS (Apple Silicon)
curl -fsSLo dednetsctl https://console.dednets.com/dist/darwin-arm64/dednetsctl
# macOS (Intel): .../dist/darwin-amd64/dednetsctl
# Linux: .../dist/linux-arm64/dednetsctl or .../dist/linux-amd64/dednetsctl
chmod +x dednetsctl && sudo mv dednetsctl /usr/local/bin/
dednetsctl mcp -h # prints the flags; nothing is started

On Windows, in PowerShell (curl there is an alias for Invoke-WebRequest and takes different arguments, so call it by its real name):

Terminal window
$dir = "$env:LOCALAPPDATA\DedNets"
New-Item -ItemType Directory -Force -Path $dir | Out-Null
Invoke-WebRequest -Uri https://console.dednets.com/dist/windows-amd64/dednetsctl.exe `
-OutFile "$dir\dednetsctl.exe"
& "$dir\dednetsctl.exe" mcp -h

The CLI is called dednetsctl since v0.4.1 (it was dedmeshctl). There is no alias, and the old /dist/<platform>/dedmeshctl URLs no longer resolve, so re-download it with the command above if you still have the old binary.

Builds are published for macOS (Apple Silicon and Intel), Linux (amd64, arm64) and Windows (amd64, as dednetsctl.exe). (Windows and Intel-Mac builds since v0.4.1.) Windows gets the CLI, not the host agent: a Windows machine can drive your DedNets account, and the hosts you enroll still run Linux or macOS.

The Windows build is cross-compiled and unit-tested, and has not been run on a Windows machine by us. mcp itself is the portable part of the CLI: it writes no files, opens no unix sockets and reads no config directory. Two areas are built but unverified there: the mTLS client-identity commands (login, client enroll, attach) write their key and certificate with POSIX file modes Windows ignores, and attach never puts the console into raw or virtual-terminal input mode, so interactive use would be line-buffered and locally echoed. Colored output (view) needs Windows Terminal or PowerShell 7; legacy conhost.exe shows the escape codes literally.

(Since v0.4.1.)

Terminal window
brew install dednets/tap/dednetsctl
dednetsctl mcp -h # prints the flags; nothing is started

The formula covers macOS (Apple Silicon and Intel) and Linux (arm64 and amd64), and installs a single binary. There is no Homebrew formula for Windows: use Option B there.

A Homebrew install pins one CLI version, the one the formula was cut from, and brew upgrade dednetsctl moves it. That is the difference from Option A, where the CLI follows whichever Console you point at. Against an older self-hosted Console a pinned CLI is exactly the case the version-skew warning below is for, so expect it and read it rather than filing it as a bug.

In the Console, go to Settings > API tokens > Create token. Name it after the agent (claude-code), tick the scopes you want it to have, and copy the dak_… secret: it is shown exactly once.

(The one-line installer above asks for this token and stores it for you, so if you took that path you only need this page open to create it.)

For the full onboarding journey below, tick Read-only, Manage hosts, Manage services and Manage watchdogs. Add Signals if you want the agent to close or report signals. For a token that can only look, tick just Read-only. What each scope unlocks: API tokens and scopes.

The agent holds this token. Give it the least you need, and revoke it from the same page when you are done.

(Since v0.4.1. macOS and Linux; on Windows use the manual configuration below.)

Terminal window
# 1. tell Claude Code where your Console is, and store your token
export DEDNETS_CONSOLE=https://console.dednets.com # omit if you use the hosted Console
dednetsctl login # Console > Settings > API tokens
# 2. install the plugin
claude plugin marketplace add dednets/dednets-claude-plugin
claude plugin install dednets@dednets

(dednetsctl login is since v0.4.3. On older versions, or if you prefer, export DEDNETS_TOKEN=dak_... instead.)

Prefer login over exporting DEDNETS_TOKEN here. The plugin’s MCP server inherits the environment of the app that launches it, so if you start Claude Code from the Dock or Spotlight rather than a terminal, it never sees anything you exported in your shell profile, and every DedNets tool fails as unauthorized. The stored token does not depend on how you launch the app. DEDNETS_TOKEN still takes priority whenever it is set.

The plugin finds the CLI for you, in this order: $DEDNETSCTL, then dednetsctl on your PATH. It also brings a skill that walks the parts the MCP tools cannot do themselves, such as running an enroll command on the machine being enrolled.

Both variables are read when Claude Code starts, so restart it after changing either one. /mcp then lists dednets.

The Console’s Use with AI button links straight back to this page, so you can start from either end. (Since v0.13.7 the Console links here instead of repeating the install blocks; between v0.4.1 and v0.13.6 it showed its own copy of them with your Console’s URL filled in.)

(Since v0.4.1.) If you took Option A in step 1, name npx as the command and skip the binary entirely:

{
"mcpServers": {
"dednets": {
"command": "npx",
"args": ["-y", "@dednets/mcp"],
"env": {
"DEDNETS_CONSOLE": "https://console.dednets.com",
"DEDNETS_TOKEN": "dak_your_token_here"
}
}
}
}

Add "-read-only" to args after the package name for a look-but-do-not-touch server.

Create .mcp.json in the project you work from:

{
"mcpServers": {
"dednets": {
"command": "dednetsctl",
"args": ["mcp"],
"env": {
"DEDNETS_CONSOLE": "https://console.dednets.com",
"DEDNETS_TOKEN": "dak_your_token_here"
}
}
}
}

Or add it from the command line:

Terminal window
claude mcp add dednets \
--env DEDNETS_CONSOLE=https://console.dednets.com \
--env DEDNETS_TOKEN=dak_your_token_here \
-- dednetsctl mcp

Then /mcp inside Claude Code should list dednets as connected.

The same block goes in the app’s config file, which differs per OS. Claude Desktop does not inherit your shell PATH, so give the absolute command path in both cases, and restart the app after editing the file. That applies to npx too: use the output of which npx (often /usr/local/bin/npx or a path under ~/.nvm/) rather than the bare word. (Since v0.4.1.)

~/Library/Application Support/Claude/claude_desktop_config.json:

{
"mcpServers": {
"dednets": {
"command": "/usr/local/bin/dednetsctl",
"args": ["mcp"],
"env": {
"DEDNETS_CONSOLE": "https://console.dednets.com",
"DEDNETS_TOKEN": "dak_your_token_here"
}
}
}
}

%APPDATA%\Claude\claude_desktop_config.json. Give the expanded absolute path to the .exe (the config file is not a shell: environment variables such as %LOCALAPPDATA% are not expanded inside it), and remember that JSON needs every backslash doubled. (Since v0.4.1.)

{
"mcpServers": {
"dednets": {
"command": "C:\\Users\\you\\AppData\\Local\\DedNets\\dednetsctl.exe",
"args": ["mcp"],
"env": {
"DEDNETS_CONSOLE": "https://console.dednets.com",
"DEDNETS_TOKEN": "dak_your_token_here"
}
}
}
}
Setting Value
Transport stdio
Command dednetsctl, or dednetsctl.exe on Windows (absolute path if the client does not use your PATH)
Args ["mcp"], or ["mcp", "-read-only"] to publish only the read tools
Env DEDNETS_CONSOLE (your Console base URL), DEDNETS_TOKEN (your dak_… token)

With npx instead, the command is npx and the args are ["-y", "@dednets/mcp"] (add "-read-only" after that for the read tools only). The environment is the same. (Since v0.4.1.)

Ask the agent: “list my DedNets hosts”. It should call host_list and answer with your fleet. If it says it has no such tool, see Troubleshooting.

Ask for It does Needs scope
“add this machine to my network” enrolls a host, then hands you the exact install command to run on it Manage hosts
“which of my hosts are online?” lists the fleet with versions and status Read-only
“give this container a public HTTPS URL” publishes an app and returns the https://… address Manage services
“open port 25565 for my game server” a raw public port, or a port on your own gateway host Manage services
“publish my dashboard but only let me and bob see it” publishes it behind a DedNets login and adds bob to its access list; everyone else is stopped at the edge (since v0.10.0) Manage services
“revoke bob’s access to that dashboard” removes him from the access list, effective on his next click (since v0.10.0) Manage services
“let host A reach host B’s database” a proxy port; nothing becomes public Manage services
“put my Proxmox console on pve.lab.example.com” a reverse proxy host on one of your gateways: turns the host’s reverse proxy on if you ask it to, points a hostname under your wildcard at an https://192.168.…:8006 upstream on that host’s own network, with HTTPS from the gateway and an optional DedNets login in front (since v0.14.0) Manage hosts for the switch, Manage services for the proxy host
“serve my app on blog.example.com” records your domain, hands you the DNS records to create, verifies it, then points the hostname at the app (since v0.4.5; owning the name and publishing on it are two steps since v0.7.0, and the agent can do both since v0.9.1) Manage services
“give every CI branch its own hostname under lab.example.com” records the wildcard *.lab.example.com, hands you the two DNS records, and verifies it once. After that the agent points any label at any app on its own - pr-1234.lab.example.com needs no new record, no new claim, no new verification and no certificate step (the first HTTPS request issues it) (wildcards since v0.6.0; the agent can publish a label since v0.9.1) Manage services
“move api.lab.example.com to the new service” / “take that hostname down” re-points one hostname in place, or stops it serving while leaving the domain and every other hostname alone (since v0.9.1) Manage services
“take blog.example.com down” removes the custom domain and every hostname bound to it, after you confirm (since v0.4.5) Manage services
“is my service healthy?” status, uptime and health-check state for one target Read-only
“alert me if my nightly backup stops running” creates a watchdog and gives you the cron wiring Manage watchdogs
“why did my watchdog alert last night?” recent transitions, uptime buckets, likely causes Read-only
“what is currently wrong?” lists open signals: one record per condition, with how many times each has been reported (since v0.9.0) Read-only
“show me the details of the SSH brute-force signal” that signal in full, with the payload the reporting script sent and its history of state changes Read-only
“the disk issue is fixed, close it” resolves the signal. This is reversible: if the condition returns, the same key reopens the same record Signals
“report a test condition so I can check the wiring” posts a real occurrence, which can reach your phone. Ask for a throwaway key you can delete afterwards Signals

The server also ships whole workflows as MCP prompts. In Claude Code they appear as slash commands after / (type /dednets to filter); other clients list them in a prompt picker. Each one is a checklist the agent follows, and each takes optional arguments. (Since v0.4.1.)

Command Does Arguments
/dednets:onboard_host enrolls a machine and waits for it to come online host, flavor
/dednets:review_fleet read-only review of every host and what it serves kind
/dednets:publish_app exports a service and hands back the public address app, host, proto
/dednets:remove_host shows what breaks, then revokes after you confirm host
/dednets:troubleshoot_watchdog reads a monitor’s history and names likely causes watchdog, range

Arguments are optional: run the command with none and it asks you for what it needs. With -read-only only review_fleet and troubleshoot_watchdog are offered, because the others need a tool that changes something.

Prefer starting from the Console? Three pages carry a Use with AI button that names the scopes your token needs, links back to this guide for the install, and lists the ready-made prompts for that page - the same text these slash commands use, so you can copy one straight into your agent:

Console page Prompts it offers
Hosts add, review, and remove hosts (since v0.4.1)
Services publish an app at a public URL (since v0.4.3)
Watchdogs troubleshoot a watchdog, read-only (since v0.4.3)

Each dialog also links back to the guide for what that page does, so you can go either way between the Console and these docs.

The agent cannot mint or revoke API tokens, change your account, or touch anyone else’s resources, whatever you ask it for.

This is a real run, not an illustration. The prompt was “deploy this machine as a new mesh host, run an HTTP service on it, expose it at a public URL, and configure a watchdog for it”, given to an agent that had the DedNets tools and a shell on a fresh Linux VM, and nothing else. What it did:

  1. Enrolled the host. It asked for the systemd install flavor (the only one since v0.12.0) and got a one-time join token plus a ready-to-run command.
  2. Ran the install command on the machine itself, over its shell. This is the step the agent cannot do on your behalf without access: it needs a shell on the target machine, or you paste the command yourself.
  3. Waited for the host to come online, checking the fleet.
  4. Started a small HTTP service on 127.0.0.1:8080 as a systemd unit, then published it as app demo with proto: http and public: true. That returned an https://demo-<username>.<domain> address which answered 200 end to end.
  5. Created a watchdog (5 minute period, 60 second grace) and wired a cron entry that pings start, runs the check, then pings success or fail. The watchdog read up on the next ping.

It took about 50 turns and a few minutes. Give the agent shell access to the target machine, or be ready to paste one install command yourself.

(Since v0.4.3.) A CLI that drifts behind its Console fails in the least readable way there is: a tool call that looks like it was called wrong. Two commands, and one switch, keep that from happening.

Terminal window
dednetsctl self-update -check # what does my Console publish?
dednetsctl self-update # fetch it, verify the sha256, swap

The one-line installer turns on a daily background check (you can decline it). It works the way you would want it to: at most one check a day, the command you typed finishes on the binary it started with and adds no delay, and a detached child does the download in the background, so the next command you run is the new version. It logs one line to ~/.cache/dednets/update.log and never prints anything.

Terminal window
dednetsctl config # what am I configured for, and why?
dednetsctl config set auto-update off # or on
dednetsctl config set console https://console.example.com

config is the command to run when something points at the wrong place: it shows the resolved Console and where each value came from, so an exported DEDNETS_CONSOLE that overrides your stored setting is visible rather than mysterious. Precedence is -console flag, then DEDNETS_CONSOLE, then the stored value; with none of those set the CLI talks to the hosted Console, https://console.dednets.com (since v0.8.1). Your token is never printed.

To disable the check in one environment only, set DEDNETS_NO_UPDATE_CHECK=1.

self-update will not touch a CLI it does not own, and says what to run instead: a Homebrew install gets brew upgrade dednets/tap/dednetsctl, and the npx shim already re-resolves itself on every start.

  • The agent has no DedNets tools. Either the config file is not where the client reads it, or dednetsctl is not on the client’s PATH (Claude Desktop: use the absolute path). Restart the client after editing the config. The fastest fix is dednetsctl setup, which writes both clients’ config for you and reports what it changed.
  • Every DedNets tool fails as unauthorized. No token reached the server. Run dednetsctl login, or set DEDNETS_TOKEN in the env block, then restart the client. Since v0.4.3 the server starts either way and says this on each call; before that it exited instead, which showed up as the next item.
  • The plugin fails to connect, with -32000 and nothing else. The server process died before it could say why. Two causes, in order of likelihood: dednetsctl is not where the plugin looks (it tries $DEDNETSCTL, then your PATH), or you are on a version before v0.4.3, where a missing token also exited. Run the wrapper by hand to see the real error: ~/.claude/plugins/cache/dednets/dednets/*/bin/dednets-mcp. If it reports cli_not_found, install the CLI or point DEDNETSCTL at it. Note that an install predating v0.4.1 provides dedmeshctl, the old name, which the plugin will not use.
  • A call was refused for scope. The error names the scope to add. You cannot widen an existing token: mint a new one with the extra scope and update the config.
  • Everything 404s or answers nonsense. DEDNETS_CONSOLE must be the full base URL including the scheme (https://console.dednets.com). Since v0.4.3, run dednetsctl config: it prints the Console actually in use and where that value came from, so an exported variable overriding your stored setting is visible.
  • install-cli.sh refuses with a checksum mismatch (since v0.4.3). The bytes it downloaded did not match the sha256 your Console publishes for that release. Nothing was installed and the download was deleted. A Console being redeployed at that moment explains it, so retry once; if it repeats, do not use that download and tell whoever operates the Console.
  • self-update says the directory is not writable (since v0.4.3). The CLI is somewhere only root can write, usually /usr/local/bin. Re-run it with sudo, or reinstall with the one-line installer, which uses ~/.local/bin.
  • A call fails with console_outdated, or the log warns about versions (since v0.4.1). The dednetsctl binary and the Console are on different releases. On its first tool call the server prints one line on stderr naming both versions and keeps running; a call that needs an endpoint the Console does not have then fails with a fix: line instead of a bare 404. Fix it from whichever side you control: ask the Console operator to upgrade, or reinstall the CLI from that Console - curl -fsSLO https://<console>/dist/darwin-arm64/dednetsctl (or the linux-… / darwin-amd64 / windows-amd64/dednetsctl.exe path for your machine). The same version with a different -<sha> suffix is not skew and is never reported.
  • My Homebrew CLI and my Console disagree on the version (since v0.4.1). Expected: brew install pins the release the formula was cut from, so a Console on a different release produces exactly the skew warning above. Run brew upgrade dednetsctl. If the Console is the older side, upgrade it, or switch to npx -y @dednets/mcp (Option A), which follows the Console.
  • brew install dednets/tap/dednetsctl says there is no available formula. That tap has not published a formula yet, or you have an old cached copy of it: brew update and retry.
  • The install command the agent gives me fails to download. Install commands are generated for a console reachable over HTTPS on port 443. A self-hosted console on another port hands out a command whose download URL does not resolve; enroll that host from the Console’s own Deploy a daemon wizard instead.
  • I want a look-but-do-not-touch setup. Add "-read-only" to args: only the read tools are published, so no tool call can change anything.
  • npx says console_too_old (since v0.4.1). That Console publishes a release older than v0.4.1, from before the CLI was renamed, so there is no dednetsctl for npx to fetch. Ask whoever operates it to upgrade, or install the CLI yourself with Option B in step 1 and point your client at the binary.
  • npx says checksum_mismatch (since v0.4.1). The bytes downloaded did not match the sha256 that Console publishes. Nothing was run and the download was deleted. A Console being redeployed at that exact moment explains it, so retry once; if it repeats, do not use that download and tell whoever operates the Console.
  • The first npx run is slow. It is fetching a CLI of roughly 12 MB and verifying it. Every later run reuses the cached copy until the Console publishes a new version. Run npx -y @dednets/mcp --probe-only once to warm it before an agent host starts the server.
  • Windows: the client shows no DedNets server at all. Download the .exe path (windows-amd64/dednetsctl.exe); the suffix-less file is not published and Windows would not run it. In claude_desktop_config.json every backslash in the path must be doubled, or the JSON is invalid and the client drops the whole server entry without saying so.