@plasius/ai-mcp 0.1.2 → 0.1.4
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 +34 -1
- package/README.md +43 -4
- package/dist/index.cjs +144 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +54 -2
- package/dist/index.d.ts +54 -2
- package/dist/index.js +136 -3
- package/dist/index.js.map +1 -1
- package/docs/adrs/adr-0002-mcp-allowlist-risk-audit.md +24 -0
- package/docs/adrs/index.md +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -18,6 +18,37 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
18
18
|
- **Security**
|
|
19
19
|
- (placeholder)
|
|
20
20
|
|
|
21
|
+
## [0.1.4] - 2026-06-22
|
|
22
|
+
|
|
23
|
+
- **Added**
|
|
24
|
+
- (placeholder)
|
|
25
|
+
|
|
26
|
+
- **Changed**
|
|
27
|
+
- (placeholder)
|
|
28
|
+
|
|
29
|
+
- **Fixed**
|
|
30
|
+
- (placeholder)
|
|
31
|
+
|
|
32
|
+
- **Security**
|
|
33
|
+
- (placeholder)
|
|
34
|
+
|
|
35
|
+
## [0.1.3] - 2026-05-20
|
|
36
|
+
|
|
37
|
+
- **Added**
|
|
38
|
+
- Added MCP tool risk classes, actor-role-based allowlist resolution, and audit metadata.
|
|
39
|
+
- Added deterministic tool-allowlist result contracts with blocked/allowed reason codes.
|
|
40
|
+
|
|
41
|
+
- **Changed**
|
|
42
|
+
- Updated the MCP feature flag to align with family orchestration gating.
|
|
43
|
+
|
|
44
|
+
- **Fixed**
|
|
45
|
+
- Release automation now prepares version/changelog updates on a release PR before publishing from protected `main`.
|
|
46
|
+
- (placeholder)
|
|
47
|
+
|
|
48
|
+
- **Security**
|
|
49
|
+
- Added restricted tool blocking and unsafe-role risk gating for safer MCP defaults.
|
|
50
|
+
- Sensitive tools are now denied for player roles unless an operator/admin context authorizes them.
|
|
51
|
+
|
|
21
52
|
## [0.1.2] - 2026-05-13
|
|
22
53
|
|
|
23
54
|
- **Added**
|
|
@@ -38,5 +69,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
38
69
|
- Added initial public package scaffold with governance, legal, docs, build, test, and pack-check baselines.
|
|
39
70
|
|
|
40
71
|
|
|
41
|
-
[0.1.1]: https://github.com/Plasius-LTD/ai-mcp/releases/tag/v0.1.1
|
|
42
72
|
[0.1.2]: https://github.com/Plasius-LTD/ai-mcp/releases/tag/v0.1.2
|
|
73
|
+
[0.1.1]: https://github.com/Plasius-LTD/ai-mcp/releases/tag/v0.1.1
|
|
74
|
+
[0.1.3]: https://github.com/Plasius-LTD/ai-mcp/releases/tag/v0.1.3
|
|
75
|
+
[0.1.4]: https://github.com/Plasius-LTD/ai-mcp/releases/tag/v0.1.4
|
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# @plasius/ai-mcp
|
|
2
2
|
|
|
3
|
-
MCP tool registry, per-call allowlist, and tool audit contracts for Plasius
|
|
3
|
+
MCP tool registry, per-call allowlist, and tool audit contracts for Plasius AI orchestration.
|
|
4
4
|
|
|
5
5
|
## Scope
|
|
6
6
|
|
|
7
|
-
This package is part of the layered `@plasius/ai-*` package family. It
|
|
7
|
+
This package is part of the layered `@plasius/ai-*` package family. It defines the external contracts for MCP tool registration, per-call allowlisting, role-gated risk decisions, and audit metadata.
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
@@ -12,12 +12,43 @@ This package is part of the layered `@plasius/ai-*` package family. It is intent
|
|
|
12
12
|
npm install @plasius/ai-mcp
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
+
## Contracts
|
|
16
|
+
|
|
17
|
+
- `AI_MCP_FEATURE_FLAGS` declares the feature flags that gate MCP behavior.
|
|
18
|
+
- `resolveAiMcpToolAllowlist` evaluates requested tool IDs against registry descriptors, feature flags, and actor roles.
|
|
19
|
+
- `isAiMcpToolAllowed` and `isAiMcpToolRiskAllowed` provide lightweight policy predicates for callers.
|
|
20
|
+
- `packageDescriptor` exposes package name, primary flag, env prefix, and summary.
|
|
21
|
+
|
|
15
22
|
## Usage
|
|
16
23
|
|
|
17
24
|
```ts
|
|
18
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
AI_MCP_FEATURE_FLAGS,
|
|
27
|
+
resolveAiMcpToolAllowlist,
|
|
28
|
+
} from "@plasius/ai-mcp";
|
|
29
|
+
|
|
30
|
+
const result = resolveAiMcpToolAllowlist({
|
|
31
|
+
requestedTools: ["rag.search", "admin.kill-switch"],
|
|
32
|
+
actorRole: "operator",
|
|
33
|
+
featureFlags: {
|
|
34
|
+
[AI_MCP_FEATURE_FLAGS.mcp]: true,
|
|
35
|
+
},
|
|
36
|
+
toolRegistry: [
|
|
37
|
+
{
|
|
38
|
+
toolId: "rag.search",
|
|
39
|
+
toolName: "RAG Search",
|
|
40
|
+
riskClass: "safe",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
toolId: "admin.kill-switch",
|
|
44
|
+
toolName: "Admin Kill Switch",
|
|
45
|
+
riskClass: "restricted",
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
});
|
|
19
49
|
|
|
20
|
-
console.log(
|
|
50
|
+
console.log(result.allowedTools);
|
|
51
|
+
console.log(result.blockedTools);
|
|
21
52
|
```
|
|
22
53
|
|
|
23
54
|
## Development
|
|
@@ -30,6 +61,14 @@ npm run test:coverage
|
|
|
30
61
|
npm run pack:check
|
|
31
62
|
```
|
|
32
63
|
|
|
64
|
+
## Release Workflow
|
|
65
|
+
|
|
66
|
+
Protected `main` releases use a two-step flow:
|
|
67
|
+
|
|
68
|
+
1. Run `.github/workflows/cd.yml` with `bump=patch|minor|major` to open or refresh a `release/vX.Y.Z` prep PR.
|
|
69
|
+
2. Merge that PR to `main`.
|
|
70
|
+
3. Rerun `.github/workflows/cd.yml` on `main` with `bump=none` to tag, draft the GitHub release, and publish to npm.
|
|
71
|
+
|
|
33
72
|
## Governance
|
|
34
73
|
|
|
35
74
|
- Security policy: [SECURITY.md](./SECURITY.md)
|
package/dist/index.cjs
CHANGED
|
@@ -21,25 +21,165 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
AI_MCP_ENV_PREFIX: () => AI_MCP_ENV_PREFIX,
|
|
24
|
+
AI_MCP_FEATURE_FLAGS: () => AI_MCP_FEATURE_FLAGS,
|
|
24
25
|
AI_MCP_FEATURE_FLAG_ID: () => AI_MCP_FEATURE_FLAG_ID,
|
|
25
26
|
AI_MCP_PACKAGE: () => AI_MCP_PACKAGE,
|
|
26
|
-
|
|
27
|
+
AI_MCP_RESOLUTION_SOURCES: () => AI_MCP_RESOLUTION_SOURCES,
|
|
28
|
+
AI_MCP_TOOL_RISK_CLASSES: () => AI_MCP_TOOL_RISK_CLASSES,
|
|
29
|
+
AI_MCP_TOOL_ROLES: () => AI_MCP_TOOL_ROLES,
|
|
30
|
+
isAiMcpToolAllowed: () => isAiMcpToolAllowed,
|
|
31
|
+
isAiMcpToolRiskAllowed: () => isAiMcpToolRiskAllowed,
|
|
32
|
+
packageDescriptor: () => packageDescriptor,
|
|
33
|
+
resolveAiMcpToolAllowlist: () => resolveAiMcpToolAllowlist
|
|
27
34
|
});
|
|
28
35
|
module.exports = __toCommonJS(index_exports);
|
|
29
36
|
var AI_MCP_PACKAGE = "@plasius/ai-mcp";
|
|
30
|
-
var AI_MCP_FEATURE_FLAG_ID = "ai.mcp.enabled";
|
|
37
|
+
var AI_MCP_FEATURE_FLAG_ID = "ai.mcp-rag.enabled";
|
|
31
38
|
var AI_MCP_ENV_PREFIX = "AI_MCP";
|
|
39
|
+
var AI_MCP_FEATURE_FLAGS = {
|
|
40
|
+
mcp: AI_MCP_FEATURE_FLAG_ID
|
|
41
|
+
};
|
|
42
|
+
var AI_MCP_TOOL_RISK_CLASSES = [
|
|
43
|
+
"safe",
|
|
44
|
+
"sensitive",
|
|
45
|
+
"privileged",
|
|
46
|
+
"restricted"
|
|
47
|
+
];
|
|
48
|
+
var AI_MCP_TOOL_ROLES = ["system", "admin", "operator", "player"];
|
|
49
|
+
var AI_MCP_RESOLUTION_SOURCES = [
|
|
50
|
+
"policy",
|
|
51
|
+
"policy-disabled",
|
|
52
|
+
"policy-allow-empty"
|
|
53
|
+
];
|
|
54
|
+
function nowIsoString() {
|
|
55
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
56
|
+
}
|
|
57
|
+
function normalizeToolIds(values) {
|
|
58
|
+
const normalized = values.map((value) => value.trim()).filter((value) => value.length > 0);
|
|
59
|
+
return Array.from(new Set(normalized));
|
|
60
|
+
}
|
|
61
|
+
function isAiMcpFeatureEnabled(featureFlag, snapshot = {}) {
|
|
62
|
+
return snapshot[featureFlag] === true;
|
|
63
|
+
}
|
|
64
|
+
function isAllowedForRole(riskClass, role) {
|
|
65
|
+
if (role === "system") {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
if (riskClass === "safe") {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
if (riskClass === "restricted") {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
if (riskClass === "privileged") {
|
|
75
|
+
return role === "admin" || role === "operator";
|
|
76
|
+
}
|
|
77
|
+
return role === "admin" || role === "operator";
|
|
78
|
+
}
|
|
79
|
+
function resolveAiMcpToolAllowlist(input) {
|
|
80
|
+
const featureEnabled = isAiMcpFeatureEnabled(
|
|
81
|
+
AI_MCP_FEATURE_FLAGS.mcp,
|
|
82
|
+
input.featureFlags
|
|
83
|
+
);
|
|
84
|
+
const actorRole = input.actorRole ?? "player";
|
|
85
|
+
const requestedTools = normalizeToolIds(input.requestedTools);
|
|
86
|
+
const featureFlagIds = featureEnabled ? [AI_MCP_FEATURE_FLAGS.mcp] : [];
|
|
87
|
+
const reasonCodes = [...input.reasonCodes ?? []];
|
|
88
|
+
const registryById = new Map(
|
|
89
|
+
(input.toolRegistry ?? []).map((tool) => [tool.toolId, tool])
|
|
90
|
+
);
|
|
91
|
+
if (!featureEnabled) {
|
|
92
|
+
reasonCodes.push("mcp-feature-disabled");
|
|
93
|
+
return {
|
|
94
|
+
requestedTools,
|
|
95
|
+
allowedTools: [],
|
|
96
|
+
blockedTools: requestedTools,
|
|
97
|
+
needsEscalation: true,
|
|
98
|
+
reasonCodes,
|
|
99
|
+
source: "policy-disabled",
|
|
100
|
+
enabledFeatureFlags: featureFlagIds,
|
|
101
|
+
audit: {
|
|
102
|
+
policyId: input.policyId ?? "mcp-policy-v1",
|
|
103
|
+
policyVersion: input.policyVersion ?? "2026-05-01",
|
|
104
|
+
correlationId: input.correlationId ?? crypto.randomUUID(),
|
|
105
|
+
requestId: input.requestId,
|
|
106
|
+
actorId: input.actorId,
|
|
107
|
+
actorRole,
|
|
108
|
+
evaluatedAtUtc: nowIsoString(),
|
|
109
|
+
result: "deny"
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
const allowedTools = [];
|
|
114
|
+
const blockedTools = [];
|
|
115
|
+
for (const requestedTool of requestedTools) {
|
|
116
|
+
const tool = registryById.get(requestedTool);
|
|
117
|
+
if (!tool) {
|
|
118
|
+
blockedTools.push(requestedTool);
|
|
119
|
+
reasonCodes.push(`tool-not-registered:${requestedTool}`);
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (!isAllowedForRole(tool.riskClass, actorRole)) {
|
|
123
|
+
blockedTools.push(requestedTool);
|
|
124
|
+
reasonCodes.push(`tool-risk-restricted:${requestedTool}`);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
allowedTools.push(requestedTool);
|
|
128
|
+
}
|
|
129
|
+
if (allowedTools.length === 0) {
|
|
130
|
+
reasonCodes.push("mcp-allowlist-deny-all");
|
|
131
|
+
}
|
|
132
|
+
if (requestedTools.length === 0) {
|
|
133
|
+
reasonCodes.push("mcp-allowlist-empty-request");
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
requestedTools,
|
|
137
|
+
allowedTools,
|
|
138
|
+
blockedTools,
|
|
139
|
+
needsEscalation: blockedTools.length > 0 && allowedTools.length > 0,
|
|
140
|
+
reasonCodes,
|
|
141
|
+
source: requestedTools.length > 0 ? "policy" : "policy-allow-empty",
|
|
142
|
+
enabledFeatureFlags: featureFlagIds,
|
|
143
|
+
audit: {
|
|
144
|
+
policyId: input.policyId ?? "mcp-policy-v1",
|
|
145
|
+
policyVersion: input.policyVersion ?? "2026-05-01",
|
|
146
|
+
correlationId: input.correlationId ?? crypto.randomUUID(),
|
|
147
|
+
requestId: input.requestId,
|
|
148
|
+
actorId: input.actorId,
|
|
149
|
+
actorRole,
|
|
150
|
+
evaluatedAtUtc: nowIsoString(),
|
|
151
|
+
result: blockedTools.length === requestedTools.length ? "deny" : blockedTools.length > 0 ? "escalate" : "allow"
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function isAiMcpToolRiskAllowed(riskClass, actorRole) {
|
|
156
|
+
return isAllowedForRole(riskClass, actorRole);
|
|
157
|
+
}
|
|
158
|
+
function isAiMcpToolAllowed(toolId, actorRole, registry) {
|
|
159
|
+
const tool = registry.find((candidate) => candidate.toolId === toolId);
|
|
160
|
+
if (!tool) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
return isAllowedForRole(tool.riskClass, actorRole);
|
|
164
|
+
}
|
|
32
165
|
var packageDescriptor = Object.freeze({
|
|
33
166
|
packageName: AI_MCP_PACKAGE,
|
|
34
167
|
featureFlagId: AI_MCP_FEATURE_FLAG_ID,
|
|
35
168
|
envPrefix: AI_MCP_ENV_PREFIX,
|
|
36
|
-
summary: "MCP tool registry, per-call allowlist, and
|
|
169
|
+
summary: "MCP tool registry, per-call allowlist, and audit contracts for Plasius AI orchestration."
|
|
37
170
|
});
|
|
38
171
|
// Annotate the CommonJS export names for ESM import in node:
|
|
39
172
|
0 && (module.exports = {
|
|
40
173
|
AI_MCP_ENV_PREFIX,
|
|
174
|
+
AI_MCP_FEATURE_FLAGS,
|
|
41
175
|
AI_MCP_FEATURE_FLAG_ID,
|
|
42
176
|
AI_MCP_PACKAGE,
|
|
43
|
-
|
|
177
|
+
AI_MCP_RESOLUTION_SOURCES,
|
|
178
|
+
AI_MCP_TOOL_RISK_CLASSES,
|
|
179
|
+
AI_MCP_TOOL_ROLES,
|
|
180
|
+
isAiMcpToolAllowed,
|
|
181
|
+
isAiMcpToolRiskAllowed,
|
|
182
|
+
packageDescriptor,
|
|
183
|
+
resolveAiMcpToolAllowlist
|
|
44
184
|
});
|
|
45
185
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export interface AiPackageDescriptor {\n readonly packageName: string;\n readonly featureFlagId: string;\n readonly envPrefix: string;\n readonly summary: string;\n}\n\nexport const AI_MCP_PACKAGE = \"@plasius/ai-mcp\";\nexport const AI_MCP_FEATURE_FLAG_ID = \"ai.mcp.enabled\";\nexport const AI_MCP_ENV_PREFIX = \"AI_MCP\";\n\nexport const packageDescriptor: AiPackageDescriptor = Object.freeze({\n packageName: AI_MCP_PACKAGE,\n featureFlagId: AI_MCP_FEATURE_FLAG_ID,\n envPrefix: AI_MCP_ENV_PREFIX,\n summary: \"MCP tool registry, per-call allowlist, and tool audit contracts for Plasius agentic AI.\",\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOO,IAAM,iBAAiB;AACvB,IAAM,yBAAyB;AAC/B,IAAM,oBAAoB;AAE1B,IAAM,oBAAyC,OAAO,OAAO;AAAA,EAClE,aAAa;AAAA,EACb,eAAe;AAAA,EACf,WAAW;AAAA,EACX,SAAS;AACX,CAAC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export interface AiPackageDescriptor {\n readonly packageName: string;\n readonly featureFlagId: string;\n readonly envPrefix: string;\n readonly summary: string;\n}\n\nexport const AI_MCP_PACKAGE = \"@plasius/ai-mcp\";\nexport const AI_MCP_FEATURE_FLAG_ID = \"ai.mcp-rag.enabled\";\nexport const AI_MCP_ENV_PREFIX = \"AI_MCP\";\n\nexport const AI_MCP_FEATURE_FLAGS = {\n mcp: AI_MCP_FEATURE_FLAG_ID,\n} as const;\n\nexport type AiMcpFeatureFlagKey =\n (typeof AI_MCP_FEATURE_FLAGS)[keyof typeof AI_MCP_FEATURE_FLAGS];\n\nexport type AiMcpFeatureFlagSnapshot = Readonly<\n Record<string, boolean | undefined>\n>;\n\nexport const AI_MCP_TOOL_RISK_CLASSES = [\n \"safe\",\n \"sensitive\",\n \"privileged\",\n \"restricted\",\n] as const;\n\nexport type AiMcpToolRiskClass = (typeof AI_MCP_TOOL_RISK_CLASSES)[number];\n\nexport const AI_MCP_TOOL_ROLES = [\"system\", \"admin\", \"operator\", \"player\"] as const;\n\nexport type AiMcpActorRole = (typeof AI_MCP_TOOL_ROLES)[number];\n\nexport const AI_MCP_RESOLUTION_SOURCES = [\n \"policy\",\n \"policy-disabled\",\n \"policy-allow-empty\",\n] as const;\n\nexport type AiMcpResolutionSource = (typeof AI_MCP_RESOLUTION_SOURCES)[number];\n\nexport interface AiMcpToolDescriptor {\n readonly toolId: string;\n readonly toolName: string;\n readonly riskClass: AiMcpToolRiskClass;\n readonly description?: string;\n}\n\nexport interface AiMcpAuditMetadata {\n readonly policyId: string;\n readonly policyVersion: string;\n readonly correlationId: string;\n readonly requestId?: string;\n readonly actorId?: string;\n readonly actorRole: AiMcpActorRole;\n readonly evaluatedAtUtc: string;\n readonly result: \"allow\" | \"deny\" | \"escalate\";\n}\n\nexport interface ResolveAiMcpToolAllowlistInput {\n readonly requestedTools: readonly string[];\n readonly featureFlags?: AiMcpFeatureFlagSnapshot;\n readonly toolRegistry?: readonly AiMcpToolDescriptor[];\n readonly actorRole?: AiMcpActorRole;\n readonly requestId?: string;\n readonly actorId?: string;\n readonly correlationId?: string;\n readonly policyId?: string;\n readonly policyVersion?: string;\n readonly reasonCodes?: readonly string[];\n}\n\nexport interface ResolveAiMcpToolAllowlistResult {\n readonly requestedTools: readonly string[];\n readonly allowedTools: readonly string[];\n readonly blockedTools: readonly string[];\n readonly needsEscalation: boolean;\n readonly reasonCodes: readonly string[];\n readonly source: AiMcpResolutionSource;\n readonly enabledFeatureFlags: readonly AiMcpFeatureFlagKey[];\n readonly audit: AiMcpAuditMetadata;\n}\n\nfunction nowIsoString(): string {\n return new Date().toISOString();\n}\n\nfunction normalizeToolIds(values: readonly string[]): string[] {\n const normalized = values\n .map((value) => value.trim())\n .filter((value) => value.length > 0);\n\n return Array.from(new Set(normalized));\n}\n\nfunction isAiMcpFeatureEnabled(\n featureFlag: AiMcpFeatureFlagKey,\n snapshot: AiMcpFeatureFlagSnapshot = {}\n): boolean {\n return snapshot[featureFlag] === true;\n}\n\nfunction isAllowedForRole(riskClass: AiMcpToolRiskClass, role: AiMcpActorRole): boolean {\n if (role === \"system\") {\n return true;\n }\n\n if (riskClass === \"safe\") {\n return true;\n }\n\n if (riskClass === \"restricted\") {\n return false;\n }\n\n if (riskClass === \"privileged\") {\n return role === \"admin\" || role === \"operator\";\n }\n\n return role === \"admin\" || role === \"operator\";\n}\n\nexport function resolveAiMcpToolAllowlist(\n input: ResolveAiMcpToolAllowlistInput\n): ResolveAiMcpToolAllowlistResult {\n const featureEnabled = isAiMcpFeatureEnabled(\n AI_MCP_FEATURE_FLAGS.mcp,\n input.featureFlags\n );\n const actorRole: AiMcpActorRole = input.actorRole ?? \"player\";\n const requestedTools = normalizeToolIds(input.requestedTools);\n const featureFlagIds: AiMcpFeatureFlagKey[] = featureEnabled\n ? [AI_MCP_FEATURE_FLAGS.mcp]\n : [];\n\n const reasonCodes = [...(input.reasonCodes ?? [])];\n const registryById = new Map(\n (input.toolRegistry ?? []).map((tool) => [tool.toolId, tool])\n );\n\n if (!featureEnabled) {\n reasonCodes.push(\"mcp-feature-disabled\");\n\n return {\n requestedTools,\n allowedTools: [],\n blockedTools: requestedTools,\n needsEscalation: true,\n reasonCodes,\n source: \"policy-disabled\",\n enabledFeatureFlags: featureFlagIds,\n audit: {\n policyId: input.policyId ?? \"mcp-policy-v1\",\n policyVersion: input.policyVersion ?? \"2026-05-01\",\n correlationId: input.correlationId ?? crypto.randomUUID(),\n requestId: input.requestId,\n actorId: input.actorId,\n actorRole,\n evaluatedAtUtc: nowIsoString(),\n result: \"deny\",\n },\n };\n }\n\n const allowedTools: string[] = [];\n const blockedTools: string[] = [];\n\n for (const requestedTool of requestedTools) {\n const tool = registryById.get(requestedTool);\n\n if (!tool) {\n blockedTools.push(requestedTool);\n reasonCodes.push(`tool-not-registered:${requestedTool}`);\n continue;\n }\n\n if (!isAllowedForRole(tool.riskClass, actorRole)) {\n blockedTools.push(requestedTool);\n reasonCodes.push(`tool-risk-restricted:${requestedTool}`);\n continue;\n }\n\n allowedTools.push(requestedTool);\n }\n\n if (allowedTools.length === 0) {\n reasonCodes.push(\"mcp-allowlist-deny-all\");\n }\n\n if (requestedTools.length === 0) {\n reasonCodes.push(\"mcp-allowlist-empty-request\");\n }\n\n return {\n requestedTools,\n allowedTools,\n blockedTools,\n needsEscalation: blockedTools.length > 0 && allowedTools.length > 0,\n reasonCodes,\n source: requestedTools.length > 0 ? \"policy\" : \"policy-allow-empty\",\n enabledFeatureFlags: featureFlagIds,\n audit: {\n policyId: input.policyId ?? \"mcp-policy-v1\",\n policyVersion: input.policyVersion ?? \"2026-05-01\",\n correlationId: input.correlationId ?? crypto.randomUUID(),\n requestId: input.requestId,\n actorId: input.actorId,\n actorRole,\n evaluatedAtUtc: nowIsoString(),\n result:\n blockedTools.length === requestedTools.length\n ? \"deny\"\n : blockedTools.length > 0\n ? \"escalate\"\n : \"allow\",\n },\n };\n}\n\nexport function isAiMcpToolRiskAllowed(\n riskClass: AiMcpToolRiskClass,\n actorRole: AiMcpActorRole\n): boolean {\n return isAllowedForRole(riskClass, actorRole);\n}\n\nexport function isAiMcpToolAllowed(\n toolId: string,\n actorRole: AiMcpActorRole,\n registry: readonly AiMcpToolDescriptor[]\n): boolean {\n const tool = registry.find((candidate) => candidate.toolId === toolId);\n\n if (!tool) {\n return false;\n }\n\n return isAllowedForRole(tool.riskClass, actorRole);\n}\n\nexport const packageDescriptor: AiPackageDescriptor = Object.freeze({\n packageName: AI_MCP_PACKAGE,\n featureFlagId: AI_MCP_FEATURE_FLAG_ID,\n envPrefix: AI_MCP_ENV_PREFIX,\n summary:\n \"MCP tool registry, per-call allowlist, and audit contracts for Plasius AI orchestration.\",\n});\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -5,8 +5,60 @@ interface AiPackageDescriptor {
|
|
|
5
5
|
readonly summary: string;
|
|
6
6
|
}
|
|
7
7
|
declare const AI_MCP_PACKAGE = "@plasius/ai-mcp";
|
|
8
|
-
declare const AI_MCP_FEATURE_FLAG_ID = "ai.mcp.enabled";
|
|
8
|
+
declare const AI_MCP_FEATURE_FLAG_ID = "ai.mcp-rag.enabled";
|
|
9
9
|
declare const AI_MCP_ENV_PREFIX = "AI_MCP";
|
|
10
|
+
declare const AI_MCP_FEATURE_FLAGS: {
|
|
11
|
+
readonly mcp: "ai.mcp-rag.enabled";
|
|
12
|
+
};
|
|
13
|
+
type AiMcpFeatureFlagKey = (typeof AI_MCP_FEATURE_FLAGS)[keyof typeof AI_MCP_FEATURE_FLAGS];
|
|
14
|
+
type AiMcpFeatureFlagSnapshot = Readonly<Record<string, boolean | undefined>>;
|
|
15
|
+
declare const AI_MCP_TOOL_RISK_CLASSES: readonly ["safe", "sensitive", "privileged", "restricted"];
|
|
16
|
+
type AiMcpToolRiskClass = (typeof AI_MCP_TOOL_RISK_CLASSES)[number];
|
|
17
|
+
declare const AI_MCP_TOOL_ROLES: readonly ["system", "admin", "operator", "player"];
|
|
18
|
+
type AiMcpActorRole = (typeof AI_MCP_TOOL_ROLES)[number];
|
|
19
|
+
declare const AI_MCP_RESOLUTION_SOURCES: readonly ["policy", "policy-disabled", "policy-allow-empty"];
|
|
20
|
+
type AiMcpResolutionSource = (typeof AI_MCP_RESOLUTION_SOURCES)[number];
|
|
21
|
+
interface AiMcpToolDescriptor {
|
|
22
|
+
readonly toolId: string;
|
|
23
|
+
readonly toolName: string;
|
|
24
|
+
readonly riskClass: AiMcpToolRiskClass;
|
|
25
|
+
readonly description?: string;
|
|
26
|
+
}
|
|
27
|
+
interface AiMcpAuditMetadata {
|
|
28
|
+
readonly policyId: string;
|
|
29
|
+
readonly policyVersion: string;
|
|
30
|
+
readonly correlationId: string;
|
|
31
|
+
readonly requestId?: string;
|
|
32
|
+
readonly actorId?: string;
|
|
33
|
+
readonly actorRole: AiMcpActorRole;
|
|
34
|
+
readonly evaluatedAtUtc: string;
|
|
35
|
+
readonly result: "allow" | "deny" | "escalate";
|
|
36
|
+
}
|
|
37
|
+
interface ResolveAiMcpToolAllowlistInput {
|
|
38
|
+
readonly requestedTools: readonly string[];
|
|
39
|
+
readonly featureFlags?: AiMcpFeatureFlagSnapshot;
|
|
40
|
+
readonly toolRegistry?: readonly AiMcpToolDescriptor[];
|
|
41
|
+
readonly actorRole?: AiMcpActorRole;
|
|
42
|
+
readonly requestId?: string;
|
|
43
|
+
readonly actorId?: string;
|
|
44
|
+
readonly correlationId?: string;
|
|
45
|
+
readonly policyId?: string;
|
|
46
|
+
readonly policyVersion?: string;
|
|
47
|
+
readonly reasonCodes?: readonly string[];
|
|
48
|
+
}
|
|
49
|
+
interface ResolveAiMcpToolAllowlistResult {
|
|
50
|
+
readonly requestedTools: readonly string[];
|
|
51
|
+
readonly allowedTools: readonly string[];
|
|
52
|
+
readonly blockedTools: readonly string[];
|
|
53
|
+
readonly needsEscalation: boolean;
|
|
54
|
+
readonly reasonCodes: readonly string[];
|
|
55
|
+
readonly source: AiMcpResolutionSource;
|
|
56
|
+
readonly enabledFeatureFlags: readonly AiMcpFeatureFlagKey[];
|
|
57
|
+
readonly audit: AiMcpAuditMetadata;
|
|
58
|
+
}
|
|
59
|
+
declare function resolveAiMcpToolAllowlist(input: ResolveAiMcpToolAllowlistInput): ResolveAiMcpToolAllowlistResult;
|
|
60
|
+
declare function isAiMcpToolRiskAllowed(riskClass: AiMcpToolRiskClass, actorRole: AiMcpActorRole): boolean;
|
|
61
|
+
declare function isAiMcpToolAllowed(toolId: string, actorRole: AiMcpActorRole, registry: readonly AiMcpToolDescriptor[]): boolean;
|
|
10
62
|
declare const packageDescriptor: AiPackageDescriptor;
|
|
11
63
|
|
|
12
|
-
export { AI_MCP_ENV_PREFIX, AI_MCP_FEATURE_FLAG_ID, AI_MCP_PACKAGE, type AiPackageDescriptor, packageDescriptor };
|
|
64
|
+
export { AI_MCP_ENV_PREFIX, AI_MCP_FEATURE_FLAGS, AI_MCP_FEATURE_FLAG_ID, AI_MCP_PACKAGE, AI_MCP_RESOLUTION_SOURCES, AI_MCP_TOOL_RISK_CLASSES, AI_MCP_TOOL_ROLES, type AiMcpActorRole, type AiMcpAuditMetadata, type AiMcpFeatureFlagKey, type AiMcpFeatureFlagSnapshot, type AiMcpResolutionSource, type AiMcpToolDescriptor, type AiMcpToolRiskClass, type AiPackageDescriptor, type ResolveAiMcpToolAllowlistInput, type ResolveAiMcpToolAllowlistResult, isAiMcpToolAllowed, isAiMcpToolRiskAllowed, packageDescriptor, resolveAiMcpToolAllowlist };
|
package/dist/index.d.ts
CHANGED
|
@@ -5,8 +5,60 @@ interface AiPackageDescriptor {
|
|
|
5
5
|
readonly summary: string;
|
|
6
6
|
}
|
|
7
7
|
declare const AI_MCP_PACKAGE = "@plasius/ai-mcp";
|
|
8
|
-
declare const AI_MCP_FEATURE_FLAG_ID = "ai.mcp.enabled";
|
|
8
|
+
declare const AI_MCP_FEATURE_FLAG_ID = "ai.mcp-rag.enabled";
|
|
9
9
|
declare const AI_MCP_ENV_PREFIX = "AI_MCP";
|
|
10
|
+
declare const AI_MCP_FEATURE_FLAGS: {
|
|
11
|
+
readonly mcp: "ai.mcp-rag.enabled";
|
|
12
|
+
};
|
|
13
|
+
type AiMcpFeatureFlagKey = (typeof AI_MCP_FEATURE_FLAGS)[keyof typeof AI_MCP_FEATURE_FLAGS];
|
|
14
|
+
type AiMcpFeatureFlagSnapshot = Readonly<Record<string, boolean | undefined>>;
|
|
15
|
+
declare const AI_MCP_TOOL_RISK_CLASSES: readonly ["safe", "sensitive", "privileged", "restricted"];
|
|
16
|
+
type AiMcpToolRiskClass = (typeof AI_MCP_TOOL_RISK_CLASSES)[number];
|
|
17
|
+
declare const AI_MCP_TOOL_ROLES: readonly ["system", "admin", "operator", "player"];
|
|
18
|
+
type AiMcpActorRole = (typeof AI_MCP_TOOL_ROLES)[number];
|
|
19
|
+
declare const AI_MCP_RESOLUTION_SOURCES: readonly ["policy", "policy-disabled", "policy-allow-empty"];
|
|
20
|
+
type AiMcpResolutionSource = (typeof AI_MCP_RESOLUTION_SOURCES)[number];
|
|
21
|
+
interface AiMcpToolDescriptor {
|
|
22
|
+
readonly toolId: string;
|
|
23
|
+
readonly toolName: string;
|
|
24
|
+
readonly riskClass: AiMcpToolRiskClass;
|
|
25
|
+
readonly description?: string;
|
|
26
|
+
}
|
|
27
|
+
interface AiMcpAuditMetadata {
|
|
28
|
+
readonly policyId: string;
|
|
29
|
+
readonly policyVersion: string;
|
|
30
|
+
readonly correlationId: string;
|
|
31
|
+
readonly requestId?: string;
|
|
32
|
+
readonly actorId?: string;
|
|
33
|
+
readonly actorRole: AiMcpActorRole;
|
|
34
|
+
readonly evaluatedAtUtc: string;
|
|
35
|
+
readonly result: "allow" | "deny" | "escalate";
|
|
36
|
+
}
|
|
37
|
+
interface ResolveAiMcpToolAllowlistInput {
|
|
38
|
+
readonly requestedTools: readonly string[];
|
|
39
|
+
readonly featureFlags?: AiMcpFeatureFlagSnapshot;
|
|
40
|
+
readonly toolRegistry?: readonly AiMcpToolDescriptor[];
|
|
41
|
+
readonly actorRole?: AiMcpActorRole;
|
|
42
|
+
readonly requestId?: string;
|
|
43
|
+
readonly actorId?: string;
|
|
44
|
+
readonly correlationId?: string;
|
|
45
|
+
readonly policyId?: string;
|
|
46
|
+
readonly policyVersion?: string;
|
|
47
|
+
readonly reasonCodes?: readonly string[];
|
|
48
|
+
}
|
|
49
|
+
interface ResolveAiMcpToolAllowlistResult {
|
|
50
|
+
readonly requestedTools: readonly string[];
|
|
51
|
+
readonly allowedTools: readonly string[];
|
|
52
|
+
readonly blockedTools: readonly string[];
|
|
53
|
+
readonly needsEscalation: boolean;
|
|
54
|
+
readonly reasonCodes: readonly string[];
|
|
55
|
+
readonly source: AiMcpResolutionSource;
|
|
56
|
+
readonly enabledFeatureFlags: readonly AiMcpFeatureFlagKey[];
|
|
57
|
+
readonly audit: AiMcpAuditMetadata;
|
|
58
|
+
}
|
|
59
|
+
declare function resolveAiMcpToolAllowlist(input: ResolveAiMcpToolAllowlistInput): ResolveAiMcpToolAllowlistResult;
|
|
60
|
+
declare function isAiMcpToolRiskAllowed(riskClass: AiMcpToolRiskClass, actorRole: AiMcpActorRole): boolean;
|
|
61
|
+
declare function isAiMcpToolAllowed(toolId: string, actorRole: AiMcpActorRole, registry: readonly AiMcpToolDescriptor[]): boolean;
|
|
10
62
|
declare const packageDescriptor: AiPackageDescriptor;
|
|
11
63
|
|
|
12
|
-
export { AI_MCP_ENV_PREFIX, AI_MCP_FEATURE_FLAG_ID, AI_MCP_PACKAGE, type AiPackageDescriptor, packageDescriptor };
|
|
64
|
+
export { AI_MCP_ENV_PREFIX, AI_MCP_FEATURE_FLAGS, AI_MCP_FEATURE_FLAG_ID, AI_MCP_PACKAGE, AI_MCP_RESOLUTION_SOURCES, AI_MCP_TOOL_RISK_CLASSES, AI_MCP_TOOL_ROLES, type AiMcpActorRole, type AiMcpAuditMetadata, type AiMcpFeatureFlagKey, type AiMcpFeatureFlagSnapshot, type AiMcpResolutionSource, type AiMcpToolDescriptor, type AiMcpToolRiskClass, type AiPackageDescriptor, type ResolveAiMcpToolAllowlistInput, type ResolveAiMcpToolAllowlistResult, isAiMcpToolAllowed, isAiMcpToolRiskAllowed, packageDescriptor, resolveAiMcpToolAllowlist };
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,150 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
var AI_MCP_PACKAGE = "@plasius/ai-mcp";
|
|
3
|
-
var AI_MCP_FEATURE_FLAG_ID = "ai.mcp.enabled";
|
|
3
|
+
var AI_MCP_FEATURE_FLAG_ID = "ai.mcp-rag.enabled";
|
|
4
4
|
var AI_MCP_ENV_PREFIX = "AI_MCP";
|
|
5
|
+
var AI_MCP_FEATURE_FLAGS = {
|
|
6
|
+
mcp: AI_MCP_FEATURE_FLAG_ID
|
|
7
|
+
};
|
|
8
|
+
var AI_MCP_TOOL_RISK_CLASSES = [
|
|
9
|
+
"safe",
|
|
10
|
+
"sensitive",
|
|
11
|
+
"privileged",
|
|
12
|
+
"restricted"
|
|
13
|
+
];
|
|
14
|
+
var AI_MCP_TOOL_ROLES = ["system", "admin", "operator", "player"];
|
|
15
|
+
var AI_MCP_RESOLUTION_SOURCES = [
|
|
16
|
+
"policy",
|
|
17
|
+
"policy-disabled",
|
|
18
|
+
"policy-allow-empty"
|
|
19
|
+
];
|
|
20
|
+
function nowIsoString() {
|
|
21
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
22
|
+
}
|
|
23
|
+
function normalizeToolIds(values) {
|
|
24
|
+
const normalized = values.map((value) => value.trim()).filter((value) => value.length > 0);
|
|
25
|
+
return Array.from(new Set(normalized));
|
|
26
|
+
}
|
|
27
|
+
function isAiMcpFeatureEnabled(featureFlag, snapshot = {}) {
|
|
28
|
+
return snapshot[featureFlag] === true;
|
|
29
|
+
}
|
|
30
|
+
function isAllowedForRole(riskClass, role) {
|
|
31
|
+
if (role === "system") {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
if (riskClass === "safe") {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
if (riskClass === "restricted") {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
if (riskClass === "privileged") {
|
|
41
|
+
return role === "admin" || role === "operator";
|
|
42
|
+
}
|
|
43
|
+
return role === "admin" || role === "operator";
|
|
44
|
+
}
|
|
45
|
+
function resolveAiMcpToolAllowlist(input) {
|
|
46
|
+
const featureEnabled = isAiMcpFeatureEnabled(
|
|
47
|
+
AI_MCP_FEATURE_FLAGS.mcp,
|
|
48
|
+
input.featureFlags
|
|
49
|
+
);
|
|
50
|
+
const actorRole = input.actorRole ?? "player";
|
|
51
|
+
const requestedTools = normalizeToolIds(input.requestedTools);
|
|
52
|
+
const featureFlagIds = featureEnabled ? [AI_MCP_FEATURE_FLAGS.mcp] : [];
|
|
53
|
+
const reasonCodes = [...input.reasonCodes ?? []];
|
|
54
|
+
const registryById = new Map(
|
|
55
|
+
(input.toolRegistry ?? []).map((tool) => [tool.toolId, tool])
|
|
56
|
+
);
|
|
57
|
+
if (!featureEnabled) {
|
|
58
|
+
reasonCodes.push("mcp-feature-disabled");
|
|
59
|
+
return {
|
|
60
|
+
requestedTools,
|
|
61
|
+
allowedTools: [],
|
|
62
|
+
blockedTools: requestedTools,
|
|
63
|
+
needsEscalation: true,
|
|
64
|
+
reasonCodes,
|
|
65
|
+
source: "policy-disabled",
|
|
66
|
+
enabledFeatureFlags: featureFlagIds,
|
|
67
|
+
audit: {
|
|
68
|
+
policyId: input.policyId ?? "mcp-policy-v1",
|
|
69
|
+
policyVersion: input.policyVersion ?? "2026-05-01",
|
|
70
|
+
correlationId: input.correlationId ?? crypto.randomUUID(),
|
|
71
|
+
requestId: input.requestId,
|
|
72
|
+
actorId: input.actorId,
|
|
73
|
+
actorRole,
|
|
74
|
+
evaluatedAtUtc: nowIsoString(),
|
|
75
|
+
result: "deny"
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const allowedTools = [];
|
|
80
|
+
const blockedTools = [];
|
|
81
|
+
for (const requestedTool of requestedTools) {
|
|
82
|
+
const tool = registryById.get(requestedTool);
|
|
83
|
+
if (!tool) {
|
|
84
|
+
blockedTools.push(requestedTool);
|
|
85
|
+
reasonCodes.push(`tool-not-registered:${requestedTool}`);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (!isAllowedForRole(tool.riskClass, actorRole)) {
|
|
89
|
+
blockedTools.push(requestedTool);
|
|
90
|
+
reasonCodes.push(`tool-risk-restricted:${requestedTool}`);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
allowedTools.push(requestedTool);
|
|
94
|
+
}
|
|
95
|
+
if (allowedTools.length === 0) {
|
|
96
|
+
reasonCodes.push("mcp-allowlist-deny-all");
|
|
97
|
+
}
|
|
98
|
+
if (requestedTools.length === 0) {
|
|
99
|
+
reasonCodes.push("mcp-allowlist-empty-request");
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
requestedTools,
|
|
103
|
+
allowedTools,
|
|
104
|
+
blockedTools,
|
|
105
|
+
needsEscalation: blockedTools.length > 0 && allowedTools.length > 0,
|
|
106
|
+
reasonCodes,
|
|
107
|
+
source: requestedTools.length > 0 ? "policy" : "policy-allow-empty",
|
|
108
|
+
enabledFeatureFlags: featureFlagIds,
|
|
109
|
+
audit: {
|
|
110
|
+
policyId: input.policyId ?? "mcp-policy-v1",
|
|
111
|
+
policyVersion: input.policyVersion ?? "2026-05-01",
|
|
112
|
+
correlationId: input.correlationId ?? crypto.randomUUID(),
|
|
113
|
+
requestId: input.requestId,
|
|
114
|
+
actorId: input.actorId,
|
|
115
|
+
actorRole,
|
|
116
|
+
evaluatedAtUtc: nowIsoString(),
|
|
117
|
+
result: blockedTools.length === requestedTools.length ? "deny" : blockedTools.length > 0 ? "escalate" : "allow"
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function isAiMcpToolRiskAllowed(riskClass, actorRole) {
|
|
122
|
+
return isAllowedForRole(riskClass, actorRole);
|
|
123
|
+
}
|
|
124
|
+
function isAiMcpToolAllowed(toolId, actorRole, registry) {
|
|
125
|
+
const tool = registry.find((candidate) => candidate.toolId === toolId);
|
|
126
|
+
if (!tool) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
return isAllowedForRole(tool.riskClass, actorRole);
|
|
130
|
+
}
|
|
5
131
|
var packageDescriptor = Object.freeze({
|
|
6
132
|
packageName: AI_MCP_PACKAGE,
|
|
7
133
|
featureFlagId: AI_MCP_FEATURE_FLAG_ID,
|
|
8
134
|
envPrefix: AI_MCP_ENV_PREFIX,
|
|
9
|
-
summary: "MCP tool registry, per-call allowlist, and
|
|
135
|
+
summary: "MCP tool registry, per-call allowlist, and audit contracts for Plasius AI orchestration."
|
|
10
136
|
});
|
|
11
137
|
export {
|
|
12
138
|
AI_MCP_ENV_PREFIX,
|
|
139
|
+
AI_MCP_FEATURE_FLAGS,
|
|
13
140
|
AI_MCP_FEATURE_FLAG_ID,
|
|
14
141
|
AI_MCP_PACKAGE,
|
|
15
|
-
|
|
142
|
+
AI_MCP_RESOLUTION_SOURCES,
|
|
143
|
+
AI_MCP_TOOL_RISK_CLASSES,
|
|
144
|
+
AI_MCP_TOOL_ROLES,
|
|
145
|
+
isAiMcpToolAllowed,
|
|
146
|
+
isAiMcpToolRiskAllowed,
|
|
147
|
+
packageDescriptor,
|
|
148
|
+
resolveAiMcpToolAllowlist
|
|
16
149
|
};
|
|
17
150
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export interface AiPackageDescriptor {\n readonly packageName: string;\n readonly featureFlagId: string;\n readonly envPrefix: string;\n readonly summary: string;\n}\n\nexport const AI_MCP_PACKAGE = \"@plasius/ai-mcp\";\nexport const AI_MCP_FEATURE_FLAG_ID = \"ai.mcp.enabled\";\nexport const AI_MCP_ENV_PREFIX = \"AI_MCP\";\n\nexport const packageDescriptor: AiPackageDescriptor = Object.freeze({\n packageName: AI_MCP_PACKAGE,\n featureFlagId: AI_MCP_FEATURE_FLAG_ID,\n envPrefix: AI_MCP_ENV_PREFIX,\n summary
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export interface AiPackageDescriptor {\n readonly packageName: string;\n readonly featureFlagId: string;\n readonly envPrefix: string;\n readonly summary: string;\n}\n\nexport const AI_MCP_PACKAGE = \"@plasius/ai-mcp\";\nexport const AI_MCP_FEATURE_FLAG_ID = \"ai.mcp-rag.enabled\";\nexport const AI_MCP_ENV_PREFIX = \"AI_MCP\";\n\nexport const AI_MCP_FEATURE_FLAGS = {\n mcp: AI_MCP_FEATURE_FLAG_ID,\n} as const;\n\nexport type AiMcpFeatureFlagKey =\n (typeof AI_MCP_FEATURE_FLAGS)[keyof typeof AI_MCP_FEATURE_FLAGS];\n\nexport type AiMcpFeatureFlagSnapshot = Readonly<\n Record<string, boolean | undefined>\n>;\n\nexport const AI_MCP_TOOL_RISK_CLASSES = [\n \"safe\",\n \"sensitive\",\n \"privileged\",\n \"restricted\",\n] as const;\n\nexport type AiMcpToolRiskClass = (typeof AI_MCP_TOOL_RISK_CLASSES)[number];\n\nexport const AI_MCP_TOOL_ROLES = [\"system\", \"admin\", \"operator\", \"player\"] as const;\n\nexport type AiMcpActorRole = (typeof AI_MCP_TOOL_ROLES)[number];\n\nexport const AI_MCP_RESOLUTION_SOURCES = [\n \"policy\",\n \"policy-disabled\",\n \"policy-allow-empty\",\n] as const;\n\nexport type AiMcpResolutionSource = (typeof AI_MCP_RESOLUTION_SOURCES)[number];\n\nexport interface AiMcpToolDescriptor {\n readonly toolId: string;\n readonly toolName: string;\n readonly riskClass: AiMcpToolRiskClass;\n readonly description?: string;\n}\n\nexport interface AiMcpAuditMetadata {\n readonly policyId: string;\n readonly policyVersion: string;\n readonly correlationId: string;\n readonly requestId?: string;\n readonly actorId?: string;\n readonly actorRole: AiMcpActorRole;\n readonly evaluatedAtUtc: string;\n readonly result: \"allow\" | \"deny\" | \"escalate\";\n}\n\nexport interface ResolveAiMcpToolAllowlistInput {\n readonly requestedTools: readonly string[];\n readonly featureFlags?: AiMcpFeatureFlagSnapshot;\n readonly toolRegistry?: readonly AiMcpToolDescriptor[];\n readonly actorRole?: AiMcpActorRole;\n readonly requestId?: string;\n readonly actorId?: string;\n readonly correlationId?: string;\n readonly policyId?: string;\n readonly policyVersion?: string;\n readonly reasonCodes?: readonly string[];\n}\n\nexport interface ResolveAiMcpToolAllowlistResult {\n readonly requestedTools: readonly string[];\n readonly allowedTools: readonly string[];\n readonly blockedTools: readonly string[];\n readonly needsEscalation: boolean;\n readonly reasonCodes: readonly string[];\n readonly source: AiMcpResolutionSource;\n readonly enabledFeatureFlags: readonly AiMcpFeatureFlagKey[];\n readonly audit: AiMcpAuditMetadata;\n}\n\nfunction nowIsoString(): string {\n return new Date().toISOString();\n}\n\nfunction normalizeToolIds(values: readonly string[]): string[] {\n const normalized = values\n .map((value) => value.trim())\n .filter((value) => value.length > 0);\n\n return Array.from(new Set(normalized));\n}\n\nfunction isAiMcpFeatureEnabled(\n featureFlag: AiMcpFeatureFlagKey,\n snapshot: AiMcpFeatureFlagSnapshot = {}\n): boolean {\n return snapshot[featureFlag] === true;\n}\n\nfunction isAllowedForRole(riskClass: AiMcpToolRiskClass, role: AiMcpActorRole): boolean {\n if (role === \"system\") {\n return true;\n }\n\n if (riskClass === \"safe\") {\n return true;\n }\n\n if (riskClass === \"restricted\") {\n return false;\n }\n\n if (riskClass === \"privileged\") {\n return role === \"admin\" || role === \"operator\";\n }\n\n return role === \"admin\" || role === \"operator\";\n}\n\nexport function resolveAiMcpToolAllowlist(\n input: ResolveAiMcpToolAllowlistInput\n): ResolveAiMcpToolAllowlistResult {\n const featureEnabled = isAiMcpFeatureEnabled(\n AI_MCP_FEATURE_FLAGS.mcp,\n input.featureFlags\n );\n const actorRole: AiMcpActorRole = input.actorRole ?? \"player\";\n const requestedTools = normalizeToolIds(input.requestedTools);\n const featureFlagIds: AiMcpFeatureFlagKey[] = featureEnabled\n ? [AI_MCP_FEATURE_FLAGS.mcp]\n : [];\n\n const reasonCodes = [...(input.reasonCodes ?? [])];\n const registryById = new Map(\n (input.toolRegistry ?? []).map((tool) => [tool.toolId, tool])\n );\n\n if (!featureEnabled) {\n reasonCodes.push(\"mcp-feature-disabled\");\n\n return {\n requestedTools,\n allowedTools: [],\n blockedTools: requestedTools,\n needsEscalation: true,\n reasonCodes,\n source: \"policy-disabled\",\n enabledFeatureFlags: featureFlagIds,\n audit: {\n policyId: input.policyId ?? \"mcp-policy-v1\",\n policyVersion: input.policyVersion ?? \"2026-05-01\",\n correlationId: input.correlationId ?? crypto.randomUUID(),\n requestId: input.requestId,\n actorId: input.actorId,\n actorRole,\n evaluatedAtUtc: nowIsoString(),\n result: \"deny\",\n },\n };\n }\n\n const allowedTools: string[] = [];\n const blockedTools: string[] = [];\n\n for (const requestedTool of requestedTools) {\n const tool = registryById.get(requestedTool);\n\n if (!tool) {\n blockedTools.push(requestedTool);\n reasonCodes.push(`tool-not-registered:${requestedTool}`);\n continue;\n }\n\n if (!isAllowedForRole(tool.riskClass, actorRole)) {\n blockedTools.push(requestedTool);\n reasonCodes.push(`tool-risk-restricted:${requestedTool}`);\n continue;\n }\n\n allowedTools.push(requestedTool);\n }\n\n if (allowedTools.length === 0) {\n reasonCodes.push(\"mcp-allowlist-deny-all\");\n }\n\n if (requestedTools.length === 0) {\n reasonCodes.push(\"mcp-allowlist-empty-request\");\n }\n\n return {\n requestedTools,\n allowedTools,\n blockedTools,\n needsEscalation: blockedTools.length > 0 && allowedTools.length > 0,\n reasonCodes,\n source: requestedTools.length > 0 ? \"policy\" : \"policy-allow-empty\",\n enabledFeatureFlags: featureFlagIds,\n audit: {\n policyId: input.policyId ?? \"mcp-policy-v1\",\n policyVersion: input.policyVersion ?? \"2026-05-01\",\n correlationId: input.correlationId ?? crypto.randomUUID(),\n requestId: input.requestId,\n actorId: input.actorId,\n actorRole,\n evaluatedAtUtc: nowIsoString(),\n result:\n blockedTools.length === requestedTools.length\n ? \"deny\"\n : blockedTools.length > 0\n ? \"escalate\"\n : \"allow\",\n },\n };\n}\n\nexport function isAiMcpToolRiskAllowed(\n riskClass: AiMcpToolRiskClass,\n actorRole: AiMcpActorRole\n): boolean {\n return isAllowedForRole(riskClass, actorRole);\n}\n\nexport function isAiMcpToolAllowed(\n toolId: string,\n actorRole: AiMcpActorRole,\n registry: readonly AiMcpToolDescriptor[]\n): boolean {\n const tool = registry.find((candidate) => candidate.toolId === toolId);\n\n if (!tool) {\n return false;\n }\n\n return isAllowedForRole(tool.riskClass, actorRole);\n}\n\nexport const packageDescriptor: AiPackageDescriptor = Object.freeze({\n packageName: AI_MCP_PACKAGE,\n featureFlagId: AI_MCP_FEATURE_FLAG_ID,\n envPrefix: AI_MCP_ENV_PREFIX,\n summary:\n \"MCP tool registry, per-call allowlist, and audit contracts for Plasius AI orchestration.\",\n});\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# ADR-0002: MCP allowlist and risk-based execution gates
|
|
2
|
+
|
|
3
|
+
- Date: 2026-05-14
|
|
4
|
+
- Status: Accepted
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
MCP execution is part of the agentic AI boundary and requires strict minimization of available tools. A request should only execute from an allowlist based on role and tool risk class to avoid accidental privileged actions.
|
|
9
|
+
|
|
10
|
+
## Decision
|
|
11
|
+
|
|
12
|
+
`@plasius/ai-mcp` now exposes a deterministic allowlist resolver that:
|
|
13
|
+
|
|
14
|
+
- requires explicit `ai.mcp-rag.enabled` feature gate;
|
|
15
|
+
- evaluates requested tool IDs against a registry of tool descriptors;
|
|
16
|
+
- enforces role-based risk-class constraints;
|
|
17
|
+
- returns structured outcomes including allowed/blocked tools and audit metadata.
|
|
18
|
+
|
|
19
|
+
## Consequences
|
|
20
|
+
|
|
21
|
+
- Tool access is explicit and inspectable per request.
|
|
22
|
+
- Downstream callers can enforce telemetry and policy with reason codes.
|
|
23
|
+
- Restricted or unknown tools are denied by default when policy is unavailable.
|
|
24
|
+
- Audits now include decision outcome and policy references for compliance traceability.
|
package/docs/adrs/index.md
CHANGED