avorelo 0.1.0 → 0.3.0
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/LICENSE +23 -16
- package/README.md +90 -51
- package/bin/avorelo.mjs +7 -0
- package/dist/avorelo.mjs +19741 -0
- package/package.json +135 -120
- package/bin/avorelo +0 -9
- package/scripts/README.md +0 -40
- package/scripts/cco-dashboard.js +0 -252
- package/scripts/cco-status.js +0 -430
- package/scripts/lib/activation/account-state.js +0 -37
- package/scripts/lib/activation/activation-runner.js +0 -546
- package/scripts/lib/activation/activation-self-healing.js +0 -480
- package/scripts/lib/activation/activation-state.js +0 -83
- package/scripts/lib/activation/activation-summary.js +0 -191
- package/scripts/lib/activation/adapters/claude-code.js +0 -77
- package/scripts/lib/activation/adapters/codex-cli.js +0 -52
- package/scripts/lib/activation/adapters/cursor.js +0 -37
- package/scripts/lib/activation/adapters/github-agent.js +0 -39
- package/scripts/lib/activation/adapters/terminal.js +0 -42
- package/scripts/lib/activation/adapters/vscode.js +0 -39
- package/scripts/lib/activation/adapters/windsurf.js +0 -37
- package/scripts/lib/activation/ai-surface-detector.js +0 -151
- package/scripts/lib/activation/connect-account.js +0 -145
- package/scripts/lib/activation/detect-environment.js +0 -75
- package/scripts/lib/activation/detect-hosts.js +0 -62
- package/scripts/lib/activation/format-activation-output.js +0 -109
- package/scripts/lib/activation/next-action.js +0 -43
- package/scripts/lib/activation/repair-engine.js +0 -219
- package/scripts/lib/activation-distribution-readiness.js +0 -507
- package/scripts/lib/adapter-conformance.js +0 -176
- package/scripts/lib/adapter-readiness.js +0 -417
- package/scripts/lib/adapter-safety-boundaries.js +0 -335
- package/scripts/lib/adapter-technical-readiness-gate.js +0 -205
- package/scripts/lib/agent-access-governance.js +0 -455
- package/scripts/lib/agent-enforcement.js +0 -765
- package/scripts/lib/agent-policy-profile.js +0 -210
- package/scripts/lib/agent-security/action-evaluator.js +0 -507
- package/scripts/lib/agent-security/adapter-registry.js +0 -98
- package/scripts/lib/agent-security/auto-policy.js +0 -139
- package/scripts/lib/agent-security/bounded-scan.js +0 -93
- package/scripts/lib/agent-security/enforcement-adapter.js +0 -174
- package/scripts/lib/agent-security/enforcement-engine.js +0 -1129
- package/scripts/lib/agent-security/file-write-adapter.js +0 -183
- package/scripts/lib/agent-security/file-write-rules.js +0 -178
- package/scripts/lib/agent-security/index.js +0 -3342
- package/scripts/lib/agent-security/instruction-risk.js +0 -181
- package/scripts/lib/agent-security/mcp-action-adapter.js +0 -185
- package/scripts/lib/agent-security/mcp-action-rules.js +0 -184
- package/scripts/lib/agent-security/package-action-adapter.js +0 -175
- package/scripts/lib/agent-security/package-action-rules.js +0 -233
- package/scripts/lib/agent-security/performance.js +0 -148
- package/scripts/lib/agent-security/permission-minimizer.js +0 -403
- package/scripts/lib/agent-security/scan-cache.js +0 -74
- package/scripts/lib/agent-security/source-trust.js +0 -146
- package/scripts/lib/ai-install-prompt.js +0 -288
- package/scripts/lib/ai-workspace-hygiene.js +0 -1499
- package/scripts/lib/alpha-activation.js +0 -520
- package/scripts/lib/alpha-feedback.js +0 -263
- package/scripts/lib/alpha-readiness-gate.js +0 -332
- package/scripts/lib/anti-gaming.js +0 -169
- package/scripts/lib/artifact-health.js +0 -431
- package/scripts/lib/attribution.js +0 -180
- package/scripts/lib/audit.js +0 -289
- package/scripts/lib/avorelo-skill-registry.js +0 -810
- package/scripts/lib/batch-jobs.js +0 -71
- package/scripts/lib/brain-pack.js +0 -578
- package/scripts/lib/brand-boundary.js +0 -424
- package/scripts/lib/brand.js +0 -74
- package/scripts/lib/browser-capability.js +0 -1048
- package/scripts/lib/browser-proof-preflight.js +0 -321
- package/scripts/lib/cache-readiness.js +0 -187
- package/scripts/lib/canonical-reentry.js +0 -162
- package/scripts/lib/capability-packs.js +0 -314
- package/scripts/lib/capability-recommender.js +0 -512
- package/scripts/lib/capability-registry.js +0 -1059
- package/scripts/lib/carry-forward-surfacing.js +0 -194
- package/scripts/lib/ccusage-adapter.js +0 -188
- package/scripts/lib/company-loop.js +0 -1149
- package/scripts/lib/config.js +0 -637
- package/scripts/lib/context-acquisition-plan.js +0 -287
- package/scripts/lib/context-budget-guard.js +0 -170
- package/scripts/lib/context-budget-scanner.js +0 -257
- package/scripts/lib/context-optimizer.js +0 -715
- package/scripts/lib/context-reduction-plan.js +0 -178
- package/scripts/lib/context-safety.js +0 -88
- package/scripts/lib/context-savings-engine.js +0 -158
- package/scripts/lib/cost-evidence.js +0 -254
- package/scripts/lib/cross-host-install-plan.js +0 -308
- package/scripts/lib/cross-host-install-readiness.js +0 -237
- package/scripts/lib/cross-host-value-flow.js +0 -268
- package/scripts/lib/dashboard.js +0 -900
- package/scripts/lib/design-partner-feedback.js +0 -346
- package/scripts/lib/entitlements.js +0 -100
- package/scripts/lib/execution-packet.js +0 -559
- package/scripts/lib/experimentation-events.js +0 -547
- package/scripts/lib/external-capability-compliance.js +0 -107
- package/scripts/lib/external-user-simulation.js +0 -166
- package/scripts/lib/failure-recovery-readiness.js +0 -81
- package/scripts/lib/failure-recovery.js +0 -419
- package/scripts/lib/feedback-intelligence.js +0 -537
- package/scripts/lib/feedback-signals.js +0 -205
- package/scripts/lib/file-integrity.js +0 -68
- package/scripts/lib/fsx.js +0 -127
- package/scripts/lib/full-readiness-gate.js +0 -451
- package/scripts/lib/guidance-builder.js +0 -174
- package/scripts/lib/hook-apply.js +0 -1019
- package/scripts/lib/hook-baseline.js +0 -310
- package/scripts/lib/hook-config-preview.js +0 -275
- package/scripts/lib/hook-contracts.js +0 -290
- package/scripts/lib/hook-safety-boundary-readiness.js +0 -80
- package/scripts/lib/host-capability-matrix.js +0 -351
- package/scripts/lib/host-support-context.js +0 -254
- package/scripts/lib/http-hook-action.js +0 -538
- package/scripts/lib/install-ai-readiness.js +0 -84
- package/scripts/lib/install-intake-risk.js +0 -1037
- package/scripts/lib/install-journey-intelligence.js +0 -329
- package/scripts/lib/intervention-guidance.js +0 -57
- package/scripts/lib/known-limitations.js +0 -115
- package/scripts/lib/l8-path-truth.js +0 -146
- package/scripts/lib/launch-hardening-gate.js +0 -436
- package/scripts/lib/launch-readiness.js +0 -628
- package/scripts/lib/learning-memory.js +0 -686
- package/scripts/lib/lifecycle-hooks.js +0 -802
- package/scripts/lib/local-package-smoke.js +0 -423
- package/scripts/lib/local-pricing.js +0 -299
- package/scripts/lib/mcp-enforcement.js +0 -311
- package/scripts/lib/mcp-least-privilege-policy.js +0 -303
- package/scripts/lib/mcp-tool-inventory.js +0 -388
- package/scripts/lib/mcp-tool-risk.js +0 -0
- package/scripts/lib/memory.js +0 -335
- package/scripts/lib/metrics.js +0 -699
- package/scripts/lib/micro-proof.js +0 -133
- package/scripts/lib/next-run-context.js +0 -436
- package/scripts/lib/operating-value.js +0 -1648
- package/scripts/lib/optimization-v3.js +0 -122
- package/scripts/lib/orchestration/adapters/_shared.js +0 -49
- package/scripts/lib/orchestration/adapters/aider.js +0 -18
- package/scripts/lib/orchestration/adapters/claude-code.js +0 -35
- package/scripts/lib/orchestration/adapters/codex.js +0 -35
- package/scripts/lib/orchestration/adapters/gemini-cli.js +0 -18
- package/scripts/lib/orchestration/adapters/git.js +0 -25
- package/scripts/lib/orchestration/adapters/index.js +0 -31
- package/scripts/lib/orchestration/adapters/lm-studio.js +0 -18
- package/scripts/lib/orchestration/adapters/ollama.js +0 -18
- package/scripts/lib/orchestration/adapters/opencode.js +0 -18
- package/scripts/lib/orchestration/adapters/openrouter.js +0 -18
- package/scripts/lib/orchestration/adapters/test-runner.js +0 -25
- package/scripts/lib/orchestration/cli.js +0 -438
- package/scripts/lib/orchestration/execution-manager.js +0 -279
- package/scripts/lib/orchestration/handoff.js +0 -314
- package/scripts/lib/orchestration/index.js +0 -456
- package/scripts/lib/orchestration/inventory.js +0 -47
- package/scripts/lib/orchestration/model-discovery.js +0 -498
- package/scripts/lib/orchestration/model-profiler.js +0 -170
- package/scripts/lib/orchestration/model-profiles.js +0 -252
- package/scripts/lib/orchestration/model-refresh-policy.js +0 -72
- package/scripts/lib/orchestration/proof-writer.js +0 -349
- package/scripts/lib/orchestration/provider-discovery/aider.js +0 -49
- package/scripts/lib/orchestration/provider-discovery/claude-code.js +0 -56
- package/scripts/lib/orchestration/provider-discovery/codex.js +0 -49
- package/scripts/lib/orchestration/provider-discovery/common.js +0 -186
- package/scripts/lib/orchestration/provider-discovery/gemini.js +0 -106
- package/scripts/lib/orchestration/provider-discovery/lm-studio.js +0 -118
- package/scripts/lib/orchestration/provider-discovery/models-dev.js +0 -12
- package/scripts/lib/orchestration/provider-discovery/ollama.js +0 -100
- package/scripts/lib/orchestration/provider-discovery/opencode.js +0 -47
- package/scripts/lib/orchestration/provider-discovery/openrouter.js +0 -44
- package/scripts/lib/orchestration/risk-classifier.js +0 -130
- package/scripts/lib/orchestration/routing-policy.js +0 -486
- package/scripts/lib/orchestration/settings.js +0 -112
- package/scripts/lib/orchestration/state.js +0 -165
- package/scripts/lib/orchestration/verification-manager.js +0 -138
- package/scripts/lib/output-profiles.js +0 -146
- package/scripts/lib/package-content-audit.js +0 -368
- package/scripts/lib/package-runtime.js +0 -278
- package/scripts/lib/plan-surface.js +0 -53
- package/scripts/lib/plans.js +0 -2318
- package/scripts/lib/policy-provider.js +0 -27
- package/scripts/lib/prelaunch-activation-readiness.js +0 -409
- package/scripts/lib/prelaunch-evidence-store.js +0 -816
- package/scripts/lib/prelaunch-intelligence.js +0 -869
- package/scripts/lib/pricing-experiment.js +0 -118
- package/scripts/lib/pro-moment-events.js +0 -77
- package/scripts/lib/pro-moment-state.js +0 -227
- package/scripts/lib/pro-moments.js +0 -1216
- package/scripts/lib/product-learning-events.js +0 -629
- package/scripts/lib/project-profile.js +0 -555
- package/scripts/lib/prompt-compiler.js +0 -280
- package/scripts/lib/prompt-lint.js +0 -32
- package/scripts/lib/prompt-suggestions.js +0 -52
- package/scripts/lib/proof-canonical.js +0 -398
- package/scripts/lib/proof-drilldown.js +0 -383
- package/scripts/lib/proof-events.js +0 -342
- package/scripts/lib/proof-history.js +0 -243
- package/scripts/lib/proof-metrics.js +0 -296
- package/scripts/lib/proof-outcome-evidence.js +0 -134
- package/scripts/lib/proof-receipt.js +0 -335
- package/scripts/lib/proof-record.js +0 -461
- package/scripts/lib/public-activation-distribution-gate.js +0 -258
- package/scripts/lib/public-cli.js +0 -3891
- package/scripts/lib/public-distribution-truth.js +0 -211
- package/scripts/lib/public-install-claim-checker.js +0 -294
- package/scripts/lib/publish-provenance-readiness.js +0 -283
- package/scripts/lib/readiness-delta.js +0 -218
- package/scripts/lib/readiness-evidence-closure.js +0 -196
- package/scripts/lib/reentry-memory-capture.js +0 -241
- package/scripts/lib/reentry-memory-retrieval.js +0 -302
- package/scripts/lib/reentry-memory-status.js +0 -146
- package/scripts/lib/reentry-memory-store.js +0 -178
- package/scripts/lib/reentry-state.js +0 -66
- package/scripts/lib/release-candidate-bundle.js +0 -166
- package/scripts/lib/remediation.js +0 -81
- package/scripts/lib/repo-map.js +0 -391
- package/scripts/lib/run-improvements-lifecycle.js +0 -330
- package/scripts/lib/run-improvements.js +0 -789
- package/scripts/lib/runtime-decision-policy.js +0 -387
- package/scripts/lib/safe-path-engine.js +0 -705
- package/scripts/lib/safe-run-controller.js +0 -887
- package/scripts/lib/score.js +0 -262
- package/scripts/lib/seamless-enforcement.js +0 -329
- package/scripts/lib/seamless-outcome.js +0 -689
- package/scripts/lib/seamless-reality-gate.js +0 -5043
- package/scripts/lib/security-risk-classifier.js +0 -511
- package/scripts/lib/security-scan.js +0 -384
- package/scripts/lib/session-context-optimizer.js +0 -1211
- package/scripts/lib/session-timing.js +0 -315
- package/scripts/lib/skill-hygiene.js +0 -805
- package/scripts/lib/skill-packs.js +0 -161
- package/scripts/lib/skills-operating-layer.js +0 -580
- package/scripts/lib/smart-work-routing.js +0 -768
- package/scripts/lib/source-catalog.js +0 -700
- package/scripts/lib/status-value-summary.js +0 -32
- package/scripts/lib/support-bundle.js +0 -578
- package/scripts/lib/task-continuation.js +0 -440
- package/scripts/lib/test-helpers.js +0 -15
- package/scripts/lib/tier.js +0 -38
- package/scripts/lib/token-context-quality-gate.js +0 -370
- package/scripts/lib/token-cost-capture.js +0 -187
- package/scripts/lib/token-cost-intelligence.js +0 -358
- package/scripts/lib/token-efficiency-evidence.js +0 -213
- package/scripts/lib/token-evidence.js +0 -699
- package/scripts/lib/tokenish.js +0 -17
- package/scripts/lib/tool-output-sandbox.js +0 -304
- package/scripts/lib/trust-audit.js +0 -136
- package/scripts/lib/unified-events.js +0 -396
- package/scripts/lib/upgrade-interruption-recovery.js +0 -407
- package/scripts/lib/usage-ledger.js +0 -201
- package/scripts/lib/value-ledger.js +0 -130
- package/scripts/lib/value-proof-calibration.js +0 -531
- package/scripts/lib/visual-qa.js +0 -231
- package/scripts/lib/voice-alpha.js +0 -29
- package/scripts/lib/work-aware-orchestration.js +0 -976
- package/scripts/lib/work-control-receipts.js +0 -577
- package/scripts/lib/work-ledger.js +0 -1123
- package/scripts/lib/work-panel-preview.js +0 -352
- package/scripts/lib/workflow-discipline.js +0 -280
- package/scripts/lib/workflow-signals.js +0 -419
- package/scripts/lib/workspace-map.js +0 -281
- package/scripts/lib/workspace-registry.js +0 -1367
- package/scripts/lib/workspace-resolver.js +0 -480
|
@@ -1,303 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
// ── MCP Least-Privilege Policy ────────────────────────────────────────────────
|
|
4
|
-
//
|
|
5
|
-
// Contract: avorelo.mcpLeastPrivilegePolicy.v1
|
|
6
|
-
//
|
|
7
|
-
// Builds least-privilege policy from risk report.
|
|
8
|
-
// Preview only — does NOT modify any MCP config.
|
|
9
|
-
|
|
10
|
-
const fs = require("node:fs");
|
|
11
|
-
const path = require("node:path");
|
|
12
|
-
const { REASON_CODES } = require("./mcp-tool-risk");
|
|
13
|
-
|
|
14
|
-
const CONTRACT = "avorelo.mcpLeastPrivilegePolicy.v1";
|
|
15
|
-
const SCHEMA_VERSION = 1;
|
|
16
|
-
|
|
17
|
-
const POLICY_DIR_REL = ".claude/cco/orchestration/mcp-tool-governance";
|
|
18
|
-
const POLICY_REL = `${POLICY_DIR_REL}/latest-policy.json`;
|
|
19
|
-
const PREVIEW_REL = `${POLICY_DIR_REL}/latest-policy-preview.json`;
|
|
20
|
-
|
|
21
|
-
// ── Policy decisions ──────────────────────────────────────────────────────────
|
|
22
|
-
|
|
23
|
-
const DECISIONS = Object.freeze({
|
|
24
|
-
ALLOW: "allow",
|
|
25
|
-
WARN: "warn",
|
|
26
|
-
APPROVAL_REQUIRED: "approval_required",
|
|
27
|
-
BLOCK: "block",
|
|
28
|
-
NOT_ENOUGH_INFORMATION: "not_enough_information",
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
32
|
-
|
|
33
|
-
function nowIso() {
|
|
34
|
-
return new Date().toISOString();
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function safeWriteJson(filePath, data) {
|
|
38
|
-
try {
|
|
39
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
40
|
-
fs.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}\n`, "utf8");
|
|
41
|
-
} catch {
|
|
42
|
-
// non-fatal
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// ── Policy decision logic ─────────────────────────────────────────────────────
|
|
47
|
-
|
|
48
|
-
function decideMcpToolPolicy(toolRisk, context, options = {}) {
|
|
49
|
-
if (!toolRisk) {
|
|
50
|
-
return {
|
|
51
|
-
decision: DECISIONS.NOT_ENOUGH_INFORMATION,
|
|
52
|
-
reasonCodes: ["NO_TOOL_RISK_DATA"],
|
|
53
|
-
safeAlternative: "Run `avorelo mcp risk` to classify tools first.",
|
|
54
|
-
safeNextAction: "Run `avorelo mcp inventory` then `avorelo mcp risk`.",
|
|
55
|
-
taskStillExecutable: true,
|
|
56
|
-
approvalRequired: false,
|
|
57
|
-
redacted: true,
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const codes = toolRisk.reasonCodes || [];
|
|
62
|
-
const riskLevel = toolRisk.riskLevel || "unknown";
|
|
63
|
-
let decision = DECISIONS.NOT_ENOUGH_INFORMATION;
|
|
64
|
-
let safeAlternative = null;
|
|
65
|
-
let safeNextAction = "Review tool risk before proceeding.";
|
|
66
|
-
let approvalRequired = false;
|
|
67
|
-
let taskStillExecutable = true;
|
|
68
|
-
|
|
69
|
-
// Critical: block suspicious metadata / tool poisoning signals
|
|
70
|
-
if (
|
|
71
|
-
codes.includes(REASON_CODES.SUSPICIOUS_TOOL_DESCRIPTION) ||
|
|
72
|
-
codes.includes(REASON_CODES.HIDDEN_UNICODE_OR_OBFUSCATION)
|
|
73
|
-
) {
|
|
74
|
-
decision = DECISIONS.BLOCK;
|
|
75
|
-
safeAlternative = "Remove or replace the tool with a verified alternative.";
|
|
76
|
-
safeNextAction = "Quarantine this tool: do not use until metadata is reviewed and verified clean.";
|
|
77
|
-
taskStillExecutable = false;
|
|
78
|
-
approvalRequired = false;
|
|
79
|
-
return { decision, reasonCodes: codes, safeAlternative, safeNextAction, taskStillExecutable, approvalRequired, redacted: true };
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Critical level: destructive/deploy/prod/shell → block or approval_required
|
|
83
|
-
if (
|
|
84
|
-
riskLevel === "critical" ||
|
|
85
|
-
codes.includes(REASON_CODES.DESTRUCTIVE_ACTION) ||
|
|
86
|
-
codes.includes(REASON_CODES.DEPLOY_OR_PROD_ACTION) ||
|
|
87
|
-
codes.includes(REASON_CODES.SHELL_COMMAND_CAPABILITY)
|
|
88
|
-
) {
|
|
89
|
-
if (riskLevel === "critical") {
|
|
90
|
-
decision = DECISIONS.BLOCK;
|
|
91
|
-
taskStillExecutable = false;
|
|
92
|
-
safeAlternative = "Use a scoped read-only alternative if the task only needs inspection.";
|
|
93
|
-
safeNextAction = "This tool requires explicit operator approval before use. Escalate to a human reviewer.";
|
|
94
|
-
} else {
|
|
95
|
-
decision = DECISIONS.APPROVAL_REQUIRED;
|
|
96
|
-
approvalRequired = true;
|
|
97
|
-
taskStillExecutable = true;
|
|
98
|
-
safeAlternative = "Scope the tool to read-only or limit to the minimum required action.";
|
|
99
|
-
safeNextAction = "Get explicit approval before using this tool. Document the task scope.";
|
|
100
|
-
}
|
|
101
|
-
return { decision, reasonCodes: codes, safeAlternative, safeNextAction, taskStillExecutable, approvalRequired, redacted: true };
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// High level → approval_required by default
|
|
105
|
-
if (riskLevel === "high") {
|
|
106
|
-
decision = DECISIONS.APPROVAL_REQUIRED;
|
|
107
|
-
approvalRequired = true;
|
|
108
|
-
taskStillExecutable = true;
|
|
109
|
-
safeAlternative = "Prefer a scoped read-only tool if available.";
|
|
110
|
-
safeNextAction = "Approval required before using this tool. State the task scope and get sign-off.";
|
|
111
|
-
return { decision, reasonCodes: codes, safeAlternative, safeNextAction, taskStillExecutable, approvalRequired, redacted: true };
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Unknown server → approval_required
|
|
115
|
-
if (codes.includes(REASON_CODES.UNKNOWN_SERVER) || codes.includes(REASON_CODES.UNTRUSTED_SOURCE)) {
|
|
116
|
-
decision = DECISIONS.APPROVAL_REQUIRED;
|
|
117
|
-
approvalRequired = true;
|
|
118
|
-
taskStillExecutable = true;
|
|
119
|
-
safeAlternative = "Register the server with a known owner before use.";
|
|
120
|
-
safeNextAction = "Identify and verify the server source before allowing. Add to approved server list.";
|
|
121
|
-
return { decision, reasonCodes: codes, safeAlternative, safeNextAction, taskStillExecutable, approvalRequired, redacted: true };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Medium level: approval_required for broad access, warn for scoped
|
|
125
|
-
if (riskLevel === "medium") {
|
|
126
|
-
if (
|
|
127
|
-
codes.includes(REASON_CODES.BROAD_FILESYSTEM_ACCESS) ||
|
|
128
|
-
codes.includes(REASON_CODES.AUTH_REQUIRED) ||
|
|
129
|
-
codes.includes(REASON_CODES.NETWORK_ACCESS)
|
|
130
|
-
) {
|
|
131
|
-
decision = DECISIONS.APPROVAL_REQUIRED;
|
|
132
|
-
approvalRequired = true;
|
|
133
|
-
taskStillExecutable = true;
|
|
134
|
-
safeAlternative = "Scope to minimum required paths/endpoints.";
|
|
135
|
-
safeNextAction = "Confirm the tool is scoped to the task before proceeding.";
|
|
136
|
-
} else {
|
|
137
|
-
decision = DECISIONS.WARN;
|
|
138
|
-
safeNextAction = "Review tool access scope before use.";
|
|
139
|
-
}
|
|
140
|
-
return { decision, reasonCodes: codes, safeAlternative, safeNextAction, taskStillExecutable, approvalRequired, redacted: true };
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Low / read-only → allow or warn
|
|
144
|
-
if (riskLevel === "low") {
|
|
145
|
-
if (codes.includes(REASON_CODES.LOW_RISK_READ_ONLY)) {
|
|
146
|
-
decision = DECISIONS.ALLOW;
|
|
147
|
-
safeNextAction = null;
|
|
148
|
-
} else {
|
|
149
|
-
decision = DECISIONS.WARN;
|
|
150
|
-
safeNextAction = "Verify read-only scope before use.";
|
|
151
|
-
}
|
|
152
|
-
return { decision, reasonCodes: codes, safeAlternative, safeNextAction, taskStillExecutable, approvalRequired, redacted: true };
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Fallback
|
|
156
|
-
decision = DECISIONS.NOT_ENOUGH_INFORMATION;
|
|
157
|
-
safeNextAction = "Run `avorelo mcp risk` to classify before proceeding.";
|
|
158
|
-
return { decision, reasonCodes: codes, safeAlternative, safeNextAction, taskStillExecutable, approvalRequired, redacted: true };
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// ── Full policy build ─────────────────────────────────────────────────────────
|
|
162
|
-
|
|
163
|
-
function buildMcpLeastPrivilegePolicy(cwd, riskReport, options = {}) {
|
|
164
|
-
if (!riskReport || riskReport.status === "pass" && riskReport.summary?.serverCount === 0) {
|
|
165
|
-
return {
|
|
166
|
-
contract: CONTRACT,
|
|
167
|
-
schemaVersion: SCHEMA_VERSION,
|
|
168
|
-
createdAt: nowIso(),
|
|
169
|
-
status: "pass",
|
|
170
|
-
policyType: "least_privilege",
|
|
171
|
-
decisions: [],
|
|
172
|
-
summary: { allow: 0, warn: 0, approvalRequired: 0, blocked: 0, notEnoughInfo: 0 },
|
|
173
|
-
caveats: ["No MCP servers found — no policy decisions needed."],
|
|
174
|
-
previewOnly: true,
|
|
175
|
-
configNotModified: true,
|
|
176
|
-
redacted: true,
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const decisions = [];
|
|
181
|
-
const context = options.context || {};
|
|
182
|
-
|
|
183
|
-
for (const toolRisk of riskReport.tools || []) {
|
|
184
|
-
const pd = decideMcpToolPolicy(toolRisk, context, options);
|
|
185
|
-
decisions.push({
|
|
186
|
-
toolId: toolRisk.toolId,
|
|
187
|
-
serverId: toolRisk.serverId,
|
|
188
|
-
name: toolRisk.name,
|
|
189
|
-
riskLevel: toolRisk.riskLevel,
|
|
190
|
-
...pd,
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
const summary = {
|
|
195
|
-
allow: decisions.filter((d) => d.decision === DECISIONS.ALLOW).length,
|
|
196
|
-
warn: decisions.filter((d) => d.decision === DECISIONS.WARN).length,
|
|
197
|
-
approvalRequired: decisions.filter((d) => d.decision === DECISIONS.APPROVAL_REQUIRED).length,
|
|
198
|
-
blocked: decisions.filter((d) => d.decision === DECISIONS.BLOCK).length,
|
|
199
|
-
notEnoughInfo: decisions.filter((d) => d.decision === DECISIONS.NOT_ENOUGH_INFORMATION).length,
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
const overallStatus =
|
|
203
|
-
summary.blocked > 0 ? "blocked" :
|
|
204
|
-
summary.approvalRequired > 0 ? "needs_approval" :
|
|
205
|
-
summary.warn > 0 ? "warn" :
|
|
206
|
-
summary.notEnoughInfo > 0 ? "needs_review" : "pass";
|
|
207
|
-
|
|
208
|
-
return {
|
|
209
|
-
contract: CONTRACT,
|
|
210
|
-
schemaVersion: SCHEMA_VERSION,
|
|
211
|
-
createdAt: nowIso(),
|
|
212
|
-
status: overallStatus,
|
|
213
|
-
policyType: "least_privilege",
|
|
214
|
-
decisions,
|
|
215
|
-
summary,
|
|
216
|
-
caveats: riskReport.caveats || [],
|
|
217
|
-
previewOnly: true,
|
|
218
|
-
configNotModified: true,
|
|
219
|
-
redacted: true,
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// ── Preview build ─────────────────────────────────────────────────────────────
|
|
224
|
-
|
|
225
|
-
function buildMcpPolicyPreview(cwd, policy, options = {}) {
|
|
226
|
-
return {
|
|
227
|
-
contract: "avorelo.mcpPolicyPreview.v1",
|
|
228
|
-
schemaVersion: 1,
|
|
229
|
-
createdAt: nowIso(),
|
|
230
|
-
status: policy.status,
|
|
231
|
-
previewOnly: true,
|
|
232
|
-
configNotModified: true,
|
|
233
|
-
note: "This preview shows what policy WOULD be applied. No MCP config has been modified.",
|
|
234
|
-
summary: policy.summary,
|
|
235
|
-
topDecisions: (policy.decisions || [])
|
|
236
|
-
.filter((d) => ["block", "approval_required"].includes(d.decision))
|
|
237
|
-
.slice(0, 10)
|
|
238
|
-
.map((d) => ({
|
|
239
|
-
name: d.name,
|
|
240
|
-
riskLevel: d.riskLevel,
|
|
241
|
-
decision: d.decision,
|
|
242
|
-
safeNextAction: d.safeNextAction,
|
|
243
|
-
reasonCodes: (d.reasonCodes || []).slice(0, 4),
|
|
244
|
-
})),
|
|
245
|
-
caveats: policy.caveats || [],
|
|
246
|
-
redacted: true,
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
function writeMcpPolicy(cwd, policy) {
|
|
251
|
-
const absPath = path.join(cwd, POLICY_REL);
|
|
252
|
-
safeWriteJson(absPath, policy);
|
|
253
|
-
return POLICY_REL;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
function writeMcpPolicyPreview(cwd, preview) {
|
|
257
|
-
const absPath = path.join(cwd, PREVIEW_REL);
|
|
258
|
-
safeWriteJson(absPath, preview);
|
|
259
|
-
return PREVIEW_REL;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
function formatMcpPolicyText(policy, options = {}) {
|
|
263
|
-
const { debug } = options;
|
|
264
|
-
const s = policy.summary || {};
|
|
265
|
-
const lines = [
|
|
266
|
-
`MCP Least-Privilege Policy [${policy.status}]`,
|
|
267
|
-
`Allow: ${s.allow} · Warn: ${s.warn} · Approval: ${s.approvalRequired} · Blocked: ${s.blocked}`,
|
|
268
|
-
`Preview only — config not modified.`,
|
|
269
|
-
];
|
|
270
|
-
|
|
271
|
-
if (debug && policy.decisions && policy.decisions.length > 0) {
|
|
272
|
-
for (const d of policy.decisions) {
|
|
273
|
-
lines.push(` ${d.name || d.toolId} → ${d.decision} [${d.riskLevel}]`);
|
|
274
|
-
if (d.safeNextAction) lines.push(` Next: ${d.safeNextAction}`);
|
|
275
|
-
}
|
|
276
|
-
} else if (s.blocked > 0 || s.approvalRequired > 0) {
|
|
277
|
-
const topIssues = (policy.decisions || [])
|
|
278
|
-
.filter((d) => ["block", "approval_required"].includes(d.decision))
|
|
279
|
-
.slice(0, 3);
|
|
280
|
-
for (const d of topIssues) {
|
|
281
|
-
lines.push(` ${d.name || d.toolId} → ${d.decision}`);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
lines.push(`Next: Run \`avorelo mcp doctor --json\` for full governance status.`);
|
|
286
|
-
return lines.join("\n");
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// ── Exports ───────────────────────────────────────────────────────────────────
|
|
290
|
-
|
|
291
|
-
module.exports = {
|
|
292
|
-
CONTRACT,
|
|
293
|
-
SCHEMA_VERSION,
|
|
294
|
-
POLICY_REL,
|
|
295
|
-
PREVIEW_REL,
|
|
296
|
-
DECISIONS,
|
|
297
|
-
buildMcpLeastPrivilegePolicy,
|
|
298
|
-
decideMcpToolPolicy,
|
|
299
|
-
buildMcpPolicyPreview,
|
|
300
|
-
writeMcpPolicy,
|
|
301
|
-
writeMcpPolicyPreview,
|
|
302
|
-
formatMcpPolicyText,
|
|
303
|
-
};
|
|
@@ -1,388 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
// ── MCP Tool Inventory ────────────────────────────────────────────────────────
|
|
4
|
-
//
|
|
5
|
-
// Contract: avorelo.mcpToolInventory.v1
|
|
6
|
-
//
|
|
7
|
-
// Discovers MCP config files locally, parses them statically, and builds a
|
|
8
|
-
// redacted inventory of servers and tool surfaces.
|
|
9
|
-
//
|
|
10
|
-
// SAFETY RULES (never violated):
|
|
11
|
-
// - Does NOT execute MCP servers
|
|
12
|
-
// - Does NOT call MCP tools
|
|
13
|
-
// - Does NOT resolve remote URLs
|
|
14
|
-
// - Does NOT read raw env values / secrets / auth headers
|
|
15
|
-
// - Normalizes paths, handles invalid JSON gracefully (needs_review, not crash)
|
|
16
|
-
|
|
17
|
-
const fs = require("node:fs");
|
|
18
|
-
const path = require("node:path");
|
|
19
|
-
|
|
20
|
-
const CONTRACT = "avorelo.mcpToolInventory.v1";
|
|
21
|
-
const SCHEMA_VERSION = 1;
|
|
22
|
-
|
|
23
|
-
const INVENTORY_DIR_REL = ".claude/cco/orchestration/mcp-tool-governance";
|
|
24
|
-
const INVENTORY_REL = `${INVENTORY_DIR_REL}/latest-inventory.json`;
|
|
25
|
-
|
|
26
|
-
// Known MCP config file locations (relative to cwd), in priority order
|
|
27
|
-
const MCP_CONFIG_CANDIDATES = [
|
|
28
|
-
".mcp.json",
|
|
29
|
-
"mcp.json",
|
|
30
|
-
".claude/settings.json",
|
|
31
|
-
".claude/settings.local.json",
|
|
32
|
-
".cursor/mcp.json",
|
|
33
|
-
".vscode/mcp.json",
|
|
34
|
-
".codex/mcp.json",
|
|
35
|
-
"AGENTS.md", // reference-only: may mention MCP servers
|
|
36
|
-
"CLAUDE.md", // reference-only
|
|
37
|
-
];
|
|
38
|
-
|
|
39
|
-
// Known MCP config formats we can parse structurally
|
|
40
|
-
const SUPPORTED_FORMATS = new Set([
|
|
41
|
-
".mcp.json",
|
|
42
|
-
"mcp.json",
|
|
43
|
-
".claude/settings.json",
|
|
44
|
-
".claude/settings.local.json",
|
|
45
|
-
".cursor/mcp.json",
|
|
46
|
-
".vscode/mcp.json",
|
|
47
|
-
".codex/mcp.json",
|
|
48
|
-
]);
|
|
49
|
-
|
|
50
|
-
// Patterns that look like secrets / env references (for redaction)
|
|
51
|
-
const SECRET_REF_PATTERN = /\$\{?[A-Z_]{4,}\}?|sk-[A-Za-z0-9]{16,}|ghp_[A-Za-z0-9]{20,}|AKIA[0-9A-Z]{16}|password|api[_-]?key|auth[_-]?token|bearer\s/i;
|
|
52
|
-
|
|
53
|
-
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
54
|
-
|
|
55
|
-
function nowIso() {
|
|
56
|
-
return new Date().toISOString();
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function safeWriteJson(filePath, data) {
|
|
60
|
-
try {
|
|
61
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
62
|
-
fs.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}\n`, "utf8");
|
|
63
|
-
} catch {
|
|
64
|
-
// non-fatal
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function safeReadFile(absPath) {
|
|
69
|
-
try {
|
|
70
|
-
return fs.readFileSync(absPath, "utf8").replace(/^/, "");
|
|
71
|
-
} catch {
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function safeParseJson(text) {
|
|
77
|
-
try {
|
|
78
|
-
return { ok: true, data: JSON.parse(text) };
|
|
79
|
-
} catch (e) {
|
|
80
|
-
return { ok: false, error: e.message };
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function redactValue(value) {
|
|
85
|
-
if (typeof value !== "string") return "[non-string]";
|
|
86
|
-
if (SECRET_REF_PATTERN.test(value)) return "[redacted]";
|
|
87
|
-
// Redact long tokens / base64-like blobs
|
|
88
|
-
if (value.length > 80 && /^[A-Za-z0-9+/=_-]{40,}$/.test(value.trim())) return "[redacted-long-token]";
|
|
89
|
-
// Trim long values
|
|
90
|
-
if (value.length > 120) return value.slice(0, 60) + "…[truncated]";
|
|
91
|
-
return value;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function summarizeArgs(args) {
|
|
95
|
-
if (!Array.isArray(args) || args.length === 0) return "none";
|
|
96
|
-
return args.map((a) => {
|
|
97
|
-
if (typeof a === "string") return redactValue(a);
|
|
98
|
-
return "[non-string-arg]";
|
|
99
|
-
}).join(" ");
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function summarizeEnv(envObj) {
|
|
103
|
-
if (!envObj || typeof envObj !== "object") return "none";
|
|
104
|
-
const keys = Object.keys(envObj);
|
|
105
|
-
if (keys.length === 0) return "none";
|
|
106
|
-
// Never log values — keys only, with count
|
|
107
|
-
return `${keys.length} env vars: ${keys.map((k) => `${k}=[redacted]`).join(", ")}`;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function classifyTransport(serverDef) {
|
|
111
|
-
if (!serverDef || typeof serverDef !== "object") return "unknown";
|
|
112
|
-
if (serverDef.transport === "stdio" || serverDef.command) return "stdio";
|
|
113
|
-
if (serverDef.transport === "http" || serverDef.url?.startsWith("http")) return "http";
|
|
114
|
-
if (serverDef.transport === "sse") return "sse";
|
|
115
|
-
return "unknown";
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function makeSummaryRef(value, label) {
|
|
119
|
-
if (!value) return null;
|
|
120
|
-
const str = typeof value === "string" ? value : JSON.stringify(value);
|
|
121
|
-
return `[${label}: ${redactValue(str.length > 80 ? str.slice(0, 40) + "…" : str)}]`;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// ── Config discovery ──────────────────────────────────────────────────────────
|
|
125
|
-
|
|
126
|
-
function discoverMcpConfigFiles(cwd, options = {}) {
|
|
127
|
-
const found = [];
|
|
128
|
-
const unsupported = [];
|
|
129
|
-
for (const rel of MCP_CONFIG_CANDIDATES) {
|
|
130
|
-
const absPath = path.join(cwd, rel);
|
|
131
|
-
try {
|
|
132
|
-
if (!fs.existsSync(absPath)) continue;
|
|
133
|
-
} catch {
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
const isSupported = SUPPORTED_FORMATS.has(rel);
|
|
137
|
-
const isMarkdown = rel.endsWith(".md");
|
|
138
|
-
if (isMarkdown) {
|
|
139
|
-
unsupported.push({ rel, reason: "markdown_reference_only" });
|
|
140
|
-
continue;
|
|
141
|
-
}
|
|
142
|
-
const stat = (() => { try { return fs.statSync(absPath); } catch { return null; } })();
|
|
143
|
-
if (!stat || !stat.isFile()) continue;
|
|
144
|
-
found.push({ rel, absPath, format: isSupported ? "json" : "unknown", supported: isSupported });
|
|
145
|
-
}
|
|
146
|
-
return { found, unsupported };
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// ── Config parsing ────────────────────────────────────────────────────────────
|
|
150
|
-
|
|
151
|
-
function parseMcpConfigFile(cwd, filePath, options = {}) {
|
|
152
|
-
const absPath = path.isAbsolute(filePath) ? filePath : path.join(cwd, filePath);
|
|
153
|
-
const relPath = path.isAbsolute(filePath) ? path.relative(cwd, filePath) : filePath;
|
|
154
|
-
const raw = safeReadFile(absPath);
|
|
155
|
-
if (raw === null) {
|
|
156
|
-
return {
|
|
157
|
-
status: "not_found",
|
|
158
|
-
configRef: relPath,
|
|
159
|
-
servers: [],
|
|
160
|
-
caveats: [`Config file not found: ${relPath}`],
|
|
161
|
-
redacted: true,
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const parsed = safeParseJson(raw);
|
|
166
|
-
if (!parsed.ok) {
|
|
167
|
-
return {
|
|
168
|
-
status: "invalid_json",
|
|
169
|
-
configRef: relPath,
|
|
170
|
-
servers: [],
|
|
171
|
-
caveats: [`Invalid JSON in ${relPath}: ${parsed.error}`],
|
|
172
|
-
redacted: true,
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const data = parsed.data;
|
|
177
|
-
const servers = [];
|
|
178
|
-
const caveats = [];
|
|
179
|
-
|
|
180
|
-
// Extract MCP servers from known config shapes
|
|
181
|
-
// Shape 1: { mcpServers: { name: { command, args, env, ... } } }
|
|
182
|
-
// Shape 2: { servers: { name: { ... } } }
|
|
183
|
-
// Shape 3: { mcp: { servers: { ... } } }
|
|
184
|
-
const serverMap =
|
|
185
|
-
(data && data.mcpServers) ||
|
|
186
|
-
(data && data.servers) ||
|
|
187
|
-
(data && data.mcp && data.mcp.servers) ||
|
|
188
|
-
null;
|
|
189
|
-
|
|
190
|
-
if (!serverMap || typeof serverMap !== "object" || Array.isArray(serverMap)) {
|
|
191
|
-
caveats.push(`No mcpServers/servers block found in ${relPath}.`);
|
|
192
|
-
return { status: "no_servers", configRef: relPath, servers: [], caveats, redacted: true };
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const names = Object.keys(serverMap);
|
|
196
|
-
for (const name of names) {
|
|
197
|
-
const def = serverMap[name];
|
|
198
|
-
if (!def || typeof def !== "object") {
|
|
199
|
-
caveats.push(`Server "${name}" has invalid definition (not an object).`);
|
|
200
|
-
continue;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
const transport = classifyTransport(def);
|
|
204
|
-
const commandRef = def.command ? makeSummaryRef(def.command, "cmd") : null;
|
|
205
|
-
const argsSummary = summarizeArgs(def.args);
|
|
206
|
-
const envRefSummary = summarizeEnv(def.env);
|
|
207
|
-
const urlRef = def.url ? makeSummaryRef(def.url, "url") : null;
|
|
208
|
-
|
|
209
|
-
// Derive tools from schema (if present in config)
|
|
210
|
-
const rawTools = (def.tools && Array.isArray(def.tools)) ? def.tools : [];
|
|
211
|
-
const parsedTools = rawTools.map((t, i) => parseToolDef(t, name, i));
|
|
212
|
-
|
|
213
|
-
servers.push({
|
|
214
|
-
serverId: `server-${name}`,
|
|
215
|
-
name,
|
|
216
|
-
sourceConfigRef: relPath,
|
|
217
|
-
transport,
|
|
218
|
-
commandRef,
|
|
219
|
-
argsSummary,
|
|
220
|
-
envRefSummary,
|
|
221
|
-
urlRef,
|
|
222
|
-
trustedSource: "unknown",
|
|
223
|
-
owner: "unknown",
|
|
224
|
-
tools: parsedTools,
|
|
225
|
-
redacted: true,
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return { status: "ok", configRef: relPath, servers, caveats, redacted: true };
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
function parseToolDef(toolDef, serverId, index) {
|
|
233
|
-
if (!toolDef || typeof toolDef !== "object") {
|
|
234
|
-
return {
|
|
235
|
-
toolId: `${serverId}-tool-${index}`,
|
|
236
|
-
serverId,
|
|
237
|
-
name: `unknown-tool-${index}`,
|
|
238
|
-
descriptionSummary: null,
|
|
239
|
-
schemaSummary: null,
|
|
240
|
-
declaredReadOnly: false,
|
|
241
|
-
declaredDestructive: false,
|
|
242
|
-
declaredNetwork: false,
|
|
243
|
-
declaredFilesystem: false,
|
|
244
|
-
declaredAuth: false,
|
|
245
|
-
metadataRiskSignals: ["MALFORMED_TOOL_DEF"],
|
|
246
|
-
redacted: true,
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const name = toolDef.name || `tool-${index}`;
|
|
251
|
-
const desc = toolDef.description || toolDef.desc || "";
|
|
252
|
-
const schema = toolDef.inputSchema || toolDef.input_schema || toolDef.parameters || null;
|
|
253
|
-
|
|
254
|
-
const descSummary = desc.length > 0 ? (desc.length > 200 ? desc.slice(0, 100) + "…[truncated]" : desc) : null;
|
|
255
|
-
const schemaSummary = schema ? `properties:${Object.keys(schema.properties || {}).length}` : null;
|
|
256
|
-
|
|
257
|
-
return {
|
|
258
|
-
toolId: `${serverId}-${name}`,
|
|
259
|
-
serverId,
|
|
260
|
-
name,
|
|
261
|
-
descriptionSummary: descSummary,
|
|
262
|
-
schemaSummary,
|
|
263
|
-
declaredReadOnly: Boolean(toolDef.readOnly || toolDef.readonly),
|
|
264
|
-
declaredDestructive: Boolean(toolDef.destructive || toolDef.dangerous),
|
|
265
|
-
declaredNetwork: Boolean(toolDef.network || toolDef.http),
|
|
266
|
-
declaredFilesystem: Boolean(toolDef.filesystem || toolDef.files),
|
|
267
|
-
declaredAuth: Boolean(toolDef.auth || toolDef.authorization),
|
|
268
|
-
metadataRiskSignals: [],
|
|
269
|
-
redacted: true,
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// ── Full inventory build ──────────────────────────────────────────────────────
|
|
274
|
-
|
|
275
|
-
function buildMcpToolInventory(cwd, options = {}) {
|
|
276
|
-
const { found, unsupported } = discoverMcpConfigFiles(cwd, options);
|
|
277
|
-
const configFiles = [];
|
|
278
|
-
const allServers = [];
|
|
279
|
-
const allTools = [];
|
|
280
|
-
const caveats = [];
|
|
281
|
-
|
|
282
|
-
if (found.length === 0 && unsupported.length === 0) {
|
|
283
|
-
return {
|
|
284
|
-
contract: CONTRACT,
|
|
285
|
-
schemaVersion: SCHEMA_VERSION,
|
|
286
|
-
createdAt: nowIso(),
|
|
287
|
-
status: "none",
|
|
288
|
-
configFiles: [],
|
|
289
|
-
servers: [],
|
|
290
|
-
tools: [],
|
|
291
|
-
unsupportedConfigs: unsupported,
|
|
292
|
-
caveats: ["No MCP config files found. This is safe — no tool surfaces to govern."],
|
|
293
|
-
redacted: true,
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
for (const configFile of found) {
|
|
298
|
-
const parsed = parseMcpConfigFile(cwd, configFile.rel, options);
|
|
299
|
-
configFiles.push({ rel: configFile.rel, status: parsed.status });
|
|
300
|
-
caveats.push(...(parsed.caveats || []));
|
|
301
|
-
|
|
302
|
-
for (const server of parsed.servers) {
|
|
303
|
-
allServers.push(server);
|
|
304
|
-
for (const tool of server.tools || []) {
|
|
305
|
-
allTools.push(tool);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const hasInvalidJson = configFiles.some((f) => f.status === "invalid_json");
|
|
311
|
-
const hasNoServers = configFiles.every((f) => ["no_servers", "not_found"].includes(f.status));
|
|
312
|
-
const status = hasInvalidJson || unsupported.length > 0
|
|
313
|
-
? "needs_review"
|
|
314
|
-
: hasNoServers && allServers.length === 0
|
|
315
|
-
? "none"
|
|
316
|
-
: allServers.length > 0
|
|
317
|
-
? "ready"
|
|
318
|
-
: "partial";
|
|
319
|
-
|
|
320
|
-
return {
|
|
321
|
-
contract: CONTRACT,
|
|
322
|
-
schemaVersion: SCHEMA_VERSION,
|
|
323
|
-
createdAt: nowIso(),
|
|
324
|
-
status,
|
|
325
|
-
configFiles,
|
|
326
|
-
servers: allServers,
|
|
327
|
-
tools: allTools,
|
|
328
|
-
unsupportedConfigs: unsupported,
|
|
329
|
-
caveats: caveats.length > 0 ? caveats : [],
|
|
330
|
-
redacted: true,
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
function writeMcpToolInventory(cwd, inventory) {
|
|
335
|
-
const absPath = path.join(cwd, INVENTORY_REL);
|
|
336
|
-
safeWriteJson(absPath, inventory);
|
|
337
|
-
return INVENTORY_REL;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
function buildMcpToolInventorySurface(cwd, options = {}) {
|
|
341
|
-
const inventory = buildMcpToolInventory(cwd, options);
|
|
342
|
-
writeMcpToolInventory(cwd, inventory);
|
|
343
|
-
return inventory;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
function formatMcpToolInventoryText(inventory, options = {}) {
|
|
347
|
-
const { debug } = options;
|
|
348
|
-
const lines = [
|
|
349
|
-
`MCP Tool Inventory [${inventory.status}]`,
|
|
350
|
-
`Config files: ${inventory.configFiles.length} · Servers: ${inventory.servers.length} · Tools: ${inventory.tools.length}`,
|
|
351
|
-
];
|
|
352
|
-
|
|
353
|
-
if (inventory.servers.length > 0) {
|
|
354
|
-
for (const s of inventory.servers) {
|
|
355
|
-
lines.push(` Server: ${s.name} [${s.transport}] trust=${s.trustedSource}`);
|
|
356
|
-
if (debug && s.tools && s.tools.length > 0) {
|
|
357
|
-
for (const t of s.tools) {
|
|
358
|
-
lines.push(` Tool: ${t.name}`);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
if (inventory.unsupportedConfigs.length > 0) {
|
|
365
|
-
lines.push(`Unsupported configs: ${inventory.unsupportedConfigs.map((u) => u.rel).join(", ")}`);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
if (inventory.caveats && inventory.caveats.length > 0 && debug) {
|
|
369
|
-
lines.push(`Caveats: ${inventory.caveats.join("; ")}`);
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
lines.push(`Next: Run \`avorelo mcp risk --json\` to classify tool risk.`);
|
|
373
|
-
return lines.join("\n");
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
// ── Exports ───────────────────────────────────────────────────────────────────
|
|
377
|
-
|
|
378
|
-
module.exports = {
|
|
379
|
-
CONTRACT,
|
|
380
|
-
SCHEMA_VERSION,
|
|
381
|
-
INVENTORY_REL,
|
|
382
|
-
discoverMcpConfigFiles,
|
|
383
|
-
parseMcpConfigFile,
|
|
384
|
-
buildMcpToolInventory,
|
|
385
|
-
writeMcpToolInventory,
|
|
386
|
-
buildMcpToolInventorySurface,
|
|
387
|
-
formatMcpToolInventoryText,
|
|
388
|
-
};
|
|
Binary file
|