--- id: mcp 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 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. :::info Requires the MCP feature on your licence Agent access is a per-instance feature you enable on a paid plan. Without it, `/api/mcp` answers `feature_unavailable` and the token form refuses to mint a token carrying an `mcp:*` scope. See [Licensing and entitlements](../hq/licensing-and-entitlements.md). ::: ## Minting a token An agent authenticates the same way any script does: with an [API token](../reference/api-tokens.md). Two scopes govern what it can reach 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 read-only tools. - **`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 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 `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 can do once there. ## Restricting a token by tag An agent token benefits from the same [tag restriction](../reference/api-tokens.md#tag-restrictions) every API token can carry, and it is the main lever for keeping an agent's blast radius small. A token restricted to `env=staging` sees only staging hosts from `list_servers`, gets a not-found error asking about a production host by ID, and cannot run a workflow that would touch one. **Worked example: a staging-only agent token.** Create a token with `mcp:read`, `mcp:write`, `servers:read`, `workflows:read` and `workflows:write`, restrict it to `env=staging`, and give it to your agent. 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) below for why that refusal is all-or-nothing rather than "run on the servers it can see." ## Connecting a client 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 session does not count, only a real API token does. Point an MCP client at it with a configuration block like this: ```json { "mcpServers": { "vantage": { "type": "http", "url": "https://YOUR-INSTANCE/api/mcp", "headers": { "Authorization": "Bearer vt_your_token_here" } } } } ``` 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 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 surface is a presentation layer over existing authority, not a new one. | Tool | What it does | Scope required | | --- | --- | --- | | `list_servers` | List servers in the fleet, optionally filtered by tags. | `mcp:read`, `servers:read` | | `get_server` | Get one server's OS, online state and tags. | `mcp:read`, `servers:read` | | `list_monitors` | List monitors and their current state. | `mcp:read`, `monitors:read` | | `get_monitor_status` | Get one monitor's up/down/pending state, last check and last error. | `mcp:read`, `monitors:read` | | `list_incidents` | List monitor incidents (outages), most recent first. A tag-restricted token only sees metric alert incidents for servers inside its tags. | `mcp:read`, `monitors:read` | | `get_monitor_samples` | Get one monitor's recent raw check results. | `mcp:read`, `monitors:read` | | `list_workflows` | List workflows with step count, target count and whether each is scheduled. | `mcp:read`, `workflows:read` | | `get_workflow` | Get one workflow's ordered steps, targets and schedule. | `mcp:read`, `workflows:read` | | `get_run` | Get one workflow run's status and per-server counts. | `mcp:read`, `workflows:read` | | `get_run_logs` | Get the ordered log lines for one server within one run. | `mcp:read`, `workflows:read` | | `list_pending_updates` | List outstanding package updates, fleet-wide or for one server. | `mcp:read`, `servers:read` | | `list_vulnerabilities` | List known CVEs affecting the fleet, one row per CVE/package pair. | `mcp:read`, `vulns:read` | | `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` | | `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` | | `update_agent` | Trigger the Vantage agent on real servers to update itself. | `mcp:write`, `servers:write` | | `assign_key` | Assign an SSH key to real servers. | `mcp:write`, `keys:write` | | `create_step` | Create a reusable workflow step. | `mcp:write`, `workflows:write` | | `create_workflow` | Create a workflow from existing step IDs. | `mcp:write`, `workflows:write` | | `create_monitor` | Create a monitor, saved disabled. Supports every type, including `heartbeat` (the ping token is returned once) and `metric`. | `mcp:write`, `monitors:write` | A known limitation worth calling out on `search_fleet`: its `version_below` argument is not implemented. Passing it gets you an error explaining that correct version ordering depends on each host's own package manager (dpkg, rpm, apk each order versions differently), which the tool has no way to resolve, so it refuses rather than guess with a naive string comparison. Omit `version_below` and it returns every matching install instead, for you 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 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 be pointed at a workflow whose saved targets are staging-only from the start. ## What an agent can create Three tools let an agent propose new work rather than only look at existing work: `create_step`, `create_workflow` and `create_monitor`. Nothing they create is armed: - A created **workflow** cannot be given a schedule. `create_workflow` refuses outright if a `schedule` argument is present at all, so scheduling stays a separate, later decision a human makes in the UI. - A created **monitor** is always saved disabled, regardless of any `enabled` argument. It checks nothing and alerts nobody until a human turns it on. - A created **step** may not reference a secret. `create_step` refuses a `secret_refs` argument, because a script an agent wrote is not something you want run with credentials attached before a person has read it. `create_workflow` applies the same two checks `run_workflow` applies before a run: it refuses to save a workflow whose targets are not entirely inside the token's tag scope, and it refuses (absent `confirm:true`) to save one whose targets exceed the 25-server fan-out limit. A workflow this token could not later run is never created in the first place. Steps an agent creates are marked with a `source` of `mcp`, which the Vantage UI uses to badge them, so a human reviewing the step library can tell at a glance which scripts came from an agent rather than a person. There is no tool to update or delete an existing step, workflow or monitor. An agent can add to your instance; it cannot change or remove anything that was already there. ## What an agent cannot do This is the section to read before turning the feature on. - **It cannot see a secret's plaintext.** `list_secret_names` returns group and key names only; no tool anywhere in the MCP surface returns a secret's value to a model. - **It cannot open a console or a shell.** There is no MCP tool for the browser console, SSH, RDP or VNC. An agent can trigger a workflow step 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 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. - **It cannot act at all without `mcp:write`.** Every write tool needs it, on top of its own resource scope, and a token that lacks it never even 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 - 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 tools only add; there is no MCP tool that updates or removes a step, 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 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 - `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 so the two surfaces cannot end up disagreeing about what a restricted token learns. A workflow's target list is filtered more directly: servers outside the token's scope are dropped from what `get_workflow` reports, and a `targets_restricted` flag says that some were held back, without saying how many. ## Every call is audited 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 `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 has this agent added to my instance" should not require digging through hundreds of read rows to answer. See the [audit log](./audit-log.md) for how to read these alongside everything else on the instance.