Atomic SRE is an open-source, LangGraph-based orchestration engine that automates the heavy lifting of Site Reliability Engineering. It pulls error logs out of CloudWatch, reads the relevant source code straight from GitHub over the Model Context Protocol (MCP), and returns a validated, structured root-cause diagnosis to your Slack channel.
- Why Atomic SRE?
- Key Capabilities
- Quick Start
- Architecture Under the Hood
- Operational Modes
- Integration Matrix
- Evaluation & Tracing
- Configuration
- Project Structure
- For Developers
- Community & Contributions
Modern microservice architectures are incredibly resilient, but when they fail, they fail in complex, cascading ways. Traditional observability platforms will alert you that an issue has occurred, and a PagerDuty ping will wake you up at 3 AM. However, the human operator is still left to:
- Trudge through thousands of log lines to find the relevant error trace.
- Cross-reference the log's service with the exact commit in the Git repository.
- Understand the surrounding code context to find the root cause.
- Formulate and deploy a fix.
Atomic SRE automates steps 1 through 4. Given a log group and a service name, it queries the relevant CloudWatch logs, reads the code repository dynamically via the Model Context Protocol (MCP), and drives an LLM reasoning loop until it produces a complete diagnosis with suggested fixes, posted directly to your Slack channel.
You are not just getting an alert; you are getting a review-ready analysis of what broke and why.
Atomic SRE does not just read stack traces; it reasons about them. The CloudWatch toolset filters on $.log_processed.severity = "error" and $.log_processed.service in your structured JSON logs, so the agent starts from the exact failing component rather than a wall of noise.
Atomic SRE connects to the hosted GitHub MCP server and uses search_code and get_file_contents to pull the relevant source in real time, exactly as a human developer would when debugging. The repository is never cloned or held in memory.
The reasoning loop only terminates when the model emits an ErrorDiagnosis tool call that passes Pydantic validation. Malformed output is rejected and fed back to the model as a structured error so it can retry, which means a run either produces a well-formed diagnosis or fails loudly.
Atomic SRE is designed to augment, not blindly replace, human engineers. Diagnoses are posted to your configured Slack channel through the Slack MCP sidecar for review. No code is committed and no infrastructure is mutated on the agent's own initiative.
- Python 3.12+ (the formatter, linter and Docker image target 3.13)
uvfor dependency management (the installer script will fetch it for you)- Docker for the Slack MCP sidecar and for the remote deployment mode
- AWS credentials with
logs:FilterLogEventson the log groups you want to diagnose
Recommended: Direct Installer (via curl)
This script verifies your environment, installs the uv package manager, clones the repository, syncs dependencies, and drops an atomic-sre wrapper into ~/.local/bin (adding it to your PATH if needed):
curl -sSL https://raw.githubusercontent.com/DsThakurRawat/Atomic-SRE/main/install.sh | bashAlternative: Install from source Not yet published to PyPI, so install directly from the repository:
pip install git+https://github.com/DsThakurRawat/Atomic-SRE.gitOr clone and sync with uv:
git clone https://github.com/DsThakurRawat/Atomic-SRE.git
cd Atomic-SRE && uv sync --devFor installer or source checkouts:
cd Atomic-SRE && uv run atomic-sreFor pip installations (or via the installer's wrapper):
atomic-sreThe Setup Wizard On first run, an interactive
questionarywizard walks you through every required credential and writes them to your user env file. It collects:
- Model provider and model: Anthropic, OpenAI, Groq, Google Gemini, OpenRouter, Ollama, or Amazon Bedrock, with the matching key (
ANTHROPIC_API_KEY,OPENAI_API_KEY,GROQ_API_KEY,GOOGLE_API_KEY,OPENROUTER_API_KEY) orOLLAMA_HOST. The default model isclaude-sonnet-4-6.- GitHub:
GITHUB_PERSONAL_ACCESS_TOKEN,GITHUB_OWNER,GITHUB_REPO,GITHUB_REF- Slack:
SLACK_BOT_TOKEN,SLACK_CHANNEL_ID- AWS: either
AWS_PROFILEorAWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY(plus optionalAWS_SESSION_TOKEN), andAWS_REGION
Settings are not read from a .env in the repository root. settings/paths.py resolves the platform user config directory via platformdirs, and every settings class points there:
| Platform | Path |
|---|---|
| Linux | ~/.config/atomic-sre/.env and config.json |
| macOS | ~/Library/Application Support/atomic-sre/.env |
| Windows | %LOCALAPPDATA%\atomic-sre\.env |
The headless entrypoint (python -m atomic_sre.run) calls load_dotenv on that same file. If you want a repo-local .env to take effect, you must export it into the environment yourself.
Atomic SRE is built on LangGraph and LangChain, using the Model Context Protocol (MCP) to fetch source code on demand rather than keeping a repository in context. The reasoning loop is a hand-built StateGraph, not a prebuilt ReAct agent, so termination is explicit and validated.
sequenceDiagram
participant Trigger as CLI / ECS Task
participant Engine as Atomic SRE Engine
participant CW as AWS CloudWatch
participant LLM as LLM (bound tools)
participant MCP as GitHub MCP
participant Slack as Slack MCP
Trigger->>Engine: diagnose_error(log_group, service, minutes)
Engine->>LLM: System prompt + diagnosis prompt
rect rgb(20, 20, 25)
Note over Engine,MCP: Autonomous tool loop (agent <-> tools)
LLM-->>Engine: ToolCall: search_error_logs
Engine->>CW: filter_log_events (severity=error, service=...)
CW-->>Engine: Structured JSON log entries
LLM-->>Engine: ToolCall: search_code
Engine->>MCP: Search repository for the failing symbol
MCP-->>Engine: Matching files and line hits
LLM-->>Engine: ToolCall: get_file_contents
Engine->>MCP: Read file at GITHUB_REF
MCP-->>Engine: Source code content
end
LLM-->>Engine: ToolCall: conversations_add_message
Engine->>Slack: Post findings to SLACK_CHANNEL_ID
LLM-->>Engine: ToolCall: ErrorDiagnosis (structured)
Engine->>Engine: Pydantic validation
Engine-->>Trigger: Validated ErrorDiagnosis
The graph compiled in engine/orchestrator.py has exactly two nodes, agent and tools, wired by the route_after_model conditional edge. Termination is driven by validation, not by a step counter:
stateDiagram-v2
[*] --> agent: START
state agent {
CallModel: Invoke LLM with bound tools
Validate: Validate any ErrorDiagnosis call
}
state Route <<choice>>
agent --> Route: route_after_model
Route --> tools: Tool calls pending
Route --> agent: Last message is a ToolMessage
Route --> [*]: Diagnosis validated (END)
state tools {
ToolNode: Execute CloudWatch / GitHub / Slack tools
}
tools --> agent
Validation rules enforced in _handle_diagnosis_validation:
- A single
ErrorDiagnosiscall that validates is stashed inAgentState["diagnosis"]and routes toEND. - A call that fails Pydantic validation is answered with a
ToolMessagenaming the offending fields, so the model can correct itself and retry. - Multiple concurrent
ErrorDiagnosiscalls in one turn are rejected outright. - If the graph ends without a valid diagnosis,
diagnose_errorraisesRuntimeErrorrather than returning a partial result.
Tools come from three places and are passed through a strict allowlist in _filter_mcp_tools:
| Tool | Source | Purpose |
|---|---|---|
search_error_logs |
Direct boto3 (engine/tools/cloudwatch.py) |
Filter a log group by severity, service, and time window |
search_code |
GitHub MCP (streamable_http) |
Locate the failing symbol across the repository |
get_file_contents |
GitHub MCP (streamable_http) |
Read a specific file at the configured ref |
conversations_add_message |
Slack MCP (sse, port 13080) |
Post findings to the configured channel |
ErrorDiagnosis |
Pydantic model bound as a tool | Terminate the loop with a structured result |
Everything else exposed by those MCP servers is filtered out. If the Slack MCP server is unreachable, a fallback conversations_add_message tool is registered that logs to the console, so the graph still runs end to end.
A successful run returns an ErrorDiagnosis (engine/models.py):
class ErrorDiagnosis(BaseModel):
summary: str # Brief summary of the issue
root_cause: str # Identified root cause
affected_services: list[str] # Services affected by this issue
suggested_fixes: list[SuggestedFix] # description, file_path, code_snippet
related_logs: list[str] # Key log messages related to the issue
timestamp: datetime # When the diagnosis was createdThe interactive shell (interfaces/interactive_shell.py) offers two modes.
Ideal for testing, debugging, or ad-hoc diagnostics from your machine. You are prompted for a CloudWatch log group, the Slack MCP sidecar is started for you with docker compose up -d slack, and diagnose_error runs in-process against remote CloudWatch and GitHub.
Packages the agent as a container and runs it as a one-off ECS Fargate task. The provisioning code lives under infrastructure/aws/ and the guided flow runs these steps in order:
- Network (VPC and private subnets)
- Security group
- Secrets (credentials into AWS Secrets Manager)
- IAM roles (task and execution roles)
- ECR repositories
- Build and push the agent image
- Task definition
- ECS cluster
Once deployed, the AWS ECS menu offers Run diagnosis job, Check deployment status, Repair deployment, Redeploy, and Clean up deployment. Diagnosis inputs are passed as container overrides on a one-off task, and cleanup tears down every resource the CLI created.
Bypasses the CLI entirely, which is what you want in CI or for debugging. Positional arguments fall back to the env vars LOG_GROUP, SERVICE_NAME and TIME_RANGE_MINUTES:
uv run python -m atomic_sre.run <log_group> <service_name> [time_range_minutes]Atomic SRE is designed to be extensible. Support is being added across the observability and LLM spectrum.
| Category | Fully Supported | Planned / In Development |
|---|---|---|
| Model Providers | Anthropic (Claude), OpenAI, Groq, Google Gemini, Ollama, OpenRouter, AWS Bedrock | vLLM (local inference) |
| Logging Platforms | AWS CloudWatch Logs | Google Cloud Observability, Azure Monitor, Datadog |
| Version Control | GitHub (via the hosted GitHub MCP server) | GitLab, Bitbucket |
| Notifications | Slack (via korotovsky/slack-mcp-server) |
Microsoft Teams, PagerDuty |
| Deployment | AWS ECS (one-off Fargate tasks), local Docker Compose | Kubernetes (Helm charts) |
Model identifiers accept either an explicit provider:model_id form (bedrock:anthropic.claude-..., ollama:llama3, openrouter:...) or a bare id whose provider is inferred from the prefix: claude* maps to Anthropic, gpt*/o1/o3/o4 to OpenAI, gemini* to Google, and anything else falls back to OpenAI.
Missing an integration you need for your stack? Open a feature request here.
LLMs can hallucinate, and in an SRE context hallucinations are dangerous. Atomic SRE ships an automated evaluation suite covering both tool-use behaviour and diagnosis quality, traced and scored through Opik. Both suites run against mocked CloudWatch, GitHub, and Slack toolsets, so they need no live infrastructure.
# Routing: does the agent select and order the right tools?
uv run atomic-sre-run-tool-call-eval
# Diagnostics: is the final root-cause analysis actually correct?
uv run atomic-sre-run-diagnosis-quality-evalGolden datasets live under src/atomic_sre/benchmarks/{routing,diagnostics}/dataset. The scoring metrics are:
| Suite | Metrics |
|---|---|
| Routing | expected_tool_selection, expected_tool_select_order, span_tools |
| Diagnostics | root_cause_correctness, affected_services_match, suggested_fixes_quality |
For detailed metric definitions, see the benchmark docs:
Beyond the setup wizard, Atomic SRE reads these variables from the user env file (see Where configuration lives) or the process environment.
| Variable | Description | Default |
|---|---|---|
MODEL |
LLM identifier for the main reasoning loop, bare or provider:model_id. |
claude-sonnet-4-6 |
ANTHROPIC_API_KEY |
Anthropic API key (Claude models). | None |
OPENAI_API_KEY |
OpenAI API key. | None |
GROQ_API_KEY |
Groq API key. | None |
GOOGLE_API_KEY |
Google API key (Gemini models). | None |
OPENROUTER_API_KEY |
OpenRouter API key. | None |
OLLAMA_HOST |
Host URL for a local Ollama instance. | http://localhost:11434 |
AWS_REGION |
Region for CloudWatch and ECS. | eu-west-2 |
AWS_ACCESS_KEY_ID |
AWS access key, if not using a profile. | None |
AWS_SECRET_ACCESS_KEY |
AWS secret key, if not using a profile. | None |
AWS_SESSION_TOKEN |
Session token for temporary credentials. | None |
AWS_PROFILE |
Named AWS profile, used instead of static keys. | None |
GITHUB_PERSONAL_ACCESS_TOKEN |
Required. Bearer token for the GitHub MCP server. | None |
GITHUB_OWNER |
Required. Repository owner. | None |
GITHUB_REPO |
Required. Repository name. | None |
GITHUB_REF |
Required. Branch, tag, or SHA to read code from. | None |
GITHUB_MCP_URL |
GitHub MCP endpoint. | https://api.githubcopilot.com/mcp/ |
SLACK_BOT_TOKEN |
Bot token consumed by the Slack MCP sidecar. | None |
SLACK_CHANNEL_ID |
Required. Target channel (Cxxxxxxxxxx). |
None |
SLACK_MCP_URL |
Slack MCP endpoint. | http://localhost:13080/sse |
OPIK_API_KEY |
Opik API key for tracing and evaluations. | None |
OPIK_WORKSPACE |
Optional Opik workspace override. | None |
The variables marked Required have no defaults. get_settings() raises a validation error if they are absent.
docker compose up -d brings up the agent container together with the Slack MCP sidecar (ghcr.io/korotovsky/slack-mcp-server) on port 13080. Compose reads its values from your shell environment, so export the variables above (or source your env file) before starting it. Start only the sidecar with docker compose up -d slack.
Atomic-SRE/
├── docs/imgs/ # CLI screenshots and banner assets
├── src/atomic_sre/
│ ├── engine/ # Reasoning core
│ │ ├── orchestrator.py # LangGraph StateGraph, model routing, MCP loading
│ │ ├── models.py # ErrorDiagnosis, SuggestedFix, LogEntry, LogQueryResult
│ │ ├── definitions.py # LoggingInterface / RepositoryInterface / MessagingInterface ABCs
│ │ ├── prompts.py # System prompt and diagnosis prompt builder
│ │ ├── settings.py # pydantic-settings config classes
│ │ └── tools/cloudwatch.py # Direct-API CloudWatch toolset
│ ├── infrastructure/aws/ # ECR, IAM, network, security groups, ECS tasks, secrets, cleanup
│ ├── interfaces/ # CLI surface
│ │ ├── main.py # Click entrypoint
│ │ ├── interactive_shell.py # Local / Remote Deployment menu
│ │ ├── setup/ # First-run configuration wizard
│ │ ├── handlers/ # local.py and remote/ (AWS ECS deployment flow)
│ │ └── console/ # Banner, styles, Rich console
│ ├── benchmarks/ # Opik evaluation suites
│ │ ├── routing/ # Tool selection and ordering
│ │ ├── diagnostics/ # Root-cause quality
│ │ └── shared/ # Shared case loader
│ ├── settings/paths.py # platformdirs-based user config paths
│ └── run.py # Headless single-shot entrypoint
├── tests/ # Pytest suite
├── docker-compose.yaml # Agent container plus Slack MCP sidecar
├── install.sh # Automated curl installer
├── pyproject.toml # Dependencies and script entrypoints
├── DEVELOPMENT.md # Developer guide
└── SPEC.md # Technical specification
Fork it, hack on it, improve it. DEVELOPMENT.md covers the internals in more depth, and AGENTS.md records the code conventions (UK English, Python 3.13 syntax, Google-style docstrings, no em dashes).
git clone https://github.com/DsThakurRawat/Atomic-SRE.git
cd Atomic-SRE
uv sync --dev
uv run atomic-sre # interactive CLIuv run pytest tests # full suite with coverage
uv run pytest tests/test_get_model.py::test_anthropic_provider_resolves # single test
uv run pre-commit run --all-files # ruff, mypy (strict), typos, bandit, trufflehogMypy runs in strict mode over src/. Tests and docs/ are excluded.
Prefer MCP. Add a Connection entry to the connections dict in _load_mcp_tools and extend the allowlist in _filter_mcp_tools, both in engine/orchestrator.py:
connections["example"] = {
"transport": "streamable_http", # or "sse" / "stdio"
"url": config.example.mcp_url,
}For a direct-API integration, implement the relevant ABC in engine/definitions.py (LoggingInterface, RepositoryInterface, MessagingInterface) and expose @tool-decorated callables from a create_*_toolset(config) factory, mirroring engine/tools/cloudwatch.py.
Built and maintained by DIVYANSH RAWAT, out of a personal obsession with running AI reliably in production.
The journey is documented in the open. Read more about the philosophy and technical deep-dives on the DIVYANSH RAWAT blog.
Contributions are highly welcomed. Whether it is adding a new logging provider, tweaking the prompts, or fixing a bug, please check the Contributing Guidelines and open a PR.


