MCP Server (AI Integration)
The Model Context Protocol (MCP) lets AI assistants like Claude, Claude Code, Cursor, and other MCP-compatible clients query your Hoppla monitoring data directly. Ask "Are all my monitors healthy?" or "What incidents happened this week?" and get answers grounded in your live data.
Most tools are read-only. A small, explicitly listed set of write actions is also available — each needs a token scope ending in :write, and they are the only way MCP can change anything. Nothing deletes.
How it works
Hoppla exposes a Streamable HTTP MCP endpoint at:
https://app.hoppla.dev/mcpThere are two ways to connect, and which you use depends on your client:
- OAuth — for clients with a "connector" UI that sign you in through a browser (claude.ai, Claude Desktop). You paste the URL, approve a consent screen, and you're done. No token to copy or store. This is the recommended path where your client supports it.
- API token — for headless or command-line clients (Claude Code, Cursor,
mcp-remote, CI, your own scripts). You create a token once and send it as aBearerheader.
Both reach the same endpoint and behave identically once connected. What a client can see and do is decided by scopes, not by how it authenticated: a token or grant only reads the data its scopes allow, and only performs the write actions its :write scopes allow.
Custom portal domains
If your team uses a custom portal domain (e.g. monitoring.yourcompany.com), use it in place of app.hoppla.dev for either method:
https://monitoring.yourcompany.com/mcpUseful for reseller-managed teams whose users may not know app.hoppla.dev. Authentication, tenant isolation, and the available tools are identical on both. OAuth discovery is relative to whichever domain you connect to, so a token granted on your portal is bound to that portal and cannot be replayed against app.hoppla.dev, or the reverse.
Connect with OAuth (recommended)
Point your client at https://app.hoppla.dev/mcp and let it handle the sign-in. Nothing to copy.
claude.ai (web and desktop)
- Open Settings → Connectors and choose Add custom connector.
- Enter the URL:
https://app.hoppla.dev/mcp. - Claude sends you to Hoppla's consent screen. Sign in if you aren't already (your normal login, including passkey or 2FA).
- On the consent screen you choose:
- Read-only (preselected) or Read and act. Read-only can query everything; "Read and act" additionally allows the write tools (acknowledge/update incidents, snooze and run checks, schedule maintenance, publish status-page updates). It never grants team, billing, or settings changes.
- Which teams the connector may reach. Tick individual teams, or "including any team I am added to later" for an all-teams grant that picks up new teams automatically.
- Approve. The window tells you it's connected and you can close it.
Claude Desktop (connector UI)
Recent Claude Desktop has the same custom-connector flow as claude.ai — add the connector with the URL above and approve the consent screen. If your Claude Desktop has no connector UI, use the API-token method below with mcp-remote.
What OAuth does for you
- Access renews itself. The access token lasts 8 hours and is refreshed silently in the background (refresh window: 30 days). You won't be asked to sign in again during normal use. If a connector sits unused for more than 30 days, you reconnect once.
- Least privilege by default. The consent screen preselects read-only, whatever the client requested, so acting access is always a deliberate choice.
- Revoke any time. Open the Developers section of your dashboard: every connected app is listed with a Disconnect button. Disconnecting ends the whole grant immediately — the live access token and its refresh token — so the connector stops working at once, not in 8 hours.
Connect with an API token
For clients that don't drive OAuth themselves. Create a token in Settings → API Tokens (see API Tokens), give it the scopes you need, and send it as a Bearer header.
TIP
Use a dedicated token for each integration, so you can revoke MCP access without disturbing your other API clients. Grant only the scopes you need — read scopes for a read-only assistant.
Claude Code
claude mcp add hoppla \
--transport http \
--url https://app.hoppla.dev/mcp \
--header "Authorization: Bearer YOUR_TOKEN"Cursor
Add to your Cursor MCP configuration (.cursor/mcp.json in your project, or global settings):
{
"mcpServers": {
"hoppla": {
"url": "https://app.hoppla.dev/mcp",
"headers": {
"Authorization": "Bearer YOUR_TOKEN"
}
}
}
}Claude Desktop without a connector UI (mcp-remote)
Older Claude Desktop speaks only the stdio transport, so it needs a bridge. mcp-remote (requires Node.js) forwards to the HTTP endpoint. Edit the config file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"hoppla": {
"command": "npx",
"args": [
"mcp-remote",
"https://app.hoppla.dev/mcp",
"--header",
"Authorization: Bearer YOUR_TOKEN"
]
}
}
}Restart Claude Desktop after saving.
TIP
If npx is slow to start, install it once (npm install -g mcp-remote) and set "command": "mcp-remote" with "args": ["https://app.hoppla.dev/mcp", "--header", "Authorization: Bearer YOUR_TOKEN"].
Any other client
Any client that speaks Streamable HTTP can connect:
| Setting | Value |
|---|---|
| URL | https://app.hoppla.dev/mcp |
| Transport | Streamable HTTP |
| Auth | Authorization: Bearer YOUR_TOKEN, or OAuth if the client supports it |
Working across several teams
A token or OAuth grant can be authorized for one team or for several at once (with OAuth you pick this on the consent screen; for an API token it's set when the token is created).
When a connection can reach more than one team, the list and dashboard tools fan out automatically and return one block per team under a by_tenant field, so every row says which team it belongs to. To work within a single team, pass a tenant_id argument (get the ids from list-accessible-tenants) and the result is scoped to that team alone.
You can only ever reach the teams the connection is authorized for — never any other team, even if a tool is called with another team's resource id. A resource id that belongs to a team you can't reach returns exactly the same "not found" as an id that doesn't exist, so the tools never reveal whether something exists elsewhere.
Available tools
43 tools. Which ones respond depends on your scopes.
Monitors
| Tool | Description |
|---|---|
list-monitors | List monitors, filterable by type, tag, or search term. Paginated (max 50 per call); the response carries has_more |
list-monitor-tags-and-groups | The tag and group names actually in use — call this before filtering list-monitors by tag |
get-monitor-details | A single monitor with its checks and latest results |
get-monitor-uptime | Uptime for the last 24h, 7d, and 30d. Says so explicitly when uptime does not apply (e.g. a cron/agent heartbeat monitor) rather than returning bare nulls |
get-monitor-report | Uptime over an explicit date range: overall percentage, incident count, day-by-day breakdown. Ranges over 90 days are trimmed to the most recent 90 |
get-monitor-performance | Response-time percentiles (avg, p50, p95, p99) over 24h, 7d, 30d, and 90d, computed from real results |
get-monitor-certificate | SSL certificate details and expiry |
get-monitor-dns-history | DNS records and recent changes |
get-monitor-broken-links | Broken-link scan results |
get-check-results | Paginated result history for one check |
get-check-timeseries | Response-time timeseries for a check over a period — per-bucket avg/min/max (plus p50/p95 and timing phases for short windows) |
get-monitor-check-detail | Latest result for one check type: lighthouse, geo, web_vitals, security_headers, domain_expiry, robots_txt, sitemap, redirects, mixed_content, dnsbl, ping, tcp, or application_health. Reports check_configured so "no such check" and "no data yet" are distinguishable |
Incidents
| Tool | Description |
|---|---|
list-incidents | Incidents, filterable by status, monitor, or archive state |
get-incident-details | A single incident with its full update timeline |
Dashboards, logs & other resources
| Tool | Description |
|---|---|
list-cron-monitors | Cron heartbeat monitors with status and schedule |
list-agent-monitors | Server agent monitors with hostname, OS, and resource status |
list-status-pages | Status pages with their monitors and visibility |
list-maintenance-windows | Maintenance windows, filterable by active/upcoming/past |
get-dashboard-stats | Aggregate stats: uptime, response time, monitor counts, total checks. Availability failures are counted as downtime; failing informational checks (e.g. Lighthouse) are listed separately under failing_informational_checks so a slow audit is not mistaken for an outage |
get-dashboard-events | Event timeline for the last 7 days: incidents created/resolved, maintenance started/ended, SSL-expiry warnings |
get-monitor-logs | Analyse a monitor's access logs: overview, timeseries, peak-hours, top, or errors. Requires log shipping. Visitor IP addresses are withheld by default (they are your visitors' personal data) and returned only when include_client_ips is passed |
Notifications & escalation
| Tool | Description |
|---|---|
list-notification-channels | Configured channels (email, Slack, webhook, etc.). Secrets are masked |
list-notification-rules | Rules mapping events to channels |
list-escalation-policies | Multi-step escalation policies and their wait times |
list-on-call-schedules | On-call schedules and their rotations |
get-current-on-call | Who is on call right now |
Team & audit
| Tool | Description |
|---|---|
list-accessible-tenants | The teams this connection can reach |
list-team-members | A team's members and their roles |
get-audit-log | Who changed what and when — monitors, incidents, status pages, channels, team members, tokens, and more, including actions taken through this MCP server. Needs settings:read. Full change diffs are admin-only |
Write actions
Each needs the matching :write scope, a plan with full API access, and a team that is not paused. A read-only connection can call none of them.
| Tool | Scope | What it does |
|---|---|---|
acknowledge-incident | incidents:write | Acknowledges an open incident and stops escalation so the on-call rotation is no longer paged. Refused if already resolved or already acknowledged |
add-incident-update | incidents:write | Posts an update to an incident timeline (max 5000 chars, max 200 per incident). Passing resolved closes the incident and sends the all-clear to subscribers |
snooze-check | checks:write | Silences one check for 1–10080 minutes (up to 7 days). The check keeps running; it just stops alerting. End it early with unsnooze-check |
unsnooze-check | checks:write | Ends a check's snooze early. Safe to call on a check that isn't snoozed |
trigger-check-run | checks:write | Runs one check now. At most once per 60s per check, 10 runs per minute overall |
create-maintenance-window | maintenance:write | Schedules a maintenance window so planned work raises no incidents. Title, start, end (≤ 30 days apart), and optionally the monitors it covers. Shows on your status pages |
post-status-page-update | status-pages:write | Publishes a status-page update — publicly visible and emailed to every confirmed subscriber. Max 5 per minute, 500 per page. Per-locale translations must be published from the dashboard |
set-check-enabled | checks:write | Turns a check on or off. Disabling stops it running (a gap in uptime history) — to keep the data and only silence alerts, use snooze-check |
set-incident-state | incidents:write | Lifecycle actions: snooze (60/240/480/1440 min), unsnooze, archive (resolved only), or unarchive |
update-incident | incidents:write | Changes title, severity, or status without posting to the public timeline. resolved closes it and sends the all-clear, once |
create-incident | incidents:write | Opens an incident by hand. This notifies people — the team's incident.created rules fire. Max 100 open per team, 5 per minute |
create-monitor | monitors:write | Creates a monitor with its template's checks. Counts against the plan's monitor limit; the interval cannot go below the plan minimum; private and internal addresses are refused |
update-monitor | monitors:write | Renames, retargets, re-intervals, pauses, or resumes a monitor. A monitor's type cannot change after creation |
update-maintenance-window | maintenance:write | Reschedules a window, changes its monitors, or lifts it with is_active: false |
A write is never applied to more than one team. If your connection can reach several teams, pass tenant_id to the write tool — it refuses rather than guessing. Every write, and every refused attempt, is recorded in the target team's audit log with the tool used and the token behind it.
Example conversations
Ask in plain language; the assistant picks the right tools, chains several when needed, and summarizes.
Health & uptime — "Are any of my monitors down right now?" · "What's the average uptime across all my monitors this month?" · "Which monitors had the worst uptime in the last 30 days?"
Performance & trends — "What's the p95 response time for my API monitor?" · "Show the response-time trend for the checkout page over 7 days." · "How are my Core Web Vitals on the homepage?"
Certificates, domains & links — "Which SSL certificates expire in the next 30 days?" · "Have any DNS records changed recently?" · "Are there broken links on my marketing site?"
Incidents — "What incidents were created in the last 7 days?" · "Give me the full timeline for the latest incident on the API monitor."
Cron & agents — "Did any scheduled task miss its last run?" · "How's CPU and memory on my reported servers?"
Alerting & on-call — "Which channels are configured, and are any disabled?" · "Walk me through how an unacknowledged incident escalates." · "Who is on call right now?"
Multiple teams — "Which teams can this connection reach?" · "Compare uptime across all of my teams this month."
A good opener: "Give me a health summary — anything down, any incidents in the last 24 hours, and any certificates expiring soon." The assistant fans out across the relevant tools and returns one readable digest.
Rate limiting
The MCP endpoint has its own limits, separate from the REST API:
| Scope | Limit |
|---|---|
| Per token | 60 requests per minute |
| Per team | 200 requests per minute (across all tokens) |
Write tools carry a second, tighter per-team throttle on top of this. Exceeding a limit returns 429 Too Many Requests.
Security
- Reads by default, writes only where listed. Every tool outside "Write actions" is read-only, and no tool deletes anything.
- Least-privilege consent. The OAuth consent screen preselects read-only regardless of what the client asked for; acting access is always a deliberate choice, and it never includes team, billing, or settings changes.
- Scope-gated. A tool works only if the connection holds the matching scope; a write needs a
:writescope. - Writes target exactly one team, are audited (success and refusal alike), and are checked against your role in that team — being an admin in one team grants nothing in another.
- Audience-bound tokens. An OAuth token is bound to the domain it was granted on and is rejected anywhere else, so a grant on a reseller portal cannot be used against
app.hoppla.dev, or the reverse. - Team isolation. A connection reads only the teams it is authorized for. Another team's resource id returns the same "not found" as a nonexistent one.
- Credential redaction. Webhook URLs, agent tokens, and channel secrets are never exposed.
- Revocable. Disconnect an OAuth app from Developers in your dashboard, or delete an API token in Settings → API Tokens — either cuts access immediately.