d6b74c42f6
Add dry-run-default connect dispatcher and per-integration scripts with gitignored local capability state, docs, and unit tests. Mutations require --apply and use nemohermes/openshell only.
286 lines
10 KiB
Bash
286 lines
10 KiB
Bash
#!/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 <<EOF
|
|
Usage: $(basename "$0") [OPTIONS]
|
|
|
|
Connect QuickBooks Online via local MCP.
|
|
|
|
QBO MCP:
|
|
Image: $QBO_MCP_IMAGE
|
|
Container: $QBO_MCP_CONTAINER
|
|
Network: $QBO_MCP_NETWORK (same Docker network as Hermes)
|
|
Type: Local MCP (stdio or network-attached)
|
|
|
|
Allowed tools (read-only):
|
|
reports, search/get invoices, bills, vendors, customers, company info
|
|
|
|
Denied tools:
|
|
create_payment, bill_payment, write/update/delete operations
|
|
|
|
Options:
|
|
--dry-run Preview only (default)
|
|
--apply Execute mutations (prompts for QBO credentials)
|
|
--help Show this help
|
|
|
|
Safety:
|
|
--dry-run is the default. Use --apply to perform mutations.
|
|
QBO credentials are stored via OpenShell provider store — never in .env.
|
|
Payment tools are explicitly denied in the MCP tool filter.
|
|
Write/update/delete operations are disabled for MVP.
|
|
|
|
Examples:
|
|
$(basename "$0") --dry-run
|
|
$(basename "$0") --apply
|
|
EOF
|
|
exit 0
|
|
;;
|
|
--dry-run)
|
|
DRY_RUN=1
|
|
APPLY=0
|
|
shift
|
|
;;
|
|
--apply)
|
|
DRY_RUN=0
|
|
APPLY=1
|
|
shift
|
|
;;
|
|
*)
|
|
log_error "Unknown argument: $1"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# ── Load .env ──────────────────────────────────────────────────────────────
|
|
load_env
|
|
|
|
SANDBOX_NAME="$(get_sandbox_name)"
|
|
|
|
log_section "QuickBooks Online Connect"
|
|
|
|
# ── Current state ──────────────────────────────────────────────────────────
|
|
current_status="$(get_status "quickbooks")"
|
|
if [[ -n "$current_status" ]]; then
|
|
log_info "Current status: $current_status"
|
|
fi
|
|
|
|
# ── Check prerequisites ────────────────────────────────────────────────────
|
|
require_cmd nemohermes "Install nemohermes CLI (part of NemoClaw platform)"
|
|
require_cmd docker "Docker required for local MCP container"
|
|
|
|
if ! nemohermes "$SANDBOX_NAME" status &>/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-* <VALUES>"
|
|
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-* <VALUES>"
|
|
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=<CLIENT_ID> \\"
|
|
log_info " -e QB_CLIENT_SECRET=<CLIENT_SECRET> \\"
|
|
log_info " -e QB_ACCESS_TOKEN=<ACCESS_TOKEN> \\"
|
|
log_info " -e QB_REALM_ID=<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."
|