@sebastienrousseau/dotfiles 0.2.519 → 0.2.521
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +200 -0
- package/LICENSE-APACHE +190 -0
- package/{LICENSE → LICENSE-MIT} +1 -1
- package/README.md +1172 -166
- package/install.sh +77 -11
- package/package.json +8 -8
- package/tools/README.md +49 -0
- package/tools/ci/install-chezmoi-verified.sh +68 -0
- package/docs/.vitepress/reports/localization-readability-audit.md +0 -73
- package/docs/AI.md +0 -179
- package/docs/CNAME +0 -1
- package/docs/CONFIG_STRATEGY.md +0 -124
- package/docs/COPYRIGHT +0 -7
- package/docs/GOVERNANCE.md +0 -98
- package/docs/MAINTAINERS.md +0 -41
- package/docs/NAMING_CONVENTIONS.md +0 -102
- package/docs/OPENCODE.md +0 -127
- package/docs/README.md +0 -84
- package/docs/STRUCTURE.md +0 -102
- package/docs/adr/ADR-001-ci-cd-pipeline.md +0 -118
- package/docs/adr/ADR-002-shell-performance.md +0 -130
- package/docs/adr/ADR-003-security-first.md +0 -158
- package/docs/adr/ADR-004-cli-architecture.md +0 -171
- package/docs/adr/ADR-005-chezmoi-choice.md +0 -99
- package/docs/adr/ADR-006-shell-selection.md +0 -124
- package/docs/adr/ADR-007-multi-shell-parity.md +0 -62
- package/docs/adr/ADR-008-alias-system-architecture.md +0 -95
- package/docs/adr/ADR-009-wallpaper-driven-theming.md +0 -131
- package/docs/adr/ADR-010-starship-transient-prompt.md +0 -144
- package/docs/adr/ADR-011-nushell-tier3-keep.md +0 -144
- package/docs/adr/ADR-012-ai-fleet-local-proxy.md +0 -79
- package/docs/adr/README.md +0 -40
- package/docs/architecture/AI_COST_OPTIMIZATION.md +0 -144
- package/docs/architecture/ARCHITECTURE.md +0 -117
- package/docs/architecture/INTEROP.md +0 -44
- package/docs/architecture/REPO_LAYOUT.md +0 -241
- package/docs/architecture/WALKTHROUGH.md +0 -86
- package/docs/architecture/fleet-deployment.md +0 -77
- package/docs/archive/EUXIS_2026_REVIEW.md +0 -127
- package/docs/archive/LEGACY_ROADMAP.md +0 -6
- package/docs/archive/MILESTONE_v0.2.493.md +0 -47
- package/docs/archive/PLAN.md +0 -199
- package/docs/archive/REPO_AUDIT.md +0 -31
- package/docs/articles/.pages +0 -6
- package/docs/articles/2026-07-05-custom-mkdocs-material-dark-theme.md +0 -216
- package/docs/articles/2026-07-05-fish-startup-abbr.md +0 -153
- package/docs/articles/2026-07-05-master-to-main-rename-runbook.md +0 -128
- package/docs/articles/index.md +0 -36
- package/docs/guides/INSTALL.md +0 -144
- package/docs/guides/NEOVIM_IDE_GUIDE.md +0 -61
- package/docs/guides/THEMING.md +0 -230
- package/docs/guides/TROUBLESHOOTING.md +0 -176
- package/docs/guides/WSL2_NIX_TROUBLESHOOTING.md +0 -792
- package/docs/index.md +0 -132
- package/docs/interop/A2A.md +0 -39
- package/docs/interop/POWERSHELL.md +0 -102
- package/docs/manual/00-introduction.md +0 -89
- package/docs/manual/01-concepts/01-architecture.md +0 -138
- package/docs/manual/01-concepts/02-trust-model.md +0 -183
- package/docs/manual/01-concepts/03-theme-engine.md +0 -186
- package/docs/manual/01-concepts/04-fleet.md +0 -148
- package/docs/manual/01-concepts/05-self-healing.md +0 -204
- package/docs/manual/02-tutorials/01-first-install.md +0 -197
- package/docs/manual/02-tutorials/02-add-wallpaper.md +0 -216
- package/docs/manual/02-tutorials/03-create-profile.md +0 -244
- package/docs/manual/02-tutorials/04-encrypt-secret.md +0 -281
- package/docs/manual/02-tutorials/05-deploy-fleet.md +0 -283
- package/docs/manual/03-reference/01-dot-cli.md +0 -450
- package/docs/manual/03-reference/02-config-files.md +0 -265
- package/docs/manual/03-reference/03-environment.md +0 -124
- package/docs/manual/03-reference/04-templates.md +0 -190
- package/docs/manual/03-reference/05-feature-flags.md +0 -187
- package/docs/manual/04-cookbook/01-recipes.md +0 -285
- package/docs/manual/04-cookbook/02-troubleshooting.md +0 -351
- package/docs/manual/04-cookbook/03-faq.md +0 -175
- package/docs/manual/05-appendices/A-platform-matrix.md +0 -101
- package/docs/manual/05-appendices/B-security-checklist.md +0 -85
- package/docs/manual/05-appendices/C-glossary.md +0 -40
- package/docs/manual/05-appendices/D-bibliography.md +0 -58
- package/docs/manual/05-appendices/E-license.md +0 -38
- package/docs/manual/_toc.yml +0 -58
- package/docs/manual/command-index.md +0 -155
- package/docs/manual/concept-index.md +0 -168
- package/docs/manual/index.md +0 -66
- package/docs/operations/ARCHITECTURE_ROADMAP.md +0 -7
- package/docs/operations/ATTESTATION.md +0 -44
- package/docs/operations/CI_CADENCE.md +0 -107
- package/docs/operations/CI_COMPOSITES.md +0 -156
- package/docs/operations/COMPLETIONS.md +0 -123
- package/docs/operations/COVERAGE.md +0 -204
- package/docs/operations/DRIFT.md +0 -107
- package/docs/operations/HARD_AUDIT_2026.md +0 -631
- package/docs/operations/MAINTENANCE.md +0 -63
- package/docs/operations/MANIFEST.md +0 -127
- package/docs/operations/MIGRATION.md +0 -109
- package/docs/operations/OPERATIONS.md +0 -188
- package/docs/operations/PERFORMANCE.md +0 -133
- package/docs/operations/REGISTRY.md +0 -90
- package/docs/operations/RELEASE_PIPELINE.md +0 -128
- package/docs/operations/RELIABILITY.md +0 -122
- package/docs/operations/RFC_v0_2_503_reorganization.md +0 -280
- package/docs/operations/ROADMAP.md +0 -10
- package/docs/operations/ROADMAP_2026.md +0 -7
- package/docs/operations/ROADMAP_V0_2_503.md +0 -10
- package/docs/operations/TESTING.md +0 -216
- package/docs/operations/TRACEABILITY.md +0 -43
- package/docs/operations/TRUSTED_AGENT_WORKSTATION.md +0 -65
- package/docs/operations/VERSION_SYNC.md +0 -393
- package/docs/reference/ALIASES.md +0 -131
- package/docs/reference/ALIASES_CHEATSHEET.md +0 -32
- package/docs/reference/ALIASES_DEPRECATIONS.md +0 -13
- package/docs/reference/FEATURES.md +0 -66
- package/docs/reference/FONTS.md +0 -112
- package/docs/reference/POWERSHELL_PARITY.md +0 -82
- package/docs/reference/PROFILES.md +0 -69
- package/docs/reference/SCREENSHOTS.md +0 -121
- package/docs/reference/SCRIPTS.md +0 -71
- package/docs/reference/SUPPORT_MATRIX.md +0 -80
- package/docs/reference/THEMES.md +0 -117
- package/docs/reference/TOOLS.md +0 -110
- package/docs/reference/UTILS.md +0 -242
- package/docs/registry.json +0 -6
- package/docs/schema/dot-env-v1.json +0 -110
- package/docs/schema/dot-registry-v1.json +0 -33
- package/docs/security/AI_ACT_COMPLIANCE.md +0 -94
- package/docs/security/AUDIT_BYPASS.md +0 -103
- package/docs/security/AUTOMATION_SECRETS.md +0 -26
- package/docs/security/CI_EGRESS_ALLOWLIST.md +0 -127
- package/docs/security/CI_PINNING.md +0 -129
- package/docs/security/COMMIT_SIGNING.md +0 -138
- package/docs/security/COMPLIANCE.md +0 -458
- package/docs/security/DEPS_DEV_EXCEPTIONS.md +0 -86
- package/docs/security/DISCLOSURE.md +0 -130
- package/docs/security/ENCRYPTION.md +0 -57
- package/docs/security/FMEA.md +0 -159
- package/docs/security/FUZZING.md +0 -114
- package/docs/security/HISTORY_FILTERING.md +0 -132
- package/docs/security/INCIDENT_RESPONSE.md +0 -579
- package/docs/security/INSTALL_VERIFICATION.md +0 -122
- package/docs/security/KEYS.md +0 -49
- package/docs/security/KEY_ROTATION.md +0 -303
- package/docs/security/MCP_POLICY.md +0 -78
- package/docs/security/POLICY_RELEASES.md +0 -37
- package/docs/security/README.md +0 -28
- package/docs/security/SCORECARD.md +0 -195
- package/docs/security/SECRETS.md +0 -158
- package/docs/security/SECURITY.md +0 -45
- package/docs/security/SECURITY_CHECKLIST.md +0 -55
- package/docs/security/SHELL_EXEMPTIONS.md +0 -145
- package/docs/security/SOUP_REGISTER.md +0 -36
- package/docs/security/THREAT_MODEL.md +0 -130
- package/docs/security/VERIFICATION_VALIDATION.md +0 -228
- package/docs/security/VERIFY_RELEASE.md +0 -201
- package/docs/security/security-pubkey.asc +0 -15
- package/docs/stylesheets/extra.css +0 -444
- package/docs/themes/README.md +0 -10
- package/docs/themes/VISUAL_INTEGRITY_REPORT.md +0 -30
- package/docs/themes/hero-shot.svg +0 -78
- package/scripts/README.md +0 -123
- package/scripts/ci/check-copyright-headers.sh +0 -8
- package/scripts/ci/check-shell-preamble.sh +0 -8
- package/scripts/ci/guard-gitleaks-checkout.sh +0 -8
- package/scripts/demo/record.sh +0 -43
- package/scripts/diagnostics/a2a-conformance.sh +0 -163
- package/scripts/diagnostics/alias-governance.sh +0 -138
- package/scripts/diagnostics/aliases-cheatsheet.sh +0 -74
- package/scripts/diagnostics/aliases-manifest.sh +0 -77
- package/scripts/diagnostics/benchmark.sh +0 -408
- package/scripts/diagnostics/conflicts.sh +0 -73
- package/scripts/diagnostics/doctor-unified.sh +0 -39
- package/scripts/diagnostics/doctor.sh +0 -751
- package/scripts/diagnostics/drift-dashboard.sh +0 -202
- package/scripts/diagnostics/health.sh +0 -623
- package/scripts/diagnostics/history-analysis.sh +0 -86
- package/scripts/diagnostics/mcp-doctor.sh +0 -582
- package/scripts/diagnostics/perf.sh +0 -453
- package/scripts/diagnostics/scorecard.sh +0 -119
- package/scripts/diagnostics/secret-governance.sh +0 -65
- package/scripts/diagnostics/security-score.sh +0 -467
- package/scripts/diagnostics/smoke-test.sh +0 -88
- package/scripts/diagnostics/snapshot.sh +0 -90
- package/scripts/diagnostics/verify.sh +0 -108
- package/scripts/diagnostics/verify_state.sh +0 -73
- package/scripts/diagnostics/version-locks.sh +0 -94
- package/scripts/diagnostics/workstation-attestation.sh +0 -187
- package/scripts/dot/commands/agent.sh +0 -485
- package/scripts/dot/commands/agents.sh +0 -336
- package/scripts/dot/commands/ai.sh +0 -587
- package/scripts/dot/commands/aliases.sh +0 -277
- package/scripts/dot/commands/appearance.sh +0 -110
- package/scripts/dot/commands/completion.sh +0 -134
- package/scripts/dot/commands/core.sh +0 -217
- package/scripts/dot/commands/diagnostics.sh +0 -265
- package/scripts/dot/commands/env-emit.sh +0 -203
- package/scripts/dot/commands/fleet.sh +0 -688
- package/scripts/dot/commands/init.sh +0 -185
- package/scripts/dot/commands/lint.sh +0 -208
- package/scripts/dot/commands/manual.sh +0 -169
- package/scripts/dot/commands/meta.sh +0 -333
- package/scripts/dot/commands/patterns.sh +0 -55
- package/scripts/dot/commands/registry.sh +0 -419
- package/scripts/dot/commands/restore.sh +0 -232
- package/scripts/dot/commands/secrets.sh +0 -296
- package/scripts/dot/commands/security.sh +0 -102
- package/scripts/dot/commands/tools.sh +0 -556
- package/scripts/dot/data/alias-deprecations.tsv +0 -2
- package/scripts/dot/powershell/Dot.psm1 +0 -319
- package/scripts/fonts/install-nerd-fonts.sh +0 -75
- package/scripts/fonts/patch-fonts.sh +0 -36
- package/scripts/git-hooks/install.sh +0 -12
- package/scripts/git-hooks/pre-commit +0 -12
- package/scripts/git-hooks/pre-commit-audit.sh +0 -146
- package/scripts/git-hooks/pre-push +0 -105
- package/scripts/git-hooks/prepare-commit-msg +0 -29
- package/scripts/lib/secrets_provider.sh +0 -185
- package/scripts/ops/ai-setup.sh +0 -71
- package/scripts/ops/bundle.sh +0 -104
- package/scripts/ops/chaos.sh +0 -50
- package/scripts/ops/chezmoi-apply.sh +0 -333
- package/scripts/ops/chezmoi-diff.sh +0 -16
- package/scripts/ops/chezmoi-remove.sh +0 -46
- package/scripts/ops/chezmoi-update.sh +0 -63
- package/scripts/ops/heal-chezmoi.sh +0 -87
- package/scripts/ops/heal-system.sh +0 -129
- package/scripts/ops/heal-tools.sh +0 -297
- package/scripts/ops/heal.sh +0 -223
- package/scripts/ops/post-apply-repair.sh +0 -107
- package/scripts/ops/prewarm.sh +0 -128
- package/scripts/ops/release.sh +0 -262
- package/scripts/ops/rollback.sh +0 -604
- package/scripts/ops/setup.sh +0 -138
- package/scripts/ops/teleport.sh +0 -34
- package/scripts/qa/check-version-consistency.sh +0 -124
- package/scripts/qa/coverage-baseline.sh +0 -61
- package/scripts/qa/docs-coverage.sh +0 -112
- package/scripts/qa/examples-coverage.sh +0 -94
- package/scripts/qa/powershell-contract.ps1 +0 -95
- package/scripts/qa/reliability-audit.sh +0 -139
- package/scripts/qa/scorecard-snapshot.sh +0 -128
- package/scripts/qa/traceability-coverage.sh +0 -117
- package/scripts/qa/validate-examples.sh +0 -27
- package/scripts/qa/wsl-contract.sh +0 -12
- package/scripts/secrets/age-init.sh +0 -82
- package/scripts/secrets/create-secrets-file.sh +0 -46
- package/scripts/secrets/encrypt-ssh-key.sh +0 -44
- package/scripts/security/backup.sh +0 -58
- package/scripts/security/check-disclosure-key-expiry.sh +0 -111
- package/scripts/security/dns-doh.sh +0 -52
- package/scripts/security/encryption-check.sh +0 -55
- package/scripts/security/enforce-policies.sh +0 -335
- package/scripts/security/firewall.sh +0 -91
- package/scripts/security/lock-configs.sh +0 -67
- package/scripts/security/lock-screen.sh +0 -56
- package/scripts/security/manage-secrets.sh +0 -429
- package/scripts/security/ssh-cert.sh +0 -204
- package/scripts/security/telemetry-kill.sh +0 -51
- package/scripts/security/usb-safety.sh +0 -52
- package/scripts/theme/apply-gnome-theme.sh +0 -333
- package/scripts/theme/extract-heic-frames.sh +0 -115
- package/scripts/theme/extract-theme.py +0 -742
- package/scripts/theme/install-boot-logo.sh +0 -63
- package/scripts/theme/install-catppuccin-themes.sh +0 -371
- package/scripts/theme/install-cursors.sh +0 -26
- package/scripts/theme/install-file-icons.sh +0 -27
- package/scripts/theme/install-grub-theme.sh +0 -62
- package/scripts/theme/install-lock-icon.sh +0 -31
- package/scripts/theme/merge-wallpaper.sh +0 -146
- package/scripts/theme/rebuild-themes.sh +0 -544
- package/scripts/theme/switch.sh +0 -449
- package/scripts/theme/wallpaper-rotate.sh +0 -137
- package/scripts/theme/wallpaper-sync.sh +0 -690
- package/scripts/tools/cmatrix.sh +0 -22
- package/scripts/tools/detect-collisions.py +0 -103
- package/scripts/tools/emoji-picker.sh +0 -49
- package/scripts/tools/figlet-banner.sh +0 -19
- package/scripts/tools/log-rotate.sh +0 -31
- package/scripts/tools/lolcat-wrap.sh +0 -20
- package/scripts/tools/pipes.sh +0 -49
- package/scripts/tuning/linux.sh +0 -186
- package/scripts/tuning/macos.sh +0 -56
- package/scripts/uninstall.sh +0 -86
- package/scripts/version-sync.sh +0 -654
- package/templates/chezmoi-data/geekom-a9.toml.example +0 -21
- package/templates/chezmoi-data/mac-m1.toml.example +0 -16
- package/templates/chezmoi-data/mac-t2-linux.toml.example +0 -21
- package/templates/chezmoi-data/surface-pro-7p.toml.example +0 -21
- package/templates/projects/go/.github/workflows/ci.yml +0 -31
- package/templates/projects/go/README.md +0 -7
- package/templates/projects/go/cmd/__PROJECT_NAME__/main.go +0 -8
- package/templates/projects/go/go.mod +0 -3
- package/templates/projects/go/go.sum +0 -0
- package/templates/projects/molecule/README.md +0 -7
- package/templates/projects/molecule/converge.yml +0 -7
- package/templates/projects/molecule/molecule.yml +0 -16
- package/templates/projects/node/.github/workflows/ci.yml +0 -30
- package/templates/projects/node/README.md +0 -7
- package/templates/projects/node/package-lock.json +0 -12
- package/templates/projects/node/package.json +0 -10
- package/templates/projects/node/src/index.js +0 -3
- package/templates/projects/packer/README.md +0 -15
- package/templates/projects/packer/main.pkr.hcl +0 -15
- package/templates/projects/python/.github/workflows/ci.yml +0 -34
- package/templates/projects/python/README.md +0 -7
- package/templates/projects/python/pyproject.toml +0 -25
- package/templates/projects/python/src/__PROJECT_NAME__/__init__.py +0 -2
- package/templates/projects/python/tests/test_basic.py +0 -3
|
@@ -1,582 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# SPDX-License-Identifier: MIT
|
|
3
|
-
# Copyright (c) 2015-2026 Sebastien Rousseau
|
|
4
|
-
## MCP configuration diagnostics and hardening checks.
|
|
5
|
-
##
|
|
6
|
-
## Validates MCP (Model Context Protocol) server configurations for security
|
|
7
|
-
## policy compliance: launcher allowlist, filesystem scope, token requirements.
|
|
8
|
-
##
|
|
9
|
-
## # Usage
|
|
10
|
-
## dot mcp [--strict|-s] [--json|-j]
|
|
11
|
-
##
|
|
12
|
-
## # Options
|
|
13
|
-
## --strict, -s: Treat policy warnings as errors (for CI enforcement)
|
|
14
|
-
## --json, -j: Emit a machine-readable summary
|
|
15
|
-
##
|
|
16
|
-
## # Dependencies
|
|
17
|
-
## - jq: JSON parsing (optional but recommended)
|
|
18
|
-
##
|
|
19
|
-
## # Checks Performed
|
|
20
|
-
## | Check | Description |
|
|
21
|
-
## |-------|-------------|
|
|
22
|
-
## | Launcher policy | Only npx/node/uvx allowed |
|
|
23
|
-
## | Filesystem scope | No broad access (/, /home, /Users) |
|
|
24
|
-
## | Arg policy | No wildcards or --unsafe flags |
|
|
25
|
-
## | Token check | Required tokens set (GITHUB_TOKEN, BRAVE_API_KEY) |
|
|
26
|
-
## | Env placeholders | All ${VAR} references resolved |
|
|
27
|
-
##
|
|
28
|
-
## # Exit Codes
|
|
29
|
-
## - 0: All checks passed
|
|
30
|
-
## - 1: Errors found (or warnings in strict mode)
|
|
31
|
-
|
|
32
|
-
set -euo pipefail
|
|
33
|
-
|
|
34
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
35
|
-
# shellcheck source=../../lib/dot/ui.sh
|
|
36
|
-
# shellcheck disable=SC1091
|
|
37
|
-
source "$SCRIPT_DIR/../../lib/dot/ui.sh"
|
|
38
|
-
|
|
39
|
-
# Parse arguments
|
|
40
|
-
STRICT_MODE=0
|
|
41
|
-
JSON_MODE=0
|
|
42
|
-
while [[ $# -gt 0 ]]; do
|
|
43
|
-
case "$1" in
|
|
44
|
-
--strict | -s)
|
|
45
|
-
STRICT_MODE=1
|
|
46
|
-
shift
|
|
47
|
-
;;
|
|
48
|
-
--json | -j)
|
|
49
|
-
JSON_MODE=1
|
|
50
|
-
shift
|
|
51
|
-
;;
|
|
52
|
-
*) shift ;;
|
|
53
|
-
esac
|
|
54
|
-
done
|
|
55
|
-
|
|
56
|
-
Errors=0
|
|
57
|
-
Warnings=0
|
|
58
|
-
SUMMARY_STATUS="healthy"
|
|
59
|
-
CONFIG_OK=0
|
|
60
|
-
JSON_VALID=0
|
|
61
|
-
SERVER_COUNT=0
|
|
62
|
-
POLICY_WARN_ON_UNPINNED_NPX=0
|
|
63
|
-
REQUIRE_APPROVED_PACKAGE_LOCK=0
|
|
64
|
-
REQUIRE_REGISTRY_ENTRY=0
|
|
65
|
-
REQUIRE_HTTPS_FOR_HTTP=0
|
|
66
|
-
REQUIRE_OAUTH_FOR_HTTP=0
|
|
67
|
-
ALLOWED_LAUNCHERS='["npx","node","uvx"]'
|
|
68
|
-
TRUSTED_TRANSPORTS='["stdio"]'
|
|
69
|
-
BLOCKED_PATHS='["/","/home","/Users"]'
|
|
70
|
-
BLOCKED_ARG_PATTERNS='["^--allow-.*","^--unsafe$","^\\\\*$"]'
|
|
71
|
-
FORBIDDEN_DEFAULT_SERVERS='[]'
|
|
72
|
-
REQUIRED_ENV_RULES='{}'
|
|
73
|
-
APPROVED_PACKAGE_LOCK='{}'
|
|
74
|
-
APPROVED_REGISTRY='{}'
|
|
75
|
-
|
|
76
|
-
log_success() {
|
|
77
|
-
if [[ "$JSON_MODE" -ne 1 ]]; then
|
|
78
|
-
ui_ok "$1" "${2:-}"
|
|
79
|
-
fi
|
|
80
|
-
return 0
|
|
81
|
-
}
|
|
82
|
-
log_fail() {
|
|
83
|
-
if [[ "$JSON_MODE" -ne 1 ]]; then
|
|
84
|
-
ui_err "$1" "${2:-}"
|
|
85
|
-
fi
|
|
86
|
-
Errors=$((Errors + 1))
|
|
87
|
-
return 0
|
|
88
|
-
}
|
|
89
|
-
log_warn() {
|
|
90
|
-
if [[ "$JSON_MODE" -ne 1 ]]; then
|
|
91
|
-
ui_warn "$1" "${2:-}"
|
|
92
|
-
fi
|
|
93
|
-
Warnings=$((Warnings + 1))
|
|
94
|
-
# In strict mode, warnings become errors
|
|
95
|
-
[[ "$STRICT_MODE" -eq 1 ]] && Errors=$((Errors + 1))
|
|
96
|
-
return 0
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
MCP_CONFIG="${MCP_CONFIG:-$HOME/.config/claude/mcp_servers.json}"
|
|
100
|
-
if [[ ! -f "$MCP_CONFIG" ]]; then
|
|
101
|
-
for cand in "$HOME/.dotfiles/defaults/dot_config/claude/mcp_servers.json" \
|
|
102
|
-
"$HOME/.dotfiles/dot_config/claude/mcp_servers.json"; do
|
|
103
|
-
[[ -f "$cand" ]] && MCP_CONFIG="$cand" && break
|
|
104
|
-
done
|
|
105
|
-
fi
|
|
106
|
-
|
|
107
|
-
REPO_ROOT="${REPO_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
|
|
108
|
-
MCP_POLICY_CONFIG="${MCP_POLICY_CONFIG:-$REPO_ROOT/defaults/dot_config/dotfiles/mcp-policy.json}"
|
|
109
|
-
MCP_LOCK_CONFIG="${MCP_LOCK_CONFIG:-$REPO_ROOT/defaults/dot_config/dotfiles/mcp-lock.json}"
|
|
110
|
-
MCP_REGISTRY_CONFIG="${MCP_REGISTRY_CONFIG:-$REPO_ROOT/defaults/dot_config/dotfiles/mcp-registry.json}"
|
|
111
|
-
|
|
112
|
-
if [[ "$JSON_MODE" -ne 1 ]]; then
|
|
113
|
-
ui_init
|
|
114
|
-
ui_dot_banner "AI and Agents"
|
|
115
|
-
ui_header "MCP Doctor"
|
|
116
|
-
echo ""
|
|
117
|
-
fi
|
|
118
|
-
|
|
119
|
-
if command -v jq >/dev/null 2>&1 && [[ -f "$MCP_POLICY_CONFIG" ]] && jq empty "$MCP_POLICY_CONFIG" >/dev/null 2>&1; then
|
|
120
|
-
ALLOWED_LAUNCHERS="$(jq -c '.profiles[.defaultProfile].allowedLaunchers // ["npx","node","uvx"]' "$MCP_POLICY_CONFIG")"
|
|
121
|
-
BLOCKED_PATHS="$(jq -c '.profiles[.defaultProfile].blockedFilesystemRoots // ["/","/home","/Users"]' "$MCP_POLICY_CONFIG")"
|
|
122
|
-
BLOCKED_ARG_PATTERNS="$(jq -c '.profiles[.defaultProfile].blockedArgPatterns // ["^--allow-.*","^--unsafe$","^\\\\*$"]' "$MCP_POLICY_CONFIG")"
|
|
123
|
-
FORBIDDEN_DEFAULT_SERVERS="$(jq -c '.profiles[.defaultProfile].forbidNetworkServersByDefault // []' "$MCP_POLICY_CONFIG")"
|
|
124
|
-
REQUIRED_ENV_RULES="$(jq -c '.profiles[.defaultProfile].requiredEnvByServer // {}' "$MCP_POLICY_CONFIG")"
|
|
125
|
-
TRUSTED_TRANSPORTS="$(jq -c '.profiles[.defaultProfile].trustedTransports // ["stdio"]' "$MCP_POLICY_CONFIG")"
|
|
126
|
-
if [[ "$(jq -r '.profiles[.defaultProfile].warnOnUnpinnedNpx // false' "$MCP_POLICY_CONFIG")" == "true" ]]; then
|
|
127
|
-
POLICY_WARN_ON_UNPINNED_NPX=1
|
|
128
|
-
fi
|
|
129
|
-
if [[ "$(jq -r '.profiles[.defaultProfile].requireApprovedPackageLock // false' "$MCP_POLICY_CONFIG")" == "true" ]]; then
|
|
130
|
-
REQUIRE_APPROVED_PACKAGE_LOCK=1
|
|
131
|
-
fi
|
|
132
|
-
if [[ "$(jq -r '.profiles[.defaultProfile].requireRegistryEntry // false' "$MCP_POLICY_CONFIG")" == "true" ]]; then
|
|
133
|
-
REQUIRE_REGISTRY_ENTRY=1
|
|
134
|
-
fi
|
|
135
|
-
if [[ "$(jq -r '.profiles[.defaultProfile].requireHttpsForHttpTransports // false' "$MCP_POLICY_CONFIG")" == "true" ]]; then
|
|
136
|
-
REQUIRE_HTTPS_FOR_HTTP=1
|
|
137
|
-
fi
|
|
138
|
-
if [[ "$(jq -r '.profiles[.defaultProfile].requireOauthForHttpTransports // false' "$MCP_POLICY_CONFIG")" == "true" ]]; then
|
|
139
|
-
REQUIRE_OAUTH_FOR_HTTP=1
|
|
140
|
-
fi
|
|
141
|
-
fi
|
|
142
|
-
|
|
143
|
-
if command -v jq >/dev/null 2>&1 && [[ -f "$MCP_LOCK_CONFIG" ]] && jq empty "$MCP_LOCK_CONFIG" >/dev/null 2>&1; then
|
|
144
|
-
APPROVED_PACKAGE_LOCK="$(jq -c '.packages // {}' "$MCP_LOCK_CONFIG")"
|
|
145
|
-
fi
|
|
146
|
-
|
|
147
|
-
if command -v jq >/dev/null 2>&1 && [[ -f "$MCP_REGISTRY_CONFIG" ]] && jq empty "$MCP_REGISTRY_CONFIG" >/dev/null 2>&1; then
|
|
148
|
-
APPROVED_REGISTRY="$(jq -c '.servers // {}' "$MCP_REGISTRY_CONFIG")"
|
|
149
|
-
fi
|
|
150
|
-
|
|
151
|
-
[[ "$JSON_MODE" -ne 1 ]] && ui_header "Config File"
|
|
152
|
-
if [[ -f "$MCP_CONFIG" ]]; then
|
|
153
|
-
CONFIG_OK=1
|
|
154
|
-
log_success "MCP config" "$MCP_CONFIG"
|
|
155
|
-
else
|
|
156
|
-
log_fail "MCP config" "not found (expected $MCP_CONFIG)"
|
|
157
|
-
fi
|
|
158
|
-
|
|
159
|
-
if [[ "$JSON_MODE" -ne 1 ]]; then
|
|
160
|
-
echo ""
|
|
161
|
-
ui_header "Policy"
|
|
162
|
-
fi
|
|
163
|
-
if [[ -f "$MCP_POLICY_CONFIG" ]]; then
|
|
164
|
-
log_success "Policy config" "$MCP_POLICY_CONFIG"
|
|
165
|
-
else
|
|
166
|
-
log_warn "Policy config" "not found (using built-in defaults)"
|
|
167
|
-
fi
|
|
168
|
-
|
|
169
|
-
if [[ "$JSON_MODE" -ne 1 ]]; then
|
|
170
|
-
echo ""
|
|
171
|
-
ui_header "Supply Chain"
|
|
172
|
-
fi
|
|
173
|
-
if [[ -f "$MCP_LOCK_CONFIG" ]]; then
|
|
174
|
-
log_success "Package lock" "$MCP_LOCK_CONFIG"
|
|
175
|
-
else
|
|
176
|
-
if [[ "$REQUIRE_APPROVED_PACKAGE_LOCK" -eq 1 ]]; then
|
|
177
|
-
log_warn "Package lock" "not found (strict-local expects a tracked lock file)"
|
|
178
|
-
else
|
|
179
|
-
log_success "Package lock" "not required"
|
|
180
|
-
fi
|
|
181
|
-
fi
|
|
182
|
-
if [[ -f "$MCP_REGISTRY_CONFIG" ]]; then
|
|
183
|
-
log_success "Registry" "$MCP_REGISTRY_CONFIG"
|
|
184
|
-
else
|
|
185
|
-
if [[ "$REQUIRE_REGISTRY_ENTRY" -eq 1 ]]; then
|
|
186
|
-
log_warn "Registry" "not found (strict-local expects a tracked registry file)"
|
|
187
|
-
else
|
|
188
|
-
log_success "Registry" "not required"
|
|
189
|
-
fi
|
|
190
|
-
fi
|
|
191
|
-
|
|
192
|
-
MCP_SERVER_CARD="${MCP_SERVER_CARD:-$REPO_ROOT/.well-known/mcp/server-card.json}"
|
|
193
|
-
if [[ "$JSON_MODE" -ne 1 ]]; then
|
|
194
|
-
echo ""
|
|
195
|
-
ui_header "Server Card (SEP-1649)"
|
|
196
|
-
fi
|
|
197
|
-
if [[ -f "$MCP_SERVER_CARD" ]]; then
|
|
198
|
-
if command -v jq >/dev/null 2>&1 && jq empty "$MCP_SERVER_CARD" >/dev/null 2>&1; then
|
|
199
|
-
log_success "Server card" "$MCP_SERVER_CARD"
|
|
200
|
-
has_card_version="$(jq -r '.cardVersion // empty' "$MCP_SERVER_CARD")"
|
|
201
|
-
has_card_name="$(jq -r '.name // empty' "$MCP_SERVER_CARD")"
|
|
202
|
-
has_card_capabilities="$(jq -e '.capabilities' "$MCP_SERVER_CARD" >/dev/null 2>&1 && echo "yes" || echo "")"
|
|
203
|
-
has_card_transport="$(jq -e '.transport' "$MCP_SERVER_CARD" >/dev/null 2>&1 && echo "yes" || echo "")"
|
|
204
|
-
if [[ -n "$has_card_version" ]]; then
|
|
205
|
-
log_success "Card version" "$has_card_version"
|
|
206
|
-
else
|
|
207
|
-
log_warn "Card version" "missing cardVersion field"
|
|
208
|
-
fi
|
|
209
|
-
if [[ -n "$has_card_name" ]]; then
|
|
210
|
-
log_success "Card name" "$has_card_name"
|
|
211
|
-
else
|
|
212
|
-
log_warn "Card name" "missing name field"
|
|
213
|
-
fi
|
|
214
|
-
if [[ -n "$has_card_capabilities" ]]; then
|
|
215
|
-
log_success "Card capabilities" "present"
|
|
216
|
-
else
|
|
217
|
-
log_warn "Card capabilities" "missing capabilities block"
|
|
218
|
-
fi
|
|
219
|
-
if [[ -n "$has_card_transport" ]]; then
|
|
220
|
-
log_success "Card transport" "present"
|
|
221
|
-
else
|
|
222
|
-
log_warn "Card transport" "missing transport block"
|
|
223
|
-
fi
|
|
224
|
-
else
|
|
225
|
-
log_fail "Server card" "invalid JSON"
|
|
226
|
-
fi
|
|
227
|
-
else
|
|
228
|
-
log_warn "Server card" "not found (expected $MCP_SERVER_CARD)"
|
|
229
|
-
fi
|
|
230
|
-
|
|
231
|
-
if [[ "$JSON_MODE" -ne 1 ]]; then
|
|
232
|
-
echo ""
|
|
233
|
-
ui_header "Validation"
|
|
234
|
-
fi
|
|
235
|
-
if command -v jq >/dev/null 2>&1; then
|
|
236
|
-
if [[ -f "$MCP_CONFIG" ]] && jq empty "$MCP_CONFIG" >/dev/null 2>&1; then
|
|
237
|
-
log_success "JSON syntax" "valid"
|
|
238
|
-
JSON_VALID=1
|
|
239
|
-
else
|
|
240
|
-
log_fail "JSON syntax" "invalid"
|
|
241
|
-
fi
|
|
242
|
-
|
|
243
|
-
if [[ "$JSON_VALID" -eq 1 ]]; then
|
|
244
|
-
SERVER_COUNT="$(jq '.mcpServers | keys | length' "$MCP_CONFIG" 2>/dev/null || echo 0)"
|
|
245
|
-
if [[ "$SERVER_COUNT" -gt 0 ]]; then
|
|
246
|
-
log_success "MCP servers" "$SERVER_COUNT configured"
|
|
247
|
-
else
|
|
248
|
-
log_fail "MCP servers" "none configured"
|
|
249
|
-
fi
|
|
250
|
-
|
|
251
|
-
if jq -e --argjson blocked "$BLOCKED_PATHS" '.mcpServers.filesystem.args[]? as $arg | $blocked[] | select(. == $arg)' "$MCP_CONFIG" >/dev/null 2>&1; then
|
|
252
|
-
log_warn "Filesystem scope" "too broad (use a project-scoped directory)"
|
|
253
|
-
else
|
|
254
|
-
log_success "Filesystem scope" "not globally broad"
|
|
255
|
-
fi
|
|
256
|
-
|
|
257
|
-
# MCP operational policy: allow known launchers only.
|
|
258
|
-
unknown_launchers="$(jq -r --argjson allowed "$ALLOWED_LAUNCHERS" '.mcpServers | to_entries[]? | select((.value.command as $cmd | [$allowed[] | select(. == $cmd)] | length) == 0) | "\(.key):\(.value.command)"' "$MCP_CONFIG" 2>/dev/null || true)"
|
|
259
|
-
if [[ -n "$unknown_launchers" ]]; then
|
|
260
|
-
while IFS= read -r item; do
|
|
261
|
-
[[ -z "$item" ]] && continue
|
|
262
|
-
log_warn "Launcher policy" "review non-standard command $item"
|
|
263
|
-
done <<<"$unknown_launchers"
|
|
264
|
-
else
|
|
265
|
-
log_success "Launcher policy" "all server launchers are allowlisted (npx/node/uvx)"
|
|
266
|
-
fi
|
|
267
|
-
|
|
268
|
-
# MCP policy: flag wildcard/potentially risky args for review.
|
|
269
|
-
risky_args="$(jq -r --argjson blocked "$BLOCKED_ARG_PATTERNS" '
|
|
270
|
-
.mcpServers
|
|
271
|
-
| to_entries[]?
|
|
272
|
-
| .key as $name
|
|
273
|
-
| (.value.args // [])[]?
|
|
274
|
-
| . as $arg
|
|
275
|
-
| select(any($blocked[]; . as $pattern | ($arg | test($pattern))))
|
|
276
|
-
| "\($name):\($arg)"
|
|
277
|
-
' "$MCP_CONFIG" 2>/dev/null || true)"
|
|
278
|
-
if [[ -n "$risky_args" ]]; then
|
|
279
|
-
while IFS= read -r item; do
|
|
280
|
-
[[ -z "$item" ]] && continue
|
|
281
|
-
log_warn "Arg policy" "review risky argument $item"
|
|
282
|
-
done <<<"$risky_args"
|
|
283
|
-
else
|
|
284
|
-
log_success "Arg policy" "no high-risk wildcard/unsafe args found"
|
|
285
|
-
fi
|
|
286
|
-
|
|
287
|
-
forbidden_default_servers="$(jq -r --argjson forbidden "$FORBIDDEN_DEFAULT_SERVERS" '
|
|
288
|
-
.mcpServers
|
|
289
|
-
| keys[]
|
|
290
|
-
| . as $server
|
|
291
|
-
| select(any($forbidden[]; . == $server))
|
|
292
|
-
' "$MCP_CONFIG" 2>/dev/null || true)"
|
|
293
|
-
if [[ -n "$forbidden_default_servers" ]]; then
|
|
294
|
-
while IFS= read -r item; do
|
|
295
|
-
[[ -z "$item" ]] && continue
|
|
296
|
-
log_warn "Default server policy" "$item enabled in strict-local profile"
|
|
297
|
-
done <<<"$forbidden_default_servers"
|
|
298
|
-
else
|
|
299
|
-
log_success "Default server policy" "local-only default set"
|
|
300
|
-
fi
|
|
301
|
-
|
|
302
|
-
invalid_transports="$(jq -r --argjson trusted "$TRUSTED_TRANSPORTS" '
|
|
303
|
-
.mcpServers
|
|
304
|
-
| to_entries[]?
|
|
305
|
-
| .key as $name
|
|
306
|
-
| (.value.transport // "stdio") as $transport
|
|
307
|
-
| select(([$trusted[] | select(. == $transport)] | length) == 0)
|
|
308
|
-
| "\($name):\($transport)"
|
|
309
|
-
' "$MCP_CONFIG" 2>/dev/null || true)"
|
|
310
|
-
if [[ -n "$invalid_transports" ]]; then
|
|
311
|
-
while IFS= read -r item; do
|
|
312
|
-
[[ -z "$item" ]] && continue
|
|
313
|
-
log_warn "Transport policy" "review untrusted transport $item"
|
|
314
|
-
done <<<"$invalid_transports"
|
|
315
|
-
else
|
|
316
|
-
log_success "Transport policy" "all servers use trusted transports"
|
|
317
|
-
fi
|
|
318
|
-
|
|
319
|
-
if [[ "$REQUIRE_HTTPS_FOR_HTTP" -eq 1 ]]; then
|
|
320
|
-
insecure_http_servers="$(jq -r '
|
|
321
|
-
.mcpServers
|
|
322
|
-
| to_entries[]?
|
|
323
|
-
| select((.value.transport // "") == "http")
|
|
324
|
-
| select((.value.url // "") | startswith("https://") | not)
|
|
325
|
-
| .key
|
|
326
|
-
' "$MCP_CONFIG" 2>/dev/null || true)"
|
|
327
|
-
if [[ -n "$insecure_http_servers" ]]; then
|
|
328
|
-
while IFS= read -r item; do
|
|
329
|
-
[[ -z "$item" ]] && continue
|
|
330
|
-
log_warn "Transport security" "$item uses non-HTTPS HTTP transport"
|
|
331
|
-
done <<<"$insecure_http_servers"
|
|
332
|
-
else
|
|
333
|
-
log_success "Transport security" "HTTP transports are HTTPS"
|
|
334
|
-
fi
|
|
335
|
-
fi
|
|
336
|
-
|
|
337
|
-
# Streamable-HTTP HTTPS validation
|
|
338
|
-
insecure_streamable_servers="$(jq -r '
|
|
339
|
-
.mcpServers
|
|
340
|
-
| to_entries[]?
|
|
341
|
-
| select((.value.transport // "") == "streamable-http")
|
|
342
|
-
| select((.value.url // "") | startswith("https://") | not)
|
|
343
|
-
| .key
|
|
344
|
-
' "$MCP_CONFIG" 2>/dev/null || true)"
|
|
345
|
-
if [[ -n "$insecure_streamable_servers" ]]; then
|
|
346
|
-
while IFS= read -r item; do
|
|
347
|
-
[[ -z "$item" ]] && continue
|
|
348
|
-
log_warn "Transport security" "$item streamable-http transport must use HTTPS"
|
|
349
|
-
done <<<"$insecure_streamable_servers"
|
|
350
|
-
fi
|
|
351
|
-
|
|
352
|
-
# Auth Profiles validation
|
|
353
|
-
if command -v jq >/dev/null 2>&1 && [[ -f "$MCP_POLICY_CONFIG" ]]; then
|
|
354
|
-
declared_auth="$(jq -c '.profiles[.defaultProfile].authProfiles // []' "$MCP_POLICY_CONFIG" 2>/dev/null)"
|
|
355
|
-
if [[ "$declared_auth" != "[]" ]]; then
|
|
356
|
-
log_success "Auth profiles" "declared: $declared_auth"
|
|
357
|
-
# Check transport/auth compatibility: streamable-http/http servers need more than "none"
|
|
358
|
-
incompatible_auth="$(jq -r --argjson registry "$APPROVED_REGISTRY" --argjson allowed_auth "$declared_auth" '
|
|
359
|
-
.mcpServers
|
|
360
|
-
| to_entries[]?
|
|
361
|
-
| .key as $name
|
|
362
|
-
| (.value.transport // "stdio") as $t
|
|
363
|
-
| select($t == "http" or $t == "streamable-http")
|
|
364
|
-
| select($registry[$name].authProfile // "none" | IN($allowed_auth[]) | not)
|
|
365
|
-
| "\($name):\($registry[$name].authProfile // "none")"
|
|
366
|
-
' "$MCP_CONFIG" 2>/dev/null || true)"
|
|
367
|
-
if [[ -n "$incompatible_auth" ]]; then
|
|
368
|
-
while IFS= read -r item; do
|
|
369
|
-
[[ -z "$item" ]] && continue
|
|
370
|
-
log_warn "Auth compatibility" "$item uses auth profile not in policy"
|
|
371
|
-
done <<<"$incompatible_auth"
|
|
372
|
-
else
|
|
373
|
-
log_success "Auth compatibility" "all server auth profiles match policy"
|
|
374
|
-
fi
|
|
375
|
-
fi
|
|
376
|
-
fi
|
|
377
|
-
|
|
378
|
-
if [[ "$REQUIRE_OAUTH_FOR_HTTP" -eq 1 ]]; then
|
|
379
|
-
non_oauth_http_servers="$(jq -r --argjson registry "$APPROVED_REGISTRY" '
|
|
380
|
-
.mcpServers
|
|
381
|
-
| to_entries[]?
|
|
382
|
-
| .key as $name
|
|
383
|
-
| select((.value.transport // "") == "http")
|
|
384
|
-
| select(($registry[$name].auth // "") != "oauth2")
|
|
385
|
-
| $name
|
|
386
|
-
' "$MCP_CONFIG" 2>/dev/null || true)"
|
|
387
|
-
if [[ -n "$non_oauth_http_servers" ]]; then
|
|
388
|
-
while IFS= read -r item; do
|
|
389
|
-
[[ -z "$item" ]] && continue
|
|
390
|
-
log_warn "Auth policy" "$item HTTP transport is not registered for OAuth2"
|
|
391
|
-
done <<<"$non_oauth_http_servers"
|
|
392
|
-
else
|
|
393
|
-
log_success "Auth policy" "HTTP transports are registry-approved for OAuth2"
|
|
394
|
-
fi
|
|
395
|
-
fi
|
|
396
|
-
|
|
397
|
-
env_vars="$(
|
|
398
|
-
{
|
|
399
|
-
jq -r '.mcpServers | to_entries[]? | (.value.env // {}) | to_entries[]?.value' "$MCP_CONFIG"
|
|
400
|
-
jq -r '.mcpServers | to_entries[]? | (.value.args // [])[]?' "$MCP_CONFIG"
|
|
401
|
-
} | sed -n 's/^\${\([A-Z0-9_]\+\)}$/\1/p' | sort -u
|
|
402
|
-
)"
|
|
403
|
-
if [[ -z "$env_vars" ]]; then
|
|
404
|
-
log_success "Server env placeholders" "none declared"
|
|
405
|
-
else
|
|
406
|
-
missing=0
|
|
407
|
-
while IFS= read -r key; do
|
|
408
|
-
[[ -z "$key" ]] && continue
|
|
409
|
-
if [[ -z "${!key:-}" ]]; then
|
|
410
|
-
log_warn "Env variable" "$key is not set"
|
|
411
|
-
missing=$((missing + 1))
|
|
412
|
-
fi
|
|
413
|
-
done <<<"$env_vars"
|
|
414
|
-
if [[ "$missing" -eq 0 ]]; then
|
|
415
|
-
log_success "Env variables" "all referenced placeholders are set"
|
|
416
|
-
fi
|
|
417
|
-
fi
|
|
418
|
-
|
|
419
|
-
while IFS=$'\t' read -r server env_key; do
|
|
420
|
-
[[ -z "${server:-}" ]] && continue
|
|
421
|
-
if ! jq -e --arg server "$server" '.mcpServers[$server]' "$MCP_CONFIG" >/dev/null 2>&1; then
|
|
422
|
-
continue
|
|
423
|
-
fi
|
|
424
|
-
if [[ -n "${!env_key:-}" ]]; then
|
|
425
|
-
log_success "Token check" "$env_key is set for $server MCP server"
|
|
426
|
-
else
|
|
427
|
-
log_warn "Token check" "$env_key missing for $server MCP server"
|
|
428
|
-
fi
|
|
429
|
-
done < <(jq -r '
|
|
430
|
-
to_entries[]
|
|
431
|
-
| .key as $server
|
|
432
|
-
| (.value // [])[]
|
|
433
|
-
| [$server, .]
|
|
434
|
-
| @tsv
|
|
435
|
-
' <<<"$REQUIRED_ENV_RULES" 2>/dev/null || true)
|
|
436
|
-
|
|
437
|
-
if [[ "$POLICY_WARN_ON_UNPINNED_NPX" -eq 1 ]]; then
|
|
438
|
-
unpinned_npx_servers="$(jq -r '
|
|
439
|
-
.mcpServers
|
|
440
|
-
| to_entries[]?
|
|
441
|
-
| select(.value.command == "npx")
|
|
442
|
-
| select((.value.args // []) | any(
|
|
443
|
-
test("^[A-Za-z0-9@._/-]+$")
|
|
444
|
-
and (startswith("-") | not)
|
|
445
|
-
and (test("^(@[^/]+/[^@]+|[^@]+)@[^@]+$") | not)
|
|
446
|
-
))
|
|
447
|
-
| .key
|
|
448
|
-
' "$MCP_CONFIG" 2>/dev/null || true)"
|
|
449
|
-
if [[ -n "$unpinned_npx_servers" ]]; then
|
|
450
|
-
while IFS= read -r item; do
|
|
451
|
-
[[ -z "$item" ]] && continue
|
|
452
|
-
log_warn "Package pinning" "$item uses unpinned npx package"
|
|
453
|
-
done <<<"$unpinned_npx_servers"
|
|
454
|
-
else
|
|
455
|
-
log_success "Package pinning" "no unpinned npx packages found"
|
|
456
|
-
fi
|
|
457
|
-
fi
|
|
458
|
-
|
|
459
|
-
if [[ "$REQUIRE_APPROVED_PACKAGE_LOCK" -eq 1 ]]; then
|
|
460
|
-
approved_package_mismatches="$(jq -r --argjson approved "$APPROVED_PACKAGE_LOCK" '
|
|
461
|
-
.mcpServers
|
|
462
|
-
| to_entries[]?
|
|
463
|
-
| .key as $server
|
|
464
|
-
| .value.command as $command
|
|
465
|
-
| ((.value.args // []) | map(select(test("^[A-Za-z0-9@._/-]+@[A-Za-z0-9._-]+$"))) | .[0] // "") as $pkg
|
|
466
|
-
| select($command == "npx")
|
|
467
|
-
| select(($approved[$server].package // "") != $pkg)
|
|
468
|
-
| "\($server)\t\($pkg)\t\($approved[$server].package // "untracked")"
|
|
469
|
-
' "$MCP_CONFIG" 2>/dev/null || true)"
|
|
470
|
-
if [[ -n "$approved_package_mismatches" ]]; then
|
|
471
|
-
while IFS=$'\t' read -r server actual expected; do
|
|
472
|
-
[[ -z "${server:-}" ]] && continue
|
|
473
|
-
log_warn "Package lock" "$server uses $actual (approved: $expected)"
|
|
474
|
-
done <<<"$approved_package_mismatches"
|
|
475
|
-
else
|
|
476
|
-
log_success "Package lock" "all active servers match approved package refs"
|
|
477
|
-
fi
|
|
478
|
-
fi
|
|
479
|
-
|
|
480
|
-
if [[ "$REQUIRE_REGISTRY_ENTRY" -eq 1 ]]; then
|
|
481
|
-
registry_mismatches="$(jq -r --argjson registry "$APPROVED_REGISTRY" '
|
|
482
|
-
.mcpServers
|
|
483
|
-
| to_entries[]?
|
|
484
|
-
| .key as $server
|
|
485
|
-
| (.value.command // "") as $command
|
|
486
|
-
| (.value.transport // "stdio") as $transport
|
|
487
|
-
| ((.value.args // []) | map(select(test("^[A-Za-z0-9@._/-]+@[A-Za-z0-9._-]+$"))) | .[0] // "") as $pkg
|
|
488
|
-
| (.value.url // "") as $url
|
|
489
|
-
| select(($registry[$server] | type) != "object"
|
|
490
|
-
or ($registry[$server].transport // "stdio") != $transport
|
|
491
|
-
or ($registry[$server].launcher // "") != $command
|
|
492
|
-
or (($command == "npx") and (($registry[$server].package // "") != $pkg))
|
|
493
|
-
or (($transport == "http") and (($registry[$server].url // "") != $url)))
|
|
494
|
-
| "\($server)\t\($transport)\t\($command)\t\($pkg)\t\($url)"
|
|
495
|
-
' "$MCP_CONFIG" 2>/dev/null || true)"
|
|
496
|
-
if [[ -n "$registry_mismatches" ]]; then
|
|
497
|
-
while IFS=$'\t' read -r server _transport _command _pkg _url; do
|
|
498
|
-
[[ -z "${server:-}" ]] && continue
|
|
499
|
-
log_warn "Registry policy" "$server is missing or diverges from the tracked MCP registry"
|
|
500
|
-
done <<<"$registry_mismatches"
|
|
501
|
-
else
|
|
502
|
-
log_success "Registry policy" "all active servers match the tracked MCP registry"
|
|
503
|
-
fi
|
|
504
|
-
fi
|
|
505
|
-
fi
|
|
506
|
-
else
|
|
507
|
-
log_warn "jq" "not installed, running limited checks"
|
|
508
|
-
if grep -q '"mcpServers"' "$MCP_CONFIG" 2>/dev/null; then
|
|
509
|
-
log_success "mcpServers key" "present"
|
|
510
|
-
else
|
|
511
|
-
log_fail "mcpServers key" "missing"
|
|
512
|
-
fi
|
|
513
|
-
fi
|
|
514
|
-
|
|
515
|
-
if [[ "$Errors" -eq 0 ]]; then
|
|
516
|
-
if [[ "$Warnings" -eq 0 ]]; then
|
|
517
|
-
SUMMARY_STATUS="healthy"
|
|
518
|
-
else
|
|
519
|
-
if [[ "$STRICT_MODE" -eq 1 ]]; then
|
|
520
|
-
SUMMARY_STATUS="failed"
|
|
521
|
-
else
|
|
522
|
-
SUMMARY_STATUS="warning"
|
|
523
|
-
fi
|
|
524
|
-
fi
|
|
525
|
-
else
|
|
526
|
-
SUMMARY_STATUS="failed"
|
|
527
|
-
fi
|
|
528
|
-
|
|
529
|
-
if [[ "$JSON_MODE" -eq 1 ]]; then
|
|
530
|
-
jq -n \
|
|
531
|
-
--arg status "$SUMMARY_STATUS" \
|
|
532
|
-
--arg config "$MCP_CONFIG" \
|
|
533
|
-
--arg policy "$MCP_POLICY_CONFIG" \
|
|
534
|
-
--argjson strict "$STRICT_MODE" \
|
|
535
|
-
--argjson server_count "$SERVER_COUNT" \
|
|
536
|
-
--argjson errors "$Errors" \
|
|
537
|
-
--argjson warnings "$Warnings" \
|
|
538
|
-
--argjson config_ok "$CONFIG_OK" \
|
|
539
|
-
--argjson json_valid "$JSON_VALID" \
|
|
540
|
-
'{
|
|
541
|
-
status: $status,
|
|
542
|
-
strict: ($strict == 1),
|
|
543
|
-
config_path: $config,
|
|
544
|
-
policy_path: $policy,
|
|
545
|
-
server_count: $server_count,
|
|
546
|
-
checks: {
|
|
547
|
-
config_present: ($config_ok == 1),
|
|
548
|
-
json_valid: ($json_valid == 1)
|
|
549
|
-
},
|
|
550
|
-
summary: {
|
|
551
|
-
errors: $errors,
|
|
552
|
-
warnings: $warnings
|
|
553
|
-
}
|
|
554
|
-
}'
|
|
555
|
-
else
|
|
556
|
-
echo ""
|
|
557
|
-
ui_header "Summary"
|
|
558
|
-
fi
|
|
559
|
-
|
|
560
|
-
if [[ "$Errors" -eq 0 ]]; then
|
|
561
|
-
if [[ "$Warnings" -eq 0 ]]; then
|
|
562
|
-
if [[ "$JSON_MODE" -ne 1 ]]; then
|
|
563
|
-
ui_ok "MCP configuration healthy"
|
|
564
|
-
fi
|
|
565
|
-
else
|
|
566
|
-
if [[ "$STRICT_MODE" -eq 1 ]]; then
|
|
567
|
-
if [[ "$JSON_MODE" -ne 1 ]]; then
|
|
568
|
-
ui_err "MCP issues found" "$Warnings policy warnings (strict mode)"
|
|
569
|
-
fi
|
|
570
|
-
exit 1
|
|
571
|
-
else
|
|
572
|
-
if [[ "$JSON_MODE" -ne 1 ]]; then
|
|
573
|
-
ui_warn "MCP configuration healthy" "$Warnings warnings"
|
|
574
|
-
fi
|
|
575
|
-
fi
|
|
576
|
-
fi
|
|
577
|
-
else
|
|
578
|
-
if [[ "$JSON_MODE" -ne 1 ]]; then
|
|
579
|
-
ui_err "MCP issues found" "$Errors errors, $Warnings warnings"
|
|
580
|
-
fi
|
|
581
|
-
exit 1
|
|
582
|
-
fi
|