@firfi/huly-mcp 0.31.0 → 0.31.1

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.
Files changed (3) hide show
  1. package/README.md +5 -2
  2. package/dist/index.cjs +339 -12
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -269,7 +269,7 @@ The roadmap is driven by SDK parity and the project principle that this server s
269
269
 
270
270
  Highest-value additions for coding agents:
271
271
 
272
- - Generic space follow-ups: role assignment mutations, role/permission definition writes, generic space creation, and module-specific wrappers above the shared space foundation.
272
+ - Generic space follow-ups: role/permission definition writes, generic space creation, and module-specific wrappers above the shared space foundation. Generic space metadata, member/owner mutations, and typed-space role member mutations are covered by the shared spaces tools.
273
273
  - SDK discovery phase 2: space types, roles, permissions, plugin configuration, sequence metadata, and richer tool hints.
274
274
  - Drive follow-ups: drive create/update/delete, item move/rename/delete, adding new versions to existing files, permissions, and comments/activity.
275
275
  - Planner/ToDos: personal and project ToDo CRUD, scheduling, complete/reopen, priority, privacy/visibility, and document action items.
@@ -298,7 +298,7 @@ Planned feature surfaces:
298
298
  - Chat and communication: direct-message send/update/delete, group DMs, channel member mutations, join/leave/request access, archive/unarchive, star/favorite channels, close/reopen conversations, pinned messages, message attachments, translation, applets, in-message polls, and guest communication settings.
299
299
  - Notifications and activity: browser/push subscription internals, provider defaults, UI presenter/viewlet metadata, and activity control/extension metadata.
300
300
  - Attachments and media: previews/preview metadata and friendly wrappers for additional object types beyond issue/document.
301
- - Core schema and workspace administration: attribute/property create/update/delete/hide, enum CRUD/options, sequence management, role assignment mutations, role/permission definition writes, generic space creation, global space admins, integrations registry, invite settings, role capability settings, and workspace setting metadata.
301
+ - Core schema and workspace administration: attribute/property create/update/delete/hide, enum CRUD/options, sequence management, role/permission definition writes, generic space creation, global space admins, integrations registry, invite settings, role capability settings, and workspace setting metadata.
302
302
  - Integrations: GitHub repository/project mappings and sync metadata (deferred), Google Calendar connect/configure/sync controls, Bitrix entity/field mappings and sync status, Gmail/email channel messages, Telegram messages, Huly Mail/Mail plugin behavior, AI assistant integration state, and AI bot configuration if server-side APIs expose stable behavior.
303
303
  - Templates, rating, support, billing, analytics, views, workbench, and preferences: message templates/categories/fields, document/person rating data blocked by unpublished `@hcengineering/rating` SDK package (#90), support conversations, billing tier/status discovery, onboarding channels, saved filtered views, user view preferences, tabs/widgets/apps, and module preference discovery/update.
304
304
  - Document-specific gaps: snapshot restore, backlinks, notes, structured action items/tables, PDF/export, advanced document relationships, and document printing/export once SDK support is safe.
@@ -740,6 +740,9 @@ SDK upgrade revisit:
740
740
  | `add_space_members` | Idempotently add members to an existing Huly space. Members accept account UUID, exact email, or exact person display name and resolve to Huly account UUIDs before replacing the full members array. |
741
741
  | `remove_space_members` | Idempotently remove members from an existing Huly space. Members accept account UUID, exact email, or exact person display name and resolve to Huly account UUIDs before replacing the full members array. |
742
742
  | `set_space_owners` | Replace owners on an existing Huly space. Owners accept account UUID, exact email, or exact person display name. By default, owners are also ensured in members. |
743
+ | `set_space_role_members` | Replace members assigned to one role on a typed Huly space while preserving all other role assignments. Role accepts a raw role _id or exact role name from the space's SpaceType. Members accept account UUID, exact email, or exact person display name; pass members=[] to clear this role. |
744
+ | `add_space_role_members` | Idempotently add members to one role on a typed Huly space while preserving all other role assignments. Role accepts a raw role _id or exact role name from the space's SpaceType. Members accept account UUID, exact email, or exact person display name. |
745
+ | `remove_space_role_members` | Idempotently remove members from one role on a typed Huly space while preserving all other role assignments. Role accepts a raw role _id or exact role name from the space's SpaceType. Members accept account UUID, exact email, or exact person display name. |
743
746
 
744
747
  ### Tag-Categories
745
748
 
package/dist/index.cjs CHANGED
@@ -154570,6 +154570,10 @@ var AmbiguousSpaceTypeMatchSchema = Schema_exports.Struct({
154570
154570
  name: NonEmptyString2,
154571
154571
  targetClass: ObjectClassName
154572
154572
  });
154573
+ var AmbiguousSpaceRoleMatchSchema = Schema_exports.Struct({
154574
+ id: RoleId,
154575
+ name: NonEmptyString2
154576
+ });
154573
154577
  var SpaceNotFoundError = class extends Schema_exports.TaggedError()(
154574
154578
  "SpaceNotFoundError",
154575
154579
  {
@@ -154614,6 +154618,54 @@ var SpaceTypeIdentifierAmbiguousError = class extends Schema_exports.TaggedError
154614
154618
  return `Space type '${this.identifier}' is ambiguous; use a space type id. Matches: ${details}`;
154615
154619
  }
154616
154620
  };
154621
+ var SpaceNotTypedError = class extends Schema_exports.TaggedError()(
154622
+ "SpaceNotTypedError",
154623
+ {
154624
+ id: SpaceId,
154625
+ name: NonEmptyString2
154626
+ }
154627
+ ) {
154628
+ get message() {
154629
+ return `Space '${this.name}' (${this.id}) is not typed; role members can only be changed on spaces with a SpaceType`;
154630
+ }
154631
+ };
154632
+ var SpaceRoleNotFoundError = class extends Schema_exports.TaggedError()(
154633
+ "SpaceRoleNotFoundError",
154634
+ {
154635
+ identifier: NonEmptyString2,
154636
+ spaceType: SpaceTypeId
154637
+ }
154638
+ ) {
154639
+ get message() {
154640
+ return `Role '${this.identifier}' not found in space type '${this.spaceType}'`;
154641
+ }
154642
+ };
154643
+ var SpaceRoleIdentifierAmbiguousError = class extends Schema_exports.TaggedError()(
154644
+ "SpaceRoleIdentifierAmbiguousError",
154645
+ {
154646
+ identifier: NonEmptyString2,
154647
+ spaceType: SpaceTypeId,
154648
+ matches: Schema_exports.Array(AmbiguousSpaceRoleMatchSchema).pipe(Schema_exports.minItems(MIN_AMBIGUOUS_SPACE_MATCHES))
154649
+ }
154650
+ ) {
154651
+ get message() {
154652
+ const details = this.matches.map((match16) => `${match16.id} (${match16.name})`).join(", ");
154653
+ return `Role '${this.identifier}' is ambiguous in space type '${this.spaceType}'; use a role id. Matches: ${details}`;
154654
+ }
154655
+ };
154656
+ var SpaceRoleAssignmentsMalformedError = class extends Schema_exports.TaggedError()(
154657
+ "SpaceRoleAssignmentsMalformedError",
154658
+ {
154659
+ space: SpaceId,
154660
+ spaceType: SpaceTypeId,
154661
+ targetClass: ObjectClassName,
154662
+ reason: NonEmptyString2
154663
+ }
154664
+ ) {
154665
+ get message() {
154666
+ return `Role assignments for space '${this.space}' and space type '${this.spaceType}' are malformed at '${this.targetClass}': ${this.reason}. Refusing to write role members to avoid access-control data loss.`;
154667
+ }
154668
+ };
154617
154669
 
154618
154670
  // src/huly/errors-test-management.ts
154619
154671
  var TestProjectNotFoundError = class extends Schema_exports.TaggedError()(
@@ -154890,6 +154942,10 @@ var HulyDomainError = Schema_exports.Union(
154890
154942
  HulyClassNotFoundError,
154891
154943
  SpaceNotFoundError,
154892
154944
  SpaceIdentifierAmbiguousError,
154945
+ SpaceNotTypedError,
154946
+ SpaceRoleNotFoundError,
154947
+ SpaceRoleIdentifierAmbiguousError,
154948
+ SpaceRoleAssignmentsMalformedError,
154893
154949
  SpaceTypeNotFoundError,
154894
154950
  SpaceTypeIdentifierAmbiguousError,
154895
154951
  TodoNotFoundError,
@@ -155423,6 +155479,7 @@ var import_api_client3 = __toESM(require_lib20(), 1);
155423
155479
  // src/huly/operations/sdk-boundary.ts
155424
155480
  var toRef = (id) => id;
155425
155481
  var toClassRef = (id) => id;
155482
+ var toMixinRef = (id) => id;
155426
155483
  var toAccountUuid = (uuid5) => uuid5;
155427
155484
  var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
155428
155485
  var validatePersonUuid = (uuid5) => {
@@ -165843,6 +165900,9 @@ var INVALID_PARAMS_TAGS = /* @__PURE__ */ new Set([
165843
165900
  "GenericObjectNotFoundError",
165844
165901
  "SpaceNotFoundError",
165845
165902
  "SpaceIdentifierAmbiguousError",
165903
+ "SpaceNotTypedError",
165904
+ "SpaceRoleNotFoundError",
165905
+ "SpaceRoleIdentifierAmbiguousError",
165846
165906
  "SpaceTypeNotFoundError",
165847
165907
  "SpaceTypeIdentifierAmbiguousError",
165848
165908
  "DriveNotFoundError",
@@ -166487,12 +166547,16 @@ var StdioServerTransport = class {
166487
166547
  };
166488
166548
 
166489
166549
  // src/domain/schemas/tool-warnings.ts
166490
- var ToolWarningCodeSchema = Schema_exports.Literal("status_metadata_unresolved").annotations({
166550
+ var ToolWarningCodeSchema = Schema_exports.Literal(
166551
+ "status_metadata_unresolved",
166552
+ "space_role_assignments_degraded"
166553
+ ).annotations({
166491
166554
  identifier: "ToolWarningCode",
166492
166555
  title: "ToolWarningCode",
166493
166556
  description: "Machine-readable code for an agent-visible MCP tool warning."
166494
166557
  });
166495
166558
  var StatusMetadataUnresolvedWarningCode = ToolWarningCodeSchema.literals[0];
166559
+ var SpaceRoleAssignmentsDegradedWarningCode = ToolWarningCodeSchema.literals[1];
166496
166560
  var ToolWarningSchema = Schema_exports.Struct({
166497
166561
  code: ToolWarningCodeSchema,
166498
166562
  message: Schema_exports.Trim.pipe(Schema_exports.nonEmptyString()).annotations({
@@ -166554,9 +166618,13 @@ var clearableText = (description) => Schema_exports.NullOr(Schema_exports.String
166554
166618
  var limitDescription = (subject) => `Maximum ${subject} to return (default: ${DEFAULT_LIMIT}).`;
166555
166619
  var SpacePermissionScopeSchema = Schema_exports.Literal("space", "workspace");
166556
166620
  var SpaceMemberIdentifier = NonEmptyString2.pipe(Schema_exports.brand("SpaceMemberIdentifier"));
166621
+ var SpaceRoleIdentifier = NonEmptyString2.pipe(Schema_exports.brand("SpaceRoleIdentifier"));
166557
166622
  var SpaceMemberIdentifierSchema = SpaceMemberIdentifier.annotations({
166558
166623
  description: "Workspace member to resolve. Accepts a Huly account UUID directly, an exact email address, or an exact person display name."
166559
166624
  });
166625
+ var SpaceRoleIdentifierSchema = SpaceRoleIdentifier.annotations({
166626
+ description: "Role to resolve within the space's SpaceType. Accepts a raw Huly role _id or an exact role name from get_space_type."
166627
+ });
166560
166628
  var SpaceRoleAssignmentSchema = Schema_exports.Struct({
166561
166629
  roleId: RoleId,
166562
166630
  members: Schema_exports.Array(AccountUuid)
@@ -166731,6 +166799,28 @@ var SetSpaceOwnersParamsSchema = Schema_exports.Struct({
166731
166799
  description: `Also add each owner to members. Defaults to ${DEFAULT_SPACE_OWNER_ENSURE_MEMBERS}.`
166732
166800
  }))
166733
166801
  });
166802
+ var SpaceRoleMemberMutationFields = {
166803
+ space: SpaceIdentifier.annotations({
166804
+ description: "Typed space _id or exact space name whose role assignment should change. The space must have a SpaceType."
166805
+ }),
166806
+ class: Schema_exports.optional(SpaceClassFilter.annotations({
166807
+ description: "Optional raw Huly space class ID used to disambiguate exact-name lookup."
166808
+ })),
166809
+ type: Schema_exports.optional(SpaceTypeId.annotations({
166810
+ description: "Optional raw Huly SpaceType _id used to disambiguate exact-name lookup."
166811
+ })),
166812
+ role: SpaceRoleIdentifierSchema,
166813
+ members: Schema_exports.Array(SpaceMemberIdentifierSchema).pipe(Schema_exports.minItems(1)).annotations({
166814
+ description: "Members to add or remove from this role. Each entry may be an account UUID, exact email address, or exact person name."
166815
+ })
166816
+ };
166817
+ var SpaceRoleMemberMutationParamsSchema = Schema_exports.Struct(SpaceRoleMemberMutationFields);
166818
+ var SetSpaceRoleMembersParamsSchema = Schema_exports.Struct({
166819
+ ...SpaceRoleMemberMutationFields,
166820
+ members: Schema_exports.Array(SpaceMemberIdentifierSchema).annotations({
166821
+ description: "Replacement member list for this role only. Each entry may be an account UUID, exact email address, or exact person name. Pass [] to clear this role."
166822
+ })
166823
+ });
166734
166824
  var listSpacesParamsJsonSchema = JSONSchema_exports.make(ListSpacesParamsSchema);
166735
166825
  var getSpaceParamsJsonSchema = JSONSchema_exports.make(GetSpaceParamsSchema);
166736
166826
  var listSpaceTypesParamsJsonSchema = JSONSchema_exports.make(ListSpaceTypesParamsSchema);
@@ -166742,6 +166832,8 @@ var updateSpaceParamsJsonSchema = withAtLeastOneRequired(
166742
166832
  );
166743
166833
  var spaceMemberMutationParamsJsonSchema = JSONSchema_exports.make(SpaceMemberMutationParamsSchema);
166744
166834
  var setSpaceOwnersParamsJsonSchema = JSONSchema_exports.make(SetSpaceOwnersParamsSchema);
166835
+ var spaceRoleMemberMutationParamsJsonSchema = JSONSchema_exports.make(SpaceRoleMemberMutationParamsSchema);
166836
+ var setSpaceRoleMembersParamsJsonSchema = JSONSchema_exports.make(SetSpaceRoleMembersParamsSchema);
166745
166837
  var parseListSpacesParams = Schema_exports.decodeUnknown(ListSpacesParamsSchema);
166746
166838
  var parseGetSpaceParams = Schema_exports.decodeUnknown(GetSpaceParamsSchema);
166747
166839
  var parseListSpaceTypesParams = Schema_exports.decodeUnknown(ListSpaceTypesParamsSchema);
@@ -166750,6 +166842,8 @@ var parseListSpacePermissionsParams = Schema_exports.decodeUnknown(ListSpacePerm
166750
166842
  var parseUpdateSpaceParams = Schema_exports.decodeUnknown(UpdateSpaceParamsSchema);
166751
166843
  var parseSpaceMemberMutationParams = Schema_exports.decodeUnknown(SpaceMemberMutationParamsSchema);
166752
166844
  var parseSetSpaceOwnersParams = Schema_exports.decodeUnknown(SetSpaceOwnersParamsSchema);
166845
+ var parseSpaceRoleMemberMutationParams = Schema_exports.decodeUnknown(SpaceRoleMemberMutationParamsSchema);
166846
+ var parseSetSpaceRoleMembersParams = Schema_exports.decodeUnknown(SetSpaceRoleMembersParamsSchema);
166753
166847
 
166754
166848
  // src/domain/schemas/sdk-discovery.ts
166755
166849
  var import_core10 = __toESM(require_lib4(), 1);
@@ -174642,7 +174736,7 @@ var makeDiagnosticsScope = Effect_exports.gen(function* () {
174642
174736
  });
174643
174737
 
174644
174738
  // src/version.ts
174645
- var VERSION = true ? "0.31.0" : "0.0.0-dev";
174739
+ var VERSION = true ? "0.31.1" : "0.0.0-dev";
174646
174740
 
174647
174741
  // src/mcp/tool-output-schema.ts
174648
174742
  var toolWarningCodeEnum = [...ToolWarningCodeSchema.literals];
@@ -175142,6 +175236,93 @@ var removeAccountUuids = (current, removals) => {
175142
175236
  var arraysEqual = (left3, right3) => left3.length === right3.length && left3.every((value3, index) => value3 === right3[index]);
175143
175237
  var optionalString = (value3) => value3 === void 0 || value3 === "" ? void 0 : value3;
175144
175238
  var optionalObjectClassName = (value3) => value3 === void 0 || value3 === "" ? void 0 : ObjectClassName.make(value3);
175239
+ var RoleAssignmentStorageSchema = Schema_exports.Record({ key: Schema_exports.String, value: Schema_exports.Array(AccountUuid) });
175240
+ var isRecordObject = (value3) => typeof value3 === "object" && value3 !== null && !Array.isArray(value3);
175241
+ var validStoredAccountUuid = (value3) => {
175242
+ const decoded = Schema_exports.decodeUnknownEither(AccountUuid)(value3);
175243
+ return decoded._tag === "Right" ? toAccountUuid(NonEmptyString2.make(decoded.right)) : void 0;
175244
+ };
175245
+ var parsedSpaceRoleAssignmentEntry = (roleId, members) => ({
175246
+ _tag: "entry",
175247
+ entry: [
175248
+ toRef(roleId),
175249
+ sortStrings(members).map(toAccountUuid)
175250
+ ]
175251
+ });
175252
+ var spaceRoleAssignmentSource = (space, spaceType) => Object.prototype.hasOwnProperty.call(space, spaceType.targetClass) ? { present: true, value: Object.entries(space).find(([key]) => key === spaceType.targetClass)?.[1] } : { present: false, value: void 0 };
175253
+ var roleAssignmentsMalformedError = (space, spaceType, reason) => new SpaceRoleAssignmentsMalformedError({
175254
+ space: SpaceId.make(space._id),
175255
+ spaceType: SpaceTypeId.make(spaceType._id),
175256
+ targetClass: ObjectClassName.make(spaceType.targetClass),
175257
+ reason: NonEmptyString2.make(reason)
175258
+ });
175259
+ var readSpaceRoleAssignmentEntries = (space, spaceType, validRoleIds) => {
175260
+ const source = spaceRoleAssignmentSource(space, spaceType);
175261
+ if (!source.present) return { entries: [], degradationReasons: [] };
175262
+ if (!isRecordObject(source.value)) {
175263
+ return {
175264
+ entries: [],
175265
+ degradationReasons: [`role assignment mixin ${spaceType.targetClass} is not an object`]
175266
+ };
175267
+ }
175268
+ const parsed = Object.entries(source.value).flatMap(([roleId, members]) => {
175269
+ if (!validRoleIds.has(toRef(roleId))) {
175270
+ return [{
175271
+ _tag: "dropped",
175272
+ reason: `role assignment '${roleId}' is not defined on space type ${spaceType._id}`
175273
+ }];
175274
+ }
175275
+ if (!Array.isArray(members)) {
175276
+ return [{ _tag: "dropped", reason: `role assignment '${roleId}' members are not an array` }];
175277
+ }
175278
+ const accountUuids = members.flatMap((member) => {
175279
+ const accountUuid = validStoredAccountUuid(member);
175280
+ return accountUuid === void 0 ? [] : [accountUuid];
175281
+ });
175282
+ const invalidMemberCount = members.length - accountUuids.length;
175283
+ return [
175284
+ ...invalidMemberCount > 0 ? [{
175285
+ _tag: "dropped",
175286
+ reason: `role assignment '${roleId}' has ${invalidMemberCount} malformed account UUID value(s)`
175287
+ }] : [],
175288
+ parsedSpaceRoleAssignmentEntry(roleId, accountUuids)
175289
+ ];
175290
+ });
175291
+ return {
175292
+ entries: parsed.flatMap((item) => item._tag === "entry" ? [item.entry] : []),
175293
+ degradationReasons: parsed.flatMap((item) => item._tag === "dropped" ? [item.reason] : [])
175294
+ };
175295
+ };
175296
+ var spaceRoleAssignmentEntries = (space, spaceType, validRoleIds) => readSpaceRoleAssignmentEntries(space, spaceType, validRoleIds).entries;
175297
+ var hasSpaceRoleAssignmentMixin = (space, spaceType) => spaceRoleAssignmentSource(space, spaceType).present;
175298
+ var strictSpaceRoleAssignments = (space, spaceType, validRoleIds) => Effect_exports.gen(function* () {
175299
+ const source = spaceRoleAssignmentSource(space, spaceType);
175300
+ if (!source.present) return {};
175301
+ const decoded = Schema_exports.decodeUnknownEither(RoleAssignmentStorageSchema)(source.value);
175302
+ if (decoded._tag === "Left") {
175303
+ return yield* roleAssignmentsMalformedError(
175304
+ space,
175305
+ spaceType,
175306
+ `expected an object whose keys are role ids and values are arrays of account UUIDs`
175307
+ );
175308
+ }
175309
+ const unknownRoleIds = Object.keys(decoded.right).filter((roleId) => !validRoleIds.has(toRef(roleId)));
175310
+ if (unknownRoleIds.length > 0) {
175311
+ return yield* roleAssignmentsMalformedError(
175312
+ space,
175313
+ spaceType,
175314
+ `unknown role assignment key(s): ${unknownRoleIds.join(", ")}`
175315
+ );
175316
+ }
175317
+ return Object.fromEntries(
175318
+ Object.entries(decoded.right).map(([roleId, members]) => [
175319
+ toRef(roleId),
175320
+ members.map((member) => toAccountUuid(NonEmptyString2.make(member)))
175321
+ ])
175322
+ );
175323
+ });
175324
+ var roleAssignmentDegradationMessage = (reasons) => `Some typed-space role assignment data was omitted because existing Huly storage is malformed: ${reasons.join("; ")}. Read results include only valid role assignments; role-member write tools will refuse to modify this space until the stored role assignment data is repaired.`;
175325
+ var spaceRoleAssignmentsMixin = (spaceType) => toMixinRef(spaceType.targetClass);
175145
175326
  var applySpaceFilters = (query, filters) => {
175146
175327
  const next4 = { ...query };
175147
175328
  if (!filters.includeArchived) {
@@ -178923,7 +179104,7 @@ var contactCoveredRationale = "Current contacts tools expose person, organizatio
178923
179104
  var cardCoveredRationale = "Current card tools cover card spaces, master tags, and card CRUD.";
178924
179105
  var chunterCoveredRationale = "Current channel and direct-message tools cover channels, channel messages, one-to-one DM listing, and thread replies.";
178925
179106
  var coreCoveredRationale = "Existing tools expose user statuses, full-text search, blobs through storage/download flows, generic association/relation discovery/mutation helpers, class/interface/mixin, attribute, enum, plugin configuration, domain index configuration, sequence, and space type capability discovery.";
178926
- var coreGapRationale = "Remaining core write-side configuration, role assignment writes, role/permission definition writes, generic space creation, class collaborator metadata, statuses, and write-side model management are represented as matrix gaps. Generic space discovery, space type/permission reads, safe existing-space metadata updates, member mutations, owner replacement, object collaborators, read-only plugin configuration, domain index configuration, and sequence discovery are covered.";
179107
+ var coreGapRationale = "Remaining core write-side configuration, role/permission definition writes, generic space creation, class collaborator metadata, statuses, and write-side model management are represented as matrix gaps. Generic space discovery, space type/permission reads, safe existing-space metadata updates, member mutations, owner replacement, typed-space role member mutations, object collaborators, read-only plugin configuration, domain index configuration, and sequence discovery are covered.";
178927
179108
  var coreNotMcpFacingRationale = "Core primitive model infrastructure, transaction classes, type wrappers, and versioning internals are not LLM-facing product resources by themselves.";
178928
179109
  var routingRow = (classId, packageName, exportName, hint) => ({
178929
179110
  classId: ObjectClassName.make(classId),
@@ -189784,14 +189965,15 @@ var toSpaceSummary = (space) => ({
189784
189965
  membersCount: Count.make(space.members.length),
189785
189966
  ownersCount: Count.make(space.owners?.length ?? 0)
189786
189967
  });
189787
- var roleAssignments = (space) => {
189788
- if (space.roles === void 0) return void 0;
189789
- return Object.entries(space.roles).map(([roleId, members]) => ({
189968
+ var roleAssignments = (space, spaceType, validRoleIds) => {
189969
+ if (spaceType === void 0) return void 0;
189970
+ const entries2 = spaceRoleAssignmentEntries(space, spaceType, validRoleIds);
189971
+ return entries2.length === 0 ? void 0 : entries2.map(([roleId, members]) => ({
189790
189972
  roleId: RoleId.make(roleId),
189791
- members: (members ?? []).map((member) => AccountUuid.make(member))
189973
+ members: members.map((member) => AccountUuid.make(member))
189792
189974
  }));
189793
189975
  };
189794
- var toSpaceDetail = (space) => ({
189976
+ var toSpaceDetail = (space, spaceType, validRoleIds = /* @__PURE__ */ new Set()) => ({
189795
189977
  id: SpaceId.make(space._id),
189796
189978
  name: space.name,
189797
189979
  description: space.description,
@@ -189802,7 +189984,7 @@ var toSpaceDetail = (space) => ({
189802
189984
  autoJoin: space.autoJoin,
189803
189985
  members: space.members.map((member) => AccountUuid.make(member)),
189804
189986
  owners: (space.owners ?? []).map((owner) => AccountUuid.make(owner)),
189805
- roleAssignments: roleAssignments(space)
189987
+ roleAssignments: roleAssignments(space, spaceType, validRoleIds)
189806
189988
  });
189807
189989
  var spaceTypeSummary = (spaceType, descriptor3) => ({
189808
189990
  id: SpaceTypeId.make(spaceType._id),
@@ -189884,8 +190066,28 @@ var listSpaces = (params) => Effect_exports.gen(function* () {
189884
190066
  });
189885
190067
  var getSpace = (params) => Effect_exports.gen(function* () {
189886
190068
  const client = yield* HulyClient;
190069
+ const diagnostics = yield* Diagnostics;
189887
190070
  const space = yield* findSpace(client, params);
189888
- return toSpaceDetail(space);
190071
+ const spaceType = space.type === void 0 ? void 0 : yield* client.findOne(
190072
+ core.class.SpaceType,
190073
+ hulyQuery({ _id: toRef(space.type) })
190074
+ );
190075
+ const roles = spaceType === void 0 ? [] : yield* client.findAll(
190076
+ core.class.Role,
190077
+ hulyQuery({ attachedTo: spaceType._id }),
190078
+ { limit: Math.max(spaceType.roles, 1) }
190079
+ );
190080
+ const validRoleIds = new Set(roles.map((role) => role._id));
190081
+ if (spaceType !== void 0) {
190082
+ const readResult = readSpaceRoleAssignmentEntries(space, spaceType, validRoleIds);
190083
+ if (readResult.degradationReasons.length > 0) {
190084
+ yield* diagnostics.warnAgent({
190085
+ code: SpaceRoleAssignmentsDegradedWarningCode,
190086
+ message: roleAssignmentDegradationMessage(readResult.degradationReasons)
190087
+ });
190088
+ }
190089
+ }
190090
+ return toSpaceDetail(space, spaceType, validRoleIds);
189889
190091
  });
189890
190092
  var listSpaceTypes = (params) => Effect_exports.gen(function* () {
189891
190093
  const client = yield* HulyClient;
@@ -190068,8 +190270,8 @@ var describeHulySpaceTypeCapabilities = (params) => Effect_exports.gen(function*
190068
190270
  roles: detail.roles,
190069
190271
  rolePermissions: detail.availablePermissions,
190070
190272
  assignmentShape: {
190071
- storedOnSpaceField: HulyConfigurationMetadataKey.make("roles"),
190072
- roleKeyField: HulyConfigurationMetadataKey.make("roleId"),
190273
+ storedOnSpaceField: HulyConfigurationMetadataKey.make(`mixin:${detail.targetClass}`),
190274
+ roleKeyField: HulyConfigurationMetadataKey.make("role._id"),
190073
190275
  memberValueShape: "accountUuidArrayOrUndefined",
190074
190276
  readProjectionTools: ["get_space"].map((tool) => HulyMcpToolName.make(tool))
190075
190277
  }
@@ -190403,6 +190605,104 @@ var searchTools = [
190403
190605
  ];
190404
190606
 
190405
190607
  // src/huly/operations/spaces-write.ts
190608
+ var roleClass = core.class.Role;
190609
+ var spaceTypeClass = core.class.SpaceType;
190610
+ var requireTypedSpaceType = (space) => Effect_exports.gen(function* () {
190611
+ if (space.type === void 0) {
190612
+ return yield* new SpaceNotTypedError({
190613
+ id: SpaceId.make(space._id),
190614
+ name: NonEmptyString2.make(space.name)
190615
+ });
190616
+ }
190617
+ return SpaceTypeId.make(space.type);
190618
+ });
190619
+ var findSpaceType2 = (client, spaceType) => Effect_exports.gen(function* () {
190620
+ const result = yield* client.findOne(
190621
+ spaceTypeClass,
190622
+ hulyQuery({ _id: toRef(spaceType) })
190623
+ );
190624
+ if (result === void 0) {
190625
+ return yield* new SpaceRoleNotFoundError({
190626
+ identifier: NonEmptyString2.make("SpaceType roles"),
190627
+ spaceType
190628
+ });
190629
+ }
190630
+ return result;
190631
+ });
190632
+ var resolveSpaceRole = (client, spaceType, role) => Effect_exports.gen(function* () {
190633
+ const byId = yield* client.findOne(
190634
+ roleClass,
190635
+ hulyQuery({
190636
+ _id: toRef(role),
190637
+ attachedTo: toRef(spaceType)
190638
+ })
190639
+ );
190640
+ if (byId !== void 0) return byId;
190641
+ const matches = yield* client.findAll(
190642
+ roleClass,
190643
+ hulyQuery({
190644
+ attachedTo: toRef(spaceType),
190645
+ name: role
190646
+ }),
190647
+ { limit: 2 }
190648
+ );
190649
+ if (matches.length === 0) {
190650
+ return yield* new SpaceRoleNotFoundError({
190651
+ identifier: NonEmptyString2.make(role),
190652
+ spaceType
190653
+ });
190654
+ }
190655
+ if (matches.length > 1) {
190656
+ return yield* new SpaceRoleIdentifierAmbiguousError({
190657
+ identifier: NonEmptyString2.make(role),
190658
+ spaceType,
190659
+ matches: matches.map((match16) => ({
190660
+ id: RoleId.make(match16._id),
190661
+ name: NonEmptyString2.make(match16.name)
190662
+ }))
190663
+ });
190664
+ }
190665
+ return matches[0];
190666
+ });
190667
+ var findSpaceTypeRoles = (client, spaceType) => client.findAll(
190668
+ roleClass,
190669
+ hulyQuery({ attachedTo: spaceType._id }),
190670
+ { limit: Math.max(spaceType.roles, 1) }
190671
+ );
190672
+ var writeSpaceRoleMembers = (client, space, spaceType, role, currentAssignments, members) => {
190673
+ const mixin = spaceRoleAssignmentsMixin(spaceType);
190674
+ const attributes = { ...currentAssignments, [role._id]: members };
190675
+ const objectId = toRef(space._id);
190676
+ const objectClass = toClassRef(space._class);
190677
+ const objectSpace = toRef(space.space);
190678
+ return hasSpaceRoleAssignmentMixin(space, spaceType) ? client.updateMixin(objectId, objectClass, objectSpace, mixin, attributes).pipe(Effect_exports.asVoid) : client.createMixin(objectId, objectClass, objectSpace, mixin, attributes).pipe(Effect_exports.asVoid);
190679
+ };
190680
+ var mutateSpaceRoleMembers = (params, mutateMembers) => Effect_exports.gen(function* () {
190681
+ const client = yield* HulyClient;
190682
+ const space = yield* findSpace(client, params);
190683
+ const spaceType = yield* requireTypedSpaceType(space);
190684
+ const spaceTypeDoc = yield* findSpaceType2(client, spaceType);
190685
+ const role = yield* resolveSpaceRole(client, spaceType, params.role);
190686
+ const validRoles = yield* findSpaceTypeRoles(client, spaceTypeDoc);
190687
+ const resolvedMembers = yield* resolveMembers(client, params.members);
190688
+ const currentAssignments = yield* strictSpaceRoleAssignments(
190689
+ space,
190690
+ spaceTypeDoc,
190691
+ new Set(validRoles.map((validRole) => validRole._id))
190692
+ );
190693
+ const currentMembers = sortStrings(currentAssignments[role._id] ?? []).map(toAccountUuid);
190694
+ const nextMembers = mutateMembers(currentMembers, resolvedMembers).map(toAccountUuid);
190695
+ const changed = !arraysEqual(currentMembers, nextMembers);
190696
+ if (changed) {
190697
+ yield* writeSpaceRoleMembers(client, space, spaceTypeDoc, role, currentAssignments, nextMembers);
190698
+ }
190699
+ return {
190700
+ id: SpaceId.make(space._id),
190701
+ roleId: RoleId.make(role._id),
190702
+ members: nextMembers.map((member) => AccountUuid.make(member)),
190703
+ changed
190704
+ };
190705
+ });
190406
190706
  var mutateSpaceMembers = (params, mutateMembers) => Effect_exports.gen(function* () {
190407
190707
  const client = yield* HulyClient;
190408
190708
  const space = yield* findSpace(client, params);
@@ -190455,6 +190755,9 @@ var setSpaceOwners = (params) => Effect_exports.gen(function* () {
190455
190755
  changed: changedOwners || changedMembers
190456
190756
  };
190457
190757
  });
190758
+ var setSpaceRoleMembers = (params) => mutateSpaceRoleMembers(params, (_currentMembers, resolvedMembers) => sortStrings(resolvedMembers).map(toAccountUuid));
190759
+ var addSpaceRoleMembers = (params) => mutateSpaceRoleMembers(params, mergeUniqueSortedAccountUuids);
190760
+ var removeSpaceRoleMembers = (params) => mutateSpaceRoleMembers(params, removeAccountUuids);
190458
190761
 
190459
190762
  // src/mcp/tools/spaces.ts
190460
190763
  var CATEGORY28 = "spaces";
@@ -190521,6 +190824,30 @@ var spaceTools = [
190521
190824
  category: CATEGORY28,
190522
190825
  inputSchema: setSpaceOwnersParamsJsonSchema,
190523
190826
  handler: createToolHandler("set_space_owners", parseSetSpaceOwnersParams, setSpaceOwners)
190827
+ },
190828
+ {
190829
+ name: "set_space_role_members",
190830
+ description: "Replace members assigned to one role on a typed Huly space while preserving all other role assignments. Role accepts a raw role _id or exact role name from the space's SpaceType. Members accept account UUID, exact email, or exact person display name; pass members=[] to clear this role.",
190831
+ category: CATEGORY28,
190832
+ inputSchema: setSpaceRoleMembersParamsJsonSchema,
190833
+ annotations: { idempotentHint: true, destructiveHint: false },
190834
+ handler: createToolHandler("set_space_role_members", parseSetSpaceRoleMembersParams, setSpaceRoleMembers)
190835
+ },
190836
+ {
190837
+ name: "add_space_role_members",
190838
+ description: "Idempotently add members to one role on a typed Huly space while preserving all other role assignments. Role accepts a raw role _id or exact role name from the space's SpaceType. Members accept account UUID, exact email, or exact person display name.",
190839
+ category: CATEGORY28,
190840
+ inputSchema: spaceRoleMemberMutationParamsJsonSchema,
190841
+ annotations: { idempotentHint: true, destructiveHint: false },
190842
+ handler: createToolHandler("add_space_role_members", parseSpaceRoleMemberMutationParams, addSpaceRoleMembers)
190843
+ },
190844
+ {
190845
+ name: "remove_space_role_members",
190846
+ description: "Idempotently remove members from one role on a typed Huly space while preserving all other role assignments. Role accepts a raw role _id or exact role name from the space's SpaceType. Members accept account UUID, exact email, or exact person display name.",
190847
+ category: CATEGORY28,
190848
+ inputSchema: spaceRoleMemberMutationParamsJsonSchema,
190849
+ annotations: { idempotentHint: true, destructiveHint: false },
190850
+ handler: createToolHandler("remove_space_role_members", parseSpaceRoleMemberMutationParams, removeSpaceRoleMembers)
190524
190851
  }
190525
190852
  ];
190526
190853
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@firfi/huly-mcp",
3
- "version": "0.31.0",
3
+ "version": "0.31.1",
4
4
  "description": "MCP server for Huly integration",
5
5
  "mcpName": "io.github.dearlordylord/huly-mcp",
6
6
  "type": "module",