@firfi/huly-mcp 0.1.30 → 0.1.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -149,7 +149,7 @@ MCP_TRANSPORT=http MCP_HTTP_PORT=8080 MCP_HTTP_HOST=0.0.0.0 npx -y @firfi/huly-m
149
149
  |------|-------------|
150
150
  | `list_issues` | Query Huly issues with optional filters. Returns issues sorted by modification date (newest first). Supports filtering by project, status, assignee, and milestone. Supports searching by title substring (titleSearch) and description content (descriptionSearch). |
151
151
  | `get_issue` | Retrieve full details for a Huly issue including markdown description. Use this to view issue content, comments, or full metadata. |
152
- | `create_issue` | Create a new issue in a Huly project. Description supports markdown formatting. Returns the created issue identifier. |
152
+ | `create_issue` | Create a new issue in a Huly project. Optionally create as a sub-issue by specifying parentIssue. Description supports markdown formatting. Returns the created issue identifier. |
153
153
  | `update_issue` | Update fields on an existing Huly issue. Only provided fields are modified. Description updates support markdown. |
154
154
  | `add_issue_label` | Add a tag/label to a Huly issue. Creates the tag if it doesn't exist in the project. |
155
155
  | `delete_issue` | Permanently delete a Huly issue. This action cannot be undone. |
package/dist/index.cjs CHANGED
@@ -154203,11 +154203,10 @@ var parseIssueIdentifier = (identifier2, projectIdentifier) => {
154203
154203
  }
154204
154204
  return { fullIdentifier: idStr, number: null };
154205
154205
  };
154206
- var findProjectAndIssue = (params) => Effect_exports.gen(function* () {
154207
- const { client, project: project3 } = yield* findProject(params.project);
154206
+ var findIssueInProject = (client, project3, identifierStr) => Effect_exports.gen(function* () {
154208
154207
  const { fullIdentifier, number: number8 } = parseIssueIdentifier(
154209
- params.identifier,
154210
- params.project
154208
+ identifierStr,
154209
+ project3.identifier
154211
154210
  );
154212
154211
  let issue2 = yield* client.findOne(
154213
154212
  tracker.class.Issue,
@@ -154227,10 +154226,15 @@ var findProjectAndIssue = (params) => Effect_exports.gen(function* () {
154227
154226
  }
154228
154227
  if (issue2 === void 0) {
154229
154228
  return yield* new IssueNotFoundError({
154230
- identifier: params.identifier,
154231
- project: params.project
154229
+ identifier: identifierStr,
154230
+ project: project3.identifier
154232
154231
  });
154233
154232
  }
154233
+ return issue2;
154234
+ });
154235
+ var findProjectAndIssue = (params) => Effect_exports.gen(function* () {
154236
+ const { client, project: project3 } = yield* findProject(params.project);
154237
+ const issue2 = yield* findIssueInProject(client, project3, params.identifier);
154234
154238
  return { client, project: project3, issue: issue2 };
154235
154239
  });
154236
154240
  var priorityToStringMap = {
@@ -173659,7 +173663,7 @@ var PostHog = class extends PostHogBackendClient {
173659
173663
  };
173660
173664
 
173661
173665
  // src/version.ts
173662
- var VERSION = true ? "0.1.29" : "0.0.0-dev";
173666
+ var VERSION = true ? "0.1.32" : "0.0.0-dev";
173663
173667
 
173664
173668
  // src/telemetry/posthog.ts
173665
173669
  var POSTHOG_API_KEY = "phc_TGfFqCGdnF0p68wuFzd5WSw1IsBvOJW0YgoMJDyZPjm";
@@ -173680,6 +173684,7 @@ var createPostHogTelemetry = (debug) => {
173680
173684
  properties: {
173681
173685
  session_id: sessionId,
173682
173686
  version: VERSION,
173687
+ $ip: null,
173683
173688
  ...properties
173684
173689
  }
173685
173690
  });
@@ -173769,22 +173774,6 @@ var TelemetryService = class _TelemetryService extends Context_exports.Tag("@hul
173769
173774
  }
173770
173775
  };
173771
173776
 
173772
- // src/utils/assertions.ts
173773
- var AssertionError = class extends Error {
173774
- _tag = "AssertionError";
173775
- constructor(message) {
173776
- super(message);
173777
- this.name = "AssertionError";
173778
- }
173779
- };
173780
- var assertExists = (value3, message) => {
173781
- if (value3 === null || value3 === void 0) {
173782
- throw new AssertionError(message ?? "Expected value to exist");
173783
- }
173784
- return value3;
173785
- };
173786
- var isExistent = (value3) => value3 !== null && value3 !== void 0;
173787
-
173788
173777
  // src/mcp/error-mapping.ts
173789
173778
  var McpErrorCode = {
173790
173779
  InvalidParams: -32602,
@@ -176005,11 +175994,30 @@ var listProjectsParamsJsonSchema = JSONSchema_exports.make(ListProjectsParamsSch
176005
175994
  var parseListProjectsParams = Schema_exports.decodeUnknown(ListProjectsParamsSchema);
176006
175995
  var parseProject = Schema_exports.decodeUnknown(ProjectSchema);
176007
175996
 
175997
+ // src/utils/normalize.ts
175998
+ var normalizeForComparison = (s) => s.replace(/[-_ ]/g, "").toLowerCase();
175999
+
176008
176000
  // src/domain/schemas/issues.ts
176009
176001
  var IssuePriorityValues = ["urgent", "high", "medium", "low", "no-priority"];
176010
- var IssuePrioritySchema = Schema_exports.Literal(...IssuePriorityValues).annotations({
176002
+ var IssuePriorityLiteral = Schema_exports.Literal(...IssuePriorityValues);
176003
+ var normalizedPriorityLookup = new Map(
176004
+ IssuePriorityValues.map((v) => [normalizeForComparison(v), v])
176005
+ );
176006
+ var IssuePrioritySchema = Schema_exports.transformOrFail(
176007
+ Schema_exports.String,
176008
+ IssuePriorityLiteral,
176009
+ {
176010
+ strict: true,
176011
+ decode: (input, _options, ast) => {
176012
+ const match16 = normalizedPriorityLookup.get(normalizeForComparison(input));
176013
+ return match16 !== void 0 ? ParseResult_exports.succeed(match16) : ParseResult_exports.fail(new ParseResult_exports.Type(ast, input, `Expected one of: ${IssuePriorityValues.join(", ")}`));
176014
+ },
176015
+ encode: ParseResult_exports.succeed
176016
+ }
176017
+ ).annotations({
176011
176018
  title: "IssuePriority",
176012
- description: "Issue priority level"
176019
+ description: "Issue priority level",
176020
+ jsonSchema: { type: "string", enum: [...IssuePriorityValues] }
176013
176021
  });
176014
176022
  var LabelSchema = Schema_exports.Struct({
176015
176023
  title: NonEmptyString2,
@@ -176112,6 +176120,9 @@ var CreateIssueParamsSchema = Schema_exports.Struct({
176112
176120
  })),
176113
176121
  status: Schema_exports.optional(StatusName.annotations({
176114
176122
  description: "Initial status (uses project default if not specified)"
176123
+ })),
176124
+ parentIssue: Schema_exports.optional(IssueIdentifier.annotations({
176125
+ description: "Parent issue identifier (e.g., 'HULY-42') to create as sub-issue"
176115
176126
  }))
176116
176127
  }).annotations({
176117
176128
  title: "CreateIssueParams",
@@ -179298,6 +179309,24 @@ var documentTools = [
179298
179309
 
179299
179310
  // src/huly/operations/components.ts
179300
179311
  var import_core27 = __toESM(require_lib4(), 1);
179312
+
179313
+ // src/utils/assertions.ts
179314
+ var AssertionError = class extends Error {
179315
+ _tag = "AssertionError";
179316
+ constructor(message) {
179317
+ super(message);
179318
+ this.name = "AssertionError";
179319
+ }
179320
+ };
179321
+ var assertExists = (value3, message) => {
179322
+ if (value3 === null || value3 === void 0) {
179323
+ throw new AssertionError(message ?? "Expected value to exist");
179324
+ }
179325
+ return value3;
179326
+ };
179327
+ var isExistent = (value3) => value3 !== null && value3 !== void 0;
179328
+
179329
+ // src/huly/operations/components.ts
179301
179330
  var findComponentByIdOrLabel = (client, projectId, componentIdOrLabel) => Effect_exports.gen(function* () {
179302
179331
  let component = yield* client.findOne(
179303
179332
  tracker.class.Component,
@@ -179487,8 +179516,9 @@ var extractUpdatedSequence = (txResult) => {
179487
179516
  return decoded._tag === "Some" ? decoded.value.object.sequence : void 0;
179488
179517
  };
179489
179518
  var resolveStatusByName = (statuses, statusName, project3) => {
179519
+ const normalizedInput = normalizeForComparison(statusName);
179490
179520
  const matchingStatus = statuses.find(
179491
- (s) => s.name.toLowerCase() === statusName.toLowerCase()
179521
+ (s) => normalizeForComparison(s.name) === normalizedInput
179492
179522
  );
179493
179523
  if (matchingStatus === void 0) {
179494
179524
  return Effect_exports.fail(new InvalidStatusError({ status: statusName, project: project3 }));
@@ -179519,7 +179549,7 @@ var listIssues = (params) => Effect_exports.gen(function* () {
179519
179549
  space: project3._id
179520
179550
  };
179521
179551
  if (params.status !== void 0) {
179522
- const statusFilter = params.status.toLowerCase();
179552
+ const statusFilter = normalizeForComparison(params.status);
179523
179553
  if (statusFilter === "open") {
179524
179554
  const doneAndCanceledStatuses = statuses.filter((s) => s.isDone || s.isCanceled).map((s) => s._id);
179525
179555
  if (doneAndCanceledStatuses.length > 0) {
@@ -179696,6 +179726,25 @@ var createIssue = (params) => Effect_exports.gen(function* () {
179696
179726
  }
179697
179727
  const priority = stringToPriority(params.priority || "no-priority");
179698
179728
  const identifier2 = `${project3.identifier}-${sequence}`;
179729
+ let attachedTo = project3._id;
179730
+ let attachedToClass = tracker.class.Project;
179731
+ let collection = "issues";
179732
+ let parents = [];
179733
+ if (params.parentIssue !== void 0) {
179734
+ const parentIssue = yield* findIssueInProject(client, project3, params.parentIssue);
179735
+ attachedTo = parentIssue._id;
179736
+ attachedToClass = tracker.class.Issue;
179737
+ collection = "subIssues";
179738
+ parents = [
179739
+ ...parentIssue.parents,
179740
+ {
179741
+ parentId: parentIssue._id,
179742
+ identifier: parentIssue.identifier,
179743
+ parentTitle: parentIssue.title,
179744
+ space: project3._id
179745
+ }
179746
+ ];
179747
+ }
179699
179748
  const issueData = {
179700
179749
  title: params.title,
179701
179750
  description: descriptionMarkupRef,
@@ -179711,7 +179760,7 @@ var createIssue = (params) => Effect_exports.gen(function* () {
179711
179760
  reportedTime: 0,
179712
179761
  reports: 0,
179713
179762
  subIssues: 0,
179714
- parents: [],
179763
+ parents,
179715
179764
  childInfo: [],
179716
179765
  dueDate: null,
179717
179766
  rank
@@ -179719,9 +179768,9 @@ var createIssue = (params) => Effect_exports.gen(function* () {
179719
179768
  yield* client.addCollection(
179720
179769
  tracker.class.Issue,
179721
179770
  project3._id,
179722
- project3._id,
179723
- tracker.class.Project,
179724
- "issues",
179771
+ attachedTo,
179772
+ attachedToClass,
179773
+ collection,
179725
179774
  issueData,
179726
179775
  issueId
179727
179776
  );
@@ -180117,7 +180166,7 @@ var issueTools = [
180117
180166
  },
180118
180167
  {
180119
180168
  name: "create_issue",
180120
- description: "Create a new issue in a Huly project. Description supports markdown formatting. Returns the created issue identifier.",
180169
+ description: "Create a new issue in a Huly project. Optionally create as a sub-issue by specifying parentIssue. Description supports markdown formatting. Returns the created issue identifier.",
180121
180170
  category: CATEGORY8,
180122
180171
  inputSchema: createIssueParamsJsonSchema,
180123
180172
  handler: createToolHandler(
@@ -181807,10 +181856,10 @@ var McpServerService = class _McpServerService extends Context_exports.Tag("@hul
181807
181856
  toolCount: registry2.definitions.length,
181808
181857
  toolsets
181809
181858
  });
181810
- const server = config3.transport === "stdio" ? createMcpServer(hulyClient, storageClient, telemetry, registry2, workspaceClient) : null;
181811
181859
  const flushTelemetry = Effect_exports.ignore(
181812
181860
  Effect_exports.tryPromise(() => telemetry.shutdown())
181813
181861
  );
181862
+ const serverRef = yield* Ref_exports.make(null);
181814
181863
  const isRunning2 = yield* Ref_exports.make(false);
181815
181864
  const operations = {
181816
181865
  run: () => Effect_exports.gen(function* () {
@@ -181821,8 +181870,9 @@ var McpServerService = class _McpServerService extends Context_exports.Tag("@hul
181821
181870
  }
181822
181871
  yield* Ref_exports.set(isRunning2, true);
181823
181872
  if (config3.transport === "stdio") {
181873
+ const stdioServer = createMcpServer(hulyClient, storageClient, telemetry, registry2, workspaceClient);
181874
+ yield* Ref_exports.set(serverRef, stdioServer);
181824
181875
  const transport = new StdioServerTransport();
181825
- const stdioServer = assertExists(server, "server must exist for stdio transport");
181826
181876
  yield* Effect_exports.tryPromise({
181827
181877
  try: () => stdioServer.connect(transport),
181828
181878
  catch: (e) => new McpServerError({
@@ -181883,14 +181933,16 @@ var McpServerService = class _McpServerService extends Context_exports.Tag("@hul
181883
181933
  }
181884
181934
  yield* Ref_exports.set(isRunning2, false);
181885
181935
  yield* flushTelemetry;
181886
- if (server) {
181936
+ const runningServer = yield* Ref_exports.get(serverRef);
181937
+ if (runningServer !== null) {
181887
181938
  yield* Effect_exports.tryPromise({
181888
- try: () => server.close(),
181939
+ try: () => runningServer.close(),
181889
181940
  catch: (e) => new McpServerError({
181890
181941
  message: `Failed to stop server: ${String(e)}`,
181891
181942
  cause: e
181892
181943
  })
181893
181944
  });
181945
+ yield* Ref_exports.set(serverRef, null);
181894
181946
  }
181895
181947
  })
181896
181948
  };