@firfi/huly-mcp 0.30.1 → 0.31.0

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 +1 -0
  2. package/dist/index.cjs +307 -56
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -489,6 +489,7 @@ SDK upgrade revisit:
489
489
  | `update_channel` | Update fields on an existing Huly channel. Only provided fields are modified. |
490
490
  | `delete_channel` | Permanently delete a Huly channel. This action cannot be undone. For reversible channel lifecycle changes, use archive_channel and unarchive_channel instead. |
491
491
  | `list_channel_messages` | List messages in a Huly channel. Returns messages sorted by date (newest first). |
492
+ | `list_external_channel_messages` | List read-only messages for an external Gmail or Telegram channel by channel name or ID. The limit defaults to 50 and is capped at 200. When this build does not include a compatible Huly external-message SDK/model for the requested provider, returns supported=false, an unsupportedReason, and an empty messages array; it never sends, replies, deletes, mutates, or returns fake messages. |
492
493
  | `send_channel_message` | Send a message to a Huly channel. Message body supports markdown formatting. |
493
494
  | `update_channel_message` | Update a channel message. Only the body can be modified. |
494
495
  | `delete_channel_message` | Permanently delete a channel message. This action cannot be undone. |
package/dist/index.cjs CHANGED
@@ -155260,6 +155260,10 @@ var HulyClient = class _HulyClient extends Context_exports.Tag("@hulymcp/HulyCli
155260
155260
  (client2) => client2.findOne(_class, query, options),
155261
155261
  "findOne failed"
155262
155262
  ),
155263
+ findAllInModel: (_class, query, options) => withClient(
155264
+ (client2) => Promise.resolve(client2.getModel().findAllSync(_class, query, options)),
155265
+ "findAllInModel failed"
155266
+ ),
155263
155267
  createDoc: (_class, space, attributes, id) => withClient(
155264
155268
  (client2) => client2.createDoc(_class, space, attributes, id),
155265
155269
  "createDoc failed"
@@ -155341,6 +155345,7 @@ var HulyClient = class _HulyClient extends Context_exports.Tag("@hulymcp/HulyCli
155341
155345
  markupUrlConfig: testMarkupUrlConfig,
155342
155346
  workbenchUrlConfig: testWorkbenchUrlConfig,
155343
155347
  findAll: noopFindAll,
155348
+ findAllInModel: noopFindAll,
155344
155349
  findOne: noopFindOne,
155345
155350
  createDoc: notImplemented("createDoc"),
155346
155351
  updateDoc: notImplemented("updateDoc"),
@@ -165740,11 +165745,14 @@ var McpErrorCode = {
165740
165745
  InternalError: -32603,
165741
165746
  ResourceNotFound: -32002
165742
165747
  };
165743
- var createErrorResponse = (text, errorCode, errorTag) => ({
165744
- content: [{ type: "text", text }],
165745
- isError: true,
165746
- _meta: { errorCode, errorTag }
165747
- });
165748
+ var createErrorResponse = (text, errorCode, errorTag, warnings = []) => {
165749
+ const warningContent = warnings.length > 0 ? [{ type: "text", text: encodeJsonText({ warnings }) }] : [];
165750
+ return {
165751
+ content: [{ type: "text", text }, ...warningContent],
165752
+ isError: true,
165753
+ _meta: { errorCode, errorTag }
165754
+ };
165755
+ };
165748
165756
  var INVALID_PARAMS_TAGS = /* @__PURE__ */ new Set([
165749
165757
  "IssueNotFoundError",
165750
165758
  "ProjectNotFoundError",
@@ -165857,13 +165865,13 @@ var INTERNAL_ERROR_PREFIX = {
165857
165865
  HulyConnectionError: "Connection error",
165858
165866
  HulyAuthError: "Authentication error"
165859
165867
  };
165860
- var mapDomainErrorToMcp = (error2) => {
165868
+ var mapDomainErrorToMcp = (error2, warnings = []) => {
165861
165869
  if (INVALID_PARAMS_TAGS.has(error2._tag)) {
165862
- return createErrorResponse(error2.message, McpErrorCode.InvalidParams);
165870
+ return createErrorResponse(error2.message, McpErrorCode.InvalidParams, void 0, warnings);
165863
165871
  }
165864
165872
  const prefix = INTERNAL_ERROR_PREFIX[error2._tag];
165865
165873
  const message = prefix !== void 0 ? `${prefix}: ${error2.message}` : error2.message;
165866
- return createErrorResponse(message, McpErrorCode.InternalError, error2._tag);
165874
+ return createErrorResponse(message, McpErrorCode.InternalError, error2._tag, warnings);
165867
165875
  };
165868
165876
  var formatParseError = (error2) => {
165869
165877
  const issues = ParseResult_exports.ArrayFormatter.formatErrorSync(error2);
@@ -165884,34 +165892,44 @@ var mapParseCauseToMcp = (cause3, toolName) => {
165884
165892
  }
165885
165893
  return createErrorResponse("An unexpected error occurred", McpErrorCode.InternalError);
165886
165894
  };
165887
- var mapDomainCauseToMcp = (cause3) => {
165895
+ var mapDomainCauseToMcp = (cause3, warnings = []) => {
165888
165896
  if (Cause_exports.isFailType(cause3)) {
165889
- return mapDomainErrorToMcp(cause3.error);
165897
+ return mapDomainErrorToMcp(cause3.error, warnings);
165890
165898
  }
165891
165899
  if (Cause_exports.isDieType(cause3)) {
165892
- return createErrorResponse("An unexpected error occurred", McpErrorCode.InternalError, "UnexpectedError");
165900
+ return createErrorResponse("An unexpected error occurred", McpErrorCode.InternalError, "UnexpectedError", warnings);
165893
165901
  }
165894
165902
  const failures3 = Chunk_exports.toArray(Cause_exports.failures(cause3));
165895
165903
  if (failures3.length > 0) {
165896
- return mapDomainErrorToMcp(failures3[0]);
165904
+ return mapDomainErrorToMcp(failures3[0], warnings);
165897
165905
  }
165898
- return createErrorResponse("An unexpected error occurred", McpErrorCode.InternalError);
165906
+ return createErrorResponse("An unexpected error occurred", McpErrorCode.InternalError, void 0, warnings);
165899
165907
  };
165900
165908
  var encodeJsonText = (value3) => {
165901
165909
  const text = JSON.stringify(value3);
165902
165910
  return typeof text === "string" ? text : "null";
165903
165911
  };
165904
- var createSuccessResponse = (result) => ({
165905
- content: [{ type: "text", text: encodeJsonText(result) }],
165906
- structuredContent: {
165912
+ var createSuccessResponse = (result, warnings = []) => ({
165913
+ content: [
165914
+ { type: "text", text: encodeJsonText(result) },
165915
+ ...warnings.length > 0 ? [{ type: "text", text: encodeJsonText({ warnings }) }] : []
165916
+ ],
165917
+ structuredContent: warnings.length > 0 ? {
165918
+ result,
165919
+ warnings
165920
+ } : {
165907
165921
  result
165908
165922
  }
165909
165923
  });
165910
165924
  var createUnknownToolError = (toolName) => createErrorResponse(`Unknown tool: ${toolName}`, McpErrorCode.InvalidParams, "UnknownTool");
165911
165925
  var createInvalidParamsError = (message, errorTag) => createErrorResponse(message, McpErrorCode.InvalidParams, errorTag);
165912
- var toMcpResponse = (response) => {
165913
- const { _meta: _, ...wire } = response;
165914
- return wire;
165926
+ var toMcpResponse = (response) => response.isError === true ? {
165927
+ content: response.content,
165928
+ isError: true
165929
+ } : {
165930
+ content: response.content,
165931
+ ...response.structuredContent === void 0 ? {} : { structuredContent: response.structuredContent },
165932
+ ...response.isError === void 0 ? {} : { isError: response.isError }
165915
165933
  };
165916
165934
 
165917
165935
  // src/mcp/http-2026-dispatcher.ts
@@ -166468,6 +166486,24 @@ var StdioServerTransport = class {
166468
166486
  }
166469
166487
  };
166470
166488
 
166489
+ // src/domain/schemas/tool-warnings.ts
166490
+ var ToolWarningCodeSchema = Schema_exports.Literal("status_metadata_unresolved").annotations({
166491
+ identifier: "ToolWarningCode",
166492
+ title: "ToolWarningCode",
166493
+ description: "Machine-readable code for an agent-visible MCP tool warning."
166494
+ });
166495
+ var StatusMetadataUnresolvedWarningCode = ToolWarningCodeSchema.literals[0];
166496
+ var ToolWarningSchema = Schema_exports.Struct({
166497
+ code: ToolWarningCodeSchema,
166498
+ message: Schema_exports.Trim.pipe(Schema_exports.nonEmptyString()).annotations({
166499
+ description: "LLM-facing explanation of what part of the returned tool payload is degraded and how the agent should interpret it."
166500
+ })
166501
+ }).annotations({
166502
+ identifier: "ToolWarning",
166503
+ title: "ToolWarning",
166504
+ description: "Warning surfaced to an agent when a tool result is intentionally degraded instead of failing."
166505
+ });
166506
+
166471
166507
  // src/domain/schemas/recurrence-primitives.ts
166472
166508
  var MAX_ZERO_BASED_MONTH_INDEX = 11;
166473
166509
  var MAX_MONTH_DAY = 31;
@@ -167252,6 +167288,7 @@ var ProjectTypeDetailSchema = Schema_exports.Struct({
167252
167288
  descriptor: NonEmptyString2,
167253
167289
  classic: Schema_exports.Boolean,
167254
167290
  isDefaultClassic: Schema_exports.Boolean,
167291
+ statusCount: Count,
167255
167292
  taskTypes: Schema_exports.Array(TaskTypeSummarySchema),
167256
167293
  statuses: Schema_exports.Array(IssueStatusSummarySchema),
167257
167294
  statusCategories: Schema_exports.Array(StatusCategorySummarySchema),
@@ -170099,6 +170136,91 @@ var parseUpdatePersonParams = Schema_exports.decodeUnknown(UpdatePersonParamsSch
170099
170136
  var parseDeletePersonParams = Schema_exports.decodeUnknown(DeletePersonParamsSchema);
170100
170137
  var parseListEmployeesParams = Schema_exports.decodeUnknown(ListEmployeesParamsSchema);
170101
170138
 
170139
+ // src/domain/schemas/external-channel-messages.ts
170140
+ var ExternalChannelMessageProviderValues = ["gmail", "telegram"];
170141
+ var ExternalChannelMessageProviderSchema = Schema_exports.Literal(...ExternalChannelMessageProviderValues);
170142
+ var DEFAULT_EXTERNAL_CHANNEL_MESSAGE_LIMIT = DEFAULT_LIMIT;
170143
+ var ListExternalChannelMessagesParamsSchema = Schema_exports.Struct({
170144
+ provider: ExternalChannelMessageProviderSchema.annotations({
170145
+ description: "External provider to read from. Supported locator values are validated for gmail and telegram; providers without a compatible installed Huly message SDK return structured unsupported results instead of fake data."
170146
+ }),
170147
+ channel: ChannelIdentifier.annotations({
170148
+ description: "External channel name or Huly channel ID locator, such as a Gmail label/inbox name or Telegram chat name/id."
170149
+ }),
170150
+ limit: Schema_exports.optional(LimitParam.annotations({
170151
+ description: `Maximum number of external messages to return (default: ${DEFAULT_EXTERNAL_CHANNEL_MESSAGE_LIMIT}, max: 200).`
170152
+ }))
170153
+ }).annotations({
170154
+ title: "ListExternalChannelMessagesParams",
170155
+ description: "Parameters for listing read-only Gmail or Telegram external channel messages."
170156
+ });
170157
+ var ExternalChannelMessageId = NonEmptyString2.pipe(Schema_exports.brand("ExternalChannelMessageId")).annotations({
170158
+ identifier: "ExternalChannelMessageId",
170159
+ title: "ExternalChannelMessageId",
170160
+ description: "Opaque external provider message ID."
170161
+ });
170162
+ var ExternalChannelMessageSubject = NonEmptyString2.pipe(
170163
+ Schema_exports.brand("ExternalChannelMessageSubject")
170164
+ ).annotations({
170165
+ identifier: "ExternalChannelMessageSubject",
170166
+ title: "ExternalChannelMessageSubject",
170167
+ description: "Non-empty external message subject. Omit the field when the provider has no subject value."
170168
+ });
170169
+ var ExternalChannelMessageSender = NonEmptyString2.pipe(Schema_exports.brand("ExternalChannelMessageSender")).annotations({
170170
+ identifier: "ExternalChannelMessageSender",
170171
+ title: "ExternalChannelMessageSender",
170172
+ description: "Non-empty normalized external message sender label or address."
170173
+ });
170174
+ var ExternalChannelMessageSenderId = NonEmptyString2.pipe(Schema_exports.brand("ExternalChannelMessageSenderId")).annotations({
170175
+ identifier: "ExternalChannelMessageSenderId",
170176
+ title: "ExternalChannelMessageSenderId",
170177
+ description: "Non-empty opaque external provider sender ID."
170178
+ });
170179
+ var ExternalChannelMessageSummarySchema = Schema_exports.Struct({
170180
+ id: ExternalChannelMessageId,
170181
+ subject: Schema_exports.optional(ExternalChannelMessageSubject),
170182
+ bodyPreview: NonEmptyString2,
170183
+ sender: Schema_exports.optional(ExternalChannelMessageSender),
170184
+ senderId: Schema_exports.optional(ExternalChannelMessageSenderId),
170185
+ createdOn: Schema_exports.optional(Timestamp),
170186
+ modifiedOn: Schema_exports.optional(Timestamp),
170187
+ url: Schema_exports.optional(UrlString)
170188
+ }).annotations({
170189
+ title: "ExternalChannelMessageSummary",
170190
+ description: "Normalized read-only summary of one external Gmail or Telegram message."
170191
+ });
170192
+ var ListExternalChannelMessagesSupportedResultSchema = Schema_exports.Struct({
170193
+ supported: Schema_exports.Literal(true),
170194
+ provider: ExternalChannelMessageProviderSchema,
170195
+ channel: ChannelIdentifier,
170196
+ limit: LimitParam,
170197
+ messages: Schema_exports.Array(ExternalChannelMessageSummarySchema)
170198
+ }).annotations({
170199
+ title: "ListExternalChannelMessagesSupportedResult",
170200
+ description: "External channel messages returned from a compatible installed Huly provider SDK/model."
170201
+ });
170202
+ var ListExternalChannelMessagesUnsupportedResultSchema = Schema_exports.Struct({
170203
+ supported: Schema_exports.Literal(false),
170204
+ provider: ExternalChannelMessageProviderSchema,
170205
+ channel: ChannelIdentifier,
170206
+ limit: LimitParam,
170207
+ unsupportedReason: NonEmptyString2,
170208
+ messages: Schema_exports.Tuple()
170209
+ }).annotations({
170210
+ title: "ListExternalChannelMessagesUnsupportedResult",
170211
+ description: "Explicit no-fake-data result when the requested external provider cannot be read in this build."
170212
+ });
170213
+ var ListExternalChannelMessagesResultSchema = Schema_exports.Union(
170214
+ ListExternalChannelMessagesSupportedResultSchema,
170215
+ ListExternalChannelMessagesUnsupportedResultSchema
170216
+ ).annotations({
170217
+ title: "ListExternalChannelMessagesResult",
170218
+ description: "Read-only external channel message listing result."
170219
+ });
170220
+ var listExternalChannelMessagesParamsJsonSchema = JSONSchema_exports.make(ListExternalChannelMessagesParamsSchema);
170221
+ var parseListExternalChannelMessagesParams = Schema_exports.decodeUnknown(ListExternalChannelMessagesParamsSchema);
170222
+ var encodeListExternalChannelMessagesResult = Schema_exports.encodeSync(ListExternalChannelMessagesResultSchema);
170223
+
170102
170224
  // src/domain/schemas/channels.ts
170103
170225
  var ListChannelsParamsBase = Schema_exports.Struct({
170104
170226
  nameSearch: Schema_exports.optional(Schema_exports.String.annotations({
@@ -174502,15 +174624,52 @@ var parseUpdateDriveFileCommentParams = Schema_exports.decodeUnknown(UpdateDrive
174502
174624
  var parseDeleteDriveFileCommentParams = Schema_exports.decodeUnknown(DeleteDriveFileCommentParamsSchema);
174503
174625
  var parseListDriveFileActivityParams = Schema_exports.decodeUnknown(ListDriveFileActivityParamsSchema);
174504
174626
 
174627
+ // src/huly/diagnostics.ts
174628
+ var Diagnostics = class extends Context_exports.Tag("@hulymcp/Diagnostics")() {
174629
+ };
174630
+ var warningLogText = (warning) => `Agent-visible tool warning [${warning.code}]: ${warning.message}`;
174631
+ var makeDiagnosticsScope = Effect_exports.gen(function* () {
174632
+ const warningsRef = yield* Ref_exports.make([]);
174633
+ return {
174634
+ service: {
174635
+ warnAgent: (warning) => Ref_exports.update(warningsRef, (warnings) => [...warnings, warning]).pipe(
174636
+ Effect_exports.zipRight(Effect_exports.logWarning(warningLogText(warning)))
174637
+ ),
174638
+ trail: (message) => Effect_exports.logInfo(`Diagnostic trail: ${message}`)
174639
+ },
174640
+ drainWarnings: Ref_exports.get(warningsRef)
174641
+ };
174642
+ });
174643
+
174505
174644
  // src/version.ts
174506
- var VERSION = true ? "0.30.1" : "0.0.0-dev";
174645
+ var VERSION = true ? "0.31.0" : "0.0.0-dev";
174507
174646
 
174508
174647
  // src/mcp/tool-output-schema.ts
174648
+ var toolWarningCodeEnum = [...ToolWarningCodeSchema.literals];
174509
174649
  var defaultToolOutputSchema = {
174510
174650
  type: "object",
174511
174651
  properties: {
174512
174652
  result: {
174513
174653
  description: "The successful tool result. The same value is also serialized as JSON in the text content for clients that do not read structuredContent."
174654
+ },
174655
+ warnings: {
174656
+ type: "array",
174657
+ description: "Optional agent-visible warnings about degraded result fidelity. Omitted when the server returned the documented happy-path payload.",
174658
+ items: {
174659
+ type: "object",
174660
+ properties: {
174661
+ code: {
174662
+ type: "string",
174663
+ enum: toolWarningCodeEnum
174664
+ },
174665
+ message: {
174666
+ type: "string",
174667
+ minLength: 1
174668
+ }
174669
+ },
174670
+ required: ["code", "message"],
174671
+ additionalProperties: false
174672
+ }
174514
174673
  }
174515
174674
  },
174516
174675
  required: ["result"]
@@ -176182,7 +176341,7 @@ var findProject = (projectIdentifier) => Effect_exports.gen(function* () {
176182
176341
  );
176183
176342
  return { client, project: project3 };
176184
176343
  });
176185
- var statusCategoryValueFromRef = (category) => category === void 0 ? "unknown" : StatusCategoryEntries.find((entry) => entry.ref === category)?.key ?? "unknown";
176344
+ var statusCategoryValueFromRef = (category) => category === void 0 ? UnknownStatusCategoryValue : StatusCategoryEntries.find((entry) => entry.ref === category)?.key ?? UnknownStatusCategoryValue;
176186
176345
  var workflowStatusFromDoc = (doc) => {
176187
176346
  return {
176188
176347
  _id: doc._id,
@@ -176195,7 +176354,7 @@ var workflowStatusFromRef = (statusRef) => {
176195
176354
  return {
176196
176355
  _id: statusRef,
176197
176356
  name,
176198
- category: "unknown"
176357
+ category: UnknownStatusCategoryValue
176199
176358
  };
176200
176359
  };
176201
176360
  var uniqueStatusRefs = (refs) => refs.reduce(
@@ -176207,6 +176366,51 @@ var uniqueStatusDocs = (statuses) => Array.from(statuses).reduce(
176207
176366
  []
176208
176367
  );
176209
176368
  var uniqueProjectTypeStatusRefs = (statuses) => uniqueStatusRefs(statuses.map((status) => status._id));
176369
+ var missingStatusRefs = (statusRefs, statusDocs) => statusRefs.filter((statusRef) => !statusDocs.some((statusDoc) => statusDoc._id === statusRef));
176370
+ var resolveByStatusRef = (statusRefs, statusDocs, fromDoc, fromRef) => {
176371
+ const statusDocsById = new Map(statusDocs.map((statusDoc) => [statusDoc._id, statusDoc]));
176372
+ return statusRefs.map((statusRef) => {
176373
+ const statusDoc = statusDocsById.get(statusRef);
176374
+ return statusDoc === void 0 ? fromRef(statusRef) : fromDoc(statusDoc);
176375
+ });
176376
+ };
176377
+ var workflowStatusesFromDocsOrRefs = (statusRefs, statusDocs) => resolveByStatusRef(statusRefs, statusDocs, workflowStatusFromDoc, workflowStatusFromRef);
176378
+ var findStatusDocs = (client, statusRefs) => Effect_exports.gen(function* () {
176379
+ const diagnostics = yield* Diagnostics;
176380
+ const remoteResult = yield* Effect_exports.either(
176381
+ client.findAll(
176382
+ core.class.Status,
176383
+ hulyQuery({ _id: { $in: [...statusRefs] } })
176384
+ )
176385
+ );
176386
+ const remoteDocs = remoteResult._tag === "Right" ? uniqueStatusDocs(remoteResult.right) : [];
176387
+ const unresolvedRefs = missingStatusRefs(statusRefs, remoteDocs);
176388
+ if (unresolvedRefs.length === 0) {
176389
+ return remoteDocs;
176390
+ }
176391
+ const modelResult = yield* Effect_exports.either(
176392
+ client.findAllInModel(
176393
+ core.class.Status,
176394
+ hulyQuery({ _id: { $in: unresolvedRefs } })
176395
+ )
176396
+ );
176397
+ const modelDocs = modelResult._tag === "Right" ? uniqueStatusDocs(modelResult.right) : [];
176398
+ const combinedDocs = uniqueStatusDocs([...remoteDocs, ...modelDocs]);
176399
+ const stillUnresolvedRefs = missingStatusRefs(statusRefs, combinedDocs);
176400
+ if (stillUnresolvedRefs.length > 0) {
176401
+ const remoteError = remoteResult._tag === "Left" ? ` Remote error: ${remoteResult.left.message}` : "";
176402
+ const modelError = modelResult._tag === "Left" ? ` Model error: ${modelResult.left.message}` : "";
176403
+ yield* diagnostics.warnAgent({
176404
+ code: StatusMetadataUnresolvedWarningCode,
176405
+ message: `Huly did not return metadata for ${stillUnresolvedRefs.length} workflow status ref(s). The tool result uses ref-derived status names and category "${UnknownStatusCategoryValue}" for those statuses; do not infer completion or cancellation semantics from those fallback names.${remoteError}${modelError}`
176406
+ });
176407
+ } else if (remoteResult._tag === "Left") {
176408
+ yield* diagnostics.trail(
176409
+ `Server status metadata lookup failed, but the local Huly model resolved all requested workflow statuses. Remote error: ${remoteResult.left.message}`
176410
+ );
176411
+ }
176412
+ return combinedDocs;
176413
+ });
176210
176414
  var findProjectWithStatuses = (projectIdentifier) => Effect_exports.gen(function* () {
176211
176415
  const client = yield* HulyClient;
176212
176416
  const project3 = yield* findOneOrFail(
@@ -176222,19 +176426,8 @@ var findProjectWithStatuses = (projectIdentifier) => Effect_exports.gen(function
176222
176426
  if (statusRefs.length === 0) {
176223
176427
  return [];
176224
176428
  }
176225
- const statusDocsResult = yield* Effect_exports.either(
176226
- client.findAll(
176227
- core.class.Status,
176228
- hulyQuery({ _id: { $in: statusRefs } })
176229
- )
176230
- );
176231
- if (statusDocsResult._tag === "Right") {
176232
- return uniqueStatusDocs(statusDocsResult.right).map(workflowStatusFromDoc);
176233
- }
176234
- yield* Effect_exports.logWarning(
176235
- `Status query failed for project ${projectIdentifier}, using fallback. statusCategory filtering is unavailable until Huly returns status metadata. Error: ${statusDocsResult.left.message}`
176236
- );
176237
- return statusRefs.map(workflowStatusFromRef);
176429
+ const statusDocs = yield* findStatusDocs(client, statusRefs);
176430
+ return workflowStatusesFromDocsOrRefs(statusRefs, statusDocs);
176238
176431
  }) : [];
176239
176432
  const defaultStatusId = project3.defaultIssueStatus || statuses[0]?._id;
176240
176433
  return { client, defaultStatusId, project: project3, projectType, statuses };
@@ -176598,19 +176791,27 @@ var createHandler = (toolName, provide4, parse5, operation, encode8) => async (a
176598
176791
  if (Exit_exports.isFailure(parseResult)) {
176599
176792
  return mapParseCauseToMcp(parseResult.cause, toolName);
176600
176793
  }
176601
- const provided = provide4({ hulyClient, storageClient, workspaceClient })(operation(parseResult.value));
176794
+ const diagnosticsScope = await Effect_exports.runPromise(makeDiagnosticsScope);
176795
+ const provided = provide4({
176796
+ hulyClient,
176797
+ storageClient,
176798
+ workspaceClient
176799
+ })(operation(parseResult.value));
176602
176800
  if (Either_exports.isLeft(provided)) {
176603
176801
  return provided.left;
176604
176802
  }
176605
- const operationResult = await Effect_exports.runPromiseExit(provided.right);
176803
+ const operationResult = await Effect_exports.runPromiseExit(
176804
+ provided.right.pipe(Effect_exports.provideService(Diagnostics, diagnosticsScope.service))
176805
+ );
176806
+ const warnings = await Effect_exports.runPromise(diagnosticsScope.drainWarnings);
176606
176807
  if (Exit_exports.isFailure(operationResult)) {
176607
- return mapDomainCauseToMcp(operationResult.cause);
176808
+ return mapDomainCauseToMcp(operationResult.cause, warnings);
176608
176809
  }
176609
176810
  try {
176610
176811
  const output = encode8 !== void 0 ? encode8(operationResult.value) : operationResult.value;
176611
- return createSuccessResponse(output);
176812
+ return createSuccessResponse(output, warnings);
176612
176813
  } catch {
176613
- return mapDomainErrorToMcp(new HulyError({ message: `Tool ${toolName} produced invalid output` }));
176814
+ return mapDomainErrorToMcp(new HulyError({ message: `Tool ${toolName} produced invalid output` }), warnings);
176614
176815
  }
176615
176816
  };
176616
176817
  var createToolHandler = (toolName, parse5, operation) => createHandler(toolName, provideHulyClient, parse5, operation);
@@ -179513,6 +179714,19 @@ var createDirectMessage = (params) => Effect_exports.gen(function* () {
179513
179714
  return { id: ChannelId.make(dmId), created: true };
179514
179715
  });
179515
179716
 
179717
+ // src/huly/operations/external-channel-messages.ts
179718
+ var EXTERNAL_CHANNEL_PACKAGE_INCOMPATIBLE_REASON = "package-incompatible: package.json and pnpm-lock.yaml include @hcengineering/contact provider refs for email/telegram, but no compatible Huly Gmail or Telegram message SDK package/model is installed; local platform-api examples only expose contact.class.Channel provider values, not external message documents";
179719
+ var listExternalChannelMessages = (params) => Effect_exports.sync(
179720
+ () => encodeListExternalChannelMessagesResult({
179721
+ supported: false,
179722
+ provider: params.provider,
179723
+ channel: params.channel,
179724
+ limit: params.limit ?? DEFAULT_EXTERNAL_CHANNEL_MESSAGE_LIMIT,
179725
+ unsupportedReason: EXTERNAL_CHANNEL_PACKAGE_INCOMPATIBLE_REASON,
179726
+ messages: []
179727
+ })
179728
+ );
179729
+
179516
179730
  // src/huly/operations/threads.ts
179517
179731
  var import_core31 = __toESM(require_lib4(), 1);
179518
179732
  var findReply = (client, channel, message, replyId) => Effect_exports.gen(function* () {
@@ -179806,6 +180020,17 @@ var channelTools = [
179806
180020
  listChannelMessages
179807
180021
  )
179808
180022
  },
180023
+ {
180024
+ name: "list_external_channel_messages",
180025
+ description: "List read-only messages for an external Gmail or Telegram channel by channel name or ID. The limit defaults to 50 and is capped at 200. When this build does not include a compatible Huly external-message SDK/model for the requested provider, returns supported=false, an unsupportedReason, and an empty messages array; it never sends, replies, deletes, mutates, or returns fake messages.",
180026
+ category: CATEGORY6,
180027
+ inputSchema: listExternalChannelMessagesParamsJsonSchema,
180028
+ handler: createToolHandler(
180029
+ "list_external_channel_messages",
180030
+ parseListExternalChannelMessagesParams,
180031
+ listExternalChannelMessages
180032
+ )
180033
+ },
179809
180034
  {
179810
180035
  name: "send_channel_message",
179811
180036
  description: "Send a message to a Huly channel. Message body supports markdown formatting.",
@@ -186694,6 +186919,18 @@ var labelTools = [
186694
186919
  // src/huly/operations/leads.ts
186695
186920
  var import_core50 = __toESM(require_lib4(), 1);
186696
186921
  var funnelAsSpace = (funnel) => toRef(funnel._id);
186922
+ var statusInfosWithFallbacks = (statusRefs, statusDocs) => resolveByStatusRef(
186923
+ statusRefs,
186924
+ statusDocs,
186925
+ (statusDoc) => ({
186926
+ _id: statusDoc._id,
186927
+ name: statusDoc.name
186928
+ }),
186929
+ (statusRef) => ({
186930
+ _id: statusRef,
186931
+ name: workflowStatusFromRef(statusRef).name
186932
+ })
186933
+ );
186697
186934
  var markupBlobRefAsMarkupRef = (value3) => value3;
186698
186935
  var normalizeLeadIdentifier = (identifier2) => {
186699
186936
  const match16 = /^(?:LEAD-)?(\d+)$/i.exec(identifier2.trim());
@@ -186742,7 +186979,7 @@ var getFunnelStatuses = (client, funnel) => Effect_exports.gen(function* () {
186742
186979
  })
186743
186980
  );
186744
186981
  }
186745
- const statusRefs = projectType.statuses.map((status) => status._id);
186982
+ const statusRefs = uniqueStatusRefs(projectType.statuses.map((status) => status._id));
186746
186983
  if (statusRefs.length === 0) {
186747
186984
  return yield* Effect_exports.fail(
186748
186985
  new HulyConnectionError({
@@ -186750,14 +186987,8 @@ var getFunnelStatuses = (client, funnel) => Effect_exports.gen(function* () {
186750
186987
  })
186751
186988
  );
186752
186989
  }
186753
- const statusDocs = yield* client.findAll(
186754
- core.class.Status,
186755
- { _id: { $in: [...statusRefs] } }
186756
- );
186757
- return statusDocs.map((doc) => ({
186758
- _id: doc._id,
186759
- name: doc.name
186760
- }));
186990
+ const statusDocs = yield* findStatusDocs(client, statusRefs);
186991
+ return statusInfosWithFallbacks(statusRefs, statusDocs);
186761
186992
  });
186762
186993
  var resolveStatusName2 = (statuses, statusId) => {
186763
186994
  const statusDoc = statuses.find((status) => status._id === statusId);
@@ -190591,7 +190822,7 @@ var STATUS_CATEGORIES = Object.values(STATUS_CATEGORY_BY_SDK_KEY).map((entry) =>
190591
190822
  name: entry.name
190592
190823
  }));
190593
190824
  var WORKFLOW_WARNING = "This changes workspace-level tracker configuration for every project using this project type.";
190594
- var toCategoryValue = (category) => category === void 0 ? "unknown" : REF_TO_CATEGORY.get(category) ?? "unknown";
190825
+ var toCategoryValue = (category) => category === void 0 ? UnknownStatusCategoryValue : REF_TO_CATEGORY.get(category) ?? UnknownStatusCategoryValue;
190595
190826
  var encodeOrConnectionError2 = (schema, value3, operation) => Schema_exports.encode(schema)(value3).pipe(
190596
190827
  Effect_exports.as(value3),
190597
190828
  Effect_exports.mapError(
@@ -190607,9 +190838,17 @@ var uniqueProjectStatuses = (statuses) => statuses.reduce(
190607
190838
  (unique, status) => unique.some((existing) => sameProjectStatus(existing, status)) ? unique : [...unique, status],
190608
190839
  []
190609
190840
  );
190610
- var getStatusDocs = (client, statusIds) => statusIds.length === 0 ? Effect_exports.succeed([]) : client.findAll(core.class.Status, hulyQuery({ _id: { $in: [...statusIds] } })).pipe(
190611
- Effect_exports.map(uniqueStatusDocs)
190612
- );
190841
+ var getStatusDocs = (client, statusIds) => statusIds.length === 0 ? Effect_exports.succeed([]) : findStatusDocs(client, statusIds);
190842
+ var fallbackStatusDoc = (statusId) => ({
190843
+ _id: statusId,
190844
+ _class: core.class.Status,
190845
+ space: core.space.Model,
190846
+ modifiedOn: 0,
190847
+ modifiedBy: core.account.System,
190848
+ ofAttribute: tracker.attribute.IssueStatus,
190849
+ name: workflowStatusFromRef(statusId).name
190850
+ });
190851
+ var statusDocsWithFallbacks = (statusIds, statusDocs) => resolveByStatusRef(statusIds, statusDocs, (statusDoc) => statusDoc, fallbackStatusDoc);
190613
190852
  var getTaskTypes = (client, taskTypeIds) => taskTypeIds.length === 0 ? Effect_exports.succeed([]) : client.findAll(task.class.TaskType, hulyQuery({ _id: { $in: [...taskTypeIds] } })).pipe(
190614
190853
  Effect_exports.map((result) => [...result])
190615
190854
  );
@@ -190628,7 +190867,9 @@ var getRecoverableStatusesByName = (client, name) => client.findAll(core.class.S
190628
190867
  );
190629
190868
  var loadWorkflowData = (client, projectType) => Effect_exports.gen(function* () {
190630
190869
  const taskTypes = yield* getTaskTypes(client, projectType.tasks);
190631
- const statuses = yield* getStatusDocs(client, uniqueStatusIds(projectType));
190870
+ const statusIds = uniqueStatusIds(projectType);
190871
+ const statusDocs = yield* getStatusDocs(client, statusIds);
190872
+ const statuses = statusDocsWithFallbacks(statusIds, statusDocs);
190632
190873
  return { projectType, taskTypes, statuses };
190633
190874
  });
190634
190875
  var isDefaultClassicProjectType = (projectType) => projectType._id === tracker.ids.ClassingProjectType || projectType.classic || normalizeForComparison(projectType.name) === "classic";
@@ -193351,6 +193592,13 @@ var DRAIN_TIMEOUT_MS2 = 3e4;
193351
193592
  var NPM_FETCH_TIMEOUT_MS = 5e3;
193352
193593
  var NPM_PACKAGE_NAME2 = "@firfi/huly-mcp";
193353
193594
  var computeOutputBytes = (response) => response.content.reduce((sum2, c) => sum2 + c.text.length, 0);
193595
+ var withResourceWarnings = (result, warnings) => warnings.length === 0 ? result : {
193596
+ ...result,
193597
+ _meta: {
193598
+ ...result._meta,
193599
+ warnings
193600
+ }
193601
+ };
193354
193602
  var deriveEditMode = (name, args2) => {
193355
193603
  if (name !== "edit_document" || args2 === void 0) return void 0;
193356
193604
  if (typeof args2 !== "object" || args2 === null || Array.isArray(args2)) return void 0;
@@ -193587,12 +193835,15 @@ var createMcpProtocolHandlers = (resolveClients, telemetry, registry2, getHulyCo
193587
193835
  resolveClients,
193588
193836
  (error2) => createResourceClientResolutionError(uri, error2)
193589
193837
  );
193838
+ const diagnosticsScope = await Effect_exports.runPromise(makeDiagnosticsScope);
193590
193839
  const resourceRead = await Effect_exports.runPromiseExit(
193591
193840
  readHulyResource(uri).pipe(
193592
- Effect_exports.provideService(HulyClient, clients.hulyClient)
193841
+ Effect_exports.provideService(HulyClient, clients.hulyClient),
193842
+ Effect_exports.provideService(Diagnostics, diagnosticsScope.service)
193593
193843
  )
193594
193844
  );
193595
- if (Exit_exports.isSuccess(resourceRead)) return resourceRead.value;
193845
+ const warnings = await Effect_exports.runPromise(diagnosticsScope.drainWarnings);
193846
+ if (Exit_exports.isSuccess(resourceRead)) return withResourceWarnings(resourceRead.value, warnings);
193596
193847
  return throwResourceReadError(uri, resourceRead.cause);
193597
193848
  } finally {
193598
193849
  leave();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@firfi/huly-mcp",
3
- "version": "0.30.1",
3
+ "version": "0.31.0",
4
4
  "description": "MCP server for Huly integration",
5
5
  "mcpName": "io.github.dearlordylord/huly-mcp",
6
6
  "type": "module",