Content
# Finxiom
Finxiom is a production-oriented agentic AI analyst system for financial fraud detection and AML compliance. It ingests single transactions or bulk payment batches, runs a governed multi-agent investigation pipeline, produces a traceable risk decision and SAR draft, and requires human approval before irreversible actions such as SAR filing or account freeze simulation.
This is not a chatbot. The system initiates analysis, routes typed handoffs across specialist agents, builds relationship evidence, matches fraud typologies, scores risk, prepares an analyst review packet, and records an audit trail.
---
## Quick Start — one command
**Prerequisites:** [Docker Desktop](https://docs.docker.com/get-docker/) · [uv](https://docs.astral.sh/uv/getting-started/installation/) · [Node.js 18+](https://nodejs.org/)
```bash
git clone <repo-url> finxiom
cd finxiom
bash install.sh
```
`install.sh` does everything in one shot:
| Step | What happens |
|---|---|
| Prerequisite check | Verifies Docker, uv, node, npm are available |
| `.env` generation | Copies `.env.example`, generates random secure secrets, optionally prompts for `OPENAI_API_KEY` |
| `frontend/.env.local` | Created automatically (no manual editing needed) |
| Docker infrastructure | Pulls and starts Postgres, Redis, Neo4j, Qdrant, MinIO, Langfuse, Jaeger, Prometheus, Grafana, Kong |
| Python deps | `uv sync --locked --all-groups` |
| DB migrations | `alembic upgrade head` |
| Sample data | Seeds bundled AML sample transactions |
| Node deps | `npm install` |
| App launch | Starts backend (port 8010), MCP server (port 8020), and Next.js dev server (port 3001) |
Once running, open **http://localhost:3001** and use the Admin console at **http://localhost:3001/admin**.
> **No OpenAI key?** The system works fully in *deterministic fallback mode* — all agent logic runs without any live LLM calls. Press Enter when `install.sh` prompts for the API key to skip it.
### Subsequent runs (after first install)
```bash
bash start.sh # start app + Docker (skip install steps)
bash start.sh --bg # start in background
bash stop.sh # stop app processes (Docker keeps running)
bash stop.sh --all # stop everything including Docker
```
Or use Make:
```bash
make install # full install + start (same as bash install.sh)
make start # quick start
make stop # stop app
```
### Fully containerised (Docker only, no local Node/uv)
```bash
cp .env.example .env # edit secrets before proceeding
docker compose --env-file .env \
-f infra/docker-compose.yml \
-f infra/docker-compose.app.yml \
up --build
```
This builds and runs the FastAPI backend, Celery worker, and MCP server as Docker containers alongside the infrastructure. The Next.js frontend still needs to run locally (`npm run dev`) because `NEXT_PUBLIC_*` env vars are embedded at build time.
---
## What Finxiom Does
- Analyzes financial transactions for structuring, shell-company movement, cross-border risk, high-value wires, and related AML patterns.
- Accepts realistic JSON and CSV bulk uploads with ISO 20022, Fedwire, SWIFT MT103, ACH, party, bank, remittance, and compliance-context fields.
- Runs bulk rows in a bounded parallel worker pool while preserving the ordered specialist-agent evidence chain inside each row: Orchestrator, Ingest, Graph, Pattern, Risk, and Alert.
- Shows live case progress in a modern analyst UI with dashboard, cases, case detail tabs, bulk upload, admin policy controls, graph traversal, SAR draft, and human review packet.
- Shows a operations readiness control room with gateway/auth status, Postgres persistence, append-only audit guard, Celery worker status, MCP status, OpenAI mode, Langfuse state, sample files, and caveats before operations start.
- Exposes governed investigation tools through MCP for approved external agents and enterprise automation clients.
- Uses risk-based human oversight: analysts validate conclusions, assumptions, exceptions, claim-to-source mapping, and high-impact actions instead of manually rereading every agent token.
- Stores each case with persisted `created_at` and `processed_at` timestamps, the transaction, final handoff, SAR draft, timeline, status, and audit events.
- Allows analysts to edit and save the SAR draft before approval; the edited draft is persisted to the case and audit ledger before the Alert Agent runs.
- Supports both `LLM mode: OpenAI live` and `LLM mode: deterministic fallback`, so the application remains operational without external model calls.
- Emits traces, metrics, and operational signals through Langfuse, OpenTelemetry, Jaeger, Prometheus, and Grafana.
## Architecture
```mermaid
flowchart TD
A["Single Transaction<br/>or Bulk JSON/CSV Upload"] --> AA["Bulk Worker Pool<br/>bounded row concurrency"]
X["Next.js Analyst UI"] --> GW["Kong API Gateway<br/>rate limit + JWT edge check"]
AA --> GW
GW --> B["FastAPI API<br/>Pydantic validation + scoped JWT"]
B --> CW["Celery Worker<br/>async agent-pipeline queue"]
CW --> C
B --> C["Orchestrator Agent<br/>zero tools, routing only"]
C --> D["Budget Envelope<br/>turns, tools, retries, runtime, tokens, repeated action guard"]
D --> E["Ingest Agent<br/>normalize, enrich, deduplicate, persist payload"]
E --> F["Graph Agent<br/>entity graph, 3-hop traversal, ring and shell detection"]
F --> G["Pattern Agent<br/>typology embeddings, similarity, anomaly clustering"]
G --> H["Risk Agent<br/>scoring policy, Structured Output SAR draft, review packet"]
H --> I{"Risk and confidence gate"}
I -->|"low risk"| J["Auto-resolve<br/>audit only"]
I -->|"high risk"| K["Human Gate<br/>approve or reject"]
I -->|"budget or progress stop"| L["Requires Human Input<br/>state summary + next question"]
K -->|"approve"| M["Alert Agent<br/>SAR filing simulation, freeze simulation, audit ledger"]
K -->|"reject"| N["Reject Action<br/>append-only audit"]
B --> O["PostgreSQL<br/>cases, audit, relational state"]
E --> P["Redis Stack<br/>cache, dedup, event fanout"]
E --> Q["MinIO<br/>raw payload snapshots"]
F --> R["Neo4j<br/>entity relationship graph"]
G --> S["Qdrant<br/>fraud typology vector search"]
H --> T["OpenAI APIs<br/>Agents SDK, models, embeddings, structured output"]
H --> U["Langfuse<br/>LLM traces and claim context"]
B --> V["OpenTelemetry<br/>Jaeger traces"]
B --> W["Prometheus + Grafana<br/>metrics and dashboards"]
Y["Finxiom MCP Server<br/>stdio or streamable HTTP"] --> B
```
## Agent Workflow
1. **Orchestrator Agent** receives a validated transaction and creates a typed handoff. It has zero tools by design.
2. **Ingest Agent** normalizes payment fields, enriches counterparties, checks duplicate transaction IDs, and stores raw payload context.
3. **Graph Agent** builds source, destination, party, and counterparty relationships, then performs traversal and shell-ring checks.
4. **Pattern Agent** embeds transaction context, searches fraud typologies, scores anomaly signals, and clusters recent activity.
5. **Risk Agent** combines configurable policy weights, confidence scores, graph findings, typology matches, source evidence, and automated checks into a final risk score and SAR draft.
6. **Human Gate** blocks high-impact action until an analyst approves or rejects the case.
7. **Alert Agent** runs only after approval and appends the SAR/freeze simulation to the audit ledger.
Every autonomous run is constrained by a budget envelope: maximum turns, tool calls, retries, runtime, output tokens, total token budget, and repeated/no-progress action limits. If the run stops making progress, Finxiom terminates the run, summarizes current state, and asks for human input.
## Tech Stack And Rationale
| Layer | Technology | Why it is used |
|---|---|---|
| Backend API | Python 3.12, FastAPI, Pydantic v2 | Async API surface, strong request/response validation, OpenAPI docs, easy typed agent handoffs. |
| API gateway | Kong Gateway | Gateway-facing operations path, rate limiting, and JWT edge validation before protected traffic reaches FastAPI. |
| Async execution | Celery, Redis broker | Production-shaped worker path for asynchronous agent pipeline execution. |
| Python packaging | uv, `pyproject.toml`, `uv.lock` | Fast deterministic backend/MCP installs, lockfile reproducibility, and modern Python project commands. |
| Agent runtime | OpenAI Agents SDK integration, OpenAI Python SDK | Multi-agent handoffs, model calls, embeddings, and structured SAR-style outputs when live mode is enabled. |
| Agent integration | Model Context Protocol SDK | Exposes Finxiom investigation, audit, and human-gate workflows to approved external agents without duplicating business logic. |
| Local fallback | Deterministic scoring and enrichment paths | Reliable offline operation and testability when model credentials or network access are unavailable. |
| Frontend | Next.js 15, React 19, TypeScript | Modern app router UI with typed components and fast local development. |
| UI/visualization | Tailwind CSS, lucide-react, D3, Recharts | Dense analyst UI, icon-driven controls, live force graph traversal, and risk charts. |
| Relational data | PostgreSQL 16 | Durable runtime case/audit JSON store plus Langfuse backing storage. |
| Graph data | Neo4j 5 Community | Entity relationship traversal and network-risk modeling. |
| Vector search | Qdrant | Fraud typology similarity search with HNSW vector indexing. |
| Cache/events | Redis Stack | Fast deduplication, cache, and event fanout patterns. |
| Object storage | MinIO | S3-compatible local storage for raw payload and evidence snapshots. |
| LLM observability | Langfuse | Trace model calls, spans, prompts, outputs, and decision context. |
| Distributed tracing | OpenTelemetry, Jaeger | Track API and pipeline execution across services. |
| Metrics | Prometheus, Grafana | Monitor request counts, latency, risk scores, pipeline runs, and breaker state. |
| Quality | pytest, ruff, mypy | Regression tests, linting, and static checks for backend code. |
| Local infra | Docker Compose | Reproducible support stack for database, graph, vector, cache, storage, and observability services. |
## Repository Map
```text
finxiom/
├── backend/ # FastAPI API, agents, schemas, services, tests
│ ├── finxiom_agents/ # Orchestrator and specialist agents
│ ├── api/routes/ # Transactions, bulk upload, cases, approvals, events, admin, health
│ ├── failsafes/ # Budget envelope, context guard, retries, circuit breakers
│ ├── observability/ # Langfuse and OpenTelemetry helpers, Prometheus metrics
│ └── tests/ # API, agent, schema, and failsafe tests
├── frontend/ # Next.js analyst application
│ ├── app/ # Dashboard, upload, cases, case detail, admin pages
│ ├── components/ # Graph, telemetry, SAR preview, human review, admin controls
│ └── public/samples/ # Bulk upload sample files
├── mcp_server/ # MCP server for governed external agent access
├── infra/ # Docker Compose, Kong, Celery worker, and observability config
├── data/seed/ # Sample single-transaction payloads and fraud typologies
├── data/runtime/ # Local generated admin policy store, ignored by git
├── docs/ # Architecture and realistic input model notes
└── scripts/ # Workflow runner, reset helper, scoped JWT generator
```
## Prerequisites
- Docker Desktop or a compatible Docker engine.
- Python 3.12 recommended.
- Node.js 20 or newer.
- An OpenAI API key for live model mode. Without it, the system runs in deterministic fallback mode.
## Environment Setup
Create a local environment file:
```bash
cd finxiom
cp .env.example .env
```
Edit `.env` before sharing or deploying:
- Set `OPENAI_API_KEY` for `LLM mode: OpenAI live`.
- Replace local values for `POSTGRES_PASSWORD`, `NEO4J_PASSWORD`, `MINIO_ACCESS_KEY`, `MINIO_SECRET_KEY`, and `JWT_SECRET_KEY`.
- Keep `.env` out of git. It is already ignored by `.gitignore`.
- Use `NEXT_PUBLIC_API_BASE_URL=http://127.0.0.1:8080` for the operations frontend so requests go through Kong. The restart script generates a browser-visible `NEXT_PUBLIC_APP_JWT` with analyst-only scopes: `transactions:write,cases:read,cases:approve`.
- Analyst pages read display thresholds through `/runtime/scoring-policy` with `cases:read`. Admin policy/readiness calls do not use the browser-visible token. They go through server actions protected by the Admin password, a signed HTTP-only Admin session cookie, and a server-generated `admin:*` JWT.
- For backend-only development, direct calls to `http://127.0.0.1:8010` are still supported.
- Keep `JWT_SECRET_KEY` aligned with the Kong JWT secret in `infra/kong/kong.yml` if you replace the local value.
## Run Locally
Python services use `uv` as the primary package manager. Install it before the backend steps if needed:
```bash
python3 -m pip install uv==0.9.17
```
Start the Docker support stack:
```bash
docker compose --env-file .env -f infra/docker-compose.yml up -d
```
This starts Postgres, Redis, Celery worker, Neo4j, Qdrant, MinIO, Langfuse, Jaeger, Prometheus, Grafana, and Kong.
Install backend dependencies:
```bash
cd backend
uv sync --locked --all-groups
cd ..
```
Run database migrations:
```bash
cd backend
uv run --locked alembic upgrade head
cd ..
```
Install frontend dependencies:
```bash
cd frontend
npm install
cd ..
```
Start the backend API:
```bash
cd backend
uv run --locked uvicorn main:app --host 127.0.0.1 --port 8010 --reload
```
In a second terminal, start the frontend:
```bash
cd frontend
export NEXT_PUBLIC_APP_JWT="$(uv --project ../backend run --locked python ../scripts/generate_jwt.py --subject app-ui --scopes transactions:write,cases:read,cases:approve)"
env_value() { grep -m1 -E "^$1=" ../.env 2>/dev/null | sed 's/^[^=]*=//'; }
export ADMIN_PASSWORD="$(env_value ADMIN_PASSWORD)"
export JWT_SECRET_KEY="$(env_value JWT_SECRET_KEY)"
export GATEWAY_JWT_ISSUER="$(env_value GATEWAY_JWT_ISSUER)"
NEXT_PUBLIC_API_BASE_URL=http://127.0.0.1:8080 \
FINXIOM_SERVER_API_BASE_URL=http://127.0.0.1:8080 \
NEXT_PUBLIC_APP_JWT="$NEXT_PUBLIC_APP_JWT" \
ADMIN_PASSWORD="$ADMIN_PASSWORD" \
JWT_SECRET_KEY="$JWT_SECRET_KEY" \
GATEWAY_JWT_ISSUER="${GATEWAY_JWT_ISSUER:-finxiom}" \
npm run dev
```
In a third terminal, optionally start the MCP server in streamable HTTP mode:
```bash
export FINXIOM_MCP_BEARER_TOKEN="$(uv --project backend run --locked python scripts/generate_jwt.py --subject mcp --scopes transactions:write,cases:read,cases:approve,admin:read,admin:write)"
uv --project backend run --locked python -m mcp_server.server --transport streamable-http --host 127.0.0.1 --port 8020
```
Check the Docker stack:
```bash
docker compose --env-file .env -f infra/docker-compose.yml ps
```
Stop local services:
```bash
docker compose --env-file .env -f infra/docker-compose.yml down
```
## Restart Operations
Use the restart utility when ports are stuck, Docker services need a clean recreate, or the workstation has been idle.
```bash
bash scripts/restart_services.sh all
```
Common targets:
| Command | What it restarts |
|---|---|
| `bash scripts/restart_services.sh all` | Docker support stack, backend, frontend, MCP. |
| `bash scripts/restart_services.sh app` | Backend, frontend, and MCP after ensuring infra is running. |
| `bash scripts/restart_services.sh backend` | FastAPI backend on port `8010`. |
| `bash scripts/restart_services.sh frontend` | Next.js frontend on port `3001`. |
| `bash scripts/restart_services.sh mcp` | Finxiom MCP server on port `8020`. |
| `bash scripts/restart_services.sh infra` | All Docker support services. |
| `bash scripts/restart_services.sh observability` | Langfuse, Jaeger, Prometheus, Grafana. |
| `bash scripts/restart_services.sh status` | Docker status, port listeners, backend/frontend checks. |
| `bash scripts/restart_services.sh dependencies` | Dependency guidance for each restart target. |
Individual Docker service targets are also supported:
```bash
bash scripts/restart_services.sh postgres
bash scripts/restart_services.sh redis
bash scripts/restart_services.sh neo4j
bash scripts/restart_services.sh qdrant
bash scripts/restart_services.sh minio
bash scripts/restart_services.sh langfuse
bash scripts/restart_services.sh jaeger
bash scripts/restart_services.sh prometheus
bash scripts/restart_services.sh grafana
```
Dependency order to remember:
- Full restart: `infra -> backend -> frontend -> mcp`.
- Backend restart: ensure `postgres`, `redis`, `neo4j`, `qdrant`, and `minio` are running first.
- Frontend restart: backend should already be healthy, otherwise API-backed pages may load with errors.
- MCP restart: backend should already be healthy because MCP tools call the Finxiom API.
- Langfuse restart: `postgres` must be healthy first.
- Grafana restart: `prometheus` should be running first.
- Postgres restart: restart Langfuse afterward; restart backend if database connection errors appear.
Makefile shortcuts:
```bash
make restart
make restart-app
make restart-backend
make restart-frontend
make restart-mcp
make restart-status
make restart-dependencies
make mcp-smoke
```
## Local URLs
| Service | URL |
|---|---|
| Main application | `http://localhost:3001/dashboard` |
| Bulk upload | `http://localhost:3001/upload` |
| Cases | `http://localhost:3001/cases` |
| Admin console | `http://localhost:3001/admin` |
| Kong gateway | `http://127.0.0.1:8080` |
| FastAPI docs | `http://127.0.0.1:8010/docs` |
| Backend health | `http://127.0.0.1:8010/health` |
| System readiness | `http://127.0.0.1:8010/system/readiness` |
| Operations readiness API | `http://127.0.0.1:8010/operations/readiness` |
| MCP streamable HTTP endpoint | `http://127.0.0.1:8020/mcp` |
| Langfuse | `http://localhost:3000` |
| Grafana | `http://localhost:3002` |
| Prometheus | `http://localhost:9090` |
| Jaeger | `http://localhost:16686` |
| Neo4j Browser | `http://localhost:7474` |
| MinIO Console | `http://localhost:9001` |
The Finxiom Admin console password is read from `ADMIN_PASSWORD`. Change the local development value before any shared or deployed environment.
## Step-By-Step Production Walkthrough
Use this flow for a full end-to-end walkthrough.
1. Confirm the app is running:
```bash
APP_JWT="$(uv --project backend run --locked python scripts/generate_jwt.py --subject app-health --scopes cases:read)"
curl -H "Authorization: Bearer $APP_JWT" http://127.0.0.1:8080/health
```
The response includes the current LLM mode, OpenAI configuration status, model names, and Langfuse status without exposing secrets.
2. Open the Admin console:
```text
http://localhost:3001/admin
```
Unlock the Admin console with the local Admin password, then click the Readiness tab and confirm Kong, JWT mode, OpenAI/Langfuse mode, Postgres case store, append-only audit trigger, Celery worker, MCP, sample files, and service listeners.
3. Open the main UI:
```text
http://localhost:3001/dashboard
```
Point out live metrics, runtime mode, Postgres case-store status, MCP status, Langfuse status, recent cases, and the analyst-style layout.
4. Open bulk upload:
```text
http://localhost:3001/upload
```
Upload one of the sample files:
```text
frontend/public/samples/sample_bulk_mixed.json
frontend/public/samples/high_risk_aml_bulk.json
frontend/public/samples/realistic_bulk_transactions.json
frontend/public/samples/realistic_bulk_transactions.csv
frontend/public/samples/clean_operations_bulk.csv
frontend/public/samples/validation_error_bulk.json
frontend/public/samples/sample_stress_realistic_50.json
frontend/public/samples/sample_stress_realistic_250.csv
```
5. Click **Run Agents On Batch**.
Bulk rows run through a bounded parallel worker pool. The default worker limit is `BULK_UPLOAD_MAX_CONCURRENCY=4`, so reviewers can see several rows validating and running agents at the same time without overwhelming OpenAI rate limits, storage, or local CPU. Use `sample_stress_realistic_50.json` for a live stress run and `sample_stress_realistic_250.csv` for a heavier throughput test, preferably in deterministic fallback mode or with an intentionally chosen Admin worker limit. Within each row, the agent handoff remains ordered because Ingest normalizes the payment, Graph adds relationship evidence, Pattern reads the graph-enriched evidence, and Risk produces the final decision packet. Invalid rows are isolated in the bulk result instead of failing the whole batch.
6. Open a high-risk case from the batch results or the Cases page.
On the case detail page, show:
- Overview with transaction and payment-message context.
- Investigation tabs with entity relationships first, then agent evidence.
- Live graph traversal where relationship hops auto-reveal.
- Confidence waterfall and per-agent evidence.
- Risk analysis and configurable scoring factors.
- SAR draft, manual SAR edit controls, and human review packet.
- Human approval/rejection gate.
7. Approve the high-risk case.
Approval triggers the Alert Agent simulation and appends the decision to the audit ledger.
8. Show observability:
- Langfuse for LLM traces when OpenAI live mode and Langfuse keys are configured.
- Grafana for Prometheus-backed metrics.
- Prometheus for raw metric queries such as `finxiom_pipeline_runs_total`.
- Jaeger for OpenTelemetry traces.
9. Show the MCP value-add:
- Start MCP with `bash scripts/restart_services.sh mcp`.
- Explain that approved enterprise agents can call governed tools such as `submit_transaction`, `get_case`, `get_human_review_packet`, and `get_case_audit`.
- Point out that `approve_case` still requires a scoped token and `confirm_irreversible_action=true`, so MCP does not bypass the human gate.
- Run `make mcp-smoke` to list MCP tools and fetch runtime/case data through the MCP protocol.
You can also run the scripted workflow via Make:
```bash
make run # run the end-to-end scripted workflow
make reset-sample-data # reseed sample data for a clean workflow run
```
These targets call the underlying scripts (`scripts/run_end_to_end.sh` and `scripts/reset_sample_data.sh`). The workflow submits a low-risk transaction, submits a high-risk transaction, prints the case URL, approves the high-risk case, and prints the audit ledger.
For the MCP value-add workflow:
```bash
make mcp-smoke
```
This runs an MCP stdio client, lists the exposed tools, then calls `get_runtime_status` and `list_cases` through MCP.
## Sample Evaluation
Run the reproducible sample evaluator before the production review to generate measurable evidence for accuracy, latency, throughput, cost, and human-gate rate:
```bash
uv --project backend run --locked python scripts/evaluate_samples.py \
--sample frontend/public/samples/sample_stress_realistic_50.json \
--concurrency 4
```
Outputs:
| File | Purpose |
|---|---|
| `data/runtime/evaluation_report.json` | Machine-readable metrics and per-row results. |
| `docs/sample_evaluation_report.md` | Human-readable KPI table for production review preparation. |
Use `--live-openai` only when you intentionally want the evaluator to call live OpenAI paths. Without that flag, the evaluator forces deterministic fallback so the report is repeatable and safe to run many times.
## Admin Configuration
The protected Admin console allows runtime policy overrides without editing code. Defaults come from `.env` and are persisted locally to `data/runtime/scoring_policy.json`.
Configurable areas include:
- Risk score thresholds and weights.
- Sanctions-review, beneficial-owner opacity, high-risk party, and PEP exposure weights.
- Structuring amount ranges.
- Shell/ring text markers and graph traversal limits.
- Vector match scores and anomaly increments.
- Per-agent confidence defaults and retry boosts.
- Human gate threshold and timeout.
- Budget envelope limits: max turns, tool calls, retries, runtime, tokens, cost, repeated actions, and output truncation.
- Circuit breaker and SSE timeout settings.
Each parameter has inline help so an operations or compliance user can understand the meaning before changing it.
## MCP Server
Finxiom includes a production-grade MCP server at `mcp_server/`. The MCP server wraps the existing FastAPI backend, so the backend remains the system of record for validation, agent execution, scoring, audit, and approval logic.
MCP tools:
| Tool | Purpose |
|---|---|
| `get_runtime_status` | Read safe runtime and LLM mode status. |
| `submit_transaction` | Submit one transaction to the agent pipeline. |
| `submit_bulk_transactions` | Submit a JSON transaction batch. |
| `list_cases` | List visible cases. |
| `get_case` | Read complete case detail. |
| `get_case_audit` | Read append-only case audit events. |
| `get_human_review_packet` | Extract decision points, claim-to-source map, checks, and exceptions. |
| `approve_case` | Trigger approved Alert Agent simulation after explicit human confirmation. |
| `reject_case` | Reject a case and append analyst note. |
| `get_scoring_policy` | Read active Admin scoring policy. |
| `update_scoring_policy` | Update active Admin scoring policy. |
| `reset_scoring_policy` | Reset Admin policy to defaults. |
Run MCP through stdio for desktop clients:
```bash
cd /Users/ag/Documents/OpenAI_Hackathon/finxiom
FINXIOM_API_BASE_URL=http://127.0.0.1:8010 \
FINXIOM_MCP_BEARER_TOKEN="$(uv --project backend run --locked python scripts/generate_jwt.py --subject mcp --scopes transactions:write,cases:read,cases:approve,admin:read,admin:write)" \
uv --project backend run --locked python -m mcp_server.server --transport stdio
```
Run MCP as a local streamable HTTP service:
```bash
bash scripts/restart_services.sh mcp
```
Security controls:
- Write tools require a scoped Finxiom bearer token.
- Use least-privilege tokens for real clients.
- `approve_case` also requires `confirm_irreversible_action=true`.
- MCP does not bypass backend validation, scoring policy, the human gate, or the audit trail.
## API Surface
Core endpoints:
| Method | Endpoint | Purpose |
|---|---|---|
| `GET` | `/health` | Runtime health and LLM mode. |
| `GET` | `/runtime/status` | Safe frontend runtime status. |
| `GET` | `/runtime/scoring-policy` | Read-only scoring/display policy for analyst pages. |
| `GET` | `/system/readiness` | Operations readiness: storage mode, MCP listener, Langfuse state, OpenAI mode, and caveats. |
| `GET` | `/operations/readiness` | Operations readiness: gateway, auth, services, Celery, samples, OpenAI evidence, and caveats. |
| `POST` | `/transactions` | Submit one transaction. |
| `POST` | `/transactions/bulk` | Submit a JSON array of transactions. |
| `POST` | `/transactions/async` | Enqueue a transaction via Celery; returns `task_id` immediately. |
| `GET` | `/tasks/{task_id}` | Poll Celery task status (PENDING/STARTED/SUCCESS/FAILURE). |
| `GET` | `/cases` | List cases. |
| `GET` | `/cases/{case_id}` | Get case details. |
| `GET` | `/cases/{case_id}/audit` | Get append-only audit events. |
| `POST` | `/cases/{case_id}/approve` | Human approval gate. |
| `POST` | `/cases/{case_id}/reject` | Human rejection gate. |
| `GET` | `/events/{case_id}` | Server-Sent Events for live case status. |
| `GET` | `/admin/scoring-policy` | Admin-only read of active policy. |
| `PUT` | `/admin/scoring-policy` | Admin-only update of active policy. |
| `POST` | `/admin/scoring-policy/reset` | Admin-only reset of active policy to defaults. |
| `GET` | `/metrics` | Prometheus metrics. |
Most protected endpoints require a scoped JWT. Generate application tokens with:
```bash
uv --project backend run --locked python scripts/generate_jwt.py --subject intake --scopes transactions:write,cases:read
uv --project backend run --locked python scripts/generate_jwt.py --subject analyst --scopes cases:read,cases:approve
uv --project backend run --locked python scripts/generate_jwt.py --subject admin --scopes admin:read,admin:write
```
Browser clients should not receive `admin:*` scopes. Analyst pages use `/runtime/scoring-policy` for read-only display thresholds; Admin UI changes are routed through the Next.js server action layer after the Admin session is unlocked. If you changed `JWT_SECRET_KEY` in `.env`, export the same value in the shell before generating manual tokens so the token signer and backend verifier match.
## LLM Runtime Modes
Finxiom intentionally displays the active model mode in the UI and health endpoint.
- `LLM mode: OpenAI live` means `OPENAI_API_KEY` is configured and live model, embedding, and structured-output paths are available.
- `LLM mode: deterministic fallback` means the pipeline uses local deterministic enrichment, graph logic, pattern matching, scoring, and SAR fallback content.
This design makes production runs reliable while keeping production integration paths visible and testable.
## Observability
Langfuse, Grafana, Prometheus, and Jaeger are part of the local Docker stack.
Use Langfuse to inspect model and agent traces after live model calls. Create a project in Langfuse if the instance is fresh, copy the generated public and secret keys into `.env`, restart the backend, and run a new transaction or bulk upload.
Use Prometheus and Grafana to inspect API and pipeline metrics. The backend exposes `/metrics`, and Docker Compose provisions Prometheus and Grafana locally.
Use Jaeger to inspect distributed traces from FastAPI and instrumented requests.
## Testing And Quality
Run backend tests:
```bash
cd backend
uv run --locked pytest tests/ -v --asyncio-mode=auto
```
Run lint and type checks:
```bash
cd backend
uv run --locked ruff check .
uv run --locked mypy .
```
Run frontend build checks:
```bash
cd frontend
npm run build
```
## Troubleshooting
If `http://localhost:3001` is unreachable, confirm the frontend dev server is running:
```bash
bash scripts/restart_services.sh frontend
```
If the frontend loads but API-backed pages show `401 Unauthorized`, restart the frontend through the restart script so it receives a fresh scoped application JWT:
```bash
bash scripts/restart_services.sh frontend
```
If requests through `http://127.0.0.1:8080` fail but direct backend requests work, verify Kong is running and that the Kong JWT secret in `infra/kong/kong.yml` matches the local `JWT_SECRET_KEY` used by `scripts/generate_jwt.py`.
If `http://127.0.0.1:8010/health` is unreachable, confirm the backend is running on port `8010`:
```bash
cd backend
uv run --locked uvicorn main:app --host 127.0.0.1 --port 8010 --reload
```
If backend tests cannot find Docker on macOS, set the Docker Desktop user socket before running pytest:
```bash
export DOCKER_HOST=unix://$HOME/.docker/run/docker.sock
```
If Docker services show blank credential warnings, always include the env file:
```bash
docker compose --env-file .env -f infra/docker-compose.yml up -d
```
If Docker had a failed or partial image pull, restart Docker Desktop, ensure enough disk space is available, and pull again:
```bash
docker compose --env-file .env -f infra/docker-compose.yml pull
docker compose --env-file .env -f infra/docker-compose.yml up -d
```
If Langfuse is blank, create a Langfuse project, update `LANGFUSE_PUBLIC_KEY` and `LANGFUSE_SECRET_KEY` in `.env`, restart the backend, and run a fresh transaction. Old deterministic-fallback runs will not create live LLM spans.
If Grafana or Prometheus appears empty, generate traffic by opening the UI, submitting transactions, or running:
```bash
bash scripts/run_end_to_end.sh
```
## Security Notes
- Do not commit `.env`, API keys, database passwords, Langfuse keys, or JWT secrets.
- The local Admin password is for development only and must be rotated before shared use.
- The browser-visible application token intentionally excludes `admin:*`; Admin reads/writes use a signed HTTP-only Admin session and server-side scoped JWT.
- Production deployment should replace the local Admin password with OIDC/SAML-backed RBAC and short-lived server-issued tokens.
- High-impact actions are gated by explicit analyst approval.
- Agent access is scoped by route-level permissions and typed handoffs.
- Tool output is truncated before being handed to the next stage.
- Audit events are append-only at the application layer, with migration support for database-level update/delete revocation.
## Additional Documentation
- `docs/architecture.md` contains deeper architecture and guardrail notes.
- `docs/production_scorecard.md` contains the scoring proof points and production talking track.
- `docs/mcp_integration.md` explains MCP tools, transports, scopes, and safety controls.
- `docs/realistic_input_model.md` explains the real-world payment fields accepted by bulk upload.
- `docs/sample_evaluation_report.md` contains the latest generated sample KPI report.
- `data/seed/` contains single-transaction sample payloads and fraud typology examples.
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available