@kadoa/mcp 0.3.8 → 0.3.9-rc.2

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 (2) hide show
  1. package/dist/index.js +501 -230
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -28820,6 +28820,17 @@ function isArrayBufferView(val) {
28820
28820
  }
28821
28821
  return result;
28822
28822
  }
28823
+ function getGlobal() {
28824
+ if (typeof globalThis !== "undefined")
28825
+ return globalThis;
28826
+ if (typeof self !== "undefined")
28827
+ return self;
28828
+ if (typeof window !== "undefined")
28829
+ return window;
28830
+ if (typeof global !== "undefined")
28831
+ return global;
28832
+ return {};
28833
+ }
28823
28834
  function forEach(obj, fn, { allOwnKeys = false } = {}) {
28824
28835
  if (obj === null || typeof obj === "undefined") {
28825
28836
  return;
@@ -28906,10 +28917,14 @@ var toString, getPrototypeOf, iterator, toStringTag, kindOf, kindOfTest = (type)
28906
28917
  } catch (e) {
28907
28918
  return false;
28908
28919
  }
28909
- }, isDate, isFile, isBlob, isFileList, isStream = (val) => isObject2(val) && isFunction(val.pipe), isFormData = (thing) => {
28920
+ }, isDate, isFile, isReactNativeBlob = (value) => {
28921
+ return !!(value && typeof value.uri !== "undefined");
28922
+ }, isReactNative = (formData) => formData && typeof formData.getParts !== "undefined", isBlob, isFileList, isStream = (val) => isObject2(val) && isFunction(val.pipe), G, FormDataCtor, isFormData = (thing) => {
28910
28923
  let kind;
28911
- return thing && (typeof FormData === "function" && thing instanceof FormData || isFunction(thing.append) && ((kind = kindOf(thing)) === "formdata" || kind === "object" && isFunction(thing.toString) && thing.toString() === "[object FormData]"));
28912
- }, isURLSearchParams, isReadableStream, isRequest, isResponse, isHeaders, trim = (str) => str.trim ? str.trim() : str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ""), _global, isContextDefined = (context) => !isUndefined(context) && context !== _global, extend2 = (a, b, thisArg, { allOwnKeys } = {}) => {
28924
+ return thing && (FormDataCtor && thing instanceof FormDataCtor || isFunction(thing.append) && ((kind = kindOf(thing)) === "formdata" || kind === "object" && isFunction(thing.toString) && thing.toString() === "[object FormData]"));
28925
+ }, isURLSearchParams, isReadableStream, isRequest, isResponse, isHeaders, trim = (str) => {
28926
+ return str.trim ? str.trim() : str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "");
28927
+ }, _global, isContextDefined = (context) => !isUndefined(context) && context !== _global, extend2 = (a, b, thisArg, { allOwnKeys } = {}) => {
28913
28928
  forEach(b, (val, key) => {
28914
28929
  if (thisArg && isFunction(val)) {
28915
28930
  Object.defineProperty(a, key, {
@@ -29089,6 +29104,8 @@ var init_utils = __esm(() => {
29089
29104
  isFile = kindOfTest("File");
29090
29105
  isBlob = kindOfTest("Blob");
29091
29106
  isFileList = kindOfTest("FileList");
29107
+ G = getGlobal();
29108
+ FormDataCtor = typeof G.FormData !== "undefined" ? G.FormData : undefined;
29092
29109
  isURLSearchParams = kindOfTest("URLSearchParams");
29093
29110
  [isReadableStream, isRequest, isResponse, isHeaders] = [
29094
29111
  "ReadableStream",
@@ -29146,6 +29163,8 @@ var init_utils = __esm(() => {
29146
29163
  isUndefined,
29147
29164
  isDate,
29148
29165
  isFile,
29166
+ isReactNativeBlob,
29167
+ isReactNative,
29149
29168
  isBlob,
29150
29169
  isRegExp,
29151
29170
  isFunction,
@@ -29197,11 +29216,20 @@ var init_AxiosError = __esm(() => {
29197
29216
  const axiosError = new AxiosError(error48.message, code || error48.code, config2, request, response);
29198
29217
  axiosError.cause = error48;
29199
29218
  axiosError.name = error48.name;
29219
+ if (error48.status != null && axiosError.status == null) {
29220
+ axiosError.status = error48.status;
29221
+ }
29200
29222
  customProps && Object.assign(axiosError, customProps);
29201
29223
  return axiosError;
29202
29224
  }
29203
29225
  constructor(message, code, config2, request, response) {
29204
29226
  super(message);
29227
+ Object.defineProperty(this, "message", {
29228
+ value: message,
29229
+ enumerable: true,
29230
+ writable: true,
29231
+ configurable: true
29232
+ });
29205
29233
  this.name = "AxiosError";
29206
29234
  this.isAxiosError = true;
29207
29235
  code && (this.code = code);
@@ -39414,6 +39442,10 @@ function toFormData(obj, formData, options) {
39414
39442
  }
39415
39443
  function defaultVisitor(value, key, path) {
39416
39444
  let arr = value;
39445
+ if (utils_default.isReactNative(formData) && utils_default.isReactNativeBlob(value)) {
39446
+ formData.append(renderKey(path, key, dots), convertValue(value));
39447
+ return false;
39448
+ }
39417
39449
  if (value && !path && typeof value === "object") {
39418
39450
  if (utils_default.endsWith(key, "{}")) {
39419
39451
  key = metaTokens ? key : key.slice(0, -2);
@@ -39763,66 +39795,70 @@ var init_defaults = __esm(() => {
39763
39795
  defaults = {
39764
39796
  transitional: transitional_default,
39765
39797
  adapter: ["xhr", "http", "fetch"],
39766
- transformRequest: [function transformRequest(data, headers) {
39767
- const contentType = headers.getContentType() || "";
39768
- const hasJSONContentType = contentType.indexOf("application/json") > -1;
39769
- const isObjectPayload = utils_default.isObject(data);
39770
- if (isObjectPayload && utils_default.isHTMLForm(data)) {
39771
- data = new FormData(data);
39772
- }
39773
- const isFormData2 = utils_default.isFormData(data);
39774
- if (isFormData2) {
39775
- return hasJSONContentType ? JSON.stringify(formDataToJSON_default(data)) : data;
39776
- }
39777
- if (utils_default.isArrayBuffer(data) || utils_default.isBuffer(data) || utils_default.isStream(data) || utils_default.isFile(data) || utils_default.isBlob(data) || utils_default.isReadableStream(data)) {
39778
- return data;
39779
- }
39780
- if (utils_default.isArrayBufferView(data)) {
39781
- return data.buffer;
39782
- }
39783
- if (utils_default.isURLSearchParams(data)) {
39784
- headers.setContentType("application/x-www-form-urlencoded;charset=utf-8", false);
39785
- return data.toString();
39786
- }
39787
- let isFileList2;
39788
- if (isObjectPayload) {
39789
- if (contentType.indexOf("application/x-www-form-urlencoded") > -1) {
39790
- return toURLEncodedForm(data, this.formSerializer).toString();
39798
+ transformRequest: [
39799
+ function transformRequest(data, headers) {
39800
+ const contentType = headers.getContentType() || "";
39801
+ const hasJSONContentType = contentType.indexOf("application/json") > -1;
39802
+ const isObjectPayload = utils_default.isObject(data);
39803
+ if (isObjectPayload && utils_default.isHTMLForm(data)) {
39804
+ data = new FormData(data);
39805
+ }
39806
+ const isFormData2 = utils_default.isFormData(data);
39807
+ if (isFormData2) {
39808
+ return hasJSONContentType ? JSON.stringify(formDataToJSON_default(data)) : data;
39809
+ }
39810
+ if (utils_default.isArrayBuffer(data) || utils_default.isBuffer(data) || utils_default.isStream(data) || utils_default.isFile(data) || utils_default.isBlob(data) || utils_default.isReadableStream(data)) {
39811
+ return data;
39812
+ }
39813
+ if (utils_default.isArrayBufferView(data)) {
39814
+ return data.buffer;
39815
+ }
39816
+ if (utils_default.isURLSearchParams(data)) {
39817
+ headers.setContentType("application/x-www-form-urlencoded;charset=utf-8", false);
39818
+ return data.toString();
39819
+ }
39820
+ let isFileList2;
39821
+ if (isObjectPayload) {
39822
+ if (contentType.indexOf("application/x-www-form-urlencoded") > -1) {
39823
+ return toURLEncodedForm(data, this.formSerializer).toString();
39824
+ }
39825
+ if ((isFileList2 = utils_default.isFileList(data)) || contentType.indexOf("multipart/form-data") > -1) {
39826
+ const _FormData = this.env && this.env.FormData;
39827
+ return toFormData_default(isFileList2 ? { "files[]": data } : data, _FormData && new _FormData, this.formSerializer);
39828
+ }
39791
39829
  }
39792
- if ((isFileList2 = utils_default.isFileList(data)) || contentType.indexOf("multipart/form-data") > -1) {
39793
- const _FormData = this.env && this.env.FormData;
39794
- return toFormData_default(isFileList2 ? { "files[]": data } : data, _FormData && new _FormData, this.formSerializer);
39830
+ if (isObjectPayload || hasJSONContentType) {
39831
+ headers.setContentType("application/json", false);
39832
+ return stringifySafely(data);
39795
39833
  }
39796
- }
39797
- if (isObjectPayload || hasJSONContentType) {
39798
- headers.setContentType("application/json", false);
39799
- return stringifySafely(data);
39800
- }
39801
- return data;
39802
- }],
39803
- transformResponse: [function transformResponse(data) {
39804
- const transitional = this.transitional || defaults.transitional;
39805
- const forcedJSONParsing = transitional && transitional.forcedJSONParsing;
39806
- const JSONRequested = this.responseType === "json";
39807
- if (utils_default.isResponse(data) || utils_default.isReadableStream(data)) {
39808
39834
  return data;
39809
39835
  }
39810
- if (data && utils_default.isString(data) && (forcedJSONParsing && !this.responseType || JSONRequested)) {
39811
- const silentJSONParsing = transitional && transitional.silentJSONParsing;
39812
- const strictJSONParsing = !silentJSONParsing && JSONRequested;
39813
- try {
39814
- return JSON.parse(data, this.parseReviver);
39815
- } catch (e) {
39816
- if (strictJSONParsing) {
39817
- if (e.name === "SyntaxError") {
39818
- throw AxiosError_default.from(e, AxiosError_default.ERR_BAD_RESPONSE, this, null, this.response);
39836
+ ],
39837
+ transformResponse: [
39838
+ function transformResponse(data) {
39839
+ const transitional = this.transitional || defaults.transitional;
39840
+ const forcedJSONParsing = transitional && transitional.forcedJSONParsing;
39841
+ const JSONRequested = this.responseType === "json";
39842
+ if (utils_default.isResponse(data) || utils_default.isReadableStream(data)) {
39843
+ return data;
39844
+ }
39845
+ if (data && utils_default.isString(data) && (forcedJSONParsing && !this.responseType || JSONRequested)) {
39846
+ const silentJSONParsing = transitional && transitional.silentJSONParsing;
39847
+ const strictJSONParsing = !silentJSONParsing && JSONRequested;
39848
+ try {
39849
+ return JSON.parse(data, this.parseReviver);
39850
+ } catch (e) {
39851
+ if (strictJSONParsing) {
39852
+ if (e.name === "SyntaxError") {
39853
+ throw AxiosError_default.from(e, AxiosError_default.ERR_BAD_RESPONSE, this, null, this.response);
39854
+ }
39855
+ throw e;
39819
39856
  }
39820
- throw e;
39821
39857
  }
39822
39858
  }
39859
+ return data;
39823
39860
  }
39824
- return data;
39825
- }],
39861
+ ],
39826
39862
  timeout: 0,
39827
39863
  xsrfCookieName: "XSRF-TOKEN",
39828
39864
  xsrfHeaderName: "X-XSRF-TOKEN",
@@ -40118,7 +40154,14 @@ var init_AxiosHeaders = __esm(() => {
40118
40154
  return this;
40119
40155
  }
40120
40156
  };
40121
- AxiosHeaders.accessor(["Content-Type", "Content-Length", "Accept", "Accept-Encoding", "User-Agent", "Authorization"]);
40157
+ AxiosHeaders.accessor([
40158
+ "Content-Type",
40159
+ "Content-Length",
40160
+ "Accept",
40161
+ "Accept-Encoding",
40162
+ "User-Agent",
40163
+ "Authorization"
40164
+ ]);
40122
40165
  utils_default.reduceDescriptors(AxiosHeaders.prototype, ({ value }, key) => {
40123
40166
  let mapped = key[0].toUpperCase() + key.slice(1);
40124
40167
  return {
@@ -41381,7 +41424,7 @@ var require_follow_redirects = __commonJS((exports, module) => {
41381
41424
  });
41382
41425
 
41383
41426
  // node_modules/axios/lib/env/data.js
41384
- var VERSION = "1.13.5";
41427
+ var VERSION = "1.13.6";
41385
41428
 
41386
41429
  // node_modules/axios/lib/helpers/parseProtocol.js
41387
41430
  function parseProtocol(url3) {
@@ -41791,11 +41834,14 @@ var progressEventReducer = (listener, isDownloadStream, freq = 3) => {
41791
41834
  }, freq);
41792
41835
  }, progressEventDecorator = (total, throttled) => {
41793
41836
  const lengthComputable = total != null;
41794
- return [(loaded) => throttled[0]({
41795
- lengthComputable,
41796
- total,
41797
- loaded
41798
- }), throttled[1]];
41837
+ return [
41838
+ (loaded) => throttled[0]({
41839
+ lengthComputable,
41840
+ total,
41841
+ loaded
41842
+ }),
41843
+ throttled[1]
41844
+ ];
41799
41845
  }, asyncDecorator = (fn) => (...args) => utils_default.asap(() => fn(...args));
41800
41846
  var init_progressEventReducer = __esm(() => {
41801
41847
  init_speedometer();
@@ -41925,10 +41971,7 @@ class Http2Sessions {
41925
41971
  };
41926
41972
  }
41927
41973
  session.once("close", removeSession);
41928
- let entry = [
41929
- session,
41930
- options
41931
- ];
41974
+ let entry = [session, options];
41932
41975
  authoritySessions ? authoritySessions.push(entry) : authoritySessions = this.sessions[authority] = [entry];
41933
41976
  return session;
41934
41977
  }
@@ -42049,12 +42092,7 @@ var init_http = __esm(() => {
42049
42092
  const authority = options.protocol + "//" + options.hostname + ":" + (options.port || (options.protocol === "https:" ? 443 : 80));
42050
42093
  const { http2Options, headers } = options;
42051
42094
  const session = http2Sessions.getSession(authority, http2Options);
42052
- const {
42053
- HTTP2_HEADER_SCHEME,
42054
- HTTP2_HEADER_METHOD,
42055
- HTTP2_HEADER_PATH,
42056
- HTTP2_HEADER_STATUS
42057
- } = http2.constants;
42095
+ const { HTTP2_HEADER_SCHEME, HTTP2_HEADER_METHOD, HTTP2_HEADER_PATH, HTTP2_HEADER_STATUS } = http2.constants;
42058
42096
  const http2Headers = {
42059
42097
  [HTTP2_HEADER_SCHEME]: options.protocol.replace(":", ""),
42060
42098
  [HTTP2_HEADER_METHOD]: options.method,
@@ -42241,9 +42279,12 @@ var init_http = __esm(() => {
42241
42279
  if (!utils_default.isStream(data)) {
42242
42280
  data = stream3.Readable.from(data, { objectMode: false });
42243
42281
  }
42244
- data = stream3.pipeline([data, new AxiosTransformStream_default({
42245
- maxRate: utils_default.toFiniteNumber(maxUploadRate)
42246
- })], utils_default.noop);
42282
+ data = stream3.pipeline([
42283
+ data,
42284
+ new AxiosTransformStream_default({
42285
+ maxRate: utils_default.toFiniteNumber(maxUploadRate)
42286
+ })
42287
+ ], utils_default.noop);
42247
42288
  onUploadProgress && data.on("progress", flushOnFinish(data, progressEventDecorator(contentLength, progressEventReducer(asyncDecorator(onUploadProgress), false, 3))));
42248
42289
  }
42249
42290
  let auth = undefined;
@@ -43073,11 +43114,7 @@ var DEFAULT_CHUNK_SIZE, isFunction2, globalFetchAPI, ReadableStream2, TextEncode
43073
43114
  }, seedCache, getFetch = (config2) => {
43074
43115
  let env = config2 && config2.env || {};
43075
43116
  const { fetch: fetch2, Request, Response: Response2 } = env;
43076
- const seeds = [
43077
- Request,
43078
- Response2,
43079
- fetch2
43080
- ];
43117
+ const seeds = [Request, Response2, fetch2];
43081
43118
  let len = seeds.length, i = len, seed, target, map3 = seedCache;
43082
43119
  while (i--) {
43083
43120
  seed = seeds[i];
@@ -43102,10 +43139,7 @@ var init_fetch = __esm(() => {
43102
43139
  Request,
43103
43140
  Response: Response2
43104
43141
  }))(utils_default.global);
43105
- ({
43106
- ReadableStream: ReadableStream2,
43107
- TextEncoder: TextEncoder2
43108
- } = utils_default.global);
43142
+ ({ ReadableStream: ReadableStream2, TextEncoder: TextEncoder2 } = utils_default.global);
43109
43143
  seedCache = new Map;
43110
43144
  adapter = getFetch();
43111
43145
  });
@@ -47552,7 +47586,7 @@ var import_debug, __require2, BASE_PATH, BaseAPI = class {
47552
47586
  const newSettings = await Promise.all(eventTypes.map(async (eventType) => {
47553
47587
  const existing = existingSettings.find((s) => s.eventType === eventType);
47554
47588
  if (existing?.id) {
47555
- const existingChannelIds = (existing.channels || []).map((c) => c.id).filter(Boolean);
47589
+ const existingChannelIds = (existing.channels || []).map((channel) => channel.id).filter(Boolean);
47556
47590
  const mergedChannelIds = [
47557
47591
  .../* @__PURE__ */ new Set([...existingChannelIds, ...channelIds])
47558
47592
  ];
@@ -47678,86 +47712,245 @@ var import_debug, __require2, BASE_PATH, BaseAPI = class {
47678
47712
  }));
47679
47713
  return channels;
47680
47714
  }
47681
- }, PUBLIC_API_URI, WSS_API_URI, REALTIME_API_URI, SDK_VERSION = "0.23.0", SDK_NAME = "kadoa-node-sdk", SDK_LANGUAGE = "node", debug5, Realtime = class {
47715
+ }, PUBLIC_API_URI, WSS_API_URI, REALTIME_API_URI, SDK_VERSION = "0.24.2", SDK_NAME = "kadoa-node-sdk", SDK_LANGUAGE = "node", debug5, isDrainControlMessage = (message) => message.type === "control.draining", isRealtimeEvent = (message) => message.type !== "heartbeat" && message.type !== "control.draining", _Realtime = class _Realtime2 {
47682
47716
  constructor(config2) {
47717
+ this.drainingSockets = /* @__PURE__ */ new Set;
47683
47718
  this.lastHeartbeat = Date.now();
47684
47719
  this.isConnecting = false;
47685
47720
  this.eventListeners = /* @__PURE__ */ new Set;
47686
47721
  this.connectionListeners = /* @__PURE__ */ new Set;
47687
47722
  this.errorListeners = /* @__PURE__ */ new Set;
47723
+ this.isClosed = false;
47724
+ this.hasConnectedOnce = false;
47725
+ this.recentEventIds = /* @__PURE__ */ new Set;
47726
+ this.recentEventIdQueue = [];
47727
+ this.maxRecentEventIds = 1000;
47688
47728
  this.apiKey = config2.apiKey;
47689
47729
  this.heartbeatInterval = config2.heartbeatInterval || 1e4;
47690
- this.reconnectDelay = config2.reconnectDelay || 5000;
47730
+ this.reconnectDelay = this.normalizeReconnectDelay(config2.reconnectDelay);
47691
47731
  this.missedHeartbeatsLimit = config2.missedHeartbeatsLimit || 30000;
47692
47732
  }
47693
47733
  async connect() {
47694
- if (this.isConnecting)
47734
+ if (this.isClosed || this.isConnecting || this.activeSocket) {
47695
47735
  return;
47736
+ }
47696
47737
  this.isConnecting = true;
47697
47738
  try {
47698
- const response = await fetch(`${PUBLIC_API_URI}/v4/oauth2/token`, {
47699
- method: "POST",
47700
- headers: {
47701
- "Content-Type": "application/json",
47702
- "x-api-key": `${this.apiKey}`,
47703
- "x-sdk-version": SDK_VERSION
47704
- }
47705
- });
47706
- const { access_token, team_id } = await response.json();
47707
- await new Promise((resolve, reject) => {
47708
- this.socket = new WebSocket(`${WSS_API_URI}?access_token=${access_token}`);
47709
- this.socket.onopen = () => {
47710
- this.isConnecting = false;
47711
- this.lastHeartbeat = Date.now();
47712
- if (this.socket?.readyState === WebSocket.OPEN) {
47713
- this.socket.send(JSON.stringify({
47714
- action: "subscribe",
47715
- channel: team_id
47716
- }));
47717
- debug5("Connected to WebSocket");
47718
- this.notifyConnectionListeners(true);
47719
- }
47720
- this.startHeartbeatCheck();
47721
- resolve();
47722
- };
47723
- this.socket.onmessage = (event) => {
47724
- try {
47725
- const data = JSON.parse(event.data);
47726
- if (data.type === "heartbeat") {
47727
- this.handleHeartbeat();
47728
- } else {
47729
- if (data?.id) {
47730
- fetch(`${REALTIME_API_URI}/api/v1/events/ack`, {
47731
- method: "POST",
47732
- headers: { "Content-Type": "application/json" },
47733
- body: JSON.stringify({ id: data.id })
47734
- });
47735
- }
47736
- this.notifyEventListeners(data);
47737
- }
47738
- } catch (err) {
47739
- debug5("Failed to parse incoming message: %O", err);
47740
- }
47741
- };
47742
- this.socket.onclose = () => {
47743
- debug5("WebSocket disconnected. Attempting to reconnect...");
47744
- this.isConnecting = false;
47745
- this.stopHeartbeatCheck();
47746
- this.notifyConnectionListeners(false, "Connection closed");
47747
- setTimeout(() => this.connect(), this.reconnectDelay);
47748
- };
47749
- this.socket.onerror = (error48) => {
47750
- debug5("WebSocket error: %O", error48);
47751
- this.isConnecting = false;
47752
- this.notifyErrorListeners(error48);
47753
- reject(error48);
47754
- };
47755
- });
47739
+ const { access_token, team_id } = await this.getOAuthToken();
47740
+ await this.openSocket(access_token, team_id, "active");
47741
+ this.hasConnectedOnce = true;
47756
47742
  } catch (err) {
47757
47743
  debug5("Failed to connect: %O", err);
47758
47744
  this.isConnecting = false;
47759
- setTimeout(() => this.connect(), this.reconnectDelay);
47745
+ this.notifyErrorListeners(err);
47746
+ if (!this.hasConnectedOnce) {
47747
+ throw err;
47748
+ }
47749
+ this.scheduleReconnect();
47750
+ }
47751
+ }
47752
+ async getOAuthToken() {
47753
+ const response = await fetch(`${PUBLIC_API_URI}/v4/oauth2/token`, {
47754
+ method: "POST",
47755
+ headers: {
47756
+ "Content-Type": "application/json",
47757
+ "x-api-key": `${this.apiKey}`,
47758
+ "x-sdk-version": SDK_VERSION
47759
+ }
47760
+ });
47761
+ return await response.json();
47762
+ }
47763
+ async openSocket(accessToken, teamId, role) {
47764
+ await new Promise((resolve, reject) => {
47765
+ const socket = new WebSocket(`${WSS_API_URI}?access_token=${accessToken}`);
47766
+ let settled = false;
47767
+ socket.onopen = () => {
47768
+ const subscribeMessage = {
47769
+ action: "subscribe",
47770
+ channel: teamId
47771
+ };
47772
+ if (this.lastCursor) {
47773
+ subscribeMessage.lastCursor = this.lastCursor;
47774
+ }
47775
+ socket.send(JSON.stringify(subscribeMessage));
47776
+ this.promoteSocket(socket, role);
47777
+ this.isConnecting = false;
47778
+ this.lastHeartbeat = Date.now();
47779
+ this.startHeartbeatCheck();
47780
+ debug5("Connected to WebSocket");
47781
+ if (!settled) {
47782
+ settled = true;
47783
+ resolve();
47784
+ }
47785
+ };
47786
+ socket.onmessage = (event) => {
47787
+ this.handleSocketMessage(socket, event.data);
47788
+ };
47789
+ socket.onclose = () => {
47790
+ this.handleSocketClose(socket);
47791
+ if (!settled) {
47792
+ settled = true;
47793
+ reject(new Error("WebSocket closed before opening"));
47794
+ }
47795
+ };
47796
+ socket.onerror = (error48) => {
47797
+ this.notifyErrorListeners(error48);
47798
+ if (!settled) {
47799
+ settled = true;
47800
+ reject(error48);
47801
+ return;
47802
+ }
47803
+ if (socket === this.activeSocket) {
47804
+ this.handleUnexpectedDisconnect("Socket error");
47805
+ }
47806
+ };
47807
+ });
47808
+ }
47809
+ promoteSocket(socket, role) {
47810
+ if (role === "replacement" && this.activeSocket && this.activeSocket !== socket) {
47811
+ this.drainingSockets.add(this.activeSocket);
47812
+ }
47813
+ this.activeSocket = socket;
47814
+ this.drainingSockets.delete(socket);
47815
+ if (role === "active" || !this.hasConnectedOnce) {
47816
+ this.notifyConnectionListeners(true);
47817
+ }
47818
+ }
47819
+ handleSocketMessage(socket, rawData) {
47820
+ try {
47821
+ const payload = typeof rawData === "string" ? rawData : rawData.toString?.() ?? "";
47822
+ const data = JSON.parse(payload);
47823
+ if (data.type === "heartbeat") {
47824
+ if (socket === this.activeSocket) {
47825
+ this.handleHeartbeat();
47826
+ }
47827
+ return;
47828
+ }
47829
+ if (isDrainControlMessage(data)) {
47830
+ this.handleDrainSignal(socket, data);
47831
+ return;
47832
+ }
47833
+ if (!isRealtimeEvent(data)) {
47834
+ return;
47835
+ }
47836
+ if (typeof data._cursor === "string") {
47837
+ this.lastCursor = data._cursor;
47838
+ }
47839
+ if (typeof data.id === "string") {
47840
+ fetch(`${REALTIME_API_URI}/api/v1/events/ack`, {
47841
+ method: "POST",
47842
+ headers: { "Content-Type": "application/json" },
47843
+ body: JSON.stringify({ id: data.id })
47844
+ }).catch((error48) => {
47845
+ debug5("Failed to acknowledge event %s: %O", data.id, error48);
47846
+ });
47847
+ }
47848
+ if (this.isDuplicateEvent(data.id)) {
47849
+ return;
47850
+ }
47851
+ this.notifyEventListeners(data);
47852
+ } catch (err) {
47853
+ debug5("Failed to parse incoming message: %O", err);
47854
+ }
47855
+ }
47856
+ handleDrainSignal(socket, message) {
47857
+ if (socket !== this.activeSocket || this.isClosed) {
47858
+ return;
47859
+ }
47860
+ debug5("Received drain signal, preparing replacement socket");
47861
+ this.drainingSockets.add(socket);
47862
+ this.scheduleDrainReconnect(message.retryAfterMs);
47863
+ }
47864
+ handleSocketClose(socket) {
47865
+ const wasActiveSocket = socket === this.activeSocket;
47866
+ this.drainingSockets.delete(socket);
47867
+ if (!wasActiveSocket) {
47868
+ return;
47869
+ }
47870
+ this.activeSocket = undefined;
47871
+ this.stopHeartbeatCheck();
47872
+ if (this.isClosed) {
47873
+ return;
47874
+ }
47875
+ if (this.drainingSockets.size > 0) {
47876
+ debug5("Draining socket closed after replacement was scheduled");
47877
+ return;
47878
+ }
47879
+ this.handleUnexpectedDisconnect("Connection closed");
47880
+ }
47881
+ handleUnexpectedDisconnect(reason) {
47882
+ this.isConnecting = false;
47883
+ this.notifyConnectionListeners(false, reason);
47884
+ this.scheduleReconnect();
47885
+ }
47886
+ scheduleReconnect(replacement = false) {
47887
+ if (this.isClosed || this.reconnectTimer) {
47888
+ return;
47889
+ }
47890
+ this.reconnectTimer = setTimeout(async () => {
47891
+ this.reconnectTimer = undefined;
47892
+ if (this.isClosed || this.isConnecting || !replacement && this.activeSocket) {
47893
+ return;
47894
+ }
47895
+ this.isConnecting = true;
47896
+ try {
47897
+ const { access_token, team_id } = await this.getOAuthToken();
47898
+ await this.openSocket(access_token, team_id, replacement ? "replacement" : "active");
47899
+ } catch (err) {
47900
+ debug5("Reconnect failed: %O", err);
47901
+ this.isConnecting = false;
47902
+ this.notifyErrorListeners(err);
47903
+ this.scheduleReconnect(replacement);
47904
+ }
47905
+ }, this.reconnectDelay);
47906
+ }
47907
+ scheduleDrainReconnect(retryAfterMs) {
47908
+ if (this.isClosed || this.reconnectTimer) {
47909
+ return;
47910
+ }
47911
+ let safeDelayMs = this.reconnectDelay;
47912
+ if (typeof retryAfterMs === "number" && Number.isFinite(retryAfterMs) && retryAfterMs >= 0 && retryAfterMs <= _Realtime2.MAX_RECONNECT_DELAY_MS) {
47913
+ safeDelayMs = Math.trunc(retryAfterMs);
47914
+ }
47915
+ this.reconnectTimer = setTimeout(async () => {
47916
+ this.reconnectTimer = undefined;
47917
+ if (this.isClosed || this.isConnecting) {
47918
+ return;
47919
+ }
47920
+ this.isConnecting = true;
47921
+ try {
47922
+ const { access_token, team_id } = await this.getOAuthToken();
47923
+ await this.openSocket(access_token, team_id, "replacement");
47924
+ } catch (err) {
47925
+ debug5("Reconnect failed: %O", err);
47926
+ this.isConnecting = false;
47927
+ this.notifyErrorListeners(err);
47928
+ this.scheduleReconnect(true);
47929
+ }
47930
+ }, safeDelayMs);
47931
+ }
47932
+ normalizeReconnectDelay(delay) {
47933
+ if (typeof delay !== "number" || !Number.isFinite(delay)) {
47934
+ return _Realtime2.DEFAULT_RECONNECT_DELAY_MS;
47760
47935
  }
47936
+ return Math.min(Math.max(0, Math.trunc(delay)), _Realtime2.MAX_RECONNECT_DELAY_MS);
47937
+ }
47938
+ isDuplicateEvent(eventId) {
47939
+ if (!eventId) {
47940
+ return false;
47941
+ }
47942
+ if (this.recentEventIds.has(eventId)) {
47943
+ return true;
47944
+ }
47945
+ this.recentEventIds.add(eventId);
47946
+ this.recentEventIdQueue.push(eventId);
47947
+ if (this.recentEventIdQueue.length > this.maxRecentEventIds) {
47948
+ const expiredId = this.recentEventIdQueue.shift();
47949
+ if (expiredId) {
47950
+ this.recentEventIds.delete(expiredId);
47951
+ }
47952
+ }
47953
+ return false;
47761
47954
  }
47762
47955
  handleHeartbeat() {
47763
47956
  debug5("Heartbeat received");
@@ -47785,22 +47978,24 @@ var import_debug, __require2, BASE_PATH, BaseAPI = class {
47785
47978
  this.errorListeners.forEach((listener) => {
47786
47979
  try {
47787
47980
  listener(error48);
47788
- } catch (error210) {
47789
- debug5("Error in error listener: %O", error210);
47981
+ } catch (listenerError) {
47982
+ debug5("Error in error listener: %O", listenerError);
47790
47983
  }
47791
47984
  });
47792
47985
  }
47793
47986
  startHeartbeatCheck() {
47987
+ this.stopHeartbeatCheck();
47794
47988
  this.missedHeartbeatCheckTimer = setInterval(() => {
47795
- if (Date.now() - this.lastHeartbeat > this.missedHeartbeatsLimit) {
47989
+ if (this.activeSocket && Date.now() - this.lastHeartbeat > this.missedHeartbeatsLimit) {
47796
47990
  debug5("No heartbeat received in 30 seconds! Closing connection.");
47797
- this.socket?.close();
47991
+ this.activeSocket.close();
47798
47992
  }
47799
47993
  }, this.heartbeatInterval);
47800
47994
  }
47801
47995
  stopHeartbeatCheck() {
47802
47996
  if (this.missedHeartbeatCheckTimer) {
47803
47997
  clearInterval(this.missedHeartbeatCheckTimer);
47998
+ this.missedHeartbeatCheckTimer = undefined;
47804
47999
  }
47805
48000
  }
47806
48001
  onEvent(listener) {
@@ -47811,6 +48006,9 @@ var import_debug, __require2, BASE_PATH, BaseAPI = class {
47811
48006
  }
47812
48007
  onConnection(listener) {
47813
48008
  this.connectionListeners.add(listener);
48009
+ if (this.isConnected()) {
48010
+ listener(true);
48011
+ }
47814
48012
  return () => {
47815
48013
  this.connectionListeners.delete(listener);
47816
48014
  };
@@ -47822,19 +48020,27 @@ var import_debug, __require2, BASE_PATH, BaseAPI = class {
47822
48020
  };
47823
48021
  }
47824
48022
  close() {
47825
- if (this.socket) {
47826
- this.stopHeartbeatCheck();
47827
- this.socket.close();
47828
- this.socket = undefined;
47829
- }
48023
+ this.isClosed = true;
48024
+ if (this.reconnectTimer) {
48025
+ clearTimeout(this.reconnectTimer);
48026
+ this.reconnectTimer = undefined;
48027
+ }
48028
+ this.stopHeartbeatCheck();
48029
+ this.activeSocket?.close();
48030
+ this.activeSocket = undefined;
48031
+ this.drainingSockets.forEach((socket) => {
48032
+ socket.close();
48033
+ });
48034
+ this.drainingSockets.clear();
48035
+ this.isConnecting = false;
47830
48036
  this.eventListeners.clear();
47831
48037
  this.connectionListeners.clear();
47832
48038
  this.errorListeners.clear();
47833
48039
  }
47834
48040
  isConnected() {
47835
- return this.socket?.readyState === WebSocket.OPEN;
48041
+ return this.activeSocket?.readyState === WebSocket.OPEN;
47836
48042
  }
47837
- }, UserService = class {
48043
+ }, Realtime, UserService = class {
47838
48044
  constructor(client) {
47839
48045
  this.client = client;
47840
48046
  }
@@ -48915,6 +49121,9 @@ var init_dist2 = __esm(() => {
48915
49121
  if (typeof WebSocket === "undefined") {
48916
49122
  global.WebSocket = __require2("ws");
48917
49123
  }
49124
+ _Realtime.DEFAULT_RECONNECT_DELAY_MS = 5000;
49125
+ _Realtime.MAX_RECONNECT_DELAY_MS = 60000;
49126
+ Realtime = _Realtime;
48918
49127
  DEFAULT_POLLING_OPTIONS = {
48919
49128
  pollIntervalMs: 1e4,
48920
49129
  timeoutMs: 5 * 60 * 1000
@@ -48958,13 +49167,37 @@ function createKadoaClient(auth) {
48958
49167
  });
48959
49168
  return client;
48960
49169
  }
48961
- var ctxRefreshMutex;
49170
+ var refreshRawMutex, ctxRefreshMutex;
48962
49171
  var init_client = __esm(() => {
48963
49172
  init_dist2();
49173
+ refreshRawMutex = new Map;
48964
49174
  ctxRefreshMutex = new WeakMap;
48965
49175
  });
48966
49176
 
48967
49177
  // src/client.ts
49178
+ var exports_client = {};
49179
+ __export(exports_client, {
49180
+ refreshSupabaseJwtRaw: () => refreshSupabaseJwtRaw,
49181
+ refreshSupabaseJwt: () => refreshSupabaseJwt,
49182
+ isJwtExpired: () => isJwtExpired,
49183
+ getValidJwt: () => getValidJwt,
49184
+ decodeJwtClaims: () => decodeJwtClaims,
49185
+ createKadoaClient: () => createKadoaClient2,
49186
+ SessionExpiredError: () => SessionExpiredError,
49187
+ KadoaSdkException: () => KadoaSdkException,
49188
+ KadoaClient: () => KadoaClient
49189
+ });
49190
+ function createKadoaClient2(auth) {
49191
+ const client = new KadoaClient({ bearerToken: auth.jwt });
49192
+ client.axiosInstance.interceptors.request.use((config2) => {
49193
+ config2.headers["x-kadoa-source"] = "mcp";
49194
+ if (auth.teamId) {
49195
+ config2.headers["x-team-id"] = auth.teamId;
49196
+ }
49197
+ return config2;
49198
+ });
49199
+ return client;
49200
+ }
48968
49201
  function decodeJwtClaims(jwt2) {
48969
49202
  try {
48970
49203
  const payload = JSON.parse(Buffer.from(jwt2.split(".")[1], "base64url").toString());
@@ -48986,36 +49219,57 @@ function isJwtExpired(jwt2) {
48986
49219
  return true;
48987
49220
  }
48988
49221
  }
48989
- async function refreshSupabaseJwt(ctx) {
48990
- if (!ctx.supabaseRefreshToken) {
48991
- console.error("[JWT_REFRESH] No refresh token available, cannot refresh");
48992
- return;
49222
+ async function refreshSupabaseJwtRaw(supabaseRefreshToken) {
49223
+ const inflight = refreshRawMutex2.get(supabaseRefreshToken);
49224
+ if (inflight) {
49225
+ console.error(`[JWT_REFRESH] DEDUP: reusing in-flight raw refresh`);
49226
+ return inflight;
48993
49227
  }
49228
+ const promise3 = _doRefreshRaw(supabaseRefreshToken).finally(() => {
49229
+ refreshRawMutex2.delete(supabaseRefreshToken);
49230
+ });
49231
+ refreshRawMutex2.set(supabaseRefreshToken, promise3);
49232
+ return promise3;
49233
+ }
49234
+ async function _doRefreshRaw(supabaseRefreshToken) {
48994
49235
  const supabaseUrl = process.env.SUPABASE_URL;
48995
49236
  if (!supabaseUrl) {
48996
49237
  console.error("[JWT_REFRESH] SUPABASE_URL not set, cannot refresh");
49238
+ return null;
49239
+ }
49240
+ const res = await fetch(`${supabaseUrl}/auth/v1/token?grant_type=refresh_token`, {
49241
+ method: "POST",
49242
+ headers: {
49243
+ "Content-Type": "application/json",
49244
+ apikey: process.env.SUPABASE_ANON_KEY
49245
+ },
49246
+ body: JSON.stringify({ refresh_token: supabaseRefreshToken })
49247
+ });
49248
+ if (res.ok) {
49249
+ const data = await res.json();
49250
+ return { jwt: data.access_token, refreshToken: data.refresh_token };
49251
+ }
49252
+ const body = await res.text().catch(() => "");
49253
+ console.error(`[JWT_REFRESH] FAIL: Supabase returned ${res.status} (refreshToken=${supabaseRefreshToken.slice(0, 12)}...): ${body}`);
49254
+ if (body.includes("session_expired") || body.includes("refresh_token_not_found") || body.includes("refresh_token_already_used")) {
49255
+ throw new SessionExpiredError("Your Kadoa session has expired due to inactivity. Please reconnect to re-authenticate.");
49256
+ }
49257
+ return null;
49258
+ }
49259
+ async function refreshSupabaseJwt(ctx) {
49260
+ if (!ctx.supabaseRefreshToken) {
49261
+ console.error("[JWT_REFRESH] No refresh token available, cannot refresh");
48997
49262
  return;
48998
49263
  }
48999
49264
  try {
49000
49265
  const refreshToken = ctx.supabaseRefreshToken;
49001
49266
  console.error(`[JWT_REFRESH] Refreshing Supabase JWT (refreshToken=${refreshToken.slice(0, 12)}..., team=${ctx.teamId ?? "unknown"})`);
49002
- const res = await fetch(`${supabaseUrl}/auth/v1/token?grant_type=refresh_token`, {
49003
- method: "POST",
49004
- headers: {
49005
- "Content-Type": "application/json",
49006
- apikey: process.env.SUPABASE_ANON_KEY
49007
- },
49008
- body: JSON.stringify({ refresh_token: refreshToken })
49009
- });
49010
- if (!res.ok) {
49011
- const body = await res.text().catch(() => "");
49012
- console.error(`[JWT_REFRESH] FAIL: Supabase returned ${res.status} (refreshToken=${refreshToken.slice(0, 12)}...): ${body}`);
49267
+ const result = await refreshSupabaseJwtRaw(refreshToken);
49268
+ if (!result)
49013
49269
  return;
49014
- }
49015
- const data = await res.json();
49016
- ctx.supabaseJwt = data.access_token;
49017
- ctx.supabaseRefreshToken = data.refresh_token;
49018
- ctx.client.setBearerToken(data.access_token);
49270
+ ctx.supabaseJwt = result.jwt;
49271
+ ctx.supabaseRefreshToken = result.refreshToken;
49272
+ ctx.client.setBearerToken(result.jwt);
49019
49273
  try {
49020
49274
  await ctx.persist?.({
49021
49275
  supabaseJwt: ctx.supabaseJwt,
@@ -49025,9 +49279,11 @@ async function refreshSupabaseJwt(ctx) {
49025
49279
  } catch (e) {
49026
49280
  console.error("[JWT_REFRESH] WARN: persist failed, tokens updated in-memory only:", e);
49027
49281
  }
49028
- console.error(`[JWT_REFRESH] OK: token refreshed (team=${ctx.teamId ?? "unknown"}, newRefreshToken=${data.refresh_token.slice(0, 12)}...)`);
49029
- return data.access_token;
49282
+ console.error(`[JWT_REFRESH] OK: token refreshed (team=${ctx.teamId ?? "unknown"}, newRefreshToken=${result.refreshToken.slice(0, 12)}...)`);
49283
+ return result.jwt;
49030
49284
  } catch (error48) {
49285
+ if (error48 instanceof SessionExpiredError)
49286
+ throw error48;
49031
49287
  console.error("[JWT_REFRESH] FAIL: threw", error48);
49032
49288
  return;
49033
49289
  }
@@ -49048,9 +49304,16 @@ async function getValidJwt(ctx) {
49048
49304
  ctxRefreshMutex2.set(ctx, promise3);
49049
49305
  return promise3;
49050
49306
  }
49051
- var ctxRefreshMutex2;
49307
+ var SessionExpiredError, refreshRawMutex2, ctxRefreshMutex2;
49052
49308
  var init_client2 = __esm(() => {
49053
49309
  init_dist2();
49310
+ SessionExpiredError = class SessionExpiredError extends Error {
49311
+ constructor(message) {
49312
+ super(message ?? "Supabase session expired. Please re-authenticate.");
49313
+ this.name = "SessionExpiredError";
49314
+ }
49315
+ };
49316
+ refreshRawMutex2 = new Map;
49054
49317
  ctxRefreshMutex2 = new WeakMap;
49055
49318
  });
49056
49319
 
@@ -49168,6 +49431,10 @@ function registerTools(server, ctx) {
49168
49431
  }
49169
49432
  return await handler(...args);
49170
49433
  } catch (error48) {
49434
+ if (error48 instanceof SessionExpiredError) {
49435
+ console.error(`[Tool Error] ${name}: session expired, user must re-authenticate`);
49436
+ return errorResult("Your session has expired. Please reconnect the MCP server to re-authenticate.");
49437
+ }
49171
49438
  let message = classifyError(error48);
49172
49439
  if (KadoaHttpException.isInstance(error48) && error48.httpStatus === 403) {
49173
49440
  try {
@@ -53892,45 +54159,6 @@ function generatePKCE() {
53892
54159
  const challenge = createHash2("sha256").update(verifier).digest("base64url");
53893
54160
  return { verifier, challenge };
53894
54161
  }
53895
- async function refreshSupabaseToken(supabaseRefreshToken, context) {
53896
- const inflight = supabaseRefreshMutex.get(supabaseRefreshToken);
53897
- if (inflight) {
53898
- console.error(`[AUTH] REFRESH_DEDUP: reusing in-flight refresh (${context})`);
53899
- return inflight;
53900
- }
53901
- const promise3 = (async () => {
53902
- const supabaseUrl = process.env.SUPABASE_URL;
53903
- if (!supabaseUrl || !supabaseRefreshToken)
53904
- return null;
53905
- try {
53906
- const res = await fetch(`${supabaseUrl}/auth/v1/token?grant_type=refresh_token`, {
53907
- method: "POST",
53908
- headers: {
53909
- "Content-Type": "application/json",
53910
- apikey: process.env.SUPABASE_ANON_KEY
53911
- },
53912
- body: JSON.stringify({ refresh_token: supabaseRefreshToken })
53913
- });
53914
- if (res.ok) {
53915
- const data = await res.json();
53916
- const newClaims = jwtClaims(data.access_token);
53917
- console.log(`[AUTH] REFRESH_OK: Supabase JWT refreshed (${context}, newEmail=${newClaims.email})`);
53918
- return { jwt: data.access_token, refreshToken: data.refresh_token };
53919
- }
53920
- const body = await res.text().catch(() => "");
53921
- console.error(`[AUTH] REFRESH_FAIL: Supabase returned ${res.status} (${context}): ${body.slice(0, 200)}`);
53922
- return null;
53923
- } catch (err) {
53924
- console.error(`[AUTH] REFRESH_FAIL: Supabase refresh threw (${context}):`, err);
53925
- return null;
53926
- }
53927
- })();
53928
- supabaseRefreshMutex.set(supabaseRefreshToken, promise3);
53929
- promise3.finally(() => {
53930
- supabaseRefreshMutex.delete(supabaseRefreshToken);
53931
- });
53932
- return promise3;
53933
- }
53934
54162
  function jwtClaims(jwt2) {
53935
54163
  try {
53936
54164
  const payload = JSON.parse(Buffer.from(jwt2.split(".")[1], "base64url").toString());
@@ -54238,12 +54466,22 @@ class KadoaOAuthProvider {
54238
54466
  let { supabaseJwt, supabaseRefreshToken } = entry;
54239
54467
  const claims = jwtClaims(entry.supabaseJwt);
54240
54468
  const context = `email=${claims.email}, team=${entry.teamId}`;
54241
- const refreshed = await refreshSupabaseToken(supabaseRefreshToken, context);
54242
- if (refreshed) {
54243
- supabaseJwt = refreshed.jwt;
54244
- supabaseRefreshToken = refreshed.refreshToken;
54245
- } else {
54246
- console.error(`[AUTH] REFRESH_WARN: using stale Supabase JWT as fallback (${context})`);
54469
+ try {
54470
+ const { refreshSupabaseJwtRaw: refreshSupabaseJwtRaw2, SessionExpiredError: SessionExpiredError2 } = await Promise.resolve().then(() => (init_client2(), exports_client));
54471
+ const refreshed = await refreshSupabaseJwtRaw2(supabaseRefreshToken);
54472
+ if (refreshed) {
54473
+ supabaseJwt = refreshed.jwt;
54474
+ supabaseRefreshToken = refreshed.refreshToken;
54475
+ console.error(`[AUTH] REFRESH_OK: Supabase JWT refreshed (${context})`);
54476
+ } else {
54477
+ console.error(`[AUTH] REFRESH_WARN: using stale Supabase JWT as fallback (${context})`);
54478
+ }
54479
+ } catch (error48) {
54480
+ if (error48 instanceof Error && error48.name === "SessionExpiredError") {
54481
+ console.error(`[AUTH] REFRESH_DEAD: session permanently expired (${context}): ${error48.message}`);
54482
+ throw new InvalidTokenError("Supabase session expired. Please re-authenticate.");
54483
+ }
54484
+ console.error(`[AUTH] REFRESH_WARN: unexpected error, using stale JWT (${context}):`, error48);
54247
54485
  }
54248
54486
  const freshClaims = jwtClaims(supabaseJwt);
54249
54487
  const teamId = freshClaims.activeTeamId ?? entry.teamId;
@@ -54370,7 +54608,7 @@ class KadoaOAuthProvider {
54370
54608
  codeChallenge: pending.params.codeChallenge,
54371
54609
  clientId: pending.client.client_id,
54372
54610
  redirectUri: pending.params.redirectUri,
54373
- expiresAt: Date.now() + 10 * 60 * 1000
54611
+ expiresAt: Date.now() + 600000
54374
54612
  }, 600);
54375
54613
  const redirectUrl = new URL(pending.params.redirectUri);
54376
54614
  redirectUrl.searchParams.set("code", mcpCode);
@@ -54799,12 +55037,11 @@ function renderLoginPage(state, error48) {
54799
55037
  function escapeHtml(str) {
54800
55038
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
54801
55039
  }
54802
- var TEAM_SELECTION_TTL, ACCESS_TOKEN_TTL, supabaseRefreshMutex;
55040
+ var TEAM_SELECTION_TTL, ACCESS_TOKEN_TTL;
54803
55041
  var init_auth2 = __esm(() => {
54804
55042
  init_errors4();
54805
55043
  TEAM_SELECTION_TTL = 10 * 60 * 1000;
54806
55044
  ACCESS_TOKEN_TTL = 7 * 24 * 3600;
54807
- supabaseRefreshMutex = new Map;
54808
55045
  });
54809
55046
 
54810
55047
  // src/redis-store.ts
@@ -54997,6 +55234,39 @@ async function startHttpServer(options) {
54997
55234
  return;
54998
55235
  }
54999
55236
  const identity = `jwt:${auth.userId.slice(0, 8)}...:team=${auth.teamId.slice(0, 8)}...`;
55237
+ if (isJwtExpired(auth.jwt)) {
55238
+ try {
55239
+ const refreshed = await refreshSupabaseJwtRaw(auth.refreshToken);
55240
+ if (refreshed) {
55241
+ auth.jwt = refreshed.jwt;
55242
+ auth.refreshToken = refreshed.refreshToken;
55243
+ const entry = await store.get("access_tokens", auth.mcpToken);
55244
+ if (entry) {
55245
+ const remainingMs = entry.expiresAt - Date.now();
55246
+ if (remainingMs > 0) {
55247
+ await store.set("access_tokens", auth.mcpToken, {
55248
+ ...entry,
55249
+ supabaseJwt: refreshed.jwt,
55250
+ supabaseRefreshToken: refreshed.refreshToken
55251
+ }, Math.ceil(remainingMs / 1000));
55252
+ console.error(`[PROACTIVE_REFRESH] OK: JWT refreshed on ${method} (${identity})`);
55253
+ }
55254
+ }
55255
+ }
55256
+ } catch (error48) {
55257
+ if (error48 instanceof SessionExpiredError) {
55258
+ console.error(`[PROACTIVE_REFRESH] Session dead on ${method} (${identity}): ${error48.message}`);
55259
+ await store.del("access_tokens", auth.mcpToken);
55260
+ res.status(401).json({
55261
+ jsonrpc: "2.0",
55262
+ error: { code: -32001, message: error48.message },
55263
+ id: req.body?.id ?? null
55264
+ });
55265
+ return;
55266
+ }
55267
+ console.error(`[PROACTIVE_REFRESH] WARN: refresh failed on ${method}, continuing with stale JWT`, error48);
55268
+ }
55269
+ }
55000
55270
  try {
55001
55271
  console.error(`[MCP] POST method=${method} auth=${identity}`);
55002
55272
  const transport = new StreamableHTTPServerTransport({
@@ -55074,6 +55344,7 @@ var init_http2 = __esm(async () => {
55074
55344
  init_bearerAuth();
55075
55345
  init_auth2();
55076
55346
  init_redis_store();
55347
+ init_client2();
55077
55348
  await init_src();
55078
55349
  });
55079
55350
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kadoa/mcp",
3
- "version": "0.3.8",
3
+ "version": "0.3.9-rc.2",
4
4
  "description": "Kadoa MCP Server — manage workflows from Claude Desktop, Cursor, and other MCP clients",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -24,7 +24,7 @@
24
24
  "prepublishOnly": "bun run check-types && bun run test:unit && bun run build"
25
25
  },
26
26
  "dependencies": {
27
- "@kadoa/node-sdk": "^0.23.0",
27
+ "@kadoa/node-sdk": "^0.24.2",
28
28
  "@modelcontextprotocol/sdk": "^1.26.0",
29
29
  "express": "^5.2.1",
30
30
  "ioredis": "^5.6.1",