docs(skill): fix sqlite3/json invocations in gmail/gcal mount steps

Three issues with the DB-edit steps that ship in #2489:

- `'$[#]'` was double-quoted in the surrounding bash string, so bash
  arith-expanded `$#` (positional-arg count, 0 in interactive shell)
  before sqlite ever saw it — silently overwrote index 0 instead of
  appending. Now escaped as `'\$[#]'`.

- `sqlite3` CLI replaced with `pnpm exec tsx scripts/q.ts` — clean
  installs have no sqlite3 binary; setup/verify.ts:5 codifies that
  NanoClaw avoids depending on it.

- `strftime('%s','now')` replaced with `datetime('now')` — the column
  stores ISO strings everywhere else; mixing epoch ints made any
  consumer doing `datetime(updated_at)` parse those rows as 1970.

Also: reworded the "approval-gated" wording to distinguish container
vs host-operator-shell invocation, and added the "Why this can't be
container.json" note to add-gcal-tool (gmail had it, gcal didn't).
This commit is contained in:
glifocat
2026-05-15 17:03:54 +02:00
parent 26eb89c771
commit 529d2db8e2
2 changed files with 18 additions and 16 deletions
+10 -8
View File
@@ -145,28 +145,30 @@ ncl groups config add-mcp-server \
--env '{"GOOGLE_OAUTH_CREDENTIALS":"/workspace/extra/.calendar-mcp/gcp-oauth.keys.json","GOOGLE_CALENDAR_MCP_TOKEN_PATH":"/workspace/extra/.calendar-mcp/credentials.json"}'
```
This is an approval-gated write — an admin must approve before it lands.
Approval behaviour depends on where you run it: from inside an agent's container `ncl` write verbs are approval-gated (admin approves before it lands); from a host operator shell with full scope, it executes immediately. Either way, the response tells you which path it took.
### Add the `.calendar-mcp` mount
There is no `ncl groups config add-mount` verb yet (tracked in [#2395](https://github.com/nanocoai/nanoclaw/issues/2395)). Until that ships, edit the DB directly:
There is no `ncl groups config add-mount` verb yet (tracked in [#2395](https://github.com/nanocoai/nanoclaw/issues/2395)). Until that ships, edit the DB directly via the in-tree wrapper (`scripts/q.ts``setup/verify.ts:5` codifies that NanoClaw avoids depending on the `sqlite3` CLI binary, so don't shell out to it):
```bash
GROUP_ID='<group-id>'
HOST_PATH="$HOME/.calendar-mcp"
MOUNT=$(jq -cn --arg h "$HOST_PATH" '{hostPath:$h, containerPath:".calendar-mcp", readonly:false}')
sqlite3 data/v2.db "UPDATE container_configs \
SET additional_mounts = json_insert(additional_mounts, '$[#]', json('$MOUNT')), \
updated_at = strftime('%s','now') \
pnpm exec tsx scripts/q.ts data/v2.db "UPDATE container_configs \
SET additional_mounts = json_insert(additional_mounts, '\$[#]', json('$MOUNT')), \
updated_at = datetime('now') \
WHERE agent_group_id = '$GROUP_ID';"
```
Run from your NanoClaw project root (where `data/v2.db` lives).
Run from your NanoClaw project root (where `data/v2.db` lives). The `$[#]` placeholder is SQLite JSON1's append-to-end notation; it's `\$`-escaped so bash doesn't arithmetic-expand it before sqlite sees it. `updated_at` is ISO-string everywhere else in the schema, so use `datetime('now')` — not `strftime('%s','now')`, which would silently mix epoch ints into a column of YYYY-MM-DD HH:MM:SS strings.
**Switch to `ncl groups config add-mount` once #2395 lands.** Update this skill at that time.
`containerPath` is relative (mount-security rejects absolute paths — additional mounts land at `/workspace/extra/<relative>`).
**Why this can't be `groups/<folder>/container.json`:** post-migration `014-container-configs`, `materializeContainerJson` in `src/container-config.ts` rewrites that file from the DB on every spawn. Anything hand-edited there is silently overwritten on next restart.
**Same-group-as-gmail tip:** if this group already has the gmail MCP + `.gmail-mcp` mount, both coexist — `ncl groups config add-mcp-server` only updates the named entry, and `json_insert` appends to `additional_mounts` without disturbing existing entries.
## Phase 4: Build and Restart
@@ -211,10 +213,10 @@ Common signals:
```
2. Remove the `.calendar-mcp` mount from the DB (no `remove-mount` verb yet — same #2395 dependency):
```bash
sqlite3 data/v2.db "UPDATE container_configs \
pnpm exec tsx scripts/q.ts data/v2.db "UPDATE container_configs \
SET additional_mounts = (SELECT json_group_array(value) FROM json_each(additional_mounts) \
WHERE json_extract(value, '\$.containerPath') != '.calendar-mcp'), \
updated_at = strftime('%s','now') \
updated_at = datetime('now') \
WHERE agent_group_id = '<group-id>';"
```
3. Remove `CALENDAR_MCP_VERSION` ARG and the calendar package from the Dockerfile install block.
+8 -8
View File
@@ -164,23 +164,23 @@ ncl groups config add-mcp-server \
--env '{"GMAIL_OAUTH_PATH":"/workspace/extra/.gmail-mcp/gcp-oauth.keys.json","GMAIL_CREDENTIALS_PATH":"/workspace/extra/.gmail-mcp/credentials.json"}'
```
This is an approval-gated write — an admin must approve before it lands.
Approval behaviour depends on where you run it: from inside an agent's container `ncl` write verbs are approval-gated (admin approves before it lands); from a host operator shell with full scope, it executes immediately. Either way, the response tells you which path it took.
### Add the `.gmail-mcp` mount
There is no `ncl groups config add-mount` verb yet (tracked in [#2395](https://github.com/nanocoai/nanoclaw/issues/2395)). Until that ships, edit the DB directly:
There is no `ncl groups config add-mount` verb yet (tracked in [#2395](https://github.com/nanocoai/nanoclaw/issues/2395)). Until that ships, edit the DB directly via the in-tree wrapper (`scripts/q.ts``setup/verify.ts:5` codifies that NanoClaw avoids depending on the `sqlite3` CLI binary, so don't shell out to it):
```bash
GROUP_ID='<group-id>'
HOST_PATH="$HOME/.gmail-mcp"
MOUNT=$(jq -cn --arg h "$HOST_PATH" '{hostPath:$h, containerPath:".gmail-mcp", readonly:false}')
sqlite3 data/v2.db "UPDATE container_configs \
SET additional_mounts = json_insert(additional_mounts, '$[#]', json('$MOUNT')), \
updated_at = strftime('%s','now') \
pnpm exec tsx scripts/q.ts data/v2.db "UPDATE container_configs \
SET additional_mounts = json_insert(additional_mounts, '\$[#]', json('$MOUNT')), \
updated_at = datetime('now') \
WHERE agent_group_id = '$GROUP_ID';"
```
Run from your NanoClaw project root (where `data/v2.db` lives). The `$[#]` placeholder appends to the JSON array.
Run from your NanoClaw project root (where `data/v2.db` lives). The `$[#]` placeholder is SQLite JSON1's append-to-end notation; it's `\$`-escaped so bash doesn't arithmetic-expand it before sqlite sees it. `updated_at` is ISO-string everywhere else in the schema, so use `datetime('now')` — not `strftime('%s','now')`, which would silently mix epoch ints into a column of YYYY-MM-DD HH:MM:SS strings.
**Switch to `ncl groups config add-mount` once #2395 lands.** Update this skill at that time.
@@ -228,10 +228,10 @@ Common signals:
```
2. Remove the `.gmail-mcp` mount from the DB (no `remove-mount` verb yet — same #2395 dependency):
```bash
sqlite3 data/v2.db "UPDATE container_configs \
pnpm exec tsx scripts/q.ts data/v2.db "UPDATE container_configs \
SET additional_mounts = (SELECT json_group_array(value) FROM json_each(additional_mounts) \
WHERE json_extract(value, '\$.containerPath') != '.gmail-mcp'), \
updated_at = strftime('%s','now') \
updated_at = datetime('now') \
WHERE agent_group_id = '<group-id>';"
```
3. Remove the `GMAIL_MCP_VERSION` ARG and the `pnpm install -g @gongrzhe/server-gmail-autoauth-mcp` block from `container/Dockerfile`.