127 lines
7.7 KiB
Markdown
127 lines
7.7 KiB
Markdown
# jarca-ledger-mcp
|
|
|
|
Jarca 가계부 전용 MCP 서버입니다. 계좌/카드, 수입·지출 거래, 예산, 월간 현금흐름, 고정비, 투자 여력 분석을 SQLite에 저장하고 MCP 도구로 제공합니다.
|
|
|
|
## 설치
|
|
|
|
```bash
|
|
cd /root/projects/jarca-ledger-mcp
|
|
python3.11 -m venv .venv
|
|
. .venv/bin/activate
|
|
pip install -e '.[dev,dashboard]'
|
|
```
|
|
|
|
기본 DB 위치는 `~/.local/share/jarca/ledger.sqlite3`입니다. 바꾸려면 `.env`에 설정하세요.
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
# JARCA_LEDGER_DB_PATH=/원하는/경로/ledger.sqlite3
|
|
```
|
|
|
|
## Hermes 등록
|
|
|
|
```bash
|
|
hermes mcp add jarca-ledger --command /root/projects/jarca-ledger-mcp/.venv/bin/jarca-ledger-mcp
|
|
hermes mcp test jarca-ledger
|
|
```
|
|
|
|
게이트웨이/현재 세션에는 `/reload-mcp` 또는 재시작 후 반영됩니다.
|
|
|
|
## Streamlit Dashboard
|
|
|
|
로컬 웹 대시보드는 실제 기본 DB `~/.local/share/jarca/ledger.sqlite3`를 읽고 씁니다.
|
|
|
|
Dashboard는 모든 조회·수정·Import 기능 앞에서 단일 관리자 로그인을 요구합니다. 관리자 ID와 password는 Streamlit secrets가 아니라 배포 secret이 주입하는 다음 환경변수에서만 읽습니다.
|
|
|
|
```bash
|
|
export JARCA_DASHBOARD_ADMIN_ID='<secret store에서 주입>'
|
|
export JARCA_DASHBOARD_ADMIN_PASSWORD='<secret store에서 주입>'
|
|
```
|
|
|
|
두 값 중 하나라도 누락됐거나 비어 있거나 읽을 수 없으면 Dashboard는 fail-closed하여 로그인 설정 오류만 표시하고 DB나 page view에 접근하지 않습니다. 실제 credential을 `.env`, Compose 파일, shell history 또는 repository 문서에 저장하지 마세요.
|
|
|
|
```bash
|
|
cd /root/projects/jarca-ledger-mcp
|
|
. .venv/bin/activate
|
|
streamlit run dashboard/app.py --server.address 0.0.0.0 --server.port 8501
|
|
```
|
|
|
|
인증 성공 상태는 현재 Streamlit browser session에서만 유지됩니다. 새 session에는 재로그인이 필요하고 sidebar의 `로그아웃`으로 즉시 인증 상태를 폐기할 수 있습니다. 잘못된 ID 또는 password 응답은 매 제출마다 최소 1초 지연되지만 애플리케이션 잠금은 없습니다. 이 지연은 새 session이나 병렬 요청으로 우회할 수 있으므로 외부에 노출할 때는 Ingress, reverse proxy 또는 동등한 배포 경계에 별도 rate limiting을 적용해야 합니다.
|
|
|
|
이 로그인은 **Streamlit Dashboard만 보호하며 MCP endpoint를 인증하지 않습니다.** MCP는 별도 요구사항과 적절한 네트워크 경계로 보호해야 합니다.
|
|
|
|
주요 화면:
|
|
|
|
- 요약: 실제 수입/지출과 계좌 입출금을 분리 표시, 월별 현금흐름, 지출 카테고리 차트
|
|
- 거래내역: 검색/필터 및 개별 거래 카테고리 수정
|
|
- 미분류: 미분류 거래 요약, 거래별 분류, 규칙 저장
|
|
- 분류 규칙: 규칙 seed/preview/apply, 사용자 규칙 생성
|
|
- 카테고리: 대/소분류 추가, 비활성화/삭제, 사용 건수 확인
|
|
- 계좌: 등록 계좌/잔액 확인
|
|
- Import: PDF/CSV/API import batch 조회 및 rollback
|
|
|
|
## 컨테이너 및 k3s 실행 구조
|
|
|
|
릴리즈 이미지는 MCP 서버와 Streamlit Dashboard의 코드 및 의존성을 모두 포함하지만, 이미지의 기본 명령(`CMD`)은 MCP 서버만 실행합니다. `EXPOSE 8000 8501`은 사용할 포트를 선언할 뿐 Streamlit 프로세스를 자동으로 시작하지 않습니다.
|
|
|
|
현재 Dashboard는 MCP를 경유하지 않고 MCP 서버와 동일한 SQLite 파일을 직접 읽고 씁니다. 따라서 k3s에서는 두 프로세스를 별도 Deployment로 나누기보다 다음과 같이 함께 배치합니다.
|
|
|
|
```text
|
|
Deployment: jarca-ledger (replicas: 1)
|
|
└─ Pod
|
|
├─ mcp container
|
|
│ ├─ 동일 릴리즈 이미지
|
|
│ ├─ 기본 CMD: jarca-ledger-mcp
|
|
│ └─ containerPort: 8000
|
|
├─ frontend container
|
|
│ ├─ 동일 릴리즈 이미지
|
|
│ ├─ command: python -m streamlit run dashboard/app.py ...
|
|
│ └─ containerPort: 8501
|
|
└─ shared PVC
|
|
└─ /data/ledger.sqlite3
|
|
```
|
|
|
|
운영 원칙:
|
|
|
|
- 하나의 Pod 안에서 MCP와 Streamlit을 **별도 컨테이너**로 실행합니다. 한 컨테이너에서 두 프로세스를 supervisor나 background command로 함께 실행하지 않습니다.
|
|
- 두 컨테이너는 동일한 PVC를 `/data`에 마운트하고 `JARCA_LEDGER_DB_PATH=/data/ledger.sqlite3`를 사용합니다.
|
|
- frontend 컨테이너에만 `JARCA_DASHBOARD_ADMIN_ID`와 `JARCA_DASHBOARD_ADMIN_PASSWORD`를 Kubernetes Secret 또는 동등한 secret store에서 주입합니다. 실제 값은 manifest나 repository에 기록하지 않습니다.
|
|
- SQLite 파일을 공유하므로 Deployment의 replica는 `1`로 유지합니다.
|
|
- MCP와 frontend는 목적별 Kubernetes Service로 분리합니다. MCP는 `8000`, frontend는 `8501`을 대상으로 합니다.
|
|
- 외부 공개가 필요하면 일반적으로 frontend Service만 Ingress 또는 NodePort에 연결하고, MCP Service는 필요한 범위에서만 노출합니다.
|
|
- ARM64 worker에서만 실행하도록 Pod의 `nodeSelector` 또는 node affinity를 설정합니다.
|
|
- 배포 이미지는 가변 태그 대신 검증된 GHCR digest로 고정합니다.
|
|
|
|
Streamlit이 SQLite에 직접 접근하지 않고 MCP/API만 호출하도록 변경하거나 네트워크 DB로 전환한 뒤에는 MCP와 frontend를 각각 독립 Deployment로 분리할 수 있습니다.
|
|
|
|
로컬 `docker-compose.yml`도 동일한 애플리케이션 이미지를 두 컨테이너로 실행하는 구조입니다. 현재 Compose 설정은 로컬에서 빌드한 `jarca-ledger:latest`를 사용하며, MCP는 호스트 `8000` 포트로, Streamlit은 호스트 `40059`에서 컨테이너 `8501` 포트로 연결됩니다. Compose 실행 전 host environment 또는 안전한 `.env` 전달 경계에 두 Dashboard credential 변수를 설정해야 하며 누락 시 Compose가 시작을 거부합니다. GHCR 릴리즈를 배포할 때는 두 서비스의 `image`를 검증된 동일 digest로 지정해야 합니다.
|
|
|
|
## 주요 도구
|
|
|
|
- `ledger_health_check`
|
|
- `create_ledger_account`, `list_ledger_accounts`
|
|
- `create_ledger_transaction`, `list_ledger_transactions`, `update_ledger_transaction`, `delete_ledger_transaction`
|
|
- `create_ledger_budget`, `list_ledger_budgets`
|
|
- `get_monthly_cashflow_report`
|
|
- `get_category_spending_report`
|
|
- `get_fixed_cost_report`
|
|
- `get_budget_vs_actual_report`
|
|
- `get_investable_cashflow_report`
|
|
- `preview_ledger_csv_import`
|
|
- `preview_kb_bank_pdf_import`, `import_kb_bank_pdf`
|
|
- `list_ledger_import_batches`, `rollback_ledger_import_batch`
|
|
- `create_category_rule`, `seed_default_category_rules`, `list_category_rules`
|
|
- `preview_categorization`, `apply_categorization_rules`
|
|
- `list_uncategorized_transactions`, `get_uncategorized_merchant_summary`
|
|
- `create_ledger_category`, `seed_default_ledger_categories`, `list_ledger_categories`, `delete_ledger_category`
|
|
|
|
## 설계 원칙
|
|
|
|
- 금액은 원화 정수 원 단위로 저장합니다.
|
|
- 거래 금액은 양수로 넣고, `type`이 `income`, `expense`, `transfer`, `refund`, `adjustment` 중 하나로 방향을 나타냅니다.
|
|
- 금융 데이터이므로 Jarca 본체 MCP와 분리된 DB/서버로 운영합니다.
|
|
- 국민은행 PDF import는 암호화 PDF 비밀번호를 입력받아 PyMuPDF로 표를 추출하고, `transaction_datetime`, `counterparty`, `balance_after`, `branch`, `raw_summary`, `raw_payload`에 원본 세부 정보를 보존합니다.
|
|
- CSV/PDF/API import는 `import_batches`에 묶음 이력을 남기고, 잘못 가져온 batch는 `rollback_ledger_import_batch`로 되돌릴 수 있습니다.
|
|
- 카테고리에는 `cashflow_kind`를 저장해 실제 가계부 수입/지출(`income`, `expense`)과 계좌이체/투자이동/분석제외(`transfer`, `investment`, `exclude`, `debt_payment`, `unknown`)를 분리합니다.
|
|
- CSV import는 먼저 preview 후 저장하는 흐름으로 확장합니다.
|