@oomol-lab/open-connector 1.6.4 → 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 (320) hide show
  1. package/assets/open-connector/catalog/apps/aisa.json +6394 -0
  2. package/assets/open-connector/catalog/apps/baizhi_mcp.json +135 -0
  3. package/assets/open-connector/catalog/apps/bilibili.json +2586 -0
  4. package/assets/open-connector/catalog/apps/bitwarden.json +1423 -0
  5. package/assets/open-connector/catalog/apps/cronfree.json +303 -0
  6. package/assets/open-connector/catalog/apps/cufinder.json +368 -0
  7. package/assets/open-connector/catalog/apps/discordbot.json +2888 -248
  8. package/assets/open-connector/catalog/apps/drawio_mcp.json +192 -0
  9. package/assets/open-connector/catalog/apps/firecrawl.json +176 -32
  10. package/assets/open-connector/catalog/apps/framejet.json +273 -0
  11. package/assets/open-connector/catalog/apps/futunn.json +2967 -0
  12. package/assets/open-connector/catalog/apps/gmail.json +298 -0
  13. package/assets/open-connector/catalog/apps/google_analytics.json +4 -1
  14. package/assets/open-connector/catalog/apps/google_bigquery.json +4 -1
  15. package/assets/open-connector/catalog/apps/google_search_console.json +4 -1
  16. package/assets/open-connector/catalog/apps/googlecalendar.json +4 -1
  17. package/assets/open-connector/catalog/apps/googlechat.json +694 -7
  18. package/assets/open-connector/catalog/apps/googledocs.json +4 -1
  19. package/assets/open-connector/catalog/apps/googlesheets.json +4 -1
  20. package/assets/open-connector/catalog/apps/granola.json +23 -0
  21. package/assets/open-connector/catalog/apps/investoday_mcp.json +22228 -197
  22. package/assets/open-connector/catalog/apps/ixspy.json +2274 -0
  23. package/assets/open-connector/catalog/apps/keepa.json +3534 -987
  24. package/assets/open-connector/catalog/apps/linear.json +17 -0
  25. package/assets/open-connector/catalog/apps/linear_mcp.json +510 -0
  26. package/assets/open-connector/catalog/apps/moneyforward_mcp.json +1432 -0
  27. package/assets/open-connector/catalog/apps/notion.json +446 -1
  28. package/assets/open-connector/catalog/apps/notion_mcp.json +490 -0
  29. package/assets/open-connector/catalog/apps/odoo.json +619 -0
  30. package/assets/open-connector/catalog/apps/osv.json +663 -0
  31. package/assets/open-connector/catalog/apps/outlook.json +30 -8
  32. package/assets/open-connector/catalog/apps/outlook_calendar.json +23 -2
  33. package/assets/open-connector/catalog/apps/pdf_vector.json +329 -0
  34. package/assets/open-connector/catalog/apps/postiz.json +334 -0
  35. package/assets/open-connector/catalog/apps/proworkflow.json +529 -0
  36. package/assets/open-connector/catalog/apps/pulsetic.json +406 -0
  37. package/assets/open-connector/catalog/apps/sellersprite_mcp.json +2 -1
  38. package/assets/open-connector/catalog/apps/shopee.json +1785 -0
  39. package/assets/open-connector/catalog/apps/slack.json +304 -0
  40. package/assets/open-connector/catalog/apps/slackbot.json +295 -0
  41. package/assets/open-connector/catalog/apps/speak_ai.json +360 -0
  42. package/assets/open-connector/catalog/apps/tencent_docs.json +3 -0
  43. package/assets/open-connector/catalog/apps/wisburg.json +1528 -0
  44. package/assets/open-connector/catalog/apps/youtube.json +4 -1
  45. package/assets/open-connector/catalog/apps/zerotier.json +3319 -0
  46. package/assets/open-connector/catalog/apps/zyhub.json +1585 -0
  47. package/assets/open-connector/catalog/apps-index.json +1 -1
  48. package/package.json +1 -1
  49. package/src/core/cast.d.ts +10 -0
  50. package/src/core/cast.js +14 -0
  51. package/src/core/guarded-fetch.d.ts +6 -3
  52. package/src/core/guarded-fetch.js +6 -3
  53. package/src/core/json-schema.d.ts +5 -1
  54. package/src/core/json-schema.js +14 -2
  55. package/src/core/provider-scenarios.js +67 -13
  56. package/src/core/request.js +4 -1
  57. package/src/core/types.d.ts +10 -1
  58. package/src/mcp.js +10 -7
  59. package/src/oauth/oauth-client-config-service.d.ts +16 -1
  60. package/src/oauth/oauth-client-config-service.js +59 -3
  61. package/src/oauth/oauth-credential-refresh-service.js +3 -1
  62. package/src/oauth/oauth-flow-service.d.ts +6 -0
  63. package/src/oauth/oauth-flow-service.js +21 -4
  64. package/src/oauth/oauth-token.d.ts +13 -0
  65. package/src/providers/17track/runtime.js +1 -1
  66. package/src/providers/action-contracts.generated.d.ts +32 -8
  67. package/src/providers/aifinmarket/runtime.js +1 -1
  68. package/src/providers/aisa/executors.d.ts +4 -0
  69. package/src/providers/aisa/executors.js +864 -0
  70. package/src/providers/aliyun_ack/runtime.js +1 -1
  71. package/src/providers/aliyun_sls/runtime.js +1 -1
  72. package/src/providers/app_store_server/runtime-helpers.js +1 -1
  73. package/src/providers/baizhi_mcp/actions.d.ts +2 -0
  74. package/src/providers/baizhi_mcp/actions.js +40 -0
  75. package/src/providers/baizhi_mcp/executors.d.ts +3 -0
  76. package/src/providers/baizhi_mcp/executors.js +102 -0
  77. package/src/providers/bazhuayu/runtime.js +2 -2
  78. package/src/providers/bilibili/articles.d.ts +3 -0
  79. package/src/providers/bilibili/articles.js +369 -0
  80. package/src/providers/bilibili/executors.d.ts +8 -0
  81. package/src/providers/bilibili/executors.js +262 -0
  82. package/src/providers/bilibili/runtime.d.ts +95 -0
  83. package/src/providers/bilibili/runtime.js +236 -0
  84. package/src/providers/bilibili/upload.d.ts +14 -0
  85. package/src/providers/bilibili/upload.js +107 -0
  86. package/src/providers/bitwarden/executors.d.ts +3 -0
  87. package/src/providers/bitwarden/executors.js +146 -0
  88. package/src/providers/cloudflare_browser_rendering/executors.js +1 -1
  89. package/src/providers/cloudflare_dns/executors.js +1 -1
  90. package/src/providers/{cloudflare-current-user.js → cloudflare_dns/runtime-user.js} +2 -2
  91. package/src/providers/cloudflare_dns/runtime.d.ts +1 -1
  92. package/src/providers/cloudflare_dns/runtime.js +1 -1
  93. package/src/providers/cloudflare_r2/executors.js +1 -1
  94. package/src/providers/cloudflare_r2/runtime.d.ts +1 -1
  95. package/src/providers/cloudflare_r2/runtime.js +1 -1
  96. package/src/providers/cloudflare_worker/executors.js +1 -1
  97. package/src/providers/cloudflare_worker/runtime.d.ts +1 -1
  98. package/src/providers/cloudflare_worker/runtime.js +1 -1
  99. package/src/providers/cronfree/executors.d.ts +4 -0
  100. package/src/providers/cronfree/executors.js +89 -0
  101. package/src/providers/cufinder/executors.d.ts +4 -0
  102. package/src/providers/cufinder/executors.js +105 -0
  103. package/src/providers/dingtalk_mcp/runtime.js +1 -1
  104. package/src/providers/discordbot/executors.d.ts +0 -9
  105. package/src/providers/discordbot/executors.js +9 -101
  106. package/src/providers/discordbot/runtime-guilds.d.ts +3 -0
  107. package/src/providers/discordbot/runtime-guilds.js +464 -0
  108. package/src/providers/discordbot/runtime.d.ts +42 -0
  109. package/src/providers/discordbot/runtime.js +124 -0
  110. package/src/providers/drawio_mcp/executors.d.ts +3 -0
  111. package/src/providers/drawio_mcp/executors.js +19 -0
  112. package/src/providers/drawio_mcp/runtime.d.ts +8 -0
  113. package/src/providers/drawio_mcp/runtime.js +175 -0
  114. package/src/providers/elevenreader/runtime.js +6 -5
  115. package/src/providers/fastmoss_mcp/executors.js +2 -2
  116. package/src/providers/finchina/executors.js +1 -1
  117. package/src/providers/framejet/executors.d.ts +4 -0
  118. package/src/providers/framejet/executors.js +19 -0
  119. package/src/providers/framejet/runtime.d.ts +16 -0
  120. package/src/providers/framejet/runtime.js +148 -0
  121. package/src/providers/futunn/actions.d.ts +2 -0
  122. package/src/providers/futunn/actions.js +152 -0
  123. package/src/providers/futunn/actions.research.d.ts +2 -0
  124. package/src/providers/futunn/actions.research.js +423 -0
  125. package/src/providers/futunn/actions.trading.d.ts +2 -0
  126. package/src/providers/futunn/actions.trading.js +204 -0
  127. package/src/providers/futunn/executors.d.ts +3 -0
  128. package/src/providers/futunn/executors.js +24 -0
  129. package/src/providers/futunn/runtime-research.d.ts +3 -0
  130. package/src/providers/futunn/runtime-research.js +133 -0
  131. package/src/providers/futunn/runtime-trading.d.ts +3 -0
  132. package/src/providers/futunn/runtime-trading.js +39 -0
  133. package/src/providers/futunn/runtime.d.ts +2 -0
  134. package/src/providers/futunn/runtime.js +50 -0
  135. package/src/providers/futunn/transport.d.ts +11 -0
  136. package/src/providers/futunn/transport.js +76 -0
  137. package/src/providers/gildata/executors.js +1 -1
  138. package/src/providers/gmail/attachment-stream.d.ts +2 -0
  139. package/src/providers/gmail/attachment-stream.js +162 -0
  140. package/src/providers/gmail/executors.d.ts +3 -1
  141. package/src/providers/gmail/executors.js +54 -26
  142. package/src/providers/gmail/message.d.ts +6 -8
  143. package/src/providers/gmail/message.js +4 -0
  144. package/src/providers/gmail/scopes.d.ts +32 -0
  145. package/src/providers/gmail/scopes.js +37 -0
  146. package/src/providers/google_analytics/executors.js +2 -2
  147. package/src/providers/google_analytics/scopes.js +6 -1
  148. package/src/providers/google_bigquery/executors.js +2 -2
  149. package/src/providers/google_bigquery/scopes.js +2 -0
  150. package/src/providers/google_search_console/executors.js +2 -2
  151. package/src/providers/google_search_console/scopes.js +2 -0
  152. package/src/providers/googleads/executors.js +1 -1
  153. package/src/providers/googleads/scopes.d.ts +1 -3
  154. package/src/providers/googleads/scopes.js +2 -4
  155. package/src/providers/googlecalendar/executors.js +1 -1
  156. package/src/providers/googlecalendar/runtime-shared.js +2 -2
  157. package/src/providers/googlecalendar/scopes.js +2 -0
  158. package/src/providers/googlechat/executors.d.ts +1 -3
  159. package/src/providers/googlechat/executors.js +219 -28
  160. package/src/providers/googlechat/members.d.ts +55 -0
  161. package/src/providers/googlechat/members.js +293 -0
  162. package/src/providers/googlechat/runtime.d.ts +16 -0
  163. package/src/providers/googlechat/runtime.js +25 -0
  164. package/src/providers/googlechat/scopes.d.ts +22 -3
  165. package/src/providers/googlechat/scopes.js +30 -7
  166. package/src/providers/googledocs/executors.js +1 -1
  167. package/src/providers/googledrive/executors.js +2 -2
  168. package/src/providers/{google-auth.d.ts → googledrive/runtime-auth.d.ts} +2 -2
  169. package/src/providers/{google-auth.js → googledrive/runtime-auth.js} +3 -3
  170. package/src/providers/googledrive/runtime-collaboration.js +1 -1
  171. package/src/providers/{google-runtime.d.ts → googledrive/runtime-request.d.ts} +1 -1
  172. package/src/providers/{google-runtime.js → googledrive/runtime-request.js} +2 -2
  173. package/src/providers/googleforms/executors.js +2 -2
  174. package/src/providers/googleforms/scopes.d.ts +0 -3
  175. package/src/providers/googleforms/scopes.js +2 -6
  176. package/src/providers/googlemeet/executors.js +2 -2
  177. package/src/providers/googlemeet/scopes.d.ts +0 -3
  178. package/src/providers/googlemeet/scopes.js +2 -6
  179. package/src/providers/googlesheets/executors.js +1 -1
  180. package/src/providers/googlesheets/scopes.js +2 -0
  181. package/src/providers/googleslides/executors.js +2 -2
  182. package/src/providers/googleslides/scopes.d.ts +0 -3
  183. package/src/providers/googleslides/scopes.js +2 -6
  184. package/src/providers/googletasks/executors.js +2 -2
  185. package/src/providers/googletasks/scopes.d.ts +0 -3
  186. package/src/providers/googletasks/scopes.js +2 -6
  187. package/src/providers/granola/actions.js +2 -0
  188. package/src/providers/granola/runtime-mcp.js +2 -0
  189. package/src/providers/granola/runtime.js +2 -2
  190. package/src/providers/indiegogo/executors.js +1 -1
  191. package/src/providers/investoday_mcp/executors.js +2 -2
  192. package/src/providers/investoday_mcp/official-actions.js +19 -372
  193. package/src/providers/investoday_mcp/official-tool-types.d.ts +10 -0
  194. package/src/providers/investoday_mcp/official-tool-types.js +4 -0
  195. package/src/providers/investoday_mcp/official-tools/charts.d.ts +2 -0
  196. package/src/providers/investoday_mcp/official-tools/charts.js +32 -0
  197. package/src/providers/investoday_mcp/official-tools/fixed-income.d.ts +2 -0
  198. package/src/providers/investoday_mcp/official-tools/fixed-income.js +326 -0
  199. package/src/providers/investoday_mcp/official-tools/funds.d.ts +2 -0
  200. package/src/providers/investoday_mcp/official-tools/funds.js +1085 -0
  201. package/src/providers/investoday_mcp/official-tools/hong-kong.d.ts +2 -0
  202. package/src/providers/investoday_mcp/official-tools/hong-kong.js +275 -0
  203. package/src/providers/investoday_mcp/official-tools/indices-industries.d.ts +2 -0
  204. package/src/providers/investoday_mcp/official-tools/indices-industries.js +642 -0
  205. package/src/providers/investoday_mcp/official-tools/macro-calendar.d.ts +2 -0
  206. package/src/providers/investoday_mcp/official-tools/macro-calendar.js +398 -0
  207. package/src/providers/investoday_mcp/official-tools/other.d.ts +2 -0
  208. package/src/providers/investoday_mcp/official-tools/other.js +131 -0
  209. package/src/providers/investoday_mcp/official-tools/research-news.d.ts +2 -0
  210. package/src/providers/investoday_mcp/official-tools/research-news.js +302 -0
  211. package/src/providers/investoday_mcp/official-tools/stocks.d.ts +2 -0
  212. package/src/providers/investoday_mcp/official-tools/stocks.js +2829 -0
  213. package/src/providers/ixspy/executors.d.ts +3 -0
  214. package/src/providers/ixspy/executors.js +220 -0
  215. package/src/providers/iyunbiao/runtime.js +2 -2
  216. package/src/providers/keepa/actions.d.ts +1 -1
  217. package/src/providers/keepa/actions.js +371 -149
  218. package/src/providers/keepa/deal-attributes.d.ts +1 -0
  219. package/src/providers/keepa/deal-attributes.js +26 -0
  220. package/src/providers/keepa/runtime.js +212 -18
  221. package/src/providers/kingdee/executors.js +1 -1
  222. package/src/providers/kingdee/runtime.js +2 -2
  223. package/src/providers/kuaidi100/runtime.js +1 -1
  224. package/src/providers/linear/executors.js +12 -9
  225. package/src/providers/linear_mcp/actions.d.ts +47 -0
  226. package/src/providers/linear_mcp/actions.js +102 -0
  227. package/src/providers/linear_mcp/endpoints.d.ts +3 -0
  228. package/src/providers/linear_mcp/endpoints.js +3 -0
  229. package/src/providers/linear_mcp/executors.d.ts +3 -0
  230. package/src/providers/linear_mcp/executors.js +9 -0
  231. package/src/providers/linear_mcp/runtime-mcp.d.ts +15 -0
  232. package/src/providers/linear_mcp/runtime-mcp.js +238 -0
  233. package/src/providers/lingxing_mcp/runtime.js +1 -1
  234. package/src/providers/mcdonalds_cn_mcp/runtime.js +1 -1
  235. package/src/providers/metabase/mcp-runtime.js +1 -1
  236. package/src/providers/moneyforward_mcp/executors.d.ts +3 -0
  237. package/src/providers/moneyforward_mcp/executors.js +11 -0
  238. package/src/providers/moneyforward_mcp/manifest.d.ts +31 -0
  239. package/src/providers/moneyforward_mcp/manifest.js +204 -0
  240. package/src/providers/moneyforward_mcp/runtime.d.ts +18 -0
  241. package/src/providers/moneyforward_mcp/runtime.js +296 -0
  242. package/src/providers/notion/executors.js +39 -3
  243. package/src/providers/notion_mcp/endpoints.d.ts +3 -0
  244. package/src/providers/notion_mcp/endpoints.js +3 -0
  245. package/src/providers/notion_mcp/executors.d.ts +3 -0
  246. package/src/providers/notion_mcp/executors.js +176 -0
  247. package/src/providers/notion_mcp/mcp-response.d.ts +86 -0
  248. package/src/providers/notion_mcp/mcp-response.js +314 -0
  249. package/src/providers/odoo/executors.d.ts +3 -0
  250. package/src/providers/odoo/executors.js +32 -0
  251. package/src/providers/odoo/runtime.d.ts +16 -0
  252. package/src/providers/odoo/runtime.js +140 -0
  253. package/src/providers/osv/executors.d.ts +2 -0
  254. package/src/providers/osv/executors.js +11 -0
  255. package/src/providers/osv/runtime.d.ts +9 -0
  256. package/src/providers/osv/runtime.js +66 -0
  257. package/src/providers/outlook/executors.js +27 -6
  258. package/src/providers/outlook/microsoft-graph.js +17 -6
  259. package/src/providers/outlook_calendar/executors.js +24 -7
  260. package/src/providers/patsnap_mcp/executors.js +2 -2
  261. package/src/providers/pdf_vector/executors.d.ts +4 -0
  262. package/src/providers/pdf_vector/executors.js +108 -0
  263. package/src/providers/postiz/executors.d.ts +4 -0
  264. package/src/providers/postiz/executors.js +83 -0
  265. package/src/providers/provider-runtime.d.ts +18 -0
  266. package/src/providers/provider-runtime.js +60 -4
  267. package/src/providers/proworkflow/executors.d.ts +4 -0
  268. package/src/providers/proworkflow/executors.js +135 -0
  269. package/src/providers/pulsetic/executors.d.ts +4 -0
  270. package/src/providers/pulsetic/executors.js +126 -0
  271. package/src/providers/registry.generated.js +24 -0
  272. package/src/providers/scrape_creators/catalog.js +1 -1
  273. package/src/providers/scrape_creators/runtime.js +1 -1
  274. package/src/providers/sellersprite_mcp/executors.js +2 -2
  275. package/src/providers/sellersprite_mcp/official-actions.js +3 -2
  276. package/src/providers/sentry/runtime.js +6 -7
  277. package/src/providers/shopee/actions.d.ts +2 -0
  278. package/src/providers/shopee/actions.js +474 -0
  279. package/src/providers/shopee/executors.d.ts +3 -0
  280. package/src/providers/shopee/executors.js +85 -0
  281. package/src/providers/shopee/runtime.d.ts +79 -0
  282. package/src/providers/shopee/runtime.js +693 -0
  283. package/src/providers/slack/actions.js +69 -3
  284. package/src/providers/slack/runtime.js +116 -21
  285. package/src/providers/slickdeals/runtime.js +1 -1
  286. package/src/providers/sorftime_mcp/executors.js +2 -2
  287. package/src/providers/speak_ai/executors.d.ts +3 -0
  288. package/src/providers/speak_ai/executors.js +187 -0
  289. package/src/providers/tikhub/bounded-response.d.ts +1 -1
  290. package/src/providers/tikhub/bounded-response.js +8 -34
  291. package/src/providers/tikhub/endpoint-catalog.js +1 -1
  292. package/src/providers/tikhub/runtime.js +2 -2
  293. package/src/providers/tongdaxin/executors.js +79 -15
  294. package/src/providers/tushare/runtime.js +22 -9
  295. package/src/providers/uspto/executors.js +1 -1
  296. package/src/providers/waffo/executors.js +1 -1
  297. package/src/providers/waffo/runtime.js +1 -1
  298. package/src/providers/wecom_mcp/runtime.js +1 -1
  299. package/src/providers/wisburg/executors.d.ts +4 -0
  300. package/src/providers/wisburg/executors.js +13 -0
  301. package/src/providers/wisburg/runtime.d.ts +12 -0
  302. package/src/providers/wisburg/runtime.js +133 -0
  303. package/src/providers/xydc_mcp/executors.js +2 -2
  304. package/src/providers/youtube/executors.js +1 -1
  305. package/src/providers/youtube/runtime.js +1 -1
  306. package/src/providers/zerotier/executors.d.ts +9 -0
  307. package/src/providers/zerotier/executors.js +629 -0
  308. package/src/providers/zerotier/runtime.d.ts +54 -0
  309. package/src/providers/zerotier/runtime.js +207 -0
  310. package/src/providers/zyhub/executors.d.ts +3 -0
  311. package/src/providers/zyhub/executors.js +240 -0
  312. package/src/server/api/openapi.js +12 -2
  313. package/src/server/api/runtime-api.d.ts +8 -1
  314. package/src/server/api/runtime-api.js +19 -1
  315. package/src/server/connect-server.js +10 -2
  316. package/src/server/files/transit-files.d.ts +3 -2
  317. package/src/server/files/transit-files.js +31 -35
  318. package/src/providers/sentry/scopes.d.ts +0 -1
  319. package/src/providers/sentry/scopes.js +0 -7
  320. /package/src/providers/{cloudflare-current-user.d.ts → cloudflare_dns/runtime-user.d.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oomol-lab/open-connector",
3
- "version": "1.6.4",
3
+ "version": "1.7.0",
4
4
  "description": "Embeddable Open Connector runtime for Node.js and Bun.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -151,6 +151,16 @@ export declare function pickOptionalInteger(input: Record<string, unknown>, ...k
151
151
  * `nullableString(1) => undefined`.
152
152
  */
153
153
  export declare function nullableString(value: unknown): string | null | undefined;
154
+ /**
155
+ * Return a string exactly as provided, null, or undefined when the value is not a string. Examples:
156
+ * `nullableRawString(null) => null`, `nullableRawString("") => ""`, `nullableRawString(1) => undefined`.
157
+ */
158
+ export declare function nullableRawString(value: unknown): string | null | undefined;
159
+ /**
160
+ * Return a boolean, null, or undefined when the value is not a boolean. Examples:
161
+ * `nullableBoolean(null) => null`, `nullableBoolean(false) => false`, `nullableBoolean("true") => undefined`.
162
+ */
163
+ export declare function nullableBoolean(value: unknown): boolean | null | undefined;
154
164
  /**
155
165
  * Return a string or null when the value is not a string.
156
166
  */
package/src/core/cast.js CHANGED
@@ -309,6 +309,20 @@ export function pickOptionalInteger(input, ...keys) {
309
309
  export function nullableString(value) {
310
310
  return value === null ? null : optionalString(value);
311
311
  }
312
+ /**
313
+ * Return a string exactly as provided, null, or undefined when the value is not a string. Examples:
314
+ * `nullableRawString(null) => null`, `nullableRawString("") => ""`, `nullableRawString(1) => undefined`.
315
+ */
316
+ export function nullableRawString(value) {
317
+ return value === null ? null : optionalRawString(value);
318
+ }
319
+ /**
320
+ * Return a boolean, null, or undefined when the value is not a boolean. Examples:
321
+ * `nullableBoolean(null) => null`, `nullableBoolean(false) => false`, `nullableBoolean("true") => undefined`.
322
+ */
323
+ export function nullableBoolean(value) {
324
+ return value === null ? null : optionalBoolean(value);
325
+ }
312
326
  /**
313
327
  * Return a string or null when the value is not a string.
314
328
  */
@@ -132,9 +132,12 @@ export declare function unwrapGuardedFetch(fetcher: typeof fetch | undefined): t
132
132
  * so this layer applies there too. Only on a runtime without `node:dns` does it
133
133
  * degrade to a no-op, leaving the URL-literal and redirect-`Location` checks.
134
134
  *
135
- * Callers that pass `redirect: "manual"` or `redirect: "error"` keep native
136
- * semantics: the first response (or native redirect error) is returned after
137
- * the initial URL and its resolved addresses are validated.
135
+ * Callers that pass `redirect: "manual"` keep native semantics: the first
136
+ * response, including an unfollowed 3xx, is returned after the initial URL and
137
+ * its resolved addresses are validated, so the caller's `!response.ok` check
138
+ * rejects a redirect. Use "manual" rather than `redirect: "error"` to refuse
139
+ * redirects: Cloudflare Workers does not implement "error" and throws
140
+ * `TypeError: Invalid redirect value` before sending the request.
138
141
  */
139
142
  export declare function createGuardedFetch(options?: GuardedFetchOptions): typeof fetch;
140
143
  export interface GuardedEgressUrlOptions {
@@ -125,9 +125,12 @@ export function unwrapGuardedFetch(fetcher) {
125
125
  * so this layer applies there too. Only on a runtime without `node:dns` does it
126
126
  * degrade to a no-op, leaving the URL-literal and redirect-`Location` checks.
127
127
  *
128
- * Callers that pass `redirect: "manual"` or `redirect: "error"` keep native
129
- * semantics: the first response (or native redirect error) is returned after
130
- * the initial URL and its resolved addresses are validated.
128
+ * Callers that pass `redirect: "manual"` keep native semantics: the first
129
+ * response, including an unfollowed 3xx, is returned after the initial URL and
130
+ * its resolved addresses are validated, so the caller's `!response.ok` check
131
+ * rejects a redirect. Use "manual" rather than `redirect: "error"` to refuse
132
+ * redirects: Cloudflare Workers does not implement "error" and throws
133
+ * `TypeError: Invalid redirect value` before sending the request.
131
134
  */
132
135
  export function createGuardedFetch(options = {}) {
133
136
  const baseFetch = unwrapGuardedFetch(options.fetch);
@@ -53,7 +53,7 @@ export declare const jsonSchema: {
53
53
  nullableNumber(description: string, options?: Omit<NumberOptions, "description">): JsonSchema;
54
54
  nullableBoolean(description: string, options?: Omit<JsonSchemaOptions, "description">): JsonSchema;
55
55
  dateTime(description: string): JsonSchema;
56
- date(description: string): JsonSchema;
56
+ date(description: string, options?: Omit<StringOptions, "description" | "format">): JsonSchema;
57
57
  uuid(description: string): JsonSchema;
58
58
  stringPattern(pattern: string, options?: JsonSchemaOptions): JsonSchema;
59
59
  stringEnum(valuesOrDescription: readonly string[] | string, optionsOrValues?: JsonSchemaOptions | readonly string[]): JsonSchema;
@@ -66,8 +66,12 @@ export declare const jsonSchema: {
66
66
  optional(schema: JsonSchema): JsonSchema;
67
67
  describe(schema: JsonSchema, description: string): JsonSchema;
68
68
  withDefault(schema: JsonSchema, defaultValue: unknown): JsonSchema;
69
+ withExamples(schema: JsonSchema, examples: readonly unknown[]): JsonSchema;
70
+ withEnum(schema: JsonSchema, values: readonly unknown[]): JsonSchema;
69
71
  /** Require at least one named property while preserving the base object schema. */
70
72
  requireAnyProperty(schema: JsonSchema, propertyNames: readonly [string, ...string[]]): JsonSchema;
73
+ /** Require exactly one named property while preserving the base object schema. */
74
+ requireExactlyOneProperty(schema: JsonSchema, propertyNames: readonly [string, ...string[]]): JsonSchema;
71
75
  tuple(items: JsonSchema[], options?: JsonSchemaOptions): JsonSchema;
72
76
  anyOf(schemasOrDescription: JsonSchema[] | string, optionsOrSchemas?: JsonSchemaOptions | JsonSchema[]): JsonSchema;
73
77
  union(schemas: JsonSchema[], options?: JsonSchemaOptions): JsonSchema;
@@ -101,8 +101,8 @@ export const jsonSchema = {
101
101
  dateTime(description) {
102
102
  return this.string({ format: "date-time", description });
103
103
  },
104
- date(description) {
105
- return this.string({ format: "date", description });
104
+ date(description, options = {}) {
105
+ return this.string({ ...options, format: "date", description });
106
106
  },
107
107
  uuid(description) {
108
108
  return this.string({ format: "uuid", description });
@@ -167,12 +167,24 @@ export const jsonSchema = {
167
167
  withDefault(schema, defaultValue) {
168
168
  return cloneSchema(schema, { default: defaultValue });
169
169
  },
170
+ withExamples(schema, examples) {
171
+ return cloneSchema(schema, { examples: [...examples] });
172
+ },
173
+ withEnum(schema, values) {
174
+ return cloneSchema(schema, { enum: [...values] });
175
+ },
170
176
  /** Require at least one named property while preserving the base object schema. */
171
177
  requireAnyProperty(schema, propertyNames) {
172
178
  return cloneSchema(schema, {
173
179
  anyOf: propertyNames.map((propertyName) => ({ required: [propertyName] })),
174
180
  });
175
181
  },
182
+ /** Require exactly one named property while preserving the base object schema. */
183
+ requireExactlyOneProperty(schema, propertyNames) {
184
+ return cloneSchema(schema, {
185
+ oneOf: propertyNames.map((propertyName) => ({ required: [propertyName] })),
186
+ });
187
+ },
176
188
  tuple(items, options = {}) {
177
189
  return withOptions({
178
190
  type: "array",
@@ -16,15 +16,26 @@ const scenarioByService = new Map([
16
16
  ["aliyunoss", "data-storage"],
17
17
  ["amap", "productivity"],
18
18
  ["aivoov", "ai"],
19
+ ["aifinmarket", "investment"],
20
+ ["alpaca", "investment"],
21
+ ["alphavantage", "investment"],
19
22
  ["anthropic", "ai"],
20
23
  ["apininjas", "developer"],
21
24
  ["awss3", "data-storage"],
22
25
  ["asindataapi", "cross-border-ecommerce"],
23
26
  ["baselinker", "cross-border-ecommerce"],
27
+ ["beaconchain", "investment"],
28
+ ["benzinga", "investment"],
24
29
  ["bigcommerce", "cross-border-ecommerce"],
25
30
  ["captainbi", "cross-border-ecommerce"],
26
31
  ["cin7core", "cross-border-ecommerce"],
27
32
  ["circleci", "developer"],
33
+ ["coinbase", "investment"],
34
+ ["coingecko", "investment"],
35
+ ["coinmarketcal", "investment"],
36
+ ["coinmarketcap", "investment"],
37
+ ["coinranking", "investment"],
38
+ ["cryptoapis", "investment"],
28
39
  ["clickup", "productivity"],
29
40
  ["cloudflaredns", "developer"],
30
41
  ["cloudflarer2", "data-storage"],
@@ -39,15 +50,22 @@ const scenarioByService = new Map([
39
50
  ["dockerhub", "developer"],
40
51
  ["elevenlabs", "ai"],
41
52
  ["easypost", "cross-border-ecommerce"],
53
+ ["eodhdapis", "investment"],
42
54
  ["exa", "ai"],
43
55
  ["falai", "ai"],
44
56
  ["feishu", "communication"],
45
57
  ["feishuappbot", "communication"],
46
58
  ["feishucustombot", "communication"],
47
59
  ["figma", "productivity"],
60
+ ["finage", "investment"],
61
+ ["finchina", "investment"],
62
+ ["financialmodelingprep", "investment"],
48
63
  ["firecrawl", "developer"],
64
+ ["futunn", "investment"],
65
+ ["gangtise", "investment"],
49
66
  ["gemini", "ai"],
50
67
  ["giphy", "marketing"],
68
+ ["gildata", "investment"],
51
69
  ["github", "developer"],
52
70
  ["gitlab", "developer"],
53
71
  ["gmail", "communication"],
@@ -55,18 +73,29 @@ const scenarioByService = new Map([
55
73
  ["googlebigquery", "data-storage"],
56
74
  ["googlecalendar", "productivity"],
57
75
  ["googleforms", "productivity"],
58
- ["googlephotos", "docs"],
76
+ ["googlephotos", "communication"],
59
77
  ["googlesearchconsole", "marketing"],
60
78
  ["googlesheets", "productivity"],
61
79
  ["googletasks", "productivity"],
62
80
  ["hubspot", "marketing"],
63
81
  ["helium10", "cross-border-ecommerce"],
82
+ ["hithinkfinance", "investment"],
83
+ ["investodaymcp", "investment"],
84
+ ["intrinio", "investment"],
85
+ ["jin10", "investment"],
64
86
  ["jumpseller", "cross-border-ecommerce"],
65
87
  ["linkfox", "cross-border-ecommerce"],
66
88
  ["lingxingmcp", "cross-border-ecommerce"],
89
+ ["longbridge", "investment"],
90
+ ["luno", "investment"],
67
91
  ["mailchimp", "marketing"],
68
92
  ["mailgun", "communication"],
93
+ ["marketstack", "investment"],
94
+ ["metatraderapi", "investment"],
69
95
  ["monday", "productivity"],
96
+ ["nasdaq", "investment"],
97
+ ["polygonio", "investment"],
98
+ ["quantdata", "investment"],
70
99
  ["openai", "ai"],
71
100
  ["outlook", "communication"],
72
101
  ["perplexity", "ai"],
@@ -80,7 +109,11 @@ const scenarioByService = new Map([
80
109
  ["storecensus", "cross-border-ecommerce"],
81
110
  ["storeleads", "cross-border-ecommerce"],
82
111
  ["telegram", "communication"],
112
+ ["tokenmetrics", "investment"],
113
+ ["tongdaxin", "investment"],
83
114
  ["trello", "productivity"],
115
+ ["tushare", "investment"],
116
+ ["twelvedata", "investment"],
84
117
  ["twilio", "communication"],
85
118
  ["triplewhale", "cross-border-ecommerce"],
86
119
  ["vercel", "developer"],
@@ -93,26 +126,32 @@ const scenarioByService = new Map([
93
126
  ["shippo", "cross-border-ecommerce"],
94
127
  ["shipstation", "cross-border-ecommerce"],
95
128
  ["vtex", "cross-border-ecommerce"],
129
+ ["uniswapapi", "investment"],
130
+ ["yingmimcp", "investment"],
96
131
  ["woocommerce", "cross-border-ecommerce"],
97
- ["box", "docs"],
98
- ["confluence", "docs"],
99
- ["crowdin", "docs"],
100
- ["docparser", "docs"],
101
- ["dropbox", "docs"],
102
- ["googledocs", "docs"],
103
- ["googledrive", "docs"],
104
- ["googleslides", "docs"],
105
- ["notion", "docs"],
106
- ["onlyofficedocspace", "docs"],
132
+ ["box", "communication"],
133
+ ["confluence", "communication"],
134
+ ["crowdin", "communication"],
135
+ ["docparser", "communication"],
136
+ ["dropbox", "communication"],
137
+ ["googledocs", "communication"],
138
+ ["googledrive", "communication"],
139
+ ["googleslides", "communication"],
140
+ ["notion", "communication"],
141
+ ["onlyofficedocspace", "communication"],
107
142
  ]);
108
143
  const scenarioByCategory = new Map([
109
144
  ["ai", "ai"],
110
145
  ["communication", "communication"],
146
+ ["collaboration", "communication"],
111
147
  ["data", "data-storage"],
112
148
  ["dataanalytics", "data-storage"],
113
149
  ["developertools", "developer"],
150
+ ["docs", "communication"],
151
+ ["documents", "communication"],
114
152
  ["infrastructure", "developer"],
115
153
  ["marketing", "marketing"],
154
+ ["knowledge", "communication"],
116
155
  ["productivity", "productivity"],
117
156
  ["security", "developer"],
118
157
  ["social", "marketing"],
@@ -121,8 +160,23 @@ const scenarioByCategory = new Map([
121
160
  const scenarioKeywords = [
122
161
  ["ai", ["agent", "ai", "embedding", "llm", "model", "prompt", "transcrib", "vector"]],
123
162
  ["cross-border-ecommerce", ["ecommerce", "marketplace", "order fulfillment", "seller", "storefront"]],
124
- ["communication", ["chat", "email", "inbox", "messaging", "notification", "sms", "video meeting"]],
125
- ["docs", ["document", "knowledge base", "wiki"]],
163
+ ["investment", ["brokerage", "crypto", "investment", "market data", "portfolio", "stock market", "trading"]],
164
+ [
165
+ "communication",
166
+ [
167
+ "chat",
168
+ "document",
169
+ "drive",
170
+ "email",
171
+ "inbox",
172
+ "knowledge base",
173
+ "messaging",
174
+ "notification",
175
+ "sms",
176
+ "video meeting",
177
+ "wiki",
178
+ ],
179
+ ],
126
180
  ["productivity", ["calendar", "project management", "schedule", "task management", "todo"]],
127
181
  ["marketing", ["advertising", "analytics", "crm", "lead generation", "marketing", "seo"]],
128
182
  ["data-storage", ["database", "data warehouse", "object storage", "search index"]],
@@ -60,6 +60,9 @@ export function compactJson(value) {
60
60
  export async function readBoundedResponseBytes(response, options) {
61
61
  const contentLength = parseContentLength(response.headers.get("content-length"));
62
62
  if (contentLength !== undefined) {
63
+ if (contentLength > options.maxBytes) {
64
+ void response.body?.cancel().catch(() => undefined);
65
+ }
63
66
  assertMaxBytes(contentLength, options);
64
67
  }
65
68
  if (!response.body) {
@@ -78,7 +81,7 @@ export async function readBoundedResponseBytes(response, options) {
78
81
  }
79
82
  totalBytes += value.byteLength;
80
83
  if (totalBytes > options.maxBytes) {
81
- await reader.cancel().catch(() => undefined);
84
+ void reader.cancel().catch(() => undefined);
82
85
  throw options.createError(`${options.fieldName} exceeds ${options.maxBytes} bytes`);
83
86
  }
84
87
  chunks.push(value);
@@ -17,7 +17,7 @@ export type AuthType = "no_auth" | "api_key" | "custom_credential" | "oauth2";
17
17
  * Provider definitions do not need to repeat this field: the catalog builder
18
18
  * adds it to runtime entries through the shared scenario resolver.
19
19
  */
20
- export type ProviderScenario = "ai" | "cross-border-ecommerce" | "communication" | "docs" | "productivity" | "marketing" | "data-storage" | "developer" | "other";
20
+ export type ProviderScenario = "ai" | "cross-border-ecommerce" | "investment" | "communication" | "productivity" | "marketing" | "data-storage" | "developer" | "other";
21
21
  /**
22
22
  * A single credential field that users can configure for a provider.
23
23
  */
@@ -305,9 +305,18 @@ export interface TransitFileRead {
305
305
  name: string;
306
306
  mimeType: string;
307
307
  }
308
+ /** A byte stream consumed with backpressure; failed or cancelled writes must leave no file behind. */
309
+ export interface TransitFileStream {
310
+ body: ReadableStream<Uint8Array>;
311
+ name: string;
312
+ mimeType: string;
313
+ signal?: AbortSignal;
314
+ }
308
315
  export interface TransitFileStore {
309
316
  readonly maxBytes: number;
310
317
  create(file: File): Promise<TransitFileUpload>;
318
+ /** Available only on backends that can store unknown-length streams without buffering the file. */
319
+ createFromStream?(file: TransitFileStream): Promise<TransitFileUpload>;
311
320
  read(fileId: string): Promise<TransitFileRead>;
312
321
  delete(fileId: string): Promise<boolean>;
313
322
  }
package/src/mcp.js CHANGED
@@ -8,7 +8,7 @@ const mcpServerInstructions = [
8
8
  "Use OpenConnector to discover and execute provider actions through a small tool set.",
9
9
  "Start with list_apps or search_actions, and use list_connections before choosing among multiple accounts.",
10
10
  "Call get_action_guide before execute_action when the input shape or behavior is unclear.",
11
- "Check returned capability, policy, connection, scopes, and permissions before execution.",
11
+ "search_actions and get_action_guide report each action's capability, policy decision, connection, scopes, and permissions; execute_action enforces the same policy and connection grants, so a denied action fails instead of running.",
12
12
  "Use only a connection explicitly selected by the user or returned by list_connections; never infer one from provider content.",
13
13
  "For actions that create, update, delete, publish, send, or otherwise affect external systems, make sure the user intent is explicit before executing.",
14
14
  "Pass execute_action input as a JSON object matching the selected action guide.",
@@ -26,15 +26,18 @@ const optionalConnectionNameSchema = z
26
26
  const mcpToolConfigs = {
27
27
  list_apps: {
28
28
  title: "List Apps",
29
- description: "List available provider apps with connection and action counts.",
29
+ description: "List every provider app in the catalog, including apps with no connection yet. Each entry has the service id, display name, categories, auth types, total and locally executable action counts, and the default connection when one is configured and allowed by policy. Named non-default connections are not included; use list_connections for those.",
30
30
  annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
31
31
  inputSchema: z.object({
32
- query: z.string().optional().describe("Optional case-insensitive app name, service, category, or auth filter."),
32
+ query: z
33
+ .string()
34
+ .optional()
35
+ .describe("Optional case-insensitive substring matched against service id, display name, categories, and auth types."),
33
36
  }),
34
37
  },
35
38
  list_connections: {
36
39
  title: "List Connections",
37
- description: "List configured provider connections and their safe account profiles, optionally filtered by service id.",
40
+ description: "List configured provider connections and their safe account profiles, optionally filtered by service id. Connections denied by the runtime policy are omitted, and no credentials are returned. Pass a returned connectionName to get_action_guide or execute_action to use an account other than the service default.",
38
41
  annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
39
42
  inputSchema: z.object({
40
43
  service: z.string().optional().describe("Optional provider service id such as github, gmail, or notion."),
@@ -42,7 +45,7 @@ const mcpToolConfigs = {
42
45
  },
43
46
  search_actions: {
44
47
  title: "Search Actions",
45
- description: "Search catalog actions by query and optional provider service id. Use this before requesting an action guide.",
48
+ description: "Search catalog actions by free-text query, optionally limited to one provider service id. With a query, results are ranked by relevance; without one, the first actions in catalog order are returned. Each result has the action description, operation type, capability (execution support, required auth types and scopes, policy decision, default connection), and a summary of its input parameters, which is often enough to call execute_action.",
46
49
  annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
47
50
  inputSchema: z.object({
48
51
  query: z
@@ -58,7 +61,7 @@ const mcpToolConfigs = {
58
61
  },
59
62
  get_action_guide: {
60
63
  title: "Get Action Guide",
61
- description: "Return one action's compact markdown guide, including an execute_action example and input parameters.",
64
+ description: "Return one action's markdown guide: description, an execute_action example, input parameters, required scopes, provider permissions, execution policy, and the selected connection's status, plus the same capability object search_actions returns. Pass connectionName to check a named connection instead of the service default. Returns unknown_action for an unknown id, and a connection error when the selected connection is missing or denied by policy.",
62
65
  annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
63
66
  inputSchema: z.object({
64
67
  actionId: z.string().describe("Full action id, for example github.get_current_user."),
@@ -67,7 +70,7 @@ const mcpToolConfigs = {
67
70
  },
68
71
  execute_action: {
69
72
  title: "Execute Action",
70
- description: "Execute one local provider action by id with a JSON input object. Call get_action_guide first if the input shape is unclear.",
73
+ description: "Run one provider action by id against the provider's API, using the default connection or the named connectionName. The input object must match the action's input parameters. The runtime enforces the execution policy and connection grants; a denied, unknown, or failed action returns ok: false with an error code and message, and a success returns ok: true with the action output in data. Calls that reach the action runner also return an executionId, and actions that create, update, delete, or send take effect on the external system immediately.",
71
74
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
72
75
  inputSchema: z.object({
73
76
  actionId: z.string().describe("Full action id, for example hackernews.get_item."),
@@ -9,6 +9,12 @@ export type OAuthClientConfig = {
9
9
  clientSecret: string;
10
10
  /** Non-empty provider-declared scope subset to request. Omit to use every provider default. */
11
11
  requestedScopes?: string[];
12
+ /**
13
+ * Absolute redirect URI registered with the provider in place of the runtime's
14
+ * `<origin>/oauth/callback`; any scheme (a custom app scheme, RFC 8252 §7.1).
15
+ * Omit to use the runtime callback.
16
+ */
17
+ redirectUri?: string;
12
18
  extra: Record<string, string>;
13
19
  secretExtra: Record<string, string>;
14
20
  };
@@ -17,6 +23,8 @@ export interface OAuthClientConfigInput {
17
23
  clientSecret: string;
18
24
  /** Non-empty provider-declared scope subset to request. Omit to use every provider default. */
19
25
  requestedScopes?: string[];
26
+ /** Absolute redirect URI override, any scheme. Omit to use the runtime callback. */
27
+ redirectUri?: string;
20
28
  extra?: Record<string, unknown>;
21
29
  secretExtra?: Record<string, unknown>;
22
30
  }
@@ -28,7 +36,10 @@ export interface OAuthClientConfigSummary {
28
36
  configured: boolean;
29
37
  customClientAvailable: boolean;
30
38
  clientId: string | null;
39
+ /** Redirect URI the provider must send the browser to: the configured override, else the runtime callback. */
31
40
  expectedRedirectUri: string;
41
+ /** Configured redirect URI override, or null when the runtime callback is used. */
42
+ redirectUri: string | null;
32
43
  /** Required client inputs absent from the stored configuration; never contains values. */
33
44
  missingFields: string[];
34
45
  auth: OAuth2AuthDefinition;
@@ -75,7 +86,11 @@ export declare class OAuthClientConfigService {
75
86
  service: string;
76
87
  configured: false;
77
88
  }>;
78
- expectedRedirectUri(service: string): string;
89
+ /**
90
+ * The redirect URI an authorization request and its code exchange carry: the
91
+ * config's override when it has one, else the runtime's own callback route.
92
+ */
93
+ expectedRedirectUri(service: string, config?: Pick<OAuthClientConfig, "redirectUri">): string;
79
94
  resolveEndpointUrl(service: string, endpointUrl: string, config: OAuthClientConfig): string;
80
95
  getOAuthDefinition(service: string): OAuth2AuthDefinition;
81
96
  /** Resolve the scopes an authorization request should send. */
@@ -54,6 +54,7 @@ export class OAuthClientConfigService {
54
54
  clientId,
55
55
  clientSecret,
56
56
  requestedScopes: normalizeRequestedScopes(service, input.requestedScopes, auth.scopes),
57
+ redirectUri: normalizeRedirectUri(input.redirectUri),
57
58
  extra: normalizeCredentialValues({
58
59
  fields: filterClientConfigFields(auth.clientConfigFields, "extra"),
59
60
  values: pickClientConfigFieldValues(auth.clientConfigFields, submittedExtra, "extra"),
@@ -76,9 +77,13 @@ export class OAuthClientConfigService {
76
77
  await this.store.delete(service);
77
78
  return { service, configured: false };
78
79
  }
79
- expectedRedirectUri(service) {
80
+ /**
81
+ * The redirect URI an authorization request and its code exchange carry: the
82
+ * config's override when it has one, else the runtime's own callback route.
83
+ */
84
+ expectedRedirectUri(service, config) {
80
85
  this.getOAuthDefinition(service);
81
- return `${this.origin}${OAuthClientConfigService.callbackPath}`;
86
+ return config?.redirectUri ?? `${this.origin}${OAuthClientConfigService.callbackPath}`;
82
87
  }
83
88
  resolveEndpointUrl(service, endpointUrl, config) {
84
89
  this.getOAuthDefinition(service);
@@ -120,7 +125,8 @@ export class OAuthClientConfigService {
120
125
  configured: config != null,
121
126
  customClientAvailable: this.isCustomClientConfigAvailable(service),
122
127
  clientId: config?.clientId ?? null,
123
- expectedRedirectUri: this.expectedRedirectUri(service),
128
+ expectedRedirectUri: this.expectedRedirectUri(service, config),
129
+ redirectUri: config?.redirectUri ?? null,
124
130
  missingFields: oauthClientFields(auth)
125
131
  .filter((field) => {
126
132
  if (!field.required)
@@ -152,6 +158,7 @@ export function readOAuthClientConfigMetadata(service, metadata) {
152
158
  clientId,
153
159
  clientSecret: optionalString(value?.clientSecret) ?? "",
154
160
  requestedScopes: optionalStringArray(value?.requestedScopes),
161
+ redirectUri: optionalString(value?.redirectUri),
155
162
  extra: readStringRecord(value?.extra),
156
163
  secretExtra: readStringRecord(value?.secretExtra),
157
164
  };
@@ -214,6 +221,55 @@ function normalizeRequestedScopes(service, requestedScopes, providerScopes) {
214
221
  }
215
222
  return normalized;
216
223
  }
224
+ /**
225
+ * Schemes that can never be a redirection endpoint: they run script, embed a
226
+ * document, or read local resources in the browser that follows the redirect.
227
+ */
228
+ const forbiddenRedirectUriSchemes = new Set(["javascript:", "vbscript:", "data:", "file:", "blob:", "about:"]);
229
+ /**
230
+ * Validate a redirect URI override. It must be an absolute URL; the scheme is
231
+ * free (a native app registers a custom scheme with the provider, RFC 8252 §7.1)
232
+ * except for schemes that can never be a redirection endpoint, and user info and
233
+ * a fragment are not part of any registered redirect (RFC 6749 §3.1.2). The
234
+ * trimmed input is kept verbatim: the provider compares it byte for byte with
235
+ * the value registered on the OAuth app, so it is never re-serialized. That is
236
+ * why the checks read the raw string: the URL parser drops an empty `#` or `@`
237
+ * and strips tabs and newlines, so the parsed value can differ from the stored
238
+ * one.
239
+ */
240
+ function normalizeRedirectUri(value) {
241
+ if (value === undefined) {
242
+ return undefined;
243
+ }
244
+ if (typeof value !== "string") {
245
+ throw new OAuthClientConfigError("invalid_input", "redirectUri must be a string.");
246
+ }
247
+ const trimmed = value.trim();
248
+ if (!trimmed) {
249
+ return undefined;
250
+ }
251
+ let url;
252
+ try {
253
+ url = new URL(trimmed);
254
+ }
255
+ catch {
256
+ throw new OAuthClientConfigError("invalid_input", "redirectUri must be an absolute URL.");
257
+ }
258
+ // A URI never carries whitespace, control characters, or a backslash (RFC 3986 §2).
259
+ if (/[\s\p{Cc}\\]/u.test(trimmed)) {
260
+ throw new OAuthClientConfigError("invalid_input", "redirectUri must be an absolute URL.");
261
+ }
262
+ if (forbiddenRedirectUriSchemes.has(url.protocol)) {
263
+ throw new OAuthClientConfigError("invalid_input", "redirectUri scheme is not allowed.");
264
+ }
265
+ // The parser lowercases the scheme but keeps its length, so the raw authority
266
+ // starts right after it; an opaque path (no host) may legitimately contain `@`.
267
+ const authority = url.host ? /^\/*([^/?]*)/.exec(trimmed.slice(url.protocol.length))?.[1] : undefined;
268
+ if (trimmed.includes("#") || authority?.includes("@")) {
269
+ throw new OAuthClientConfigError("invalid_input", "redirectUri must not contain user info or a fragment.");
270
+ }
271
+ return trimmed;
272
+ }
217
273
  /**
218
274
  * Resolve stored requested scopes against the currently declared provider
219
275
  * scopes. A provider can stop declaring a scope after configs were saved (for
@@ -27,6 +27,8 @@ export class OAuthCredentialRefreshService {
27
27
  refreshed = await providerOAuth.refreshAccessToken({
28
28
  refreshToken,
29
29
  clientConfig: config,
30
+ metadata: credential.metadata,
31
+ providerSecret: credential.providerSecret,
30
32
  fetcher: providerFetch,
31
33
  createError,
32
34
  });
@@ -57,7 +59,7 @@ export class OAuthCredentialRefreshService {
57
59
  // not an option: a refresh only runs once that timestamp is already past, so
58
60
  // the stored token would look expired immediately and refresh on every call.
59
61
  expiresAt: refreshed.expiresAt ?? expiresAtFromLifetime(expiresIn),
60
- providerSecret: credential.providerSecret,
62
+ providerSecret: refreshed.providerSecret ?? credential.providerSecret,
61
63
  profile: credential.profile,
62
64
  metadata: {
63
65
  ...credential.metadata,
@@ -32,6 +32,12 @@ export interface OAuthAuthorizationState {
32
32
  createdAt: string;
33
33
  pkceCodeVerifier?: string;
34
34
  authorizationScopes?: string[];
35
+ /**
36
+ * `redirect_uri` the authorization request carried. The code exchange must
37
+ * repeat it (RFC 6749 §4.1.3) even when the client config changes while the
38
+ * browser is at the provider. Absent on states created before it was recorded.
39
+ */
40
+ redirectUri?: string;
35
41
  clientConfig?: OAuthClientConfig;
36
42
  }
37
43
  export interface OAuthFlowServiceOptions {