business-card-watchdog
Provides tools to route processed business card data to Google Contacts, enabling contact creation and updates.
Provides tools to route processed business card data to Odoo/Odollo tenants for CRM and contact management.
Integrates with Syncthing to watch phone camera directories for new business card images, enabling automated processing.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@business-card-watchdogprocess new business cards from the SyncThing folder"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Business Card Watchdog
Business Card Watchdog is an installable, user-scoped service for processing large batches of business card images from watched folders, including SyncThing phone-camera directories.
The design builds on the local business-card-to-contact skill:
OCR and normalize each card into reviewed contact JSON.
Generate candidate contact photos.
Route processed card data and image artifacts to Google Contacts and/or Odoo/Odollo tenants.
Use deterministic App Intelligence style orchestration for large batches: host-owned state, ledgers, approvals, replay logs, and explicit sink policies.
Expose CLI, API, MCP, and watched-folder surfaces from one shared core.
Install
From this repo, use uv:
uv venv .venv
uv pip install --python .venv/bin/python -e ".[api,watch,vision,dev]"Run commands through the venv:
.venv/bin/bcw status --jsonFor deterministic rectangle/contour hints in the image prefilter, include the vision extra:
uv pip install --python .venv/bin/python -e ".[vision]"Related MCP server: mcp-dev-brasil
User Config
Config lives under the user scope:
~/.config/business-card-watchdog/config.toml
~/.local/share/business-card-watchdog/
~/.cache/business-card-watchdog/Create an editable starter config:
.venv/bin/bcw init-configThe SyncThing source can be configured as a normal path or an alias such as:
[paths]
sync_phone = "/mnt/e/SyncThing/Phone Camera"
[watch]
inputs = ["$fsr:sync_phone"]First Commands
Inspect local readiness and safe next commands:
.venv/bin/bcw status
.venv/bin/bcw operator-dashboardThe operator dashboard includes matching CLI commands, API routes, and MCP tool argument templates for read-only continuation, live-pilot handoff inspection, selected-target approval, and selected-target command-copy gates.
Dry-run a batch:
.venv/bin/bcw process "/mnt/e/SyncThing/Phone Camera" --dry-runStart polling watch mode:
.venv/bin/bcw watch --onceServe the API, if fastapi and uvicorn are installed:
.venv/bin/bcw api --host 127.0.0.1 --port 8787Read-only API inspection surfaces include GET /operator/dashboard and GET /actions/next.
Print the MCP tool manifest:
.venv/bin/bcw mcp-manifestInspect runs, jobs, review queue, sinks, and watcher state:
.venv/bin/bcw runs list
.venv/bin/bcw runs summary <run-id>
.venv/bin/bcw runs phase-report <run-id>
.venv/bin/bcw runs dry-run-closeout <run-id> --json
.venv/bin/bcw runs dry-run-review-handoff <run-id> --json
.venv/bin/bcw runs dry-run-safe-loop <run-id> --limit 5 --json
.venv/bin/bcw runs review-route-readiness <run-id> --json
.venv/bin/bcw runs lookup-selection-packet <run-id> --operator <operator> --json
.venv/bin/bcw runs close-lookup-prerequisites <run-id> --operator <operator> --json
.venv/bin/bcw runs selected-target-approval-boundary <run-id> --operator <operator> --json
.venv/bin/bcw runs selected-target-command-copy-packet <run-id> --operator <operator> --response <operator-response> --acknowledgement <operator-acknowledgement> --json
.venv/bin/bcw operator-dashboard --run-id <run-id>
.venv/bin/bcw drills review-routing
.venv/bin/bcw sinks lookup-readiness <job-id> --run-id <run-id> --sink google_contacts --json
.venv/bin/bcw jobs list --run-id <run-id>
.venv/bin/bcw jobs show <job-id> --run-id <run-id>
.venv/bin/bcw reviews list --run-id <run-id>
.venv/bin/bcw actions next --run-id <run-id> --json
.venv/bin/bcw sinks check --json
.venv/bin/bcw watch-status --json
.venv/bin/bcw watch-backlog-preflight --json
.venv/bin/bcw watch-dry-run-readiness --json
.venv/bin/bcw watch-dry-run-selection-handoff --json
.venv/bin/bcw drills watch-dry-run-selection
.venv/bin/bcw drills watch-dry-run-executionAdd --json to run, job, and review commands when consuming them from automation.
drills multi-card-preclassification creates a synthetic phone-photo-like image
with multiple card rectangles and proves the deterministic OpenCV prefilter records
candidate card boxes before OCR/App Intelligence. It uses only cache-local fixture
data and makes no private, enrichment, or sink calls.
watch-backlog-preflight inspects configured watch inputs without processing
files. It reports only aggregate backlog counts, redacted input references, a
recommended next action, and stop conditions. If backlog is present, use
watch-dry-run-selection-handoff --json, validate the returned operator
response template with watch-dry-run-validate-response, and request
watch-dry-run-command-copy-packet with the required acknowledgement. The packet
returns a scoped command such as
watch --once --dry-run --input-ref <input_ref> --limit <n> as copyable text
only after validation; it does not execute the private-source dry run.
drills watch-dry-run-selection creates a synthetic watched source and rehearses
the preflight, handoff, response validation, and command-copy packet without
using configured watch inputs. It writes markdown sample output for operator
review and does not execute the copied command, run OCR/App Intelligence, or call
network/sink surfaces.
drills watch-dry-run-execution creates a synthetic watched source and executes
one real watcher scan through the dry-run batch orchestrator using a fixture
skill adapter. It proves seen-file persistence, contact artifact creation, and
dry-run sink payload planning without using configured watch inputs or calling
live sink/network surfaces.
watch-dry-run-readiness inspects the configured watch backlog without
processing files and emits the operator checklist required before any
private-source watch dry run. It keeps paths and filenames redacted, requires
fixture execution review plus response validation, and does not return a
copyable dry-run command.
For the Plan 0060 Milestone 9 path from the current empty contact store to live
target candidates, follow
docs/operations/milestone-9-dry-run-to-live-target.md. That checklist starts
with the synthetic watcher drill, then requires an operator-selected private
watched input, dry-run command-copy acknowledgement, dry-run closeout, contact
projection, review, route readiness, and live-target candidate inspection before
any selected live target can be created.
After an operator-selected dry-run batch completes, run
runs dry-run-closeout <run-id> --json before review or live-pilot selection.
The closeout reads only the local run ledger and checks that the run completed in
dry-run mode with no live/pilot event markers, no live/pilot artifacts, no
writes, and no network calls.
Then run runs dry-run-review-handoff <run-id> --json to summarize review
groups, phase state, safe agent-loop actions, and explicit operator actions from
the same local dry-run ledger. The handoff does not run enrichment or live sink
calls; live-pilot commands in the packet are no-write inspection commands.
If the handoff reports safe auto actions, run
runs dry-run-safe-loop <run-id> --limit <n> --json to execute a bounded
host-owned action loop. The loop is gated by dry-run closeout and review handoff,
executes only the safe-action allowlist, and stops before explicit operator or
live sink boundaries.
Ordinary dry-run batch processing also writes card_candidates.json beside a job's
preclassification.json when deterministic OpenCV boxes exist. Those records are
stable pre-OCR candidate boxes for later fanout; they are not contacts and cannot be
routed or enriched without OCR/App Intelligence verification. The same run also
writes candidate_work_items.json with one pending child OCR/App Intelligence work
item per candidate box so later agent loops can process large multi-card photos
without losing parent image lineage. When crops can be materialized, the run writes
candidate_crops.json and image files under the job artifact directory, then marks
those child work items crop_ready for later OCR/App Intelligence verification.
The run also writes child_verification_requests.json as a dry-run execution
contract for the crop-ready items; these requests are not executed and cannot route,
enrich, or write contacts. Synthetic/offline runs then write
child_verification_results.json and per-candidate fixture result files so the
review workflow can be built without production OCR/App Intelligence or sink calls.
Those child results are promoted into review-pending child_contact_promotions.json
records and normal contact-candidate files with parent/child lineage preserved.
Use reviews children --run-id <run-id> --json to inspect those promoted child
candidates before review, routing, enrichment, or sink work. Use
reviews child-review <candidate-id> --run-id <run-id> --action approve_child_for_routing --json
to approve a promoted child candidate into local reviewed-child-contact artifacts;
that approval still does not call enrichment, dedupe, routing, or live sinks.
After approval, reviews child-route-prep-queue --run-id <run-id> --json lists
child contacts ready for dry-run route preparation, and
reviews child-route-prep <candidate-id> --run-id <run-id> --json writes local
child lookup and sink plan artifacts with parent/child lineage. This prep still
does not run live lookup, resolve duplicates, enrich, or write sinks. Use
reviews child-lookup-result <candidate-id> --run-id <run-id> --matches-by-sink-json '{}' --json
to import supplied child lookup evidence, then
reviews child-assess-duplicates <candidate-id> --run-id <run-id> --json to
classify downstream duplicate state without live sink calls. Use
reviews child-resolve-duplicate <candidate-id> --run-id <run-id> --decision create_new --json
to record an explicit child duplicate decision, then
reviews child-sink-plan-gate <candidate-id> --run-id <run-id> --json to verify
whether the child sink plan is still blocked or cleared for future preflight.
Once clear, reviews child-sink-apply-preflight <candidate-id> --run-id <run-id> --json
previews child sink apply readiness, and
reviews child-selected-target-handoff <candidate-id> --run-id <run-id> --sink google_contacts --json
writes a no-live operator handoff packet without creating a selected target.
Use reviews child-validate-selected-target-response <candidate-id> --run-id <run-id> --response <operator-response> --json
to validate a copied child handoff response, then
reviews child-selected-target-execution-checklist <candidate-id> --run-id <run-id> --response <operator-response> --json
to create a no-live checklist that still blocks selected-target creation and
live sink execution. Use
reviews child-selected-target-command-copy-packet <candidate-id> --run-id <run-id> --response <operator-response> --acknowledgement <operator-acknowledgement> --json
to show the next offline command only after the acknowledgement names the run,
child candidate, sink, and operator. It never returns a live GWS/Odollo/Odoo
command. Use
reviews child-selected-target-audit <candidate-id> --run-id <run-id> --json
to preview the current child selected-target packet and whether a replacement
would require abandonment. The audit never creates, replaces, or abandons a
selected target. Use
reviews child-abandon-selected-target <candidate-id> --run-id <run-id> --operator <operator> --reason <reason> --json
to record a no-live child abandonment artifact, then
reviews child-selected-target-replacement-reset <candidate-id> --run-id <run-id> --sink google_contacts --operator <operator> --json
to verify that a replacement handoff preview is unblocked. Use
reviews child-replacement-handoff-refresh <candidate-id> --run-id <run-id> --sink google_contacts --operator <operator> --json
to refresh the no-live handoff and mark prior child selected-target response,
checklist, command-copy, and audit artifacts stale. Then use
reviews child-validate-replacement-response <candidate-id> --run-id <run-id> --response <operator-response> --json
to validate the replacement response only after the refresh packet and staleness
marker are ready. Then use
reviews child-replacement-execution-checklist <candidate-id> --run-id <run-id> --response <operator-response> --json
and
reviews child-replacement-command-copy-packet <candidate-id> --run-id <run-id> --response <operator-response> --acknowledgement <operator-acknowledgement> --json
to create replacement-specific no-live checklist and offline command-copy
packets. Use
reviews child-replacement-closeout-status <candidate-id> --run-id <run-id> --json
to summarize stale predecessor artifacts, refreshed replacement packet readiness,
and no-live counters in one local rollup artifact. Review bundles, review HTML,
review workbook CSVs, and operator-dashboard expose that closeout state so
agent loops do not need to know the child artifact path.
drills review-routing creates a synthetic fixture run and proves review approval,
review bundle/workbook export, duplicate lookup planning, dry-run sink routing, and
apply preflight without configured watch inputs, private images, enrichment calls, or
live sink operations.
drills live-pilot-rehearsal builds on that synthetic fixture and rehearses the
operator-selected live-pilot gate through preflight, selected target creation,
selected-target audit, lookup handoff, redacted readiness export, execution
checklist, and command copy packet. It writes preflight and rehearsal markdown
sample outputs, but does not execute the copied command or call live sinks.
drills child-replacement-readiness creates a synthetic child replacement run and
exports review bundle, review HTML, review workbook, and operator dashboard sample
outputs. It proves the blocked audit/validation states before refresh and the
ready handoff, response, checklist, command-copy, and closeout states after
replacement refresh. It uses fixture data only and does not call live sinks.
drills watch-dry-run-selection exports sample output for the configured-watch
dry-run approval workflow using only a synthetic watch source. It proves the
command-copy packet can be reached without touching private SyncThing inputs or
executing a private-source watch dry-run command.
drills watch-dry-run-execution exports sample output for a synthetic watched
dry-run execution. It does execute fixture OCR/spec/review/normalization/routing
through the real batch orchestrator, but it does not use configured SyncThing
inputs and does not call live sinks.
watch-dry-run-readiness is the no-processing configured-source checkpoint
between fixture execution review and any operator-selected private dry run. It
reports only redacted aggregate backlog state and keeps private-source watch
execution blocked behind the existing response-validation and command-copy
packet. When unblocked, command-copy text is scoped to one selected input ref
and a positive source/document limit.
runs dry-run-closeout is the post-dry-run checkpoint. It reads the completed
run ledger, records an optional closeout artifact, and keeps review/routing or
live-pilot selection blocked if it sees a non-dry run, incomplete run, live/pilot
ledger markers, write attempts, or network calls.
runs dry-run-review-handoff is the post-closeout review and routing checkpoint.
It composes the closeout, review bundle groups, phase report, and next-action
counts so an operator or agent loop can continue safe dry-run planning without
crossing into live lookup/write/readback.
runs dry-run-safe-loop is the bounded post-handoff executor. It runs only
host-owned safe actions such as dry-run lookup planning and local adapter-request
artifact preparation, then recomputes the handoff state so the next operator or
agent sees the new boundary.
offline-pilot-gap-audit inspects offline drill and documentation coverage and
reports the remaining live/operator-only boundaries without running private
source processing, enrichment, or live sink calls.
Operator-Selected Live Pilots
Live lookup, write, and readback are not part of generic continuation or broad batch automation. Use one explicit run_id, job_id, sink, operator, and scope at a time, starting with non-sensitive test card data.
Review the selected target gates before any non-simulated sink call:
.venv/bin/bcw operator-selected-live-smoke-preflight --run-id <run-id> --sink google_contacts --no-write
.venv/bin/bcw operator-live-pilot-readiness-packet --run-id <run-id> --sink google_contacts --no-write
.venv/bin/bcw live-selection-requirements --run-id <run-id> --sink google_contacts --no-write
.venv/bin/bcw sinks live-selection-packet <job-id> --run-id <run-id> --sink google_contacts --operator <operator> --scope lookup
.venv/bin/bcw runs live-pilot-handoff <run-id> --no-write
.venv/bin/bcw runs selected-target-approval-boundary <run-id> --operator <operator> --sink google_contacts --no-write
# Run the packet's printed "Validate prefilled response:" command first, then replace
# safety_confirmation=<tenant-profile-account-confirmation> with the operator's tenant/profile confirmation.
.venv/bin/bcw runs live-pilot-validate-response <run-id> --response "run_id=<run-id> job_id=<job-id> sink=google_contacts operator=<operator> scope=lookup safety_confirmation=<operator confirms intended tenant/profile>"
.venv/bin/bcw runs selected-target-command-copy-packet <run-id> --operator <operator> --response "run_id=<run-id> job_id=<job-id> sink=google_contacts operator=<operator> scope=lookup safety_confirmation=<operator confirms intended tenant/profile>" --acknowledgement "acknowledge run_id=<run-id> job_id=<job-id> sink=google_contacts operator=<operator> copy command"
.venv/bin/bcw sinks select-live-target <job-id> --run-id <run-id> --sink google_contacts --operator <operator> --scope lookup --safety-confirmation "<operator confirms intended tenant/profile>" --json
.venv/bin/bcw sinks selected-target-audit <job-id> --run-id <run-id> --scope lookup --no-write
.venv/bin/bcw runs live-pilot-status <run-id> --no-writeUse --sink odoo for Odollo/Odoo targets. Start with operator-selected-live-smoke-preflight for a composed no-live view of the offline gap audit, live readiness audit, selection requirements, required operator fields, and stop conditions. Use operator-live-pilot-readiness-packet when an agent loop needs one no-live packet that combines dashboard state, selected-target gate commands, operator preflight summary, and synthetic rehearsal expectations before selected-target approval. Use live-selection-requirements when you need the exact candidate-specific packet and approval commands. Use runs selected-target-approval-boundary --no-write to compose the approval template, response validation, selected-target preflight, and selected-target preview without creating selected_live_target.json. Use runs selected-target-command-copy-packet to require a matching acknowledgement before copying the selected-target creation command. Use the selection packet's Validate prefilled response: command for the first read-only check, then validate the filled response after replacing the confirmation placeholder. Use runs live-pilot-handoff --no-write when an agent loop needs the current concrete operator response template; it does not create selected_live_target.json. Validation is read-only and only returns blockers or the exact select-target command. If the selection packet reports that replacement requires abandonment, run sinks abandon-live-pilot for the current target before selecting a replacement. Before any non-simulated lookup/write/readback command, run runs live-pilot-readiness-export-from-response, runs live-pilot-execution-checklist-from-response, and runs live-pilot-command-copy-packet-from-response; copy only the returned command_copy_text when the packet is ready_for_operator_copy. Use docs/operations/live-pilot-checklists.md for the full lookup, write, readback, and closeout procedure.
After a selected target already exists, validation no longer returns a second select-target command. It reports the active-target state and starts the ordered post-selection sequence at selected-target audit and lookup-smoke handoff.
Install a user-scope systemd unit file without enabling or starting it:
.venv/bin/bcw service install --json
systemctl --user daemon-reload
systemctl --user enable business-card-watchdog.service
systemctl --user start business-card-watchdog.serviceRemove the generated unit file:
.venv/bin/bcw service uninstall --json
systemctl --user daemon-reloadThe service commands write only under the user config directory, normally:
~/.config/systemd/user/business-card-watchdog.serviceCurrent State
This is an initial scaffold. It has deterministic run ledgers, config loading, routing decisions, CLI/API/MCP surfaces, and a subprocess adapter for the existing business-card-to-contact skill. bcw status reports safe next commands, stop conditions, and zero write/network counters. External writes are intentionally explicit and dry-run first.
Operational details live in:
docs/operations/user-scope-install-and-service.mddocs/operations/review-workflow.mddocs/operations/review-workflow-sample-output.mddocs/operations/public-upstream-validation.mddocs/operations/live-pilot-checklists.mddocs/operations/operator-selected-live-smoke-preflight-sample-output.mddocs/operations/live-pilot-sample-output.mddocs/operations/child-replacement-sample-output.mddocs/operations/release-readiness.md
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/CochranResearchGroup/business-card-watchdog'
If you have feedback or need assistance with the MCP directory API, please join our Discord server