release: v3.29.1 - Git MCP + GitHub MCP catalog entries
Add Git MCP Server (12 tools, uvx setup) and GitHub MCP Server (Issues/PRs/Projects, remote Copilot + self-hosted PAT-only) to §8.2 MCP Server Catalog. Document real-world fix for Incompatible auth server error via gh auth token + manual header injection. Also ships: CC v2.1.63 tracking, HTTP hooks, observability quality patterns, config lifecycle §9.23, terminal personalization, tool comparison table extensions, MCP server 3 new tools. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
155b07a589
commit
252148fe75
20 changed files with 1802 additions and 34 deletions
208
examples/agents/cyber-defense/README.md
Normal file
208
examples/agents/cyber-defense/README.md
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
# Cyber Defense Agent Team
|
||||
|
||||
A 4-agent pipeline that detects security threats in log files. Built natively with Claude Code Agent Teams.
|
||||
|
||||
**This example exists to compare two approaches**: LangGraph (the Python framework) vs Claude Code Agent Teams (native). Same system, two architectures. The delta tells you when to use which.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
log-ingestor (haiku)
|
||||
↓
|
||||
anomaly-detector (sonnet)
|
||||
↓
|
||||
risk-classifier (sonnet)
|
||||
↓
|
||||
threat-reporter (sonnet)
|
||||
↓
|
||||
cyber-defense-report.md
|
||||
```
|
||||
|
||||
Each agent has a single responsibility and passes data to the next via shared JSON files. The orchestration skill (`/cyber-defense-team`) sequences the spawns and reports results.
|
||||
|
||||
**Usage**: `/cyber-defense-team /var/log/nginx/access.log`
|
||||
|
||||
---
|
||||
|
||||
## LangGraph vs Claude Code Agent Teams
|
||||
|
||||
Same system built twice. Here's the full comparison.
|
||||
|
||||
### The LangGraph Version (~150 lines of Python)
|
||||
|
||||
```python
|
||||
from typing import TypedDict, List
|
||||
from langgraph.graph import StateGraph, END
|
||||
from langgraph.checkpoint.memory import MemorySaver
|
||||
from langchain_core.tools import tool
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langchain_core.messages import HumanMessage, SystemMessage
|
||||
import json
|
||||
|
||||
# ─── State Definition ─────────────────────────────────────────────────────────
|
||||
class DefenseState(TypedDict):
|
||||
raw_logs: str
|
||||
parsed_events: List[dict]
|
||||
anomalies: List[dict]
|
||||
risk_level: str # LOW | MEDIUM | HIGH | CRITICAL
|
||||
report: str
|
||||
|
||||
# ─── Tools (Python functions the LLM can call) ────────────────────────────────
|
||||
@tool
|
||||
def detect_patterns(logs: str) -> List[dict]:
|
||||
"""Extract structured events from raw log text."""
|
||||
events = []
|
||||
for line in logs.split("\n"):
|
||||
if any(k in line for k in ["ERROR", "FAILED", "UNAUTHORIZED"]):
|
||||
events.append({"type": "security_event", "raw": line})
|
||||
elif "WARNING" in line:
|
||||
events.append({"type": "warning", "raw": line})
|
||||
return events
|
||||
|
||||
@tool
|
||||
def detect_anomalies(events: List[dict]) -> List[dict]:
|
||||
"""Detect statistical anomalies in event patterns."""
|
||||
anomalies = []
|
||||
error_count = sum(1 for e in events if e["type"] == "security_event")
|
||||
if error_count > 5:
|
||||
anomalies.append({"type": "high_error_rate", "count": error_count, "severity": "HIGH"})
|
||||
return anomalies
|
||||
|
||||
@tool
|
||||
def lookup_threat(event_type: str) -> dict:
|
||||
"""Look up known threat signatures."""
|
||||
db = {
|
||||
"UNAUTHORIZED": {"description": "Auth bypass attempt"},
|
||||
"SQL_INJECTION": {"cve": "CVE-2021-1234", "description": "SQLi pattern"}
|
||||
}
|
||||
return db.get(event_type, {"description": "Unknown threat"})
|
||||
|
||||
# ─── LLM Setup ────────────────────────────────────────────────────────────────
|
||||
llm = ChatOpenAI(model="gpt-4o-mini")
|
||||
llm_with_tools = llm.bind_tools([detect_patterns, detect_anomalies, lookup_threat])
|
||||
|
||||
# ─── Nodes (one function per agent role) ──────────────────────────────────────
|
||||
def ingest_node(state: DefenseState) -> DefenseState:
|
||||
events = detect_patterns.invoke(state["raw_logs"])
|
||||
return {**state, "parsed_events": events}
|
||||
|
||||
def detect_node(state: DefenseState) -> DefenseState:
|
||||
anomalies = detect_anomalies.invoke(state["parsed_events"])
|
||||
return {**state, "anomalies": anomalies}
|
||||
|
||||
def classify_node(state: DefenseState) -> DefenseState:
|
||||
result = llm_with_tools.invoke([
|
||||
SystemMessage("Classify risk as LOW/MEDIUM/HIGH/CRITICAL."),
|
||||
HumanMessage(f"Anomalies: {json.dumps(state['anomalies'])}")
|
||||
])
|
||||
risk = "HIGH" if state["anomalies"] else "LOW"
|
||||
return {**state, "risk_level": risk}
|
||||
|
||||
def report_node(state: DefenseState) -> DefenseState:
|
||||
result = llm_with_tools.invoke([
|
||||
SystemMessage("You are a Senior Security Analyst. Write a Markdown incident report."),
|
||||
HumanMessage(f"Risk: {state['risk_level']}\nAnomalies: {json.dumps(state['anomalies'])}")
|
||||
])
|
||||
return {**state, "report": result.content}
|
||||
|
||||
# ─── Conditional Edge ─────────────────────────────────────────────────────────
|
||||
def should_report(state: DefenseState) -> str:
|
||||
return "report" if state["anomalies"] else END
|
||||
|
||||
# ─── Graph Assembly ───────────────────────────────────────────────────────────
|
||||
builder = StateGraph(DefenseState)
|
||||
builder.add_node("ingest", ingest_node)
|
||||
builder.add_node("detect", detect_node)
|
||||
builder.add_node("classify", classify_node)
|
||||
builder.add_node("report", report_node)
|
||||
|
||||
builder.set_entry_point("ingest")
|
||||
builder.add_edge("ingest", "detect")
|
||||
builder.add_edge("detect", "classify")
|
||||
builder.add_conditional_edges("classify", should_report)
|
||||
builder.add_edge("report", END)
|
||||
|
||||
# ─── Memory ───────────────────────────────────────────────────────────────────
|
||||
checkpointer = MemorySaver()
|
||||
app = builder.compile(checkpointer=checkpointer)
|
||||
|
||||
# ─── Entry Point ──────────────────────────────────────────────────────────────
|
||||
def analyze_logs(logs: str) -> str:
|
||||
config = {"configurable": {"thread_id": "security-session-1"}}
|
||||
result = app.invoke(
|
||||
{"raw_logs": logs, "parsed_events": [], "anomalies": [], "risk_level": "", "report": ""},
|
||||
config
|
||||
)
|
||||
return result.get("report", "No threats detected.")
|
||||
```
|
||||
|
||||
### The Claude Code Version (~60 lines of YAML/Markdown)
|
||||
|
||||
Four agent files, one skill file. No graph assembly, no TypedDict, no boilerplate.
|
||||
|
||||
```
|
||||
examples/agents/cyber-defense/
|
||||
├── log-ingestor.md (~40 lines)
|
||||
├── anomaly-detector.md (~50 lines)
|
||||
├── risk-classifier.md (~55 lines)
|
||||
└── threat-reporter.md (~45 lines)
|
||||
|
||||
examples/skills/cyber-defense-team/
|
||||
└── SKILL.md (~70 lines)
|
||||
```
|
||||
|
||||
Each agent file is a YAML frontmatter (name, model, tools) + a plain English system prompt describing role, inputs, outputs, and constraints. The skill file sequences the agents with `Agent tool` calls.
|
||||
|
||||
---
|
||||
|
||||
## Side-by-Side Comparison
|
||||
|
||||
| Dimension | LangGraph | Claude Code Agent Teams |
|
||||
|-----------|-----------|------------------------|
|
||||
| **Total code** | ~150 lines Python | ~60 lines YAML/Markdown |
|
||||
| **State management** | Explicit `TypedDict` definition | Implicit — JSON files between agents |
|
||||
| **Memory** | Manual `MemorySaver` setup | Native (files persist by default) |
|
||||
| **Tool definition** | `@tool` decorated Python functions | MCP servers or built-in tools |
|
||||
| **Conditional logic** | `add_conditional_edges()` | Natural language in skill ("if no anomalies, skip to Step 5") |
|
||||
| **Model selection** | One model for all nodes | Per-agent (haiku for parsing, sonnet for reasoning) |
|
||||
| **Debugging** | `print()` + LangSmith (paid) | Native Claude Code UI |
|
||||
| **New agent role** | New function + `add_node()` + `add_edge()` | New `.md` file |
|
||||
| **Onboarding** | Learn LangGraph API | Read a Markdown file |
|
||||
| **Deployment** | FastAPI or Gradio (~50 more lines) | `claude` CLI, done |
|
||||
| **Dependencies** | `langgraph`, `langchain`, `langchain-openai` | None (built into Claude Code) |
|
||||
|
||||
---
|
||||
|
||||
## When to Use Which
|
||||
|
||||
**Use Claude Code Agent Teams when:**
|
||||
- You want to move fast — prototype to working system in 30 minutes
|
||||
- Your team includes non-developers who may read or edit agent prompts
|
||||
- The pipeline is internal tooling (not a public API endpoint)
|
||||
- You need to iterate on agent behavior frequently (edit a `.md` file, done)
|
||||
|
||||
**Use LangGraph when:**
|
||||
- You need to embed the system inside a larger Python application
|
||||
- You want to expose the pipeline as a REST API for external consumers
|
||||
- You need deterministic state transitions that must be unit-tested
|
||||
- Your team is already Python-native and has LangGraph expertise
|
||||
|
||||
**The honest tradeoff**: LangGraph gives you more programmatic control and Python ecosystem access. Claude Code Agent Teams give you less boilerplate, faster iteration, and no infrastructure to maintain. For internal tooling and knowledge work pipelines, the Claude Code approach typically wins on total cost of ownership.
|
||||
|
||||
---
|
||||
|
||||
## Files in This Example
|
||||
|
||||
| File | Agent Role | Model | Responsibility |
|
||||
|------|-----------|-------|----------------|
|
||||
| `log-ingestor.md` | Stage 1 | haiku | Parse raw logs → `cyber-defense-events.json` |
|
||||
| `anomaly-detector.md` | Stage 2 | sonnet | Detect patterns → `cyber-defense-anomalies.json` |
|
||||
| `risk-classifier.md` | Stage 3 | sonnet | Score risk → `cyber-defense-risk.json` |
|
||||
| `threat-reporter.md` | Stage 4 | sonnet | Generate → `cyber-defense-report.md` |
|
||||
| `../skills/cyber-defense-team/SKILL.md` | Orchestrator | — | Sequence agents, handle errors, summarize |
|
||||
|
||||
---
|
||||
|
||||
**Inspired by**: Maryam Miradi's SMART COMPASS framework — same system, different stack.
|
||||
71
examples/agents/cyber-defense/anomaly-detector.md
Normal file
71
examples/agents/cyber-defense/anomaly-detector.md
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
---
|
||||
name: anomaly-detector
|
||||
description: Detect statistical anomalies and attack patterns from structured security events. Second stage of the cyber defense pipeline — reads cyber-defense-events.json and produces anomalies.
|
||||
model: sonnet
|
||||
tools: Read
|
||||
---
|
||||
|
||||
# Anomaly Detector Agent
|
||||
|
||||
Second stage. Read structured events from `cyber-defense-events.json`, detect anomalies and known attack patterns.
|
||||
|
||||
**Role**: Pattern recognition and anomaly scoring. No classification of severity — that's the risk-classifier's job.
|
||||
|
||||
## Input
|
||||
|
||||
Read `cyber-defense-events.json` produced by log-ingestor.
|
||||
|
||||
## Detection Rules
|
||||
|
||||
### Volume Anomalies
|
||||
- AUTH_FAILURE > 10 in any 5-minute window → brute force attempt
|
||||
- Same source IP appearing in > 5 AUTH_FAILURE events → credential stuffing
|
||||
- ERROR spike > 3x baseline → potential DoS or application crash
|
||||
|
||||
### Pattern Anomalies
|
||||
- Sequential port scanning signatures in source IPs
|
||||
- SQL keywords in request paths (`SELECT`, `UNION`, `DROP`, `--`)
|
||||
- Path traversal patterns (`../`, `%2e%2e`, `..%2F`)
|
||||
- XSS vectors (`<script>`, `javascript:`, `onerror=`)
|
||||
|
||||
### Behavioral Anomalies
|
||||
- Access to `/admin`, `/config`, `/.env`, `/.git` from external IPs
|
||||
- High-frequency requests from single IP (> 100/min)
|
||||
- Off-hours activity if timestamps available
|
||||
|
||||
## Output Format
|
||||
|
||||
Write detected anomalies to `cyber-defense-anomalies.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"anomalies_found": 3,
|
||||
"anomalies": [
|
||||
{
|
||||
"id": "A001",
|
||||
"type": "BRUTE_FORCE",
|
||||
"confidence": 0.94,
|
||||
"description": "23 AUTH_FAILURE events from IP 192.168.1.105 in 8 minutes",
|
||||
"affected_events": [1, 4, 7, 12],
|
||||
"source_ip": "192.168.1.105",
|
||||
"evidence": "23 failures, 0 successes from same IP"
|
||||
},
|
||||
{
|
||||
"id": "A002",
|
||||
"type": "SQL_INJECTION",
|
||||
"confidence": 0.87,
|
||||
"description": "SQLi pattern detected in /api/users endpoint",
|
||||
"affected_events": [34],
|
||||
"source_ip": "10.0.0.44",
|
||||
"evidence": "Request contained 'UNION SELECT' in path parameter"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Constraints
|
||||
|
||||
- Report confidence score (0.0-1.0) for each anomaly — don't be binary
|
||||
- Link anomalies to specific event IDs from cyber-defense-events.json
|
||||
- If zero anomalies: write `{"anomalies_found": 0, "anomalies": []}` and report "No anomalies detected. Logs appear clean."
|
||||
- Do not suggest risk levels — that's risk-classifier's scope
|
||||
62
examples/agents/cyber-defense/log-ingestor.md
Normal file
62
examples/agents/cyber-defense/log-ingestor.md
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
---
|
||||
name: log-ingestor
|
||||
description: Parse raw logs into structured security events. First stage of the cyber defense pipeline — reads log files and extracts typed events (errors, warnings, auth failures, anomalies).
|
||||
model: haiku
|
||||
tools: Read, Glob
|
||||
---
|
||||
|
||||
# Log Ingestor Agent
|
||||
|
||||
First stage of the cyber defense pipeline. Parse raw logs and produce structured event data for downstream agents.
|
||||
|
||||
**Role**: Read logs → extract structured events. No analysis, no judgment — pure parsing.
|
||||
|
||||
## Input
|
||||
|
||||
Raw log content passed in the task description, or a file path to read.
|
||||
|
||||
## Process
|
||||
|
||||
1. Read the log content
|
||||
2. Classify each line by event type:
|
||||
- `AUTH_FAILURE` — failed login, unauthorized access, permission denied
|
||||
- `SECURITY_EVENT` — known attack patterns (SQLi, XSS, path traversal)
|
||||
- `ERROR` — application errors with stack traces
|
||||
- `WARNING` — non-critical anomalies
|
||||
- `INFO` — normal operations (include for baseline)
|
||||
3. Extract metadata per event: timestamp, source IP (if present), service, message
|
||||
|
||||
## Output Format
|
||||
|
||||
Write parsed events to a shared file `cyber-defense-events.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"total_lines": 842,
|
||||
"parsed_events": [
|
||||
{
|
||||
"id": 1,
|
||||
"type": "AUTH_FAILURE",
|
||||
"timestamp": "2024-01-15T14:23:01Z",
|
||||
"source_ip": "192.168.1.105",
|
||||
"service": "nginx",
|
||||
"message": "user 'admin' failed login from 192.168.1.105",
|
||||
"raw": "[2024-01-15 14:23:01] FAILED LOGIN: user 'admin'..."
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"AUTH_FAILURE": 23,
|
||||
"SECURITY_EVENT": 4,
|
||||
"ERROR": 17,
|
||||
"WARNING": 89,
|
||||
"INFO": 709
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Constraints
|
||||
|
||||
- Do not interpret or analyze — only classify and structure
|
||||
- If timestamp is missing, use `"timestamp": null`
|
||||
- If source IP is absent, use `"source_ip": null`
|
||||
- Write the JSON file, then report: "Ingested X lines → Y events (Z security-relevant)"
|
||||
71
examples/agents/cyber-defense/risk-classifier.md
Normal file
71
examples/agents/cyber-defense/risk-classifier.md
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
---
|
||||
name: risk-classifier
|
||||
description: Classify overall risk level from detected anomalies. Third stage of the cyber defense pipeline — reads cyber-defense-anomalies.json and assigns CRITICAL/HIGH/MEDIUM/LOW with justification.
|
||||
model: sonnet
|
||||
tools: Read
|
||||
---
|
||||
|
||||
# Risk Classifier Agent
|
||||
|
||||
Third stage. Read `cyber-defense-anomalies.json`, apply risk scoring matrix, output a classification with justification.
|
||||
|
||||
**Role**: Translate technical anomalies into a business risk decision. One output: a risk level + rationale.
|
||||
|
||||
## Input
|
||||
|
||||
Read `cyber-defense-anomalies.json` produced by anomaly-detector.
|
||||
|
||||
## Risk Scoring Matrix
|
||||
|
||||
### CRITICAL (immediate action required)
|
||||
- Active exploitation confirmed (successful auth after brute force)
|
||||
- Data exfiltration indicators (large outbound transfers, DB dumps)
|
||||
- Ransomware or malware execution patterns
|
||||
- Compromise of admin credentials
|
||||
|
||||
### HIGH (respond within 1 hour)
|
||||
- Brute force attack in progress (no success yet)
|
||||
- SQL injection or path traversal detected
|
||||
- Multiple anomaly types from same source
|
||||
- Privilege escalation attempts
|
||||
|
||||
### MEDIUM (respond within 24 hours)
|
||||
- Isolated SQLi probe (single attempt, low confidence)
|
||||
- Off-hours access from known internal IP
|
||||
- Moderate error spike without clear attack pattern
|
||||
- Single high-confidence anomaly, low business impact
|
||||
|
||||
### LOW (monitor, no immediate action)
|
||||
- Reconnaissance patterns only (port scan, fingerprinting)
|
||||
- Single auth failure from unknown IP
|
||||
- Low-confidence anomalies (< 0.5)
|
||||
- Zero anomalies → always LOW
|
||||
|
||||
## Output Format
|
||||
|
||||
Write classification to `cyber-defense-risk.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"risk_level": "HIGH",
|
||||
"score": 74,
|
||||
"primary_threat": "BRUTE_FORCE",
|
||||
"rationale": "Active brute force attack from 192.168.1.105 (23 failures, still ongoing based on timestamps). No successful auth yet — window still open. SQL injection probe from separate IP adds compounding risk.",
|
||||
"anomalies_considered": ["A001", "A002"],
|
||||
"recommended_action": "Block IP 192.168.1.105 immediately. Review /api/users access logs for A002 source IP. Check for any successful logins in the last 30 minutes.",
|
||||
"escalate_to_human": true
|
||||
}
|
||||
```
|
||||
|
||||
## Decision Rules
|
||||
|
||||
- If anomalies_found = 0 → always `LOW`, `escalate_to_human: false`
|
||||
- If any anomaly confidence > 0.9 AND type is BRUTE_FORCE or SQL_INJECTION → minimum `HIGH`
|
||||
- If multiple anomaly types from same source IP → upgrade one level
|
||||
- `escalate_to_human: true` for HIGH and CRITICAL
|
||||
|
||||
## Constraints
|
||||
|
||||
- One risk level, not a range
|
||||
- Rationale must reference specific anomaly IDs
|
||||
- `recommended_action` must be concrete (not "monitor the situation")
|
||||
75
examples/agents/cyber-defense/threat-reporter.md
Normal file
75
examples/agents/cyber-defense/threat-reporter.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
---
|
||||
name: threat-reporter
|
||||
description: Generate a human-readable security incident report. Final stage of the cyber defense pipeline — reads all three JSON files and produces a Markdown report for security teams.
|
||||
model: sonnet
|
||||
tools: Read, Write
|
||||
---
|
||||
|
||||
# Threat Reporter Agent
|
||||
|
||||
Final stage. Read all pipeline outputs, synthesize a structured incident report in Markdown.
|
||||
|
||||
**Role**: Translate machine-readable analysis into a clear, actionable report for security teams (and non-technical stakeholders).
|
||||
|
||||
## Input
|
||||
|
||||
Read all three files produced by previous agents:
|
||||
1. `cyber-defense-events.json` — raw event stats
|
||||
2. `cyber-defense-anomalies.json` — detected anomalies
|
||||
3. `cyber-defense-risk.json` — risk classification
|
||||
|
||||
## Report Structure
|
||||
|
||||
```markdown
|
||||
# Security Incident Report
|
||||
**Generated**: [ISO timestamp]
|
||||
**Risk Level**: [CRITICAL|HIGH|MEDIUM|LOW] — [score]/100
|
||||
**Requires Human Review**: [Yes|No]
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
[2-3 sentences. What happened, how bad, what to do right now.
|
||||
Written for non-technical readers.]
|
||||
|
||||
## Threat Details
|
||||
|
||||
### [Anomaly ID] — [Anomaly Type]
|
||||
- **Confidence**: [X]%
|
||||
- **Source**: [IP if available]
|
||||
- **Description**: [What was detected and why it matters]
|
||||
- **Evidence**: [Key data points]
|
||||
|
||||
## Recommended Actions
|
||||
|
||||
### Immediate (do now)
|
||||
- [ ] [Specific action with target]
|
||||
|
||||
### Short-term (within 24h)
|
||||
- [ ] [Specific action]
|
||||
|
||||
### Monitoring
|
||||
- [ ] [What to watch for]
|
||||
|
||||
## Log Statistics
|
||||
| Metric | Count |
|
||||
|--------|-------|
|
||||
| Total log lines | X |
|
||||
| Security-relevant events | X |
|
||||
| Anomalies detected | X |
|
||||
|
||||
---
|
||||
*Report generated by cyber-defense Agent Team — Claude Code*
|
||||
```
|
||||
|
||||
## Writing Guidelines
|
||||
|
||||
- Executive Summary: one sentence for each of: what happened, severity, immediate action. Non-technical language.
|
||||
- Threat Details: technical precision. Include IPs, timestamps, confidence scores.
|
||||
- Actions: specific and measurable. Not "review logs" but "grep auth.log for 192.168.1.105 and count successful logins after 14:15 UTC."
|
||||
- If risk_level = LOW and no anomalies: the executive summary should be one sentence: "Log analysis complete — no threats detected."
|
||||
|
||||
## Output
|
||||
|
||||
Write the report to `cyber-defense-report.md` and print a one-line summary:
|
||||
`Report saved → cyber-defense-report.md | Risk: [LEVEL] | Actions: [N]`
|
||||
Loading…
Add table
Add a link
Reference in a new issue