feat: deploy jarca ledger network mcp service

- add Docker deployment for independent jarca-ledger container
- enable FastMCP streamable-http runtime configuration
- document dcy-013, REQ-019, and BAT-015 verification evidence
- update structure/runtime baselines and deployment runbook
This commit is contained in:
Jarca
2026-07-07 23:04:42 +09:00
parent a7b18c435d
commit 2ccfc9c064
19 changed files with 829 additions and 16 deletions
@@ -0,0 +1,44 @@
# BAT-015 Design
## Architecture Decision
`jarca-ledger` is deployed as an independent Docker service that directly serves FastMCP `streamable-http` transport on port 8000. `genichin-agent` is no longer the process launcher for Jarca Ledger MCP. It may remain a client, but the Jarca Ledger service lifecycle belongs to Docker.
## Runtime topology
```text
finance-agent / Hermes client
│
│ Streamable HTTP MCP
▼
http://192.168.55.253:8000/mcp
│
▼
jarca-ledger container
├─ jarca-ledger-mcp FastMCP server
├─ /data/ledger.sqlite3
└─ Docker healthcheck on 127.0.0.1:8000
│
▼
Synology host /volume1/docker/jarca-ledger/data
```
## Transport contract
- Default remains `stdio` when no env is set.
- `JARCA_MCP_TRANSPORT=streamable-http` runs Uvicorn/FastMCP network transport.
- `JARCA_MCP_HOST=0.0.0.0`, `JARCA_MCP_PORT=8000`, `JARCA_MCP_STREAMABLE_HTTP_PATH=/mcp` define the Docker service endpoint.
- Tool names and tool payload contracts remain unchanged.
## Data migration design
- Source: `jarca:/root/.local/share/jarca/ledger.sqlite3`
- Target: `jarca-ledger:/data/ledger.sqlite3`
- Snapshot method: SQLite `Connection.backup()` to avoid copying a live DB mid-write.
- Pre-replacement safety: back up the new empty `/data/ledger.sqlite3` under `/data/backups/pre_migration_empty_<timestamp>.sqlite3`.
- Verification: sha256, `PRAGMA integrity_check`, table counts, MCP `get_ledger_db_status`.
## Security and exposure boundary
- This batch exposes MCP only on the Synology host/internal network port mapping `8000:8000`.
- Internet exposure, reverse proxy, TLS, and auth policy are not included and must be handled as a separate approved change before external access.
@@ -0,0 +1,41 @@
# BAT-015 Implementation
## Code changes
- `jarca_ledger_mcp/adapters/mcp/server.py`
- Added `McpRuntimeConfig` dataclass.
- Added env parsing for `JARCA_MCP_TRANSPORT`, `JARCA_MCP_HOST`, `JARCA_MCP_PORT`, `JARCA_MCP_STREAMABLE_HTTP_PATH`, `JARCA_MCP_SSE_PATH`, `JARCA_MCP_MESSAGE_PATH`, `JARCA_MCP_STATELESS_HTTP`.
- Instantiates FastMCP with configured host/port/path values.
- Calls `mcp.run(transport=_runtime_config.transport)`.
- `tests/test_mcp_http_runtime.py`
- Added config tests for default stdio, streamable-http env, and invalid transport rejection.
## Deployment files
- `Dockerfile`
- Builds `jarca-ledger:latest` from `python:3.12-slim`.
- Installs `.[dashboard]` dependencies.
- Exposes port 8000 and runs `jarca-ledger-mcp`.
- Adds TCP healthcheck against `127.0.0.1:${JARCA_MCP_PORT}`.
- `.dockerignore`
- Excludes `.env`, DBs, financial source files, tests/docs/scripts not needed in image, and local artifacts.
- `docker-compose.yml`
- Defines `container_name: jarca-ledger`.
- Maps `8000:8000`.
- Mounts `/volume1/docker/jarca-ledger/data:/data`.
- Sets streamable-http MCP env.
- Uses `restart: unless-stopped`.
## Data migration implementation
1. Created a consistent source snapshot inside the old `jarca` container using SQLite backup API.
2. Copied snapshot to the host temp path.
3. Backed up the initial empty `jarca-ledger` DB under `/data/backups/pre_migration_empty_20260705152129.sqlite3`.
4. Stopped `jarca-ledger`, removed WAL/SHM sidecars, copied the snapshot to `/data/ledger.sqlite3`, normalized permissions, and restarted.
5. Waited for Docker health to become `healthy`.
## Documentation changes
- Added `docs/runbooks/docker-network-mcp-deploy.md`.
- Added this StagePilot post-implementation trace: dcy-013, REQ-019, BAT-015.
- Updated `docs/project-structure.md` and `docs/runtime-flows.md` to include Docker/network MCP runtime.
@@ -0,0 +1,20 @@
# BAT-015 Docker network MCP deployment
- Profile: standard
- Status: release-candidate
- Included REQ: REQ-019
- Discovery: dcy-013
- Owner: 렛저장인
- Date (KST): 2026-07-07
## 목표
REQ-019에 따라 `jarca-ledger`를 `genichin-agent` 생명주기에서 분리된 독립 Docker service로 배포하고, streamable-http MCP endpoint와 Synology volume 기반 운영 DB를 검증한다.
## 산출물
- FastMCP runtime transport 설정화
- Dockerfile / `.dockerignore` / `docker-compose.yml`
- Docker network MCP 배포 runbook
- 기존 `jarca` 운영 DB 이전
- pytest, Docker health, MCP smoke, DB integrity/table-count verification
@@ -0,0 +1,24 @@
# BAT-015 Planning
## Scope
- Implement and verify REQ-019 Docker network MCP deployment.
- Preserve existing stdio MCP compatibility while enabling `streamable-http` runtime mode.
- Deploy `jarca-ledger` as an independent Docker service with Synology bind volume.
- Migrate the existing operating DB from `jarca` to `jarca-ledger` with backup/snapshot evidence.
- Refresh deployment runbook and baseline docs.
## Design Gate
- Required: yes
- Rationale: this batch changes runtime topology, deployment ownership, exposed MCP transport, Docker health behavior, and operating DB location. A design/evidence trail is required even though the code diff is small.
## Work plan
1. Add MCP runtime config helpers for stdio/sse/streamable-http transport selection.
2. Add tests for default stdio, streamable-http env config, and invalid transport rejection.
3. Add Dockerfile, `.dockerignore`, compose service, healthcheck, and runbook.
4. Build and run `jarca-ledger` with `/volume1/docker/jarca-ledger/data:/data`.
5. Verify network MCP using a streamable-http client against `http://192.168.55.253:8000/mcp`.
6. Snapshot existing `jarca` DB with SQLite backup API, back up the new empty DB, replace `/data/ledger.sqlite3`, restart, and verify table counts/integrity.
7. Update StagePilot Discovery/SRS/BAT/baseline docs.
@@ -0,0 +1,141 @@
# BAT-015 Verification
## Verification summary
Status: PASS
The Docker network MCP deployment is implemented and running. `jarca-ledger` is independent of `genichin-agent`, exposes streamable-http MCP on port 8000, uses `/volume1/docker/jarca-ledger/data` as the operating volume, and serves the migrated operating ledger DB.
## Acceptance criteria evidence
### AC-1. Runtime config tests pass
- Command: `python -m pytest tests/test_mcp_http_runtime.py -q`
- Result: PASS
- Output: `... [100%]`
### AC-2. Full regression suite passes
- Command: `python -m pytest -q`
- Result: PASS
- Output: `........................ss..................ss... [100%]`
### AC-3. Docker build and independent service start succeed
- Command: `docker compose build`
- Result: PASS, image `jarca-ledger:latest` built.
- Command: `docker compose up -d --no-build`
- Result: container `jarca-ledger` created/started.
Docker state evidence:
```text
state=running health=healthy restart=unless-stopped
```
Mount evidence:
```text
/volume1/docker/jarca-ledger/data -> /data (bind)
```
Log evidence:
```text
StreamableHTTP session manager started
Application startup complete.
Uvicorn running on http://0.0.0.0:8000
```
### AC-4. Network MCP smoke passes
Endpoint used from `genichin-agent` container:
```text
http://192.168.55.253:8000/mcp
```
Smoke evidence:
```text
tool_count 56
ledger_health_check success true
db_path /data/ledger.sqlite3
schema_version 10
active_transactions 6225
```
### AC-5. Operating DB migration preserved data
Source DB before migration:
```text
jarca:/root/.local/share/jarca/ledger.sqlite3
size 8343552
sha256 f43532a41db3fd64edfe26d3dcc6f54ab8cf34bf79018fed9e53324996e4413b
accounts 8
transactions 6312
import_batches 13
category_rules 46
ledger_categories 62
```
Migration snapshot evidence:
```text
snapshot_integrity ok
snapshot_size 8343552
snapshot_sha256 e940a237e25e71561fe1af332f29cb11e92f02612b4ff89a7ab562585f82f278
```
Post-migration target evidence:
```text
path /data/ledger.sqlite3
size 8343552
sha256 e940a237e25e71561fe1af332f29cb11e92f02612b4ff89a7ab562585f82f278
integrity ok
accounts 8
transactions 6312
budgets 0
import_batches 13
category_rules 46
ledger_categories 62
manual_assets 0
asset_snapshots 0
```
MCP `get_ledger_db_status` evidence:
```text
integrity_check ok
table_counts.accounts 8
table_counts.transactions 6312
table_counts.import_batches 13
table_counts.category_rules 46
table_counts.ledger_categories 62
```
### AC-6. Documentation and baselines updated
- `docs/runbooks/docker-network-mcp-deploy.md` added.
- `docs/project-structure.md` updated with Dockerfile/compose/network MCP service and deployment boundaries.
- `docs/runtime-flows.md` updated with Docker network MCP and DB migration flows.
- StagePilot trace added: `dcy-013`, `REQ-019`, `BAT-015`.
## Safety boundary
- No raw financial statement source files were copied into the image.
- `.dockerignore` excludes DB/source financial files from image build context.
- Existing empty target DB was backed up before replacement.
- Existing source DB was not modified; migration used a snapshot copy.
## Blocking Issues
- None for internal Synology network operation.
## Follow-up candidates
- Add remote MCP endpoint registration to the finance-agent profile.
- Consider a separate auth/TLS/reverse-proxy Discovery before any external network exposure.
- Consider an automated backup scheduler as a separate operations requirement.
+1
View File
@@ -22,3 +22,4 @@
| BAT-012 | standard | release-candidate | REQ-016 | dcy-011 | [bat-012_20260703_import-pipeline-split](bat-012_20260703_import-pipeline-split/) |
| BAT-013 | standard | release-candidate | REQ-017 | dcy-011 | [bat-013_20260703_advanced-workflow-service-split](bat-013_20260703_advanced-workflow-service-split/) |
| BAT-014 | standard | release-candidate | REQ-018 | dcy-011 | [bat-014_20260703_adapter-surface-narrowing-baseline-refresh](bat-014_20260703_adapter-surface-narrowing-baseline-refresh/) |
| BAT-015 | standard | release-candidate | REQ-019 | dcy-013 | [bat-015_20260707_docker-network-mcp-deployment](bat-015_20260707_docker-network-mcp-deployment/) |