MCP Server
Breeze exposes a built-in Model Context Protocol (MCP) server that lets external AI clients – Claude Desktop, Cursor, and any other MCP-compatible application – interact directly with your fleet. Instead of copying data between dashboards and chat windows, you can ask an AI assistant to query devices, triage alerts, run scripts, and manage patches, all within the client you already use.
The MCP server reuses the same tools, guardrails, and audit infrastructure as the built-in Breeze AI assistant, so every operation is scoped to your organization and governed by the same tiered permission model.
How MCP works
Section titled “How MCP works”The Model Context Protocol is an open standard that defines how AI applications discover and invoke tools and resources hosted by external servers. A single MCP connection gives the AI client a live catalogue of everything it can do inside Breeze – no custom plugin code required.
Breeze implements the 2024-11-05 protocol version with three capabilities:
| Capability | Description |
|---|---|
| Tools | 100+ callable operations across device management, monitoring, alerting, compliance, backups, configuration policies, ticketing, billing, analytics, and more |
| Resources | Read-only data endpoints (breeze://devices, breeze://alerts, etc.) |
| Prompts | Five guided MSP workflows the AI client can launch as ready-made starting points (see Guided prompts) |
On connection the server also returns an instructions string describing the Partner → Organization → Site → Device Group → Device hierarchy and a read-before-write approach, so the AI client orients itself to your tenant before it acts.
Transport
Section titled “Transport”Breeze speaks both remote MCP transports on the same path, /api/v1/mcp/sse:
| Transport | How | Protocol revisions | Use it when |
|---|---|---|---|
| Streamable HTTP (recommended) | POST /api/v1/mcp/sse with a JSON-RPC body; the server answers inline JSON and issues an Mcp-Session-Id on initialize. Echo it on every later request, together with the negotiated MCP-Protocol-Version header. |
2025-03-26, 2025-06-18, 2025-11-25, and 2024-11-05 (negotiated on initialize) |
Clients that support Streamable HTTP. |
| HTTP + SSE (legacy) | GET /api/v1/mcp/sse opens the event stream; the endpoint event names /api/v1/mcp/message for POSTs. |
2024-11-05 | Older clients that only offer an “SSE” transport option. |
initialize reports the negotiated protocolVersion, serverInfo.version (the Breeze API release), and instructions. Every tool in tools/list carries a title, MCP annotations (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) derived from Breeze’s guardrail classifications, and _meta["app.breeze/domain"]. Annotations are hints for clients; they do not bypass permissions or approval checks.
Successful object results from tools/call include structuredContent matching the redacted JSON in the text block. Pure error objects, non-object results (arrays, scalars, and plain text), and summarized results omit structuredContent. The _chat.outputCompacted marker identifies a shortened list; natively shaped results retain structuredContent.
Batch requests are not supported for any negotiated protocol version. Send one JSON-RPC request per POST. DELETE /api/v1/mcp/sse currently returns 204 as a no-op; it does not end a session.
tools/list returns a single page by default. Pagination is experimental and enabled only when the operator sets MCP_TOOLS_LIST_PAGE_SIZE to a positive value. Every connected client must follow nextCursor until it is absent. Cursors are offsets, so a catalog change mid-enumeration can skip tools; tenant-tool discovery failures can also change the list between pages.
Finding your MCP URL
Section titled “Finding your MCP URL”Your tenant’s MCP SSE endpoint is surfaced in two places in the dashboard so you can copy it without having to construct it by hand:
- Login page — a compact “Connecting an AI agent?” card sits below the sign-in form, so you can grab the URL before you authenticate.
- Settings → Connected Apps — a full card above the apps list with a one-click copy button, ready to paste into Claude.ai, ChatGPT, Cursor, or any other MCP-compatible client.
Both cards resolve the URL from the tenant you’re connecting to, so what’s shown is what the AI client should use.
Authentication
Section titled “Authentication”All MCP endpoints require an API key with ai:* scopes. Authentication is performed via the X-API-Key header.
Creating an API key
Section titled “Creating an API key”-
Navigate to Settings > API Keys in the Breeze dashboard.
-
Click Create API Key.
-
Enter a descriptive name (e.g., “Claude Desktop MCP”).
-
Select the scopes you need (see Scope-based access control below).
-
Copy the generated key (it starts with
brz_). You will not be able to see it again.
Key format
Section titled “Key format”API keys follow the format brz_<random>. The server stores only a SHA-256 hash of the key; the plaintext is never persisted.
OAuth 2.1
Section titled “OAuth 2.1”For AI clients that implement OAuth 2.1 with Dynamic Client Registration (DCR) and PKCE — including Claude.ai and ChatGPT — Breeze can authorize MCP connections through a real OAuth handshake instead of pasted API keys. The user pastes the tenant’s /mcp/sse URL into the client, signs in to Breeze, grants the AI client a scoped session, and the assistant receives a refreshable access token bound to that user and organization. No API key is created or copied.
OAuth is enabled with the MCP_OAUTH_ENABLED=true server setting. Once enabled, Breeze acts as a standards-compliant authorization server and advertises the following endpoints (all discoverable automatically by the client — there is nothing to configure by hand):
| Endpoint | Purpose |
|---|---|
/.well-known/oauth-authorization-server |
Authorization server metadata (RFC 8414) — issuer, endpoints, supported scopes and grant types |
/.well-known/oauth-protected-resource |
Protected-resource metadata (RFC 9728) — tells the client which authorization server protects the MCP endpoint |
/.well-known/jwks.json |
Public signing keys for validating access tokens |
/oauth/auth |
Authorization endpoint — user sign-in and consent (PKCE S256 required) |
/oauth/token |
Token endpoint — authorization_code and refresh_token grants |
/oauth/reg |
Dynamic Client Registration (RFC 7591) — clients self-register |
/oauth/token/revocation |
Token revocation (RFC 7009) |
/oauth/token/introspection |
Token introspection (RFC 7662) |
Registered redirect URIs must be HTTPS, with one exception: native/CLI clients that bind a local callback server may register plain-HTTP loopback redirect URIs — http://127.0.0.1:<port>, http://[::1]:<port>, or http://localhost:<port> (any port, any path). This is what lets Claude Code (the CLI) complete its OAuth handshake; hostname matching is exact, so lookalike hosts such as localhost.example.com are still rejected.
OAuth sessions use the mcp:read, mcp:write, and mcp:execute scopes (plus openid and offline_access), which map to the same Tier 1 / 2 / 3 model as the ai:* API-key scopes. Access tokens are short-lived (30 minutes) and signed with EdDSA; refresh tokens last 14 days and rotate on every use. Revoking a grant invalidates all of its sibling access and refresh tokens immediately.
When OAuth is enabled, the MCP endpoints accept an Authorization: Bearer <access_token> header and return a WWW-Authenticate challenge pointing OAuth-capable clients at the protected-resource metadata.
API key authentication via the X-API-Key header continues to work for every client, and remains the only option for self-hosted MCP clients (Claude Desktop, Cursor) that don’t implement OAuth.
Partner service principal keys (partner-wide automation)
Section titled “Partner service principal keys (partner-wide automation)”An organization API key reaches one organization, and an OAuth grant is tied to a signed-in user and expires. For an unattended automation that must work across every organization of the partner, use a partner service principal (the Service principals settings page, /settings/partner-service-principals) and send its brz_sp_… key in the same X-API-Key header.
- The principal must be granted the opt-in MCP scope
ai:read, plusai:write,ai:executeorai:execute_adminas needed (each of those requiresai:read; a principal holding one without it is rejected when saved and refused by the MCP endpoint). They are never selected by default, they grant nothing on the Partner API, and a principal without one is refused by the MCP endpoint with 403. - The key runs at partner scope: every organization of the partner, plus partner-wide rows, the same reach a partner admin’s own partner-scope OAuth session has.
- Per-tool permission checks use the live partner role of the principal’s owner (the admin who created it), exactly as they do for an organization service principal key. Only the owner can issue or rotate the principal’s keys, re-enable it or widen it (another partner-wide admin can still revoke a key, disable the principal or narrow it), and the MCP endpoint accepts only keys the owner issued. See Who can manage a partner service principal.
- The key is bound to the owner’s sign-in credential, like the owner’s own API keys: the owner’s password change or reset, an administrator changing the owner’s account status, or a change to the owner’s MFA factors ends every key issued before it. Signing out does not. Removing the owner from the partner, narrowing them below all-organization access, or reducing their role below the principal’s MCP scopes also denies the next request (the reach is never silently narrowed). The key never acts as the owner in the database: it cannot read the owner’s own sessions or other user-private rows, and its per-tool rate limits are its own.
- A partner service principal cannot create approval intents or queue cloud-to-cloud restores (those need a re-authorizable principal); such calls are refused.
- Revoking the key, disabling the principal, letting either expire, or a source-CIDR mismatch denies the next request. Nothing is cached.
- Tier 3 stays approval-only unless the operator names the principal in
MCP_UNATTENDED_TIER3_PRINCIPALSaspartner_sp:<principal id>. The entry names the principal, not a key, so rotating keys keeps it. It applies in every organization the principal reaches. - The list of ticket actions the Partner API’s
tickets:writescope withholds (among them moving a ticket between organizations and editing or deleting comments) applies to the Partner API only. Over MCP the same key reaches themanage_ticketstool under its owner’s live role and the usual tiers: internal notes and comment edits or deletes are Tier 2 (ai:write), while a customer-visible reply and moving a ticket between organizations are Tier 3 and stay approval-gated unless the principal is listed inMCP_UNATTENDED_TIER3_PRINCIPALS. - Every JSON-RPC request (initialize, tools/list, tools/call and so on) and every tool call is audited with the key id as actor (requests refused at authentication, and the SSE stream open/close transport requests, are not) and
principalType: "partner_service_principal"plus the principal id in the details. - It is an API-key-class machine caller everywhere else too: actions that require an interactive user session (for example authoring maintenance links) are refused, and its Tier 3 execution ledger names the principal and opens no user-owned session.
Connecting Claude Code
Section titled “Connecting Claude Code”claude mcp add --transport http breeze-rmm https://your-breeze-instance.example.com/api/v1/mcp/sse \ --header "X-API-Key: brz_your_api_key_here"This URL is the Streamable HTTP endpoint; the client negotiates the protocol version automatically.
Connecting Claude Desktop
Section titled “Connecting Claude Desktop”-
Open Claude Desktop and go to Settings > Developer > Edit Config (or directly edit
~/Library/Application Support/Claude/claude_desktop_config.jsonon macOS). -
Add the Breeze MCP server to the
mcpServerssection: -
Restart Claude Desktop. You should see the Breeze tools appear in the tool picker.
{ "mcpServers": { "breeze": { "url": "https://your-breeze-instance.example.com/api/v1/mcp/sse", "headers": { "X-API-Key": "brz_your_api_key_here" } } }}{ "mcpServers": { "breeze": { "url": "https://your-breeze-instance.example.com/api/v1/mcp/sse", "headers": { "X-API-Key": "brz_your_api_key_here" } } }}The config file on Windows is located at %APPDATA%\Claude\claude_desktop_config.json.
This URL is the Streamable HTTP endpoint; the client negotiates the protocol version automatically.
Connecting Cursor
Section titled “Connecting Cursor”-
Open Cursor and go to Settings > MCP.
-
Click + Add new global MCP server.
-
Enter the following configuration:
-
Save and reload the window. Breeze tools will appear in Cursor’s agent mode.
{ "mcpServers": { "breeze": { "url": "https://your-breeze-instance.example.com/api/v1/mcp/sse", "headers": { "X-API-Key": "brz_your_api_key_here" } } }}This URL is the Streamable HTTP endpoint; the client negotiates the protocol version automatically.
Available tools
Section titled “Available tools”Tools are the primary way AI clients interact with Breeze. Each tool maps to one or more fleet operations and is classified into a tier that determines how it executes. The MCP server filters the tool list based on the scopes attached to your API key – you only see tools you are authorized to use.
List tools return hasMore and nextCursor in their result; when hasMore is true, call the same tool again with cursor set to the previous nextCursor and the same filters to get the next page. A result whose payload was too large to return in full is stored as an artifact and comes back with an artifact.handle instead – read_artifact pages through that stored copy by byte offset. read_artifact only reads an artifact captured by your own chat session or agent run, and capture itself only happens on hosted deployments with the AI workspace enabled or, since v0.117.0, on a self-hosted deployment that opts in with BREEZE_AI_ARTIFACT_CAPTURE_ENABLED (see Environment → AI); otherwise an oversized result is truncated; an MCP tools/call has no session to anchor a capture to, so read_artifact over MCP usually returns no_capture_anchor today. It is listed for forward compatibility while MCP-side capture anchoring is designed.
Device tools
Section titled “Device tools”| Tool | Tier | Description |
|---|---|---|
query_devices |
1 | Search and filter devices by status, OS, site, tags, or hostname. Returns summary list with totals. |
get_device_details |
1 | Comprehensive device detail including hardware specs, network interfaces, disks, and recent metrics. |
analyze_metrics |
1 | Query CPU, RAM, disk, and network time-series data with aggregation (raw, hourly, daily). Up to 168 hours back. |
get_active_users |
1 | Active user sessions per device or fleet-wide, with reboot safety signals based on idle thresholds. |
get_user_experience_metrics |
1 | Login performance and session behavior trends per device or user over time. |
get_device_context |
1 | Retrieve AI memory about a device – known issues, quirks, follow-ups, and preferences from past interactions. |
set_device_context |
2 | Record new context/memory about a device for future conversations. |
resolve_device_context |
2 | Mark a context entry as resolved. Hidden from active context but preserved in history. |
Alert tools
Section titled “Alert tools”| Tool | Tier | Description |
|---|---|---|
manage_alerts |
1/2 | List, get, acknowledge, or resolve alerts. List/get are Tier 1; acknowledge/resolve are Tier 2. |
manage_alert_rules |
1/2/3 | Full alert rule lifecycle: list, get, create, update, delete rules; test rules; list notification channels; alert summary. Create/update are Tier 2; delete is Tier 3. |
list_incidents |
1 | List incidents by organization, status, severity, classification, assignee, or date range. |
list_remediation_suggestions |
1 | List remediation suggestions by organization, source, device, or status, limited to accessible devices. |
find_proven_fixes |
1 | Find fixes proven by observed outcomes on the same alert, anomaly episode, or a device plus a structured alert condition (deviceId + problem, never free text) across your clients, with track records (counts only — never another client’s details). |
Command and script execution
Section titled “Command and script execution”| Tool | Tier | Description |
|---|---|---|
execute_command |
3 | Execute system commands (list processes, manage services, file operations, event log queries) on a device. |
run_script |
3 | Execute a script by ID on up to 10 devices. |
manage_services |
3 | List, start, stop, or restart system services on a device. List is Tier 2; start/stop/restart are Tier 3. |
Since v0.117.0 an organization-scoped API key can also see and run its partner’s partner-wide scripts (those created for “All organizations”) through list_scripts, get_script_details and run_script. This is read access only; the key gains no new write access, and it never sees another partner’s scripts.
To make sure run_script executes exactly the content you reviewed, take contentSha256 from get_script_details and pass it to run_script as expectedContentSha256. If the script was edited in between, run_script refuses with script_content_mismatch before anything is dispatched. Omit the field to keep the previous behavior.
File and disk tools
Section titled “File and disk tools”| Tool | Tier | Description |
|---|---|---|
file_operations |
2/3 | List directory contents (Tier 2, auto-execute + audit). Read, write, delete, mkdir, rename (Tier 3 — reading file contents requires approval because the agent reads as root/LocalSystem). |
analyze_disk_usage |
1/2 | Filesystem analysis with cleanup candidate detection, for any fixed volume on the device (path), from the latest stored scan (Tier 1). refresh: true runs a fresh scan on an online device (Tier 2: needs ai:write and the devices:execute permission). |
disk_cleanup |
1/3 | Preview cleanup candidates for a volume (Tier 1). Execute cleanup by deleting selected paths from a previewed run (Tier 3). |
system_cleanup |
1/2/3 | List the OS-native maintenance cleaners a device offers — Windows Disk Cleanup handlers and DISM component cleanup, macOS local snapshots and Homebrew, Linux package caches and journal — with an “up to” reclaim estimate (Tier 2: list asks the device live, so it needs ai:write and devices:execute). Run the selected actions (Tier 3); run returns a cleanupRunId immediately and status (Tier 1) reads the run’s progress and result. |
Security tools
Section titled “Security tools”| Tool | Tier | Description |
|---|---|---|
security_scan |
3 | Run security scans, check status, quarantine/remove/restore threats. |
get_security_posture |
1 | Fleet-wide or device-level security posture scores with factor breakdowns and recommendations. |
Fleet orchestration tools
Section titled “Fleet orchestration tools”| Tool | Tier | Description |
|---|---|---|
manage_configuration_policy |
2/3 | Configuration policy lifecycle: activate/deactivate (Tier 2); create, update, delete (Tier 3). |
configuration_policy_compliance |
1 | Compliance status: org-wide summary across all configuration policies, or per-device status for one policy. |
manage_deployments |
1/2/3 | Staged software deployments: list, get, device status, create, start, pause, resume, cancel. |
manage_patches |
1/2/3 | Patch management. List and compliance are Tier 1. Scan, approve/decline/defer, and bulk approve are Tier 2. Install and rollback are Tier 3. |
manage_groups |
1/2/3 | Device group lifecycle: list, get, preview dynamic filters, membership log, create, update, delete, add/remove devices. |
manage_maintenance_windows |
1/2/3 | Maintenance windows: list, get, check active, create, update, delete. |
manage_automations |
1/2/3 | Automation lifecycle: list, get, history, create, update, delete, enable, disable, manual run. |
generate_report |
1/2 | Reports: list, generate, get report data, create/update/delete definitions, view history. |
list_org_contacts |
1 | List an organization’s contacts by site or role, respecting organization and site access. |
list_sites |
1 | List accessible sites with device counts, optionally filtered by organization or name. |
get_site |
1 | Retrieve one accessible site’s details by site ID. |
list_ai_agents |
1 | List accessible AI agents and their enabled state, optionally including disabled agents. |
list_ai_agent_runs |
1 | List recent accessible AI agent runs with status, verdict, and cost, filtered by agent or organization. |
get_ai_agent_run |
1 | Retrieve one accessible AI agent run’s trace by run ID. |
Network tools
Section titled “Network tools”| Tool | Tier | Description |
|---|---|---|
network_discovery |
3 | Initiate a network discovery scan from a device (ping, ARP, or full). |
list_network_assets |
1 | List discovered network assets by organization, site, approval status, asset type, or linked device. |
get_network_asset |
1 | Retrieve one accessible discovered network asset’s identity, addresses, linked device, and last-seen details. |
Diagnostic tools
Section titled “Diagnostic tools”| Tool | Tier | Description |
|---|---|---|
search_agent_logs |
1 | Search agent diagnostic logs with filters for device, level, component, time range, and message text. |
set_agent_log_level |
2 | Temporarily increase agent log shipping verbosity for debugging. Auto-reverts after a set duration. |
query_audit_log |
1 | Search the audit log for recent actions, filterable by action, resource, actor type, and time range. |
read_artifact |
1 | Page through a stored large tool-result artifact by byte offset, using the artifact.handle from an earlier result. Scoped to your own session or agent run; see the note above “Available tools”. |
Configuration policy tools
Section titled “Configuration policy tools”| Tool | Tier | Description |
|---|---|---|
list_configuration_policies |
1 | List configuration policies with linked feature types. |
get_effective_configuration |
1 | Resolve the effective configuration for a device through the full hierarchy (device > group > site > org > partner). |
preview_configuration_change |
1 | Preview how adding or removing policy assignments would change effective configuration. |
apply_configuration_policy |
2 | Assign a configuration policy to a target in the hierarchy. |
remove_configuration_policy_assignment |
2 | Remove a configuration policy assignment. |
Ticketing tools
Section titled “Ticketing tools”| Tool | Tier | Description |
|---|---|---|
manage_tickets |
1/2/3 | Full ticket lifecycle. List/get are Tier 1. Create, comment, assign, update status, update fields (subject, description, category, priority, due date, tags, device, submitter), link/unlink alerts, create a ticket from an alert, edit/delete comments, and time tracking (log time entry, start/stop timer) are Tier 2. Moving a ticket to a different organization is Tier 3. |
manage_ticket_checklist |
1/2 | Ticket checklist steps and checklist templates. Listing steps and listing or reading templates are Tier 1. Adding, editing (label and detail), deleting and reordering steps, and applying a template, are Tier 2. Requires partner or system scope. Ticking a step done is not available to tools, and a step that is already ticked cannot be edited or deleted through the tool. |
list_time_entries |
1 | List logged work by ticket, user, organization, dates, billing status, or approval. Requires partner or system scope; non-admins see their own entries. |
get_running_timer |
1 | Retrieve the caller’s running time entry, or null when no timer is running. Requires partner or system scope and a user principal. |
get_ticket_approval_status |
1 | Read a ticket’s budget and customer-approval status: consumed and held time, approved extensions and requests. Read-only; there is no tool to decide, record consent, write off or set a budget. Requires partner or system scope. |
get_timesheet |
1 | Retrieve a weekly timesheet. Requires partner or system scope and a user principal; another user’s timesheet requires admin access. |
Billing tools
Section titled “Billing tools”| Tool | Tier | Description |
|---|---|---|
list_invoices |
2 | List invoices across the organizations you can access, newest first, with optional organization and status filters. |
get_invoice |
2 | Full accounting view of one invoice — header plus all line items. |
manage_invoices |
2/3 | Invoice lifecycle. Draft create/update/delete, line management (manual, catalog, bundle, and contract lines), assembling a draft from an organization’s billable period or from a ticket’s billable time and parts, and creating a Stripe payment link are Tier 2. Financial finalization — issue, void, record payment, void payment — is Tier 3. |
search_catalog |
2 | Search the partner product catalog (hardware, software, services, bundles) by name or type. Returns prices as {currencyCode, unitPrice} entries; optional currencyCode filters to items with a price in that currency. |
get_catalog_item |
2 | Full detail for one catalog item, including bundle components and prices per currency. |
lookup_distributor_product |
2 | Live TD SYNNEX (EC Express) price & availability lookup for a single distributor SKU or manufacturer part number that isn’t in your catalog yet — returns reseller cost, MSRP, currency, total stock, and per-warehouse availability. Partner-scoped: organization-scoped callers get availability but reseller cost is redacted, and a caller with no partner in context receives an error with no outbound call made. |
manage_catalog |
2 | Catalog maintenance: create, update, and archive items; manage bundle components; and set or remove currency-specific organization overrides. set_price and remove_price manage price-book rows with catalogId and currencyCode; set_price also takes price: { unitPrice }. All actions are Tier 2. |
list_contracts |
2 | List recurring contracts across accessible organizations, newest first, with optional filters. |
get_contract |
2 | Full contract view — header, lines, and billing-period history. |
manage_contracts |
2/3 | Contract lifecycle. Draft create/update/delete and line management are Tier 2. Lifecycle state changes — activate, pause, resume, cancel — are Tier 3. |
manage_quotes |
2/3 | Quote/proposal lifecycle. Draft, content-block, and line management, declining a quote, and creating a payment link are Tier 2. Sending a quote to the customer is Tier 3. |
Available resources
Section titled “Available resources”Resources provide read-only access to fleet data via the resources/read JSON-RPC method. The AI client can read these to get context before invoking tools.
| URI | Name | Description | Limit |
|---|---|---|---|
breeze://devices |
Device Inventory | All managed devices with hostname, status, OS, agent version, and last seen time. | 500 |
breeze://alerts |
Active Alerts | Currently active alerts with title, severity, status, and device ID. | 200 |
breeze://scripts |
Script Library | Available scripts with name, description, language, and category. | 200 |
breeze://automations |
Automation Rules | Configured automation rules with name, description, enabled state, and trigger. | 200 |
breeze://devices/{id} |
Device Detail | Full detail for a specific device by UUID. | 1 |
All resource reads are org-scoped – the API key’s organization determines which records are visible.
Guided prompts
Section titled “Guided prompts”Beyond individual tools, the server advertises five guided prompts – ready-made workflows for the tasks MSPs run most often. An AI client lists them with prompts/list and expands one with prompts/get; the prompt returns a structured starting message (with any argument you supply filled in) that steers the assistant through the right sequence of tools. They’re a fast on-ramp: rather than describing a whole procedure, you pick the workflow and let it drive.
| Prompt | What it does | Arguments |
|---|---|---|
breeze-fleet-triage |
A read-only health sweep of the fleet – surfaces what needs attention right now. | scope (optional org or site) |
breeze-device-investigate |
Deep-dives a single device and summarizes the likely root cause (read-only). | device (name, hostname, or ID) |
breeze-patch-remediate |
Safely patches a target – scan, confirm, then apply. Touches higher-risk execute tools, so it stays approval-gated. | target (device, site, or org) |
breeze-incident-kickoff |
Opens and structures an incident with a timeline, evidence collection, and a report. | summary (required), scope (optional) |
breeze-turnkey-setup |
An opinionated baseline-configuration wizard – partner-wide by default so a policy applies across every organization. | scope (optional; defaults to partner-wide) |
Prompts don’t bypass permissions or approvals: every tool a prompt drives is still subject to your API key’s scopes, the step-up approval flow, and the production allowlist.
Scope-based access control
Section titled “Scope-based access control”The tools visible and callable through MCP are determined by the scopes on your API key. Breeze uses a four-scope model for AI operations:
| Scope | Access level | Tools exposed |
|---|---|---|
ai:read |
Read-only fleet data | All Tier 1 tools (query devices, analyze metrics, list alerts, etc.) and all resources. Required for SSE connection. Reads that send a live command to a device (listing its processes, services, scheduled tasks or files, a fresh disk scan) are Tier 2 and need ai:write. |
ai:write |
Low-risk mutations | All Tier 1 + Tier 2 tools (acknowledge alerts, set device context, apply configuration policies, etc.) |
ai:execute |
Destructive operations | All Tier 1 + 2 + 3 tools (execute commands, run scripts, security scans, etc.) |
ai:execute_admin |
Production-hardened execute | Same as ai:execute, but required when the MCP_REQUIRE_EXECUTE_ADMIN flag is enabled in production. |
A wildcard scope (*) grants access to all tools.
How scope filtering works
Section titled “How scope filtering works”When a client calls tools/list, the server filters the full tool catalogue:
- Tier 1 tools (read-only) are always included if the key has
ai:read. - Tier 2 tools are included if the key has
ai:write(orai:execute, which implies write). - Tier 3+ tools are included only when some action of them is below Tier 3 and the key has
ai:execute; a wholly Tier 3 tool is never listed — except for a principal named inMCP_UNATTENDED_TIER3_PRINCIPALSthat also passes theai:execute/ai:execute_admin/ production-allowlist gates for that tool. - A Tier 3 action is refused over MCP regardless of scope — see the caution above — unless the caller is a principal named in
MCP_UNATTENDED_TIER3_PRINCIPALS. For every other principal,MCP_REQUIRE_EXECUTE_ADMINandMCP_EXECUTE_TOOL_ALLOWLISTonly affect which tools a key may see and reach, never whether a Tier 3 action executes; for a named principal they gate its Tier 3 calls as well.
When a client calls tools/call, the server re-checks scopes before execution. Attempting to call a tool your key lacks permission for returns a JSON-RPC error:
{ "jsonrpc": "2.0", "id": 1, "error": { "code": -32603, "message": "Tool \"execute_command\" requires ai:execute scope" }}RBAC permission checks
Section titled “RBAC permission checks”In addition to API key scopes, every tool call passes through the Breeze RBAC system. The user who created the API key must have the appropriate role-based permissions for the resource and action. For example, calling run_script requires the scripts.execute permission on the creator’s role.
Rate limiting
Section titled “Rate limiting”Rate limiting is enforced at multiple levels to protect your fleet and the Breeze API.
Transport-level limits
Section titled “Transport-level limits”These apply in production (NODE_ENV=production) and are backed by Redis sliding-window counters:
| Endpoint | Default limit | Window | Env override |
|---|---|---|---|
GET /api/v1/mcp/sse |
30 connections | 60 seconds | MCP_SSE_RATE_LIMIT_PER_MINUTE |
POST /api/v1/mcp/message |
120 requests | 60 seconds | MCP_MESSAGE_RATE_LIMIT_PER_MINUTE |
When a rate limit is exceeded, the server returns HTTP 429 with a Retry-After header.
Per-tool rate limits
Section titled “Per-tool rate limits”Each tool has its own sliding-window rate limit, enforced per user:
| Tool | Limit | Window |
|---|---|---|
execute_command |
10 | 5 min |
run_script |
5 | 5 min |
security_scan |
3 | 10 min |
network_discovery |
2 | 10 min |
file_operations |
20 | 5 min |
manage_services |
10 | 5 min |
analyze_disk_usage |
10 | 5 min |
disk_cleanup |
3 | 10 min |
system_cleanup |
30 | 60 min |
manage_configuration_policy |
20 | 5 min |
configuration_policy_compliance |
30 | 5 min |
manage_deployments |
10 | 10 min |
manage_patches |
15 | 5 min |
manage_groups |
20 | 5 min |
manage_maintenance_windows |
15 | 5 min |
manage_automations |
10 | 10 min |
manage_alert_rules |
15 | 5 min |
generate_report |
10 | 5 min |
set_device_context |
20 | 5 min |
resolve_device_context |
20 | 5 min |
Tools without an entry in this table (e.g., query_devices, get_device_details) have no per-tool rate limit.
These are the defaults. An organisation can raise all of them together with the Per-tool rate limit multiplier AI budget setting (a whole number from 1 to 10). Each limit becomes ceil(limit × multiplier), and the multiplier can never lower or remove a limit. Over MCP, the API key’s organisation selects the multiplier. A partner-scoped caller with no organisation uses the partner’s value. The counter stays per user and per tool. See AI → Raising the per-tool limits.
Session management
Section titled “Session management”The MCP server supports concurrent SSE sessions with the following constraints:
| Parameter | Default | Env override |
|---|---|---|
| Max total SSE sessions (server-wide) | 100 | – |
| Max SSE sessions per API key | 5 | MCP_MAX_SSE_SESSIONS_PER_KEY |
| Session TTL | 30 minutes | – |
Stale sessions are cleaned up automatically when they exceed the 30-minute TTL. Each SSE session is owned by the API key that created it; the server verifies ownership before delivering responses to prevent cross-key session hijacking.
When the session limit is reached, the server returns HTTP 503 (“Too many active MCP sessions”) for server-wide limits, or HTTP 429 (“Too many active MCP sessions for this API key”) for per-key limits.
Security considerations
Section titled “Security considerations”Organization isolation
Section titled “Organization isolation”API keys are always scoped to a single organization. The MCP server constructs an AuthContext from the API key that enforces org-level data isolation on every query and mutation. There is no way to access data from another organization through MCP, even with a wildcard scope.
Guardrails
Section titled “Guardrails”Every tools/call request passes through the guardrail system before execution:
- Scope check – verifies the API key has the required scope for the tool’s effective tier.
- Guardrail check – evaluates action-level tier escalation (e.g.,
file_operationswithaction: "delete"escalates from Tier 1 to Tier 3). - RBAC permission check – verifies the API key creator has the required role-based permission.
- Per-tool rate limit – enforces the sliding-window rate limit for the specific tool.
- Production allowlist – for Tier 3+ tools in production, the tool must appear in the
MCP_EXECUTE_TOOL_ALLOWLISTenvironment variable.
If any check fails, the request is rejected with an appropriate error before the tool handler executes.
Step-up approval (out-of-band)
Section titled “Step-up approval (out-of-band)”Beyond scope and RBAC checks, a sensitive tool call can require an explicit out-of-band approval before it executes. When step-up is required, Breeze creates an approval request, pushes it to the user’s trusted mobile device, and blocks the action until the user decides on their phone:
- Approved — the tool call proceeds.
- Denied or expired — the tool call fails with the recorded reason.
- Reported — the user flagged the request as unrecognised; the requesting OAuth client’s access is revoked immediately.
This makes the mobile app a second factor for high-risk automation: even a valid API key cannot complete a step-up-gated action without a live approval from a trusted device. See Approval Mode for the full request lifecycle, risk tiers, and trusted-device management.
Production allowlist for destructive tools
Section titled “Production allowlist for destructive tools”In production, Tier 3+ (destructive) tools are blocked by default unless explicitly listed in the MCP_EXECUTE_TOOL_ALLOWLIST environment variable. This is a comma-separated list of tool names:
# Allow specific toolsMCP_EXECUTE_TOOL_ALLOWLIST=execute_command,run_script,manage_services
# Allow all (not recommended)MCP_EXECUTE_TOOL_ALLOWLIST=*If the environment variable is unset or empty, all Tier 3+ tools are blocked in production.
Audit logging
Section titled “Audit logging”Every JSON-RPC request is recorded in the Breeze audit log with:
- The API key ID as the actor
- The JSON-RPC method (e.g.,
mcp.tools.call) - Whether a session was active
- Success or failure result
- Error message (if any)
You can query MCP audit events using the query_audit_log tool or the Audit Log page in the dashboard.
Input validation
Section titled “Input validation”All tool inputs are validated against Zod schemas before execution. Invalid inputs are rejected with a descriptive error before reaching the tool handler.
Environment variables
Section titled “Environment variables”MCP server
Section titled “MCP server”| Variable | Default | Description |
|---|---|---|
MCP_EXECUTE_TOOL_ALLOWLIST |
(empty) | Comma-separated list of Tier 3+ tool names allowed in production. Set to * to allow all. |
MCP_REQUIRE_EXECUTE_ADMIN |
true |
In production, Tier 3+ tools require the ai:execute_admin scope. Set to false to fall back to ai:execute. (No effect outside production.) |
MCP_MAX_SSE_SESSIONS_PER_KEY |
5 |
Maximum concurrent SSE sessions per API key. |
MCP_SSE_RATE_LIMIT_PER_MINUTE |
30 |
Maximum SSE connections per API key per minute. |
MCP_MESSAGE_RATE_LIMIT_PER_MINUTE |
120 |
Maximum JSON-RPC messages per API key per minute. |
MCP_MESSAGE_MAX_BODY_BYTES |
65536 |
Maximum accepted JSON-RPC request body size, in bytes (64 KB). Larger requests are rejected. |
MCP_TOOLS_LIST_PAGE_SIZE |
0 (unset) |
Experimental pagination: 0/unset returns a single page. Set to e.g. 50 only when every connected client follows nextCursor. Cursors are offsets; a catalog change mid-enumeration can skip tools. |
MCP_SESSION_TTL_SECONDS |
660 |
Idle lifetime, in seconds, of a minted streamable-HTTP session principal in Redis (11 minutes). |
OAuth 2.1
Section titled “OAuth 2.1”These apply only when OAuth is enabled. When MCP_OAUTH_ENABLED=true in production, OAUTH_JWKS_PRIVATE_JWK and OAUTH_COOKIE_SECRET are required — the API refuses to boot without them.
| Variable | Default | Description |
|---|---|---|
MCP_OAUTH_ENABLED |
false |
Master switch for OAuth 2.1. When true, the /oauth/* and /.well-known/oauth-* routes mount and MCP endpoints accept Authorization: Bearer tokens. |
OAUTH_ISSUER |
(empty) | Public issuer base URL (e.g. https://api.example.com). Used to build every advertised OAuth endpoint URL. |
OAUTH_RESOURCE_URL |
(empty) | Protected-resource identifier advertised in /.well-known/oauth-protected-resource (RFC 9728). |
OAUTH_JWKS_PRIVATE_JWK |
(empty) | EdDSA private signing key (JWK JSON) for access tokens. Required in production when OAuth is enabled. |
OAUTH_JWKS_PUBLIC_JWK |
(empty) | Matching public key, advertised at /.well-known/jwks.json. Derived from the private key if unset. |
OAUTH_COOKIE_SECRET |
(empty) | Secret used to sign sign-in / consent session cookies. Required in production when OAuth is enabled. |
OAUTH_DCR_ENABLED |
false |
Allow Dynamic Client Registration at POST /oauth/reg. Off by default. When true in production you must also set one of the two posture flags below, or the API refuses to boot. |
OAUTH_DCR_REQUIRE_IAT |
false |
Gate DCR behind an initial access token issued out-of-band. Closes the public-spam vector, but is incompatible with public MCP clients (Claude.ai / ChatGPT) — they register anonymously and cannot supply an IAT. Use OAUTH_DCR_ALLOW_ANONYMOUS instead for a public server. |
OAUTH_DCR_ALLOW_ANONYMOUS |
false |
Deliberately permit anonymous DCR. This is the required posture for connecting Claude.ai / ChatGPT (see above). Spam risk is bounded by the controls on /oauth/reg: per-IP rate limit, forced public clients, mandatory PKCE S256, software_id rejection, and a daily stale-client GC. |
OAUTH_CONSENT_URL_BASE |
(empty) | Optional origin for the consent UI redirect (e.g. http://localhost:4321 for split API/web dev). Defaults to a relative path, which works when the API and web app share an origin behind a reverse proxy. |
API reference
Section titled “API reference”The MCP server implements the following JSON-RPC 2.0 methods:
initialize
Section titled “initialize”Returns server capabilities and metadata.
// Request{ "jsonrpc": "2.0", "id": 1, "method": "initialize" }
// Response{ "jsonrpc": "2.0", "id": 1, "result": { "protocolVersion": "2024-11-05", "capabilities": { "tools": { "listChanged": false }, "resources": { "subscribe": false, "listChanged": false }, "prompts": { "listChanged": false } }, "serverInfo": { "name": "breeze-rmm", "version": "1.0.0" }, "instructions": "You are connected to Breeze RMM — a multi-tenant RMM platform for MSPs. ..." }}prompts/list
Section titled “prompts/list”Returns the catalogue of guided workflow prompts (see Guided prompts).
// Request{ "jsonrpc": "2.0", "id": 6, "method": "prompts/list" }prompts/get
Section titled “prompts/get”Returns a prompt’s rendered messages, ready to seed a conversation. Pass the prompt name and any arguments.
// Request{ "jsonrpc": "2.0", "id": 7, "method": "prompts/get", "params": { "name": "breeze-device-investigate", "arguments": { "device": "ACME-LT-014" } }}tools/list
Section titled “tools/list”Returns the filtered list of available tools based on API key scopes.
// Request{ "jsonrpc": "2.0", "id": 2, "method": "tools/list" }
// Response{ "jsonrpc": "2.0", "id": 2, "result": { "tools": [ { "name": "query_devices", "description": "Search and filter devices in the organization...", "inputSchema": { "type": "object", "properties": { "..." } } } ] }}tools/call
Section titled “tools/call”Invokes a tool with the given arguments.
// Request{ "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "query_devices", "arguments": { "status": "offline", "limit": 10 } }}
// Response (success){ "jsonrpc": "2.0", "id": 3, "result": { "content": [{ "type": "text", "text": "{\"devices\":[...],\"total\":42,\"showing\":10}" }] }}
// Response (error){ "jsonrpc": "2.0", "id": 3, "result": { "content": [{ "type": "text", "text": "{\"error\":\"Device not found or access denied\"}" }], "isError": true }}resources/list
Section titled “resources/list”Returns the catalogue of available resources.
// Request{ "jsonrpc": "2.0", "id": 4, "method": "resources/list" }resources/read
Section titled “resources/read”Reads a specific resource by URI.
// Request{ "jsonrpc": "2.0", "id": 5, "method": "resources/read", "params": { "uri": "breeze://devices" }}
// Response{ "jsonrpc": "2.0", "id": 5, "result": { "contents": [{ "uri": "breeze://devices", "mimeType": "application/json", "text": "[{\"id\":\"...\",\"hostname\":\"...\",\"status\":\"online\",...}]" }] }}Troubleshooting
Section titled “Troubleshooting”“Missing X-API-Key header” (401)
Section titled ““Missing X-API-Key header” (401)”The request does not include an X-API-Key header. Ensure your MCP client configuration includes the header. In Claude Desktop and Cursor, this goes in the headers block of the server config.
“Invalid API key format” (401)
Section titled ““Invalid API key format” (401)”The key does not start with brz_. Verify you copied the full key from the API Keys page.
“Rate limit exceeded” (429)
Section titled ““Rate limit exceeded” (429)”You have exceeded the per-minute transport rate limit. The response includes a Retry-After header with the number of seconds to wait. If this happens frequently, increase the limit via MCP_SSE_RATE_LIMIT_PER_MINUTE or MCP_MESSAGE_RATE_LIMIT_PER_MINUTE.
“Too many active MCP sessions” (503 or 429)
Section titled ““Too many active MCP sessions” (503 or 429)”Either the server-wide limit (100 sessions) or the per-key limit (default 5) has been reached. Close unused MCP clients, or increase the per-key limit via MCP_MAX_SSE_SESSIONS_PER_KEY.
“Service temporarily unavailable” (503)
Section titled ““Service temporarily unavailable” (503)”Redis is not available. The MCP server requires Redis for rate limiting in production. Check that your Redis instance is running and the REDIS_URL environment variable is set.
Tool returns “requires ai:execute scope”
Section titled “Tool returns “requires ai:execute scope””The API key does not have the ai:execute scope. Regenerate the key with the required scope, or use a key that already has it.
Tool returns “not in MCP_EXECUTE_TOOL_ALLOWLIST”
Section titled “Tool returns “not in MCP_EXECUTE_TOOL_ALLOWLIST””In production, Tier 3+ tools must be explicitly allowlisted. Add the tool name to the MCP_EXECUTE_TOOL_ALLOWLIST environment variable.
Tools not appearing in Claude Desktop
Section titled “Tools not appearing in Claude Desktop”- Verify the SSE endpoint URL is correct and reachable from your machine.
- Check that the API key has at least the
ai:readscope. - Restart Claude Desktop after editing the config file.
- Open the Claude Desktop developer console to check for connection errors.
“Unknown tool” errors
Section titled ““Unknown tool” errors”The tool name in tools/call does not match any registered tool. Use tools/list to see the current catalogue. Tool names are case-sensitive.
Stale SSE session
Section titled “Stale SSE session”SSE sessions expire after 30 minutes. If you see errors about missing sessions, the client should reconnect to /api/v1/mcp/sse to obtain a new session.