Deploy a shared MCP-tool permissions allowlist; detect legacy .claude/settings.json

Two related fixes, discovered together while debugging Wareco's duplicated
"Project" scope MCP entries:

1. New custom/setup/machine/settings.json template + a merge-safe deploy
   step in sync-bcquality-knowledge.ps1 (section 10): auto-approves the
   read-only/already-protocol-gated tool calls across the three
   CURABIS-managed MCP servers (businesscentral's 15 static tools, al's
   11 dev-loop tools, microsoft-learn's 3 docs tools) via
   ~/.claude/settings.json's permissions.allow -- merged into whatever
   already exists, never overwritten, since that file also carries a
   developer's personal settings. Tested against a real 298-entry
   settings.json: preserved every existing key/array untouched, added
   only the 14 genuinely-missing entries.

   Found and fixed two real bugs while building this: -AsHashtable
   doesn't exist in Windows PowerShell 5.1 (this script also runs via
   `powershell`, not just `pwsh`) -- switched to PSCustomObject +
   Add-Member. And Set-Content -Encoding utf8 writes a BOM in PS5.1 with
   no utf8NoBOM option -- switched to [System.IO.File]::WriteAllText
   with an explicit no-BOM UTF8Encoding, since the original file had no
   BOM and a JSON parser choking on one would have silently broken every
   developer's settings.json.

2. Wareco's committed .claude/settings.json still has the pre-migration
   Dynamic Tool Mode tool names (bc_actions_search/describe) and an
   enabledMcpjsonServers entry for al/businesscentral -- the latter is
   why the MCP servers panel shows them duplicated under "Project" scope
   next to the correct "User" scope registration. Added detection +
   confirmed-removal migration step (mirroring the existing .mcp.json
   migration's multi-developer coordination caveat) and Roemer station
   16 to catch this on other pre-migration repos (gtt-marine likely has
   the same file).
This commit is contained in:
Michael Dieringer 2026-08-04 07:31:46 +02:00
parent c58c3f6e0f
commit a3b79eedc2
4 changed files with 141 additions and 1 deletions

View file

@ -1,7 +1,7 @@
--- ---
kind: action-skill kind: action-skill
id: curabis-standards-inspector id: curabis-standards-inspector
version: 7 version: 8
title: Rømer — Standards Inspector title: Rømer — Standards Inspector
description: > description: >
Owns the uniformity inspection across CURABIS repos: walks one full Owns the uniformity inspection across CURABIS repos: walks one full
@ -140,6 +140,15 @@ Walk ALL stations, every time. A partial round creates false confidence
mistake. This station has nothing to check against an org that has mistake. This station has nothing to check against an org that has
never run Mode C — a clean round with an empty registry is one line, never run Mode C — a clean round with an empty registry is one line,
same as any other station. same as any other station.
16. **Legacy repo-committed `.claude/settings.json`** (2026-08-03). Grep
`.claude/settings.json` for the old Dynamic Tool Mode tool names
(`bc_actions_search`, `bc_actions_describe`, `bc_actions_invoke`) or an
`enabledMcpjsonServers` entry for `al`/`businesscentral` — same
multi-developer coordination caveat as the `.mcp.json` migration
(station covers detection only; removal needs explicit developer
confirmation via curabis-standard.agent.md's Mode B step 5). Found live
in the `Wareco` repo: causes MCP servers to show duplicated under
"Project" scope in the panel alongside the correct "User" scope entry.
CURABIS-ROEMER-001 Measure against the written standard only. Every finding CURABIS-ROEMER-001 Measure against the written standard only. Every finding
cites the standard it deviates from — a rule file, the template table, or a cites the standard it deviates from — a rule file, the template table, or a

View file

@ -73,6 +73,7 @@ old HTTP-encoding pitfalls do not exist here).
|---|---| |---|---|
| bc-mcp-bridge.js | `{BASE}/bc-mcp-bridge.js` | | bc-mcp-bridge.js | `{BASE}/bc-mcp-bridge.js` |
| bc-mcp.config.template.json | `{BASE}/machine/bc-mcp.config.template.json` | | bc-mcp.config.template.json | `{BASE}/machine/bc-mcp.config.template.json` |
| settings.json (permissions template) | `{BASE}/machine/settings.json` (merged into `~/.claude/settings.json`, never overwritten wholesale — see Step 3c) |
| bcquality.agent.md | `{BASE}/templates/bcquality.agent.md` | | bcquality.agent.md | `{BASE}/templates/bcquality.agent.md` |
| immanuel.agent.md | `{AGENTS_BASE}/immanuel.agent.md` | | immanuel.agent.md | `{AGENTS_BASE}/immanuel.agent.md` |
| carlin.agent.md | `{AGENTS_BASE}/carlin.agent.md` | | carlin.agent.md | `{AGENTS_BASE}/carlin.agent.md` |
@ -720,6 +721,41 @@ independently of the `.mcp.json` migration above, since removing the *file*
doesn't affect any `.mcp.json` entry that still references the old doesn't affect any `.mcp.json` entry that still references the old
repo-relative walk-up form until that entry itself is migrated per step 3. repo-relative walk-up form until that entry itself is migrated per step 3.
**5. `.claude/settings.json` — legacy repo-committed permissions block
(2026-08-03, same multi-developer coordination caveat as step 3)**
A repo from before the BC MCP static-tool-mode migration (2026-08-03) may
have a git-committed `.claude/settings.json` with a `permissions.allow`
block naming the OLD Dynamic Tool Mode tool names
(`mcp__businesscentral__bc_actions_search` / `bc_actions_describe` /
`bc_actions_invoke`) and/or an `enabledMcpjsonServers` entry for `al` /
`businesscentral`. Found live in the `Wareco` repo during a full developer
onboarding: the stale `enabledMcpjsonServers` entry causes the MCP servers
panel to show `al`/`businesscentral` duplicated under "Project" scope
alongside the correct "User" scope registration — confusing, and every
other CURABIS repo from before the migration likely has the same file.
If `.claude/settings.json` contains `bc_actions_search`, `bc_actions_describe`,
`bc_actions_invoke`, or an `enabledMcpjsonServers` entry for `al`/`businesscentral`:
```
⚠️ .claude/settings.json indeholder en forældet tilladelsesliste fra før
static-tool-mode-migreringen (bc_actions_search/describe/invoke er de gamle
værktøjsnavne) og/eller enabledMcpjsonServers for al/businesscentral, som
duplikerer den korrekte user-scope-registrering under "Project" i MCP-panelet.
Skal jeg fjerne den forældede permissions-blok og enabledMcpjsonServers-linjen
fra .claude/settings.json? (ja/nej) De aktuelle, korrekte værktøjsnavne bliver
i stedet dækket af ~/.claude/settings.json (maskin-globalt, Step 3c).
```
Only remove the stale entries with explicit confirmation — same reasoning
as `.mcp.json`: this file is git-committed and shared, and a developer who
hasn't migrated their own machine's `~/.claude/settings.json` yet (Step 3c)
would lose their auto-approvals if this is pulled before they have. If the
file becomes empty afterward, propose deleting it entirely in the same
confirmation.
### HEARTBEAT.md token substitution (Mode B) ### HEARTBEAT.md token substitution (Mode B)
When creating HEARTBEAT.md from template in Mode B: When creating HEARTBEAT.md from template in Mode B:

View file

@ -0,0 +1,36 @@
{
"permissions": {
"allow": [
"mcp__businesscentral__List_ActiveTasks_PAG6102900",
"mcp__businesscentral__List_Consultants_PAG50009",
"mcp__businesscentral__List_NewTasks_PAG6102905",
"mcp__businesscentral__List_ProjectAIScores_PAG6102906",
"mcp__businesscentral__List_ProjectRepositories_PAG6102904",
"mcp__businesscentral__List_ProjectWeberScores_PAG6102908",
"mcp__businesscentral__List_Projects_PAG6102901",
"mcp__businesscentral__List_TaskComments_PAG6102902",
"mcp__businesscentral__Create_NewTask_PAG6102905",
"mcp__businesscentral__Create_ProjectAIScore_PAG6102906",
"mcp__businesscentral__Create_ProjectWeberScore_PAG6102908",
"mcp__businesscentral__Create_TaskComment_PAG6102902",
"mcp__businesscentral__Modify_ActiveTask_PAG6102900",
"mcp__businesscentral__Modify_ProjectRepository_PAG6102904",
"mcp__businesscentral__Modify_TaskComment_PAG6102902",
"mcp__al__al_addproject",
"mcp__al__al_auth_login",
"mcp__al__al_auth_logout",
"mcp__al__al_build",
"mcp__al__al_compile",
"mcp__al__al_downloadsymbols",
"mcp__al__al_getdiagnostics",
"mcp__al__al_getpackagedependencies",
"mcp__al__al_publish",
"mcp__al__al_run_tests",
"mcp__al__al_symbolsearch",
"mcp__al__al_symbolrelations",
"mcp__microsoft-learn__microsoft_docs_search",
"mcp__microsoft-learn__microsoft_code_sample_search",
"mcp__microsoft-learn__microsoft_docs_fetch"
]
}
}

View file

@ -222,3 +222,62 @@ function Ensure-UserMcpHttpServer {
} }
Ensure-UserMcpHttpServer -Name 'microsoft-learn' -Url 'https://learn.microsoft.com/api/mcp' Ensure-UserMcpHttpServer -Name 'microsoft-learn' -Url 'https://learn.microsoft.com/api/mcp'
# --- 10. Permissions allowlist -> ~/.claude/settings.json (MERGE, never overwrite) ---
# Unlike the other artifacts above, settings.json is not pure BCQuality content -
# it also carries a developer's personal settings (theme, model, hooks, etc).
# Only merge the permissions.allow entries from the template below into whatever
# already exists; never replace the file wholesale. Scope: the three CURABIS-
# managed MCP servers only (businesscentral, al, microsoft-learn) - all either
# read-only or already gated by Smiley's own protocol checks (red/green
# confirmation, independent review), so the tool-permission prompt is redundant
# friction here, not a real safety boundary. 2026-08-03: added after a developer
# had to click through the same MCP approval prompts repeatedly across sessions.
$settingsTemplate = Join-Path $clone 'custom\setup\machine\settings.json'
$settingsDest = Join-Path $env:USERPROFILE '.claude\settings.json'
if (Test-Path $settingsTemplate) {
# NB: -AsHashtable (ConvertFrom-Json) findes kun i PowerShell 6+. Dette script
# koeres ogsaa via `powershell` (Windows PowerShell 5.1) paa udviklermaskiner,
# saa vi bruger PSCustomObject + Add-Member i stedet - virker paa begge.
$templateAllow = (Get-Content $settingsTemplate -Raw | ConvertFrom-Json).permissions.allow
$settings = $null
if (Test-Path $settingsDest) {
$raw = Get-Content $settingsDest -Raw
if ($raw -and $raw.Trim()) {
$settings = $raw | ConvertFrom-Json
}
}
if (-not $settings) { $settings = [PSCustomObject]@{} }
if (-not (Get-Member -InputObject $settings -Name 'permissions' -MemberType NoteProperty)) {
$settings | Add-Member -MemberType NoteProperty -Name 'permissions' -Value ([PSCustomObject]@{})
}
if (-not (Get-Member -InputObject $settings.permissions -Name 'allow' -MemberType NoteProperty)) {
$settings.permissions | Add-Member -MemberType NoteProperty -Name 'allow' -Value @()
}
$existingAllow = [System.Collections.Generic.List[string]]::new()
foreach ($r in @($settings.permissions.allow)) { $existingAllow.Add([string]$r) }
$added = 0
foreach ($rule in $templateAllow) {
if ($existingAllow -notcontains $rule) {
$existingAllow.Add($rule)
$added++
}
}
$settings.permissions.allow = $existingAllow.ToArray()
# NB: Set-Content -Encoding utf8 skriver en BOM i Windows PowerShell 5.1 (ingen
# utf8NoBOM-mulighed der) - fundet under test 2026-08-03: settings.json havde
# ingen BOM originalt, og en tilfoejet BOM kan knaekke JSON-parsere der laeser
# filen. [System.IO.File]::WriteAllText med en explicit no-BOM UTF8Encoding
# virker identisk paa PS5.1 og PS7.
$json = $settings | ConvertTo-Json -Depth 20
[System.IO.File]::WriteAllText($settingsDest, $json, [System.Text.UTF8Encoding]::new($false))
Write-Host "Permissions-allowlist merged ind i $settingsDest - $added ny(e) regel(er) tilfoejet."
} else {
Write-Warning "settings.json-skabelon ikke fundet i klonen, springer over."
}