Local-first, MCP-native multi-agent platform — job tracker + daily briefing for software engineers, with tracing, evals, and budgets.
Project description
Aegis
A local-first, MCP-native multi-agent platform you control. Aegis ships as a working personal job-tracker + daily briefing for software engineers — but the substrate underneath (agents, tracing, evals, MCP) is built to be lifted into any domain where you want LLM workflows you can debug, eval, and budget.
Built to be read, forked, and bent to your use case. Everything happens on your machine — your data, your API key, your prompts.
What you can do today
Out of the box — two workflows wired end-to-end:
| Workflow | What it does | Trigger |
|---|---|---|
| Job tracker | Paste a JD (text or URL). A Researcher → Analyst → Critic pipeline extracts the role, scores fit against your profile.yaml, and loops if the critic flags issues. Persists with cost, refinement count, and a trace link. |
add_job MCP tool, POST /jobs, or curl |
| Daily briefing | Fans out to GitHub (your activity + notifications), Hacker News, and your job pipeline. Prioritises items, synthesises a markdown briefing, then self-scores and refines. Falls open if a source fails. | run_briefing MCP tool, POST /briefing/run, or curl |
Inspect anything — every LLM call emits a span. Run aegis api and
open http://localhost:8000/trace/<run_id> for a tree of every prompt,
response, cost, and latency, or pull the JSON at /trace/<run_id>/json.
Catch regressions — YAML golden cases under evals/<eval_name>/cases/*.yaml
run through the real pipeline; the runner writes a row to eval_runs with
git SHA, score, and per-case detail.
Budgets that bite — per-run cap (AEGIS_LLM_MAX_COST_USD_PER_RUN) and
daily aggregate cap (AEGIS_LLM_MAX_COST_USD_PER_DAY) checked before every
LLM call. Tenacity retries handle transient Anthropic / Postgres failures.
What's actually reusable — the building blocks
Aegis is small (~3k LOC). Pick what you need:
- MCP server scaffold (
src/aegis/mcp_server.py) — FastMCP wiring with tools, resources, prompts, and a working Claude Desktop integration. Add a tool by writing one@mcp.tool()function. - Multi-agent framework (
src/aegis/agents/) —AgentBasegives every agent automatic tracing, cost attribution, and uniformAgentResult. Drop in a new agent by subclassing and writing_run. - LangGraph workflow pattern (
src/aegis/workflows/job_analysis/graph.py) — Researcher → Analyst → Critic with conditional refinement. Easy to copy and rewire into a different shape. - Tracing infra (
src/aegis/tracing/) — spans persisted to Postgres (agent_tracestable) with a built-in HTML viewer. No Datadog needed. - Eval harness (
scripts/run_eval.py,evals/*/cases/*.yaml) — golden cases as YAML, assertion-based scoring (not exact-match), regression rows ineval_runs. Each case still produces a trace you can open. - LLM gateway (
src/aegis/llm/gateway.py) — Anthropic direct (no LangChain), with cost tracking, per-run + per-day budgets, retry on 5xx / timeouts / rate-limits. - FastAPI surface (
src/aegis/app.py,src/aegis/api/) — REST routers for jobs, briefing, traces, health. Exposed viaaegis api. Health check returns 503 when degraded.
Quick start
Prerequisites
- Docker Desktop — Aegis runs Postgres + Redis locally.
- uv —
curl -LsSf https://astral.sh/uv/install.sh | sh - Anthropic API key — required. Generate at https://console.anthropic.com/settings/keys.
- GitHub personal access token — optional, only for the daily briefing.
Generate at https://github.com/settings/tokens. Classic token needs the
notificationsscope; fine-grained needsNotifications: Read. Without it, briefings still run but skip the GitHub section.
Install
uv tool install aegis-agents
aegis init # prompts for ANTHROPIC_API_KEY + GITHUB_TOKEN (optional)
aegis up # boots Postgres + Redis, applies migrations
aegis init writes ~/.config/aegis/{.env,compose.yml,profile.yaml}. Re-run
with --force to regenerate. Override the location with AEGIS_HOME=/path.
Edit ~/.config/aegis/profile.yaml to personalise fit scoring (skills, target
roles, deal-breakers).
Wire to Claude
Claude Code:
claude mcp add aegis -- aegis serve
Claude Desktop — add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"aegis": {
"command": "aegis",
"args": ["serve"]
}
}
}
Restart Claude. The aegis tools show up under the connector menu.
Daily ops
| Command | What it does |
|---|---|
aegis up |
Start Postgres + Redis (data preserved across restarts) |
aegis down |
Stop the stack |
aegis migrate |
Apply new migrations after an aegis-agents upgrade |
aegis doctor |
Diagnose prerequisites, config, and infra |
aegis serve |
Run the MCP server (what Claude calls) |
aegis api |
Run the REST API + trace viewer at http://localhost:8000/docs |
aegis init --force |
Regenerate ~/.config/aegis/ files |
Daily workflow
Morning briefing
In Claude: "Run my daily briefing" — fires run_briefing, returns
prioritised markdown with trace_url. There's no background scheduler;
briefings only fire when you (or Claude) ask.
Adding a job
- Copy a job posting (text or URL).
- In Claude: "Add this job: [paste]" →
add_jobruns Researcher → Analyst → Critic, loops up toAEGIS_MAX_REFINEMENTSrounds if the critic flags issues, then persists.
Or curl (requires aegis api running):
curl -X POST http://localhost:8000/jobs \
-H "Content-Type: application/json" \
-d '{"text": "Senior Backend Engineer at Stripe..."}'
Inspecting a run
- "List my recent Aegis runs" in Claude →
list_recent_runsshows newest-first runs withrun_idand trace links. - "Get the trace for
<run_id>" → flat span summary. - With
aegis apirunning,http://localhost:8000/trace/<run_id>→ full HTML tree with every prompt and response.
MCP surface
Tools
| Tool | What it does |
|---|---|
run_briefing |
Run a full briefing, return markdown + cost + trace pointer |
get_latest_briefing |
Most recent successful briefing |
add_job |
Run the multi-agent pipeline on a JD, persist + return analysis |
list_jobs |
List tracked jobs, optionally filtered by status |
update_job_status |
Move a job through the pipeline (saved → applied → …) |
get_follow_ups |
Stale-for-N-days jobs that need a nudge |
get_top_job_fits |
Saved jobs with fit_score >= min_score |
get_trace |
Span summary for a run_id |
list_recent_runs |
Newest-first run summaries with trace URLs |
Resources: aegis://briefing/latest, aegis://jobs/summary
Prompts: daily_briefing_prompt
Evals — keep prompts honest
The eval harness is maintainer tooling — it ships in the repo, not the package. Clone the repo and run:
uv run python scripts/seed_evals.py # mirror evals/*/cases/*.yaml → eval_golden_cases
uv run python scripts/run_eval.py job_fit_v1 # run the suite
Each YAML case declares assertions against the analysis (not exact-match output, since LLMs aren't deterministic):
case_id: stripe_senior_backend_apply
eval_name: job_fit_v1
input:
url_or_text: |
Senior Backend Engineer — Stripe (Remote, US) ...
expected:
recommendation: apply
fit_score: {min: 0.70, max: 1.00}
must_include_skills: [python, postgresql]
must_not_have_deal_breakers: true
The runner writes a row to eval_runs per invocation (git SHA, score,
pass count, per-case detail). Diff successive runs to track prompt
regressions.
When you hit a bad output in agent_traces, the recipe is: copy the JD
into a new case file with the correct expected, re-seed, re-run, fix the
prompt until it passes.
Profile configuration
aegis init writes ~/.config/aegis/profile.yaml from the bundled
template. Edit it to personalise fit-scoring and briefing output:
skills: [python, golang, distributed-systems]
target_roles: [backend-engineer, ai-engineer]
experience_years: 3
preferred_locations: [singapore, remote]
deal_breakers: [php, wordpress]
summary: |
Backend engineer with 3 years of experience building distributed
systems in Python and Go. Interested in AI / agent platforms.
Set AEGIS_PROFILE_PATH=/some/other/path.yaml to point Aegis at a
profile outside the default location.
Tuning
| Env var | Default | Purpose |
|---|---|---|
AEGIS_LLM_MODEL |
claude-sonnet-4-5 |
Anthropic model for every agent |
AEGIS_LLM_MAX_COST_USD_PER_RUN |
0.50 |
Hard cap per gateway instance |
AEGIS_LLM_MAX_COST_USD_PER_DAY |
5.0 |
Aggregate cap across the last 24h (0 disables) |
AEGIS_MAX_REFINEMENTS |
2 |
Max critic ⇄ analyst loops |
AEGIS_PROFILE_PATH |
~/.config/aegis/profile.yaml |
Where the candidate profile lives |
LOG_LEVEL |
INFO |
Loguru level — DEBUG for verbose tracing |
Project structure
aegis/
├── src/aegis/
│ ├── agents/ # Researcher, Analyst, Critic + auto-tracing base class
│ ├── workflows/
│ │ └── job_analysis/ # LangGraph wiring + service
│ ├── briefing/ # LangGraph daily-briefing nodes + graph + service
│ ├── jobs/ # Job service facade exposed by API and MCP
│ ├── sources/ # GitHub, HN, internal jobs source for the briefing
│ ├── llm/ # Anthropic gateway (cost-tracked, budget-capped, retrying)
│ ├── tracing/ # Span recorder + repository + HTML viewer
│ ├── db/ # asyncpg engine + repositories
│ ├── api/ # FastAPI routers (jobs, briefing, traces, health)
│ ├── app.py # FastAPI factory
│ └── mcp_server.py # FastMCP server (tools, resources, prompts)
├── migrations/ # Numbered SQL migrations
├── evals/ # YAML golden cases per eval_name
├── scripts/
│ ├── seed_evals.py # YAML → eval_golden_cases
│ └── run_eval.py # Run a suite, write eval_runs
├── tests/
│ ├── unit/ # Mocked LLM, no infra
│ └── integration/ # testcontainers, real Postgres
└── pyproject.toml
Development
Working on Aegis itself (rather than using it):
git clone https://github.com/nareus/aegis
cd aegis
uv sync # install deps + dev tools
aegis init # one-time, sets up ~/.config/aegis/
aegis up # start Postgres + Redis
uv run python -m aegis.cli serve # run MCP server from source
uv run pytest # full suite, all network mocked
uv run pytest tests/unit/ # fast, no Docker
uv run pytest tests/integration/ -v # spins up Postgres via testcontainers
uv run python scripts/seed_evals.py # mirror eval cases to Postgres
uv run python scripts/run_eval.py job_fit_v1
scripts/ and evals/ ship in the repo for regression testing and aren't
bundled into the published package.
What's deliberately not here (yet)
So you know what you're signing up for:
- No background scheduler — briefings run only when triggered.
- No auth on the FastAPI surface — assume loopback / firewalled. Add an API-key middleware before exposing publicly.
- No SSRF allow-list in the researcher fetch — fine for trusted local use; restrict before exposing to untrusted JD URLs.
- No LangGraph checkpointing — a pod restart mid-workflow loses the run.
- No multi-tenancy — single profile, single user, single DB.
These are intentional cuts for a local-first tool.
License
MIT. Fork it, rewire it, ship it.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file aegis_agents-0.1.1.tar.gz.
File metadata
- Download URL: aegis_agents-0.1.1.tar.gz
- Upload date:
- Size: 174.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.12 {"installer":{"name":"uv","version":"0.10.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
94e0afe0f32bba4fc2600f7530130bc6740a0fbd335b5e2afe408fbe7453b391
|
|
| MD5 |
5ed20e69440851a426cc7bbc055e5eb7
|
|
| BLAKE2b-256 |
81945b1463bafcfb8f37c5ed2fc280162d6e2e7e3fb48b45002e6a4e6de439b0
|
File details
Details for the file aegis_agents-0.1.1-py3-none-any.whl.
File metadata
- Download URL: aegis_agents-0.1.1-py3-none-any.whl
- Upload date:
- Size: 58.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.12 {"installer":{"name":"uv","version":"0.10.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
452f0429d040e996a0668b2c306e796649acaa07f9521841bf493f5963d772f5
|
|
| MD5 |
42a31de0ea1481424e1afe98b05bae43
|
|
| BLAKE2b-256 |
08ade9413b705d25eab4581bdf178b5374c7960ace44f45ff4bd1ec26f779b18
|