Self-management MCP toolset + online SQLite backups - #25
Open
selic wants to merge 3 commits into
Open
Conversation
…pies Closes #3. The database holds everything that isn't reproducible from config — upstream specs, roles/grants/tool settings, users, DCR clients, refresh-token families, credential refs — and a lost file meant rebuilding it by hand. src/db/backup.ts snapshots via VACUUM INTO (a plain file copy of a live db can capture a torn page mid-WAL), keeps the newest BACKUP_KEEP locally, and can ship each snapshot off-instance to Azure Blob with DefaultAzureCredential (lazily imported, so deployments without it pay nothing). Upload failures are logged but never fail the snapshot: a local copy beats none. The scheduler is unref'd and stops on shutdown; :memory: databases opt out automatically. Admin API: GET /api/backups (config + what's on disk) and POST /api/backups (snapshot now). Restore stays manual and is documented in docs/backups.md, including the -wal/-shm cleanup step that otherwise bites. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Closes #2. Routine gateway administration — what's connected, which groups are enabled, turn a tier off, raise a tool's tier, close an upstream for a role, install a preset, snapshot the database — is now doable from any MCP client instead of a browser trip. Boundaries, deliberately narrow: the tools are listed only for admins and every call re-checks isAdmin (list filtering is UX, the call check is the boundary), with the same "not available" text an unknown tool gets so the set is no existence oracle. The `gw` namespace is now reserved, so a federated server can neither shadow these names nor be shadowed by them. gw_list_servers redacts header/env VALUES — keys plus ref-vs-env-vs-literal shape only, so a spec with a literal token can't be read back out over MCP. Credential writes, secret reads and user administration stay on the HTTP surface. gw_remove_server requires confirm: true. GATEWAY_SELF_TOOLS=off removes the whole surface. domain/tool-targets.ts extracts the "which tools does this selector touch" resolution now shared by the admin API, the /me prefs API and this toolset, so the three can't drift on what a tier or group selector means. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2. Closes #3. Not deployed — prod is in use; this is merge-ready and waits for a quiet window.
Backups (#3)
src/db/backup.ts: snapshots viaVACUUM INTO(a plain copy of a live database can capture a torn page mid-WAL), newestBACKUP_KEEPkept locally, optional Azure Blob shipping withDefaultAzureCredential(lazily imported — deployments without it pay nothing). Upload failures are logged but never fail the snapshot: a local copy beats none. Scheduler is unref'd and stops on shutdown;:memory:databases opt out.GET /api/backups(config + what's on disk) andPOST /api/backups(snapshot now). Restore stays manual, documented indocs/backups.md— including the-wal/-shmcleanup that otherwise bites.Defaults: daily, keep 7,
<DB dir>/backups. Knobs:BACKUP_INTERVAL_HOURS,BACKUP_KEEP,BACKUP_DIR,BACKUP_BLOB_CONTAINER_URL.Self-management toolset (#2)
src/mcp/self-tools.ts:gw_status,gw_list_servers,gw_list_tools(filters +groupsOnlysummary),gw_set_tools_enabled(by tier/group/tool/whole server),gw_set_tool_tier,gw_set_grant(by role name),gw_list_presets/gw_install_preset(withdryRun),gw_set_server_enabled,gw_remove_server(confirm: true),gw_refresh_catalog,gw_backup_now.Security posture:
isAdminand returns the same "not available" wording an unknown tool gets, so the toolset is no existence oracle.gwnamespace reserved inparseUpstreamSpec, so a federated server can neither shadow these names nor be shadowed.gw_list_serversshows header/env keys and whether each value is akv:/bao:ref, a${VAR}, or a literal — never the literal. (Worth noting the HTTP/api/upstreamsstill returns literals; that asymmetry is deliberate for the browser session but is worth a follow-up.)GATEWAY_SELF_TOOLS=offremoves the surface entirely.domain/tool-targets.tsextracts the selector→tools resolution now shared by the admin API,/meprefs, and this toolset, so the three can't drift on what "tier" or "group" means.Tests
256 passing (26 new): 9 backup tests (snapshot is a queryable database, retention, upload failure tolerance, fake-timer schedule) and 15 toolset tests (admin gate on list and call, redaction asserts the literal is absent, group summary, every mutating tool including the confirm guard and preset dry-run), plus MCP-session integration proving a viewer sees no
gw_*and is refused when calling anyway.One dependency added:
@azure/storage-blob(lazy-imported, only for off-instance backups).🤖 Generated with Claude Code