Problem
After working through a full SIPI bugfix session in a LOOM workspace (sipi-sentry-crash-fixing), several configuration gaps became apparent. The generated .claude/settings.json was missing settings that had to be added manually or via settings.local.json during the session. This friction could be eliminated by extending LOOM's config and code generation.
Findings
What LOOM already handles well
additionalDirectories — auto-generated from manifest repos
enabledPlugins + extraKnownMarketplaces — fully configured
permissions.allow — gh, git, cargo, WebFetch rules
sandbox — enabled, auto_allow, excluded commands, filesystem, network
presets — the rust preset works well for Rust projects
model — emitted correctly when set in config.toml (verified on origin/main)
Gap 1: No preset for C++/Nix projects
The sipi repo needed these permissions that don't exist in any preset:
[agents.claude-code.presets.cpp-nix]
allowed_tools = [
"Bash(nix develop:*)",
"Bash(make nix-*)",
"Bash(cmake *)",
"Bash(gcc *)",
"Bash(g++ *)",
"Bash(ls:*)",
]
Fix: Add a cpp-nix preset to config.toml. No code changes needed — just config.
Gap 2: Skill permissions not pre-configured
The eng plugin skills (eng:workflows:plan, eng:workflows:work, eng:plan-review, eng:plan-deepen) had to be manually allowed via settings.local.json. These are used in virtually every workspace.
Fix: Add to global allowed_tools in config.toml:
"Skill(eng:workflows:plan)",
"Skill(eng:workflows:work)",
"Skill(eng:plan-review)",
"Skill(eng:plan-deepen)",
Gap 3: MCP tool permissions not pre-configured
Sentry and Linear MCP servers were connected mid-session. Their tool permissions had to be manually approved each time. If these MCP servers are always connected at the user level, the tool permissions should be pre-allowed.
Fix: Add to global allowed_tools:
# Sentry
"mcp__sentry__find_organizations",
"mcp__sentry__find_projects",
"mcp__sentry__search_issues",
"mcp__sentry__get_issue_details",
"mcp__sentry__search_events",
"mcp__sentry__get_issue_tag_values",
"mcp__sentry__analyze_issue_with_seer",
# Linear
"mcp__linear-server__list_teams",
"mcp__linear-server__save_issue",
"mcp__linear-server__list_issues",
"mcp__linear-server__get_issue",
Blocker: These are bare tool names without parentheses. The current validate_permission_entry() in config/mod.rs requires the ToolName(specifier) format and would reject them. The validator needs to be extended first (see Gap 4).
Gap 4: Permission validator rejects bare MCP tool names
validate_permission_entry() requires entries to match ToolName(specifier) format:
if !trimmed.ends_with(')') || !trimmed.contains('(') {
anyhow::bail!("{context}: invalid format '{trimmed}' — expected ToolName(specifier)");
}
Bare MCP tool names like mcp__sentry__find_organizations don't have parentheses and would fail validation. Claude Code itself accepts them — the validator is too strict.
Fix: Allow entries that start with mcp__ to pass without requiring () specifiers. Something like:
// Bare MCP tool names are valid (e.g., "mcp__sentry__find_organizations")
if trimmed.starts_with("mcp__") && !trimmed.contains('(') {
return Ok(());
}
Similarly, Skill(name) entries should also be accepted — the validator currently allows them since they have parentheses, but worth confirming.
Gap 5: No MCP server configuration generation
LOOM doesn't generate .mcp.json for the workspace. MCP servers (Sentry, Linear, Context7) had to be connected manually each session.
Possible future feature: A [agents.claude-code.mcp_servers] section in config.toml that generates a workspace-level .mcp.json. This is lower priority since MCP servers configured at the user level (~/.claude/) are inherited by all projects.
Gap 6: settings.local.json pattern
LOOM only generates settings.json. The settings.local.json (gitignored, user-specific overrides) was created manually. This is probably fine as-is — the .local pattern exists for per-session overrides that shouldn't be committed. But LOOM could document this pattern in the generated CLAUDE.md.
Proposed Changes
Quick wins (config.toml only — no code changes)
- Add Skill permissions to global
allowed_tools
- Add
cpp-nix preset
Code changes required
- Extend permission validator to accept bare MCP tool names (Gap 4)
mcp__* names without () specifiers should pass validation
- Unblocks adding MCP tool permissions to
config.toml (Gap 3)
Future / nice-to-have
- MCP server config generation (Gap 5)
- Document
settings.local.json pattern in generated CLAUDE.md (Gap 6)
Context
This was discovered during the sipi-sentry-crash-fixing workspace where:
The session required ~8 manual permission approvals that could have been pre-configured.
Problem
After working through a full SIPI bugfix session in a LOOM workspace (
sipi-sentry-crash-fixing), several configuration gaps became apparent. The generated.claude/settings.jsonwas missing settings that had to be added manually or viasettings.local.jsonduring the session. This friction could be eliminated by extending LOOM's config and code generation.Findings
What LOOM already handles well
additionalDirectories— auto-generated from manifest reposenabledPlugins+extraKnownMarketplaces— fully configuredpermissions.allow— gh, git, cargo, WebFetch rulessandbox— enabled, auto_allow, excluded commands, filesystem, networkpresets— therustpreset works well for Rust projectsmodel— emitted correctly when set in config.toml (verified onorigin/main)Gap 1: No preset for C++/Nix projects
The
sipirepo needed these permissions that don't exist in any preset:Fix: Add a
cpp-nixpreset toconfig.toml. No code changes needed — just config.Gap 2: Skill permissions not pre-configured
The
engplugin skills (eng:workflows:plan,eng:workflows:work,eng:plan-review,eng:plan-deepen) had to be manually allowed viasettings.local.json. These are used in virtually every workspace.Fix: Add to global
allowed_toolsinconfig.toml:Gap 3: MCP tool permissions not pre-configured
Sentry and Linear MCP servers were connected mid-session. Their tool permissions had to be manually approved each time. If these MCP servers are always connected at the user level, the tool permissions should be pre-allowed.
Fix: Add to global
allowed_tools:Blocker: These are bare tool names without parentheses. The current
validate_permission_entry()inconfig/mod.rsrequires theToolName(specifier)format and would reject them. The validator needs to be extended first (see Gap 4).Gap 4: Permission validator rejects bare MCP tool names
validate_permission_entry()requires entries to matchToolName(specifier)format:Bare MCP tool names like
mcp__sentry__find_organizationsdon't have parentheses and would fail validation. Claude Code itself accepts them — the validator is too strict.Fix: Allow entries that start with
mcp__to pass without requiring()specifiers. Something like:Similarly,
Skill(name)entries should also be accepted — the validator currently allows them since they have parentheses, but worth confirming.Gap 5: No MCP server configuration generation
LOOM doesn't generate
.mcp.jsonfor the workspace. MCP servers (Sentry, Linear, Context7) had to be connected manually each session.Possible future feature: A
[agents.claude-code.mcp_servers]section inconfig.tomlthat generates a workspace-level.mcp.json. This is lower priority since MCP servers configured at the user level (~/.claude/) are inherited by all projects.Gap 6:
settings.local.jsonpatternLOOM only generates
settings.json. Thesettings.local.json(gitignored, user-specific overrides) was created manually. This is probably fine as-is — the.localpattern exists for per-session overrides that shouldn't be committed. But LOOM could document this pattern in the generatedCLAUDE.md.Proposed Changes
Quick wins (config.toml only — no code changes)
allowed_toolscpp-nixpresetCode changes required
mcp__*names without()specifiers should pass validationconfig.toml(Gap 3)Future / nice-to-have
settings.local.jsonpattern in generated CLAUDE.md (Gap 6)Context
This was discovered during the
sipi-sentry-crash-fixingworkspace where:sipi(C++23 image server),dasch-specs(specifications)rustpreset didn't apply to a C++ project)The session required ~8 manual permission approvals that could have been pre-configured.