#!/usr/bin/env bash # scripts/connect/connect-quickbooks.sh — S7: QuickBooks Online connect helper # # Connects QuickBooks Online via local MCP (intuit/quickbooks-online-mcp-server). # The MCP server runs as a container on the same Docker network as Hermes. # # Allows: reports, search/get invoices, bills, vendors, customers, company info. # Denies: create_payment, bill_payment, money movement; write/update/delete off for MVP. # # Platform-first: uses nemohermes config set for MCP registration. # # Usage: # ./scripts/connect/connect-quickbooks.sh [--dry-run|--apply] # ./scripts/connect/connect-quickbooks.sh --help set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # Source shared helpers # shellcheck source=../lib/common.sh source "$SCRIPT_DIR/../lib/common.sh" # shellcheck source=../lib/env.sh source "$SCRIPT_DIR/../lib/env.sh" # shellcheck source=../lib/connect_state.sh source "$SCRIPT_DIR/../lib/connect_state.sh" # ── Defaults ─────────────────────────────────────────────────────────────── DRY_RUN=1 APPLY=0 # QBO MCP configuration QBO_MCP_IMAGE="ghcr.io/intuit/quickbooks-online-mcp-server:latest" QBO_MCP_CONTAINER="lumina-qbo-mcp" QBO_MCP_NETWORK="lumina-network" # Allowed tools (read-only) QBO_ALLOWED_TOOLS=( "get_report" "search_invoice" "get_invoice" "search_bill" "get_bill" "search_vendor" "get_vendor" "search_customer" "get_customer" "get_company_info" ) # Denied tools QBO_DENIED_TOOLS=( "create_payment" "bill_payment" "create_invoice" "update_invoice" "delete_invoice" "create_bill" "update_bill" "delete_bill" ) # ── Parse args ───────────────────────────────────────────────────────────── while [[ $# -gt 0 ]]; do case "$1" in --help|-h) cat </dev/null 2>&1; then log_error "Sandbox '$SANDBOX_NAME' not found. Run S4 first." exit 1 fi # ── Dry-run mode ─────────────────────────────────────────────────────────── if [[ $DRY_RUN -eq 1 ]]; then log_info "[DRY-RUN] QuickBooks Online connect preview:" log_info "" log_info " MCP Image: $QBO_MCP_IMAGE" log_info " Container: $QBO_MCP_CONTAINER" log_info " Network: $QBO_MCP_NETWORK" log_info " Sandbox: $SANDBOX_NAME" log_info "" log_info " Allowed tools (read-only):" for tool in "${QBO_ALLOWED_TOOLS[@]}"; do log_info " - $tool" done log_info "" log_info " Denied tools:" for tool in "${QBO_DENIED_TOOLS[@]}"; do log_info " - $tool" done log_info "" log_info " Steps to connect:" log_info " 1. Owner creates QBO app at developer.intuit.com" log_info " 2. Owner completes OAuth flow to get access token" log_info " 3. Operator stores credentials: openshell provider set qbo-* " log_info " 4. Operator starts MCP container on Docker network" log_info " 5. Operator registers MCP: nemohermes $SANDBOX_NAME config set mcp_servers.quickbooks" log_info " 6. Tool allowlist/denylist applied via nemohermes config" log_info "" log_info " To execute: $(basename "$0") --apply" exit 0 fi # ── Apply mode ───────────────────────────────────────────────────────────── log_info "QuickBooks Online connect (apply mode):" log_info "" log_info "The owner must first:" log_info " 1. Create a QBO application at developer.intuit.com" log_info " 2. Complete OAuth flow to obtain Client ID, Client Secret, and Access Token" log_info " 3. Provide credentials to the operator" log_info "" # Store credentials via OpenShell provider store if openshell_available; then log_info "Storing QBO credentials in OpenShell provider store…" local_client_id="" local_client_secret="" local_access_token="" local_realm_id="" read -rp "QBO Client ID: " local_client_id read -rsp "QBO Client Secret: " local_client_secret echo "" read -rsp "QBO Access Token: " local_access_token echo "" read -rp "QBO Realm ID (company ID): " local_realm_id if [[ -z "$local_client_id" || -z "$local_client_secret" || -z "$local_access_token" ]]; then log_warn "Incomplete QBO credentials. Skipping." set_status "quickbooks" "skipped" "Operator skipped — incomplete credentials" exit 0 fi # Store each credential (never echo them). Track failures. # NOTE: Credentials passed as CLI args to openshell are briefly visible in # /proc/*/cmdline and ps output. This is a platform limitation of openshell # which does not yet support --from-stdin for provider values. store_fail=0 if openshell provider set qbo-client-id "$local_client_id" 2>&1; then log_info " qbo-client-id stored." else log_warn " qbo-client-id store failed — check openshell provider store." store_fail=1 fi if openshell provider set qbo-client-secret "$local_client_secret" 2>&1; then log_info " qbo-client-secret stored." else log_warn " qbo-client-secret store failed — check openshell provider store." store_fail=1 fi if openshell provider set qbo-access-token "$local_access_token" 2>&1; then log_info " qbo-access-token stored." else log_warn " qbo-access-token store failed — check openshell provider store." store_fail=1 fi if [[ -n "$local_realm_id" ]]; then if openshell provider set qbo-realm-id "$local_realm_id" 2>&1; then log_info " qbo-realm-id stored." else log_warn " qbo-realm-id store failed — check openshell provider store." store_fail=1 fi fi if [[ $store_fail -eq 1 ]]; then log_warn "Some credentials failed to store. Verify with: openshell provider list" else log_info "QBO credentials stored in provider store." fi else log_warn "openshell not available. Cannot store credentials in provider store." log_warn "Store manually: openshell provider set qbo-* " fi # Check if MCP container already exists if docker ps -a --format '{{.Names}}' | grep -q "^${QBO_MCP_CONTAINER}$"; then log_info "QBO MCP container '$QBO_MCP_CONTAINER' already exists." log_info "To recreate: docker rm -f $QBO_MCP_CONTAINER" else log_info "QBO MCP container not yet created." log_info "When ready, start with:" log_info " docker run -d --name $QBO_MCP_CONTAINER \\" log_info " --network $QBO_MCP_NETWORK \\" log_info " -e QB_CLIENT_ID= \\" log_info " -e QB_CLIENT_SECRET= \\" log_info " -e QB_ACCESS_TOKEN= \\" log_info " -e QB_REALM_ID= \\" log_info " $QBO_MCP_IMAGE" log_info "" log_info "Or add to deploy/compose/docker-compose.yml for managed lifecycle." fi # Register MCP server in Hermes config log_info "Registering QBO MCP server in Hermes config…" if nemohermes "$SANDBOX_NAME" config set \ mcp_servers.quickbooks.type "local" 2>&1; then log_info "QBO MCP type registered." else log_warn "MCP config registration may need manual setup." log_warn "Manual: nemohermes $SANDBOX_NAME config set mcp_servers.quickbooks" fi # Apply tool allowlist log_info "Applying QBO tool allowlist (read-only)…" log_info " Allowed: ${QBO_ALLOWED_TOOLS[*]}" log_info " Denied: ${QBO_DENIED_TOOLS[*]}" # Note: The actual tool filtering is done via mcp_servers config with # tools.include / tools.exclude. The exact nemohermes subcommand varies # by platform version. This is deferred until the platform CLI supports # per-server tool filtering in a stable form. log_info "[DEFERRED] Tool filters will be applied via nemohermes config set" log_info " when the platform CLI supports per-server tools.include/exclude." set_status "quickbooks" "connected" "QBO local MCP registered (read-only tools)" log_info "QuickBooks Online connect complete."