@oomol-lab/open-connector 1.6.5 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/assets/open-connector/catalog/apps/aisa.json +6138 -312
  2. package/assets/open-connector/catalog/apps/baizhi_mcp.json +135 -0
  3. package/assets/open-connector/catalog/apps/discordbot.json +2888 -248
  4. package/assets/open-connector/catalog/apps/drawio_mcp.json +192 -0
  5. package/assets/open-connector/catalog/apps/firecrawl.json +176 -32
  6. package/assets/open-connector/catalog/apps/framejet.json +273 -0
  7. package/assets/open-connector/catalog/apps/futunn.json +2967 -0
  8. package/assets/open-connector/catalog/apps/gmail.json +225 -0
  9. package/assets/open-connector/catalog/apps/google_analytics.json +4 -1
  10. package/assets/open-connector/catalog/apps/google_bigquery.json +4 -1
  11. package/assets/open-connector/catalog/apps/google_search_console.json +4 -1
  12. package/assets/open-connector/catalog/apps/googlecalendar.json +4 -1
  13. package/assets/open-connector/catalog/apps/googlechat.json +694 -7
  14. package/assets/open-connector/catalog/apps/googledocs.json +4 -1
  15. package/assets/open-connector/catalog/apps/googlesheets.json +4 -1
  16. package/assets/open-connector/catalog/apps/granola.json +23 -0
  17. package/assets/open-connector/catalog/apps/keepa.json +3534 -987
  18. package/assets/open-connector/catalog/apps/linear.json +17 -0
  19. package/assets/open-connector/catalog/apps/linear_mcp.json +510 -0
  20. package/assets/open-connector/catalog/apps/moneyforward_mcp.json +1432 -0
  21. package/assets/open-connector/catalog/apps/notion.json +446 -1
  22. package/assets/open-connector/catalog/apps/notion_mcp.json +490 -0
  23. package/assets/open-connector/catalog/apps/odoo.json +619 -0
  24. package/assets/open-connector/catalog/apps/outlook.json +30 -8
  25. package/assets/open-connector/catalog/apps/outlook_calendar.json +23 -2
  26. package/assets/open-connector/catalog/apps/postiz.json +334 -0
  27. package/assets/open-connector/catalog/apps/proworkflow.json +529 -0
  28. package/assets/open-connector/catalog/apps/sellersprite_mcp.json +2 -1
  29. package/assets/open-connector/catalog/apps/slack.json +223 -0
  30. package/assets/open-connector/catalog/apps/slackbot.json +214 -0
  31. package/assets/open-connector/catalog/apps/tencent_docs.json +3 -0
  32. package/assets/open-connector/catalog/apps/wisburg.json +1528 -0
  33. package/assets/open-connector/catalog/apps/youtube.json +4 -1
  34. package/assets/open-connector/catalog/apps/zerotier.json +3319 -0
  35. package/assets/open-connector/catalog/apps-index.json +1 -1
  36. package/package.json +1 -1
  37. package/src/core/cast.d.ts +10 -0
  38. package/src/core/cast.js +14 -0
  39. package/src/core/guarded-fetch.d.ts +6 -3
  40. package/src/core/guarded-fetch.js +6 -3
  41. package/src/core/provider-scenarios.js +67 -13
  42. package/src/core/request.js +4 -1
  43. package/src/core/types.d.ts +1 -1
  44. package/src/mcp.js +10 -7
  45. package/src/oauth/oauth-client-config-service.d.ts +16 -1
  46. package/src/oauth/oauth-client-config-service.js +59 -3
  47. package/src/oauth/oauth-flow-service.d.ts +6 -0
  48. package/src/oauth/oauth-flow-service.js +4 -2
  49. package/src/providers/17track/runtime.js +1 -1
  50. package/src/providers/action-contracts.generated.d.ts +17 -5
  51. package/src/providers/aifinmarket/runtime.js +1 -1
  52. package/src/providers/aisa/executors.js +734 -46
  53. package/src/providers/aliyun_ack/runtime.js +1 -1
  54. package/src/providers/aliyun_sls/runtime.js +1 -1
  55. package/src/providers/app_store_server/runtime-helpers.js +1 -1
  56. package/src/providers/baizhi_mcp/actions.d.ts +2 -0
  57. package/src/providers/baizhi_mcp/actions.js +40 -0
  58. package/src/providers/baizhi_mcp/executors.d.ts +3 -0
  59. package/src/providers/baizhi_mcp/executors.js +102 -0
  60. package/src/providers/bazhuayu/runtime.js +2 -2
  61. package/src/providers/bilibili/runtime.js +2 -4
  62. package/src/providers/bilibili/upload.js +9 -7
  63. package/src/providers/cloudflare_browser_rendering/executors.js +1 -1
  64. package/src/providers/cloudflare_dns/executors.js +1 -1
  65. package/src/providers/{cloudflare-current-user.js → cloudflare_dns/runtime-user.js} +2 -2
  66. package/src/providers/cloudflare_dns/runtime.d.ts +1 -1
  67. package/src/providers/cloudflare_dns/runtime.js +1 -1
  68. package/src/providers/cloudflare_r2/executors.js +1 -1
  69. package/src/providers/cloudflare_r2/runtime.d.ts +1 -1
  70. package/src/providers/cloudflare_r2/runtime.js +1 -1
  71. package/src/providers/cloudflare_worker/executors.js +1 -1
  72. package/src/providers/cloudflare_worker/runtime.d.ts +1 -1
  73. package/src/providers/cloudflare_worker/runtime.js +1 -1
  74. package/src/providers/dingtalk_mcp/runtime.js +1 -1
  75. package/src/providers/discordbot/executors.d.ts +0 -9
  76. package/src/providers/discordbot/executors.js +9 -101
  77. package/src/providers/discordbot/runtime-guilds.d.ts +3 -0
  78. package/src/providers/discordbot/runtime-guilds.js +464 -0
  79. package/src/providers/discordbot/runtime.d.ts +42 -0
  80. package/src/providers/discordbot/runtime.js +124 -0
  81. package/src/providers/drawio_mcp/executors.d.ts +3 -0
  82. package/src/providers/drawio_mcp/executors.js +19 -0
  83. package/src/providers/drawio_mcp/runtime.d.ts +8 -0
  84. package/src/providers/drawio_mcp/runtime.js +175 -0
  85. package/src/providers/fastmoss_mcp/executors.js +2 -2
  86. package/src/providers/finchina/executors.js +1 -1
  87. package/src/providers/framejet/executors.d.ts +4 -0
  88. package/src/providers/framejet/executors.js +19 -0
  89. package/src/providers/framejet/runtime.d.ts +16 -0
  90. package/src/providers/framejet/runtime.js +148 -0
  91. package/src/providers/futunn/actions.d.ts +2 -0
  92. package/src/providers/futunn/actions.js +152 -0
  93. package/src/providers/futunn/actions.research.d.ts +2 -0
  94. package/src/providers/futunn/actions.research.js +423 -0
  95. package/src/providers/futunn/actions.trading.d.ts +2 -0
  96. package/src/providers/futunn/actions.trading.js +204 -0
  97. package/src/providers/futunn/executors.d.ts +3 -0
  98. package/src/providers/futunn/executors.js +24 -0
  99. package/src/providers/futunn/runtime-research.d.ts +3 -0
  100. package/src/providers/futunn/runtime-research.js +133 -0
  101. package/src/providers/futunn/runtime-trading.d.ts +3 -0
  102. package/src/providers/futunn/runtime-trading.js +39 -0
  103. package/src/providers/futunn/runtime.d.ts +2 -0
  104. package/src/providers/futunn/runtime.js +50 -0
  105. package/src/providers/futunn/transport.d.ts +11 -0
  106. package/src/providers/futunn/transport.js +76 -0
  107. package/src/providers/gildata/executors.js +1 -1
  108. package/src/providers/gmail/executors.js +3 -3
  109. package/src/providers/gmail/message.d.ts +6 -8
  110. package/src/providers/gmail/message.js +4 -0
  111. package/src/providers/gmail/scopes.d.ts +32 -0
  112. package/src/providers/gmail/scopes.js +37 -0
  113. package/src/providers/google_analytics/executors.js +2 -2
  114. package/src/providers/google_analytics/scopes.js +6 -1
  115. package/src/providers/google_bigquery/executors.js +2 -2
  116. package/src/providers/google_bigquery/scopes.js +2 -0
  117. package/src/providers/google_search_console/executors.js +2 -2
  118. package/src/providers/google_search_console/scopes.js +2 -0
  119. package/src/providers/googleads/executors.js +1 -1
  120. package/src/providers/googleads/scopes.d.ts +1 -3
  121. package/src/providers/googleads/scopes.js +2 -4
  122. package/src/providers/googlecalendar/executors.js +1 -1
  123. package/src/providers/googlecalendar/runtime-shared.js +2 -2
  124. package/src/providers/googlecalendar/scopes.js +2 -0
  125. package/src/providers/googlechat/executors.d.ts +1 -3
  126. package/src/providers/googlechat/executors.js +219 -28
  127. package/src/providers/googlechat/members.d.ts +55 -0
  128. package/src/providers/googlechat/members.js +293 -0
  129. package/src/providers/googlechat/runtime.d.ts +16 -0
  130. package/src/providers/googlechat/runtime.js +25 -0
  131. package/src/providers/googlechat/scopes.d.ts +22 -3
  132. package/src/providers/googlechat/scopes.js +30 -7
  133. package/src/providers/googledocs/executors.js +1 -1
  134. package/src/providers/googledrive/executors.js +2 -2
  135. package/src/providers/{google-auth.d.ts → googledrive/runtime-auth.d.ts} +2 -2
  136. package/src/providers/{google-auth.js → googledrive/runtime-auth.js} +3 -3
  137. package/src/providers/googledrive/runtime-collaboration.js +1 -1
  138. package/src/providers/{google-runtime.d.ts → googledrive/runtime-request.d.ts} +1 -1
  139. package/src/providers/{google-runtime.js → googledrive/runtime-request.js} +2 -2
  140. package/src/providers/googleforms/executors.js +2 -2
  141. package/src/providers/googleforms/scopes.d.ts +0 -3
  142. package/src/providers/googleforms/scopes.js +2 -6
  143. package/src/providers/googlemeet/executors.js +2 -2
  144. package/src/providers/googlemeet/scopes.d.ts +0 -3
  145. package/src/providers/googlemeet/scopes.js +2 -6
  146. package/src/providers/googlesheets/executors.js +1 -1
  147. package/src/providers/googlesheets/scopes.js +2 -0
  148. package/src/providers/googleslides/executors.js +2 -2
  149. package/src/providers/googleslides/scopes.d.ts +0 -3
  150. package/src/providers/googleslides/scopes.js +2 -6
  151. package/src/providers/googletasks/executors.js +2 -2
  152. package/src/providers/googletasks/scopes.d.ts +0 -3
  153. package/src/providers/googletasks/scopes.js +2 -6
  154. package/src/providers/granola/actions.js +2 -0
  155. package/src/providers/granola/runtime-mcp.js +2 -0
  156. package/src/providers/granola/runtime.js +2 -2
  157. package/src/providers/indiegogo/executors.js +1 -1
  158. package/src/providers/investoday_mcp/executors.js +2 -2
  159. package/src/providers/iyunbiao/runtime.js +2 -2
  160. package/src/providers/keepa/actions.d.ts +1 -1
  161. package/src/providers/keepa/actions.js +371 -149
  162. package/src/providers/keepa/deal-attributes.d.ts +1 -0
  163. package/src/providers/keepa/deal-attributes.js +26 -0
  164. package/src/providers/keepa/runtime.js +212 -18
  165. package/src/providers/kingdee/executors.js +1 -1
  166. package/src/providers/kingdee/runtime.js +2 -2
  167. package/src/providers/kuaidi100/runtime.js +1 -1
  168. package/src/providers/linear/executors.js +12 -9
  169. package/src/providers/linear_mcp/actions.d.ts +47 -0
  170. package/src/providers/linear_mcp/actions.js +102 -0
  171. package/src/providers/linear_mcp/endpoints.d.ts +3 -0
  172. package/src/providers/linear_mcp/endpoints.js +3 -0
  173. package/src/providers/linear_mcp/executors.d.ts +3 -0
  174. package/src/providers/linear_mcp/executors.js +9 -0
  175. package/src/providers/linear_mcp/runtime-mcp.d.ts +15 -0
  176. package/src/providers/linear_mcp/runtime-mcp.js +238 -0
  177. package/src/providers/lingxing_mcp/runtime.js +1 -1
  178. package/src/providers/mcdonalds_cn_mcp/runtime.js +1 -1
  179. package/src/providers/metabase/mcp-runtime.js +1 -1
  180. package/src/providers/moneyforward_mcp/executors.d.ts +3 -0
  181. package/src/providers/moneyforward_mcp/executors.js +11 -0
  182. package/src/providers/moneyforward_mcp/manifest.d.ts +31 -0
  183. package/src/providers/moneyforward_mcp/manifest.js +204 -0
  184. package/src/providers/moneyforward_mcp/runtime.d.ts +18 -0
  185. package/src/providers/moneyforward_mcp/runtime.js +296 -0
  186. package/src/providers/notion/executors.js +39 -3
  187. package/src/providers/notion_mcp/endpoints.d.ts +3 -0
  188. package/src/providers/notion_mcp/endpoints.js +3 -0
  189. package/src/providers/notion_mcp/executors.d.ts +3 -0
  190. package/src/providers/notion_mcp/executors.js +176 -0
  191. package/src/providers/notion_mcp/mcp-response.d.ts +86 -0
  192. package/src/providers/notion_mcp/mcp-response.js +314 -0
  193. package/src/providers/odoo/executors.d.ts +3 -0
  194. package/src/providers/odoo/executors.js +32 -0
  195. package/src/providers/odoo/runtime.d.ts +16 -0
  196. package/src/providers/odoo/runtime.js +140 -0
  197. package/src/providers/outlook/executors.js +27 -6
  198. package/src/providers/outlook/microsoft-graph.js +17 -6
  199. package/src/providers/outlook_calendar/executors.js +24 -7
  200. package/src/providers/patsnap_mcp/executors.js +2 -2
  201. package/src/providers/postiz/executors.d.ts +4 -0
  202. package/src/providers/postiz/executors.js +83 -0
  203. package/src/providers/provider-runtime.d.ts +15 -0
  204. package/src/providers/provider-runtime.js +57 -4
  205. package/src/providers/proworkflow/executors.d.ts +4 -0
  206. package/src/providers/proworkflow/executors.js +135 -0
  207. package/src/providers/registry.generated.js +12 -0
  208. package/src/providers/scrape_creators/catalog.js +1 -1
  209. package/src/providers/scrape_creators/runtime.js +1 -1
  210. package/src/providers/sellersprite_mcp/executors.js +2 -2
  211. package/src/providers/sellersprite_mcp/official-actions.js +3 -2
  212. package/src/providers/sentry/runtime.js +6 -7
  213. package/src/providers/slack/actions.js +52 -3
  214. package/src/providers/slack/runtime.js +58 -21
  215. package/src/providers/slickdeals/runtime.js +1 -1
  216. package/src/providers/sorftime_mcp/executors.js +2 -2
  217. package/src/providers/tikhub/bounded-response.d.ts +1 -1
  218. package/src/providers/tikhub/bounded-response.js +8 -34
  219. package/src/providers/tikhub/endpoint-catalog.js +1 -1
  220. package/src/providers/tikhub/runtime.js +2 -2
  221. package/src/providers/tongdaxin/executors.js +79 -15
  222. package/src/providers/uspto/executors.js +1 -1
  223. package/src/providers/waffo/executors.js +1 -1
  224. package/src/providers/waffo/runtime.js +1 -1
  225. package/src/providers/wecom_mcp/runtime.js +1 -1
  226. package/src/providers/wisburg/executors.d.ts +4 -0
  227. package/src/providers/wisburg/executors.js +13 -0
  228. package/src/providers/wisburg/runtime.d.ts +12 -0
  229. package/src/providers/wisburg/runtime.js +133 -0
  230. package/src/providers/xydc_mcp/executors.js +2 -2
  231. package/src/providers/youtube/executors.js +1 -1
  232. package/src/providers/youtube/runtime.js +1 -1
  233. package/src/providers/zerotier/executors.d.ts +9 -0
  234. package/src/providers/zerotier/executors.js +629 -0
  235. package/src/providers/zerotier/runtime.d.ts +54 -0
  236. package/src/providers/zerotier/runtime.js +207 -0
  237. package/src/server/api/openapi.js +12 -2
  238. package/src/server/api/runtime-api.d.ts +8 -1
  239. package/src/server/api/runtime-api.js +19 -1
  240. package/src/server/connect-server.js +10 -2
  241. package/src/providers/sentry/scopes.d.ts +0 -1
  242. package/src/providers/sentry/scopes.js +0 -7
  243. /package/src/providers/{cloudflare-current-user.d.ts → cloudflare_dns/runtime-user.d.ts} +0 -0
@@ -0,0 +1 @@
1
+ export declare const keepaDealAttributeNames: readonly ["material", "type", "manufacturer", "brand", "model", "color", "size", "unitType", "scent", "itemForm", "pattern", "style", "itemTypeKeyword", "targetAudienceKeyword", "edition", "format", "author", "binding", "languages", "brandStoreName", "brandStoreUrlName", "websiteDisplayGroup", "websiteDisplayGroupName", "salesRankDisplayGroup"];
@@ -0,0 +1,26 @@
1
+ export const keepaDealAttributeNames = [
2
+ "material",
3
+ "type",
4
+ "manufacturer",
5
+ "brand",
6
+ "model",
7
+ "color",
8
+ "size",
9
+ "unitType",
10
+ "scent",
11
+ "itemForm",
12
+ "pattern",
13
+ "style",
14
+ "itemTypeKeyword",
15
+ "targetAudienceKeyword",
16
+ "edition",
17
+ "format",
18
+ "author",
19
+ "binding",
20
+ "languages",
21
+ "brandStoreName",
22
+ "brandStoreUrlName",
23
+ "websiteDisplayGroup",
24
+ "websiteDisplayGroupName",
25
+ "salesRankDisplayGroup",
26
+ ];
@@ -1,6 +1,7 @@
1
1
  import { compactObject, optionalRawString, optionalRecord, rawStringOrNull } from "../../core/cast.js";
2
- import { createProviderTimeout, isAbortLikeError, ProviderRequestError, providerUserAgent, readProviderTextBody, requiredInputString, } from "../provider-runtime.js";
2
+ import { createProviderTimeout, isAbortLikeError, providerInputError, ProviderRequestError, providerUserAgent, readProviderTextBody, requiredInputString, } from "../provider-runtime.js";
3
3
  import { keepaDealPriceTypes as dealPriceTypes, keepaHistoryTypes as historyTypes } from "./actions.js";
4
+ import { keepaDealAttributeNames } from "./deal-attributes.js";
4
5
  export const keepaApiBaseUrl = "https://api.keepa.com";
5
6
  const keepaTimeStartMinutes = 21_564_000;
6
7
  const keepaDefaultTimeoutMs = 120_000;
@@ -211,6 +212,137 @@ export const keepaActionHandlers = {
211
212
  meta: normalizeMeta(payload),
212
213
  };
213
214
  },
215
+ async search_products(input, context) {
216
+ const marketplace = requireMarketplace(input.marketplace);
217
+ const payload = await requestKeepaAction(context, {
218
+ path: "/search",
219
+ query: {
220
+ domain: marketplaceDomainIdByCode[marketplace],
221
+ type: "product",
222
+ term: requiredInputString(input.term, "term"),
223
+ ...(input.asinsOnly === true ? { "asins-only": 1 } : {}),
224
+ ...buildStatsQuery(input),
225
+ ...(typeof input.updateHours === "number" ? { update: input.updateHours } : {}),
226
+ ...(input.includeHistory === false ? { history: 0 } : {}),
227
+ ...(input.includeRating === true ? { rating: 1 } : {}),
228
+ },
229
+ });
230
+ const products = readObjectArray(payload.products).map(normalizeProductSnapshot);
231
+ return {
232
+ marketplace,
233
+ asins: input.asinsOnly === true ? readStringArray(payload.asinList) : products.map((product) => product.asin),
234
+ products,
235
+ meta: normalizeMeta(payload),
236
+ };
237
+ },
238
+ async lookup_categories(input, context) {
239
+ const marketplace = requireMarketplace(input.marketplace);
240
+ const categoryIds = readIntegerArray(input.categoryIds);
241
+ if (categoryIds.includes(0) && categoryIds.length !== 1) {
242
+ throw providerInputError("root category lookup must use categoryIds [0] alone");
243
+ }
244
+ const payload = await requestKeepaAction(context, {
245
+ path: "/category",
246
+ query: {
247
+ domain: marketplaceDomainIdByCode[marketplace],
248
+ category: categoryIds.join(","),
249
+ parents: input.includeParents === true ? 1 : 0,
250
+ },
251
+ });
252
+ return {
253
+ marketplace,
254
+ categories: objectMapValues(payload.categories),
255
+ categoryParents: objectMapValues(payload.categoryParents),
256
+ meta: normalizeMeta(payload),
257
+ };
258
+ },
259
+ async find_sellers(input, context) {
260
+ const marketplace = requireMarketplace(input.marketplace);
261
+ const filters = optionalRecord(input.filters);
262
+ if (!filters)
263
+ throw providerInputError("filters must be an object");
264
+ if (!Object.keys(filters).some((key) => !["page", "perPage", "sort", "minMatch"].includes(key))) {
265
+ throw providerInputError("Seller Finder requires at least one business filter");
266
+ }
267
+ const page = typeof filters.page === "number" ? filters.page : 0;
268
+ const perPage = filters.perPage === 0 ? 100 : typeof filters.perPage === "number" ? filters.perPage : 50;
269
+ if (page > 0 && page * perPage >= 10_000) {
270
+ throw providerInputError("Seller Finder page starts beyond the 10000-result limit");
271
+ }
272
+ const payload = await requestKeepaAction(context, {
273
+ path: "/sellerquery",
274
+ query: { domain: marketplaceDomainIdByCode[marketplace] },
275
+ body: filters,
276
+ });
277
+ return {
278
+ marketplace,
279
+ sellerIds: readStringArray(payload.sellerIdList),
280
+ totalResults: asNullableInteger(payload.totalResults),
281
+ meta: normalizeMeta(payload),
282
+ };
283
+ },
284
+ async get_most_rated_sellers(input, context) {
285
+ const marketplace = requireMarketplace(input.marketplace);
286
+ const payload = await requestKeepaAction(context, {
287
+ path: "/topseller",
288
+ query: { domain: marketplaceDomainIdByCode[marketplace] },
289
+ });
290
+ const window = windowList(readStringArray(payload.sellerIdList), input);
291
+ return { marketplace, sellerIds: window.items, ...window.info, meta: normalizeMeta(payload) };
292
+ },
293
+ async get_seller_storefront(input, context) {
294
+ const marketplace = requireMarketplace(input.marketplace);
295
+ const sellerId = requiredInputString(input.sellerId, "sellerId");
296
+ const payload = await requestKeepaAction(context, {
297
+ path: "/seller",
298
+ query: { domain: marketplaceDomainIdByCode[marketplace], seller: sellerId, storefront: 1 },
299
+ });
300
+ const seller = optionalRecord(optionalRecord(payload.sellers)?.[sellerId]);
301
+ const asins = readStringArray(seller?.asinList);
302
+ const lastSeen = Array.isArray(seller?.asinListLastSeen) ? seller.asinListLastSeen : [];
303
+ const window = windowList(asins.map((asin, index) => ({ asin, lastSeen: asNullableInteger(lastSeen[index]) })), input);
304
+ const { asinList: _asinList, asinListLastSeen: _asinListLastSeen, ...sellerWithoutList } = seller ?? {};
305
+ return {
306
+ marketplace,
307
+ seller: seller ? normalizeSeller(sellerId, sellerWithoutList) : null,
308
+ items: window.items,
309
+ ...window.info,
310
+ meta: normalizeMeta(payload),
311
+ };
312
+ },
313
+ async get_lightning_deal(input, context) {
314
+ const marketplace = requireMarketplace(input.marketplace);
315
+ const payload = await requestKeepaAction(context, {
316
+ path: "/lightningdeal",
317
+ query: {
318
+ domain: marketplaceDomainIdByCode[marketplace],
319
+ asin: requiredInputString(input.asin, "asin"),
320
+ ...(typeof input.state === "string" ? { state: input.state } : {}),
321
+ },
322
+ });
323
+ return {
324
+ marketplace,
325
+ lightningDeals: readObjectArray(payload.lightningDeals),
326
+ meta: normalizeMeta(payload),
327
+ };
328
+ },
329
+ async list_lightning_deals(input, context) {
330
+ const marketplace = requireMarketplace(input.marketplace);
331
+ const payload = await requestKeepaAction(context, {
332
+ path: "/lightningdeal",
333
+ query: {
334
+ domain: marketplaceDomainIdByCode[marketplace],
335
+ ...(typeof input.state === "string" ? { state: input.state } : {}),
336
+ },
337
+ });
338
+ const window = windowList(readObjectArray(payload.lightningDeals), input);
339
+ return {
340
+ marketplace,
341
+ lightningDeals: window.items,
342
+ ...window.info,
343
+ meta: normalizeMeta(payload),
344
+ };
345
+ },
214
346
  };
215
347
  export async function validateKeepaCredential(apiKey, fetcher, signal) {
216
348
  const payload = await requestKeepa({
@@ -232,39 +364,80 @@ export async function validateKeepaCredential(apiKey, fetcher, signal) {
232
364
  },
233
365
  };
234
366
  }
367
+ function requestKeepaAction(context, input) {
368
+ return requestKeepa({ ...input, context, phase: "execute" });
369
+ }
235
370
  function buildProductQuery(input, marketplace, history) {
236
- const asins = readAsinArray(input.asins);
237
- assertProductRequestRules(input, asins);
371
+ const asins = input.asins === undefined ? [] : readAsinArray(input.asins);
372
+ const codes = readStringArray(input.codes).map((code) => code.trim());
373
+ if ((asins.length === 0) === (codes.length === 0)) {
374
+ throw providerInputError("provide either asins or codes");
375
+ }
376
+ const itemCount = asins.length || codes.length;
377
+ if (typeof input.offers === "number" && itemCount > 20) {
378
+ throw providerInputError("offers can only be requested for up to 20 ASINs or product codes");
379
+ }
380
+ if (input.codeLimit !== undefined && codes.length === 0) {
381
+ throw providerInputError("codeLimit requires codes");
382
+ }
383
+ if (input.onlyLiveOffers === true && typeof input.offers !== "number") {
384
+ throw providerInputError("onlyLiveOffers requires offers");
385
+ }
386
+ if (input.includeStock === true && typeof input.offers !== "number") {
387
+ throw providerInputError("includeStock requires offers");
388
+ }
238
389
  return {
239
- asin: asins.join(","),
390
+ ...(asins.length > 0 ? { asin: asins.join(",") } : { code: codes.join(",") }),
240
391
  domain: marketplaceDomainIdByCode[marketplace],
241
392
  history: history ? 1 : 0,
242
- ...(typeof input.statsDays === "number" ? { stats: input.statsDays } : {}),
393
+ ...buildStatsQuery(input),
394
+ ...(typeof input.codeLimit === "number" ? { "code-limit": input.codeLimit } : {}),
243
395
  ...(typeof input.updateHours === "number" ? { update: input.updateHours } : {}),
244
396
  ...(typeof input.offers === "number" ? { offers: input.offers } : {}),
245
397
  ...(input.onlyLiveOffers === true ? { "only-live-offers": 1 } : {}),
246
398
  ...(input.includeBuyBox === true ? { buybox: 1 } : {}),
247
399
  ...(input.includeRating === true ? { rating: 1 } : {}),
400
+ ...(input.includeVideos === true ? { videos: 1 } : {}),
401
+ ...(input.includeAPlus === true ? { aplus: 1 } : {}),
402
+ ...(input.includeStock === true ? { stock: 1 } : {}),
403
+ ...(input.includeHistoricalVariations === true ? { "historical-variations": 1 } : {}),
248
404
  };
249
405
  }
406
+ function buildStatsQuery(input) {
407
+ const range = optionalRecord(input.statsRange);
408
+ if (input.statsDays !== undefined && range) {
409
+ throw providerInputError("statsDays and statsRange are mutually exclusive");
410
+ }
411
+ if (range) {
412
+ const start = typeof range.start === "number" ? range.start : Date.parse(String(range.start));
413
+ const end = typeof range.end === "number" ? range.end : Date.parse(String(range.end));
414
+ if (typeof range.start !== typeof range.end || start >= end) {
415
+ throw providerInputError("statsRange requires matching types and start before end");
416
+ }
417
+ return { stats: `${range.start},${range.end}` };
418
+ }
419
+ return typeof input.statsDays === "number" ? { stats: input.statsDays } : {};
420
+ }
250
421
  function buildDealRequest(input, marketplace, priceType) {
251
422
  const metricDefinition = keepaMetricByType.get(priceType);
252
423
  if (!metricDefinition) {
253
- throw new ProviderRequestError(400, `unsupported deal price type: ${priceType}`);
424
+ throw providerInputError(`unsupported deal price type: ${priceType}`);
254
425
  }
255
426
  const sortName = typeof input.sortBy === "string" && input.sortBy in dealSortTypeByName
256
427
  ? input.sortBy
257
428
  : "newest";
258
429
  const sortType = dealSortTypeByName[sortName];
259
430
  if (sortType === 1 && input.invertSort === true) {
260
- throw new ProviderRequestError(400, "newest deal sorting cannot be inverted");
431
+ throw providerInputError("newest deal sorting cannot be inverted");
261
432
  }
262
433
  const ranges = {
263
434
  currentRange: readOptionalIntegerArray(input.currentRange),
264
435
  deltaRange: readOptionalIntegerArray(input.deltaRange),
265
436
  deltaPercentRange: readOptionalIntegerArray(input.deltaPercentRange),
437
+ deltaLastRange: readOptionalIntegerArray(input.deltaLastRange),
266
438
  salesRankRange: readOptionalIntegerArray(input.salesRankRange),
267
439
  };
440
+ const attributes = Object.fromEntries(keepaDealAttributeNames.map((name) => [name, readOptionalStringArray(input[name])]));
268
441
  return compactObject({
269
442
  page: typeof input.page === "number" ? input.page : 0,
270
443
  domainId: marketplaceDomainIdByCode[marketplace],
@@ -274,13 +447,17 @@ function buildDealRequest(input, marketplace, priceType) {
274
447
  isLowest: input.isLowestEver === true,
275
448
  isLowest90: input.isLowest90Days === true,
276
449
  isLowestOffer: input.isLowestOffer === true,
450
+ isHighest: input.isHighest === true,
277
451
  isBackInStock: input.isBackInStock === true,
278
452
  isOutOfStock: input.isOutOfStock === true,
279
453
  hasReviews: input.hasReviews === true,
280
454
  isPrimeExclusive: input.isPrimeExclusive === true,
281
455
  mustHaveAmazonOffer: input.mustHaveAmazonOffer === true,
282
456
  mustNotHaveAmazonOffer: input.mustNotHaveAmazonOffer === true,
283
- filterErotic: true,
457
+ filterErotic: typeof input.filterErotic === "boolean" ? input.filterErotic : undefined,
458
+ singleVariation: input.singleVariation === true,
459
+ isRisers: input.isRisers === true,
460
+ warehouseConditions: readOptionalIntegerArray(input.warehouseConditions),
284
461
  isRangeEnabled: Object.values(ranges).some((value) => value !== undefined),
285
462
  isFilterEnabled: [
286
463
  input.isLowestEver,
@@ -292,16 +469,38 @@ function buildDealRequest(input, marketplace, priceType) {
292
469
  input.isPrimeExclusive,
293
470
  input.mustHaveAmazonOffer,
294
471
  input.mustNotHaveAmazonOffer,
472
+ input.isHighest,
473
+ input.singleVariation,
474
+ input.isRisers,
475
+ input.warehouseConditions,
295
476
  input.minimumRating,
296
477
  input.titleSearch,
297
- ].some((value) => value !== undefined && value !== false),
478
+ ...Object.values(attributes),
479
+ ].some((value) => value !== undefined && value !== false) || input.filterErotic !== undefined,
298
480
  includeCategories: readOptionalIntegerArray(input.includeCategories),
299
481
  excludeCategories: readOptionalIntegerArray(input.excludeCategories),
300
- titleSearch: optionalRawString(input.titleSearch),
482
+ titleSearch: typeof input.titleSearch === "string" ? input.titleSearch : undefined,
301
483
  minRating: typeof input.minimumRating === "number" ? input.minimumRating : undefined,
302
484
  ...ranges,
485
+ ...attributes,
303
486
  });
304
487
  }
488
+ function windowList(items, input) {
489
+ const offset = typeof input.offset === "number" ? input.offset : 0;
490
+ const limit = input.limit === null ? null : typeof input.limit === "number" ? input.limit : 100;
491
+ const result = items.slice(offset, limit === null ? undefined : offset + limit);
492
+ const next = offset + result.length;
493
+ const hasMore = next < items.length;
494
+ return {
495
+ items: result,
496
+ info: {
497
+ totalAvailable: items.length,
498
+ returnedCount: result.length,
499
+ hasMore,
500
+ nextOffset: hasMore ? next : null,
501
+ },
502
+ };
503
+ }
305
504
  async function requestKeepa(input) {
306
505
  const url = new URL(input.path, keepaApiBaseUrl);
307
506
  url.searchParams.set("key", input.context.apiKey);
@@ -617,14 +816,6 @@ function validateCategorySearchTerm(term) {
617
816
  throw new ProviderRequestError(400, "each category search keyword must contain at least three characters");
618
817
  }
619
818
  }
620
- function assertProductRequestRules(input, asins) {
621
- if (typeof input.offers === "number" && asins.length > 20) {
622
- throw new ProviderRequestError(400, "offers can only be requested for up to 20 ASINs");
623
- }
624
- if (input.onlyLiveOffers === true && typeof input.offers !== "number") {
625
- throw new ProviderRequestError(400, "onlyLiveOffers requires offers");
626
- }
627
- }
628
819
  function requireInputObject(value, fieldName) {
629
820
  const object = optionalRecord(value);
630
821
  if (!object) {
@@ -683,6 +874,9 @@ function readIntegerArray(value) {
683
874
  function readOptionalIntegerArray(value) {
684
875
  return Array.isArray(value) ? readIntegerArray(value) : undefined;
685
876
  }
877
+ function readOptionalStringArray(value) {
878
+ return Array.isArray(value) ? readStringArray(value) : undefined;
879
+ }
686
880
  function objectMapValues(value) {
687
881
  const object = optionalRecord(value);
688
882
  if (!object) {
@@ -26,7 +26,7 @@ export const proxy = defineProviderProxy({
26
26
  allowedEndpoint: isBusinessEndpoint,
27
27
  allowPrivateNetwork: isPrivateNetworkAccessAllowed,
28
28
  sensitiveHeaders: ["kdservice-sessionid"],
29
- redirect: "error",
29
+ redirect: "manual",
30
30
  async customizeRequest({ context, fetcher, headers }) {
31
31
  const values = (await requireCustomCredential(context, service)).values;
32
32
  const kingdee = readCredential(values);
@@ -57,7 +57,7 @@ export async function login(credential, fetcher, parentSignal) {
57
57
  return runProviderRequest({ label: "Kingdee login", signal: parentSignal }, async (signal) => {
58
58
  const response = await fetcher(new URL("Kingdee.BOS.WebApi.ServicesStub.AuthService.LoginByAppSecret.common.kdsvc", credential.baseUrl), {
59
59
  method: "POST",
60
- redirect: "error",
60
+ redirect: "manual",
61
61
  signal,
62
62
  headers: { "content-type": "application/json", accept: "application/json" },
63
63
  body: JSON.stringify({
@@ -111,7 +111,7 @@ async function execute(action, input, context) {
111
111
  const response = await context.fetcher(new URL(`Kingdee.BOS.WebApi.ServicesStub.DynamicFormService.${kingdeeMethods[action]}.common.kdsvc`, context.credential.baseUrl), {
112
112
  method: "POST",
113
113
  signal,
114
- redirect: "error",
114
+ redirect: "manual",
115
115
  headers: { "content-type": "application/json", "kdservice-sessionid": sessionId },
116
116
  body: JSON.stringify(body),
117
117
  });
@@ -150,7 +150,7 @@ function withKuaidi100Client(context, phase, run) {
150
150
  transport: "streamable_http",
151
151
  fetcher: context.fetcher,
152
152
  headers: { responseFormat: "json", "user-agent": providerUserAgent },
153
- redirect: "error",
153
+ redirect: "manual",
154
154
  signal: context.signal,
155
155
  mapError: (error) => mapKuaidi100McpError(error, phase),
156
156
  }, run);
@@ -1,5 +1,5 @@
1
1
  import { compactObject, optionalBoolean } from "../../core/cast.js";
2
- import { createProviderFetch, createProviderProxyUrl, defineProviderExecutors, normalizeProviderProxyHeaders, ProviderRequestError, providerUserAgent, readProviderProxyErrorMessage, readProviderProxyResponse, toProviderProxyError, } from "../provider-runtime.js";
2
+ import { createProviderFetch, createProviderProxyUrl, defineProviderExecutors, normalizeProviderProxyHeaders, ProviderRequestError, providerUserAgent, readProviderProxyErrorMessage, readProviderProxyResponse, toProviderProxyError, withRetryAfterSeconds, } from "../provider-runtime.js";
3
3
  const linearApiBaseUrl = "https://api.linear.app";
4
4
  const linearGraphqlUrl = "https://api.linear.app/graphql";
5
5
  const linearFetch = createProviderFetch({ skipDnsValidation: true });
@@ -697,8 +697,8 @@ export const linearActionHandlers = {
697
697
  async list_linear_issues(input, context) {
698
698
  const assigneeId = await resolveAssigneeFilterId(context, getOptionalString(input.assignee_id));
699
699
  const payload = await linearGraphqlOperation(context, `
700
- query ListLinearIssues($after: String, $first: Int, $filter: IssueFilter) {
701
- issues(after: $after, first: $first, includeArchived: false, filter: $filter) {
700
+ query ListLinearIssues($after: String, $first: Int, $filter: IssueFilter, $includeArchived: Boolean, $orderBy: PaginationOrderBy) {
701
+ issues(after: $after, first: $first, includeArchived: $includeArchived, filter: $filter, orderBy: $orderBy) {
702
702
  nodes {
703
703
  ${issueFields}
704
704
  }
@@ -710,7 +710,9 @@ export const linearActionHandlers = {
710
710
  `, {
711
711
  after: getOptionalString(input.after),
712
712
  first: getOptionalNumber(input.first),
713
- filter: buildIssuesFilter(getOptionalString(input.project_id), assigneeId),
713
+ filter: buildIssuesFilter(getOptionalString(input.project_id), assigneeId, getOptionalString(input.updated_after)),
714
+ includeArchived: optionalBoolean(input.include_archived) ?? false,
715
+ orderBy: getOptionalString(input.order_by),
714
716
  });
715
717
  return {
716
718
  issues: (payload.issues?.nodes ?? []).map(mapIssueSummary),
@@ -1051,7 +1053,7 @@ export const proxy = async (input, context) => {
1051
1053
  const response = await linearFetch(url, init);
1052
1054
  if (!response.ok) {
1053
1055
  const text = await readProviderProxyErrorMessage(response, "");
1054
- throw new ProviderRequestError(response.status, text || `linear request failed with HTTP ${response.status}`);
1056
+ throw new ProviderRequestError(response.status, text || `linear request failed with HTTP ${response.status}`, withRetryAfterSeconds(response));
1055
1057
  }
1056
1058
  return { ok: true, response: await readProviderProxyResponse(response) };
1057
1059
  }
@@ -1117,7 +1119,7 @@ async function linearGraphqlRequest(authorization, fetcher, query, variables) {
1117
1119
  });
1118
1120
  const body = await readJson(response);
1119
1121
  if (!response.ok) {
1120
- throwLinearHttpError(response.status, body);
1122
+ throwLinearHttpError(response.status, body, withRetryAfterSeconds(response));
1121
1123
  }
1122
1124
  return body;
1123
1125
  }
@@ -1454,10 +1456,11 @@ async function resolveAssigneeFilterId(context, assigneeId) {
1454
1456
  const viewer = await fetchLinearViewer(context.authorization, context.fetcher);
1455
1457
  return String(viewer.id);
1456
1458
  }
1457
- function buildIssuesFilter(projectId, assigneeId) {
1459
+ function buildIssuesFilter(projectId, assigneeId, updatedAfter) {
1458
1460
  const filter = compactObject({
1459
1461
  project: projectId ? { id: { eq: projectId } } : undefined,
1460
1462
  assignee: assigneeId ? { id: { eq: assigneeId } } : undefined,
1463
+ updatedAt: updatedAfter ? { gte: updatedAfter } : undefined,
1461
1464
  });
1462
1465
  return Object.keys(filter).length > 0 ? filter : undefined;
1463
1466
  }
@@ -1800,7 +1803,7 @@ async function readJson(response) {
1800
1803
  };
1801
1804
  }
1802
1805
  }
1803
- function throwLinearHttpError(status, body) {
1806
+ function throwLinearHttpError(status, body, details) {
1804
1807
  const message = extractErrorMessage(body);
1805
1808
  if (status === 400) {
1806
1809
  throw new ProviderRequestError(400, message);
@@ -1809,7 +1812,7 @@ function throwLinearHttpError(status, body) {
1809
1812
  throw new ProviderRequestError(401, message);
1810
1813
  }
1811
1814
  if (status === 429) {
1812
- throw new ProviderRequestError(429, message);
1815
+ throw new ProviderRequestError(429, message, details);
1813
1816
  }
1814
1817
  throw new ProviderRequestError(502, message, status >= 500 ? 500 : status);
1815
1818
  }
@@ -0,0 +1,47 @@
1
+ import type { ActionDefinition } from "../../core/types.ts";
2
+ /** The signed-in Linear user as the MCP get_user tool reports it. */
3
+ export interface LinearMcpUser {
4
+ id: string;
5
+ email?: string;
6
+ displayName?: string;
7
+ name?: string;
8
+ }
9
+ /**
10
+ * Typed fields lifted from one Linear MCP issue record. Linear publishes no
11
+ * output schema for its MCP tools, so every field but `id` is best effort and
12
+ * the untouched text travels beside it as `raw`.
13
+ */
14
+ export interface LinearMcpIssue {
15
+ id: string;
16
+ identifier?: string;
17
+ uuid?: string;
18
+ title?: string;
19
+ description?: string;
20
+ descriptionTruncated?: boolean;
21
+ url?: string;
22
+ state?: string;
23
+ priority?: unknown;
24
+ assignee?: string;
25
+ assigneeId?: string;
26
+ creator?: string;
27
+ creatorId?: string;
28
+ team?: string;
29
+ project?: string;
30
+ labels?: string[];
31
+ createdAt?: string;
32
+ updatedAt?: string;
33
+ archivedAt?: string;
34
+ completedAt?: string;
35
+ dueDate?: string;
36
+ }
37
+ /** Typed fields lifted from one Linear MCP comment record. */
38
+ export interface LinearMcpComment {
39
+ id: string;
40
+ body?: string;
41
+ user?: string;
42
+ userId?: string;
43
+ createdAt?: string;
44
+ updatedAt?: string;
45
+ url?: string;
46
+ }
47
+ export declare const linearMcpActions: ActionDefinition[];
@@ -0,0 +1,102 @@
1
+ import { s } from "../../core/json-schema.js";
2
+ import { defineProviderAction } from "../../core/provider-definition.js";
3
+ const service = "linear_mcp";
4
+ const readScope = ["read"];
5
+ const rawSchema = s.string("The untouched text Linear's MCP tool returned. Read it when a typed field is missing: Linear can change its MCP output shape without notice.");
6
+ const userSchema = s.looseObject("The signed-in Linear user.", {
7
+ id: s.nonEmptyString("Linear user ID; the same viewer ID the Linear GraphQL API reports."),
8
+ email: s.optional(s.string("The user's email address, when returned.")),
9
+ displayName: s.optional(s.string("The user's display name, when returned.")),
10
+ name: s.optional(s.string("The user's full name, when returned.")),
11
+ });
12
+ const issueSchema = s.looseObject("A Linear issue. Typed fields are lifted from the MCP record when present; fields not listed here are only in the action's raw text.", {
13
+ id: s.nonEmptyString("Issue ID as Linear MCP returned it. This may be the human identifier such as ENG-123 rather than the UUID; see identifier and uuid."),
14
+ identifier: s.optional(s.string("Human identifier such as ENG-123, when known.")),
15
+ uuid: s.optional(s.string("Issue UUID, when known.")),
16
+ title: s.optional(s.string("Issue title.")),
17
+ description: s.optional(s.string("Issue description in Markdown. Linear MCP may clip it in lists; see descriptionTruncated.")),
18
+ descriptionTruncated: s.optional(s.boolean("Whether the description carries Linear's truncation marker. Call get_issue for the full text.")),
19
+ url: s.optional(s.string("Link to the issue in Linear.")),
20
+ state: s.optional(s.string("Workflow state name.")),
21
+ priority: s.optional(s.unknown("Priority as returned: a number or a label.")),
22
+ assignee: s.optional(s.string("Assignee name, when assigned.")),
23
+ assigneeId: s.optional(s.string("Assignee user ID, when the record carries one.")),
24
+ creator: s.optional(s.string("Creator name, when returned.")),
25
+ creatorId: s.optional(s.string("Creator user ID, when the record carries one.")),
26
+ team: s.optional(s.string("Team name or key.")),
27
+ project: s.optional(s.string("Project name, when the issue belongs to one.")),
28
+ labels: s.optional(s.stringArray("Label names.")),
29
+ createdAt: s.optional(s.string("Creation time as returned by Linear.")),
30
+ updatedAt: s.optional(s.string("Last update time as returned by Linear.")),
31
+ archivedAt: s.optional(s.string("Archive time, when archived.")),
32
+ completedAt: s.optional(s.string("Completion time, when completed.")),
33
+ dueDate: s.optional(s.string("Due date, when set.")),
34
+ });
35
+ const commentSchema = s.looseObject("A Linear issue comment.", {
36
+ id: s.nonEmptyString("Comment ID."),
37
+ body: s.optional(s.string("Comment body in Markdown.")),
38
+ user: s.optional(s.string("Author name, when returned.")),
39
+ userId: s.optional(s.string("Author user ID, when the record carries one.")),
40
+ createdAt: s.optional(s.string("Creation time as returned by Linear.")),
41
+ updatedAt: s.optional(s.string("Last edit time, when returned.")),
42
+ url: s.optional(s.string("Link to the comment in Linear.")),
43
+ });
44
+ const listIssuesInputSchema = s.object("Filters forwarded to Linear's MCP list_issues tool. Which filters the tool honours is decided by Linear's server; unknown values are rejected there.", {
45
+ updatedAt: s.string("Only issues updated at or after this time, as the tool accepts it (an ISO 8601 timestamp or a relative duration such as -P1D)."),
46
+ orderBy: s.string("Sort field, such as updatedAt or createdAt."),
47
+ limit: s.integer("Maximum number of issues to return on this page.", { minimum: 1, maximum: 250 }),
48
+ cursor: s.nonEmptyString("Cursor returned by the previous page of the same query."),
49
+ }, { optional: ["updatedAt", "orderBy", "limit", "cursor"] });
50
+ export const linearMcpActions = [
51
+ defineProviderAction(service, {
52
+ name: "get_self",
53
+ operationType: "read",
54
+ description: "Get the signed-in Linear user through Linear's MCP get_user tool.",
55
+ requiredScopes: readScope,
56
+ inputSchema: s.object({}),
57
+ outputSchema: s.requiredObject("The signed-in user.", {
58
+ user: s.nullable(userSchema),
59
+ raw: rawSchema,
60
+ }),
61
+ }),
62
+ defineProviderAction(service, {
63
+ name: "list_issues",
64
+ operationType: "read",
65
+ description: "List Linear issues through Linear's MCP list_issues tool with optional updated-since, ordering, limit, and cursor pagination. Descriptions may be clipped; call get_issue for the full text.",
66
+ requiredScopes: readScope,
67
+ followUpActions: ["linear_mcp.get_issue", "linear_mcp.list_comments"],
68
+ inputSchema: listIssuesInputSchema,
69
+ outputSchema: s.requiredObject("One page of issues.", {
70
+ issues: s.array("Issues on this page.", issueSchema),
71
+ hasNextPage: s.boolean("Whether another page follows."),
72
+ cursor: s.nullable(s.string("Cursor for the next page, when one is available.")),
73
+ raw: rawSchema,
74
+ }),
75
+ }),
76
+ defineProviderAction(service, {
77
+ name: "get_issue",
78
+ operationType: "read",
79
+ description: "Get one Linear issue with its full description through Linear's MCP get_issue tool.",
80
+ requiredScopes: readScope,
81
+ inputSchema: s.requiredObject("The issue to read.", {
82
+ id: s.nonEmptyString("Issue ID or human identifier such as ENG-123, as returned by list_issues."),
83
+ }),
84
+ outputSchema: s.requiredObject("The requested issue.", {
85
+ issue: s.nullable(issueSchema),
86
+ raw: rawSchema,
87
+ }),
88
+ }),
89
+ defineProviderAction(service, {
90
+ name: "list_comments",
91
+ operationType: "read",
92
+ description: "List the comments on one Linear issue through Linear's MCP list_comments tool.",
93
+ requiredScopes: readScope,
94
+ inputSchema: s.requiredObject("The issue whose comments to read.", {
95
+ issueId: s.nonEmptyString("Issue ID or human identifier such as ENG-123."),
96
+ }),
97
+ outputSchema: s.requiredObject("Comments on the issue.", {
98
+ comments: s.array("Comments as returned, oldest first when Linear orders them.", commentSchema),
99
+ raw: rawSchema,
100
+ }),
101
+ }),
102
+ ];
@@ -0,0 +1,3 @@
1
+ /** Linear's official remote MCP server (RFC 8707 resource) and its OAuth authorization server. */
2
+ export declare const linearMcpEndpoint = "https://mcp.linear.app/mcp";
3
+ export declare const linearMcpIssuer = "https://mcp.linear.app";
@@ -0,0 +1,3 @@
1
+ /** Linear's official remote MCP server (RFC 8707 resource) and its OAuth authorization server. */
2
+ export const linearMcpEndpoint = "https://mcp.linear.app/mcp";
3
+ export const linearMcpIssuer = "https://mcp.linear.app";
@@ -0,0 +1,3 @@
1
+ import type { CredentialValidators, ProviderExecutors } from "../../core/types.ts";
2
+ export declare const executors: ProviderExecutors;
3
+ export declare const credentialValidators: CredentialValidators;
@@ -0,0 +1,9 @@
1
+ import { defineOAuthProviderExecutors } from "../provider-runtime.js";
2
+ import { linearMcpActionHandlers, validateLinearMcpCredential } from "./runtime-mcp.js";
3
+ const service = "linear_mcp";
4
+ export const executors = defineOAuthProviderExecutors(service, linearMcpActionHandlers, {
5
+ skipDnsValidation: true,
6
+ });
7
+ export const credentialValidators = {
8
+ oauth2: validateLinearMcpCredential,
9
+ };
@@ -0,0 +1,15 @@
1
+ import type { CredentialValidationResult, CredentialValidatorOptions, ResolvedCredential } from "../../core/types.ts";
2
+ import type { OAuthProviderContext, ProviderActionHandlers, ProviderActionSources, ProviderRuntimeHandler } from "../provider-runtime.ts";
3
+ declare const service = "linear_mcp";
4
+ /** The Linear MCP tool behind each action. Sign-in requires every one of them (see the validator). */
5
+ export declare const linearMcpToolsByAction: ProviderActionSources<typeof service, string>;
6
+ export declare const linearMcpActionHandlers: ProviderActionHandlers<typeof service, ProviderRuntimeHandler<OAuthProviderContext>>;
7
+ /**
8
+ * Validate the OAuth grant against the MCP server: every tool the actions map
9
+ * must be listed (a rename shows up here, at sign-in, never mid-poll), then the
10
+ * self tool supplies the profile with the same viewer ID the GraphQL API reports.
11
+ */
12
+ export declare function validateLinearMcpCredential(credential: Extract<ResolvedCredential, {
13
+ authType: "oauth2";
14
+ }>, options: CredentialValidatorOptions): Promise<CredentialValidationResult>;
15
+ export {};