# CLAUDE.md — AttestMCP Project Rules

## What this project is
AttestMCP is a transparent MCP (Model Context Protocol) proxy that generates tamper-evident audit trails for AI agent memory operations. It sits between agent clients and memory MCP servers, intercepting JSON-RPC calls to add identity attribution, policy enforcement, and compliance evidence generation.

## Architecture
- **Downstream (agent-facing):** FastAPI HTTP endpoint at `/mcp` accepting MCP Streamable HTTP
- **Upstream (memory-server-facing):** mcp SDK `ClientSession` wrapped in a thin adapter
- **Audit store:** SQLite with WAL mode, synchronous append-only writes, SHA-256 hash-chained records
- **Identity:** JWT signature validation via PyJWT against configured JWKS endpoint
- **Observability:** Prometheus metrics (requests_proxied, audit_records_written, chain_verification_failures, upstream_latency)

## Critical invariants — never violate these
1. **Audit completeness:** If a caller receives a response, the audit record MUST already exist in SQLite. Synchronous write before returning. Never async/fire-and-forget for audit records.
2. **Hash chain integrity:** Every record's `chain_hash` = SHA-256(previous `chain_hash` + canonical JSON of current record). The chain MUST be verified in CI on every test run.
3. **Canonical serialization:** Audit records are serialized to JSON with: keys sorted alphabetically, no whitespace, UTC timestamps in ISO 8601 with microsecond precision, Unicode NFC normalization. This is frozen — changing it invalidates all existing chains.
4. **Zero behavioral change:** The proxy MUST NOT modify request or response payloads. The agent and memory server must behave identically with or without the proxy.
5. **Identity on every record:** Every audit record MUST have an `agent_identity` block, even if it's `source: "anonymous"` with `trust_level: "anonymous"`.

## Code standards
- Python 3.12+, strict mypy, ruff linting
- All public functions have type annotations
- All modules have docstrings
- Tests use pytest + pytest-asyncio
- Security: no stack traces in responses, no PII in logs without explicit redaction config, validate all inputs with Pydantic

## File structure
```
src/attestmcp/
├── __init__.py         # Version and package metadata
├── models.py           # AuditRecord, AgentIdentity, ChainVerification dataclasses
├── canonical.py        # Frozen canonical JSON serialization
├── store.py            # AuditStore protocol + SQLite implementation
├── identity.py         # JWT extraction and validation
├── proxy.py            # FastAPI app + MCP proxy logic
├── metrics.py          # Prometheus counters and histograms
├── evidence.py         # Evidence export (JSON lines, HTML report)
├── config.py           # Pydantic Settings for all configuration
└── cli.py              # CLI entry point
tests/
├── test_models.py
├── test_canonical.py
├── test_store.py       # Must include tamper detection test
├── test_identity.py
└── test_proxy.py       # Integration test with mock upstream
```

## Testing rules
- `verify_chain()` runs in CI on every test — if the chain breaks silently, tests fail
- Integration tests with external APIs (Mem0) are marked `@pytest.mark.integration` and gated on env vars
- No test should require network access unless marked as integration
- Minimum coverage target: 90% on models, canonical, and store modules

## When working on this codebase
- Read this file first
- Run `ruff check src/ tests/` before committing
- Run `mypy src/` before committing
- Run `pytest` and verify all pass before committing
- Never modify the canonical serialization spec without explicit approval
