@shortcut/mcp 0.1.5 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +52 -3
  2. package/dist/index.js +573 -550
  3. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -9589,26 +9589,26 @@ var require_get_intrinsic = __commonJS((exports, module) => {
9589
9589
  }
9590
9590
  }
9591
9591
  var errorProto;
9592
- var doEval = function doEval(name2) {
9592
+ var doEval = function doEval(name) {
9593
9593
  var value;
9594
- if (name2 === "%AsyncFunction%") {
9594
+ if (name === "%AsyncFunction%") {
9595
9595
  value = getEvalledConstructor("async function () {}");
9596
- } else if (name2 === "%GeneratorFunction%") {
9596
+ } else if (name === "%GeneratorFunction%") {
9597
9597
  value = getEvalledConstructor("function* () {}");
9598
- } else if (name2 === "%AsyncGeneratorFunction%") {
9598
+ } else if (name === "%AsyncGeneratorFunction%") {
9599
9599
  value = getEvalledConstructor("async function* () {}");
9600
- } else if (name2 === "%AsyncGenerator%") {
9600
+ } else if (name === "%AsyncGenerator%") {
9601
9601
  var fn = doEval("%AsyncGeneratorFunction%");
9602
9602
  if (fn) {
9603
9603
  value = fn.prototype;
9604
9604
  }
9605
- } else if (name2 === "%AsyncIteratorPrototype%") {
9605
+ } else if (name === "%AsyncIteratorPrototype%") {
9606
9606
  var gen = doEval("%AsyncGenerator%");
9607
9607
  if (gen && getProto) {
9608
9608
  value = getProto(gen.prototype);
9609
9609
  }
9610
9610
  }
9611
- INTRINSICS[name2] = value;
9611
+ INTRINSICS[name] = value;
9612
9612
  return value;
9613
9613
  };
9614
9614
  var LEGACY_ALIASES = {
@@ -9688,8 +9688,8 @@ var require_get_intrinsic = __commonJS((exports, module) => {
9688
9688
  });
9689
9689
  return result;
9690
9690
  };
9691
- var getBaseIntrinsic = function getBaseIntrinsic(name2, allowMissing) {
9692
- var intrinsicName = name2;
9691
+ var getBaseIntrinsic = function getBaseIntrinsic(name, allowMissing) {
9692
+ var intrinsicName = name;
9693
9693
  var alias;
9694
9694
  if (hasOwn(LEGACY_ALIASES, intrinsicName)) {
9695
9695
  alias = LEGACY_ALIASES[intrinsicName];
@@ -9701,7 +9701,7 @@ var require_get_intrinsic = __commonJS((exports, module) => {
9701
9701
  value = doEval(intrinsicName);
9702
9702
  }
9703
9703
  if (typeof value === "undefined" && !allowMissing) {
9704
- throw new $TypeError("intrinsic " + name2 + " exists, but is not available. Please file an issue!");
9704
+ throw new $TypeError("intrinsic " + name + " exists, but is not available. Please file an issue!");
9705
9705
  }
9706
9706
  return {
9707
9707
  alias,
@@ -9709,19 +9709,19 @@ var require_get_intrinsic = __commonJS((exports, module) => {
9709
9709
  value
9710
9710
  };
9711
9711
  }
9712
- throw new $SyntaxError("intrinsic " + name2 + " does not exist!");
9712
+ throw new $SyntaxError("intrinsic " + name + " does not exist!");
9713
9713
  };
9714
- module.exports = function GetIntrinsic(name2, allowMissing) {
9715
- if (typeof name2 !== "string" || name2.length === 0) {
9714
+ module.exports = function GetIntrinsic(name, allowMissing) {
9715
+ if (typeof name !== "string" || name.length === 0) {
9716
9716
  throw new $TypeError("intrinsic name must be a non-empty string");
9717
9717
  }
9718
9718
  if (arguments.length > 1 && typeof allowMissing !== "boolean") {
9719
9719
  throw new $TypeError('"allowMissing" argument must be a boolean');
9720
9720
  }
9721
- if ($exec(/^%?[^%]*%?$/, name2) === null) {
9721
+ if ($exec(/^%?[^%]*%?$/, name) === null) {
9722
9722
  throw new $SyntaxError("`%` may not be present anywhere but at the beginning and end of the intrinsic name");
9723
9723
  }
9724
- var parts = stringToPath(name2);
9724
+ var parts = stringToPath(name);
9725
9725
  var intrinsicBaseName = parts.length > 0 ? parts[0] : "";
9726
9726
  var intrinsic = getBaseIntrinsic("%" + intrinsicBaseName + "%", allowMissing);
9727
9727
  var intrinsicRealName = intrinsic.name;
@@ -9749,7 +9749,7 @@ var require_get_intrinsic = __commonJS((exports, module) => {
9749
9749
  } else if (value != null) {
9750
9750
  if (!(part in value)) {
9751
9751
  if (!allowMissing) {
9752
- throw new $TypeError("base intrinsic for " + name2 + " exists, but the property is not available.");
9752
+ throw new $TypeError("base intrinsic for " + name + " exists, but the property is not available.");
9753
9753
  }
9754
9754
  return;
9755
9755
  }
@@ -10306,9 +10306,9 @@ var require_ms = __commonJS((exports, module) => {
10306
10306
  }
10307
10307
  return ms + " ms";
10308
10308
  }
10309
- function plural(ms, msAbs, n, name2) {
10309
+ function plural(ms, msAbs, n, name) {
10310
10310
  var isPlural = msAbs >= n * 1.5;
10311
- return Math.round(ms / n) + " " + name2 + (isPlural ? "s" : "");
10311
+ return Math.round(ms / n) + " " + name + (isPlural ? "s" : "");
10312
10312
  }
10313
10313
  });
10314
10314
 
@@ -10437,19 +10437,19 @@ var require_common = __commonJS((exports, module) => {
10437
10437
  createDebug.enable("");
10438
10438
  return namespaces;
10439
10439
  }
10440
- function enabled(name2) {
10441
- if (name2[name2.length - 1] === "*") {
10440
+ function enabled(name) {
10441
+ if (name[name.length - 1] === "*") {
10442
10442
  return true;
10443
10443
  }
10444
10444
  let i;
10445
10445
  let len;
10446
10446
  for (i = 0, len = createDebug.skips.length;i < len; i++) {
10447
- if (createDebug.skips[i].test(name2)) {
10447
+ if (createDebug.skips[i].test(name)) {
10448
10448
  return false;
10449
10449
  }
10450
10450
  }
10451
10451
  for (i = 0, len = createDebug.names.length;i < len; i++) {
10452
- if (createDebug.names[i].test(name2)) {
10452
+ if (createDebug.names[i].test(name)) {
10453
10453
  return true;
10454
10454
  }
10455
10455
  }
@@ -10751,17 +10751,17 @@ var require_node = __commonJS((exports, module) => {
10751
10751
  return "colors" in exports.inspectOpts ? Boolean(exports.inspectOpts.colors) : tty.isatty(process.stderr.fd);
10752
10752
  }
10753
10753
  function formatArgs(args) {
10754
- const { namespace: name2, useColors: useColors2 } = this;
10754
+ const { namespace: name, useColors: useColors2 } = this;
10755
10755
  if (useColors2) {
10756
10756
  const c = this.color;
10757
10757
  const colorCode = "\x1B[3" + (c < 8 ? c : "8;5;" + c);
10758
- const prefix = ` ${colorCode};1m${name2} \x1B[0m`;
10758
+ const prefix = ` ${colorCode};1m${name} \x1B[0m`;
10759
10759
  args[0] = prefix + args[0].split(`
10760
10760
  `).join(`
10761
10761
  ` + prefix);
10762
10762
  args.push(colorCode + "m+" + module.exports.humanize(this.diff) + "\x1B[0m");
10763
10763
  } else {
10764
- args[0] = getDate() + name2 + " " + args[0];
10764
+ args[0] = getDate() + name + " " + args[0];
10765
10765
  }
10766
10766
  }
10767
10767
  function getDate() {
@@ -10959,13 +10959,13 @@ var require_follow_redirects = __commonJS((exports, module) => {
10959
10959
  this._ending = true;
10960
10960
  }
10961
10961
  };
10962
- RedirectableRequest.prototype.setHeader = function(name2, value) {
10963
- this._options.headers[name2] = value;
10964
- this._currentRequest.setHeader(name2, value);
10962
+ RedirectableRequest.prototype.setHeader = function(name, value) {
10963
+ this._options.headers[name] = value;
10964
+ this._currentRequest.setHeader(name, value);
10965
10965
  };
10966
- RedirectableRequest.prototype.removeHeader = function(name2) {
10967
- delete this._options.headers[name2];
10968
- this._currentRequest.removeHeader(name2);
10966
+ RedirectableRequest.prototype.removeHeader = function(name) {
10967
+ delete this._options.headers[name];
10968
+ this._currentRequest.removeHeader(name);
10969
10969
  };
10970
10970
  RedirectableRequest.prototype.setTimeout = function(msecs, callback) {
10971
10971
  var self2 = this;
@@ -11542,20 +11542,20 @@ var require_axios = __commonJS((exports, module) => {
11542
11542
  var reduceDescriptors = (obj, reducer) => {
11543
11543
  const descriptors2 = Object.getOwnPropertyDescriptors(obj);
11544
11544
  const reducedDescriptors = {};
11545
- forEach(descriptors2, (descriptor, name2) => {
11545
+ forEach(descriptors2, (descriptor, name) => {
11546
11546
  let ret;
11547
- if ((ret = reducer(descriptor, name2, obj)) !== false) {
11548
- reducedDescriptors[name2] = ret || descriptor;
11547
+ if ((ret = reducer(descriptor, name, obj)) !== false) {
11548
+ reducedDescriptors[name] = ret || descriptor;
11549
11549
  }
11550
11550
  });
11551
11551
  Object.defineProperties(obj, reducedDescriptors);
11552
11552
  };
11553
11553
  var freezeMethods = (obj) => {
11554
- reduceDescriptors(obj, (descriptor, name2) => {
11555
- if (isFunction(obj) && ["arguments", "caller", "callee"].indexOf(name2) !== -1) {
11554
+ reduceDescriptors(obj, (descriptor, name) => {
11555
+ if (isFunction(obj) && ["arguments", "caller", "callee"].indexOf(name) !== -1) {
11556
11556
  return false;
11557
11557
  }
11558
- const value = obj[name2];
11558
+ const value = obj[name];
11559
11559
  if (!isFunction(value))
11560
11560
  return;
11561
11561
  descriptor.enumerable = false;
@@ -11565,7 +11565,7 @@ var require_axios = __commonJS((exports, module) => {
11565
11565
  }
11566
11566
  if (!descriptor.set) {
11567
11567
  descriptor.set = () => {
11568
- throw Error("Can not rewrite read-only method '" + name2 + "'");
11568
+ throw Error("Can not rewrite read-only method '" + name + "'");
11569
11569
  };
11570
11570
  }
11571
11571
  });
@@ -11873,8 +11873,8 @@ var require_axios = __commonJS((exports, module) => {
11873
11873
  params && toFormData(params, this, options);
11874
11874
  }
11875
11875
  var prototype = AxiosURLSearchParams.prototype;
11876
- prototype.append = function append(name2, value) {
11877
- this._pairs.push([name2, value]);
11876
+ prototype.append = function append(name, value) {
11877
+ this._pairs.push([name, value]);
11878
11878
  };
11879
11879
  prototype.toString = function toString(encoder) {
11880
11880
  const _encode = encoder ? function(value) {
@@ -12010,8 +12010,8 @@ var require_axios = __commonJS((exports, module) => {
12010
12010
  }
12011
12011
  }, options));
12012
12012
  }
12013
- function parsePropPath(name2) {
12014
- return utils$1.matchAll(/\w+|\[(\w*)]/g, name2).map((match) => {
12013
+ function parsePropPath(name) {
12014
+ return utils$1.matchAll(/\w+|\[(\w*)]/g, name).map((match) => {
12015
12015
  return match[0] === "[]" ? "" : match[1] || match[0];
12016
12016
  });
12017
12017
  }
@@ -12029,33 +12029,33 @@ var require_axios = __commonJS((exports, module) => {
12029
12029
  }
12030
12030
  function formDataToJSON(formData) {
12031
12031
  function buildPath(path, value, target, index) {
12032
- let name2 = path[index++];
12033
- if (name2 === "__proto__")
12032
+ let name = path[index++];
12033
+ if (name === "__proto__")
12034
12034
  return true;
12035
- const isNumericKey = Number.isFinite(+name2);
12035
+ const isNumericKey = Number.isFinite(+name);
12036
12036
  const isLast = index >= path.length;
12037
- name2 = !name2 && utils$1.isArray(target) ? target.length : name2;
12037
+ name = !name && utils$1.isArray(target) ? target.length : name;
12038
12038
  if (isLast) {
12039
- if (utils$1.hasOwnProp(target, name2)) {
12040
- target[name2] = [target[name2], value];
12039
+ if (utils$1.hasOwnProp(target, name)) {
12040
+ target[name] = [target[name], value];
12041
12041
  } else {
12042
- target[name2] = value;
12042
+ target[name] = value;
12043
12043
  }
12044
12044
  return !isNumericKey;
12045
12045
  }
12046
- if (!target[name2] || !utils$1.isObject(target[name2])) {
12047
- target[name2] = [];
12046
+ if (!target[name] || !utils$1.isObject(target[name])) {
12047
+ target[name] = [];
12048
12048
  }
12049
- const result = buildPath(path, value, target[name2], index);
12050
- if (result && utils$1.isArray(target[name2])) {
12051
- target[name2] = arrayToObject(target[name2]);
12049
+ const result = buildPath(path, value, target[name], index);
12050
+ if (result && utils$1.isArray(target[name])) {
12051
+ target[name] = arrayToObject(target[name]);
12052
12052
  }
12053
12053
  return !isNumericKey;
12054
12054
  }
12055
12055
  if (utils$1.isFormData(formData) && utils$1.isFunction(formData.entries)) {
12056
12056
  const obj = {};
12057
- utils$1.forEachEntry(formData, (name2, value) => {
12058
- buildPath(parsePropPath(name2), value, obj, 0);
12057
+ utils$1.forEachEntry(formData, (name, value) => {
12058
+ buildPath(parsePropPath(name), value, obj, 0);
12059
12059
  });
12060
12060
  return obj;
12061
12061
  }
@@ -12634,10 +12634,10 @@ var require_axios = __commonJS((exports, module) => {
12634
12634
  var CRLF_BYTES_COUNT = 2;
12635
12635
 
12636
12636
  class FormDataPart {
12637
- constructor(name2, value) {
12637
+ constructor(name, value) {
12638
12638
  const { escapeName } = this.constructor;
12639
12639
  const isStringValue = utils$1.isString(value);
12640
- let headers = `Content-Disposition: form-data; name="${escapeName(name2)}"${!isStringValue && value.name ? `; filename="${escapeName(value.name)}"` : ""}${CRLF}`;
12640
+ let headers = `Content-Disposition: form-data; name="${escapeName(name)}"${!isStringValue && value.name ? `; filename="${escapeName(value.name)}"` : ""}${CRLF}`;
12641
12641
  if (isStringValue) {
12642
12642
  value = textEncoder.encode(String(value).replace(/\r?\n|\r\n?/g, CRLF));
12643
12643
  } else {
@@ -12646,7 +12646,7 @@ var require_axios = __commonJS((exports, module) => {
12646
12646
  this.headers = textEncoder.encode(headers + CRLF);
12647
12647
  this.contentLength = isStringValue ? value.byteLength : value.size;
12648
12648
  this.size = this.headers.byteLength + this.contentLength + CRLF_BYTES_COUNT;
12649
- this.name = name2;
12649
+ this.name = name;
12650
12650
  this.value = value;
12651
12651
  }
12652
12652
  async* encode() {
@@ -12659,8 +12659,8 @@ var require_axios = __commonJS((exports, module) => {
12659
12659
  }
12660
12660
  yield CRLF_BYTES;
12661
12661
  }
12662
- static escapeName(name2) {
12663
- return String(name2).replace(/[\r\n"]/g, (match) => ({
12662
+ static escapeName(name) {
12663
+ return String(name).replace(/[\r\n"]/g, (match) => ({
12664
12664
  "\r": "%0D",
12665
12665
  "\n": "%0A",
12666
12666
  '"': "%22"
@@ -12682,8 +12682,8 @@ var require_axios = __commonJS((exports, module) => {
12682
12682
  const boundaryBytes = textEncoder.encode("--" + boundary + CRLF);
12683
12683
  const footerBytes = textEncoder.encode("--" + boundary + "--" + CRLF + CRLF);
12684
12684
  let contentLength = footerBytes.byteLength;
12685
- const parts = Array.from(form.entries()).map(([name2, value]) => {
12686
- const part = new FormDataPart(name2, value);
12685
+ const parts = Array.from(form.entries()).map(([name, value]) => {
12686
+ const part = new FormDataPart(name, value);
12687
12687
  contentLength += part.size;
12688
12688
  return part;
12689
12689
  });
@@ -13291,20 +13291,20 @@ var require_axios = __commonJS((exports, module) => {
13291
13291
  return origin2.protocol === url2.protocol && origin2.host === url2.host && (isMSIE || origin2.port === url2.port);
13292
13292
  })(new URL(platform.origin), platform.navigator && /(msie|trident)/i.test(platform.navigator.userAgent)) : () => true;
13293
13293
  var cookies = platform.hasStandardBrowserEnv ? {
13294
- write(name2, value, expires, path, domain, secure) {
13295
- const cookie = [name2 + "=" + encodeURIComponent(value)];
13294
+ write(name, value, expires, path, domain, secure) {
13295
+ const cookie = [name + "=" + encodeURIComponent(value)];
13296
13296
  utils$1.isNumber(expires) && cookie.push("expires=" + new Date(expires).toGMTString());
13297
13297
  utils$1.isString(path) && cookie.push("path=" + path);
13298
13298
  utils$1.isString(domain) && cookie.push("domain=" + domain);
13299
13299
  secure === true && cookie.push("secure");
13300
13300
  document.cookie = cookie.join("; ");
13301
13301
  },
13302
- read(name2) {
13303
- const match = document.cookie.match(new RegExp("(^|;\\s*)(" + name2 + ")=([^;]*)"));
13302
+ read(name) {
13303
+ const match = document.cookie.match(new RegExp("(^|;\\s*)(" + name + ")=([^;]*)"));
13304
13304
  return match ? decodeURIComponent(match[3]) : null;
13305
13305
  },
13306
- remove(name2) {
13307
- this.write(name2, "", Date.now() - 86400000);
13306
+ remove(name) {
13307
+ this.write(name, "", Date.now() - 86400000);
13308
13308
  }
13309
13309
  } : {
13310
13310
  write() {},
@@ -13883,17 +13883,17 @@ var require_axios = __commonJS((exports, module) => {
13883
13883
  };
13884
13884
  });
13885
13885
  var deprecatedWarnings = {};
13886
- validators$1.transitional = function transitional(validator2, version2, message) {
13886
+ validators$1.transitional = function transitional(validator2, version, message) {
13887
13887
  function formatMessage(opt, desc) {
13888
13888
  return "[Axios v" + VERSION + "] Transitional option '" + opt + "'" + desc + (message ? ". " + message : "");
13889
13889
  }
13890
13890
  return (value, opt, opts) => {
13891
13891
  if (validator2 === false) {
13892
- throw new AxiosError(formatMessage(opt, " has been removed" + (version2 ? " in " + version2 : "")), AxiosError.ERR_DEPRECATED);
13892
+ throw new AxiosError(formatMessage(opt, " has been removed" + (version ? " in " + version : "")), AxiosError.ERR_DEPRECATED);
13893
13893
  }
13894
- if (version2 && !deprecatedWarnings[opt]) {
13894
+ if (version && !deprecatedWarnings[opt]) {
13895
13895
  deprecatedWarnings[opt] = true;
13896
- console.warn(formatMessage(opt, " has been deprecated since v" + version2 + " and will be removed in the near future"));
13896
+ console.warn(formatMessage(opt, " has been deprecated since v" + version + " and will be removed in the near future"));
13897
13897
  }
13898
13898
  return validator2 ? validator2(value, opt, opts) : true;
13899
13899
  };
@@ -14567,9 +14567,201 @@ var require_lib = __commonJS((exports) => {
14567
14567
  __exportStar(require_data_contracts(), exports);
14568
14568
  });
14569
14569
 
14570
- // package.json
14571
- var name = "@shortcut/mcp";
14572
- var version = "0.1.5";
14570
+ // src/client/cache.ts
14571
+ class Cache {
14572
+ cache;
14573
+ age;
14574
+ constructor() {
14575
+ this.cache = new Map;
14576
+ this.age = 0;
14577
+ }
14578
+ get(key) {
14579
+ return this.cache.get(key) ?? null;
14580
+ }
14581
+ values() {
14582
+ return Array.from(this.cache.values());
14583
+ }
14584
+ setMany(values) {
14585
+ this.cache.clear();
14586
+ for (const [key, value] of values) {
14587
+ this.cache.set(key, value);
14588
+ }
14589
+ this.age = Date.now();
14590
+ }
14591
+ clear() {
14592
+ this.cache.clear();
14593
+ this.age = 0;
14594
+ }
14595
+ get isStale() {
14596
+ return Date.now() - this.age > 1000 * 60 * 5;
14597
+ }
14598
+ }
14599
+
14600
+ // src/client/shortcut.ts
14601
+ class ShortcutClientWrapper {
14602
+ client;
14603
+ currentUser = null;
14604
+ userCache;
14605
+ workflowCache;
14606
+ constructor(client) {
14607
+ this.client = client;
14608
+ this.userCache = new Cache;
14609
+ this.workflowCache = new Cache;
14610
+ }
14611
+ async loadMembers() {
14612
+ if (this.userCache.isStale) {
14613
+ const response = await this.client.listMembers({});
14614
+ const members = response?.data ?? null;
14615
+ if (members) {
14616
+ this.userCache.setMany(members.map((member) => [member.id, member]));
14617
+ }
14618
+ }
14619
+ }
14620
+ async loadWorkflows() {
14621
+ if (this.workflowCache.isStale) {
14622
+ const response = await this.client.listWorkflows();
14623
+ const workflows = response?.data ?? null;
14624
+ if (workflows) {
14625
+ this.workflowCache.setMany(workflows.map((workflow) => [workflow.id, workflow]));
14626
+ }
14627
+ }
14628
+ }
14629
+ async getCurrentUser() {
14630
+ if (this.currentUser)
14631
+ return this.currentUser;
14632
+ const response = await this.client.getCurrentMemberInfo();
14633
+ const user = response?.data;
14634
+ if (!user)
14635
+ return null;
14636
+ this.currentUser = user;
14637
+ return user;
14638
+ }
14639
+ async getUser(userId) {
14640
+ const response = await this.client.getMember(userId, {});
14641
+ const user = response?.data;
14642
+ if (!user)
14643
+ return null;
14644
+ return user;
14645
+ }
14646
+ async getUserMap(userIds) {
14647
+ await this.loadMembers();
14648
+ return new Map(userIds.map((id) => [id, this.userCache.get(id)]).filter((user) => user[1] !== null));
14649
+ }
14650
+ async getUsers(userIds) {
14651
+ await this.loadMembers();
14652
+ return userIds.map((id) => this.userCache.get(id)).filter((user) => user !== null);
14653
+ }
14654
+ async getWorkflowMap(workflowIds) {
14655
+ await this.loadWorkflows();
14656
+ return new Map(workflowIds.map((id) => [id, this.workflowCache.get(id)]).filter((workflow) => workflow[1] !== null));
14657
+ }
14658
+ async getWorkflows() {
14659
+ await this.loadWorkflows();
14660
+ return Array.from(this.workflowCache.values());
14661
+ }
14662
+ async getWorkflow(workflowPublicId) {
14663
+ const response = await this.client.getWorkflow(workflowPublicId);
14664
+ const workflow = response?.data;
14665
+ if (!workflow)
14666
+ return null;
14667
+ return workflow;
14668
+ }
14669
+ async getTeams() {
14670
+ const response = await this.client.listGroups();
14671
+ const groups = response?.data ?? [];
14672
+ return groups;
14673
+ }
14674
+ async getTeam(teamPublicId) {
14675
+ const response = await this.client.getGroup(teamPublicId);
14676
+ const group = response?.data;
14677
+ if (!group)
14678
+ return null;
14679
+ return group;
14680
+ }
14681
+ async createStory(params) {
14682
+ const response = await this.client.createStory(params);
14683
+ const story = response?.data ?? null;
14684
+ if (!story)
14685
+ throw new Error(`Failed to create the story: ${response.status}`);
14686
+ return story;
14687
+ }
14688
+ async updateStory(storyPublicId, params) {
14689
+ const response = await this.client.updateStory(storyPublicId, params);
14690
+ const story = response?.data ?? null;
14691
+ if (!story)
14692
+ throw new Error(`Failed to update the story: ${response.status}`);
14693
+ return story;
14694
+ }
14695
+ async getStory(storyPublicId) {
14696
+ const response = await this.client.getStory(storyPublicId);
14697
+ const story = response?.data ?? null;
14698
+ if (!story)
14699
+ return null;
14700
+ return story;
14701
+ }
14702
+ async getEpic(epicPublicId) {
14703
+ const response = await this.client.getEpic(epicPublicId);
14704
+ const epic = response?.data ?? null;
14705
+ if (!epic)
14706
+ return null;
14707
+ return epic;
14708
+ }
14709
+ async getIteration(iterationPublicId) {
14710
+ const response = await this.client.getIteration(iterationPublicId);
14711
+ const iteration = response?.data ?? null;
14712
+ if (!iteration)
14713
+ return null;
14714
+ return iteration;
14715
+ }
14716
+ async getMilestone(milestonePublicId) {
14717
+ const response = await this.client.getMilestone(milestonePublicId);
14718
+ const milestone = response?.data ?? null;
14719
+ if (!milestone)
14720
+ return null;
14721
+ return milestone;
14722
+ }
14723
+ async searchStories(query) {
14724
+ const response = await this.client.searchStories({ query, page_size: 25, detail: "slim" });
14725
+ const stories = response?.data?.data;
14726
+ const total = response?.data?.total;
14727
+ if (!stories)
14728
+ return { stories: null, total: null };
14729
+ return { stories, total };
14730
+ }
14731
+ async searchIterations(query) {
14732
+ const response = await this.client.searchIterations({ query, page_size: 25, detail: "slim" });
14733
+ const iterations = response?.data?.data;
14734
+ const total = response?.data?.total;
14735
+ if (!iterations)
14736
+ return { iterations: null, total: null };
14737
+ return { iterations, total };
14738
+ }
14739
+ async searchEpics(query) {
14740
+ const response = await this.client.searchEpics({ query, page_size: 25, detail: "slim" });
14741
+ const epics = response?.data?.data;
14742
+ const total = response?.data?.total;
14743
+ if (!epics)
14744
+ return { epics: null, total: null };
14745
+ return { epics, total };
14746
+ }
14747
+ async searchMilestones(query) {
14748
+ const response = await this.client.searchMilestones({ query, page_size: 25, detail: "slim" });
14749
+ const milestones = response?.data?.data;
14750
+ const total = response?.data?.total;
14751
+ if (!milestones)
14752
+ return { milestones: null, total: null };
14753
+ return { milestones, total };
14754
+ }
14755
+ async listIterationStories(iterationPublicId) {
14756
+ const response = await this.client.listIterationStories(iterationPublicId, {
14757
+ includes_description: false
14758
+ });
14759
+ const stories = response?.data;
14760
+ if (!stories)
14761
+ return { stories: null, total: null };
14762
+ return { stories, total: stories.length };
14763
+ }
14764
+ }
14573
14765
 
14574
14766
  // node_modules/zod/lib/index.mjs
14575
14767
  var util;
@@ -15438,11 +15630,11 @@ function datetimeRegex(args) {
15438
15630
  regex = `${regex}(${opts.join("|")})`;
15439
15631
  return new RegExp(`^${regex}$`);
15440
15632
  }
15441
- function isValidIP(ip, version2) {
15442
- if ((version2 === "v4" || !version2) && ipv4Regex.test(ip)) {
15633
+ function isValidIP(ip, version) {
15634
+ if ((version === "v4" || !version) && ipv4Regex.test(ip)) {
15443
15635
  return true;
15444
15636
  }
15445
- if ((version2 === "v6" || !version2) && ipv6Regex.test(ip)) {
15637
+ if ((version === "v6" || !version) && ipv6Regex.test(ip)) {
15446
15638
  return true;
15447
15639
  }
15448
15640
  return false;
@@ -15465,11 +15657,11 @@ function isValidJWT(jwt, alg) {
15465
15657
  return false;
15466
15658
  }
15467
15659
  }
15468
- function isValidCidr(ip, version2) {
15469
- if ((version2 === "v4" || !version2) && ipv4CidrRegex.test(ip)) {
15660
+ function isValidCidr(ip, version) {
15661
+ if ((version === "v4" || !version) && ipv4CidrRegex.test(ip)) {
15470
15662
  return true;
15471
15663
  }
15472
- if ((version2 === "v6" || !version2) && ipv6CidrRegex.test(ip)) {
15664
+ if ((version === "v6" || !version) && ipv6CidrRegex.test(ip)) {
15473
15665
  return true;
15474
15666
  }
15475
15667
  return false;
@@ -19498,11 +19690,11 @@ var getRefs = (options) => {
19498
19690
  ..._options,
19499
19691
  currentPath,
19500
19692
  propertyPath: undefined,
19501
- seen: new Map(Object.entries(_options.definitions).map(([name2, def]) => [
19693
+ seen: new Map(Object.entries(_options.definitions).map(([name, def]) => [
19502
19694
  def._def,
19503
19695
  {
19504
19696
  def: def._def,
19505
- path: [..._options.basePath, _options.definitionPath, name2],
19697
+ path: [..._options.basePath, _options.definitionPath, name],
19506
19698
  jsonSchema: undefined
19507
19699
  }
19508
19700
  ]))
@@ -20600,34 +20792,34 @@ var addMeta = (def, refs, jsonSchema) => {
20600
20792
  // node_modules/zod-to-json-schema/dist/esm/zodToJsonSchema.js
20601
20793
  var zodToJsonSchema = (schema, options) => {
20602
20794
  const refs = getRefs(options);
20603
- const definitions = typeof options === "object" && options.definitions ? Object.entries(options.definitions).reduce((acc, [name3, schema2]) => ({
20795
+ const definitions = typeof options === "object" && options.definitions ? Object.entries(options.definitions).reduce((acc, [name2, schema2]) => ({
20604
20796
  ...acc,
20605
- [name3]: parseDef(schema2._def, {
20797
+ [name2]: parseDef(schema2._def, {
20606
20798
  ...refs,
20607
- currentPath: [...refs.basePath, refs.definitionPath, name3]
20799
+ currentPath: [...refs.basePath, refs.definitionPath, name2]
20608
20800
  }, true) ?? {}
20609
20801
  }), {}) : undefined;
20610
- const name2 = typeof options === "string" ? options : options?.nameStrategy === "title" ? undefined : options?.name;
20611
- const main = parseDef(schema._def, name2 === undefined ? refs : {
20802
+ const name = typeof options === "string" ? options : options?.nameStrategy === "title" ? undefined : options?.name;
20803
+ const main = parseDef(schema._def, name === undefined ? refs : {
20612
20804
  ...refs,
20613
- currentPath: [...refs.basePath, refs.definitionPath, name2]
20805
+ currentPath: [...refs.basePath, refs.definitionPath, name]
20614
20806
  }, false) ?? {};
20615
20807
  const title = typeof options === "object" && options.name !== undefined && options.nameStrategy === "title" ? options.name : undefined;
20616
20808
  if (title !== undefined) {
20617
20809
  main.title = title;
20618
20810
  }
20619
- const combined = name2 === undefined ? definitions ? {
20811
+ const combined = name === undefined ? definitions ? {
20620
20812
  ...main,
20621
20813
  [refs.definitionPath]: definitions
20622
20814
  } : main : {
20623
20815
  $ref: [
20624
20816
  ...refs.$refStrategy === "relative" ? [] : refs.basePath,
20625
20817
  refs.definitionPath,
20626
- name2
20818
+ name
20627
20819
  ].join("/"),
20628
20820
  [refs.definitionPath]: {
20629
20821
  ...definitions,
20630
- [name2]: main
20822
+ [name]: main
20631
20823
  }
20632
20824
  };
20633
20825
  if (refs.target === "jsonSchema7") {
@@ -20722,9 +20914,9 @@ class McpServer {
20722
20914
  tools: {}
20723
20915
  });
20724
20916
  this.server.setRequestHandler(ListToolsRequestSchema, () => ({
20725
- tools: Object.entries(this._registeredTools).map(([name2, tool]) => {
20917
+ tools: Object.entries(this._registeredTools).map(([name, tool]) => {
20726
20918
  return {
20727
- name: name2,
20919
+ name,
20728
20920
  description: tool.description,
20729
20921
  inputSchema: tool.inputSchema ? zodToJsonSchema(tool.inputSchema, {
20730
20922
  strictUnions: true
@@ -20856,8 +21048,8 @@ class McpServer {
20856
21048
  return { resources: [...resources, ...templateResources] };
20857
21049
  });
20858
21050
  this.server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => {
20859
- const resourceTemplates = Object.entries(this._registeredResourceTemplates).map(([name2, template]) => ({
20860
- name: name2,
21051
+ const resourceTemplates = Object.entries(this._registeredResourceTemplates).map(([name, template]) => ({
21052
+ name,
20861
21053
  uriTemplate: template.resourceTemplate.uriTemplate.toString(),
20862
21054
  ...template.metadata
20863
21055
  }));
@@ -20890,9 +21082,9 @@ class McpServer {
20890
21082
  prompts: {}
20891
21083
  });
20892
21084
  this.server.setRequestHandler(ListPromptsRequestSchema, () => ({
20893
- prompts: Object.entries(this._registeredPrompts).map(([name2, prompt]) => {
21085
+ prompts: Object.entries(this._registeredPrompts).map(([name, prompt]) => {
20894
21086
  return {
20895
- name: name2,
21087
+ name,
20896
21088
  description: prompt.description,
20897
21089
  arguments: prompt.argsSchema ? promptArgumentsFromSchema(prompt.argsSchema) : undefined
20898
21090
  };
@@ -20919,7 +21111,7 @@ class McpServer {
20919
21111
  this.setCompletionRequestHandler();
20920
21112
  this._promptHandlersInitialized = true;
20921
21113
  }
20922
- resource(name2, uriOrTemplate, ...rest) {
21114
+ resource(name, uriOrTemplate, ...rest) {
20923
21115
  let metadata;
20924
21116
  if (typeof rest[0] === "object") {
20925
21117
  metadata = rest.shift();
@@ -20930,15 +21122,15 @@ class McpServer {
20930
21122
  throw new Error(`Resource ${uriOrTemplate} is already registered`);
20931
21123
  }
20932
21124
  this._registeredResources[uriOrTemplate] = {
20933
- name: name2,
21125
+ name,
20934
21126
  metadata,
20935
21127
  readCallback
20936
21128
  };
20937
21129
  } else {
20938
- if (this._registeredResourceTemplates[name2]) {
20939
- throw new Error(`Resource template ${name2} is already registered`);
21130
+ if (this._registeredResourceTemplates[name]) {
21131
+ throw new Error(`Resource template ${name} is already registered`);
20940
21132
  }
20941
- this._registeredResourceTemplates[name2] = {
21133
+ this._registeredResourceTemplates[name] = {
20942
21134
  resourceTemplate: uriOrTemplate,
20943
21135
  metadata,
20944
21136
  readCallback
@@ -20946,9 +21138,9 @@ class McpServer {
20946
21138
  }
20947
21139
  this.setResourceRequestHandlers();
20948
21140
  }
20949
- tool(name2, ...rest) {
20950
- if (this._registeredTools[name2]) {
20951
- throw new Error(`Tool ${name2} is already registered`);
21141
+ tool(name, ...rest) {
21142
+ if (this._registeredTools[name]) {
21143
+ throw new Error(`Tool ${name} is already registered`);
20952
21144
  }
20953
21145
  let description;
20954
21146
  if (typeof rest[0] === "string") {
@@ -20959,16 +21151,16 @@ class McpServer {
20959
21151
  paramsSchema = rest.shift();
20960
21152
  }
20961
21153
  const cb = rest[0];
20962
- this._registeredTools[name2] = {
21154
+ this._registeredTools[name] = {
20963
21155
  description,
20964
21156
  inputSchema: paramsSchema === undefined ? undefined : z.object(paramsSchema),
20965
21157
  callback: cb
20966
21158
  };
20967
21159
  this.setToolRequestHandlers();
20968
21160
  }
20969
- prompt(name2, ...rest) {
20970
- if (this._registeredPrompts[name2]) {
20971
- throw new Error(`Prompt ${name2} is already registered`);
21161
+ prompt(name, ...rest) {
21162
+ if (this._registeredPrompts[name]) {
21163
+ throw new Error(`Prompt ${name} is already registered`);
20972
21164
  }
20973
21165
  let description;
20974
21166
  if (typeof rest[0] === "string") {
@@ -20979,7 +21171,7 @@ class McpServer {
20979
21171
  argsSchema = rest.shift();
20980
21172
  }
20981
21173
  const cb = rest[0];
20982
- this._registeredPrompts[name2] = {
21174
+ this._registeredPrompts[name] = {
20983
21175
  description,
20984
21176
  argsSchema: argsSchema === undefined ? undefined : z.object(argsSchema),
20985
21177
  callback: cb
@@ -20991,8 +21183,8 @@ var EMPTY_OBJECT_JSON_SCHEMA = {
20991
21183
  type: "object"
20992
21184
  };
20993
21185
  function promptArgumentsFromSchema(schema) {
20994
- return Object.entries(schema.shape).map(([name2, field]) => ({
20995
- name: name2,
21186
+ return Object.entries(schema.shape).map(([name, field]) => ({
21187
+ name,
20996
21188
  description: field.description,
20997
21189
  required: !field.isOptional()
20998
21190
  }));
@@ -21110,220 +21302,71 @@ class StdioServerTransport {
21110
21302
  // src/server.ts
21111
21303
  var import_client = __toESM(require_lib(), 1);
21112
21304
 
21113
- // src/client/cache.ts
21114
- class Cache {
21115
- cache;
21116
- age;
21117
- constructor() {
21118
- this.cache = new Map;
21119
- this.age = 0;
21120
- }
21121
- get(key) {
21122
- return this.cache.get(key) ?? null;
21123
- }
21124
- values() {
21125
- return Array.from(this.cache.values());
21126
- }
21127
- setMany(values) {
21128
- this.cache.clear();
21129
- for (const [key, value] of values) {
21130
- this.cache.set(key, value);
21131
- }
21132
- this.age = Date.now();
21133
- }
21134
- clear() {
21135
- this.cache.clear();
21136
- this.age = 0;
21137
- }
21138
- get isStale() {
21139
- return Date.now() - this.age > 1000 * 60 * 5;
21140
- }
21141
- }
21305
+ // package.json
21306
+ var name = "@shortcut/mcp";
21307
+ var version = "0.2.1";
21142
21308
 
21143
- // src/client/shortcut.ts
21144
- class ShortcutClientWrapper {
21309
+ // src/tools/base.ts
21310
+ class BaseTools {
21145
21311
  client;
21146
- currentUser = null;
21147
- userCache;
21148
- workflowCache;
21149
21312
  constructor(client) {
21150
21313
  this.client = client;
21151
- this.userCache = new Cache;
21152
- this.workflowCache = new Cache;
21153
21314
  }
21154
- async loadMembers() {
21155
- if (this.userCache.isStale) {
21156
- const response = await this.client.listMembers({});
21157
- const members = response?.data ?? null;
21158
- if (members) {
21159
- this.userCache.setMany(members.map((member) => [member.id, member]));
21160
- }
21161
- }
21162
- }
21163
- async loadWorkflows() {
21164
- if (this.workflowCache.isStale) {
21165
- const response = await this.client.listWorkflows();
21166
- const workflows = response?.data ?? null;
21167
- if (workflows) {
21168
- this.workflowCache.setMany(workflows.map((workflow) => [workflow.id, workflow]));
21169
- }
21170
- }
21171
- }
21172
- async getCurrentUser() {
21173
- if (this.currentUser)
21174
- return this.currentUser;
21175
- const response = await this.client.getCurrentMemberInfo();
21176
- const user = response?.data;
21177
- if (!user)
21178
- return null;
21179
- this.currentUser = user;
21180
- return user;
21181
- }
21182
- async getUser(userId) {
21183
- const response = await this.client.getMember(userId, {});
21184
- const user = response?.data;
21185
- if (!user)
21186
- return null;
21187
- return user;
21188
- }
21189
- async getUserMap(userIds) {
21190
- await this.loadMembers();
21191
- return new Map(userIds.map((id) => [id, this.userCache.get(id)]).filter((user) => user[1] !== null));
21192
- }
21193
- async getUsers(userIds) {
21194
- await this.loadMembers();
21195
- return userIds.map((id) => this.userCache.get(id)).filter((user) => user !== null);
21196
- }
21197
- async getWorkflowMap(workflowIds) {
21198
- await this.loadWorkflows();
21199
- return new Map(workflowIds.map((id) => [id, this.workflowCache.get(id)]).filter((workflow) => workflow[1] !== null));
21200
- }
21201
- async getWorkflows() {
21202
- await this.loadWorkflows();
21203
- return Array.from(this.workflowCache.values());
21204
- }
21205
- async getWorkflow(workflowPublicId) {
21206
- const response = await this.client.getWorkflow(workflowPublicId);
21207
- const workflow = response?.data;
21208
- if (!workflow)
21209
- return null;
21210
- return workflow;
21211
- }
21212
- async getTeams() {
21213
- const response = await this.client.listGroups();
21214
- const groups = response?.data ?? [];
21215
- return groups;
21216
- }
21217
- async getTeam(teamPublicId) {
21218
- const response = await this.client.getGroup(teamPublicId);
21219
- const group = response?.data;
21220
- if (!group)
21221
- return null;
21222
- return group;
21223
- }
21224
- async createStory(params) {
21225
- const response = await this.client.createStory(params);
21226
- const story = response?.data ?? null;
21227
- if (!story)
21228
- throw new Error(`Failed to create the story: ${response.status}`);
21229
- return story;
21230
- }
21231
- async updateStory(storyPublicId, params) {
21232
- const response = await this.client.updateStory(storyPublicId, params);
21233
- const story = response?.data ?? null;
21234
- if (!story)
21235
- throw new Error(`Failed to update the story: ${response.status}`);
21236
- return story;
21237
- }
21238
- async getStory(storyPublicId) {
21239
- const response = await this.client.getStory(storyPublicId);
21240
- const story = response?.data ?? null;
21241
- if (!story)
21242
- return null;
21243
- return story;
21244
- }
21245
- async getEpic(epicPublicId) {
21246
- const response = await this.client.getEpic(epicPublicId);
21247
- const epic = response?.data ?? null;
21248
- if (!epic)
21249
- return null;
21250
- return epic;
21251
- }
21252
- async getIteration(iterationPublicId) {
21253
- const response = await this.client.getIteration(iterationPublicId);
21254
- const iteration = response?.data ?? null;
21255
- if (!iteration)
21256
- return null;
21257
- return iteration;
21258
- }
21259
- async getMilestone(milestonePublicId) {
21260
- const response = await this.client.getMilestone(milestonePublicId);
21261
- const milestone = response?.data ?? null;
21262
- if (!milestone)
21263
- return null;
21264
- return milestone;
21265
- }
21266
- async searchStories(query) {
21267
- const response = await this.client.searchStories({ query, page_size: 25, detail: "slim" });
21268
- const stories = response?.data?.data;
21269
- const total = response?.data?.total;
21270
- if (!stories)
21271
- return { stories: null, total: null };
21272
- return { stories, total };
21273
- }
21274
- async searchIterations(query) {
21275
- const response = await this.client.searchIterations({ query, page_size: 25, detail: "slim" });
21276
- const iterations = response?.data?.data;
21277
- const total = response?.data?.total;
21278
- if (!iterations)
21279
- return { iterations: null, total: null };
21280
- return { iterations, total };
21281
- }
21282
- async searchEpics(query) {
21283
- const response = await this.client.searchEpics({ query, page_size: 25, detail: "slim" });
21284
- const epics = response?.data?.data;
21285
- const total = response?.data?.total;
21286
- if (!epics)
21287
- return { epics: null, total: null };
21288
- return { epics, total };
21289
- }
21290
- async searchMilestones(query) {
21291
- const response = await this.client.searchMilestones({ query, page_size: 25, detail: "slim" });
21292
- const milestones = response?.data?.data;
21293
- const total = response?.data?.total;
21294
- if (!milestones)
21295
- return { milestones: null, total: null };
21296
- return { milestones, total };
21297
- }
21298
- async listIterationStories(iterationPublicId) {
21299
- const response = await this.client.listIterationStories(iterationPublicId, {
21300
- includes_description: false
21301
- });
21302
- const stories = response?.data;
21303
- if (!stories)
21304
- return { stories: null, total: null };
21305
- return { stories, total: stories.length };
21315
+ toResult(content) {
21316
+ return { content: [{ type: "text", text: content }] };
21306
21317
  }
21307
21318
  }
21308
21319
 
21309
21320
  // src/tools/utils/format.ts
21310
- var formatStoryList = (stories, users) => {
21311
- return stories.map((story) => `- sc-${story.id}: ${story.name} (Type: ${story.story_type}, State: ${story.completed ? "Completed" : story.started ? "In Progress" : "Not Started"}, Team: ${story.group_id ? `${story.group_id}` : "[None]"}, Epic: ${story.epic_id ? `${story.epic_id}` : "[None]"}, Iteration: ${story.iteration_id ? `${story.iteration_id}` : "[None]"}, Owners: ${story.owner_ids.map((ownerId) => users.get(ownerId)).filter((owner) => owner !== null).map((owner) => `@${owner.profile.mention_name}`).join(", ") || "[None]"})`).join(`
21312
- `);
21321
+ var formatAsUnorderedList = (items, label) => {
21322
+ return `${label ? `${label}:` : ""}${items?.length ? `${label ? `
21323
+ ` : ""}${items.map((item) => `- ${item}`).join(`
21324
+ `)}` : `${label ? " " : ""}[None]`}`;
21325
+ };
21326
+ var formatStoryList = (stories, users, label) => {
21327
+ return formatAsUnorderedList(stories.map((story) => `sc-${story.id}: ${story.name} (Type: ${story.story_type}, State: ${story.completed ? "Completed" : story.started ? "In Progress" : "Not Started"}, Team: ${story.group_id ? `${story.group_id}` : "[None]"}, Epic: ${story.epic_id ? `${story.epic_id}` : "[None]"}, Iteration: ${story.iteration_id ? `${story.iteration_id}` : "[None]"}, Owners: ${story.owner_ids.map((ownerId) => users.get(ownerId)).filter((owner) => owner !== null).map((owner) => `@${owner.profile.mention_name}`).join(", ") || "[None]"})`), label);
21313
21328
  };
21314
- var formatMemberList = (ids, users) => {
21315
- return ids.map((id) => {
21329
+ var formatMemberList = (ids, users, label = "Members") => {
21330
+ return formatAsUnorderedList((ids || []).map((id) => {
21316
21331
  const user = users.get(id);
21317
- return user ? `- id=${user.id} @${user.profile.mention_name}` : `- id=${id} [Unknown]`;
21318
- }).join(`
21319
- `);
21332
+ return user ? `id=${user.id} @${user.profile.mention_name}` : `id=${id} [Unknown]`;
21333
+ }), label);
21320
21334
  };
21321
21335
  var formatWorkflowList = (ids, workflows) => {
21322
- return ids.map((id) => workflows.get(id)).filter((workflow) => !!workflow).map((workflow) => {
21336
+ return formatAsUnorderedList((ids || []).map((id) => workflows.get(id)).filter((workflow) => !!workflow).map((workflow) => {
21323
21337
  const defaultState = workflow.states.find((state) => state.id === workflow.default_state_id);
21324
- return `- id=${workflow.id} name=${workflow.name}. Default state: ${defaultState ? `id=${defaultState.id} name=${defaultState.name}` : "[Unknown]"}`;
21325
- }).join(`
21326
- `);
21338
+ return `id=${workflow.id} name=${workflow.name}. Default state: ${defaultState ? `id=${defaultState.id} name=${defaultState.name}` : "[Unknown]"}`;
21339
+ }), "Workflows");
21340
+ };
21341
+ var formatPullRequestList = (branches) => {
21342
+ return formatAsUnorderedList((branches || []).flatMap((branch) => branch.pull_requests || []).map((pr) => `Title: ${pr.title}, Merged: ${pr.merged ? "Yes" : "No"}, URL: ${pr.url}`), "Pull Requests");
21343
+ };
21344
+ var formatTaskList = (tasks) => {
21345
+ return formatAsUnorderedList((tasks || []).map((task) => `[${task.complete ? "X" : " "}] ${task.description}`), "Tasks");
21346
+ };
21347
+
21348
+ // src/tools/utils/search.ts
21349
+ var getKey = (prop) => {
21350
+ if (prop.startsWith("is"))
21351
+ return `is:${prop.slice(2).toLowerCase()}`;
21352
+ if (prop.startsWith("has"))
21353
+ return `has:${prop.slice(3).toLowerCase()}`;
21354
+ return prop;
21355
+ };
21356
+ var buildSearchQuery = async (params, currentUser) => {
21357
+ const query = Object.entries(params).map(([key, value]) => {
21358
+ const q = getKey(key);
21359
+ if ((key === "owner" || key === "requester") && value === "me")
21360
+ return `${q}:${currentUser?.mention_name || value}`;
21361
+ if (typeof value === "boolean")
21362
+ return value ? q : `!${q}`;
21363
+ if (typeof value === "number")
21364
+ return `${q}:${value}`;
21365
+ if (typeof value === "string" && value.includes(" "))
21366
+ return `${q}:"${value}"`;
21367
+ return `${q}:${value}`;
21368
+ }).join(" ");
21369
+ return query;
21327
21370
  };
21328
21371
 
21329
21372
  // src/tools/utils/validation.ts
@@ -21353,38 +21396,175 @@ var is = (field) => z.boolean().optional().describe(`Find only entities that are
21353
21396
  var has = (field) => z.boolean().optional().describe(`Find only entities that have ${field} when true, or only entities that do not have ${field} when false.`);
21354
21397
  var user = (field) => z.string().optional().describe(`Find entities where the ${field} match the specified user. This must either be the user's mention name or the keyword "me" for the current user.`);
21355
21398
 
21356
- // src/tools/utils/search.ts
21357
- var getKey = (prop) => {
21358
- if (prop.startsWith("is"))
21359
- return `is:${prop.slice(2).toLowerCase()}`;
21360
- if (prop.startsWith("has"))
21361
- return `has:${prop.slice(3).toLowerCase()}`;
21362
- return prop;
21363
- };
21364
- var buildSearchQuery = async (params, currentUser) => {
21365
- const query = Object.entries(params).map(([key, value]) => {
21366
- const q = getKey(key);
21367
- if ((key === "owner" || key === "requester") && value === "me")
21368
- return `${q}:${currentUser?.mention_name || value}`;
21369
- if (typeof value === "boolean")
21370
- return value ? q : `!${q}`;
21371
- if (typeof value === "number")
21372
- return `${q}:${value}`;
21373
- if (typeof value === "string" && value.includes(" "))
21374
- return `${q}:"${value}"`;
21375
- return `${q}:${value}`;
21376
- }).join(" ");
21377
- return query;
21378
- };
21399
+ // src/tools/epics.ts
21400
+ class EpicTools extends BaseTools {
21401
+ static create(client, server) {
21402
+ const tools = new EpicTools(client);
21403
+ server.tool("get-epic", "Get a Shortcut epic by public ID", { epicPublicId: z.number().positive().describe("The public ID of the epic to get") }, async ({ epicPublicId }) => await tools.getEpic(epicPublicId));
21404
+ server.tool("search-epics", "Find Shortcut epics.", {
21405
+ id: z.number().optional().describe("Find only epics with the specified public ID"),
21406
+ name: z.string().optional().describe("Find only epics matching the specified name"),
21407
+ description: z.string().optional().describe("Find only epics matching the specified description"),
21408
+ state: z.enum(["unstarted", "started", "done"]).optional().describe("Find only epics matching the specified state"),
21409
+ objective: z.number().optional().describe("Find only epics matching the specified objective"),
21410
+ owner: user("owner"),
21411
+ requester: user("requester"),
21412
+ team: z.string().optional().describe("Find only epics matching the specified team. Should be a team's mention name."),
21413
+ comment: z.string().optional().describe("Find only epics matching the specified comment"),
21414
+ isUnstarted: is("unstarted"),
21415
+ isStarted: is("started"),
21416
+ isDone: is("completed"),
21417
+ isArchived: is("archived").default(false),
21418
+ isOverdue: is("overdue"),
21419
+ hasOwner: has("an owner"),
21420
+ hasComment: has("a comment"),
21421
+ hasDeadline: has("a deadline"),
21422
+ hasLabel: has("a label"),
21423
+ created: date2,
21424
+ updated: date2,
21425
+ completed: date2,
21426
+ due: date2
21427
+ }, async (params) => await tools.searchEpics(params));
21428
+ return tools;
21429
+ }
21430
+ async searchEpics(params) {
21431
+ const currentUser = await this.client.getCurrentUser();
21432
+ const query = await buildSearchQuery(params, currentUser);
21433
+ const { epics, total } = await this.client.searchEpics(query);
21434
+ if (!epics)
21435
+ throw new Error(`Failed to search for epics matching your query: "${query}"`);
21436
+ if (!epics.length)
21437
+ return this.toResult(`Result: No epics found.`);
21438
+ return this.toResult(`Result (first ${epics.length} shown of ${total} total epics found):
21439
+ ${formatAsUnorderedList(epics.map((epic) => `${epic.id}: ${epic.name}`))}`);
21440
+ }
21441
+ async getEpic(epicPublicId) {
21442
+ const epic = await this.client.getEpic(epicPublicId);
21443
+ if (!epic)
21444
+ throw new Error(`Failed to retrieve Shortcut epic with public ID: ${epicPublicId}`);
21445
+ return this.toResult(`Epic: ${epicPublicId}
21446
+ URL: ${epic.app_url}
21447
+ Name: ${epic.name}
21448
+ Archived: ${epic.archived ? "Yes" : "No"}
21449
+ Completed: ${epic.completed ? "Yes" : "No"}
21450
+ Started: ${epic.started ? "Yes" : "No"}
21451
+ Due date: ${epic.deadline ? epic.deadline : "[Not set]"}
21452
+ Team: ${epic.group_id ? `${epic.group_id}` : "[None]"}
21453
+ Objective: ${epic.milestone_id ? `${epic.milestone_id}` : "[None]"}
21379
21454
 
21380
- // src/tools/base.ts
21381
- class BaseTools {
21382
- client;
21383
- constructor(client) {
21384
- this.client = client;
21455
+ Description:
21456
+ ${epic.description}`);
21457
+ }
21458
+ }
21459
+
21460
+ // src/tools/iterations.ts
21461
+ class IterationTools extends BaseTools {
21462
+ static create(client, server) {
21463
+ const tools = new IterationTools(client);
21464
+ server.tool("get-iteration-stories", "Get stories in a specific iteration by iteration public ID", { iterationPublicId: z.number().positive().describe("The public ID of the iteration") }, async ({ iterationPublicId }) => await tools.getIterationStories(iterationPublicId));
21465
+ server.tool("get-iteration", "Get a Shortcut iteration by public ID", {
21466
+ iterationPublicId: z.number().positive().describe("The public ID of the iteration to get")
21467
+ }, async ({ iterationPublicId }) => await tools.getIteration(iterationPublicId));
21468
+ server.tool("search-iterations", "Find Shortcut iterations.", {
21469
+ id: z.number().optional().describe("Find only iterations with the specified public ID"),
21470
+ name: z.string().optional().describe("Find only iterations matching the specified name"),
21471
+ description: z.string().optional().describe("Find only iterations matching the specified description"),
21472
+ state: z.enum(["started", "unstarted", "done"]).optional().describe("Find only iterations matching the specified state"),
21473
+ team: z.string().optional().describe("Find only iterations matching the specified team. Should be a team mention name."),
21474
+ created: date2,
21475
+ updated: date2,
21476
+ startDate: date2,
21477
+ endDate: date2
21478
+ }, async (params) => await tools.searchIterations(params));
21479
+ return tools;
21480
+ }
21481
+ async getIterationStories(iterationPublicId) {
21482
+ const { stories } = await this.client.listIterationStories(iterationPublicId);
21483
+ if (!stories)
21484
+ throw new Error(`Failed to retrieve Shortcut stories in iteration with public ID: ${iterationPublicId}.`);
21485
+ const owners = await this.client.getUserMap(stories.flatMap((story) => story.owner_ids));
21486
+ return this.toResult(`Result (${stories.length} stories found):
21487
+ ${formatStoryList(stories, owners)}`);
21488
+ }
21489
+ async searchIterations(params) {
21490
+ const currentUser = await this.client.getCurrentUser();
21491
+ const query = await buildSearchQuery(params, currentUser);
21492
+ const { iterations, total } = await this.client.searchIterations(query);
21493
+ if (!iterations)
21494
+ throw new Error(`Failed to search for iterations matching your query: "${query}".`);
21495
+ if (!iterations.length)
21496
+ return this.toResult(`Result: No iterations found.`);
21497
+ return this.toResult(`Result (first ${iterations.length} shown of ${total} total iterations found):
21498
+ ${formatAsUnorderedList(iterations.map((iteration) => `${iteration.id}: ${iteration.name} (Start date: ${iteration.start_date}, End date: ${iteration.end_date})`))}`);
21499
+ }
21500
+ async getIteration(iterationPublicId) {
21501
+ const iteration = await this.client.getIteration(iterationPublicId);
21502
+ if (!iteration)
21503
+ throw new Error(`Failed to retrieve Shortcut iteration with public ID: ${iterationPublicId}.`);
21504
+ return this.toResult(`Iteration: ${iterationPublicId}
21505
+ Url: ${iteration.app_url}
21506
+ Name: ${iteration.name}
21507
+ Start date: ${iteration.start_date}
21508
+ End date: ${iteration.end_date}
21509
+ Completed: ${iteration.status === "completed" ? "Yes" : "No"}
21510
+ Started: ${iteration.status === "started" ? "Yes" : "No"}
21511
+ Team: ${iteration.group_ids?.length ? `${iteration.group_ids.join(", ")}` : "[None]"}
21512
+
21513
+ Description:
21514
+ ${iteration.description}`);
21515
+ }
21516
+ }
21517
+
21518
+ // src/tools/objectives.ts
21519
+ class ObjectiveTools extends BaseTools {
21520
+ static create(client, server) {
21521
+ const tools = new ObjectiveTools(client);
21522
+ server.tool("get-objective", "Get a Shortcut objective by public ID", {
21523
+ objectivePublicId: z.number().positive().describe("The public ID of the objective to get")
21524
+ }, async ({ objectivePublicId }) => await tools.getObjective(objectivePublicId));
21525
+ server.tool("search-objectives", "Find Shortcut objectives.", {
21526
+ id: z.number().optional().describe("Find objectives matching the specified id"),
21527
+ name: z.string().optional().describe("Find objectives matching the specified name"),
21528
+ description: z.string().optional().describe("Find objectives matching the specified description"),
21529
+ state: z.enum(["unstarted", "started", "done"]).optional().describe("Find objectives matching the specified state"),
21530
+ owner: user("owner"),
21531
+ requester: user("requester"),
21532
+ team: z.string().optional().describe("Find objectives matching the specified team. Should be a team mention name."),
21533
+ isUnstarted: is("unstarted"),
21534
+ isStarted: is("started"),
21535
+ isDone: is("completed"),
21536
+ isArchived: is("archived"),
21537
+ hasOwner: has("an owner"),
21538
+ created: date2,
21539
+ updated: date2,
21540
+ completed: date2
21541
+ }, async (params) => await tools.searchObjectives(params));
21542
+ return tools;
21385
21543
  }
21386
- toResult(content) {
21387
- return { content: [{ type: "text", text: content }] };
21544
+ async searchObjectives(params) {
21545
+ const currentUser = await this.client.getCurrentUser();
21546
+ const query = await buildSearchQuery(params, currentUser);
21547
+ const { milestones, total } = await this.client.searchMilestones(query);
21548
+ if (!milestones)
21549
+ throw new Error(`Failed to search for milestones matching your query: "${query}"`);
21550
+ if (!milestones.length)
21551
+ return this.toResult(`Result: No milestones found.`);
21552
+ return this.toResult(`Result (first ${milestones.length} shown of ${total} total milestones found):
21553
+ ${formatAsUnorderedList(milestones.map((milestone) => `${milestone.id}: ${milestone.name}`))}`);
21554
+ }
21555
+ async getObjective(objectivePublicId) {
21556
+ const objective = await this.client.getMilestone(objectivePublicId);
21557
+ if (!objective)
21558
+ throw new Error(`Failed to retrieve Shortcut objective with public ID: ${objectivePublicId}`);
21559
+ return this.toResult(`Objective: ${objectivePublicId}
21560
+ Url: ${objective.app_url}
21561
+ Name: ${objective.name}
21562
+ Archived: ${objective.archived ? "Yes" : "No"}
21563
+ Completed: ${objective.completed ? "Yes" : "No"}
21564
+ Started: ${objective.started ? "Yes" : "No"}
21565
+
21566
+ Description:
21567
+ ${objective.description}`);
21388
21568
  }
21389
21569
  }
21390
21570
 
@@ -21392,8 +21572,12 @@ class BaseTools {
21392
21572
  class StoryTools extends BaseTools {
21393
21573
  static create(client, server) {
21394
21574
  const tools = new StoryTools(client);
21395
- server.tool("get-story-branch-name", 'Get a valid branch name for a specific story. The branch name is a combination of story ID, owner, and story name in the format "[owner]/sc-[id]/[name]". The story name will be truncated if the total length of the branch name exceeds 50 characters.', { storyPublicId: z.number().positive().describe("The public Id of the story") }, async ({ storyPublicId }) => await tools.getStoryBranchName(storyPublicId));
21396
- server.tool("get-story", "Get a Shortcut story by public ID", { storyPublicId: z.number().positive().describe("The public ID of the story to get") }, async ({ storyPublicId }) => await tools.getStory(storyPublicId));
21575
+ server.tool("get-story-branch-name", 'Get a valid branch name for a specific story. The branch name is a combination of story ID, owner, and story name in the format "[owner]/sc-[id]/[name]". The story name will be truncated if the total length of the branch name exceeds 50 characters.', {
21576
+ storyPublicId: z.number().positive().describe("The public Id of the story")
21577
+ }, async ({ storyPublicId }) => await tools.getStoryBranchName(storyPublicId));
21578
+ server.tool("get-story", "Get a Shortcut story by public ID", {
21579
+ storyPublicId: z.number().positive().describe("The public ID of the story to get")
21580
+ }, async ({ storyPublicId }) => await tools.getStory(storyPublicId));
21397
21581
  server.tool("search-stories", "Find Shortcut stories.", {
21398
21582
  id: z.number().optional().describe("Find only stories with the specified public ID"),
21399
21583
  name: z.string().optional().describe("Find only stories matching the specified name"),
@@ -21451,9 +21635,21 @@ The story will be added to the default state for the workflow.
21451
21635
  epic: z.number().optional().describe("The epic id of the epic the story belongs to"),
21452
21636
  team: z.string().optional().describe("The team id of the team the story belongs to. Required unless a workflow is specified."),
21453
21637
  workflow: z.number().optional().describe("The workflow to add the story to. Required unless a team is specified.")
21454
- }, async ({ name: name2, description, type, owner, epic, team, workflow }) => await tools.createStory({ name: name2, description, type, owner, epic, team, workflow }));
21455
- server.tool("assign-current-user-as-owner", "Assign the current user as the owner of a story", { storyPublicId: z.number().positive().describe("The public ID of the story") }, async ({ storyPublicId }) => await tools.assignCurrentUserAsOwner(storyPublicId));
21456
- server.tool("unassign-current-user-as-owner", "Unassign the current user as the owner of a story", { storyPublicId: z.number().positive().describe("The public ID of the story") }, async ({ storyPublicId }) => await tools.unassignCurrentUserAsOwner(storyPublicId));
21638
+ }, async ({ name: name2, description, type, owner, epic, team, workflow }) => await tools.createStory({
21639
+ name: name2,
21640
+ description,
21641
+ type,
21642
+ owner,
21643
+ epic,
21644
+ team,
21645
+ workflow
21646
+ }));
21647
+ server.tool("assign-current-user-as-owner", "Assign the current user as the owner of a story", {
21648
+ storyPublicId: z.number().positive().describe("The public ID of the story")
21649
+ }, async ({ storyPublicId }) => await tools.assignCurrentUserAsOwner(storyPublicId));
21650
+ server.tool("unassign-current-user-as-owner", "Unassign the current user as the owner of a story", {
21651
+ storyPublicId: z.number().positive().describe("The public ID of the story")
21652
+ }, async ({ storyPublicId }) => await tools.unassignCurrentUserAsOwner(storyPublicId));
21457
21653
  return tools;
21458
21654
  }
21459
21655
  async assignCurrentUserAsOwner(storyPublicId) {
@@ -21560,14 +21756,19 @@ Blocked: ${story.blocked ? "Yes" : "No"}
21560
21756
  Blocking: ${story.blocker ? "Yes" : "No"}
21561
21757
  Due date: ${story.deadline ? story.deadline : "[None]"}
21562
21758
  Team: ${story.group_id ? `${story.group_id}` : "[None]"}
21563
- Owners:${story.owner_ids.length ? `
21564
- ${formatMemberList(story.owner_ids, users)}` : " [None]"}
21759
+ ${formatMemberList(story.owner_ids, users, "Owners")}
21565
21760
  Epic: ${story.epic_id ? `${story.epic_id}` : "[None]"}
21566
21761
  Iteration: ${story.iteration_id ? `${story.iteration_id}` : "[None]"}
21567
21762
 
21568
21763
  Description:
21569
21764
  ${story.description}
21570
21765
 
21766
+ ${formatAsUnorderedList(story.external_links, "External Links")}
21767
+
21768
+ ${formatPullRequestList(story.branches)}
21769
+
21770
+ ${formatTaskList(story.tasks)}
21771
+
21571
21772
  Comments:
21572
21773
  ${(story.comments || []).map((comment) => {
21573
21774
  const mentionName = comment.author_id ? users.get(comment.author_id)?.profile?.mention_name : null;
@@ -21579,199 +21780,6 @@ ${comment.text || ""}`;
21579
21780
  }
21580
21781
  }
21581
21782
 
21582
- // src/tools/user.ts
21583
- class UserTools extends BaseTools {
21584
- static create(client, server) {
21585
- const tools = new UserTools(client);
21586
- server.tool("get-current-user", "Get the current user", async () => await tools.getCurrentUser());
21587
- return tools;
21588
- }
21589
- async getCurrentUser() {
21590
- const user2 = await this.client.getCurrentUser();
21591
- if (!user2)
21592
- throw new Error("Failed to retrieve current user.");
21593
- return this.toResult(`Current user:
21594
- Id: ${user2.id}
21595
- Mention name: @${user2.mention_name}
21596
- Full name: ${user2.name}`);
21597
- }
21598
- }
21599
-
21600
- // src/tools/epics.ts
21601
- class EpicTools extends BaseTools {
21602
- static create(client, server) {
21603
- const tools = new EpicTools(client);
21604
- server.tool("get-epic", "Get a Shortcut epic by public ID", { epicPublicId: z.number().positive().describe("The public ID of the epic to get") }, async ({ epicPublicId }) => await tools.getEpic(epicPublicId));
21605
- server.tool("search-epics", "Find Shortcut epics.", {
21606
- id: z.number().optional().describe("Find only epics with the specified public ID"),
21607
- name: z.string().optional().describe("Find only epics matching the specified name"),
21608
- description: z.string().optional().describe("Find only epics matching the specified description"),
21609
- state: z.enum(["unstarted", "started", "done"]).optional().describe("Find only epics matching the specified state"),
21610
- objective: z.number().optional().describe("Find only epics matching the specified objective"),
21611
- owner: user("owner"),
21612
- requester: user("requester"),
21613
- team: z.string().optional().describe("Find only epics matching the specified team. Should be a team's mention name."),
21614
- comment: z.string().optional().describe("Find only epics matching the specified comment"),
21615
- isUnstarted: is("unstarted"),
21616
- isStarted: is("started"),
21617
- isDone: is("completed"),
21618
- isArchived: is("archived").default(false),
21619
- isOverdue: is("overdue"),
21620
- hasOwner: has("an owner"),
21621
- hasComment: has("a comment"),
21622
- hasDeadline: has("a deadline"),
21623
- hasLabel: has("a label"),
21624
- created: date2,
21625
- updated: date2,
21626
- completed: date2,
21627
- due: date2
21628
- }, async (params) => await tools.searchEpics(params));
21629
- return tools;
21630
- }
21631
- async searchEpics(params) {
21632
- const currentUser = await this.client.getCurrentUser();
21633
- const query = await buildSearchQuery(params, currentUser);
21634
- const { epics, total } = await this.client.searchEpics(query);
21635
- if (!epics)
21636
- throw new Error(`Failed to search for epics matching your query: "${query}"`);
21637
- if (!epics.length)
21638
- return this.toResult(`Result: No epics found.`);
21639
- return this.toResult(`Result (first ${epics.length} shown of ${total} total epics found):
21640
- ${epics.map((epic) => `- ${epic.id}: ${epic.name}`).join(`
21641
- `)}`);
21642
- }
21643
- async getEpic(epicPublicId) {
21644
- const epic = await this.client.getEpic(epicPublicId);
21645
- if (!epic)
21646
- throw new Error(`Failed to retrieve Shortcut epic with public ID: ${epicPublicId}`);
21647
- return this.toResult(`Epic: ${epicPublicId}
21648
- URL: ${epic.app_url}
21649
- Name: ${epic.name}
21650
- Archived: ${epic.archived ? "Yes" : "No"}
21651
- Completed: ${epic.completed ? "Yes" : "No"}
21652
- Started: ${epic.started ? "Yes" : "No"}
21653
- Due date: ${epic.deadline ? epic.deadline : "[Not set]"}
21654
- Team: ${epic.group_id ? `${epic.group_id}` : "[None]"}
21655
- Objective: ${epic.milestone_id ? `${epic.milestone_id}` : "[None]"}
21656
-
21657
- Description:
21658
- ${epic.description}`);
21659
- }
21660
- }
21661
-
21662
- // src/tools/objectives.ts
21663
- class ObjectiveTools extends BaseTools {
21664
- static create(client, server) {
21665
- const tools = new ObjectiveTools(client);
21666
- server.tool("get-objective", "Get a Shortcut objective by public ID", {
21667
- objectivePublicId: z.number().positive().describe("The public ID of the objective to get")
21668
- }, async ({ objectivePublicId }) => await tools.getObjective(objectivePublicId));
21669
- server.tool("search-objectives", "Find Shortcut objectives.", {
21670
- id: z.number().optional().describe("Find objectives matching the specified id"),
21671
- name: z.string().optional().describe("Find objectives matching the specified name"),
21672
- description: z.string().optional().describe("Find objectives matching the specified description"),
21673
- state: z.enum(["unstarted", "started", "done"]).optional().describe("Find objectives matching the specified state"),
21674
- owner: user("owner"),
21675
- requester: user("requester"),
21676
- team: z.string().optional().describe("Find objectives matching the specified team. Should be a team mention name."),
21677
- isUnstarted: is("unstarted"),
21678
- isStarted: is("started"),
21679
- isDone: is("completed"),
21680
- isArchived: is("archived"),
21681
- hasOwner: has("an owner"),
21682
- created: date2,
21683
- updated: date2,
21684
- completed: date2
21685
- }, async (params) => await tools.searchObjectives(params));
21686
- return tools;
21687
- }
21688
- async searchObjectives(params) {
21689
- const currentUser = await this.client.getCurrentUser();
21690
- const query = await buildSearchQuery(params, currentUser);
21691
- const { milestones, total } = await this.client.searchMilestones(query);
21692
- if (!milestones)
21693
- throw new Error(`Failed to search for milestones matching your query: "${query}"`);
21694
- if (!milestones.length)
21695
- return this.toResult(`Result: No milestones found.`);
21696
- return this.toResult(`Result (first ${milestones.length} shown of ${total} total milestones found):
21697
- ${milestones.map((milestone) => `- ${milestone.id}: ${milestone.name}`).join(`
21698
- `)}`);
21699
- }
21700
- async getObjective(objectivePublicId) {
21701
- const objective = await this.client.getMilestone(objectivePublicId);
21702
- if (!objective)
21703
- throw new Error(`Failed to retrieve Shortcut objective with public ID: ${objectivePublicId}`);
21704
- return this.toResult(`Objective: ${objectivePublicId}
21705
- Url: ${objective.app_url}
21706
- Name: ${objective.name}
21707
- Archived: ${objective.archived ? "Yes" : "No"}
21708
- Completed: ${objective.completed ? "Yes" : "No"}
21709
- Started: ${objective.started ? "Yes" : "No"}
21710
-
21711
- Description:
21712
- ${objective.description}`);
21713
- }
21714
- }
21715
-
21716
- // src/tools/iterations.ts
21717
- class IterationTools extends BaseTools {
21718
- static create(client, server) {
21719
- const tools = new IterationTools(client);
21720
- server.tool("get-iteration-stories", "Get stories in a specific iteration by iteration public ID", { iterationPublicId: z.number().positive().describe("The public ID of the iteration") }, async ({ iterationPublicId }) => await tools.getIterationStories(iterationPublicId));
21721
- server.tool("get-iteration", "Get a Shortcut iteration by public ID", {
21722
- iterationPublicId: z.number().positive().describe("The public ID of the iteration to get")
21723
- }, async ({ iterationPublicId }) => await tools.getIteration(iterationPublicId));
21724
- server.tool("search-iterations", "Find Shortcut iterations.", {
21725
- id: z.number().optional().describe("Find only iterations with the specified public ID"),
21726
- name: z.string().optional().describe("Find only iterations matching the specified name"),
21727
- description: z.string().optional().describe("Find only iterations matching the specified description"),
21728
- state: z.enum(["started", "unstarted", "done"]).optional().describe("Find only iterations matching the specified state"),
21729
- team: z.string().optional().describe("Find only iterations matching the specified team. Should be a team mention name."),
21730
- created: date2,
21731
- updated: date2,
21732
- startDate: date2,
21733
- endDate: date2
21734
- }, async (params) => await tools.searchIterations(params));
21735
- return tools;
21736
- }
21737
- async getIterationStories(iterationPublicId) {
21738
- const { stories } = await this.client.listIterationStories(iterationPublicId);
21739
- if (!stories)
21740
- throw new Error(`Failed to retrieve Shortcut stories in iteration with public ID: ${iterationPublicId}.`);
21741
- const owners = await this.client.getUserMap(stories.flatMap((story) => story.owner_ids));
21742
- return this.toResult(`Result (${stories.length} stories found):
21743
- ${formatStoryList(stories, owners)}`);
21744
- }
21745
- async searchIterations(params) {
21746
- const currentUser = await this.client.getCurrentUser();
21747
- const query = await buildSearchQuery(params, currentUser);
21748
- const { iterations, total } = await this.client.searchIterations(query);
21749
- if (!iterations)
21750
- throw new Error(`Failed to search for iterations matching your query: "${query}".`);
21751
- if (!iterations.length)
21752
- return this.toResult(`Result: No iterations found.`);
21753
- return this.toResult(`Result (first ${iterations.length} shown of ${total} total iterations found):
21754
- ${iterations.map((iteration) => `- ${iteration.id}: ${iteration.name} (Start date: ${iteration.start_date}, End date: ${iteration.end_date})`).join(`
21755
- `)}`);
21756
- }
21757
- async getIteration(iterationPublicId) {
21758
- const iteration = await this.client.getIteration(iterationPublicId);
21759
- if (!iteration)
21760
- throw new Error(`Failed to retrieve Shortcut iteration with public ID: ${iterationPublicId}.`);
21761
- return this.toResult(`Iteration: ${iterationPublicId}
21762
- Url: ${iteration.app_url}
21763
- Name: ${iteration.name}
21764
- Start date: ${iteration.start_date}
21765
- End date: ${iteration.end_date}
21766
- Completed: ${iteration.status === "completed" ? "Yes" : "No"}
21767
- Started: ${iteration.status === "started" ? "Yes" : "No"}
21768
- Team: ${iteration.group_ids?.length ? `${iteration.group_ids.join(", ")}` : "[None]"}
21769
-
21770
- Description:
21771
- ${iteration.description}`);
21772
- }
21773
- }
21774
-
21775
21783
  // src/tools/teams.ts
21776
21784
  class TeamTools extends BaseTools {
21777
21785
  static create(client, server) {
@@ -21789,8 +21797,7 @@ class TeamTools extends BaseTools {
21789
21797
  Name: ${team.name}
21790
21798
  Mention name: ${team.mention_name}
21791
21799
  Description: ${team.description}
21792
- Members:${team.member_ids.length > 0 ? `
21793
- ${formatMemberList(team.member_ids, users)}` : " [None]"}`);
21800
+ ${formatMemberList(team.member_ids, users)}`);
21794
21801
  }
21795
21802
  async getTeams() {
21796
21803
  const teams = await this.client.getTeams();
@@ -21803,13 +21810,30 @@ ${teams.map((team) => `Id: ${team.id}
21803
21810
  Name: ${team.name}
21804
21811
  Description: ${team.description}
21805
21812
  Number of Members: ${team.member_ids.length}
21806
- Workflows: ${team.workflow_ids.length > 0 ? `
21807
- ${formatWorkflowList(team.workflow_ids, workflows)}` : "[None]"}`).join(`
21813
+ ${formatWorkflowList(team.workflow_ids, workflows)}`).join(`
21808
21814
 
21809
21815
  `)}`);
21810
21816
  }
21811
21817
  }
21812
21818
 
21819
+ // src/tools/user.ts
21820
+ class UserTools extends BaseTools {
21821
+ static create(client, server) {
21822
+ const tools = new UserTools(client);
21823
+ server.tool("get-current-user", "Get the current user", async () => await tools.getCurrentUser());
21824
+ return tools;
21825
+ }
21826
+ async getCurrentUser() {
21827
+ const user2 = await this.client.getCurrentUser();
21828
+ if (!user2)
21829
+ throw new Error("Failed to retrieve current user.");
21830
+ return this.toResult(`Current user:
21831
+ Id: ${user2.id}
21832
+ Mention name: @${user2.mention_name}
21833
+ Full name: ${user2.name}`);
21834
+ }
21835
+ }
21836
+
21813
21837
  // src/tools/workflows.ts
21814
21838
  class WorkflowTools extends BaseTools {
21815
21839
  static create(client, server) {
@@ -21826,8 +21850,7 @@ class WorkflowTools extends BaseTools {
21826
21850
  Name: ${workflow.name}
21827
21851
  Description: ${workflow.description}
21828
21852
  States:
21829
- ${workflow.states.map((state) => `- id=${state.id} name=${state.name} (default: ${state.id === workflow.default_state_id ? "yes" : "no"}, type: ${state.type})`).join(`
21830
- `)}`);
21853
+ ${formatAsUnorderedList(workflow.states.map((state) => `id=${state.id} name=${state.name} (default: ${state.id === workflow.default_state_id ? "yes" : "no"}, type: ${state.type})`))}`);
21831
21854
  }
21832
21855
  async listWorkflows() {
21833
21856
  const workflows = await this.client.getWorkflows();