Skip to content
ApparentlyTejasPublic

About

AI-powered engineering-ops platform: service monitoring, metrics, and incident management, exposed to Claude via MCP.

Resources

Stars

7 stars

Watchers

0 watching

Forks

Repository files navigation

DevPulse

CI License Python FastAPI Ruff mypy Coverage

AI-powered engineering operations platform. Centralizes service monitoring, metrics, incident management, and exposes infrastructure to Claude via the Model Context Protocol (MCP).

Stack

  • Backend: FastAPI, Python 3.12, SQLAlchemy 2.0 (async), Alembic, PostgreSQL 16 + pgvector, Redis 7, APScheduler + arq.
  • AI: Anthropic SDK, MCP Python SDK.
  • Frontend: React 18 + TypeScript + Vite + shadcn/ui (Phase 3).
  • Infra: Docker, Docker Compose, GitHub Actions, AWS (EC2/RDS/S3), Terraform, Nginx (Phase 4).

Repository layout

devpulse/
├── backend/           FastAPI app, workers, MCP server (one Python package)
├── frontend/          React app (Phase 3)
├── infrastructure/    Terraform, nginx, postgres init scripts
├── docs/              Architecture, ADRs, runbooks
├── docker-compose.yml Local dev orchestration
└── Makefile           Common developer commands

See docs/adr/ for architecture decisions.

Local development

Requirements: Docker, Docker Compose v2, GNU Make.

cp .env.example .env       # then edit secrets
make up                    # starts postgres, redis, api
curl http://localhost:8000/healthz
make logs                  # tail container logs
make down                  # stop everything

Run tests / lint / typecheck:

make test
make lint
make typecheck

Running lint / type-check / coverage locally (without Docker)

CI (.github/workflows/ci.yml) runs ruff, mypy, and the full pytest suite with coverage on every push/PR to main. You can run the same checks locally with uv, from backend/:

cd backend
uv sync --extra dev                 # install runtime + dev dependencies

uv run ruff check .                 # lint
uv run ruff format --check .        # formatting

uv run mypy app                     # strict type check

uv run pytest --cov=app --cov-report=term-missing --cov-report=html
# open backend/htmlcov/index.html for the full coverage report

Integration tests spin up ephemeral Postgres/Redis containers via testcontainers, so Docker must be running locally — no other setup or secrets required.

Project status

Phase Area Status
Phase 1 Core backend (auth → incidents) Done (auth, orgs, services, metrics, alerts, incidents)
Phase 2 AI layer (Claude triage, MCP, RAG) In progress — Claude-backed incident triage and an MCP server shipped; RAG next
Phase 3 Frontend —
Phase 4 Production infrastructure (AWS) In progress — pagination, rate limiting, Claude timeouts/retries, prod secret guard shipped; deployment + observability next
Phase 5 Polish, docs, demo —

AI incident triage

Every incident can be triaged by Claude on demand:

POST /api/v1/organizations/{org_id}/incidents/{incident_id}/triage
GET  /api/v1/organizations/{org_id}/incidents/{incident_id}/triage   # history, newest first

Claude gets the incident's title, severity, status, linked service (if any), and description, and returns a structured assessment via a forced tool call (summary, suggested_severity, confidence) — not freeform text — so the response shape is reliable regardless of model phrasing. Each triage run is persisted (incident_triages table) rather than overwriting the last one, so an incident keeps a full triage history as new information comes in.

Requires ANTHROPIC_API_KEY (see .env.example). Backed by app/modules/ai/. Both triage and postmortem generation share one per-org, per-minute rate limit (ANTHROPIC_RATE_LIMIT_PER_MINUTE, default 20) — see Production hardening.

AI postmortem generation

POST /api/v1/organizations/{org_id}/incidents/{incident_id}/postmortem
GET  /api/v1/organizations/{org_id}/incidents/{incident_id}/postmortem   # history, newest first

Claude writes the postmortem strictly from an incident's recorded facts — title, severity, status, open/resolve timestamps, linked service, description, and its full triage history — and is explicitly instructed not to invent a root cause, metric, or system name the data doesn't support. If the facts don't establish a root cause, the postmortem says so plainly and names what information would be needed, rather than guessing one. Like triage, every generation is persisted (postmortems table) rather than overwriting the last one.

Requires ANTHROPIC_API_KEY (see .env.example). Backed by app/modules/ai/.

MCP server

backend/mcp_server/ exposes incidents, services, triage, and postmortem generation to any MCP client (Claude Desktop, or anything else speaking the protocol) as tools: list_incidents, get_incident, create_incident, update_incident_status, triage_incident, list_incident_triages, generate_postmortem, list_postmortems, list_services.

It's a separate, lightweight process from the API itself — it holds no database connection of its own. Every tool call is a plain HTTP request against the already-running DevPulse API, so it inherits the exact same authorization rules (org membership, etc.) as every other API client, rather than duplicating that logic.

cd backend
uv sync --extra dev

DEVPULSE_MCP_ACCESS_TOKEN=<a valid access token>
DEVPULSE_MCP_ORG_ID=<the org to scope every call to>
DEVPULSE_MCP_API_BASE_URL=http://localhost:8000/api/v1   # default; override for a deployed API

uv run devpulse-mcp

Point an MCP client's stdio config at uv run devpulse-mcp (run from backend/, with the three env vars above set) to connect it.

Production hardening

Moving past demo-level behavior, in progress:

  • Pagination — every list endpoint whose result set grows without bound (incidents, services, alert events, metrics) returns a {items, total, limit, offset} page (limit capped at 200) instead of an unbounded bare array. Config objects whose cardinality is inherently small and human-bounded (orgs a user belongs to, org members, alert rules) intentionally keep returning a bare list.
  • Rate limiting — triage and postmortem generation (the two endpoints that call the paid Claude API) share one Redis-backed, per-org, per-minute budget (ANTHROPIC_RATE_LIMIT_PER_MINUTE), so a bug or an abusive client can't run up unbounded API spend. A 429 includes Retry-After.
  • Claude request timeout + retries — the Anthropic SDK's own default read timeout is 600s, which would hold a DB session open for minutes on a stalled call; ANTHROPIC_REQUEST_TIMEOUT_SECONDS (default 30) and ANTHROPIC_MAX_RETRIES (default 2, the SDK's own default made explicit) bound that.
  • Prod secret guard — Settings refuses to construct when APP_ENV=prod and SECURITY_JWT_SECRET is still the default or the .env.example placeholder (or under 32 chars), so a deployment can't silently go live signing tokens with a secret anyone could read in this repo.

Not yet done: an actual deployment target (there's a Dockerfile, but nothing is live anywhere), error tracking (Sentry or equivalent), and request correlation IDs.

About

AI-powered engineering-ops platform: service monitoring, metrics, and incident management, exposed to Claude via MCP.

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages