Files
jarca-ledger-mcp/README.md
T

11 KiB

jarca-ledger-mcp

Jarca 가계부 전용 MCP 서버입니다. 계좌/카드, 수입·지출 거래, 예산, 월간 현금흐름, 고정비, 투자 여력 분석을 SQLite에 저장하고 MCP 도구로 제공합니다.

설치

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에 설정하세요.

cp .env.example .env
# JARCA_LEDGER_DB_PATH=/원하는/경로/ledger.sqlite3

Hermes 등록

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와 독립적인 서명 secret은 Streamlit secrets가 아니라 배포 secret이 주입하는 다음 환경변수에서만 읽습니다.

export JARCA_DASHBOARD_ADMIN_ID='<secret store에서 주입>'
export JARCA_DASHBOARD_ADMIN_PASSWORD='<secret store에서 주입>'
export JARCA_DASHBOARD_SIGNING_SECRET='<32 byte 이상의 무작위 secret을 주입>'

세 값 중 하나라도 누락됐거나 비어 있거나 읽을 수 없으면 Dashboard는 fail-closed하여 로그인 설정 오류만 표시하고 DB나 page view에 접근하지 않습니다. 서명 secret은 32 byte 이상이어야 합니다. 실제 credential이나 서명 secret을 .env, Compose 파일, shell history 또는 repository 문서에 저장하지 마세요.

cd /root/projects/jarca-ledger-mcp
. .venv/bin/activate
uvicorn dashboard.server:app --host 0.0.0.0 --port 8501

인증 성공 시 Dashboard의 동일 ASGI 프로세스에 포함된 server-side 인증 endpoint가 __Host-jarca_dashboard_auth cookie를 발급합니다. Cookie는 Secure, HttpOnly, SameSite=Lax, Path=/, no Domain이며 token의 sub는 관리자 ID가 아닌 비민감 고정 role dashboard-admin입니다. 기본 절대 만료는 24시간이며 로그인 상태 유지 (30일)을 선택하면 30일입니다. 일반 요청이나 재접속은 만료를 연장하지 않습니다. 관리자 ID, password 또는 서명 secret을 교체하면 현재 설정에 묶인 서명 검증이 실패합니다. malformed·expired·tampered·rotation-invalid 또는 configuration-failure cookie는 보호 자원 전에 거부되고 same-origin /api/auth/cleanup에서 matching deletion 후 미인증 상태로 전이합니다. 로그아웃도 browser cookie를 삭제하지만 stateless token을 서버에서 개별 폐기하지 않으므로 사고 대응 시 credential 또는 서명 secret을 교체해야 합니다.

Secure cookie를 사용하므로 browser가 접근하는 공개 origin은 HTTPS여야 하며 login/logout endpoint는 ASGI request scheme도 HTTPS가 아니면 거부합니다. Login과 logout은 same-origin HTTPS POST만 허용하고 cleanup GET은 same-origin HTTPS Referer를 검증합니다. TLS를 Ingress 또는 reverse proxy에서 종료할 때는 JARCA_DASHBOARD_FORWARDED_ALLOW_IPS를 실제 proxy source IP/CIDR allowlist로 설정해야 합니다. 기본값 127.0.0.1은 cluster ingress를 신뢰하지 않는 fail-closed 값입니다. * 또는 불특정 network를 신뢰하지 말고 proxy가 Host, Origin, Referer, X-Forwarded-Proto를 정확히 보존·설정하게 하세요. 인증 cookie의 발급·검증은 Dashboard 프로세스가 직접 수행합니다. 잘못된 ID 또는 password 응답은 매 제출마다 최소 1초 지연되지만 애플리케이션 잠금은 없습니다. 이 지연은 병렬 요청으로 우회할 수 있으므로 외부 노출 경계에 별도 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로 나누기보다 다음과 같이 함께 배치합니다.

Deployment: jarca-ledger (replicas: 1)
└─ Pod
   ├─ mcp container
   │  ├─ 동일 릴리즈 이미지
   │  ├─ 기본 CMD: jarca-ledger-mcp
   │  └─ containerPort: 8000
   ├─ frontend container
   │  ├─ 동일 릴리즈 이미지
   │  ├─ command: uvicorn dashboard.server:app ...
   │  └─ containerPort: 8501
   └─ shared PVC
      └─ /data/ledger.sqlite3

운영 원칙:

  • 하나의 Pod 안에서 MCP와 Streamlit을 별도 컨테이너로 실행합니다. 한 컨테이너에서 두 프로세스를 supervisor나 background command로 함께 실행하지 않습니다.
  • 두 컨테이너는 동일한 PVC를 /data에 마운트하고 JARCA_LEDGER_DB_PATH=/data/ledger.sqlite3를 사용합니다.
  • 이미지와 Pod는 UID/GID 1000:1000으로 실행하며, PVC 쓰기 권한은 fsGroup: 1000으로 설정합니다.
  • frontend 컨테이너에만 JARCA_DASHBOARD_ADMIN_ID, JARCA_DASHBOARD_ADMIN_PASSWORD, JARCA_DASHBOARD_SIGNING_SECRET을 Kubernetes Secret 또는 동등한 secret store에서 주입합니다. 실제 값은 manifest나 repository에 기록하지 않습니다.
  • SQLite 파일을 공유하므로 Deployment의 replica는 1, update 전략은 Recreate로 유지합니다.
  • Base는 Dashboard용 ClusterIP Service(8501)만 생성합니다. 인증 계층이 없는 MCP port 8000에는 Service를 만들지 않고 NetworkPolicy로 ingress를 차단합니다. MCP 원격 접근이 필요하면 environment overlay에서 인증된 gateway와 제한된 ingress 정책을 함께 정의합니다.
  • 외부 공개가 필요하면 Dashboard Service만 environment overlay의 Ingress 또는 NodePort에 연결합니다.
  • ARM64 worker에서만 실행하도록 Pod의 nodeSelector 또는 node affinity를 설정합니다.
  • 배포 이미지는 가변 태그 대신 검증된 gitea.jsfamily.quest/jsfamily/jarca-ledger-mcp Gitea Container Registry digest로 고정합니다.

검증된 k3s Kustomize base는 deploy/k8s/base에 있으며, Secret 준비·digest pinning·배포 절차는 deploy/k8s/README.md를 따릅니다. Base에는 실제 Secret, Ingress, TLS와 cluster별 설정을 포함하지 않습니다.

Streamlit이 SQLite에 직접 접근하지 않고 MCP/API만 호출하도록 변경하거나 네트워크 DB로 전환한 뒤에는 MCP와 frontend를 각각 독립 Deployment로 분리할 수 있습니다.

로컬 docker-compose.yml도 동일한 애플리케이션 이미지를 두 컨테이너로 실행하는 구조입니다. 현재 Compose 설정은 로컬에서 빌드한 jarca-ledger:latest를 사용하며, MCP는 호스트 8000 포트로, Dashboard는 호스트 40059에서 컨테이너 8501 포트로 연결됩니다. Compose 실행 전 host environment 또는 안전한 .env 전달 경계에 세 Dashboard 인증 변수를 설정해야 하며 누락 시 Compose가 시작을 거부합니다. Browser 접근에는 HTTPS reverse proxy를 앞에 두어야 합니다. Bind mount 대상 /volume1/docker/jarca-ledger/data는 UID/GID 1000:1000이 쓸 수 있어야 합니다. Gitea Container Registry 릴리즈를 배포할 때는 두 서비스의 image를 검증된 동일 gitea.jsfamily.quest/jsfamily/jarca-ledger-mcp 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

설계 원칙

작업 규칙

  • 스키마 변경 작업 중에는 schema version 자동 증가를 금지합니다. 현재 코드·DB·schema의 버전은 16으로 동결하며, 사용자가 마지막에 명시적으로 올리라고 지시한 경우에만 증가합니다.

  • 버전 동결은 기존 DB 반영과 멱등성 검증 의무를 면제하지 않습니다. 스키마 변경 시 기존 DB 업그레이드·재실행 안전성을 계속 검증합니다.

  • 자동 bump를 전제로 한 테스트 업데이트도 금지합니다. 기존 미커밋 변경은 보존하고, 이 규칙 기록만으로 코드·DB·schema 자체를 변경하지 않습니다.

  • 금액은 원화 정수 원 단위로 저장합니다.

  • 거래 금액은 양수로 넣고, 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 후 저장하는 흐름으로 확장합니다.