wg-easy-mcp
This server provides an MCP interface to administer a wg-easy WireGuard VPN instance.
Client management: List all WireGuard clients (with filtering and sorting), get details by ID, create new clients (auto-generated keys/IPs, optional expiry), update any combination of settings (name, DNS, MTU, allowed IPs, keepalive, expiry, enabled state), enable/disable clients, and delete via a two-step confirmation flow.
Configuration & access: Retrieve a client's WireGuard
.conffile (contains private key), get a QR code (SVG) for mobile setup, and generate a one-time download link (if enabled). All sensitive details except the one-time link require proper authorization.Server inspection: View release/update status, general settings, and WireGuard interface configuration (secrets redacted).
MCP safety annotations: All tools are tagged as read-only, destructive, or idempotent for safe use by AI clients like Claude or Codex.
Allows managing WireGuard VPN instances through wg-easy, providing tools to list, create, update, enable/disable, and delete clients, fetch client configuration files and QR codes, generate one-time links, and retrieve server information.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@wg-easy-mcpList all WireGuard clients with their connection status and traffic"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
wg-easy-mcp
A Model Context Protocol (MCP) server for administering wg-easy (WireGuard Easy) instances.
Lets MCP clients like Claude Code, Claude Desktop or Codex manage your WireGuard VPN: list, create, update, enable/disable and delete clients, fetch configuration files and QR codes, and inspect the server status — all through the wg-easy v15 REST API.
Eleven tools is the ceiling, not the floor: WG_EASY_ALLOW_TOOLS=essential
registers a curated six instead, and a model picks the right tool far more
reliably from six than from eleven — see
choosing which tools load.

What makes it different
The full client lifecycle over the wg-easy v15 REST API, including .conf
files, QR codes and one-time download links.
Partial updates merge. An update reads the current client state and changes only the fields you named, instead of overwriting the rest with defaults.
disable_client stays ungated on purpose. Every other write asks a person
first through MCP elicitation; that one only ever withdraws access, and making it
harder would be making the safe move the slow one.
Related MCP server: amp-mcp-server
Requirements
Node.js ≥ 22
A running wg-easy v15+ instance
2FA (TOTP) must be disabled for the account used by this server — the wg-easy API only supports Basic Authentication and does not work with 2FA enabled
Note: The wg-easy REST API is not yet declared stable and may change between releases. This server targets wg-easy v15.
Configuration
Configuration is provided via environment variables:
Variable | Required | Description |
| yes | Base URL of the wg-easy web UI, e.g. |
| yes | Username of a wg-easy admin account |
| yes | Password of that account |
| no | Set to |
| no | Comma-separated tool names, |
| no | Same syntax; removed from whatever |
| no |
|
Use
https://. With a plain-httpURL the Basic Auth credentials and all WireGuard private keys travel unencrypted; the server prints a warning unless the host is local. For self-signed certificates prefer a proper internal CA overWG_EASY_INSECURE_TLS.
Without credentials the server still starts and lists its tools (so registries and inspectors can introspect it), but every tool call fails with setup instructions instead of reaching the wg-easy API.
Choosing which tools load
WG_EASY_ALLOW_TOOLS and WG_EASY_DENY_TOOLS take comma-separated tool names;
a trailing * matches a whole family. essential is a curated preset of
six: get_server_info, list_clients, get_client, create_client, enable_client, disable_client.
get_client_config, get_client_qrcode and generate_one_time_link are not in
it, and neither is delete_client: all four either destroy something
irreversibly or hand out a peer's private key. Name them where you want them.
WG_EASY_ALLOW_TOOLS=essential
WG_EASY_ALLOW_TOOLS=essential,get_client_config
WG_EASY_ALLOW_TOOLS=list_clients,get_client_config
WG_EASY_DENY_TOOLS=delete_client,create_clientAn entry that matches no tool aborts startup and names it, so a typo cannot
silently hide a tool — an absent tool is not something anyone traces back to an
environment variable. A filtered tool is never registered, so it is absent from
tools/list and unknown to tools/call alike, exactly like a write tool under
WG_EASY_READ_ONLY.
If you run several of these servers at once, mcp-hub
is the other answer — its /hub endpoint replaces every server's tools with six
meta-tools.
Installation
Claude Code
claude mcp add wg-easy -s user \
-e WG_EASY_URL=https://vpn.example.com:51821 \
-e WG_EASY_USERNAME=admin \
-e WG_EASY_PASSWORD=your-password \
-- npx -y wg-easy-mcpClaude Desktop
Add to your claude_desktop_config.json:
{
"mcpServers": {
"wg-easy": {
"command": "npx",
"args": ["-y", "wg-easy-mcp"],
"env": {
"WG_EASY_URL": "https://vpn.example.com:51821",
"WG_EASY_USERNAME": "admin",
"WG_EASY_PASSWORD": "your-password"
}
}
}
}Codex
Add to your ~/.codex/config.toml:
[mcp_servers.wg-easy]
command = "npx"
args = ["-y", "wg-easy-mcp"]
env = { WG_EASY_URL = "https://vpn.example.com:51821", WG_EASY_USERNAME = "admin", WG_EASY_PASSWORD = "your-password" }From source
git clone https://github.com/ni-c/wg-easy-mcp.git
cd wg-easy-mcp
npm install
npm run build
# then use `node /path/to/wg-easy-mcp/dist/index.js` as the commandDocker
A multi-arch image (linux/amd64, linux/arm64) with an SBOM and build provenance is published to GitHub Container Registry:
docker run -i --rm \
-e WG_EASY_URL=https://vpn.example.com:51821 \
-e WG_EASY_USERNAME=admin \
-e WG_EASY_PASSWORD=your-password \
ghcr.io/ni-c/wg-easy-mcp:latestThe image talks MCP over stdio, so clients need docker run -i (no port is
exposed):
{
"mcpServers": {
"wg-easy": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e",
"WG_EASY_URL",
"-e",
"WG_EASY_USERNAME",
"-e",
"WG_EASY_PASSWORD",
"ghcr.io/ni-c/wg-easy-mcp:latest"
],
"env": {
"WG_EASY_URL": "https://vpn.example.com:51821",
"WG_EASY_USERNAME": "admin",
"WG_EASY_PASSWORD": "your-password"
}
}
}
}Through mcp-hub
A client that cannot spawn a local process — ChatGPT connectors, Claude on the web,
Cursor, LibreChat — reaches wg-easy-mcp through mcp-hub: one
container serves many stdio MCP servers over Streamable HTTP, with an OAuth 2.1 login
behind a single password and long-lived tokens for the clients that cannot do OAuth. Its
/hub endpoint puts every server behind six meta-tools, so one connector reaches all of
them without N×tool schemas in the model's context, and it speaks both protocol revisions
— a question this server asks travels through it to the person at the far end.
Its /config/mcp.json uses Claude Code's format, so the entry is the one you already
have:
{
"mcpServers": {
"wg-easy": {
"command": "npx",
"args": ["-y", "wg-easy-mcp"],
"env": { "WG_EASY_ALLOW_TOOLS": "essential" },
"denyTools": ["delete_client"]
}
}
}allowTools and denyTools there are the hub's own per-server filter, which is not
the same thing as *_ALLOW_TOOLS in env — the difference, and the mistake it invites,
are in the client guide.
Tools
Tool | Description |
| List all WireGuard clients with status and traffic statistics |
| Get the full details of a single client |
| Create a new client ( |
| Update a client; only the provided fields are changed |
| Let a client connect again — re-arms a key pair already installed on the peer |
| Block a client; it keeps its configuration and keys |
| Permanently delete a client |
| Get the client's WireGuard |
| Get the client configuration as a QR code (SVG) |
| Generate a one-time config download link, valid five minutes |
| Release/update status, general settings and interface configuration (secrets redacted) |
👤 asks a person through MCP elicitation · falls back to a two-call
confirm_token where the client cannot show a dialog.
Structured output
Every tool declares an outputSchema and answers with structuredContent
alongside the text block, so a client can use the result without parsing prose:
{
"untrusted": true,
"source": "wg-easy",
"count": 2,
"clients": [{ "id": 1, "name": "laptop", "enabled": true }],
}The untrusted marker is a field and not only a sentence in the text, because a
client that reads the structured half and ignores the text would otherwise get
free-form client names, DNS entries and endpoints with no framing at all. Every
tool carries it except delete_client, which reports an id this server was
given and nothing that came back from the instance.
Three answers changed shape to fit, and all three for the same reason: a schema
whose root is not an object is served to a 2025-era client rewritten as
{result: …}, so the tool would answer differently depending on who asked.
Tool | Was | Is |
| a bare array |
|
| the |
|
| the SVG markup |
|
An oversized answer is now shortened as an object rather than cut as a
string: the longest text field is shortened first, then list entries are
dropped, and a truncated field says what was cut and how much there was. A
document sliced at a byte offset is not a smaller answer, it is an unparseable
one — and the two channels have to carry the same value.
What wg-easy sends is described with every field optional and unknown fields allowed; only what this server builds is exact. The SDK validates each result against its schema before it goes out, so a stricter shape would turn a wg-easy release that adds a field into a tool that fails outright.
Safety
Five tools ask a person, not just the model.
create_client,update_client,enable_client,delete_clientandgenerate_one_time_linkraise a real dialog through MCP elicitation where the client supports it. Only one of the five destroys anything — the others issue a VPN credential, re-arm one, can widen a route, and mint an unauthenticated URL that hands out a private key.disable_clientis the one write tool that never asks: it can only withdraw access. Where the client cannot show a dialog they fall back to a random token valid for 5 minutes and bound to the exact target (forupdate_client, to the exact edit), which proves the call was made twice with the same arguments and nothing more.ELICITATION=falsetakes that fallback deliberately; it never removes the guard. See Asking a person.Key material is redacted everywhere it is not the point. A field name is matched by its suffix —
password,passwd,passphrase,secret,token,apiKey,privateKey,preSharedKey, plus anything starting withtotp— sometricsPassword, which carries the argon2 hash of the metrics token, is covered along with every other<prefix>Secretwg-easy invents. (keyis not a suffix: it would takepublicKeywith it.) Values are replaced with[redacted]at every nesting level — inget_server_info's admin responses, which carry the WireGuard server key, and inlist_clientsandget_client, which carry each client's own key. Live one-time-link tokens are redacted from the same two, becauseGET /cnf/<token>serves the whole configuration with no login at all;expiresAtsurvives, so a listing still shows that a link is live.get_client_config,get_client_qrcodeandgenerate_one_time_linkare the deliberate exceptions: handing a peer its configuration is what they are for, and somebody asked.Everything the wg-easy API returns carries an explicit untrusted-data marker and a 60 000-character budget, measured on the text that is actually emitted. Client names, DNS entries and endpoints are free-form strings, so they are marked as data to report rather than instructions to follow, control characters and BiDi overrides are stripped from them (field names included), and a single oversized field cannot flood the model's context.
Nothing the instance sends is taken on trust. Every field an output schema types is checked at the boundary and left out when it does not hold, so one record with a string
idor a1e999cannot take a whole listing down withOutput validation error. Entries that are not client records at all are counted inskippedrather than dropped in silence.Response bodies have a ceiling (8 MiB, refused on a declared
content-lengthbefore a byte is read) and the status is read before the body, so a401behind a large proxy page is still a401. A refused login is repeated from memory for ten seconds rather than retried.A
WG_EASY_URLcontaining embedded credentials (user:password@host) is rejected at startup — they would otherwise be echoed in the startup log and prefixed onto every request. Only its origin and path are kept, and no startup diagnostic echoes a value back.Upstream error bodies are labelled as untrusted, stripped of control characters and cut at 200 characters; HTML error pages (reverse proxies) are dropped before being returned to the MCP client.
Caller input has a length: names and filters at 200 characters, addresses at 64, list parameters at 64 entries, and a client id bounded in its pattern rather than after
Number().WG_EASY_INSECURE_TLSonly relaxes certificate validation for the wg-easy connection — it does not disable TLS verification process-wide.WG_EASY_READ_ONLY=trueregisterslist_clients,get_clientandget_server_info, and nothing else.get_client_configandget_client_qrcodeare reads and still not in that set: what they read is a client's private key in the clear, and a read-only mode that leaves key disclosure standing is not the mode its name promises.Tools carry MCP annotations (
readOnlyHint,destructiveHint,idempotentHint) so hosts can apply appropriate permission policies.Keep in mind that
get_client_configandget_client_qrcodereturn the client's private key, and agenerate_one_time_linkURL allows an unauthenticated config download — treat tool output as sensitive.
The full trust model is in SECURITY.md and, in prose, at wg-easy-mcp.ni-c.de/guide/security.
Not exposed, on purpose
wg-easy v15 or newer only. Older versions expose a different, session-based API that this server does not implement.
No server administration. The tools cover the client lifecycle; the instance's own configuration, its admin accounts and its host stay outside the tool list.
Safety
Five tools ask a person first, through MCP elicitation:
create_client,update_client,enable_client,delete_clientandgenerate_one_time_link. Only one of them destroys anything — the others are on the list becausedestructiveHintis the wrong axis for what they do. A new client is a credential that reaches every network behind the VPN,update_clientcan widenserverAllowedIps, andenable_clientre-arms a key pair that is already installed on a peer.The approval is bound to the exact edit, so approving a rename does not license a later call that widens the routes.
disable_clientdeliberately stays ungated: it only ever withdraws access, and making the safe move the slow one would be the wrong trade.Client names, addresses and the instance's own strings are marked as untrusted data, and oversized output is truncated with the omission stated.
WG_EASY_READ_ONLY=trueregisters the read tools and nothing else.
Documentation
The full guide, tool reference and security notes live at
wg-easy-mcp.ni-c.de (source in docs/).
Development
npm install
npm run build # compile TypeScript to dist/
npm test # run the vitest test suite
npm run lint # oxlint + prettier check
npm run test:coverageCI runs the suite on Node 22 and 24 and adds npm audit, CodeQL and a Trivy scan of the container image on both architectures. See CONTRIBUTING.md.
The documentation site lives in docs/ with its own manifest:
cd docs && npm install && npm run devReleasing
Bump the version in
package.jsonand add aCHANGELOG.mdentry.Commit, then tag and push:
git tag -a vX.Y.Z -m "vX.Y.Z" && git push origin main vX.Y.Z
The release workflow runs the test suite, publishes to npm (via trusted publishing, no token, with provenance), creates a GitHub release from the changelog entry and updates the entry in the official MCP Registry (io.github.ni-c/wg-easy-mcp, via GitHub OIDC). The container image is published to GHCR by the CI workflow on the same tag.
server.json lists both an npm and an OCI package; the registry job syncs the version into both before publishing. If it ever fails, fix main and re-run mcp-registry.yml via workflow_dispatch — re-running the tag job checks out the old tree.
Releasing
Releases are tag-driven. Bump package.json, move the [Unreleased] notes in
CHANGELOG.md under the new version, commit, then:
git tag -s vX.Y.Z -m "vX.Y.Z"
git push origin main vX.Y.ZThe release workflow publishes to npm via Trusted Publishing (OIDC, with provenance), pushes the multi-arch container image to GHCR, creates the GitHub release from the CHANGELOG section, and updates the entry in the official MCP registry.
Contributing
Issues, discussions and pull requests are welcome — see CONTRIBUTING.md. For vulnerabilities please use private reporting rather than a public issue; the policy is in SECURITY.md.
License
MIT © Willi Thiel
Available Tools
11 toolscreate_clientCreate WireGuard clientA
Create a new WireGuard client. Keys and IP addresses are generated by wg-easy. Returns the new client ID.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Display name of the new client | |
| expiresAt | No | Optional expiry date as ISO string (e.g. 2026-12-31). Omit for no expiry. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden of disclosing behavior. It mentions that keys and IPs are auto-generated by wg-easy and that the new client ID is returned. However, it does not disclose potential side effects, permission requirements, or whether the client is immediately active.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two concise sentences that front-load the core action and provide useful follow-up context. No unnecessary words or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description is sufficient for a simple creation tool with full schema coverage. It explains the core behavior, auto-generation, and return value. It lacks optional details like default state or uniqueness constraints, but these are not critical for basic usage.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema fully describes both parameters (name required, expiresAt optional) with descriptions, so baseline is 3. The description adds no additional parameter-level meaning; it only notes that keys/IPs are generated, which is implied by the tool's purpose.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: creating a new WireGuard client. It specifies the action (create), the resource (WireGuard client), and additional outcomes (generated keys/IPs, returns client ID). The verb 'create' distinguishes it from sibling tools that list, get, update, enable, disable, delete, or fetch configs.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage when a new client needs to be added, but it does not explicitly state when to use this tool versus alternatives like update_client or enable_client. It provides no exclusion criteria or conditional guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_clientDelete WireGuard clientADestructive
Permanently delete a WireGuard client. This is irreversible: the client loses VPN access and its keys cannot be restored. The first call returns a short-lived confirmation token; ask the user for confirmation, then call again with confirmToken.
| Name | Required | Description | Default |
|---|---|---|---|
| clientId | Yes | Numeric ID of the client (see list_clients) | |
| confirmToken | No | Confirmation token from a previous delete_client call for the same client. Omit on the first call. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes well beyond the destructiveHint annotation by explicitly stating the irreversible consequences: 'This is irreversible: the client loses VPN access and its keys cannot be restored.' It also reveals the behavioral nuance of requiring a confirmation token, which is not available in annotations. This fully informs the agent of the tool's operational traits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three sentences, each earning its place: purpose, irreversibility, and the confirmation flow. It is front-loaded with the primary action and contains no filler or redundant phrases. This is an excellent balance of completeness and brevity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the lack of an output schema, the description adequately covers all necessary behavioral context for a destructive operation. It explains the irreversible nature, the confirmation token workflow, and the impact on the client. The tool's complexity is fully addressed without needing to describe return formats.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema provides 100% coverage of both parameters (clientId and confirmToken) with their own descriptions. The tool description adds extra semantics by explaining why confirmToken is needed and its role in the two-step confirmation process. While the schema alone would be sufficient, the description enriches the understanding of the parameter behavior.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's primary function: 'Permanently delete a WireGuard client.' This is a specific verb+resource combination that fully distinguishes it from sibling tools like get_client, update_client, or disable_client. The title reinforces the action, and the description adds critical scope (permanent deletion, loss of VPN access).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear usage context by explaining the two-step confirmation flow: 'The first call returns a short-lived confirmation token; ask the user for confirmation, then call again with confirmToken.' This tells the agent when to call again and how to use the token. However, it does not explicitly mention alternatives (e.g., when to prefer disable_client over delete_client), so it lacks explicit when-not conditions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
disable_clientDisable WireGuard clientAIdempotent
Disable a WireGuard client. The client keeps its configuration but can no longer connect.
| Name | Required | Description | Default |
|---|---|---|---|
| clientId | Yes | Numeric ID of the client (see list_clients) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With idempotentHint annotation present, the description adds value by detailing the non-destructive nature ('keeps its configuration') and the effect ('can no longer connect'), going beyond the annotation. It omits active connection handling but remains sufficient given the annotation coverage.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, direct and free of fluff. The purpose and key behavioral constraint are front-loaded, with no unnecessary words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Simple one-parameter tool with good schema and annotation coverage. Description covers purpose, effect, and non-destructive behavior; no output schema exists. Adequate for tool selection and invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Input schema covers 100% of parameters, with clientId described as 'Numeric ID of the client (see list_clients).' The description adds no parameter-level detail, so the baseline score of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description states 'Disable a WireGuard client' with a specific verb and resource. It further clarifies the client 'keeps its configuration but can no longer connect,' distinguishing it from delete_client and clearly opposing enable_client.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides clear context: it disables access while preserving configuration, implying a temporary revocation use case. However, it does not explicitly name alternatives like delete_client or enable_client, so exclusions are implicit rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
enable_clientEnable WireGuard clientAIdempotent
Enable a WireGuard client so it can connect again.
| Name | Required | Description | Default |
|---|---|---|---|
| clientId | Yes | Numeric ID of the client (see list_clients) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare idempotentHint=true, so the description does not need to restate that. The description adds minimal behavioral context beyond the annotation, such as the effect of enabling the client. It does not contradict the annotations, but also does not disclose additional details like what happens if the client is already enabled.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, concise sentence that directly states the tool's purpose. It contains no fluff or redundant information, making it optimally concise and well-structured for quick agent comprehension.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is simple with one parameter and no output schema, and the description combined with the idempotent annotation provides sufficient context for an agent to understand the operation. However, it does not mention return value or behavior when already enabled, which would be useful but is not critical given the idempotent annotation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema fully documents the single parameter (clientId) with a description and constraints, achieving 100% schema coverage. The description itself adds no parameter-specific information, so it relies on the schema as baseline. No further semantic enrichment is provided.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb 'Enable' with a clear resource 'WireGuard client' and adds the purpose 'so it can connect again', which clearly differentiates it from siblings like disable_client or create_client. It also implicitly indicates a state transition from disabled to enabled.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'so it can connect again' provides clear context that this tool is for re-enabling a previously disabled client, which distinguishes it from create_client or update_client. However, it does not explicitly state when not to use it or name alternatives, so it misses the top score for explicit exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
generate_one_time_linkGenerate one-time config linkA
Generate a one-time download link for a client configuration that can be shared with the end user. Requires WG_ENABLE_ONE_TIME_LINKS to be enabled on the wg-easy instance. SENSITIVE: anyone with the link can download the full client configuration without authentication — share it only with the intended user.
| Name | Required | Description | Default |
|---|---|---|---|
| clientId | Yes | Numeric ID of the client (see list_clients) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses the one-time nature, the server-side requirement (WG_ENABLE_ONE_TIME_LINKS), and a critical security behavior: the link grants unauthenticated access to the full config. This is valuable beyond the empty annotations, but it doesn't cover error handling or expiration details beyond 'one-time'.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two concise sentences front-load the purpose and then add the requirement and security warning. No filler or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter tool with no output schema, the description adequately covers purpose, usage, and a critical security caveat. It lacks explicit error behavior when the environment flag is disabled, but this is minor given the simplicity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema fully documents clientId with a clear description ('Numeric ID of the client (see list_clients)'). The description adds no additional parameter context, so the baseline score of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Clearly states the tool generates a one-time download link for a client configuration, distinguishing it from sibling tools like get_client_config (which returns the config directly) and get_client_qrcode (which generates a QR code). The verb 'generate' and resource 'one-time download link' are specific.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides context that the link is meant to be shared with end users and requires WG_ENABLE_ONE_TIME_LINKS to be enabled. It implicitly suggests using this instead of direct config retrieval when sharing externally, though it doesn't explicitly name alternative tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_clientGet WireGuard clientARead-only
Get the full details of a single WireGuard client.
| Name | Required | Description | Default |
|---|---|---|---|
| clientId | Yes | Numeric ID of the client (see list_clients) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint: true, covering the safety profile. The description adds the scoping promise of 'full details' and 'single', but does not disclose return format, pagination, error behavior, or authentication requirements, providing limited additional behavioral transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single sentence of eight words, front-loaded with the verb 'Get', and contains no filler words. Every word contributes to understanding the tool's purpose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple single-parameter read tool with a read-only annotation, the description is mostly adequate. It clearly states the operation and scope, though 'full details' is vague and there is no output schema to define the return structure. Still, the context is sufficient for an agent to select the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The only parameter clientId is fully described in the schema with a type, range, and a cross-reference to list_clients, giving 100% schema coverage. The description adds no parameter-specific semantics, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses the specific verb 'Get', identifies the resource as 'WireGuard client', and specifies scope with 'single', which clearly distinguishes it from list_clients and other get variants like get_client_config or get_client_qrcode.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage for fetching a single client's details via the word 'single', but it does not explicitly state when to use this tool versus alternatives, nor does it mention exclusions or prerequisites beyond the schema's reference to list_clients.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_client_configGet WireGuard client configurationARead-only
Get the WireGuard configuration file (wg .conf format) for a client. SENSITIVE: the output contains the client private key — treat it as a secret and do not repeat it unnecessarily.
| Name | Required | Description | Default |
|---|---|---|---|
| clientId | Yes | Numeric ID of the client (see list_clients) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotation already declares readOnlyHint=true, but the description adds critical security context: the output contains the client private key and should be treated as secret. It also discloses the output format (wg .conf), going beyond the structured annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the core function and a concise security warning. Every word earns its place with no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has a single parameter, read-only annotation, and no output schema. The description explains the return format ('wg .conf') and adds the vital sensitivity caveat, covering all essential context for correct use.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema fully describes clientId with 'Numeric ID of the client (see list_clients)', so baseline 3 applies. The description adds no extra parameter meaning, but none is needed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Get') and resource ('WireGuard configuration file (wg .conf format)') clearly distinguishing it from siblings like get_client_qrcode or get_client. It states exactly what is returned.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No explicit guidance on when to use this tool vs alternatives. The description does not mention get_client_qrcode, generate_one_time_link, or other related tools, so an agent receives no help choosing among them beyond the tool's name.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_client_qrcodeGet WireGuard client QR codeARead-only
Get the client configuration as a QR code (SVG markup) for scanning with the WireGuard mobile app. SENSITIVE: the QR code encodes the client private key — treat it as a secret.
| Name | Required | Description | Default |
|---|---|---|---|
| clientId | Yes | Numeric ID of the client (see list_clients) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses that the QR code encodes the client private key and explicitly warns to treat it as a secret, providing critical security context beyond the readOnlyHint annotation. It also specifies the output format as SVG markup, which is useful behavioral information.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two concise sentences: the first states the purpose and output format, the second delivers a necessary security warning. Every word adds value, with no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter read-only tool with full schema coverage, the description adequately covers the essential context: what it returns (SVG QR code), why it is used (mobile app scanning), and a critical sensitivity warning. The lack of an output schema is mitigated by the explicit mention of the return format.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The only parameter, clientId, is fully described in the schema with type, range, and a reference to list_clients. The description adds no extra parameter semantics, so the baseline of 3 is appropriate given 100% schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it retrieves the client configuration as a QR code in SVG markup for use with the WireGuard mobile app. The specific verb 'Get' plus the resource 'client QR code' distinguishes it from sibling tools like get_client_config, which returns plain configuration text.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'for scanning with the WireGuard mobile app' provides clear context on when to use this tool. However, it does not explicitly mention alternatives or state when not to use it, e.g., versus get_client_config, so it slightly misses the top score.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_server_infoGet wg-easy server infoARead-only
Get information about the wg-easy instance: release/update status, general settings and the WireGuard interface configuration. Secret fields (private keys, passwords) are redacted.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The readOnlyHint annotation already signals a read-only operation, and the description adds useful behavioral context by stating that secret fields (private keys, passwords) are redacted. This goes beyond the annotation and gives the agent important safety information.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise and front-loaded, consisting of two sentences that first state the main purpose and then add the redaction behavior. Every sentence earns its place with no wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple getter with no parameters and no output schema, the description covers the key information categories (release/update status, settings, interface config) and the redaction behavior. It is slightly limited in not detailing the return format, but this is a minor gap for a tool of this simplicity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so there is no parameter semantics to clarify. The description doesn't need to add anything here, which meets the baseline of 4 for parameter-less tools.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: to get information about the wg-easy instance, specifying categories such as release/update status, general settings, and WireGuard interface configuration. This distinguishes it from the sibling tools, which are all client-focused (list_clients, create_client, etc.).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies it is for server-level information, but it does not explicitly state when to use this tool over alternatives or mention any exclusions. Usage guidance is therefore implied rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_clientsList WireGuard clientsARead-only
List all WireGuard clients of the wg-easy instance, including their status (enabled/expired), addresses and traffic statistics.
| Name | Required | Description | Default |
|---|---|---|---|
| sort | No | Sort by name, ascending or descending | |
| filter | No | Optional name filter (substring match) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The readOnlyHint annotation already indicates a safe read operation, and the description adds meaningful context by specifying the returned fields (status, addresses, traffic statistics) and that it lists all clients. It does not overstate behavior or contradict annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, well-structured sentence that starts with the action and includes all essential information without unnecessary words. It wastes no space and is easy to parse.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a relatively simple list operation, the description plus schema and readOnlyHint provide enough context. It states the scope ('all clients') and the key return contents. A minor gap is the absence of a mention of response shape or array format, but this is not critical given no output schema and the straightforward nature of the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, with both 'sort' and 'filter' clearly documented in the input schema. The tool description adds no additional parameter-level detail, but the schema fully covers semantics, so the baseline score of 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('List') and clearly identifies the resource ('all WireGuard clients of the wg-easy instance') and the information returned (status, addresses, traffic statistics). This effectively distinguishes it from sibling tools like get_client, create_client, and delete_client.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'List all WireGuard clients' clearly conveys when to use this tool: when you need a complete overview of clients rather than a single client (get_client) or a mutating operation. However, it does not explicitly mention alternative tools or exclusion criteria, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_clientUpdate WireGuard clientA
Update a WireGuard client. Only the provided fields are changed; all other settings are preserved.
| Name | Required | Description | Default |
|---|---|---|---|
| dns | No | DNS servers for the client, or null to use the server default | |
| mtu | No | MTU for the client | |
| name | No | New display name | |
| enabled | No | Enable or disable the client | |
| clientId | Yes | Numeric ID of the client (see list_clients) | |
| expiresAt | No | Expiry date as ISO string, or null to remove the expiry | |
| allowedIps | No | CIDRs routed through the tunnel on the client side, or null to use the server default | |
| ipv4Address | No | IPv4 address of the client | |
| ipv6Address | No | IPv6 address of the client | |
| serverAllowedIps | No | Additional CIDRs the server routes to this client | |
| persistentKeepalive | No | Persistent keepalive interval in seconds (0 = off) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations are empty, so the description carries the burden of behavioral disclosure. It explicitly states that only provided fields are changed and all other settings are preserved, which is important partial-update semantics. It does not mention permissions or error behavior, so it is not a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences with no filler. The most important behavioral detail, partial updates with preservation of other settings, is included immediately after the purpose statement.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the core update semantics, but with no annotations and no output schema, there is no mention of return values, error behavior, or how this relates to dedicated enable/disable tools. For an 11-parameter mutation tool, slightly more context would be helpful.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all parameters. The description adds only the general partial-update concept and no per-parameter detail, which matches the baseline for full schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a clear verb and resource: 'Update a WireGuard client.' The added sentence about partial updates distinguishes it from create/delete/get tools and clarifies its scope. This is unambiguous and easily distinguished from sibling tools like create_client or delete_client.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage: use when you need to modify an existing client. However, it does not explicitly state when to prefer this over siblings like enable_client or disable_client, nor does it mention exclusions or alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
1 tool update
- Changed
update_client2 fields changed- removed
Input schema / properties / expiresAt / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Input schema / properties / expiresAt / typeAdded value: +[ + "string", + "null" +]
10 tool updates
v0.2.2- Changed
create_client1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
delete_client4 fields changed- removed
Input schema / additionalPropertiesRemoved value: -false - added
Input schema / properties / clientId / maximumAdded value: +9007199254740991 - removed
Input schema / properties / confirmRemoved value: -{ - "default": false, - "description": "Must be true to actually delete the client. Ask the user for confirmation first.", - "type": "boolean" -} - added
Input schema / properties / confirmTokenAdded value: +{ + "description": "Confirmation token from a previous delete_client call for the same client. Omit on the first call.", + "type": "string" +}
- Changed
disable_client2 fields changed- removed
Input schema / additionalPropertiesRemoved value: -false - added
Input schema / properties / clientId / maximumAdded value: +9007199254740991
- Changed
enable_client2 fields changed- removed
Input schema / additionalPropertiesRemoved value: -false - added
Input schema / properties / clientId / maximumAdded value: +9007199254740991
- Changed
generate_one_time_link2 fields changed- removed
Input schema / additionalPropertiesRemoved value: -false - added
Input schema / properties / clientId / maximumAdded value: +9007199254740991
- Changed
get_client2 fields changed- removed
Input schema / additionalPropertiesRemoved value: -false - added
Input schema / properties / clientId / maximumAdded value: +9007199254740991
- Changed
get_client_config2 fields changed- removed
Input schema / additionalPropertiesRemoved value: -false - added
Input schema / properties / clientId / maximumAdded value: +9007199254740991
- Changed
get_client_qrcode2 fields changed- removed
Input schema / additionalPropertiesRemoved value: -false - added
Input schema / properties / clientId / maximumAdded value: +9007199254740991
- Changed
list_clients1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
update_client8 fields changed- removed
Input schema / additionalPropertiesRemoved value: -false - added
Input schema / properties / clientId / maximumAdded value: +9007199254740991 - added
Input schema / properties / expiresAt / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Input schema / properties / expiresAt / typeRemoved value: -[ - "string", - "null" -] - added
Input schema / properties / mtu / maximumAdded value: +9007199254740991 - added
Input schema / properties / mtu / minimumAdded value: +-9007199254740991 - added
Input schema / properties / persistentKeepalive / maximumAdded value: +9007199254740991 - added
Input schema / properties / persistentKeepalive / minimumAdded value: +-9007199254740991
11 tool updates
v0.1.0- First observed
create_client - First observed
delete_client - First observed
disable_client - First observed
enable_client - First observed
generate_one_time_link - First observed
get_client - First observed
get_client_config - First observed
get_client_qrcode - First observed
get_server_info - First observed
list_clients - First observed
update_client
TDQS
Scored across 11 tools
Each tool maps cleanly to a distinct resource and action: client list/get/create/update/enable/disable/delete, server info, and three clearly separated config delivery methods (raw file, QR code, one-time link). Although multiple tools surface client configuration, their output formats and purposes are explicitly distinct.
All tool names follow a consistent verb_noun snake_case pattern, using standard lifecycle verbs (list, get, create, update, enable, disable, delete). Config-related tools are uniformly prefixed with get_client_ or generate_one_time_link, making the naming predictable.
Eleven tools is a well-scoped size for a WireGuard client management server: it covers the full client lifecycle plus configuration delivery and server info without redundancy. Each tool serves a distinct need and none feels like filler.
The surface provides complete CRUD lifecycle coverage for clients (list, get, create, update, delete) along with operational actions (enable/disable) that wg-easy users need. Configuration retrieval is covered in multiple practical formats, including a shareable one-time link, and server info rounds out the domain.
Maintenance
Related MCP Connectors
An MCP server that let you interact with Cycloid.io Internal Development Portal and Platform
MCP server wrapping the Tesla Fleet API and TeslaMate API
Tailscale device, route, DNS, key, user, and ACL management over MCP and CLI.
An MCP server that provides an API to LLMs to manage their JumpCloud resources.
Related MCP Servers
- AlicenseBqualityBmaintenanceMCP server to help manage a WHMCS installation.6239 npm20MIT
- AlicenseAqualityDmaintenanceMCP server that wraps CubeCoders AMP to list, inspect, and control game-server instances.84MIT
- AlicenseAqualityCmaintenanceMCP server for monitoring and controlling WiFi routers (OpenWrt) via SSH, enabling device listing, status checks, WiFi configuration, rebooting, and more.78 npmMIT
- AlicenseAqualityBmaintenanceMCP server for safely inspecting and operating a local Caddy admin API. Provides read-only tools and dry-run mutations for config management.7MIT