@firfi/huly-mcp 0.17.2 → 0.18.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 +6 -6
  2. package/dist/index.cjs +419 -191
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -273,7 +273,7 @@ Resource roadmap:
273
273
  |------|-------------|
274
274
  | `list_projects` | List all Huly projects. Returns projects sorted by name. Supports filtering by archived status. |
275
275
  | `get_project` | Get full details of a Huly project including its statuses. Returns project name, description, archived flag, default status, and all available statuses. |
276
- | `list_statuses` | List all issue statuses for a Huly project with category info. Returns status name, isDone, isCanceled, and isDefault flags. Use this to discover valid statuses before creating or updating issues. |
276
+ | `list_statuses` | List all issue statuses for a Huly project with workflow category and default info. Returns status name, category, and isDefault. Use this to discover valid statuses before creating or updating issues. |
277
277
  | `create_project` | Create a new Huly tracker project. Idempotent: returns existing project if one with the same identifier already exists (created=false). Identifier must be 1-5 uppercase alphanumeric chars starting with a letter. |
278
278
  | `update_project` | Update a Huly project. Only provided fields are modified. Set description to null to clear it. |
279
279
  | `delete_project` | Permanently delete a Huly project. All issues, milestones, and components in this project will be orphaned. This action cannot be undone. |
@@ -283,7 +283,7 @@ Resource roadmap:
283
283
  | Tool | Description |
284
284
  |------|-------------|
285
285
  | `preview_deletion` | Preview the impact of deleting a Huly entity before actually deleting it. Shows affected sub-entities, relations, and warnings. Supports issues, projects, components, and milestones. Use this to understand cascade effects before calling a delete operation. |
286
- | `list_issues` | Query Huly issues with optional filters. Returns issues sorted by modification date (newest first). Supports filtering by project, status, assignee, component, and parentIssue (to list children of a specific issue). Supports searching by title substring (titleSearch) and description content (descriptionSearch). |
286
+ | `list_issues` | Query Huly issues with optional filters. Returns issues sorted by modification date (newest first). Supports filtering by project, exact workflow status name (status), Huly SDK task.statusCategory key (statusCategory: UnStarted, ToDo, Active, Won, Lost), assignee, component, and parentIssue (to list children of a specific issue). Supports searching by title substring (titleSearch) and description content (descriptionSearch). |
287
287
  | `get_issue` | Retrieve full details for a Huly issue including markdown description. Use this to view issue content, comments, or full metadata. |
288
288
  | `create_issue` | Create a new issue in a Huly project. Optionally set taskType by ID or display name; it is resolved within the target project's project type, and status is validated against that task type's workflow. Use list_task_types or get_project_type to discover valid task types and statuses. Optionally create as a sub-issue by specifying parentIssue. Description supports markdown formatting. Returns the created issue identifier. |
289
289
  | `update_issue` | Update fields on an existing Huly issue. Optionally set taskType by ID or display name; it is resolved within the target project's project type, and the status is preserved only when valid for the new task type. Use list_task_types or get_project_type to discover valid task types and statuses. Only provided fields are modified. Description updates support markdown. |
@@ -453,9 +453,9 @@ Resource roadmap:
453
453
  | `list_associations` | List Huly association definitions: class-level typed links that define which document classes may be related. Use this before create_relation to discover association IDs, source/target classes, and whether relation writes are supported. |
454
454
  | `create_association` | Idempotently create one Huly association definition between two non-system classes. Use sourceClass/targetClass with sourceRole/targetRole and cardinality; returns an existing identical association by default. |
455
455
  | `delete_association` | Idempotently delete one Huly association definition only when no concrete relations reference it. If relations exist, delete_relation must clean them up first; deleting an already-missing association is a successful no-op. |
456
- | `list_relations` | List concrete Huly relation instances under an association, optionally filtered by source and target documents. Requires at least one filter to avoid broad workspace scans. |
457
- | `create_relation` | Idempotently create one concrete relation between two resolved documents for a writable association. Enforces association endpoint classes, direction, duplicate handling, automation-only restrictions, and cardinality. |
458
- | `delete_relation` | Idempotently delete one concrete relation by relation ID or by exact association/source/target triple. Triple deletes use the same direction semantics as create_relation and fail if the selector is ambiguous. |
456
+ | `list_relations` | List concrete Huly relation instances under an association, optionally filtered by source and target documents. Endpoint locators support raw, issue, document, and card. Requires at least one filter to avoid broad workspace scans. |
457
+ | `create_relation` | Idempotently create one concrete relation between two resolved documents for a writable association. Endpoint locators support raw, issue, document, and card. Enforces association endpoint classes, direction, duplicate handling, automation-only restrictions, and cardinality. |
458
+ | `delete_relation` | Idempotently delete one concrete relation by relation ID or by exact association/source/target triple. Triple endpoint locators support raw, issue, document, and card. Triple deletes use the same direction semantics as create_relation and fail if the selector is ambiguous. |
459
459
 
460
460
  ### Activity
461
461
 
@@ -568,7 +568,7 @@ Resource roadmap:
568
568
  | `get_project_type` | Inspect one Huly tracker project type in a single call. Accepts projectType as ID or display name; when omitted, uses the unambiguous Classic tracker type. Returns task types, statuses, categories, and task-type-to-status mappings. |
569
569
  | `list_task_types` | List Huly issue/task types. Optionally filter by projectType ID or display name. Returns task type identity, parent project type, kind, issue class, and available status count. |
570
570
  | `create_task_type` | Add a Huly issue/task type to a project type idempotently by normalized name. Copies required workflow configuration from an existing template task type unless templateTaskType is supplied. Returns created, IDs, affected task type IDs, and a workspace-level workflow warning. |
571
- | `create_issue_status` | Add a Huly issue workflow status idempotently by normalized name within a project type and task type scope. Accepts category as backlog, todo, active, done, or canceled; taskType may be ID or display name, and omission applies the status to every task type in the project type. |
571
+ | `create_issue_status` | Add a Huly issue workflow status idempotently by normalized name within a project type and task type scope. Accepts category as a Huly SDK task.statusCategory key: UnStarted, ToDo, Active, Won, Lost; taskType may be ID or display name, and omission applies the status to every task type in the project type. |
572
572
 
573
573
  ### Test-Management
574
574
 
package/dist/index.cjs CHANGED
@@ -77235,7 +77235,7 @@ var require_client5 = __commonJS({
77235
77235
  module2.exports = __toCommonJS2(client_exports);
77236
77236
  var import_core54 = require_lib4();
77237
77237
  var import_collaborator_client2 = require_lib7();
77238
- var import_text4 = require_lib9();
77238
+ var import_text5 = require_lib9();
77239
77239
  var import_text_markdown3 = require_lib17();
77240
77240
  function createMarkupOperations(url4, workspace, token, config3) {
77241
77241
  return new MarkupOperationsImpl(url4, workspace, token, config3);
@@ -77260,12 +77260,12 @@ var require_client5 = __commonJS({
77260
77260
  async fetchMarkup(objectClass, objectId, objectAttr, doc, format7) {
77261
77261
  const collabId = (0, import_core54.makeCollabId)(objectClass, objectId, objectAttr);
77262
77262
  const markup = await this.collaborator.getMarkup(collabId, doc);
77263
- const json3 = (0, import_text4.markupToJSON)(markup);
77263
+ const json3 = (0, import_text5.markupToJSON)(markup);
77264
77264
  switch (format7) {
77265
77265
  case "markup":
77266
77266
  return markup;
77267
77267
  case "html":
77268
- return (0, import_text4.jsonToHTML)(json3);
77268
+ return (0, import_text5.jsonToHTML)(json3);
77269
77269
  case "markdown":
77270
77270
  return (0, import_text_markdown3.markupToMarkdown)(json3, { refUrl: this.refUrl, imageUrl: this.imageUrl });
77271
77271
  default:
@@ -77279,10 +77279,10 @@ var require_client5 = __commonJS({
77279
77279
  markup = value3;
77280
77280
  break;
77281
77281
  case "html":
77282
- markup = (0, import_text4.jsonToMarkup)((0, import_text4.htmlToJSON)(value3));
77282
+ markup = (0, import_text5.jsonToMarkup)((0, import_text5.htmlToJSON)(value3));
77283
77283
  break;
77284
77284
  case "markdown":
77285
- markup = (0, import_text4.jsonToMarkup)((0, import_text_markdown3.markdownToMarkup)(value3, { refUrl: this.refUrl, imageUrl: this.imageUrl }));
77285
+ markup = (0, import_text5.jsonToMarkup)((0, import_text_markdown3.markdownToMarkup)(value3, { refUrl: this.refUrl, imageUrl: this.imageUrl }));
77286
77286
  break;
77287
77287
  default:
77288
77288
  throw new Error("Unknown content format");
@@ -151180,7 +151180,9 @@ var Email = Schema_exports.NonEmptyString.pipe(
151180
151180
  }),
151181
151181
  Schema_exports.brand("Email")
151182
151182
  );
151183
- var StatusName = NonEmptyString2.pipe(Schema_exports.brand("StatusName"));
151183
+ var StatusName = NonEmptyString2.annotations({
151184
+ description: "Exact workflow status display name from the target project. Status names are workspace data, not a fixed enum; call list_statuses or get_project_type to discover valid values."
151185
+ }).pipe(Schema_exports.brand("StatusName"));
151184
151186
  var PersonName = NonEmptyString2.pipe(Schema_exports.brand("PersonName"));
151185
151187
  var PersonRefInput = Schema_exports.Union(Email, PersonName);
151186
151188
  var ComponentLabel = NonEmptyString2.pipe(Schema_exports.brand("ComponentLabel"));
@@ -151585,6 +151587,7 @@ var RelationDirectionSchema = Schema_exports.Literal(...RelationDirectionValues)
151585
151587
  var DefaultRelationDirection = "source-to-target";
151586
151588
  var relationDirectionDescription = `Relation traversal direction: ${enumValuesDescription(RelationDirectionValues)}. Defaults to ${DefaultRelationDirection}.`;
151587
151589
  var RelationIfExistsSchema = Schema_exports.Literal("return_existing", "fail");
151590
+ var RelationEndpointFieldSchema = Schema_exports.Literal("source", "target");
151588
151591
  var AssociationIfExistsSchema = Schema_exports.Literal("return_existing", "fail");
151589
151592
  var RawObjectLocatorSchema = Schema_exports.Struct({
151590
151593
  kind: Schema_exports.Literal("raw"),
@@ -151613,19 +151616,29 @@ var DocumentObjectLocatorSchema = Schema_exports.Struct({
151613
151616
  description: "Teamspace name or ID. If omitted, document title matches must be unique across the workspace."
151614
151617
  }))
151615
151618
  });
151619
+ var CardObjectLocatorSchema = Schema_exports.Struct({
151620
+ kind: Schema_exports.Literal("card"),
151621
+ card: CardIdentifier.annotations({
151622
+ description: "Card ID or exact card title. Card IDs can be resolved without cardSpace; title lookup requires cardSpace."
151623
+ }),
151624
+ cardSpace: Schema_exports.optional(CardSpaceIdentifier.annotations({
151625
+ description: "Card space name or ID. Required when card is a title so title lookup is scoped and not ambiguous across the workspace."
151626
+ }))
151627
+ });
151616
151628
  var GenericObjectLocatorSchema = Schema_exports.Union(
151617
151629
  RawObjectLocatorSchema,
151618
151630
  IssueObjectLocatorSchema,
151619
- DocumentObjectLocatorSchema
151631
+ DocumentObjectLocatorSchema,
151632
+ CardObjectLocatorSchema
151620
151633
  ).annotations({
151621
151634
  title: "GenericObjectLocator",
151622
- description: "Explicit locator for a Huly document endpoint. Use raw for known _id values, issue for tracker issues, or document for Huly documents. Card locators are intentionally not included until a robust card resolver is available."
151635
+ description: "Explicit locator for a Huly document endpoint. Use raw for known _id/class pairs, issue for tracker issues, document for Huly documents, or card for Huly cards."
151623
151636
  });
151624
151637
  var ResolvedObjectSummarySchema = Schema_exports.Struct({
151625
151638
  id: DocId,
151626
151639
  class: ObjectClassName,
151627
151640
  display: NonEmptyString2,
151628
- locatorKind: Schema_exports.Literal("raw", "issue", "document"),
151641
+ locatorKind: Schema_exports.Literal("raw", "issue", "document", "card"),
151629
151642
  warning: Schema_exports.optional(Schema_exports.String)
151630
151643
  });
151631
151644
  var AssociationSummarySchema = Schema_exports.Struct({
@@ -151986,7 +151999,7 @@ var RelationDirectionAmbiguousError = class extends Schema_exports.TaggedError()
151986
151999
  var RelationEndpointClassMismatchError = class extends Schema_exports.TaggedError()(
151987
152000
  "RelationEndpointClassMismatchError",
151988
152001
  {
151989
- field: Schema_exports.String,
152002
+ field: RelationEndpointFieldSchema,
151990
152003
  expectedClass: Schema_exports.String,
151991
152004
  actualClass: Schema_exports.String
151992
152005
  }
@@ -151998,7 +152011,7 @@ var RelationEndpointClassMismatchError = class extends Schema_exports.TaggedErro
151998
152011
  var GenericObjectIdentifierAmbiguousError = class extends Schema_exports.TaggedError()(
151999
152012
  "GenericObjectIdentifierAmbiguousError",
152000
152013
  {
152001
- field: Schema_exports.String,
152014
+ field: RelationEndpointFieldSchema,
152002
152015
  identifier: Schema_exports.String,
152003
152016
  candidates: Schema_exports.Array(Schema_exports.Struct({
152004
152017
  id: DocId,
@@ -152015,7 +152028,7 @@ var GenericObjectIdentifierAmbiguousError = class extends Schema_exports.TaggedE
152015
152028
  var GenericObjectLocatorInvalidError = class extends Schema_exports.TaggedError()(
152016
152029
  "GenericObjectLocatorInvalidError",
152017
152030
  {
152018
- field: Schema_exports.String,
152031
+ field: RelationEndpointFieldSchema,
152019
152032
  reason: Schema_exports.String
152020
152033
  }
152021
152034
  ) {
@@ -152026,7 +152039,7 @@ var GenericObjectLocatorInvalidError = class extends Schema_exports.TaggedError(
152026
152039
  var GenericObjectNotFoundError = class extends Schema_exports.TaggedError()(
152027
152040
  "GenericObjectNotFoundError",
152028
152041
  {
152029
- field: Schema_exports.String,
152042
+ field: RelationEndpointFieldSchema,
152030
152043
  identifier: Schema_exports.String,
152031
152044
  class: Schema_exports.optional(Schema_exports.String)
152032
152045
  }
@@ -152855,14 +152868,51 @@ var HulyDomainError = Schema_exports.Union(
152855
152868
  // src/huly/operations/markup.ts
152856
152869
  var import_text = __toESM(require_lib9(), 1);
152857
152870
  var import_text_markdown = __toESM(require_lib17(), 1);
152871
+
152872
+ // src/huly/operations/inline-comment-mark.ts
152873
+ var INLINE_COMMENT_MARK_TYPE = "inline-comment";
152874
+ var isInlineCommentMark = (mark) => mark.type === INLINE_COMMENT_MARK_TYPE;
152875
+ var isMarkdownSerializableMark = (mark) => !isInlineCommentMark(mark);
152876
+
152877
+ // src/huly/operations/markup.ts
152858
152878
  var jsonAsMarkup = import_text.jsonToMarkup;
152859
152879
  var testMarkupUrlConfig = {
152860
152880
  refUrl: UrlString.make("https://test.invalid/browse?workspace=test"),
152861
152881
  imageUrl: UrlString.make("https://test.invalid/files?workspace=test&file=")
152862
152882
  };
152883
+ var removeMarkdownUnsupportedMarks = (marks) => {
152884
+ if (marks === void 0) {
152885
+ return { marks: void 0, changed: false };
152886
+ }
152887
+ const filtered = marks.filter(isMarkdownSerializableMark);
152888
+ return { marks: filtered, changed: filtered.length !== marks.length };
152889
+ };
152890
+ var sanitizeContentForMarkdown = (content) => {
152891
+ if (content === void 0) {
152892
+ return { content: void 0, changed: false };
152893
+ }
152894
+ const sanitized = content.map(sanitizeNodeForMarkdown);
152895
+ return {
152896
+ content: sanitized,
152897
+ changed: sanitized.some((node, index) => node !== content[index])
152898
+ };
152899
+ };
152900
+ var sanitizeNodeForMarkdown = (node) => {
152901
+ const content = sanitizeContentForMarkdown(node.content);
152902
+ const marks = removeMarkdownUnsupportedMarks(node.marks);
152903
+ if (!content.changed && !marks.changed) {
152904
+ return node;
152905
+ }
152906
+ return {
152907
+ ...node,
152908
+ ...content.content === void 0 ? {} : { content: content.content },
152909
+ ...marks.marks === void 0 ? {} : { marks: marks.marks }
152910
+ };
152911
+ };
152912
+ var markupNodeToMarkdownString = (node, urls, serialize2 = import_text_markdown.markupToMarkdown) => serialize2(sanitizeNodeForMarkdown(node), urls);
152863
152913
  var markupToMarkdownString = (markup, urls) => {
152864
152914
  const json3 = (0, import_text.markupToJSON)(markup);
152865
- return (0, import_text_markdown.markupToMarkdown)(json3, urls);
152915
+ return markupNodeToMarkdownString(json3, urls);
152866
152916
  };
152867
152917
  var markdownToMarkupString = (markdown, urls) => {
152868
152918
  const json3 = (0, import_text_markdown.markdownToMarkup)(markdown, urls);
@@ -152981,7 +153031,7 @@ function fromInternalMarkup(markup, format7, opts, sdk) {
152981
153031
  case "html":
152982
153032
  return sdk.jsonToHTML(sdk.markupToJSON(markup));
152983
153033
  case "markdown":
152984
- return sdk.markupToMarkdown(sdk.markupToJSON(markup), opts);
153034
+ return markupNodeToMarkdownString(sdk.markupToJSON(markup), opts, sdk.markupToMarkdown);
152985
153035
  default:
152986
153036
  absurd(format7);
152987
153037
  throw new Error(`Invalid format: ${format7}`);
@@ -163495,101 +163545,73 @@ var GetHulyContextResultSchema = Schema_exports.Struct({
163495
163545
  });
163496
163546
  var getHulyContextResultJsonSchema = JSONSchema_exports.make(GetHulyContextResultSchema);
163497
163547
 
163498
- // src/domain/schemas/projects.ts
163499
- var ProjectSummarySchema = Schema_exports.Struct({
163500
- identifier: ProjectIdentifier,
163501
- name: NonEmptyString2,
163502
- description: Schema_exports.optional(Schema_exports.String),
163503
- archived: Schema_exports.Boolean
163504
- }).annotations({
163505
- title: "ProjectSummary",
163506
- description: "Project summary for list operations"
163507
- });
163508
- var ListProjectsParamsSchema = Schema_exports.Struct({
163509
- includeArchived: Schema_exports.optional(Schema_exports.Boolean.annotations({
163510
- description: "Include archived projects in results (default: false, showing only active)"
163511
- })),
163512
- limit: Schema_exports.optional(
163513
- LimitParam.annotations({
163514
- description: "Maximum number of projects to return (default: 50)"
163515
- })
163516
- )
163517
- }).annotations({
163518
- title: "ListProjectsParams",
163519
- description: "Parameters for listing projects"
163520
- });
163521
- var ProjectSchema = Schema_exports.Struct({
163522
- identifier: ProjectIdentifier,
163523
- name: NonEmptyString2,
163524
- description: Schema_exports.optional(Schema_exports.String),
163525
- archived: Schema_exports.Boolean,
163526
- defaultStatus: Schema_exports.optional(StatusName),
163527
- statuses: Schema_exports.optional(Schema_exports.Array(StatusName))
163528
- }).annotations({
163529
- title: "Project",
163530
- description: "Full project with status information"
163531
- });
163532
- var GetProjectParamsSchema = Schema_exports.Struct({
163533
- project: ProjectIdentifier.annotations({ description: "Project identifier (e.g., 'HULY')" })
163534
- }).annotations({ title: "GetProjectParams", description: "Parameters for getting a project" });
163535
- var CreateProjectParamsSchema = Schema_exports.Struct({
163536
- name: NonEmptyString2.annotations({ description: "Project name" }),
163537
- identifier: Schema_exports.String.pipe(
163538
- Schema_exports.pattern(/^[A-Z][A-Z0-9_]{0,4}$/)
163539
- ).annotations({
163540
- description: "Unique project identifier, 1-5 uppercase alphanumeric chars starting with letter (e.g., 'HULY', 'QA')"
163541
- }),
163542
- description: Schema_exports.optional(Schema_exports.String.annotations({ description: "Project description" })),
163543
- private: Schema_exports.optional(Schema_exports.Boolean.annotations({ description: "Whether project is private (default: false)" }))
163544
- }).annotations({ title: "CreateProjectParams", description: "Parameters for creating a project" });
163545
- var UPDATE_PROJECT_FIELDS = ["name", "description"];
163546
- var UpdateProjectParamsSchema = Schema_exports.Struct({
163547
- project: ProjectIdentifier.annotations({ description: "Project identifier to update" }),
163548
- name: Schema_exports.optional(NonEmptyString2.annotations({ description: "New project name" })),
163549
- description: Schema_exports.optional(
163550
- Schema_exports.NullOr(Schema_exports.String).annotations({ description: "New description (null to clear)" })
163551
- )
163552
- }).annotations({
163553
- title: "UpdateProjectParams",
163554
- description: `Parameters for updating a project. ${atLeastOneUpdateFieldMessage(UPDATE_PROJECT_FIELDS)}`
163555
- });
163556
- var DeleteProjectParamsSchema = Schema_exports.Struct({
163557
- project: ProjectIdentifier.annotations({ description: "Project identifier to delete" })
163558
- }).annotations({ title: "DeleteProjectParams", description: "Parameters for deleting a project" });
163559
- var ListStatusesParamsSchema = Schema_exports.Struct({
163560
- project: ProjectIdentifier.annotations({ description: "Project identifier (e.g., 'HULY')" })
163561
- }).annotations({ title: "ListStatusesParams", description: "Parameters for listing project statuses" });
163562
- var StatusDetailSchema = Schema_exports.Struct({
163563
- name: StatusName,
163564
- isDone: Schema_exports.Boolean,
163565
- isCanceled: Schema_exports.Boolean,
163566
- isDefault: Schema_exports.Boolean
163567
- }).annotations({
163568
- title: "StatusDetail",
163569
- description: "Issue status with category and default info"
163570
- });
163571
- var listProjectsParamsJsonSchema = JSONSchema_exports.make(ListProjectsParamsSchema);
163572
- var listStatusesParamsJsonSchema = JSONSchema_exports.make(ListStatusesParamsSchema);
163573
- var getProjectParamsJsonSchema = JSONSchema_exports.make(GetProjectParamsSchema);
163574
- var createProjectParamsJsonSchema = JSONSchema_exports.make(CreateProjectParamsSchema);
163575
- var updateProjectParamsJsonSchema = withAtLeastOneRequired(
163576
- JSONSchema_exports.make(UpdateProjectParamsSchema),
163577
- UPDATE_PROJECT_FIELDS
163578
- );
163579
- var deleteProjectParamsJsonSchema = JSONSchema_exports.make(DeleteProjectParamsSchema);
163580
- var parseListProjectsParams = Schema_exports.decodeUnknown(ListProjectsParamsSchema);
163581
- var parseListStatusesParams = Schema_exports.decodeUnknown(ListStatusesParamsSchema);
163582
- var parseGetProjectParams = Schema_exports.decodeUnknown(GetProjectParamsSchema);
163583
- var parseCreateProjectParams = Schema_exports.decodeUnknown(CreateProjectParamsSchema);
163584
- var parseUpdateProjectParams = Schema_exports.decodeUnknown(UpdateProjectParamsSchema);
163585
- var parseDeleteProjectParams = Schema_exports.decodeUnknown(DeleteProjectParamsSchema);
163586
- var parseProject = Schema_exports.decodeUnknown(ProjectSchema);
163548
+ // src/huly/huly-plugins.ts
163549
+ var activity = require_lib23().default;
163550
+ var attachment = require_lib24().default;
163551
+ var calendar = require_lib25().default;
163552
+ var cardPlugin = require_lib26().default;
163553
+ var chunter = require_lib27().default;
163554
+ var contact = require_lib28().default;
163555
+ var core = require_lib4().default;
163556
+ var documentPlugin = require_lib29().default;
163557
+ var notification = require_lib30().default;
163558
+ var tags = require_lib31().default;
163559
+ var task = require_lib34().default;
163560
+ var time3 = require_lib35().default;
163561
+ var tracker = require_lib36().default;
163587
163562
 
163588
163563
  // src/domain/schemas/task-management.ts
163589
- var StatusCategoryValues = ["backlog", "todo", "active", "done", "canceled"];
163564
+ var StatusCategoryBySdkKey = {
163565
+ UnStarted: task.statusCategory.UnStarted,
163566
+ ToDo: task.statusCategory.ToDo,
163567
+ Active: task.statusCategory.Active,
163568
+ Won: task.statusCategory.Won,
163569
+ Lost: task.statusCategory.Lost
163570
+ };
163571
+ var StatusCategoryKeys = [
163572
+ "UnStarted",
163573
+ "ToDo",
163574
+ "Active",
163575
+ "Won",
163576
+ "Lost"
163577
+ ];
163578
+ var exactStatusCategoryKeys = (value3) => value3;
163579
+ exactStatusCategoryKeys(true);
163580
+ var StatusCategoryEntries = [
163581
+ { key: "UnStarted", ref: StatusCategoryBySdkKey.UnStarted },
163582
+ { key: "ToDo", ref: StatusCategoryBySdkKey.ToDo },
163583
+ { key: "Active", ref: StatusCategoryBySdkKey.Active },
163584
+ { key: "Won", ref: StatusCategoryBySdkKey.Won },
163585
+ { key: "Lost", ref: StatusCategoryBySdkKey.Lost }
163586
+ ];
163587
+ var exactStatusCategoryEntries = (value3) => value3;
163588
+ exactStatusCategoryEntries(true);
163589
+ var StatusCategoryValues = StatusCategoryKeys;
163590
163590
  var UnknownStatusCategoryValue = "unknown";
163591
163591
  var StatusCategoryValueSchema = Schema_exports.Literal(...StatusCategoryValues, UnknownStatusCategoryValue);
163592
- var CreateStatusCategoryValueSchema = Schema_exports.Literal(...StatusCategoryValues);
163592
+ var KnownStatusCategoryValueLiteral = Schema_exports.Literal(...StatusCategoryValues);
163593
+ var normalizedStatusCategoryLookup = new Map(
163594
+ StatusCategoryValues.map((value3) => [value3.toLowerCase(), value3])
163595
+ );
163596
+ var KnownStatusCategoryValueSchema = Schema_exports.transformOrFail(
163597
+ Schema_exports.String,
163598
+ KnownStatusCategoryValueLiteral,
163599
+ {
163600
+ strict: true,
163601
+ decode: (input, _options, ast) => {
163602
+ const match16 = normalizedStatusCategoryLookup.get(input.toLowerCase());
163603
+ return match16 !== void 0 ? ParseResult_exports.succeed(match16) : ParseResult_exports.fail(
163604
+ new ParseResult_exports.Type(ast, input, `Expected one of: ${enumValuesDescription(StatusCategoryValues)}`)
163605
+ );
163606
+ },
163607
+ encode: ParseResult_exports.succeed
163608
+ }
163609
+ ).annotations({
163610
+ title: "KnownStatusCategoryValue",
163611
+ description: `Huly SDK task.statusCategory key: ${enumValuesDescription(StatusCategoryValues)}`,
163612
+ jsonSchema: { type: "string", enum: [...StatusCategoryValues] }
163613
+ });
163614
+ var CreateStatusCategoryValueSchema = KnownStatusCategoryValueSchema;
163593
163615
  var TaskTypeKindSchema = Schema_exports.Literal("task", "subtask", "both");
163594
163616
  var ProjectTypeRefSchema = NonEmptyString2.pipe(Schema_exports.brand("ProjectTypeRef"));
163595
163617
  var TaskTypeRefSchema = NonEmptyString2.pipe(Schema_exports.brand("TaskTypeRef"));
@@ -163663,7 +163685,7 @@ var CreateIssueStatusParamsSchema = Schema_exports.Struct({
163663
163685
  })),
163664
163686
  name: NonEmptyString2.annotations({ description: "Display name for the workflow status to add." }),
163665
163687
  category: CreateStatusCategoryValueSchema.annotations({
163666
- description: `Business category for the status: ${enumValuesDescription(StatusCategoryValues)}.`
163688
+ description: `Huly SDK task.statusCategory key: ${enumValuesDescription(StatusCategoryValues)}.`
163667
163689
  }),
163668
163690
  taskType: Schema_exports.optional(TaskTypeRefSchema.annotations({
163669
163691
  description: "Optional task type ID or display name to scope the status to. If omitted, the status is added to every task type in the project type."
@@ -163702,6 +163724,95 @@ var parseListTaskTypesParams = Schema_exports.decodeUnknown(ListTaskTypesParamsS
163702
163724
  var parseCreateTaskTypeParams = Schema_exports.decodeUnknown(CreateTaskTypeParamsSchema);
163703
163725
  var parseCreateIssueStatusParams = Schema_exports.decodeUnknown(CreateIssueStatusParamsSchema);
163704
163726
 
163727
+ // src/domain/schemas/projects.ts
163728
+ var ProjectSummarySchema = Schema_exports.Struct({
163729
+ identifier: ProjectIdentifier,
163730
+ name: NonEmptyString2,
163731
+ description: Schema_exports.optional(Schema_exports.String),
163732
+ archived: Schema_exports.Boolean
163733
+ }).annotations({
163734
+ title: "ProjectSummary",
163735
+ description: "Project summary for list operations"
163736
+ });
163737
+ var ListProjectsParamsSchema = Schema_exports.Struct({
163738
+ includeArchived: Schema_exports.optional(Schema_exports.Boolean.annotations({
163739
+ description: "Include archived projects in results (default: false, showing only active)"
163740
+ })),
163741
+ limit: Schema_exports.optional(
163742
+ LimitParam.annotations({
163743
+ description: "Maximum number of projects to return (default: 50)"
163744
+ })
163745
+ )
163746
+ }).annotations({
163747
+ title: "ListProjectsParams",
163748
+ description: "Parameters for listing projects"
163749
+ });
163750
+ var ProjectSchema = Schema_exports.Struct({
163751
+ identifier: ProjectIdentifier,
163752
+ name: NonEmptyString2,
163753
+ description: Schema_exports.optional(Schema_exports.String),
163754
+ archived: Schema_exports.Boolean,
163755
+ defaultStatus: Schema_exports.optional(StatusName),
163756
+ statuses: Schema_exports.optional(Schema_exports.Array(StatusName))
163757
+ }).annotations({
163758
+ title: "Project",
163759
+ description: "Full project with status information"
163760
+ });
163761
+ var GetProjectParamsSchema = Schema_exports.Struct({
163762
+ project: ProjectIdentifier.annotations({ description: "Project identifier (e.g., 'HULY')" })
163763
+ }).annotations({ title: "GetProjectParams", description: "Parameters for getting a project" });
163764
+ var CreateProjectParamsSchema = Schema_exports.Struct({
163765
+ name: NonEmptyString2.annotations({ description: "Project name" }),
163766
+ identifier: Schema_exports.String.pipe(
163767
+ Schema_exports.pattern(/^[A-Z][A-Z0-9_]{0,4}$/)
163768
+ ).annotations({
163769
+ description: "Unique project identifier, 1-5 uppercase alphanumeric chars starting with letter (e.g., 'HULY', 'QA')"
163770
+ }),
163771
+ description: Schema_exports.optional(Schema_exports.String.annotations({ description: "Project description" })),
163772
+ private: Schema_exports.optional(Schema_exports.Boolean.annotations({ description: "Whether project is private (default: false)" }))
163773
+ }).annotations({ title: "CreateProjectParams", description: "Parameters for creating a project" });
163774
+ var UPDATE_PROJECT_FIELDS = ["name", "description"];
163775
+ var UpdateProjectParamsSchema = Schema_exports.Struct({
163776
+ project: ProjectIdentifier.annotations({ description: "Project identifier to update" }),
163777
+ name: Schema_exports.optional(NonEmptyString2.annotations({ description: "New project name" })),
163778
+ description: Schema_exports.optional(
163779
+ Schema_exports.NullOr(Schema_exports.String).annotations({ description: "New description (null to clear)" })
163780
+ )
163781
+ }).annotations({
163782
+ title: "UpdateProjectParams",
163783
+ description: `Parameters for updating a project. ${atLeastOneUpdateFieldMessage(UPDATE_PROJECT_FIELDS)}`
163784
+ });
163785
+ var DeleteProjectParamsSchema = Schema_exports.Struct({
163786
+ project: ProjectIdentifier.annotations({ description: "Project identifier to delete" })
163787
+ }).annotations({ title: "DeleteProjectParams", description: "Parameters for deleting a project" });
163788
+ var ListStatusesParamsSchema = Schema_exports.Struct({
163789
+ project: ProjectIdentifier.annotations({ description: "Project identifier (e.g., 'HULY')" })
163790
+ }).annotations({ title: "ListStatusesParams", description: "Parameters for listing project statuses" });
163791
+ var StatusDetailSchema = Schema_exports.Struct({
163792
+ name: StatusName,
163793
+ category: StatusCategoryValueSchema,
163794
+ isDefault: Schema_exports.Boolean
163795
+ }).annotations({
163796
+ title: "StatusDetail",
163797
+ description: "Issue status with workflow category and default info"
163798
+ });
163799
+ var listProjectsParamsJsonSchema = JSONSchema_exports.make(ListProjectsParamsSchema);
163800
+ var listStatusesParamsJsonSchema = JSONSchema_exports.make(ListStatusesParamsSchema);
163801
+ var getProjectParamsJsonSchema = JSONSchema_exports.make(GetProjectParamsSchema);
163802
+ var createProjectParamsJsonSchema = JSONSchema_exports.make(CreateProjectParamsSchema);
163803
+ var updateProjectParamsJsonSchema = withAtLeastOneRequired(
163804
+ JSONSchema_exports.make(UpdateProjectParamsSchema),
163805
+ UPDATE_PROJECT_FIELDS
163806
+ );
163807
+ var deleteProjectParamsJsonSchema = JSONSchema_exports.make(DeleteProjectParamsSchema);
163808
+ var parseListProjectsParams = Schema_exports.decodeUnknown(ListProjectsParamsSchema);
163809
+ var parseListStatusesParams = Schema_exports.decodeUnknown(ListStatusesParamsSchema);
163810
+ var parseGetProjectParams = Schema_exports.decodeUnknown(GetProjectParamsSchema);
163811
+ var parseCreateProjectParams = Schema_exports.decodeUnknown(CreateProjectParamsSchema);
163812
+ var parseUpdateProjectParams = Schema_exports.decodeUnknown(UpdateProjectParamsSchema);
163813
+ var parseDeleteProjectParams = Schema_exports.decodeUnknown(DeleteProjectParamsSchema);
163814
+ var parseProject = Schema_exports.decodeUnknown(ProjectSchema);
163815
+
163705
163816
  // src/domain/schemas/cards.ts
163706
163817
  var ListCardSpacesParamsSchema = Schema_exports.Struct({
163707
163818
  includeArchived: Schema_exports.optional(Schema_exports.Boolean.annotations({
@@ -164081,7 +164192,10 @@ var ListIssuesParamsBase = Schema_exports.Struct({
164081
164192
  description: "Project identifier (e.g., 'HULY')"
164082
164193
  }),
164083
164194
  status: Schema_exports.optional(StatusName.annotations({
164084
- description: "Filter by status name"
164195
+ description: "Filter by exact workflow status name. Does not accept category aliases."
164196
+ })),
164197
+ statusCategory: Schema_exports.optional(KnownStatusCategoryValueSchema.annotations({
164198
+ description: `Filter by Huly SDK task.statusCategory key: ${enumValuesDescription(StatusCategoryValues)}. Use status for exact project-specific status names.`
164085
164199
  })),
164086
164200
  assignee: Schema_exports.optional(PersonRefInput.annotations({
164087
164201
  description: "Filter by assignee email or display name"
@@ -164124,6 +164238,9 @@ var ListIssuesParamsSchema = ListIssuesParamsBase.pipe(
164124
164238
  if (params.titleSearch !== void 0 && params.titleRegex !== void 0) {
164125
164239
  return "Cannot provide both 'titleSearch' and 'titleRegex'. Use one or the other.";
164126
164240
  }
164241
+ if (params.status !== void 0 && params.statusCategory !== void 0) {
164242
+ return "Cannot provide both 'status' and 'statusCategory'. Use status for exact workflow status names or statusCategory for Huly workflow categories.";
164243
+ }
164127
164244
  if (params.assignee !== void 0 && params.hasAssignee !== void 0) {
164128
164245
  return "Cannot provide both 'assignee' and 'hasAssignee'. Use one or the other.";
164129
164246
  }
@@ -167891,7 +168008,7 @@ var parseDeleteTestResultParams = Schema_exports.decodeUnknown(DeleteTestResultP
167891
168008
  var parseRunTestPlanParams = Schema_exports.decodeUnknown(RunTestPlanParamsSchema);
167892
168009
 
167893
168010
  // src/version.ts
167894
- var VERSION = true ? "0.17.2" : "0.0.0-dev";
168011
+ var VERSION = true ? "0.18.1" : "0.0.0-dev";
167895
168012
 
167896
168013
  // src/mcp/error-mapping.ts
167897
168014
  var McpErrorCode = {
@@ -168105,21 +168222,6 @@ var clampLimit = (limit) => Math.min(limit ?? DEFAULT_LIMIT, MAX_LIMIT);
168105
168222
  // src/huly/operations/update-guards.ts
168106
168223
  var requireUpdateFields = (operation, params, fields) => hasAtLeastOneDefined(params, fields) ? Effect_exports.void : Effect_exports.fail(new NoUpdateFieldsError({ operation, fields: fields.map(String) }));
168107
168224
 
168108
- // src/huly/huly-plugins.ts
168109
- var activity = require_lib23().default;
168110
- var attachment = require_lib24().default;
168111
- var calendar = require_lib25().default;
168112
- var cardPlugin = require_lib26().default;
168113
- var chunter = require_lib27().default;
168114
- var contact = require_lib28().default;
168115
- var core = require_lib4().default;
168116
- var documentPlugin = require_lib29().default;
168117
- var notification = require_lib30().default;
168118
- var tags = require_lib31().default;
168119
- var task = require_lib34().default;
168120
- var time3 = require_lib35().default;
168121
- var tracker = require_lib36().default;
168122
-
168123
168225
  // src/huly/operations/channel-messages-shared.ts
168124
168226
  var findChannelMessage = (params) => Effect_exports.gen(function* () {
168125
168227
  const { channel, client } = yield* findChannel(params.channel);
@@ -168203,7 +168305,7 @@ var buildSocialIdToPersonNameMap = (client, socialIds) => Effect_exports.gen(fun
168203
168305
  for (const si of socialIdentities) {
168204
168306
  const person = personById.get(si.attachedTo);
168205
168307
  if (person !== void 0) {
168206
- result.set(si._id, person.name);
168308
+ result.set(si._id, PersonName.make(person.name));
168207
168309
  }
168208
168310
  }
168209
168311
  return result;
@@ -168219,7 +168321,7 @@ var buildAccountUuidToNameMap = (client, accountUuids) => Effect_exports.gen(fun
168219
168321
  const result = /* @__PURE__ */ new Map();
168220
168322
  for (const emp of employees) {
168221
168323
  if (emp.personUuid !== void 0) {
168222
- result.set(emp.personUuid, emp.name);
168324
+ result.set(emp.personUuid, PersonName.make(emp.name));
168223
168325
  }
168224
168326
  }
168225
168327
  return result;
@@ -168275,7 +168377,7 @@ var getChannel = (params) => Effect_exports.gen(function* () {
168275
168377
  description: channel.description || void 0,
168276
168378
  private: channel.private,
168277
168379
  archived: channel.archived,
168278
- members: memberNames?.map((m) => PersonName.make(m)),
168380
+ members: memberNames,
168279
168381
  messages: channel.messages,
168280
168382
  modifiedOn: channel.modifiedOn,
168281
168383
  createdOn: channel.createdOn
@@ -168357,7 +168459,7 @@ var listChannelMessages = (params) => Effect_exports.gen(function* () {
168357
168459
  return {
168358
168460
  id: MessageId.make(msg._id),
168359
168461
  body: markupToMarkdownString(msg.message, markupUrlConfig),
168360
- sender: senderName !== void 0 ? PersonName.make(senderName) : void 0,
168462
+ sender: senderName,
168361
168463
  senderId: msg.modifiedBy,
168362
168464
  createdOn: msg.createdOn,
168363
168465
  modifiedOn: msg.modifiedOn,
@@ -168408,7 +168510,7 @@ var listDirectMessages = (params) => Effect_exports.gen(function* () {
168408
168510
  ];
168409
168511
  const accountUuidToName = yield* buildAccountUuidToNameMap(client, uniqueAccountUuids);
168410
168512
  const summaries = dms.map((dm) => {
168411
- const participants = dm.members.map((m) => accountUuidToName.get(m)).filter((n) => n !== void 0).map((n) => PersonName.make(n));
168513
+ const participants = dm.members.map((m) => accountUuidToName.get(m)).filter((n) => n !== void 0);
168412
168514
  const participantIds = dm.members.map((m) => AccountUuid.make(m));
168413
168515
  return {
168414
168516
  id: ChannelId.make(dm._id),
@@ -168539,12 +168641,21 @@ var countOccurrences = (text, search) => {
168539
168641
 
168540
168642
  // src/huly/operations/documents-inline-comments.ts
168541
168643
  var import_core11 = __toESM(require_lib4(), 1);
168644
+ var import_text4 = __toESM(require_lib9(), 1);
168645
+
168646
+ // src/huly/operations/markup-traversal.ts
168542
168647
  var import_text3 = __toESM(require_lib9(), 1);
168543
- var INLINE_COMMENT_MARK_TYPE = "inline-comment";
168648
+ var traverseAllMarks = (root, visit) => {
168649
+ (0, import_text3.traverseAllMarks)(root, (node, mark) => {
168650
+ visit(node, mark);
168651
+ });
168652
+ };
168653
+
168654
+ // src/huly/operations/documents-inline-comments.ts
168544
168655
  var extractInlineComments = (root) => {
168545
168656
  const threadMap = /* @__PURE__ */ new Map();
168546
- (0, import_text3.traverseAllMarks)(root, (textNode, mark) => {
168547
- if (String(mark.type) !== INLINE_COMMENT_MARK_TYPE) return;
168657
+ traverseAllMarks(root, (textNode, mark) => {
168658
+ if (!isInlineCommentMark(mark)) return;
168548
168659
  const threadId = mark.attrs?.thread;
168549
168660
  if (typeof threadId !== "string" || threadId === "") return;
168550
168661
  const text = textNode.text ?? "";
@@ -168574,7 +168685,7 @@ var listInlineComments = (params) => Effect_exports.gen(function* () {
168574
168685
  doc.content,
168575
168686
  "markup"
168576
168687
  );
168577
- const root = (0, import_text3.markupToJSON)(rawMarkup);
168688
+ const root = (0, import_text4.markupToJSON)(rawMarkup);
168578
168689
  const extracted = extractInlineComments(root);
168579
168690
  if (extracted.length === 0) {
168580
168691
  return { comments: [], total: 0 };
@@ -168904,25 +169015,20 @@ var findProject = (projectIdentifier) => Effect_exports.gen(function* () {
168904
169015
  );
168905
169016
  return { client, project: project3 };
168906
169017
  });
168907
- var statusInfoFromDoc = (doc) => {
168908
- const categoryStr = doc.category ? doc.category : "";
169018
+ var statusCategoryValueFromRef = (category) => category === void 0 ? "unknown" : StatusCategoryEntries.find((entry) => entry.ref === category)?.key ?? "unknown";
169019
+ var workflowStatusFromDoc = (doc) => {
168909
169020
  return {
168910
169021
  _id: doc._id,
168911
169022
  name: doc.name,
168912
- isDone: categoryStr === task.statusCategory.Won,
168913
- isCanceled: categoryStr === task.statusCategory.Lost
169023
+ category: statusCategoryValueFromRef(doc.category)
168914
169024
  };
168915
169025
  };
168916
- var fallbackStatusInfoFromRef = (statusRef) => {
169026
+ var workflowStatusFromRef = (statusRef) => {
168917
169027
  const name = statusRef.includes(":") ? statusRef.slice(statusRef.lastIndexOf(":") + 1) : statusRef;
168918
- const nameLower = name.toLowerCase();
168919
- const isDone5 = nameLower.includes("done") || nameLower.includes("complete") || nameLower.includes("finished") || nameLower.includes("resolved") || nameLower.includes("closed");
168920
- const isCanceled = nameLower.includes("cancel") || nameLower.includes("reject") || nameLower.includes("abort") || nameLower.includes("wontfix") || nameLower.includes("invalid");
168921
169028
  return {
168922
169029
  _id: statusRef,
168923
169030
  name,
168924
- isDone: isDone5,
168925
- isCanceled
169031
+ category: "unknown"
168926
169032
  };
168927
169033
  };
168928
169034
  var uniqueStatusRefs = (refs) => refs.reduce(
@@ -168956,12 +169062,12 @@ var findProjectWithStatuses = (projectIdentifier) => Effect_exports.gen(function
168956
169062
  )
168957
169063
  );
168958
169064
  if (statusDocsResult._tag === "Right") {
168959
- return uniqueStatusDocs(statusDocsResult.right).map(statusInfoFromDoc);
169065
+ return uniqueStatusDocs(statusDocsResult.right).map(workflowStatusFromDoc);
168960
169066
  }
168961
169067
  yield* Effect_exports.logWarning(
168962
- `Status query failed for project ${projectIdentifier}, using fallback. Category-based filtering (open/done/canceled) will use name heuristics. Error: ${statusDocsResult.left.message}`
169068
+ `Status query failed for project ${projectIdentifier}, using fallback. statusCategory filtering is unavailable until Huly returns status metadata. Error: ${statusDocsResult.left.message}`
168963
169069
  );
168964
- return statusRefs.map(fallbackStatusInfoFromRef);
169070
+ return statusRefs.map(workflowStatusFromRef);
168965
169071
  }) : [];
168966
169072
  const defaultStatusId = project3.defaultIssueStatus || statuses[0]?._id;
168967
169073
  return { client, defaultStatusId, project: project3, projectType, statuses };
@@ -170876,7 +170982,7 @@ var findDirectMessage = (identifier2) => Effect_exports.gen(function* () {
170876
170982
  const accountUuid = client.getAccountUuid();
170877
170983
  const accountUuids = [
170878
170984
  ...new Set(
170879
- employees.map((e) => e.personUuid).filter((u) => u !== void 0 && u !== accountUuid)
170985
+ employees.map((e) => e.personUuid).filter((u) => u !== void 0).filter((u) => u !== accountUuid)
170880
170986
  )
170881
170987
  ];
170882
170988
  if (accountUuids.length === 0) {
@@ -170935,7 +171041,7 @@ var listDirectMessageMessages = (params) => Effect_exports.gen(function* () {
170935
171041
  return {
170936
171042
  id: MessageId.make(msg._id),
170937
171043
  body: markupToMarkdownString(msg.message, markupUrlConfig),
170938
- sender: senderName !== void 0 ? PersonName.make(senderName) : void 0,
171044
+ sender: senderName,
170939
171045
  senderId: msg.modifiedBy,
170940
171046
  createdOn: msg.createdOn,
170941
171047
  modifiedOn: msg.modifiedOn,
@@ -171087,7 +171193,7 @@ var listThreadReplies = (params) => Effect_exports.gen(function* () {
171087
171193
  return {
171088
171194
  id: ThreadReplyId.make(msg._id),
171089
171195
  body: markupToMarkdownString(msg.message, markupUrlConfig),
171090
- sender: senderName !== void 0 ? PersonName.make(senderName) : void 0,
171196
+ sender: senderName,
171091
171197
  senderId: msg.modifiedBy,
171092
171198
  createdOn: msg.createdOn,
171093
171199
  modifiedOn: msg.modifiedOn,
@@ -173556,6 +173662,90 @@ var resolveDocumentWithoutTeamspace = (client, identifier2, field) => Effect_exp
173556
173662
  }
173557
173663
  return resolvedSummary(byTitle[0], "document");
173558
173664
  });
173665
+ var findCardById = (client, identifier2) => client.findOne(
173666
+ cardPlugin.class.Card,
173667
+ hulyQuery({ _id: toRef(identifier2) })
173668
+ );
173669
+ var findCardSpace2 = (client, identifier2, field) => Effect_exports.gen(function* () {
173670
+ const byId = yield* client.findOne(
173671
+ cardPlugin.class.CardSpace,
173672
+ hulyQuery({ _id: toRef(identifier2) })
173673
+ );
173674
+ if (byId !== void 0) {
173675
+ return byId;
173676
+ }
173677
+ const byName = yield* client.findAll(
173678
+ cardPlugin.class.CardSpace,
173679
+ hulyQuery({ name: identifier2, archived: false }),
173680
+ { limit: 2 }
173681
+ );
173682
+ if (byName.length === 0) {
173683
+ return yield* new GenericObjectNotFoundError({
173684
+ field,
173685
+ identifier: identifier2,
173686
+ class: cardPlugin.class.CardSpace
173687
+ });
173688
+ }
173689
+ if (byName.length > 1) {
173690
+ return yield* new GenericObjectIdentifierAmbiguousError({
173691
+ field,
173692
+ identifier: identifier2,
173693
+ candidates: byName.map((space) => ({
173694
+ id: DocId.make(space._id),
173695
+ class: ObjectClassName.make(space._class),
173696
+ display: space.name
173697
+ }))
173698
+ });
173699
+ }
173700
+ return byName[0];
173701
+ });
173702
+ var resolveCardInSpace = (client, identifier2, cardSpace, field) => Effect_exports.gen(function* () {
173703
+ const byId = yield* client.findOne(
173704
+ cardPlugin.class.Card,
173705
+ hulyQuery({ _id: toRef(identifier2), space: cardSpace._id })
173706
+ );
173707
+ if (byId !== void 0) {
173708
+ return resolvedSummary(byId, "card");
173709
+ }
173710
+ const byTitle = yield* client.findAll(
173711
+ cardPlugin.class.Card,
173712
+ hulyQuery({ title: identifier2, space: cardSpace._id }),
173713
+ { limit: 2 }
173714
+ );
173715
+ if (byTitle.length === 0) {
173716
+ return yield* new GenericObjectNotFoundError({
173717
+ field,
173718
+ identifier: identifier2,
173719
+ class: cardPlugin.class.Card
173720
+ });
173721
+ }
173722
+ if (byTitle.length > 1) {
173723
+ return yield* new GenericObjectIdentifierAmbiguousError({
173724
+ field,
173725
+ identifier: identifier2,
173726
+ candidates: byTitle.map((card) => ({
173727
+ id: DocId.make(card._id),
173728
+ class: ObjectClassName.make(card._class),
173729
+ display: card.title
173730
+ }))
173731
+ });
173732
+ }
173733
+ return resolvedSummary(byTitle[0], "card");
173734
+ });
173735
+ var resolveCardLocator = (client, locator, field) => Effect_exports.gen(function* () {
173736
+ if (locator.cardSpace !== void 0) {
173737
+ const cardSpace = yield* findCardSpace2(client, locator.cardSpace, field);
173738
+ return yield* resolveCardInSpace(client, locator.card, cardSpace, field);
173739
+ }
173740
+ const byId = yield* findCardById(client, locator.card);
173741
+ if (byId !== void 0) {
173742
+ return resolvedSummary(byId, "card");
173743
+ }
173744
+ return yield* new GenericObjectLocatorInvalidError({
173745
+ field,
173746
+ reason: `card '${locator.card}' was not found by ID; exact card title lookup requires cardSpace`
173747
+ });
173748
+ });
173559
173749
  var resolveGenericObject = (client, locator, expectedClass, field) => Effect_exports.gen(function* () {
173560
173750
  switch (locator.kind) {
173561
173751
  case "raw": {
@@ -173594,6 +173784,11 @@ var resolveGenericObject = (client, locator, expectedClass, field) => Effect_exp
173594
173784
  yield* validateExpectedClass(summary5, expectedClass, field);
173595
173785
  return summary5;
173596
173786
  }
173787
+ case "card": {
173788
+ const summary5 = yield* resolveCardLocator(client, locator, field);
173789
+ yield* validateExpectedClass(summary5, expectedClass, field);
173790
+ return summary5;
173791
+ }
173597
173792
  }
173598
173793
  });
173599
173794
  var unresolvedRelationEndpoint = (id, className, warning) => ({
@@ -174079,7 +174274,7 @@ var genericAssociationTools = [
174079
174274
  },
174080
174275
  {
174081
174276
  name: "list_relations",
174082
- description: "List concrete Huly relation instances under an association, optionally filtered by source and target documents. Requires at least one filter to avoid broad workspace scans.",
174277
+ description: "List concrete Huly relation instances under an association, optionally filtered by source and target documents. Endpoint locators support raw, issue, document, and card. Requires at least one filter to avoid broad workspace scans.",
174083
174278
  category: CATEGORY11,
174084
174279
  inputSchema: listRelationsParamsJsonSchema,
174085
174280
  handler: createEncodedToolHandler(
@@ -174091,7 +174286,7 @@ var genericAssociationTools = [
174091
174286
  },
174092
174287
  {
174093
174288
  name: "create_relation",
174094
- description: "Idempotently create one concrete relation between two resolved documents for a writable association. Enforces association endpoint classes, direction, duplicate handling, automation-only restrictions, and cardinality.",
174289
+ description: "Idempotently create one concrete relation between two resolved documents for a writable association. Endpoint locators support raw, issue, document, and card. Enforces association endpoint classes, direction, duplicate handling, automation-only restrictions, and cardinality.",
174095
174290
  category: CATEGORY11,
174096
174291
  inputSchema: createRelationParamsJsonSchema,
174097
174292
  annotations: {
@@ -174109,7 +174304,7 @@ var genericAssociationTools = [
174109
174304
  },
174110
174305
  {
174111
174306
  name: "delete_relation",
174112
- description: "Idempotently delete one concrete relation by relation ID or by exact association/source/target triple. Triple deletes use the same direction semantics as create_relation and fail if the selector is ambiguous.",
174307
+ description: "Idempotently delete one concrete relation by relation ID or by exact association/source/target triple. Triple endpoint locators support raw, issue, document, and card. Triple deletes use the same direction semantics as create_relation and fail if the selector is ambiguous.",
174113
174308
  category: CATEGORY11,
174114
174309
  inputSchema: deleteRelationParamsJsonSchema,
174115
174310
  annotations: {
@@ -174586,36 +174781,30 @@ var resolveStatusName = (statuses, statusId) => {
174586
174781
  const statusDoc = statuses.find((s) => s._id === statusId);
174587
174782
  return statusDoc?.name ?? "Unknown";
174588
174783
  };
174784
+ var hasUnknownStatusCategory = (statuses) => statuses.some((status) => status.category === "unknown");
174785
+ var requireKnownStatusCategories = (statuses, category, project3) => hasUnknownStatusCategory(statuses) ? Effect_exports.fail(
174786
+ new HulyConnectionError({
174787
+ message: `Cannot filter project '${project3}' issues by status category '${category}' because Huly did not return complete status category metadata. Use an exact status name instead.`
174788
+ })
174789
+ ) : Effect_exports.void;
174790
+ var statusIdsByCategory = (statuses, category) => statuses.filter((status) => status.category === category).map((status) => status._id);
174589
174791
  var listIssues = (params) => Effect_exports.gen(function* () {
174590
174792
  const { client, project: project3, statuses } = yield* findProjectWithStatuses(params.project);
174591
174793
  const query = {
174592
174794
  space: project3._id
174593
174795
  };
174594
- if (params.status !== void 0) {
174595
- const statusFilter = normalizeForComparison(params.status);
174596
- if (statusFilter === "open") {
174597
- const doneAndCanceledStatuses = statuses.filter((s) => s.isDone || s.isCanceled).map((s) => s._id);
174598
- if (doneAndCanceledStatuses.length > 0) {
174599
- query.status = { $nin: doneAndCanceledStatuses };
174600
- }
174601
- } else if (statusFilter === "done") {
174602
- const doneStatuses = statuses.filter((s) => s.isDone).map((s) => s._id);
174603
- if (doneStatuses.length > 0) {
174604
- query.status = { $in: doneStatuses };
174605
- } else {
174606
- return [];
174607
- }
174608
- } else if (statusFilter === "canceled") {
174609
- const canceledStatuses = statuses.filter((s) => s.isCanceled).map((s) => s._id);
174610
- if (canceledStatuses.length > 0) {
174611
- query.status = { $in: canceledStatuses };
174612
- } else {
174613
- return [];
174614
- }
174796
+ if (params.statusCategory !== void 0) {
174797
+ yield* requireKnownStatusCategories(statuses, params.statusCategory, params.project);
174798
+ const matchingStatuses = statusIdsByCategory(statuses, params.statusCategory);
174799
+ if (matchingStatuses.length > 0) {
174800
+ query.status = { $in: matchingStatuses };
174615
174801
  } else {
174616
- query.status = yield* resolveStatusByName(statuses, params.status, params.project);
174802
+ return [];
174617
174803
  }
174618
174804
  }
174805
+ if (params.status !== void 0) {
174806
+ query.status = yield* resolveStatusByName(statuses, params.status, params.project);
174807
+ }
174619
174808
  if (params.assignee !== void 0) {
174620
174809
  const assigneePerson = yield* findPersonByEmailOrName(client, params.assignee);
174621
174810
  if (assigneePerson !== void 0) {
@@ -175645,7 +175834,7 @@ var CATEGORY12 = "issues";
175645
175834
  var issueTools = [
175646
175835
  {
175647
175836
  name: "list_issues",
175648
- description: "Query Huly issues with optional filters. Returns issues sorted by modification date (newest first). Supports filtering by project, status, assignee, component, and parentIssue (to list children of a specific issue). Supports searching by title substring (titleSearch) and description content (descriptionSearch).",
175837
+ description: `Query Huly issues with optional filters. Returns issues sorted by modification date (newest first). Supports filtering by project, exact workflow status name (status), Huly SDK task.statusCategory key (statusCategory: ${enumValuesDescription(StatusCategoryValues)}), assignee, component, and parentIssue (to list children of a specific issue). Supports searching by title substring (titleSearch) and description content (descriptionSearch).`,
175649
175838
  category: CATEGORY12,
175650
175839
  inputSchema: listIssuesParamsJsonSchema,
175651
175840
  handler: createToolHandler(
@@ -177407,8 +177596,7 @@ var listStatuses = (params) => Effect_exports.gen(function* () {
177407
177596
  const { defaultStatusId, statuses } = yield* findProjectWithStatuses(params.project);
177408
177597
  const details = statuses.map((s) => ({
177409
177598
  name: StatusName.make(s.name),
177410
- isDone: s.isDone,
177411
- isCanceled: s.isCanceled,
177599
+ category: s.category,
177412
177600
  isDefault: s._id === defaultStatusId
177413
177601
  }));
177414
177602
  return { statuses: details, total: details.length };
@@ -177512,7 +177700,7 @@ var projectTools = [
177512
177700
  },
177513
177701
  {
177514
177702
  name: "list_statuses",
177515
- description: "List all issue statuses for a Huly project with category info. Returns status name, isDone, isCanceled, and isDefault flags. Use this to discover valid statuses before creating or updating issues.",
177703
+ description: "List all issue statuses for a Huly project with workflow category and default info. Returns status name, category, and isDefault. Use this to discover valid statuses before creating or updating issues.",
177516
177704
  category: CATEGORY18,
177517
177705
  inputSchema: listStatusesParamsJsonSchema,
177518
177706
  handler: createToolHandler(
@@ -177685,20 +177873,20 @@ var tagCategoryTools = [
177685
177873
  var import_core38 = __toESM(require_lib4(), 1);
177686
177874
  var import_platform3 = __toESM(require_lib(), 1);
177687
177875
  var STATUS_CATEGORY_BY_SDK_KEY = {
177688
- UnStarted: { value: "backlog", ref: task.statusCategory.UnStarted, name: "Backlog" },
177689
- ToDo: { value: "todo", ref: task.statusCategory.ToDo, name: "Todo" },
177690
- Active: { value: "active", ref: task.statusCategory.Active, name: "Active" },
177691
- Won: { value: "done", ref: task.statusCategory.Won, name: "Done" },
177692
- Lost: { value: "canceled", ref: task.statusCategory.Lost, name: "Canceled" }
177876
+ UnStarted: { value: "UnStarted", ref: StatusCategoryBySdkKey.UnStarted, name: "UnStarted" },
177877
+ ToDo: { value: "ToDo", ref: StatusCategoryBySdkKey.ToDo, name: "ToDo" },
177878
+ Active: { value: "Active", ref: StatusCategoryBySdkKey.Active, name: "Active" },
177879
+ Won: { value: "Won", ref: StatusCategoryBySdkKey.Won, name: "Won" },
177880
+ Lost: { value: "Lost", ref: StatusCategoryBySdkKey.Lost, name: "Lost" }
177693
177881
  };
177694
177882
  var exactStatusCategoryMapping = (value3) => value3;
177695
177883
  exactStatusCategoryMapping(true);
177696
177884
  var CATEGORY_TO_REF = {
177697
- backlog: STATUS_CATEGORY_BY_SDK_KEY.UnStarted.ref,
177698
- todo: STATUS_CATEGORY_BY_SDK_KEY.ToDo.ref,
177699
- active: STATUS_CATEGORY_BY_SDK_KEY.Active.ref,
177700
- done: STATUS_CATEGORY_BY_SDK_KEY.Won.ref,
177701
- canceled: STATUS_CATEGORY_BY_SDK_KEY.Lost.ref
177885
+ UnStarted: STATUS_CATEGORY_BY_SDK_KEY.UnStarted.ref,
177886
+ ToDo: STATUS_CATEGORY_BY_SDK_KEY.ToDo.ref,
177887
+ Active: STATUS_CATEGORY_BY_SDK_KEY.Active.ref,
177888
+ Won: STATUS_CATEGORY_BY_SDK_KEY.Won.ref,
177889
+ Lost: STATUS_CATEGORY_BY_SDK_KEY.Lost.ref
177702
177890
  };
177703
177891
  var REF_TO_CATEGORY = new Map(
177704
177892
  Object.values(STATUS_CATEGORY_BY_SDK_KEY).map((entry) => [entry.ref, entry.value])
@@ -178111,7 +178299,7 @@ var taskManagementTools = [
178111
178299
  },
178112
178300
  {
178113
178301
  name: "create_issue_status",
178114
- description: "Add a Huly issue workflow status idempotently by normalized name within a project type and task type scope. Accepts category as backlog, todo, active, done, or canceled; taskType may be ID or display name, and omission applies the status to every task type in the project type.",
178302
+ description: `Add a Huly issue workflow status idempotently by normalized name within a project type and task type scope. Accepts category as a Huly SDK task.statusCategory key: ${enumValuesDescription(StatusCategoryValues)}; taskType may be ID or display name, and omission applies the status to every task type in the project type.`,
178115
178303
  category: CATEGORY22,
178116
178304
  inputSchema: createIssueStatusParamsJsonSchema,
178117
178305
  annotations: { idempotentHint: true },
@@ -190293,6 +190481,37 @@ var McpServerService = class _McpServerService extends Context_exports.Tag("@hul
190293
190481
  }
190294
190482
  };
190295
190483
 
190484
+ // src/mcp/stdio-output.ts
190485
+ var redirectedMethods = [
190486
+ "debug",
190487
+ "info",
190488
+ "log",
190489
+ "warn",
190490
+ "error"
190491
+ ];
190492
+ var redirectConsoleToStderr = (target = console) => {
190493
+ const original = {
190494
+ debug: target.debug,
190495
+ error: target.error,
190496
+ info: target.info,
190497
+ log: target.log,
190498
+ warn: target.warn
190499
+ };
190500
+ const writeToStderr = original.error.bind(target);
190501
+ for (const method of redirectedMethods) {
190502
+ target[method] = writeToStderr;
190503
+ }
190504
+ return {
190505
+ restore: () => {
190506
+ target.debug = original.debug;
190507
+ target.error = original.error;
190508
+ target.info = original.info;
190509
+ target.log = original.log;
190510
+ target.warn = original.warn;
190511
+ }
190512
+ };
190513
+ };
190514
+
190296
190515
  // src/index.ts
190297
190516
  var getTransportType = Config_exports.string("MCP_TRANSPORT").pipe(
190298
190517
  Config_exports.withDefault("stdio"),
@@ -190320,6 +190539,9 @@ var getLazyEnvs = Config_exports.string("LAZY_ENVS").pipe(
190320
190539
  Config_exports.withDefault("false"),
190321
190540
  Effect_exports.map((v) => v.toLowerCase() === "true")
190322
190541
  );
190542
+ var restoreConsoleRedirect = (redirect) => Effect_exports.sync(() => {
190543
+ redirect?.restore();
190544
+ });
190323
190545
  var buildCombinedClientLayer = () => {
190324
190546
  const configLayer = HulyConfigService.layer;
190325
190547
  const hulyClientLayer = HulyClient.layer.pipe(
@@ -190411,8 +190633,7 @@ var buildAppLayer = (transport, httpPort, httpHost, mcpAuthToken, autoExit, auth
190411
190633
  const mcpServerLayer = McpServerService.layer(mcpServerConfig).pipe(Layer_exports.provide(TelemetryService.layer));
190412
190634
  return Layer_exports.merge(mcpServerLayer, HttpServerFactoryService.defaultLayer);
190413
190635
  };
190414
- var main = Effect_exports.gen(function* () {
190415
- const transport = yield* getTransportType;
190636
+ var runConfiguredServer = (transport) => Effect_exports.gen(function* () {
190416
190637
  const httpPort = yield* getHttpPort;
190417
190638
  const httpHost = yield* getHttpHost;
190418
190639
  const mcpAuthToken = transport === "http" ? Option_exports.map(yield* getMcpAuthToken, Redacted_exports.value).pipe(Option_exports.getOrUndefined) : void 0;
@@ -190446,6 +190667,13 @@ var main = Effect_exports.gen(function* () {
190446
190667
  Effect_exports.scoped
190447
190668
  );
190448
190669
  });
190670
+ var main = Effect_exports.gen(function* () {
190671
+ const transport = yield* getTransportType;
190672
+ const consoleRedirect = yield* Effect_exports.sync(() => transport === "stdio" ? redirectConsoleToStderr() : void 0);
190673
+ yield* runConfiguredServer(transport).pipe(
190674
+ Effect_exports.ensuring(restoreConsoleRedirect(consoleRedirect))
190675
+ );
190676
+ });
190449
190677
  var isMainModule = (() => {
190450
190678
  if (typeof require !== "undefined" && require.main === module) return true;
190451
190679
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@firfi/huly-mcp",
3
- "version": "0.17.2",
3
+ "version": "0.18.1",
4
4
  "description": "MCP server for Huly integration",
5
5
  "mcpName": "io.github.dearlordylord/huly-mcp",
6
6
  "type": "module",
@@ -111,6 +111,6 @@
111
111
  "verify-version": "node -e \"const v=require('./package.json').version; const d=require('fs').readFileSync('dist/index.cjs','utf8'); if(!d.includes('\\\"'+v+'\\\"'))throw new Error('dist version mismatch: expected '+v)\"",
112
112
  "verify-registry-metadata": "node scripts/sync-registry-metadata.mjs --check",
113
113
  "sync-registry-metadata": "node scripts/sync-registry-metadata.mjs",
114
- "update-readme": "node scripts/update-readme-tools.mjs"
114
+ "update-readme": "tsx scripts/update-readme-tools.mjs"
115
115
  }
116
116
  }