chore: replace em dashes with hyphens, add no-em-dash rule to CLAUDE.md
Deploy / deploy (push) Successful in 2m47s
Deploy / deploy (push) Successful in 2m47s
This commit is contained in:
+22
-22
@@ -4,8 +4,8 @@ title: Agent access (MCP)
|
||||
sidebar_label: Agent access (MCP)
|
||||
---
|
||||
|
||||
The Model Context Protocol is how an LLM agent — Claude, or any other MCP
|
||||
client — talks to a tool server over a defined wire format instead of a
|
||||
The Model Context Protocol is how an LLM agent - Claude, or any other MCP
|
||||
client - talks to a tool server over a defined wire format instead of a
|
||||
one-off integration. Connecting Vantage to an agent this way lets it answer
|
||||
questions about your fleet and, if you choose to let it, act on it: run a
|
||||
workflow, apply updates, or draft a step for you to review.
|
||||
@@ -24,18 +24,18 @@ An agent authenticates the same way any script does: with an
|
||||
on the MCP endpoint specifically, on top of whatever resource scopes
|
||||
(`servers:read`, `workflows:write`, and so on) it also needs:
|
||||
|
||||
- **`mcp:read`** — permission to reach the MCP endpoint at all and use its
|
||||
- **`mcp:read`** - permission to reach the MCP endpoint at all and use its
|
||||
read-only tools.
|
||||
- **`mcp:write`** — permission to use its write tools: running a workflow,
|
||||
- **`mcp:write`** - permission to use its write tools: running a workflow,
|
||||
applying updates, assigning a key, or creating a step, workflow or monitor.
|
||||
|
||||
Without `mcp:write`, write tools are not merely refused when called — they
|
||||
Without `mcp:write`, write tools are not merely refused when called - they
|
||||
are left out of the tool listing entirely, so a read-only agent never learns
|
||||
they exist. A tool it cannot see is not one it can talk a user into asking
|
||||
for.
|
||||
|
||||
Each tool additionally needs the ordinary resource scope for what it touches
|
||||
— `run_workflow` needs `workflows:write`, `list_servers` needs
|
||||
- `run_workflow` needs `workflows:write`, `list_servers` needs
|
||||
`servers:read`, and so on, exactly as the [scopes table](../reference/api-tokens.md#scopes)
|
||||
describes for any other token. `mcp:read` or `mcp:write` decides whether the
|
||||
MCP surface is open to this token at all; the resource scope decides what it
|
||||
@@ -56,7 +56,7 @@ and cannot run a workflow that would touch one.
|
||||
It can list and inspect staging servers and run a workflow already
|
||||
configured to target them. Point it at a workflow whose saved targets
|
||||
include even one production host, though, and `run_workflow` refuses the
|
||||
call outright — see [what an agent cannot do](#what-an-agent-cannot-do)
|
||||
call outright - see [what an agent cannot do](#what-an-agent-cannot-do)
|
||||
below for why that refusal is all-or-nothing rather than "run on the
|
||||
servers it can see."
|
||||
|
||||
@@ -64,7 +64,7 @@ servers it can see."
|
||||
|
||||
Agent access lives at `/api/mcp` on your instance, over the streamable HTTP
|
||||
transport, and takes a bearer token the same way the REST API does. It has
|
||||
no separate authentication of its own — a browser tab with a signed-in
|
||||
no separate authentication of its own - a browser tab with a signed-in
|
||||
session does not count, only a real API token does.
|
||||
|
||||
Point an MCP client at it with a configuration block like this:
|
||||
@@ -83,15 +83,15 @@ Point an MCP client at it with a configuration block like this:
|
||||
|
||||
The endpoint is stateless and answers POST requests only: it holds no
|
||||
per-connection session, so each call is self-contained and there is nothing
|
||||
to resume. A GET request — a client probing for the endpoint, or a browser
|
||||
navigating to it directly — gets a `405`, by design, rather than a page. That
|
||||
to resume. A GET request - a client probing for the endpoint, or a browser
|
||||
navigating to it directly - gets a `405`, by design, rather than a page. That
|
||||
is the protocol-correct response from a server that does not offer the
|
||||
GET/SSE half of MCP, not a misconfiguration.
|
||||
|
||||
## The tool list
|
||||
|
||||
Every tool below calls the same service code the REST API and the Vantage
|
||||
UI call. Nothing in this list has its own path to the database — the MCP
|
||||
UI call. Nothing in this list has its own path to the database - the MCP
|
||||
surface is a presentation layer over existing authority, not a new one.
|
||||
|
||||
| Tool | What it does | Scope required |
|
||||
@@ -111,7 +111,7 @@ surface is a presentation layer over existing authority, not a new one.
|
||||
| `get_server_packages` | List installed packages on one server, optionally filtered by name. | `mcp:read`, `vulns:read` |
|
||||
| `search_fleet` | Search every server's installed packages by name across the fleet. | `mcp:read`, `vulns:read` |
|
||||
| `list_audit_events` | List recent audit log events. | `mcp:read`, `settings:read` |
|
||||
| `list_secret_names` | List secret group and key names — metadata only, never a value. | `mcp:read`, `secrets:read` |
|
||||
| `list_secret_names` | List secret group and key names - metadata only, never a value. | `mcp:read`, `secrets:read` |
|
||||
| `run_workflow` | Run a workflow against the servers it is already saved to target. | `mcp:write`, `workflows:write` |
|
||||
| `cancel_run` | Cancel an in-progress workflow run. | `mcp:write`, `workflows:write` |
|
||||
| `apply_updates` | Apply pending OS package updates on real servers. | `mcp:write`, `servers:write` |
|
||||
@@ -132,13 +132,13 @@ or the agent to filter.
|
||||
## Running a workflow: targets are fixed, not chosen
|
||||
|
||||
`run_workflow` does not take a target list. A workflow runs the servers and
|
||||
tags it was already saved with — an agent cannot redirect it at a different
|
||||
tags it was already saved with - an agent cannot redirect it at a different
|
||||
part of the fleet on the fly. Before starting the run, Vantage checks that
|
||||
those saved targets sit entirely inside the calling token's tag
|
||||
restriction. If even one of them falls outside it, the whole call is
|
||||
refused; there is no partial run against just the servers the token happens
|
||||
to be able to see. A token scoped to `env=staging` cannot use a
|
||||
production-and-staging workflow to touch only the staging half — it has to
|
||||
production-and-staging workflow to touch only the staging half - it has to
|
||||
be pointed at a workflow whose saved targets are staging-only from the
|
||||
start.
|
||||
|
||||
@@ -184,7 +184,7 @@ This is the section to read before turning the feature on.
|
||||
that runs a script, but it cannot open an interactive session on a
|
||||
machine.
|
||||
- **It cannot exceed its tag restriction.** Every tool that resolves
|
||||
servers — by ID, by tag, or through a saved workflow — resolves them
|
||||
servers - by ID, by tag, or through a saved workflow - resolves them
|
||||
through the token's own scope. A server outside that scope is invisible
|
||||
to `list_servers` and `get_server`, not-found to anything asking about it
|
||||
by ID, and excluded from what a write tool will act on.
|
||||
@@ -193,7 +193,7 @@ This is the section to read before turning the feature on.
|
||||
sees those tools in the listing.
|
||||
- **It cannot touch more than 25 servers in one call without saying so
|
||||
explicitly.** Any write tool that would affect more than 25 servers is
|
||||
refused with a message asking for `confirm:true` before it will proceed —
|
||||
refused with a message asking for `confirm:true` before it will proceed -
|
||||
cheap insurance against a mis-parsed tag selector reaching the whole
|
||||
fleet on one badly phrased instruction.
|
||||
- **It cannot edit or delete anything that already exists.** The creation
|
||||
@@ -201,10 +201,10 @@ This is the section to read before turning the feature on.
|
||||
workflow or monitor a person defined.
|
||||
|
||||
A monitor whose runner server sits outside a token's scope is not hidden
|
||||
from that token — it still appears in `list_monitors`. The REST API reaches
|
||||
from that token - it still appears in `list_monitors`. The REST API reaches
|
||||
that outcome by redacting: it replaces the runner with a
|
||||
`RunnerRestricted` placeholder when the runner falls outside the caller's
|
||||
visible servers. The MCP tools reach the same outcome a different way —
|
||||
visible servers. The MCP tools reach the same outcome a different way -
|
||||
`list_monitors` and `get_monitor_status` project a monitor onto a summary
|
||||
that never carries a runner field for any caller, restricted or not, so
|
||||
there is nothing there to redact in the first place. Two mechanisms, chosen
|
||||
@@ -219,16 +219,16 @@ many.
|
||||
Every tool call reaches the audit log, whether it read something or changed
|
||||
something:
|
||||
|
||||
- A successful call — read or write — is recorded as `mcp.tool_call`.
|
||||
- A call refused by policy — missing scope, an out-of-scope target, or a
|
||||
fan-out over 25 servers without confirmation — is recorded as
|
||||
- A successful call - read or write - is recorded as `mcp.tool_call`.
|
||||
- A call refused by policy - missing scope, an out-of-scope target, or a
|
||||
fan-out over 25 servers without confirmation - is recorded as
|
||||
`mcp.tool_denied`, naming the specific gate that refused it, so a human
|
||||
reading the log can tell "the agent was stopped" from "the agent tried
|
||||
and the underlying action failed."
|
||||
- A write tool that reached the underlying service and that service
|
||||
rejected is recorded as `mcp.tool_failed`.
|
||||
- Creating a step, workflow or monitor is recorded separately as
|
||||
`mcp.created`, naming what was made and its ID — a question like "what
|
||||
`mcp.created`, naming what was made and its ID - a question like "what
|
||||
has this agent added to my instance" should not require digging through
|
||||
hundreds of read rows to answer.
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ Port `465` uses implicit TLS; anything else uses STARTTLS.
|
||||
### Credentials are never read back
|
||||
|
||||
The SMTP `password`, the Telegram `token` and the webhook, Slack and Discord
|
||||
`url`s come back from `GET /api/channels` as `••••••••` — a webhook URL is the
|
||||
`url`s come back from `GET /api/channels` as `••••••••` - a webhook URL is the
|
||||
authorisation to post to that channel, so it is treated as a credential like
|
||||
the rest. Writing that value back unchanged keeps the stored one, which is what
|
||||
lets you rename a channel without retyping its password. Anything else you send
|
||||
|
||||
@@ -89,7 +89,7 @@ metrics is normal rather than a fault.
|
||||
|
||||
### OS updates
|
||||
|
||||
Agents check for pending package updates hourly and report the count — the
|
||||
Agents check for pending package updates hourly and report the count - the
|
||||
machine's own package manager on Linux, the Windows Update COM API on Windows.
|
||||
From the server page you can:
|
||||
|
||||
@@ -113,7 +113,7 @@ Opens a browser SSH, RDP or VNC session. See [Browser console](./browser-console
|
||||
Windows agents register, heartbeat, run workflow steps, report inventory,
|
||||
check and apply OS updates, report workloads (services and containers), and
|
||||
serve the browser console. They do not manage `authorized_keys`, and they are
|
||||
not covered by package inventory or CVE scanning — the vulnerability feeds
|
||||
not covered by package inventory or CVE scanning - the vulnerability feeds
|
||||
this project uses carry no Windows data.
|
||||
|
||||
## Removing a server
|
||||
|
||||
@@ -6,12 +6,12 @@ sidebar_label: Status pages
|
||||
|
||||
A status page is a public page reporting a chosen set of monitors as up-front
|
||||
components, with a 90-day history and an uptime percentage per component. It
|
||||
needs no session and no token to read — anyone with the link can open it,
|
||||
needs no session and no token to read - anyone with the link can open it,
|
||||
which is the point: it is what you hand a customer instead of an incident
|
||||
email.
|
||||
|
||||
Requires the **Status pages** licence feature. If the licence lapses, or the
|
||||
tier does not include the feature, the page keeps serving — it renders an
|
||||
tier does not include the feature, the page keeps serving - it renders an
|
||||
explanation rather than data or a broken page, so a customer who follows an
|
||||
old link never sees an error.
|
||||
|
||||
@@ -28,7 +28,7 @@ https://<your-vantage-address>/status/<page-id>
|
||||
On **Vantage Cloud** that address is your instance's own subdomain, so the page
|
||||
is at `https://<your-instance>.vantage.hostxtra.co.uk/status/<page-id>`.
|
||||
|
||||
On a **self-hosted** install it is whatever address you reach Vantage on —
|
||||
On a **self-hosted** install it is whatever address you reach Vantage on -
|
||||
`https://vantage.acme.com/status/<page-id>`, or an IP and port on a LAN
|
||||
install. A self-hosted install serves exactly one Vantage instance, so no
|
||||
subdomain is needed to say which one you mean. The **Copy** control next to the
|
||||
@@ -37,26 +37,26 @@ the one to hand out.
|
||||
|
||||
**The page id cannot be changed after creation.** Once you have shared the
|
||||
link, changing the id would break it, so pick something you would still be
|
||||
happy with in a year — `platform`, `api`, a customer's own name for a
|
||||
happy with in a year - `platform`, `api`, a customer's own name for a
|
||||
dedicated page.
|
||||
|
||||
## Draft versus published
|
||||
|
||||
A new page starts unpublished. Unpublished pages answer *not found* to
|
||||
anyone who requests them, including you, from a browser without a session —
|
||||
anyone who requests them, including you, from a browser without a session -
|
||||
so you can build out the components and copy before announcing it. Toggle
|
||||
**Published** when it is ready. Un-publishing later takes it back to *not
|
||||
found* rather than deleting anything.
|
||||
|
||||
**Delete page**, in the editor header, is the only way to correct a page id you
|
||||
regret — the id is fixed once created. It takes the page, its sections and its
|
||||
regret - the id is fixed once created. It takes the page, its sections and its
|
||||
authored incidents with it; monitors and their history are untouched. If you
|
||||
only want the page off the internet, un-publish it instead.
|
||||
|
||||
## Sections and components
|
||||
|
||||
A page is organised into **sections** — arbitrary groupings such as "API" or
|
||||
"Region: EU" — each holding one or more **components**. A component is a
|
||||
A page is organised into **sections** - arbitrary groupings such as "API" or
|
||||
"Region: EU" - each holding one or more **components**. A component is a
|
||||
monitor plus a **display name** you choose for this page.
|
||||
|
||||
The display name is never the monitor's own name unless you type it in. An
|
||||
@@ -64,7 +64,7 @@ internal monitor name ("prod-db-primary-eu1") is rarely what you want a
|
||||
customer reading; give it whatever name makes sense to them, and change it
|
||||
for a different page without touching the monitor.
|
||||
|
||||
If a monitor listed on a page is later deleted, its component still appears —
|
||||
If a monitor listed on a page is later deleted, its component still appears -
|
||||
reading `Unknown` rather than up or down, because nothing is checking it any
|
||||
more and claiming otherwise would be a false claim of health.
|
||||
|
||||
@@ -77,7 +77,7 @@ more and claiming otherwise would be a false claim of health.
|
||||
- A 90-day history bar per component.
|
||||
- Any active incidents, upcoming maintenance, and a rolling history of both.
|
||||
- An optional banner across the top of the page, for anything you want said
|
||||
regardless of component state. It is one notice with one appearance — there
|
||||
regardless of component state. It is one notice with one appearance - there
|
||||
are no severity levels to choose between.
|
||||
|
||||
A visitor never sees a target URL, host or port, the check's expected status
|
||||
@@ -90,17 +90,17 @@ reachable is on this page.
|
||||
|
||||
Two kinds of entries appear on a page's timeline:
|
||||
|
||||
- **Automatic** — a monitor going down opens an incident on any page that
|
||||
- **Automatic** - a monitor going down opens an incident on any page that
|
||||
lists it, with no action from you. These appear the moment the monitor's
|
||||
state changes and close the moment it recovers.
|
||||
- **Authored** — an incident or maintenance window you create by hand, with
|
||||
- **Authored** - an incident or maintenance window you create by hand, with
|
||||
its own title, impact and a set of affected components you choose. You
|
||||
post updates to it (Investigating → Identified → Monitoring → Resolved) as
|
||||
the situation develops, and each update is timestamped and kept on the
|
||||
page's history.
|
||||
|
||||
An authored incident is attached to one or more pages explicitly when you
|
||||
create it — it does not follow a monitor onto every page that monitor happens
|
||||
create it - it does not follow a monitor onto every page that monitor happens
|
||||
to be listed on.
|
||||
|
||||
### Scheduling maintenance
|
||||
@@ -111,7 +111,7 @@ is in progress and its affected components are within the scheduled time,
|
||||
those components are drawn as "under maintenance" instead of up or down.
|
||||
|
||||
**Maintenance changes how a day is drawn, never the uptime number itself.**
|
||||
The 90-day percentage is computed from what actually happened — a component
|
||||
The 90-day percentage is computed from what actually happened - a component
|
||||
that stayed up throughout a maintenance window still shows as up in its
|
||||
history, it is only the live status pill that reads "under maintenance" for
|
||||
the duration.
|
||||
@@ -120,6 +120,6 @@ the duration.
|
||||
|
||||
A visitor's read of a page is cached for up to 30 seconds, so posting an
|
||||
update or flipping Published does not necessarily change what a visitor sees
|
||||
instantly — though most authoring actions invalidate that cache immediately,
|
||||
instantly - though most authoring actions invalidate that cache immediately,
|
||||
so in practice it usually shows within a second or two. If a change genuinely
|
||||
does not appear, reloading after 30 seconds always will.
|
||||
|
||||
@@ -4,7 +4,7 @@ title: Workloads
|
||||
sidebar_label: Workloads
|
||||
---
|
||||
|
||||
A **workload** is one Docker container or one service — a systemd unit on
|
||||
A **workload** is one Docker container or one service - a systemd unit on
|
||||
Linux, a Windows service on Windows. Every server reports what it is running,
|
||||
and you can start, stop and restart those workloads, and read their recent
|
||||
logs, without opening a console.
|
||||
|
||||
Reference in New Issue
Block a user