@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 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 agentic AI.
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 is intentionally bootstrapped with a small public contract surface so implementation can evolve behind tracked Feature/Story/Task work.
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 { packageDescriptor } from "@plasius/ai-mcp";
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(packageDescriptor.packageName);
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
- packageDescriptor: () => packageDescriptor
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 tool audit contracts for Plasius agentic AI."
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
- packageDescriptor
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
@@ -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 tool audit contracts for Plasius agentic AI."
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
- packageDescriptor
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: \"MCP tool registry, per-call allowlist, and tool audit contracts for Plasius agentic AI.\",\n});\n"],"mappings":";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":[]}
@@ -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.
@@ -1,3 +1,4 @@
1
1
  # Architecture Decision Records
2
2
 
3
3
  - [ADR-0001: @plasius/ai-mcp Package Boundary](./adr-0001-package-boundary.md)
4
+ - [ADR-0002: MCP tool allowlist and risk-based execution gates](./adr-0002-mcp-allowlist-risk-audit.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plasius/ai-mcp",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "MCP tool registry, per-call allowlist, and tool audit contracts for Plasius agentic AI.",
5
5
  "type": "module",
6
6
  "sideEffects": false,