Skip to main content
Glama
si-essence

Pagerlane MCP Connector

by si-essence

pagerlane-mcp (Pagerlane MCP connector)

Works with Keep (keephq). Not affiliated with or endorsed by Keep Alerting LTD.

Pagerlane calls a Keep API you already run. It uses stdio or stateless Streamable HTTP. It does not start Keep.

Tool names use the keep_ prefix because each tool wraps a Keep API route.

The server reads Keep's public REST API only. It contains no Keep source code.

TypeScript 6 is intentional. typescript-eslint strict type-aware rules need the JavaScript compiler API, which TypeScript 7 does not ship.

Setup

npm install
npm run ci          # typecheck, lint, test, build
npm start           # stdio
npm run start:http  # Streamable HTTP at http://localhost:3000/mcp and /health

Copy .env.example to .env and export the variables in your shell, or set them in the client config. KEEP_API_URL is required. Point it at your Keep API, for example http://localhost:8080 or https://api.keephq.dev.

Claude Desktop / any stdio client:

{
  "mcpServers": {
    "pagerlane": {
      "command": "node",
      "args": ["/workspace/connectors/pagerlane/dist/index.js"],
      "env": {
        "KEEP_API_URL": "http://localhost:8080",
        "KEEP_API_KEY": "...",
        "KEEP_CONTACT": "you@example.com"
      }
    }
  }
}

Docker (HTTP, no authentication, keep it on localhost):

docker build -t pagerlane-mcp .
docker run -p 3000:3000 \
  -e KEEP_API_URL=http://host.docker.internal:8080 \
  -e KEEP_API_KEY=your-keep-api-key \
  pagerlane-mcp

Register every tool with registerConnectorTool. The helper requires a Zod object schema and a non-empty description (toolDescription). Calling server.registerTool anywhere else fails lint.

Related MCP server: Keephub MCP Server

Environment

Variable

Default

Role

KEEP_API_URL

none, required

Keep API origin and optional path. No userinfo, query, or hash

KEEP_API_KEY

unset

Required for an upstream call. Sent only as X-API-KEY. At least 8 characters. Never printed

KEEP_CONTACT

contact@example.com

Contact inside the User-Agent. Must not contain the API key

KEEP_PER_MINUTE

30

Local sliding-window cap

KEEP_CACHE_TTL_SEC

60

TTL for successful reads. 0 disables the cache. Writes are never cached and clear the cache

KEEP_TIMEOUT_MS

20000

Upstream timeout

MCP_TRANSPORT

stdio

stdio or http. --http forces HTTP

PORT

3000

HTTP listen port

Invalid values are reported by field name and issue code. The rejected value is not included in the error.

Auth matches Keep's APIKeyHeader(name="X-API-KEY") in keep/identitymanager/authverifierbase.py:21 (commit 465e991). The key is never placed in the URL, logs, or tool output.

Tools

Routes are taken from Keep commit 465e991 (keep/api/routes/*.py with prefixes in keep/api/api.py). docs/openapi.json is v0.24.5 and may be stale. When it disagrees with the routes, the routes win.

Tool

Method and path

Source

Notes

keep_list_alerts

GET /alerts?limit=

keep/api/api.py:295, keep/api/routes/alerts.py:253

Keep default limit=1000

keep_search_alerts

POST /alerts/query

alerts.py:195, body QueryDto in keep/api/models/query.py:10

CEL search (cel, limit, offset, sort_by, sort_dir). POST /alerts/search (alerts.py:1293) is the older SearchEngine path and is not used

keep_get_alert

GET /alerts/{fingerprint}

alerts.py:784

keep_list_incidents

GET /incidents

api.py:296, incidents.py:125

limit (default 25), offset, sorting (IncidentSorting in incident.py:284), cel, candidate, predicted, plus declared status, severity, assignees, sources, affected_services query params

keep_get_incident

GET /incidents/{incident_id}

incidents.py:349

Optional include_alerts also calls GET /incidents/{incident_id}/alerts (incidents.py:542)

keep_list_providers

GET /providers

api.py:291, providers.py:68

Strips details, config, authentication, and other credential keys. Installed providers can hold secrets (ProvidersFactory.get_installed_providers, include_details=True by default). This result is not cached

keep_list_workflows

GET /workflows

api.py:298, workflows.py:178

A comment at workflows.py:182 says this GET should be deprecated and removed in favor of POST /workflows/query (workflows.py:195). This tool still uses GET

keep_get_topology

GET /topology

api.py:324, topology.py:44

Optional provider_ids, services, environment, include_empty_deps

keep_list_applications

GET /topology/applications

topology.py:65

Services come from topology, not a separate services list

keep_acknowledge_alert

POST /alerts/enrich?dispose_on_new_alert=true

alerts.py:1043, query flag alerts.py:1052, body EnrichAlertRequestBody in alert.py:372

Enrichments { status: "acknowledged", dismissed: "false", dismissUntil: "" }. Same path Keep UI uses in keep-ui/features/alerts/alert-change-status/ui/alert-change-status-modal.tsx

keep_resolve_alert

POST /alerts/enrich?dispose_on_new_alert=true

same as acknowledge

Enrichments { status: "resolved", dismissed: "false", dismissUntil: "" }

keep_enrich_alert

POST /alerts/enrich

alerts.py:1043, alert.py:372

fingerprint plus string enrichments map. dispose_on_new_alert defaults to false, matching the API

keep_update_incident

POST /incidents/{incident_id}/status, POST /incidents/{incident_id}/severity, or PUT /incidents/{incident_id}

incidents.py:839 (IncidentStatusChangeDto in incident.py:22), incidents.py:863 (IncidentSeverityChangeDto in incident.py:37), incidents.py:380 (IncidentDtoIn in incident.py:42), db.py:4129

One change object. Status and severity use their POST routes and send comment (status also sends tagged_users). fields reads the incident, then PUTs the four mutable fields so an omitted user_summary or same_incident_in_the_past_id is not cleared (db.py:4165, db.py:4173). Severity is not sent on PUT

keep_run_workflow

POST /workflows/{workflow_id}/run

workflows.py:357, get_event_from_body at workflows.py:315

JSON { type, body, inputs }. Omitted type means alert. Alert status and severity are optional. Keep defaults them to firing and info (alert.py:249). Replay query params event_type and event_id load a stored alert. Incident replay is not implemented (workflows.py:407)

Read tools use readOnlyHint: true. Write tools use readOnlyHint: false. Acknowledge, resolve, enrich, and incident update set idempotentHint: true. Workflow run sets idempotentHint: false because each call creates a new execution.

update_incident_from_dto_by_id (keep/api/core/db.py:4129) assigns user_summary on every user PUT (db.py:4173). It also assigns same_incident_in_the_past_id when that key is in the DTO dict (db.py:4165). IncidentDtoIn.dict() always contains the key, so a partial PUT clears both fields. Other DTO fields stay as they are when the new value is null. IncidentSeverity values are tuples such as ("critical", 5) (keep/api/models/db/incident.py:37). Severity changes use POST /incidents/{incident_id}/severity with { severity, comment }.

keep_update_incident takes incident_id, confirm, and one change. change.kind is status, severity, or fields. A fields change sets at least one of user_generated_name, assignee, user_summary, or same_incident_in_the_past_id. That last field is a UUID, or null to clear the link. With confirm: true, the tool GETs /incidents/{incident_id}, parses those four fields, and PUTs them with your values over the stored ones. The PUT body leaves severity out. A dry-run makes no upstream call. The preview body lists only the fields you set. Its summary says unspecified user_summary and same_incident_in_the_past_id will be preserved by reading the current incident first.

GET /workflows (workflows.py:178) is the list this connector calls. The handler has a comment that it should be deprecated and removed (workflows.py:182) in favor of POST /workflows/query (workflows.py:195). The tool still calls GET.

POST /workflows/{workflow_id}/run (workflows.py:357) accepts an optional JSON object plus the query params event_type and event_id. get_event_from_body (workflows.py:315) reads type, body, and inputs. Omitted type means alert. Omitted body means the outer object is the event. AlertDto (alert.py:75) declares status (alert.py:78) and severity (alert.py:79). Its root validator (alert.py:249) sets a missing or invalid status to firing and a missing or invalid severity to info. The same function fills a missing name with manual-run, a missing lastReceived with the current time, and id from fingerprint when id is missing. The tool types status and severity when you send them. An alert run rejects the incident-only statuses merged and deleted. An incident run rejects the alert-only statuses suppressed, pending, and maintenance. Omit type to run an alert. For an incident event, IncidentDto sets an invalid status to firing (incident.py:159). When both replay query params are set, Keep loads the stored alert. Incident replay raises NotImplementedError in this commit (workflows.py:407).

Confirm and dry-run

Every write tool's input includes confirm: z.literal(true).optional(). The only accepted value is the boolean true. confirm: false and confirm: "true" fail Zod and make no upstream call.

When confirm is omitted, the tool does not call Keep. It returns a preview:

{
  "dryRun": true,
  "method": "POST",
  "path": "alerts/enrich",
  "query": { "dispose_on_new_alert": true },
  "body": { "fingerprint": "fp-1", "enrichments": { "status": "acknowledged" } },
  "summary": "Acknowledge alert fp-1 via POST /alerts/enrich with status=acknowledged."
}

One shared WritePlan is built by a pure function, then either previewed or executed. A direct write is { kind: "request", method, path, query, body, summary }. An incident field update is { kind: "read-merge-put", path, previewBody, summary } plus a pure merge used only after confirm is true. That plan GETs the incident, merges, and PUTs. The dry-run does not call Keep.

Limits

  • In-memory TTL cache (KEEP_CACHE_TTL_SEC, default 60 seconds) for reads. A cache hit does not spend the local rate limit. The cache is dropped when the process exits. Writes skip the cache and clear it after a successful call so later reads refetch.

  • Local sliding window (KEEP_PER_MINUTE, default 30 requests per 60 seconds) around upstream calls.

  • When the local window is full, the tool returns isError with a [rate-limit] message and a retry hint. Cached results are still served.

  • Upstream limits still apply. HTTP 429 is returned as [rate-limit]. HTTP 404 is [not-found]. Other upstream failures are [upstream].

  • Response bodies larger than 1,000,000 characters are rejected.

  • The API key is redacted from tool results, error text, and logs, including its URL-encoded form. It is refused in the query string, the base URL, and the User-Agent.

  • HTTP mode has no authentication. Do not expose it beyond localhost.

Enterprise and Cloud-only endpoints

This connector does not wrap Keep Enterprise or AI endpoints.

  • Identity managers under ee/identitymanager/ (Auth0, Azure AD, Keycloak) are Keep Enterprise. OSS Keep still authenticates API keys through keep/identitymanager/authverifierbase.py.

  • GET /ai/stats and PUT /ai/{algorithm_id}/settings live in MIT keep/api/routes/ai.py and are not exposed here.

  • Incident PUT accepts generatedByAi (incidents.py:387). This connector does not send that query flag.

  • Incident DTOs include AI-ish fields such as ai_generated_name and is_predicted. Those can appear in GET responses. This connector does not call incident-AI helpers.

License and attribution

  • Keep's repository is MIT except ee/, which is under the Keep Enterprise License. This connector uses only the public REST API and contains no Keep code.

  • "Keep" is a trademark of Keep Alerting LTD. This project is not affiliated with, endorsed by, or sponsored by Keep Alerting LTD. No Keep logo is included.

  • User-Agent is pagerlane-mcp/0.1.0 (<KEEP_CONTACT>). Point KEEP_CONTACT at a real address before production use.

  • You are responsible for complying with the Keep instance's own access policy and with any provider credentials stored in that instance.

License

MIT, see LICENSE. This is an independent, unofficial connector and is not endorsed by the Pagerlane project or its owners.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    Enables Claude to interact with Karma Alert dashboard to monitor and analyze Kubernetes alerts. Provides tools to check alert status, filter by cluster/severity, get detailed alert information, and analyze alert statistics and trends.
    14
    1
    MIT
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables AI assistants to read and manage a self-hosted JonDash dashboard through an authenticated API, with tools for status checks and planned service/user management.
    1
    -