isc-transforms-mcp 1.0.14 → 1.0.15

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.
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "sailpoint.isc.transforms.identityAttribute.schema.json",
4
4
  "title": "SailPoint ISC Transform Schema - identityAttribute",
5
- "description": "Strict schema derived from SailPoint official Identity Attribute operation documentation. Retrieves the value of a user's identity attribute by its system (camel-cased) name.",
5
+ "description": "Strict schema for the SailPoint ISC identityAttribute transform. Retrieves the value of an identity attribute by its camelCase system name. IMPORTANT LIMITATION: This transform is NOT intended for use within another identity profile attribute's calculation. Due to multi-threaded identity processing, the referenced attribute may not yet exist or may hold stale data at evaluation time. Intended use cases are provisioning policies and entitlement request forms. For identity profile attribute mappings that need source account data, use accountAttribute instead.",
6
6
  "type": "object",
7
7
  "additionalProperties": false,
8
8
  "required": [
@@ -12,16 +12,18 @@
12
12
  ],
13
13
  "properties": {
14
14
  "type": {
15
- "const": "identityAttribute"
15
+ "const": "identityAttribute",
16
+ "description": "Transform operation type. Must be exactly 'identityAttribute'."
16
17
  },
17
18
  "name": {
18
19
  "type": "string",
19
- "minLength": 1
20
+ "minLength": 1,
21
+ "description": "Display name for this transform, shown in UI dropdowns and provisioning policy mappings."
20
22
  },
21
23
  "requiresPeriodicRefresh": {
22
24
  "type": "boolean",
23
25
  "default": false,
24
- "description": "Whether the transform logic should be reevaluated every evening as part of identity refresh. Default false."
26
+ "description": "If true, re-evaluates this transform during the nightly identity refresh cycle. Default is false."
25
27
  },
26
28
  "attributes": {
27
29
  "type": "object",
@@ -29,15 +31,20 @@
29
31
  "required": [
30
32
  "name"
31
33
  ],
34
+ "description": "Required container for identityAttribute operation attributes.",
32
35
  "properties": {
33
36
  "name": {
34
37
  "type": "string",
35
38
  "minLength": 1,
36
- "description": "System (camel-cased) name of the identity attribute to retrieve (e.g., 'email', 'uid')."
39
+ "description": "The camelCase system name of the identity attribute to retrieve. This is the internal attribute name, not the display label. Examples: 'uid' (SailPoint User Name), 'email', 'identificationNumber' (Employee Number), 'manager', 'department', 'displayName'. Find the system name in Admin UI → Identity Profiles → Identity Attributes."
37
40
  },
38
41
  "input": {
39
- "description": "Optional explicit input data. If omitted, the transform uses input from the source+attribute combination configured in the UI. Modeled as a nested transform object.",
40
- "oneOf": [
42
+ "description": "Optional explicit input data for this transform. If omitted, the transform uses the source and attribute combination configured in the identity profile UI. When provided, must be a nested transform object or a static string value.",
43
+ "anyOf": [
44
+ {
45
+ "type": "string",
46
+ "description": "Static string input value."
47
+ },
41
48
  {
42
49
  "$ref": "#/$defs/NestedTransform"
43
50
  }
@@ -49,7 +56,7 @@
49
56
  "$defs": {
50
57
  "NestedTransform": {
51
58
  "type": "object",
52
- "description": "Nested transform object used as explicit input (name optional in docs).",
59
+ "description": "A nested transform object used as explicit input to this transform. Provides the input data that feeds the identityAttribute lookup. The 'name' field is optional in nested transforms per SailPoint docs.",
53
60
  "additionalProperties": false,
54
61
  "required": [
55
62
  "type",
@@ -57,18 +64,22 @@
57
64
  ],
58
65
  "properties": {
59
66
  "id": {
60
- "type": "string"
67
+ "type": "string",
68
+ "description": "Optional ID when referencing an existing saved transform."
61
69
  },
62
70
  "name": {
63
71
  "type": "string",
64
- "minLength": 1
72
+ "minLength": 1,
73
+ "description": "Optional display name for the nested transform."
65
74
  },
66
75
  "type": {
67
76
  "type": "string",
68
- "minLength": 1
77
+ "minLength": 1,
78
+ "description": "The operation type of the nested transform (e.g., 'accountAttribute', 'static')."
69
79
  },
70
80
  "requiresPeriodicRefresh": {
71
- "type": "boolean"
81
+ "type": "boolean",
82
+ "description": "Whether this nested transform re-evaluates during nightly refresh."
72
83
  },
73
84
  "attributes": {
74
85
  "type": "object",
@@ -80,25 +91,39 @@
80
91
  },
81
92
  "examples": [
82
93
  {
83
- "attributes": {
84
- "name": "email"
85
- },
94
+ "name": "Get SailPoint User Name",
86
95
  "type": "identityAttribute",
87
- "name": "Identity Attribute Transform"
88
- },
89
- {
90
96
  "attributes": {
91
97
  "name": "uid"
92
- },
93
- "type": "identityAttribute",
94
- "name": "Identity Attribute Transform"
98
+ }
95
99
  },
96
100
  {
101
+ "name": "Get Employee Number",
102
+ "type": "identityAttribute",
97
103
  "attributes": {
98
104
  "name": "identificationNumber"
99
- },
105
+ }
106
+ },
107
+ {
108
+ "name": "Get Manager",
100
109
  "type": "identityAttribute",
101
- "name": "Identity Attribute Transform"
110
+ "attributes": {
111
+ "name": "manager"
112
+ }
113
+ },
114
+ {
115
+ "name": "Get Email with Explicit Input",
116
+ "type": "identityAttribute",
117
+ "attributes": {
118
+ "name": "email",
119
+ "input": {
120
+ "type": "accountAttribute",
121
+ "attributes": {
122
+ "sourceName": "HR Source",
123
+ "attributeName": "workEmail"
124
+ }
125
+ }
126
+ }
102
127
  }
103
128
  ]
104
129
  }
@@ -1073,7 +1073,40 @@ function lintStatic(attrs) {
1073
1073
  return msgs;
1074
1074
  }
1075
1075
  // ---------------------------------------------------------------------------
1076
- // 25. indexOf / lastIndexOf
1076
+ // 25. identityAttribute name format + critical use-case limitation
1077
+ // Docs: https://developer.sailpoint.com/docs/extensibility/transforms/operations/identity-attribute
1078
+ // ---------------------------------------------------------------------------
1079
+ function lintIdentityAttribute(attrs) {
1080
+ const msgs = [];
1081
+ // 1. name: must be a camelCase system name — no spaces, ideally no hyphens
1082
+ const name = attrs?.name;
1083
+ if (typeof name === "string" && name.trim().length > 0) {
1084
+ if (/\s/.test(name)) {
1085
+ push(msgs, "error", `attributes.name '${name}' contains whitespace. Identity attribute system names are camelCase with no spaces ` +
1086
+ "(e.g., 'uid', 'email', 'identificationNumber'). Check the identity profile attribute's system name in the Admin UI.", "attributes.name");
1087
+ }
1088
+ else if (/-/.test(name)) {
1089
+ push(msgs, "warn", `attributes.name '${name}' contains a hyphen. Identity attribute system names are camelCase ` +
1090
+ "(e.g., 'identificationNumber', not 'identification-number'). Verify this is the exact system name shown in the Admin UI.", "attributes.name");
1091
+ }
1092
+ }
1093
+ // 2. Critical use-case limitation — identityAttribute is NOT safe inside identity profile attribute calculations
1094
+ push(msgs, "warn", "identityAttribute is NOT intended for use within another identity profile attribute's calculation. " +
1095
+ "Due to multi-threaded identity processing, the referenced attribute may not yet exist or may hold stale data at evaluation time. " +
1096
+ "Intended use: provisioning policies and entitlement request forms. " +
1097
+ "If you need an account attribute value inside an identity profile mapping, use accountAttribute instead.", "type");
1098
+ // 3. input: must be a nested transform object if provided
1099
+ if (attrs?.input !== undefined) {
1100
+ const inp = attrs.input;
1101
+ if (!(isPlainObject(inp) && typeof inp.type === "string")) {
1102
+ push(msgs, "warn", "input must be a nested transform object {type, attributes} providing explicit input data. " +
1103
+ "If omitted, the transform uses the source+attribute combination configured in the identity profile UI.", "attributes.input");
1104
+ }
1105
+ }
1106
+ return msgs;
1107
+ }
1108
+ // ---------------------------------------------------------------------------
1109
+ // 27. indexOf / lastIndexOf
1077
1110
  // ---------------------------------------------------------------------------
1078
1111
  function lintIndexOf(t, attrs) {
1079
1112
  const msgs = [];
@@ -1086,7 +1119,7 @@ function lintIndexOf(t, attrs) {
1086
1119
  return msgs;
1087
1120
  }
1088
1121
  // ---------------------------------------------------------------------------
1089
- // 26. randomAlphaNumeric / randomNumeric — length
1122
+ // 28. randomAlphaNumeric / randomNumeric — length
1090
1123
  // ---------------------------------------------------------------------------
1091
1124
  function lintRandom(t, attrs) {
1092
1125
  const msgs = [];
@@ -1102,7 +1135,7 @@ function lintRandom(t, attrs) {
1102
1135
  return msgs;
1103
1136
  }
1104
1137
  // ---------------------------------------------------------------------------
1105
- // 27. rfc5646 — format type check
1138
+ // 29. rfc5646 — format type check
1106
1139
  // ---------------------------------------------------------------------------
1107
1140
  function lintRfc5646(attrs) {
1108
1141
  const msgs = [];
@@ -1194,6 +1227,8 @@ export function lintTransform(input) {
1194
1227
  messages.push(...lintSubstring(attrs));
1195
1228
  if (requestedType === "static")
1196
1229
  messages.push(...lintStatic(attrs));
1230
+ if (requestedType === "identityAttribute")
1231
+ messages.push(...lintIdentityAttribute(attrs));
1197
1232
  if (requestedType === "indexOf" || requestedType === "lastIndexOf")
1198
1233
  messages.push(...lintIndexOf(requestedType, attrs));
1199
1234
  if (requestedType === "randomAlphaNumeric" || requestedType === "randomNumeric")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "isc-transforms-mcp",
3
- "version": "1.0.14",
3
+ "version": "1.0.15",
4
4
  "type": "module",
5
5
  "description": "MCP server for SailPoint Identity Security Cloud (ISC) Transform authoring — scaffold, strict lint, catalog, and safe upsert to live tenants.",
6
6
  "author": {