Skip to content

Commit d9b4e05

Browse files
committed
Full Google Cloud + Dynatrace stack: Vertex AI Gemini, Cloud Firestore, live Dynatrace
- Persistence migrated MongoDB -> Google Cloud Firestore (100% Google Cloud) - Gemini on Vertex AI (Cloud Run service identity) + live Dynatrace REST - Official Dynatrace MCP server integration (local + code path) - README/INTEGRATIONS/.env docs + frontend Powered by updated
1 parent b122ede commit d9b4e05

29 files changed

Lines changed: 620 additions & 168 deletions

.env.example

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,32 @@
11
# ── BACKEND (copy to backend/.env) ───────────────────────────────────────────
22

3-
# Gemini — recommended. Without it, agents use the scenario-accurate offline mode.
4-
GEMINI_API_KEY=your_gemini_api_key_here
5-
# Optional: pin a specific model. Otherwise a resilient candidate chain is used.
6-
GEMINI_MODEL=
3+
# Google Cloud AI — Gemini.
4+
# - On Cloud Run: set GOOGLE_GENAI_USE_VERTEXAI=true + GOOGLE_CLOUD_PROJECT;
5+
# auth is the service's own identity (no key needed) → Gemini via Vertex AI.
6+
# - Locally: a GEMINI_API_KEY (AI Studio) is the simplest fallback.
7+
GOOGLE_GENAI_USE_VERTEXAI=false
78
GOOGLE_CLOUD_PROJECT=your_gcp_project_id
9+
GOOGLE_CLOUD_LOCATION=us-central1
10+
GEMINI_API_KEY=your_gemini_api_key_here
11+
GEMINI_MODEL=gemini-2.5-flash
812

913
# Demo mode — true uses simulated Dynatrace telemetry (no account needed).
1014
DEMO_MODE=true
1115

12-
# Optional: real Dynatrace (only used when DEMO_MODE=false)
16+
# Live Dynatrace via the OFFICIAL MCP server (preferred). DT_ENVIRONMENT is your
17+
# platform/apps URL; the agent connects to @dynatrace-oss/dynatrace-mcp-server.
18+
# (Local uses an interactive browser login. Leave unset on headless hosts unless
19+
# you also provide OAUTH_CLIENT_ID/OAUTH_CLIENT_SECRET/OAUTH_URN.)
20+
DT_ENVIRONMENT=https://your-env-id.apps.dynatrace.com
21+
22+
# Legacy fallback: classic Dynatrace REST (used only if DT_ENVIRONMENT is unset).
1323
DYNATRACE_URL=https://your-env-id.live.dynatrace.com
1424
DYNATRACE_API_TOKEN=your_dynatrace_api_token_here
1525

16-
# Optional: MongoDB Atlas for persistent incident history (graceful if unset)
17-
MONGODB_URI=mongodb+srv://user:pass@cluster.mongodb.net/specterops
26+
# Persistent incident history — Google Cloud Firestore (graceful in-memory fallback).
27+
# Just set GOOGLE_CLOUD_PROJECT (above) and enable Firestore on the project; on
28+
# Cloud Run it authenticates as the service identity. Set to disable explicitly:
29+
# FIRESTORE_DISABLED=true
1830

1931
# Optional: Slack incoming webhook to deliver post-mortems to a channel
2032
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/your/webhook/url

INTEGRATIONS.md

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -66,18 +66,22 @@ Lets SpecterOps pull **real open problems** from your tenant and analyze them.
6666

6767
---
6868

69-
## 3. MongoDB Atlas (persistent history) — free M0
69+
## 3. Google Cloud Firestore (persistent history) — free tier
7070

71-
Without it, incidents live in memory and reset on restart. With it, history persists.
71+
Without it, incidents live in memory and reset on restart. With it, history persists —
72+
on Google Cloud, so the whole stack stays on Google + Dynatrace (fully track-compliant).
7273

73-
1. Create a free account at **https://www.mongodb.com/atlas** and a free **M0** cluster.
74-
2. **Database Access** → Add a database user (username + password).
75-
3. **Network Access** → Add IP → allow your IP (or `0.0.0.0/0` for testing).
76-
4. **Connect → Drivers → Python** → copy the SRV connection string and insert your password:
74+
1. In the [Google Cloud Console](https://console.cloud.google.com), select your project.
75+
2. Enable the API and create the default database (Native mode):
76+
```bash
77+
gcloud services enable firestore.googleapis.com
78+
gcloud firestore databases create --location=nam5
7779
```
78-
MONGODB_URI=mongodb+srv://USER:PASSWORD@cluster0.xxxxx.mongodb.net/specterops
79-
```
80-
5. Put it in `backend/.env`, restart. The status badge flips to **DB · persisted**.
80+
3. Set `GOOGLE_CLOUD_PROJECT=<your-project-id>` in `backend/.env`.
81+
- **Local:** run `gcloud auth application-default login` once for credentials.
82+
- **Cloud Run:** nothing to do — it authenticates as the service's own identity
83+
(grant the runtime service account `roles/datastore.user`).
84+
4. Restart. The status badge flips to **Firestore · persisted**.
8185

8286
---
8387

README.md

Lines changed: 32 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ evidence-backed post-mortem in **under 60 seconds** — no human in the loop.
2323
[![Partner: Dynatrace](https://img.shields.io/badge/Partner-Dynatrace-9b6cff)]()
2424
[![AI: Gemini 2.5 Flash](https://img.shields.io/badge/AI-Gemini%202.5%20Flash-4f8cff)]()
2525
[![Frontend: Firebase](https://img.shields.io/badge/Frontend-Firebase%20Hosting-f5a623)]()
26-
[![Backend: Render](https://img.shields.io/badge/Backend-Render-46e3b7)]()
26+
[![Backend: Cloud Run](https://img.shields.io/badge/Backend-Cloud%20Run-4f8cff)]()
2727
[![Python 3.11+](https://img.shields.io/badge/Python-3.11%2B-3776ab.svg)]()
2828
[![React 18](https://img.shields.io/badge/React-18-61dafb.svg)]()
2929

@@ -57,18 +57,18 @@ cascade across three services — with **94% confidence, in ~0.3s of compute.**
5757
## ✨ What's inside
5858

5959
- 🤖 **Four autonomous agents** orchestrated end-to-end, reasoning with **Google Gemini 2.5 Flash**.
60-
- 🟣 **Live Dynatrace integration****browse all open problems** in your tenant and investigate any one you pick (or auto-grab the latest); the finished RCA can be **pushed back onto the problem as a comment** (closing the loop).
60+
- 🟣 **Official Dynatrace MCP server integration**the agent is a Model Context Protocol client that calls real MCP tools (`list_problems`, `execute_dql`, `find_entity_by_name`) to **browse all open problems** and investigate any one with live telemetry.
6161
- 🧪 **Scenario library + custom incidents** — four real-world failure scenarios (missing index, memory leak, bad deploy, dependency outage) plus a **"describe your own"** form.
6262
- 📡 **Real-time SSE streaming** — every agent's start/finish streams live to the dashboard.
6363
- 🕸️ **D3 causal graph** + **Three.js** 3D hero + **Framer Motion** — a business-grade UI, not a toy.
6464
- 🔐 **Firebase auth** (Google + email) with a graceful demo fallback; investigations are saved **per user**.
65-
- 💾 **MongoDB Atlas persistence** — history survives restarts; graceful in-memory fallback when unset.
65+
- 💾 **Cloud Firestore persistence** — history survives restarts on Google Cloud; graceful in-memory fallback when unset.
6666
- 📤 **Export & deliver** — copy/download the post-mortem as Markdown, or **send it to Slack**.
6767
- 🛡️ **Resilient by design** — a Gemini model-fallback chain, a scenario-accurate offline mode, and graceful degradation everywhere mean it runs with **zero accounts** and never breaks on stage.
6868
-**Tested**`pytest` suite + **GitHub Actions CI** on every push.
6969

7070
> Every integration is optional and upgrades a capability from *demo**real*. See
71-
> **[INTEGRATIONS.md](INTEGRATIONS.md)** for exact setup (Gemini, Dynatrace, MongoDB, Firebase, Slack).
71+
> **[INTEGRATIONS.md](INTEGRATIONS.md)** for exact setup (Gemini, Dynatrace, Firestore, Firebase, Slack).
7272
7373
---
7474

@@ -169,47 +169,45 @@ cd frontend && npm install && npm run dev
169169
| Layer | Technology |
170170
|:------|:-----------|
171171
| **Agents / Backend** | Python 3.11 · FastAPI · asyncio · Server-Sent Events |
172-
| **Reasoning** | Google **Gemini 2.5 Flash** via the `google-genai` SDK (resilient model-fallback chain) |
173-
| **Observability** | **Dynatrace** v2 API — problems · metrics · entities · logs |
172+
| **Reasoning** | Google **Gemini 2.5 Flash** on **Vertex AI** (Google Cloud), via the `google-genai` SDK |
173+
| **Observability** | **Dynatrace** via the official **MCP server**`list_problems` · `execute_dql` · `find_entity_by_name` |
174174
| **Frontend** | React 18 + Vite · Tailwind CSS · Framer Motion · **Three.js** (3D hero) · **D3** force-directed graph |
175175
| **Auth** | Firebase Authentication (Google + email) with graceful demo fallback |
176-
| **Persistence** | MongoDB Atlas · graceful in-memory fallback |
177-
| **Deployment** | Docker · **Render** (backend) · **Firebase Hosting** (frontend) — 100% free tier |
176+
| **Persistence** | **Google Cloud Firestore** · graceful in-memory fallback |
177+
| **Deployment** | Docker · **Google Cloud Run** (backend) · **Firebase Hosting** (frontend) |
178178
| **Quality** | `pytest` + `pytest-asyncio` · GitHub Actions CI |
179179

180180
</div>
181181

182-
### Dynatrace integration
182+
### Dynatrace integration — via the official MCP server
183183

184-
The `TraceArchaeologist` talks to Dynatrace through one async client
185-
([`dynatrace_client.py`](backend/mcp/dynatrace_client.py)). The **"Investigate latest live
186-
Dynatrace problem"** action auto-pulls a real open problem and runs the full pipeline on it;
187-
the finished RCA can be **pushed back onto the problem** as a comment (closing the loop).
184+
This is the heart of the Dynatrace track. The `TraceArchaeologist` agent is a **Model
185+
Context Protocol client** ([`dynatrace_mcp.py`](backend/connectors/dynatrace_mcp.py)) that
186+
connects to the **official Dynatrace MCP server** (`@dynatrace-oss/dynatrace-mcp-server`)
187+
and calls real MCP tools to investigate live incidents:
188188

189-
| Capability | Endpoint | Token scope |
190-
|:---------------|:----------------------------------|:-------------------|
191-
| Open problems | `GET /api/v2/problems` | `problems.read` |
192-
| Problem detail | `GET /api/v2/problems/{id}` | `problems.read` |
193-
| Metrics | `GET /api/v2/metrics/query` | `metrics.read` |
194-
| Topology | `GET /api/v2/entities` | `entities.read` |
195-
| Logs | `GET /api/v2/logs/search` | `logs.read` |
196-
| Push RCA back | `POST /api/v2/problems/{id}/comments` | `problems.write` |
189+
| MCP tool | What SpecterOps does with it |
190+
|:---|:---|
191+
| **`list_problems`** | Pull live open problems from the tenant (the picker + the chosen incident) |
192+
| **`execute_dql`** | Fetch logs / events / spans / metrics via Grail DQL as investigation evidence |
193+
| **`find_entity_by_name`** | Resolve the affected monitored entity |
197194

198-
`DEMO_MODE=true` uses a deterministic simulation ([`scenarios.py`](backend/mcp/scenarios.py))
199-
so the system runs with **zero accounts**; `DEMO_MODE=false` investigates **real** problems.
200-
Response time (µs) and failure rate (%) from live Dynatrace are unit-normalized; values that
201-
can't be resolved are hidden rather than shown wrong.
195+
A single MCP server process + session is spawned and reused, so the OAuth login happens once.
196+
`DEMO_MODE=true` uses a deterministic simulation ([`scenarios.py`](backend/connectors/scenarios.py))
197+
so the system also runs with **zero accounts**; a classic Dynatrace REST client remains as a
198+
legacy fallback. Live values are unit-normalized; anything that can't be resolved is hidden
199+
rather than shown wrong.
202200

203201
### Environment variables & going live
204202

205203
Everything runs with no keys. Each one upgrades a capability from demo → real — see the full
206-
step-by-step **[INTEGRATIONS.md](INTEGRATIONS.md)** (Gemini, Dynatrace, MongoDB, Firebase, Slack).
204+
step-by-step **[INTEGRATIONS.md](INTEGRATIONS.md)** (Gemini, Dynatrace, Firestore, Firebase, Slack).
207205

208206
| Variable | Unlocks |
209207
|:---|:---|
210-
| `GEMINI_API_KEY` (+ `GEMINI_MODEL=gemini-2.5-flash`) | Real AI reasoning (vs offline fallback) |
208+
| `GOOGLE_GENAI_USE_VERTEXAI=true` + `GOOGLE_CLOUD_PROJECT` + `GOOGLE_CLOUD_LOCATION` | Gemini reasoning on **Vertex AI** (Google Cloud) |
211209
| `DEMO_MODE=false` + `DYNATRACE_URL` + `DYNATRACE_API_TOKEN` | Investigate real Dynatrace problems |
212-
| `MONGODB_URI` | Persistent incident history |
210+
| `GOOGLE_CLOUD_PROJECT` (+ Firestore enabled) | Persistent incident history on **Cloud Firestore** |
213211
| `VITE_FIREBASE_*` (in `frontend/.env`) | Real Google / email sign-in |
214212
| `SLACK_WEBHOOK_URL` | Deliver post-mortems to Slack |
215213

@@ -240,17 +238,17 @@ DEMO_MODE=true python -m pytest # 7 passed
240238
```
241239
SpecterOps/
242240
├── .github/workflows/ci.yml # backend tests + frontend build
243-
├── render.yaml # Render Blueprint (backend deploy)
244241
├── firebase.json · .firebaserc # Firebase Hosting (frontend deploy)
245-
├── INTEGRATIONS.md # go-live guide (Gemini · Dynatrace · Mongo · Firebase · Slack)
242+
├── backend/Dockerfile # container → Google Cloud Run (Node + Python + MCP)
243+
├── INTEGRATIONS.md # go-live guide (Gemini · Dynatrace · Firestore · Firebase · Slack)
246244
├── LICENSE # MIT
247245
├── backend/ # FastAPI app
248246
│ ├── agents/ # orchestrator + the 4 agents
249-
│ ├── mcp/ # Dynatrace client + scenario engine
247+
│ ├── connectors/ # Dynatrace MCP client + scenario engine
250248
│ ├── models/ # Pydantic domain models
251249
│ ├── routes/ # incidents · webhook · SSE stream · config
252250
│ ├── utils/ # Gemini client (model fallback chain)
253-
│ ├── storage.py # MongoDB persistence (graceful fallback)
251+
│ ├── storage.py # Cloud Firestore persistence (graceful fallback)
254252
│ └── tests/ # pytest suite
255253
└── frontend/ # React + Vite dashboard
256254
└── src/components/ # launcher · agents · D3 graph · evidence · post-mortem · auth
@@ -274,7 +272,7 @@ SpecterOps/
274272
## 🗺️ Roadmap
275273

276274
Already shipped: live Dynatrace problem pull + RCA push-back, scenario library + custom
277-
incidents, MongoDB persistence, Firebase auth, Slack delivery. Next:
275+
incidents, Cloud Firestore persistence, Firebase auth, Slack delivery. Next:
278276

279277
- 🤖 Approval-gated **auto-remediation execution** (wire the proposed fix into CI/CD)
280278
- 👥 **Per-user Dynatrace** connect (multi-tenant, OAuth) so each user investigates their own tenant
@@ -290,5 +288,5 @@ incidents, MongoDB persistence, Firebase auth, Slack delivery. Next:
290288

291289
<div align="center">
292290
<br/>
293-
<sub>Built with Gemini 2.5 Flash · Dynatrace · Render · Firebase — for the Google Cloud Rapid Agent Hackathon 2026.</sub>
291+
<sub>Built with Gemini 2.5 Flash on Vertex AI · Dynatrace MCP · Cloud Run · Firebase — for the Google Cloud Rapid Agent Hackathon 2026.</sub>
294292
</div>

backend/.dockerignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
venv/
2+
**/__pycache__/
3+
*.pyc
4+
.env
5+
*.log
6+
.pytest_cache/
7+
_*.py

backend/.gcloudignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Keep the Cloud Build upload small and free of secrets / local junk.
2+
venv/
3+
**/__pycache__/
4+
*.pyc
5+
.env
6+
*.log
7+
.pytest_cache/
8+
_*.py

backend/Dockerfile

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
11
FROM python:3.11-slim
22
WORKDIR /app
3+
4+
# Node.js — required to run the official Dynatrace MCP server
5+
# (@dynatrace-oss/dynatrace-mcp-server) that the agent connects to as an MCP client.
6+
RUN apt-get update \
7+
&& apt-get install -y --no-install-recommends curl ca-certificates gnupg \
8+
&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
9+
&& apt-get install -y --no-install-recommends nodejs \
10+
&& npm install -g @dynatrace-oss/dynatrace-mcp-server \
11+
&& apt-get purge -y curl gnupg && apt-get autoremove -y \
12+
&& apt-get clean && rm -rf /var/lib/apt/lists/*
13+
314
COPY requirements.txt .
415
RUN pip install --no-cache-dir -r requirements.txt
516
COPY . .
617
EXPOSE 8080
7-
# Bind the platform-provided $PORT (Render, Cloud Run, etc.); default 8080 locally.
18+
# Bind the platform-provided $PORT (Cloud Run, Render, etc.); default 8080 locally.
819
CMD ["sh", "-c", "uvicorn main:app --host 0.0.0.0 --port ${PORT:-8080} --workers 1"]

backend/agents/blame_agent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from datetime import datetime, timedelta
1212
from typing import Any, Dict
1313

14-
from mcp import scenarios
14+
from connectors import scenarios
1515
from models.incident import (
1616
AgentStep,
1717
CausalEdge,

backend/agents/narrator_agent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from typing import List
1010

11-
from mcp import scenarios
11+
from connectors import scenarios
1212
from models.incident import AgentStep, Incident
1313
from utils.gemini_client import gemini_reason
1414

backend/agents/orchestrator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from agents.trace_agent import run_trace_archaeologist
2323
from models.incident import Incident, IncidentStatus
2424

25-
# In-memory stores (sufficient for the demo; swap for MongoDB in production).
25+
# In-memory hot cache; durably mirrored to Google Cloud Firestore (see storage.py).
2626
INCIDENTS: Dict[str, Incident] = {}
2727
SSE_QUEUES: Dict[str, List[asyncio.Queue]] = {}
2828
# Per-incident event log so late subscribers can replay everything they missed.

backend/agents/sentinel_agent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import json
1111
from typing import Any, Dict
1212

13-
from mcp import scenarios
13+
from connectors import scenarios
1414
from models.incident import AgentStep, Incident, IncidentSeverity
1515
from utils.gemini_client import gemini_json
1616

0 commit comments

Comments
 (0)