feat(keep-mcp): v0.1 read-only MCP server (sidecar container)#6619
Draft
harisrujanc wants to merge 4 commits into
Draft
feat(keep-mcp): v0.1 read-only MCP server (sidecar container)#6619harisrujanc wants to merge 4 commits into
harisrujanc wants to merge 4 commits into
Conversation
Introduces keep-mcp/ — a standalone Python package that exposes Keep's alerts, incidents, and topology to LLM agents via the Model Context Protocol. Ships as an independent container alongside keep-backend and keep-frontend, matching the existing keep-websocket-server sidecar pattern. Tools (v0.1, read-only): - search_alerts(cel, limit, offset) - list_incidents(status, severity, limit, offset, cel) - get_incident_with_alerts(incident_id, alerts_limit) - get_topology(service) Resources: - keep://alerts - keep://incidents - keep://topology Transports: stdio (local desktop clients) and streamable-http (remote). Auth: X-API-KEY forwarded from KEEP_MCP_KEEP_API_KEY; no direct DB or secret-manager access — RBAC and tenancy stay in keep-backend. Ops: /healthz + /readyz, Docker HEALTHCHECK, Alpine python:3.13.5 base matching Dockerfile.api, non-root user (UID 1000). Wired into docker-compose behind the 'mcp' profile so it stays out of the default 'docker compose up'.
- delete KeepAPIError + _extract_detail; rely on httpx.raise_for_status - delete get_alerts_by_fingerprints (no v0.1 caller) - delete client.ping(); inline into /readyz - delete INCIDENT_STATUSES/SEVERITIES/_validate_subset (backend already 422s) - delete keep://alerts/incidents/topology resources (tools cover it) - split get_incident_with_alerts into get_incident + list_incident_alerts - inline build_server() into main() — one caller, no fake factory - drop starlette (transitive via mcp) and ruff (unwired) deps - delete empty tests/__init__.py - update tests to expect httpx.HTTPStatusError All 5 tests pass; MCP handshake + tools/list verified end-to-end.
Ultra pass: this is a proxy to 5 REST endpoints. It doesn't need a client class, a config module, a factory, or three ceilings on limit. - delete client.py, config.py, .gitignore - fold everything into __main__.py: 5 tools call httpx directly - drop pydantic and pydantic-settings deps (os.environ.get is fine) - delete /readyz (add back with the Helm chart) - delete client-side limit clamps and status default (backend handles) - shrink README to 20 lines 3/3 tests pass; streamable-http boots; MCP handshake works.
55b9a38 to
3d658f5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #6618.
Adds
keep-mcp— a Model Context Protocol server for Keep, shipped as a sidecar container next tokeep-backend/keep-frontend(same pattern askeep-websocket-server). Thin adapter over the Keep REST API; no direct DB or secret-manager access. RBAC and tenancy stay inkeep-backend.v0.1 surface (read-only)
Tools
search_alerts(cel, limit, offset)list_incidents(status, severity, limit, offset, cel)— defaults to active (firing+acknowledged)get_incident(incident_id)list_incident_alerts(incident_id, limit, offset)get_topology(service)Transports
stdio— for local desktop clients (Claude Desktop, Copilot CLI, Cursor)streamable-http— for remote deployments (OAuth 2.1 planned for v0.2)Try it
What's in the diff
keep-mcp/— standalone Poetry package (~300 LOC): config, httpx client, FastMCP entrypoint, 5 unit testsdocker/Dockerfile.mcp— Alpinepython:3.13.5, non-root UID 1000, mirrorsDockerfile.apidocker-compose.yml/docker-compose.common.yml—keep-mcpservice behind themcpprofilekeep-backendis not modified in this PR.Verified
pytest→ 5/5 passdocker compose config --servicesunchanged for default profile;keep-mcponly appears under--profile mcpinitialize+tools/listhandshake returns all 5 tools/healthz→ 200,/readyz→ 503 when backend unreachable (correct)Deferred to follow-up PRs
POST /providers/{id}/invoke/{method}inkeep-backend+ LGTM passthrough tools (Loki / Mimir / Tempo / Grafana) +triage_alertMCP prompt + newgrafana_tempo_providerenrich_alert,create_incident,run_workflow) with idempotency + dry-run + audit, gated byMCP_ENABLE_MUTATING_TOOLS.github/workflows/mcp.yml, Helm sub-chart inkeephq/helm-chartsMarking as draft for early feedback on shape/direction before I add CI + docs.